From 0435463439471a81477ccdc0313f312abb14331e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 30 Dec 2021 15:49:31 +0100 Subject: [PATCH 1/7] feat(diagnostics): use Default::default() expression instead of todo! when missing fields Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/handlers/missing_fields.rs | 21 +++++++++++++++---- crates/syntax/src/ast/make.rs | 3 +++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs index 8d17a7e714a..715ac8f9740 100644 --- a/crates/ide_diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs @@ -1,6 +1,6 @@ use either::Either; -use hir::{db::AstDatabase, InFile}; -use ide_db::{assists::Assist, source_change::SourceChange}; +use hir::{db::AstDatabase, InFile, 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}; @@ -63,6 +63,19 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option { + Some(make::ext::expr_default()) + } + _ => Some(make::ext::expr_todo()), + } + }; + for (f, ty) in missing_fields.iter() { let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) { cov_mark::hit!(field_shorthand); @@ -70,10 +83,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option ast::Expr { expr_from_text("todo!()") } + pub fn expr_default() -> ast::Expr { + expr_from_text("Default::default()") + } pub fn empty_block_expr() -> ast::BlockExpr { block_expr(None, None) } From df6fa50f92af1232df5c044b8fe223817588ec8b Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 31 Dec 2021 16:11:17 +0100 Subject: [PATCH 2/7] feat(diagnostics): add new config to fill default expression Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ide/src/lib.rs | 2 +- .../src/handlers/missing_fields.rs | 21 ++++++++------- crates/ide_diagnostics/src/lib.rs | 12 +++++++++ crates/rust-analyzer/src/config.rs | 27 +++++++++++++++++-- docs/user/generated_config.adoc | 5 ++++ editors/code/package.json | 15 ++++++++++- 6 files changed, 69 insertions(+), 13 deletions(-) diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 16a2b4a1130..b8482d0222b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -118,7 +118,7 @@ pub use ide_db::{ symbol_index::Query, RootDatabase, SymbolKind, }; -pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, Severity}; +pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, ExprFillDefaultMode, Severity}; pub use ide_ssr::SsrError; pub use syntax::{TextRange, TextSize}; pub use text_edit::{Indel, TextEdit}; diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs index 715ac8f9740..b5860fb8587 100644 --- a/crates/ide_diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs @@ -64,15 +64,18 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option 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()) + match default_trait { + 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()), } }; @@ -83,10 +86,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option Self { + Self::Todo + } +} + #[derive(Default, Debug, Clone)] pub struct DiagnosticsConfig { pub disable_experimental: bool, pub disabled: FxHashSet, + pub expr_fill_default: ExprFillDefaultMode, } struct DiagnosticsContext<'a> { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 867f2bd1f0e..9cef5584576 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,8 +11,8 @@ use std::{ffi::OsString, iter, path::PathBuf}; use flycheck::FlycheckConfig; use ide::{ - AssistConfig, CompletionConfig, DiagnosticsConfig, HighlightRelatedConfig, HoverConfig, - HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope, + AssistConfig, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightRelatedConfig, + HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig, Snippet, SnippetScope, }; use ide_db::helpers::{ insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -45,6 +45,8 @@ use crate::{ // parsing the old name. config_data! { struct ConfigData { + /// How assists will fill missing elements in an expression. + assist_exprFillDefault: ExprFillDefaultDef = "\"todo\"", /// How imports should be grouped into use statements. assist_importGranularity | assist_importMergeBehavior | @@ -694,6 +696,10 @@ impl Config { DiagnosticsConfig { disable_experimental: !self.data.diagnostics_enableExperimental, disabled: self.data.diagnostics_disabled.clone(), + expr_fill_default: match self.data.assist_exprFillDefault { + ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, + ExprFillDefaultDef::DefaultImpl => ExprFillDefaultMode::DefaultImpl, + }, } } pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { @@ -1059,6 +1065,15 @@ enum ManifestOrProjectJson { ProjectJson(ProjectJsonData), } +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub enum ExprFillDefaultDef { + #[serde(alias = "todo")] + Todo, + #[serde(alias = "defaultImpl")] + DefaultImpl, +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ImportGranularityDef { @@ -1253,6 +1268,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Merge imports from the same module into a single `use` statement." ], }, + "ExprFillDefaultDef" => set! { + "type": "string", + "enum": ["todo", "defaultImpl"], + "enumDescriptions": [ + "Fill missing elements with 'todo' macro", + "Fill missing elements with Default::default()" + ], + }, "ImportGranularityDef" => set! { "type": "string", "enum": ["preserve", "crate", "module", "item"], diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 494051b3060..28350374d13 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1,3 +1,8 @@ +[[rust-analyzer.assist.exprFillDefault]]rust-analyzer.assist.exprFillDefault (default: `"todo"`):: ++ +-- +How assists will fill missing elements in an expression. +-- [[rust-analyzer.assist.importGranularity]]rust-analyzer.assist.importGranularity (default: `"crate"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 550b28e1cdb..cbe9bf54427 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -378,6 +378,19 @@ "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" }, "$generated-start": {}, + "rust-analyzer.assist.exprFillDefault": { + "markdownDescription": "How assists will fill missing elements in an expression.", + "default": "todo", + "type": "string", + "enum": [ + "todo", + "defaultImpl" + ], + "enumDescriptions": [ + "Fill missing elements with 'todo' macro", + "Fill missing elements with Default::default()" + ] + }, "rust-analyzer.assist.importGranularity": { "markdownDescription": "How imports should be grouped into use statements.", "default": "crate", @@ -1494,4 +1507,4 @@ ] } } -} +} \ No newline at end of file From 336c899a07063c56c4a0b9f4764fbe5f9729ea51 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 4 Jan 2022 15:59:00 +0100 Subject: [PATCH 3/7] add better default behavior on fill struct fields diagnostic Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/hir/src/lib.rs | 24 ++++++ crates/hir_expand/src/name.rs | 1 + .../src/handlers/missing_fields.rs | 84 +++++++++++++++++-- crates/rust-analyzer/src/config.rs | 2 +- crates/syntax/src/ast/make.rs | 23 ++++- 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 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 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 { + 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": { From f4ce0d78bb03b89c1dc96a63c35ea751b9708fb9 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 6 Jan 2022 15:42:29 +0100 Subject: [PATCH 4/7] add better default behavior on fill struct fields diagnostic Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/hir/src/lib.rs | 5 +- crates/hir_ty/src/method_resolution.rs | 1 + .../src/handlers/missing_fields.rs | 119 ++++++++++++++---- crates/ide_diagnostics/src/tests.rs | 16 ++- 4 files changed, 103 insertions(+), 38 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8d5dbd28ac8..3b734ac6629 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2695,7 +2695,7 @@ impl Type { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. pub fn iterate_assoc_items( - self, + &self, db: &dyn HirDatabase, krate: Crate, mut callback: impl FnMut(AssocItem) -> Option, @@ -2709,7 +2709,7 @@ impl Type { } fn iterate_assoc_items_dyn( - self, + &self, db: &dyn HirDatabase, krate: Crate, callback: &mut dyn FnMut(AssocItemId) -> bool, @@ -2751,6 +2751,7 @@ impl Type { ) -> Option { let _p = profile::span("iterate_method_candidates"); let mut slot = None; + self.iterate_method_candidates_dyn( db, krate, diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index cb6b6ec39fb..1a451ae79f5 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -542,6 +542,7 @@ pub fn iterate_method_candidates_dyn( let deref_chain = autoderef_method_receiver(db, krate, ty); let mut deref_chains = stdx::slice_tails(&deref_chain); + deref_chains.try_for_each(|deref_chain| { iterate_method_candidates_with_autoref( deref_chain, diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs index cded84c54eb..8aca6ab3752 100644 --- a/crates/ide_diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs @@ -1,7 +1,7 @@ use either::Either; use hir::{ db::{AstDatabase, HirDatabase}, - known, HirDisplay, InFile, SemanticsScope, Type, + known, AssocItem, HirDisplay, InFile, Type, }; use ide_db::{assists::Assist, helpers::FamousDefs, source_change::SourceChange}; use rustc_hash::FxHashMap; @@ -74,8 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option Some(make::ext::expr_todo()), crate::ExprFillDefaultMode::DefaultImpl => { - let scope = ctx.sema.scope(&root); - let default_constr = get_default_constructor(ctx, d, &scope, ty); + let default_constr = get_default_constructor(ctx, d, ty); match default_constr { Some(default_constr) => Some(default_constr), _ => Some(make::ext::expr_todo()), @@ -134,7 +133,6 @@ fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Ty fn get_default_constructor( ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields, - scope: &SemanticsScope, ty: &Type, ) -> Option { if let Some(builtin_ty) = ty.as_builtin() { @@ -151,33 +149,35 @@ fn get_default_constructor( 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() + // Look for a ::new() associated function + let has_new_func = ty + .iterate_assoc_items(ctx.sema.db, krate, |assoc_item| { + if let AssocItem::Function(func) = assoc_item { + if func.name(ctx.sema.db) == known::new + && func.assoc_fn_params(ctx.sema.db).is_empty() && func.self_param(ctx.sema.db).is_none() { return Some(()); } - None - }, - ) + } + + None + }) .is_some(); - if has_new_method { + if has_new_func { 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, &[]) { + } else if !ty.is_array() + && ty.impls_trait( + ctx.sema.db, + FamousDefs(&ctx.sema, Some(krate)).core_default_Default()?, + &[], + ) + { Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module))) } else { None @@ -264,7 +264,7 @@ fn here() {} macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } fn main() { - let _x = id![Foo {a:42, b: todo!() }]; + let _x = id![Foo {a:42, b: 0 }]; } pub struct Foo { pub a: i32, pub b: i32 } @@ -286,7 +286,7 @@ fn test_fn() { struct TestStruct { one: i32, two: i64 } fn test_fn() { - let s = TestStruct { one: todo!(), two: todo!() }; + let s = TestStruct { one: 0, two: 0 }; } "#, ); @@ -306,7 +306,7 @@ impl TestStruct { struct TestStruct { one: i32 } impl TestStruct { - fn test_fn() { let s = Self { one: todo!() }; } + fn test_fn() { let s = Self { one: 0 }; } } "#, ); @@ -354,7 +354,72 @@ fn test_fn() { struct TestStruct { one: i32, two: i64 } fn test_fn() { - let s = TestStruct{ two: 2, one: todo!() }; + let s = TestStruct{ two: 2, one: 0 }; +} +", + ); + } + + #[test] + fn test_fill_struct_fields_new() { + check_fix( + r#" +struct TestWithNew(usize); +impl TestWithNew { + pub fn new() -> Self { + Self(0) + } +} +struct TestStruct { one: i32, two: TestWithNew } + +fn test_fn() { + let s = TestStruct{ $0 }; +} +"#, + r" +struct TestWithNew(usize); +impl TestWithNew { + pub fn new() -> Self { + Self(0) + } +} +struct TestStruct { one: i32, two: TestWithNew } + +fn test_fn() { + let s = TestStruct{ one: 0, two: TestWithNew::new() }; +} +", + ); + } + + #[test] + fn test_fill_struct_fields_default() { + check_fix( + r#" +//- minicore: default +struct TestWithDefault(usize); +impl Default for TestWithDefault { + pub fn default() -> Self { + Self(0) + } +} +struct TestStruct { one: i32, two: TestWithDefault } + +fn test_fn() { + let s = TestStruct{ $0 }; +} +"#, + r" +struct TestWithDefault(usize); +impl Default for TestWithDefault { + pub fn default() -> Self { + Self(0) + } +} +struct TestStruct { one: i32, two: TestWithDefault } + +fn test_fn() { + let s = TestStruct{ one: 0, two: TestWithDefault::default() }; } ", ); @@ -374,7 +439,7 @@ fn test_fn() { struct TestStruct { r#type: u8 } fn test_fn() { - TestStruct { r#type: todo!() }; + TestStruct { r#type: 0 }; } ", ); @@ -485,7 +550,7 @@ fn f() { let b = 1usize; S { a, - b: todo!(), + b: 0, }; } "#, diff --git a/crates/ide_diagnostics/src/tests.rs b/crates/ide_diagnostics/src/tests.rs index a2b92c4ff91..479a121c681 100644 --- a/crates/ide_diagnostics/src/tests.rs +++ b/crates/ide_diagnostics/src/tests.rs @@ -9,7 +9,7 @@ use ide_db::{ use stdx::trim_indent; use test_utils::{assert_eq_text, extract_annotations}; -use crate::{DiagnosticsConfig, Severity}; +use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity}; /// Takes a multi-file input fixture with annotated cursor positions, /// and checks that: @@ -36,14 +36,12 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let diagnostic = super::diagnostics( - &db, - &DiagnosticsConfig::default(), - &AssistResolveStrategy::All, - file_position.file_id, - ) - .pop() - .expect("no diagnostics"); + let mut conf = DiagnosticsConfig::default(); + conf.expr_fill_default = ExprFillDefaultMode::DefaultImpl; + let diagnostic = + super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) + .pop() + .expect("no diagnostics"); let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth]; let actual = { let source_change = fix.source_change.as_ref().unwrap(); From 0a4239a8153a17dda816678dfd870e1e5d10a101 Mon Sep 17 00:00:00 2001 From: Coenen Benjamin Date: Fri, 7 Jan 2022 14:07:02 +0100 Subject: [PATCH 5/7] Update crates/rust-analyzer/src/config.rs Co-authored-by: Lukas Wirth --- crates/rust-analyzer/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2d4d24e98d6..86fcff01ba9 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 T::default()" + "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ], }, "ImportGranularityDef" => set! { From b60a29ca9428e95c995f12ab8eff6160957188fe Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:13:34 +0100 Subject: [PATCH 6/7] feat(diagnostics): use default expression instead of todo! when missing fields Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ide_diagnostics/src/handlers/missing_fields.rs | 3 +-- crates/ide_diagnostics/src/lib.rs | 2 +- crates/ide_diagnostics/src/tests.rs | 2 +- crates/rust-analyzer/src/config.rs | 10 +++++----- crates/syntax/src/ast/make.rs | 2 +- editors/code/package.json | 6 +++--- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs index 8aca6ab3752..186bfc3d584 100644 --- a/crates/ide_diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs @@ -73,7 +73,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option Some(make::ext::expr_todo()), - crate::ExprFillDefaultMode::DefaultImpl => { + crate::ExprFillDefaultMode::Default => { let default_constr = get_default_constructor(ctx, d, ty); match default_constr { Some(default_constr) => Some(default_constr), @@ -159,7 +159,6 @@ fn get_default_constructor( if let AssocItem::Function(func) = assoc_item { if func.name(ctx.sema.db) == known::new && func.assoc_fn_params(ctx.sema.db).is_empty() - && func.self_param(ctx.sema.db).is_none() { return Some(()); } diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs index f4c613840e3..86d76751ad6 100644 --- a/crates/ide_diagnostics/src/lib.rs +++ b/crates/ide_diagnostics/src/lib.rs @@ -132,7 +132,7 @@ pub enum Severity { #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExprFillDefaultMode { Todo, - DefaultImpl, + Default, } impl Default for ExprFillDefaultMode { fn default() -> Self { diff --git a/crates/ide_diagnostics/src/tests.rs b/crates/ide_diagnostics/src/tests.rs index 479a121c681..7d06e9d36ff 100644 --- a/crates/ide_diagnostics/src/tests.rs +++ b/crates/ide_diagnostics/src/tests.rs @@ -37,7 +37,7 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::default(); - conf.expr_fill_default = ExprFillDefaultMode::DefaultImpl; + conf.expr_fill_default = ExprFillDefaultMode::Default; let diagnostic = super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) .pop() diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 86fcff01ba9..6be889df67a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -698,7 +698,7 @@ impl Config { disabled: self.data.diagnostics_disabled.clone(), expr_fill_default: match self.data.assist_exprFillDefault { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, - ExprFillDefaultDef::DefaultImpl => ExprFillDefaultMode::DefaultImpl, + ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, }, } } @@ -1070,8 +1070,8 @@ enum ManifestOrProjectJson { pub enum ExprFillDefaultDef { #[serde(alias = "todo")] Todo, - #[serde(alias = "defaultImpl")] - DefaultImpl, + #[serde(alias = "default")] + Default, } #[derive(Deserialize, Debug, Clone)] @@ -1270,9 +1270,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "ExprFillDefaultDef" => set! { "type": "string", - "enum": ["todo", "defaultImpl"], + "enum": ["todo", "default"], "enumDescriptions": [ - "Fill missing elements with 'todo' macro", + "Fill missing expressions with the 'todo' macro", "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ], }, diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 12d5b7decc6..35750209754 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -76,7 +76,7 @@ pub mod ext { expr_from_text(r#""""#) } pub fn empty_char() -> ast::Expr { - expr_from_text("''") + expr_from_text("'\x00'") } pub fn default_bool() -> ast::Expr { expr_from_text("false") diff --git a/editors/code/package.json b/editors/code/package.json index 6bc62fc9d7f..c81fd0b3d2d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -384,11 +384,11 @@ "type": "string", "enum": [ "todo", - "defaultImpl" + "default" ], "enumDescriptions": [ - "Fill missing elements with 'todo' macro", - "Fill missing elements with T::default()" + "Fill missing expressions with the 'todo' macro", + "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ] }, "rust-analyzer.assist.importGranularity": { From 8e0a05eb70d2a3506e1441fb491c0f8b6ae4ac59 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 7 Jan 2022 15:01:37 +0100 Subject: [PATCH 7/7] feat(diagnostics): use default expression instead of todo! when missing fields Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/rust-analyzer/src/config.rs | 4 ++-- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6be889df67a..449402da459 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -45,7 +45,7 @@ use crate::{ // parsing the old name. config_data! { struct ConfigData { - /// How assists will fill missing elements in an expression. + /// Placeholder for missing expressions in assists. assist_exprFillDefault: ExprFillDefaultDef = "\"todo\"", /// How imports should be grouped into use statements. assist_importGranularity | @@ -1272,7 +1272,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "type": "string", "enum": ["todo", "default"], "enumDescriptions": [ - "Fill missing expressions with the 'todo' macro", + "Fill missing expressions with the `todo` macro", "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ], }, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 28350374d13..179e81cd363 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1,7 +1,7 @@ [[rust-analyzer.assist.exprFillDefault]]rust-analyzer.assist.exprFillDefault (default: `"todo"`):: + -- -How assists will fill missing elements in an expression. +Placeholder for missing expressions in assists. -- [[rust-analyzer.assist.importGranularity]]rust-analyzer.assist.importGranularity (default: `"crate"`):: + diff --git a/editors/code/package.json b/editors/code/package.json index c81fd0b3d2d..aaf5d49b715 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -379,7 +379,7 @@ }, "$generated-start": {}, "rust-analyzer.assist.exprFillDefault": { - "markdownDescription": "How assists will fill missing elements in an expression.", + "markdownDescription": "Placeholder for missing expressions in assists.", "default": "todo", "type": "string", "enum": [ @@ -387,7 +387,7 @@ "default" ], "enumDescriptions": [ - "Fill missing expressions with the 'todo' macro", + "Fill missing expressions with the `todo` macro", "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ] }, @@ -1507,4 +1507,4 @@ ] } } -} \ No newline at end of file +}