diff options
| author | Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> | 2022-01-04 15:59:00 +0100 |
|---|---|---|
| committer | Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> | 2022-01-04 15:59:00 +0100 |
| commit | 336c899a07063c56c4a0b9f4764fbe5f9729ea51 (patch) | |
| tree | e009ff7c5f790d8071ec0b0cad3a944ab2fe1ff5 | |
| parent | df6fa50f92af1232df5c044b8fe223817588ec8b (diff) | |
| download | rust-336c899a07063c56c4a0b9f4764fbe5f9729ea51.tar.gz rust-336c899a07063c56c4a0b9f4764fbe5f9729ea51.zip | |
add better default behavior on fill struct fields diagnostic
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
| -rw-r--r-- | crates/hir/src/lib.rs | 24 | ||||
| -rw-r--r-- | crates/hir_expand/src/name.rs | 1 | ||||
| -rw-r--r-- | crates/ide_diagnostics/src/handlers/missing_fields.rs | 84 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/config.rs | 2 | ||||
| -rw-r--r-- | crates/syntax/src/ast/make.rs | 23 | ||||
| -rw-r--r-- | editors/code/package.json | 2 |
6 files changed, 123 insertions, 13 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6306ae534da..8d5dbd28ac8 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1685,6 +1685,26 @@ impl BuiltinType { pub fn name(self) -> 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)] @@ -2573,6 +2593,10 @@ impl Type { 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 { let adt_id = match *self.ty.kind(Interner) { TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id, diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 23f81d3c7f5..3636ab62131 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs @@ -226,6 +226,7 @@ pub mod known { iter_mut, len, is_empty, + new, // Builtin macros asm, assert, diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs index b5860fb8587..cded84c54eb 100644 --- a/crates/ide_diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs @@ -1,9 +1,16 @@ 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 rustc_hash::FxHashMap; use stdx::format_to; -use syntax::{algo, ast::make, AstNode, SyntaxNodePtr}; +use syntax::{ + algo, + ast::{self, make}, + AstNode, SyntaxNodePtr, +}; use text_edit::TextEdit; 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 { crate::ExprFillDefaultMode::Todo => Some(make::ext::expr_todo()), crate::ExprFillDefaultMode::DefaultImpl => { - let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate(); - let default_trait = FamousDefs(&ctx.sema, Some(krate)).core_default_Default(); - - match default_trait { - Some(default_trait) if ty.impls_trait(ctx.sema.db, default_trait, &[]) => { - Some(make::ext::expr_default()) - } + let scope = ctx.sema.scope(&root); + let default_constr = get_default_constructor(ctx, d, &scope, ty); + match default_constr { + Some(default_constr) => Some(default_constr), _ => 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)] mod tests { use crate::tests::{check_diagnostics, check_fix}; diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9cef5584576..2d4d24e98d6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -1273,7 +1273,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "enum": ["todo", "defaultImpl"], "enumDescriptions": [ "Fill missing elements with 'todo' macro", - "Fill missing elements with Default::default()" + "Fill missing elements with T::default()" ], }, "ImportGranularityDef" => set! { diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 1a46b84c966..12d5b7decc6 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -59,8 +59,27 @@ pub mod ext { pub fn expr_todo() -> ast::Expr { expr_from_text("todo!()") } - pub fn expr_default() -> ast::Expr { - expr_from_text("Default::default()") + pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr { + 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 { block_expr(None, None) diff --git a/editors/code/package.json b/editors/code/package.json index cbe9bf54427..6bc62fc9d7f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -388,7 +388,7 @@ ], "enumDescriptions": [ "Fill missing elements with 'todo' macro", - "Fill missing elements with Default::default()" + "Fill missing elements with T::default()" ] }, "rust-analyzer.assist.importGranularity": { |
