]> git.lizzy.rs Git - rust.git/commitdiff
add better default behavior on fill struct fields diagnostic
authorBenjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Thu, 6 Jan 2022 14:42:29 +0000 (15:42 +0100)
committerBenjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Thu, 6 Jan 2022 14:42:29 +0000 (15:42 +0100)
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
crates/hir/src/lib.rs
crates/hir_ty/src/method_resolution.rs
crates/ide_diagnostics/src/handlers/missing_fields.rs
crates/ide_diagnostics/src/tests.rs

index 8d5dbd28ac8e8a50167d3f91975758d441e91df3..3b734ac662902c8c628cda2c2b055fb81d125855 100644 (file)
@@ -2695,7 +2695,7 @@ pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item =
     // 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<T>(
-        self,
+        &self,
         db: &dyn HirDatabase,
         krate: Crate,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
@@ -2709,7 +2709,7 @@ pub fn iterate_assoc_items<T>(
     }
 
     fn iterate_assoc_items_dyn(
-        self,
+        &self,
         db: &dyn HirDatabase,
         krate: Crate,
         callback: &mut dyn FnMut(AssocItemId) -> bool,
@@ -2751,6 +2751,7 @@ pub fn iterate_method_candidates<T>(
     ) -> Option<T> {
         let _p = profile::span("iterate_method_candidates");
         let mut slot = None;
+
         self.iterate_method_candidates_dyn(
             db,
             krate,
index cb6b6ec39fb25188868a51ae11c4f9e53ea249a1..1a451ae79f513cf77e6f2679bfd3fdf0ada6e47d 100644 (file)
@@ -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,
index cded84c54eb1cb44c8365e7f23723cf043b2e7cb..8aca6ab37521e6f68d030c632039a39544e43562 100644 (file)
@@ -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<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 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<ast::Expr> {
     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,
     };
 }
 "#,
index a2b92c4ff91ebbb0fd32d9301465d6ba9126f83f..479a121c681e61e518f3703d2df76d7649f512a8 100644 (file)
@@ -9,7 +9,7 @@
 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();