mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
instanciate_empty_structs
This commit is contained in:
parent
65874dfff2
commit
8e3bbaa57b
2 changed files with 179 additions and 4 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use ide_db::imports::import_assets::item_for_path_search;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};
|
use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};
|
||||||
|
@ -7,6 +8,58 @@ use crate::{
|
||||||
AssistContext, AssistId, AssistKind, Assists,
|
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
|
// Assist: generate_new
|
||||||
//
|
//
|
||||||
// Adds a new inherent impl for a type.
|
// 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 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
|
let params = field_list
|
||||||
.fields()
|
.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(", ");
|
.format(", ");
|
||||||
let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
|
|
||||||
|
|
||||||
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
|
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@ use hir::{
|
||||||
db::{AstDatabase, HirDatabase},
|
db::{AstDatabase, HirDatabase},
|
||||||
known, AssocItem, HirDisplay, InFile, Type,
|
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 stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo,
|
algo,
|
||||||
|
@ -14,6 +17,58 @@ use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{fix, Diagnostic, DiagnosticsContext};
|
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
|
// Diagnostic: missing-fields
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
|
// 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 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 build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| {
|
||||||
let edit = {
|
let edit = {
|
||||||
let mut builder = TextEdit::builder();
|
let mut builder = TextEdit::builder();
|
||||||
|
@ -110,7 +170,26 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
|
||||||
Some(generate_fill_expr(ty))
|
Some(generate_fill_expr(ty))
|
||||||
}
|
}
|
||||||
} else {
|
} 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(
|
let field = make::record_expr_field(
|
||||||
make::name_ref(&f.name(ctx.sema.db).to_smol_str()),
|
make::name_ref(&f.name(ctx.sema.db).to_smol_str()),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue