Skip to content

Commit f4ce0d7

Browse files
committed
add better default behavior on fill struct fields diagnostic
Signed-off-by: Benjamin Coenen <[email protected]>
1 parent 336c899 commit f4ce0d7

File tree

4 files changed

+103
-38
lines changed

4 files changed

+103
-38
lines changed

crates/hir/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2695,7 +2695,7 @@ impl Type {
26952695
// This would be nicer if it just returned an iterator, but that runs into
26962696
// lifetime problems, because we need to borrow temp `CrateImplDefs`.
26972697
pub fn iterate_assoc_items<T>(
2698-
self,
2698+
&self,
26992699
db: &dyn HirDatabase,
27002700
krate: Crate,
27012701
mut callback: impl FnMut(AssocItem) -> Option<T>,
@@ -2709,7 +2709,7 @@ impl Type {
27092709
}
27102710

27112711
fn iterate_assoc_items_dyn(
2712-
self,
2712+
&self,
27132713
db: &dyn HirDatabase,
27142714
krate: Crate,
27152715
callback: &mut dyn FnMut(AssocItemId) -> bool,
@@ -2751,6 +2751,7 @@ impl Type {
27512751
) -> Option<T> {
27522752
let _p = profile::span("iterate_method_candidates");
27532753
let mut slot = None;
2754+
27542755
self.iterate_method_candidates_dyn(
27552756
db,
27562757
krate,

crates/hir_ty/src/method_resolution.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ pub fn iterate_method_candidates_dyn(
542542

543543
let deref_chain = autoderef_method_receiver(db, krate, ty);
544544
let mut deref_chains = stdx::slice_tails(&deref_chain);
545+
545546
deref_chains.try_for_each(|deref_chain| {
546547
iterate_method_candidates_with_autoref(
547548
deref_chain,

crates/ide_diagnostics/src/handlers/missing_fields.rs

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use either::Either;
22
use hir::{
33
db::{AstDatabase, HirDatabase},
4-
known, HirDisplay, InFile, SemanticsScope, Type,
4+
known, AssocItem, HirDisplay, InFile, Type,
55
};
66
use ide_db::{assists::Assist, helpers::FamousDefs, source_change::SourceChange};
77
use rustc_hash::FxHashMap;
@@ -74,8 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
7474
let generate_fill_expr = |ty: &Type| match ctx.config.expr_fill_default {
7575
crate::ExprFillDefaultMode::Todo => Some(make::ext::expr_todo()),
7676
crate::ExprFillDefaultMode::DefaultImpl => {
77-
let scope = ctx.sema.scope(&root);
78-
let default_constr = get_default_constructor(ctx, d, &scope, ty);
77+
let default_constr = get_default_constructor(ctx, d, ty);
7978
match default_constr {
8079
Some(default_constr) => Some(default_constr),
8180
_ => Some(make::ext::expr_todo()),
@@ -134,7 +133,6 @@ fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Ty
134133
fn get_default_constructor(
135134
ctx: &DiagnosticsContext<'_>,
136135
d: &hir::MissingFields,
137-
scope: &SemanticsScope,
138136
ty: &Type,
139137
) -> Option<ast::Expr> {
140138
if let Some(builtin_ty) = ty.as_builtin() {
@@ -151,33 +149,35 @@ fn get_default_constructor(
151149
return Some(make::ext::empty_str());
152150
}
153151
}
152+
154153
let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
155154
let module = krate.root_module(ctx.sema.db);
156-
let default_trait = FamousDefs(&ctx.sema, Some(krate)).core_default_Default()?;
157-
let traits_in_scope = scope.visible_traits();
158155

159-
// Look for a ::new() method
160-
// FIXME: doesn't work for now
161-
let has_new_method = ty
162-
.iterate_method_candidates(
163-
ctx.sema.db,
164-
krate,
165-
&traits_in_scope,
166-
Some(&known::new),
167-
|_, func| {
168-
if func.assoc_fn_params(ctx.sema.db).is_empty()
156+
// Look for a ::new() associated function
157+
let has_new_func = ty
158+
.iterate_assoc_items(ctx.sema.db, krate, |assoc_item| {
159+
if let AssocItem::Function(func) = assoc_item {
160+
if func.name(ctx.sema.db) == known::new
161+
&& func.assoc_fn_params(ctx.sema.db).is_empty()
169162
&& func.self_param(ctx.sema.db).is_none()
170163
{
171164
return Some(());
172165
}
173-
None
174-
},
175-
)
166+
}
167+
168+
None
169+
})
176170
.is_some();
177171

178-
if has_new_method {
172+
if has_new_func {
179173
Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module)))
180-
} else if !ty.is_array() && ty.impls_trait(ctx.sema.db, default_trait, &[]) {
174+
} else if !ty.is_array()
175+
&& ty.impls_trait(
176+
ctx.sema.db,
177+
FamousDefs(&ctx.sema, Some(krate)).core_default_Default()?,
178+
&[],
179+
)
180+
{
181181
Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module)))
182182
} else {
183183
None
@@ -264,7 +264,7 @@ fn here() {}
264264
macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
265265
266266
fn main() {
267-
let _x = id![Foo {a:42, b: todo!() }];
267+
let _x = id![Foo {a:42, b: 0 }];
268268
}
269269
270270
pub struct Foo { pub a: i32, pub b: i32 }
@@ -286,7 +286,7 @@ fn test_fn() {
286286
struct TestStruct { one: i32, two: i64 }
287287
288288
fn test_fn() {
289-
let s = TestStruct { one: todo!(), two: todo!() };
289+
let s = TestStruct { one: 0, two: 0 };
290290
}
291291
"#,
292292
);
@@ -306,7 +306,7 @@ impl TestStruct {
306306
struct TestStruct { one: i32 }
307307
308308
impl TestStruct {
309-
fn test_fn() { let s = Self { one: todo!() }; }
309+
fn test_fn() { let s = Self { one: 0 }; }
310310
}
311311
"#,
312312
);
@@ -354,7 +354,72 @@ fn test_fn() {
354354
struct TestStruct { one: i32, two: i64 }
355355
356356
fn test_fn() {
357-
let s = TestStruct{ two: 2, one: todo!() };
357+
let s = TestStruct{ two: 2, one: 0 };
358+
}
359+
",
360+
);
361+
}
362+
363+
#[test]
364+
fn test_fill_struct_fields_new() {
365+
check_fix(
366+
r#"
367+
struct TestWithNew(usize);
368+
impl TestWithNew {
369+
pub fn new() -> Self {
370+
Self(0)
371+
}
372+
}
373+
struct TestStruct { one: i32, two: TestWithNew }
374+
375+
fn test_fn() {
376+
let s = TestStruct{ $0 };
377+
}
378+
"#,
379+
r"
380+
struct TestWithNew(usize);
381+
impl TestWithNew {
382+
pub fn new() -> Self {
383+
Self(0)
384+
}
385+
}
386+
struct TestStruct { one: i32, two: TestWithNew }
387+
388+
fn test_fn() {
389+
let s = TestStruct{ one: 0, two: TestWithNew::new() };
390+
}
391+
",
392+
);
393+
}
394+
395+
#[test]
396+
fn test_fill_struct_fields_default() {
397+
check_fix(
398+
r#"
399+
//- minicore: default
400+
struct TestWithDefault(usize);
401+
impl Default for TestWithDefault {
402+
pub fn default() -> Self {
403+
Self(0)
404+
}
405+
}
406+
struct TestStruct { one: i32, two: TestWithDefault }
407+
408+
fn test_fn() {
409+
let s = TestStruct{ $0 };
410+
}
411+
"#,
412+
r"
413+
struct TestWithDefault(usize);
414+
impl Default for TestWithDefault {
415+
pub fn default() -> Self {
416+
Self(0)
417+
}
418+
}
419+
struct TestStruct { one: i32, two: TestWithDefault }
420+
421+
fn test_fn() {
422+
let s = TestStruct{ one: 0, two: TestWithDefault::default() };
358423
}
359424
",
360425
);
@@ -374,7 +439,7 @@ fn test_fn() {
374439
struct TestStruct { r#type: u8 }
375440
376441
fn test_fn() {
377-
TestStruct { r#type: todo!() };
442+
TestStruct { r#type: 0 };
378443
}
379444
",
380445
);
@@ -485,7 +550,7 @@ fn f() {
485550
let b = 1usize;
486551
S {
487552
a,
488-
b: todo!(),
553+
b: 0,
489554
};
490555
}
491556
"#,

crates/ide_diagnostics/src/tests.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ide_db::{
99
use stdx::trim_indent;
1010
use test_utils::{assert_eq_text, extract_annotations};
1111

12-
use crate::{DiagnosticsConfig, Severity};
12+
use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
1313

1414
/// Takes a multi-file input fixture with annotated cursor positions,
1515
/// and checks that:
@@ -36,14 +36,12 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
3636
let after = trim_indent(ra_fixture_after);
3737

3838
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
39-
let diagnostic = super::diagnostics(
40-
&db,
41-
&DiagnosticsConfig::default(),
42-
&AssistResolveStrategy::All,
43-
file_position.file_id,
44-
)
45-
.pop()
46-
.expect("no diagnostics");
39+
let mut conf = DiagnosticsConfig::default();
40+
conf.expr_fill_default = ExprFillDefaultMode::DefaultImpl;
41+
let diagnostic =
42+
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id)
43+
.pop()
44+
.expect("no diagnostics");
4745
let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
4846
let actual = {
4947
let source_change = fix.source_change.as_ref().unwrap();

0 commit comments

Comments
 (0)