]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide-diagnostics/src/handlers/missing_fields.rs
cleanup
[rust.git] / crates / ide-diagnostics / src / handlers / missing_fields.rs
index 3160004380ff01cc6f0a9f741eac7cb8ad300897..5739a8785382ed4c126986c654131b07c031dbce 100644 (file)
@@ -3,7 +3,10 @@
     db::{AstDatabase, HirDatabase},
     known, AssocItem, HirDisplay, InFile, Type,
 };
-use ide_db::{assists::Assist, famous_defs::FamousDefs, source_change::SourceChange, FxHashMap};
+use ide_db::{
+    assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
+    source_change::SourceChange, FxHashMap,
+};
 use stdx::format_to;
 use syntax::{
     algo,
 
 use crate::{fix, Diagnostic, DiagnosticsContext};
 
+// TODO: how to depupicate with `ide-assists/generate_new`
+pub fn use_trivial_constructor(
+    db: &ide_db::RootDatabase,
+    path: ast::Path,
+    ty: &hir::Type,
+) -> Option<ast::Expr> {
+    match ty.as_adt() {
+        Some(hir::Adt::Enum(x)) => {
+            let variants = x.variants(db);
+
+            if variants.len() == 1 {
+                let variant = variants[0];
+
+                if variant.fields(db).is_empty() {
+                    let path = ast::make::path_qualified(
+                        path,
+                        syntax::ast::make::path_segment(ast::make::name_ref(
+                            &variant.name(db).to_smol_str(),
+                        )),
+                    );
+
+                    let is_record = variant.kind(db) == hir::StructKind::Record;
+
+                    return Some(if is_record {
+                        ast::Expr::RecordExpr(syntax::ast::make::record_expr(
+                            path,
+                            ast::make::record_expr_field_list(std::iter::empty()),
+                        ))
+                    } else {
+                        syntax::ast::make::expr_path(path)
+                    });
+                }
+            }
+        }
+        Some(hir::Adt::Struct(x)) => {
+            if x.fields(db).is_empty() {
+                return Some(syntax::ast::make::expr_path(path));
+            }
+        }
+        _ => {}
+    }
+
+    None
+}
+
 // Diagnostic: missing-fields
 //
 // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
@@ -55,6 +103,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
 
     let root = ctx.sema.db.parse_or_expand(d.file)?;
 
+    let current_module = match &d.field_list_parent {
+        Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).unwrap().module(),
+        Either::Right(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).unwrap().module(),
+    };
+
     let build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| {
         let edit = {
             let mut builder = TextEdit::builder();
@@ -110,7 +163,26 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
                         Some(generate_fill_expr(ty))
                     }
                 } else {
-                    Some(generate_fill_expr(ty))
+                    let expr = (|| -> Option<ast::Expr> {
+                        let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
+
+                        let type_path = current_module.find_use_path(
+                            ctx.sema.db,
+                            item_for_path_search(ctx.sema.db, item_in_ns)?,
+                        )?;
+
+                        use_trivial_constructor(
+                            &ctx.sema.db,
+                            ide_db::helpers::mod_path_to_ast(&type_path),
+                            &ty,
+                        )
+                    })();
+
+                    if expr.is_some() {
+                        expr
+                    } else {
+                        Some(generate_fill_expr(ty))
+                    }
                 };
                 let field = make::record_expr_field(
                     make::name_ref(&f.name(ctx.sema.db).to_smol_str()),
@@ -172,6 +244,9 @@ fn get_default_constructor(
         if builtin_ty.is_str() {
             return Some(make::ext::empty_str());
         }
+        if builtin_ty.is_bool() {
+            return Some(make::ext::default_bool());
+        }
     }
 
     let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
@@ -192,10 +267,13 @@ fn get_default_constructor(
         })
         .is_some();
 
+    let famous_defs = FamousDefs(&ctx.sema, krate);
     if has_new_func {
         Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module)))
+    } else if ty.as_adt() == famous_defs.core_option_Option()?.ty(ctx.sema.db).as_adt() {
+        Some(make::ext::option_none())
     } else if !ty.is_array()
-        && ty.impls_trait(ctx.sema.db, FamousDefs(&ctx.sema, krate).core_default_Default()?, &[])
+        && ty.impls_trait(ctx.sema.db, famous_defs.core_default_Default()?, &[])
     {
         Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module)))
     } else {
@@ -295,17 +373,18 @@ pub struct Foo { pub a: i32, pub b: i32 }
     fn test_fill_struct_fields_empty() {
         check_fix(
             r#"
-struct TestStruct { one: i32, two: i64 }
+//- minicore: option
+struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
 
 fn test_fn() {
     let s = TestStruct {$0};
 }
 "#,
             r#"
-struct TestStruct { one: i32, two: i64 }
+struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
 
 fn test_fn() {
-    let s = TestStruct { one: 0, two: 0 };
+    let s = TestStruct { one: 0, two: 0, three: None, four: false };
 }
 "#,
         );
@@ -415,7 +494,7 @@ fn test_fn() {
     fn test_fill_struct_fields_default() {
         check_fix(
             r#"
-//- minicore: default
+//- minicore: default, option
 struct TestWithDefault(usize);
 impl Default for TestWithDefault {
     pub fn default() -> Self {