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 { + 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::>(); + + 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 { + 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 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 Option { + 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()),