mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-19 10:00:27 +00:00
add better default behavior on fill struct fields diagnostic
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
parent
df6fa50f92
commit
336c899a07
6 changed files with 123 additions and 13 deletions
|
@ -1685,6 +1685,26 @@ impl BuiltinType {
|
||||||
pub fn name(self) -> Name {
|
pub fn name(self) -> Name {
|
||||||
self.inner.as_name()
|
self.inner.as_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_int(&self) -> bool {
|
||||||
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Int(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_uint(&self) -> bool {
|
||||||
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Uint(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_float(&self) -> bool {
|
||||||
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_char(&self) -> bool {
|
||||||
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_str(&self) -> bool {
|
||||||
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -2573,6 +2593,10 @@ impl Type {
|
||||||
matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
|
matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_array(&self) -> bool {
|
||||||
|
matches!(&self.ty.kind(Interner), TyKind::Array(..))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
|
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
|
||||||
let adt_id = match *self.ty.kind(Interner) {
|
let adt_id = match *self.ty.kind(Interner) {
|
||||||
TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
|
TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
|
||||||
|
|
|
@ -226,6 +226,7 @@ pub mod known {
|
||||||
iter_mut,
|
iter_mut,
|
||||||
len,
|
len,
|
||||||
is_empty,
|
is_empty,
|
||||||
|
new,
|
||||||
// Builtin macros
|
// Builtin macros
|
||||||
asm,
|
asm,
|
||||||
assert,
|
assert,
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{db::AstDatabase, InFile, Type};
|
use hir::{
|
||||||
|
db::{AstDatabase, HirDatabase},
|
||||||
|
known, HirDisplay, InFile, SemanticsScope, Type,
|
||||||
|
};
|
||||||
use ide_db::{assists::Assist, helpers::FamousDefs, source_change::SourceChange};
|
use ide_db::{assists::Assist, helpers::FamousDefs, source_change::SourceChange};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
|
use syntax::{
|
||||||
|
algo,
|
||||||
|
ast::{self, make},
|
||||||
|
AstNode, SyntaxNodePtr,
|
||||||
|
};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{fix, Diagnostic, DiagnosticsContext};
|
use crate::{fix, Diagnostic, DiagnosticsContext};
|
||||||
|
@ -67,13 +74,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
|
||||||
let generate_fill_expr = |ty: &Type| match ctx.config.expr_fill_default {
|
let generate_fill_expr = |ty: &Type| match ctx.config.expr_fill_default {
|
||||||
crate::ExprFillDefaultMode::Todo => Some(make::ext::expr_todo()),
|
crate::ExprFillDefaultMode::Todo => Some(make::ext::expr_todo()),
|
||||||
crate::ExprFillDefaultMode::DefaultImpl => {
|
crate::ExprFillDefaultMode::DefaultImpl => {
|
||||||
let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
|
let scope = ctx.sema.scope(&root);
|
||||||
let default_trait = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
|
let default_constr = get_default_constructor(ctx, d, &scope, ty);
|
||||||
|
match default_constr {
|
||||||
match default_trait {
|
Some(default_constr) => Some(default_constr),
|
||||||
Some(default_trait) if ty.impls_trait(ctx.sema.db, default_trait, &[]) => {
|
|
||||||
Some(make::ext::expr_default())
|
|
||||||
}
|
|
||||||
_ => Some(make::ext::expr_todo()),
|
_ => Some(make::ext::expr_todo()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +122,68 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
|
||||||
|
let ty_str = match ty.as_adt() {
|
||||||
|
Some(adt) => adt.name(db).to_string(),
|
||||||
|
None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
make::ty(&ty_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_constructor(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::MissingFields,
|
||||||
|
scope: &SemanticsScope,
|
||||||
|
ty: &Type,
|
||||||
|
) -> Option<ast::Expr> {
|
||||||
|
if let Some(builtin_ty) = ty.as_builtin() {
|
||||||
|
if builtin_ty.is_int() || builtin_ty.is_uint() {
|
||||||
|
return Some(make::ext::zero_number());
|
||||||
|
}
|
||||||
|
if builtin_ty.is_float() {
|
||||||
|
return Some(make::ext::zero_float());
|
||||||
|
}
|
||||||
|
if builtin_ty.is_char() {
|
||||||
|
return Some(make::ext::empty_char());
|
||||||
|
}
|
||||||
|
if builtin_ty.is_str() {
|
||||||
|
return Some(make::ext::empty_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
|
||||||
|
let module = krate.root_module(ctx.sema.db);
|
||||||
|
let default_trait = FamousDefs(&ctx.sema, Some(krate)).core_default_Default()?;
|
||||||
|
let traits_in_scope = scope.visible_traits();
|
||||||
|
|
||||||
|
// Look for a ::new() method
|
||||||
|
// FIXME: doesn't work for now
|
||||||
|
let has_new_method = ty
|
||||||
|
.iterate_method_candidates(
|
||||||
|
ctx.sema.db,
|
||||||
|
krate,
|
||||||
|
&traits_in_scope,
|
||||||
|
Some(&known::new),
|
||||||
|
|_, func| {
|
||||||
|
if func.assoc_fn_params(ctx.sema.db).is_empty()
|
||||||
|
&& func.self_param(ctx.sema.db).is_none()
|
||||||
|
{
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if has_new_method {
|
||||||
|
Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module)))
|
||||||
|
} else if !ty.is_array() && ty.impls_trait(ctx.sema.db, default_trait, &[]) {
|
||||||
|
Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_diagnostics, check_fix};
|
use crate::tests::{check_diagnostics, check_fix};
|
||||||
|
|
|
@ -1273,7 +1273,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"enum": ["todo", "defaultImpl"],
|
"enum": ["todo", "defaultImpl"],
|
||||||
"enumDescriptions": [
|
"enumDescriptions": [
|
||||||
"Fill missing elements with 'todo' macro",
|
"Fill missing elements with 'todo' macro",
|
||||||
"Fill missing elements with Default::default()"
|
"Fill missing elements with T::default()"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"ImportGranularityDef" => set! {
|
"ImportGranularityDef" => set! {
|
||||||
|
|
|
@ -59,8 +59,27 @@ pub mod ext {
|
||||||
pub fn expr_todo() -> ast::Expr {
|
pub fn expr_todo() -> ast::Expr {
|
||||||
expr_from_text("todo!()")
|
expr_from_text("todo!()")
|
||||||
}
|
}
|
||||||
pub fn expr_default() -> ast::Expr {
|
pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
|
||||||
expr_from_text("Default::default()")
|
expr_from_text(&format!("{}::default()", ty))
|
||||||
|
}
|
||||||
|
pub fn expr_ty_new(ty: &ast::Type) -> ast::Expr {
|
||||||
|
expr_from_text(&format!("{}::new()", ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero_number() -> ast::Expr {
|
||||||
|
expr_from_text("0")
|
||||||
|
}
|
||||||
|
pub fn zero_float() -> ast::Expr {
|
||||||
|
expr_from_text("0.0")
|
||||||
|
}
|
||||||
|
pub fn empty_str() -> ast::Expr {
|
||||||
|
expr_from_text(r#""""#)
|
||||||
|
}
|
||||||
|
pub fn empty_char() -> ast::Expr {
|
||||||
|
expr_from_text("''")
|
||||||
|
}
|
||||||
|
pub fn default_bool() -> ast::Expr {
|
||||||
|
expr_from_text("false")
|
||||||
}
|
}
|
||||||
pub fn empty_block_expr() -> ast::BlockExpr {
|
pub fn empty_block_expr() -> ast::BlockExpr {
|
||||||
block_expr(None, None)
|
block_expr(None, None)
|
||||||
|
|
|
@ -388,7 +388,7 @@
|
||||||
],
|
],
|
||||||
"enumDescriptions": [
|
"enumDescriptions": [
|
||||||
"Fill missing elements with 'todo' macro",
|
"Fill missing elements with 'todo' macro",
|
||||||
"Fill missing elements with Default::default()"
|
"Fill missing elements with T::default()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"rust-analyzer.assist.importGranularity": {
|
"rust-analyzer.assist.importGranularity": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue