Unnamed repository; edit this file 'description' to name the repository.
add better default behavior on fill struct fields diagnostic
Signed-off-by: Benjamin Coenen <[email protected]>
Benjamin Coenen 2022-01-04
parent df6fa50 · commit 336c899
-rw-r--r--crates/hir/src/lib.rs24
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/ide_diagnostics/src/handlers/missing_fields.rs84
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/syntax/src/ast/make.rs23
-rw-r--r--editors/code/package.json2
6 files changed, 123 insertions, 13 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 6306ae534d..8d5dbd28ac 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1685,6 +1685,26 @@ impl BuiltinType {
pub fn name(self) -> Name {
self.inner.as_name()
}
+
+ pub fn is_int(&self) -> bool {
+ matches!(self.inner, hir_def::builtin_type::BuiltinType::Int(_))
+ }
+
+ pub fn is_uint(&self) -> bool {
+ matches!(self.inner, hir_def::builtin_type::BuiltinType::Uint(_))
+ }
+
+ pub fn is_float(&self) -> bool {
+ matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
+ }
+
+ pub fn is_char(&self) -> bool {
+ matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
+ }
+
+ pub fn is_str(&self) -> bool {
+ matches!(self.inner, hir_def::builtin_type::BuiltinType::Str)
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -2573,6 +2593,10 @@ impl Type {
matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
}
+ pub fn is_array(&self) -> bool {
+ matches!(&self.ty.kind(Interner), TyKind::Array(..))
+ }
+
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
let adt_id = match *self.ty.kind(Interner) {
TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 23f81d3c7f..3636ab6213 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -226,6 +226,7 @@ pub mod known {
iter_mut,
len,
is_empty,
+ new,
// Builtin macros
asm,
assert,
diff --git a/crates/ide_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs
index b5860fb858..cded84c54e 100644
--- a/crates/ide_diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs
@@ -1,9 +1,16 @@
use either::Either;
-use hir::{db::AstDatabase, InFile, Type};
+use hir::{
+ db::{AstDatabase, HirDatabase},
+ known, HirDisplay, InFile, SemanticsScope, Type,
+};
use ide_db::{assists::Assist, helpers::FamousDefs, source_change::SourceChange};
use rustc_hash::FxHashMap;
use stdx::format_to;
-use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
+use syntax::{
+ algo,
+ ast::{self, make},
+ AstNode, SyntaxNodePtr,
+};
use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticsContext};
@@ -67,13 +74,10 @@ 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 krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
- let default_trait = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
-
- match default_trait {
- Some(default_trait) if ty.impls_trait(ctx.sema.db, default_trait, &[]) => {
- Some(make::ext::expr_default())
- }
+ let scope = ctx.sema.scope(&root);
+ let default_constr = get_default_constructor(ctx, d, &scope, ty);
+ match default_constr {
+ Some(default_constr) => Some(default_constr),
_ => Some(make::ext::expr_todo()),
}
}
@@ -118,6 +122,68 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
)])
}
+fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
+ let ty_str = match ty.as_adt() {
+ Some(adt) => adt.name(db).to_string(),
+ None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()),
+ };
+
+ make::ty(&ty_str)
+}
+
+fn get_default_constructor(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::MissingFields,
+ scope: &SemanticsScope,
+ ty: &Type,
+) -> Option<ast::Expr> {
+ if let Some(builtin_ty) = ty.as_builtin() {
+ if builtin_ty.is_int() || builtin_ty.is_uint() {
+ return Some(make::ext::zero_number());
+ }
+ if builtin_ty.is_float() {
+ return Some(make::ext::zero_float());
+ }
+ if builtin_ty.is_char() {
+ return Some(make::ext::empty_char());
+ }
+ if builtin_ty.is_str() {
+ 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()
+ && func.self_param(ctx.sema.db).is_none()
+ {
+ return Some(());
+ }
+ None
+ },
+ )
+ .is_some();
+
+ if has_new_method {
+ 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, &[]) {
+ Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module)))
+ } else {
+ None
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_diagnostics, check_fix};
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 9cef558457..2d4d24e98d 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -1273,7 +1273,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"enum": ["todo", "defaultImpl"],
"enumDescriptions": [
"Fill missing elements with 'todo' macro",
- "Fill missing elements with Default::default()"
+ "Fill missing elements with T::default()"
],
},
"ImportGranularityDef" => set! {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 1a46b84c96..12d5b7decc 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -59,8 +59,27 @@ pub mod ext {
pub fn expr_todo() -> ast::Expr {
expr_from_text("todo!()")
}
- pub fn expr_default() -> ast::Expr {
- expr_from_text("Default::default()")
+ pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
+ expr_from_text(&format!("{}::default()", ty))
+ }
+ pub fn expr_ty_new(ty: &ast::Type) -> ast::Expr {
+ expr_from_text(&format!("{}::new()", ty))
+ }
+
+ pub fn zero_number() -> ast::Expr {
+ expr_from_text("0")
+ }
+ pub fn zero_float() -> ast::Expr {
+ expr_from_text("0.0")
+ }
+ pub fn empty_str() -> ast::Expr {
+ expr_from_text(r#""""#)
+ }
+ pub fn empty_char() -> ast::Expr {
+ expr_from_text("''")
+ }
+ pub fn default_bool() -> ast::Expr {
+ expr_from_text("false")
}
pub fn empty_block_expr() -> ast::BlockExpr {
block_expr(None, None)
diff --git a/editors/code/package.json b/editors/code/package.json
index cbe9bf5442..6bc62fc9d7 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -388,7 +388,7 @@
],
"enumDescriptions": [
"Fill missing elements with 'todo' macro",
- "Fill missing elements with Default::default()"
+ "Fill missing elements with T::default()"
]
},
"rust-analyzer.assist.importGranularity": {