about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsoruh <mail@soruh.de>2022-06-15 02:41:28 +0200
committersoruh <mail@soruh.de>2022-06-15 02:41:28 +0200
commit8e3bbaa57b12904b88ff17ffca3bc3e44ebf772c (patch)
treedf0607122d27a41666882609eabfcf96789e02ff
parent65874dfff205335840cc9265ba06d74f234b719d (diff)
downloadrust-8e3bbaa57b12904b88ff17ffca3bc3e44ebf772c.tar.gz
rust-8e3bbaa57b12904b88ff17ffca3bc3e44ebf772c.zip
instanciate_empty_structs
-rw-r--r--crates/ide-assists/src/handlers/generate_new.rs100
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs83
2 files changed, 179 insertions, 4 deletions
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 6a1f710f6d5..fc17c5626e1 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,3 +1,4 @@
+use ide_db::imports::import_assets::item_for_path_search;
 use itertools::Itertools;
 use stdx::format_to;
 use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};
@@ -7,6 +8,58 @@ use crate::{
     AssistContext, AssistId, AssistKind, Assists,
 };
 
+// TODO: how to depupicate with `ide-diagnostics/mssing_fields`
+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(),
+                        )),
+                    );
+
+                    use hir::StructKind::*;
+                    let is_record = match variant.kind(db) {
+                        Record => true,
+                        Tuple => false,
+                        Unit => false,
+                    };
+
+                    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)) => {
+            let fields = x.fields(db);
+
+            if fields.is_empty() {
+                return Some(syntax::ast::make::expr_path(path));
+            }
+        }
+        _ => {}
+    }
+
+    None
+}
+
 // Assist: generate_new
 //
 // Adds a new inherent impl for a type.
@@ -48,11 +101,54 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
         let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
 
+        let current_module = ctx.sema.scope(strukt.syntax()).unwrap().module();
+
+        let trivial_constructors = field_list
+            .fields()
+            .map(|f| {
+                let ty = ctx.sema.resolve_type(&f.ty()?)?;
+
+                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)?)?;
+
+                let expr = use_trivial_constructor(
+                    &ctx.sema.db,
+                    ide_db::helpers::mod_path_to_ast(&type_path),
+                    &ty,
+                )?;
+
+                Some(format!("{}: {}", f.name()?.syntax(), expr))
+            })
+            .collect::<Vec<_>>();
+
+        dbg!(&trivial_constructors);
+
         let params = field_list
             .fields()
-            .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
+            .enumerate()
+            .filter_map(|(i, f)| {
+                if trivial_constructors[i].is_none() {
+                    Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))
+                } else {
+                    None
+                }
+            })
+            .format(", ");
+
+        let fields = field_list
+            .fields()
+            .enumerate()
+            .filter_map(|(i, f)| {
+                let contructor = trivial_constructors[i].clone();
+                if contructor.is_some() {
+                    contructor
+                } else {
+                    Some(f.name()?.to_string())
+                }
+            })
             .format(", ");
-        let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
 
         format_to!(buf, "    {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
 
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 891547aaef5..ff7d8de8fdc 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -3,7 +3,10 @@ use hir::{
     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,
@@ -14,6 +17,58 @@ use text_edit::TextEdit;
 
 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(),
+                        )),
+                    );
+
+                    use hir::StructKind::*;
+                    let is_record = match variant.kind(db) {
+                        Record => true,
+                        Tuple => false,
+                        Unit => false,
+                    };
+
+                    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)) => {
+            let fields = x.fields(db);
+
+            if fields.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 +110,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 +170,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()),