Skip to content

Commit e8c8039

Browse files
bors[bot]bnjjj
andauthored
Merge #6544
6544: add suggestion ..Default::default() for remaining struct fields in a constructor r=bnjjj a=bnjjj I'm not sure I should import `assists` crate inside `completions`, maybe we should move out `FamousDefs` from `assists` ? Let me know :) close #6492 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Co-authored-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
2 parents d1ea9d1 + 9f15de7 commit e8c8039

File tree

4 files changed

+118
-4
lines changed

4 files changed

+118
-4
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/assists/src/utils.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ pub mod convert {
257257
}
258258
}
259259
260+
pub mod default {
261+
pub trait Default {
262+
fn default() -> Self;
263+
}
264+
}
265+
260266
pub mod iter {
261267
pub use self::traits::{collect::IntoIterator, iterator::Iterator};
262268
mod traits {
@@ -327,7 +333,7 @@ pub mod option {
327333
}
328334
329335
pub mod prelude {
330-
pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}};
336+
pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
331337
}
332338
#[prelude_import]
333339
pub use prelude::*;
@@ -345,6 +351,10 @@ pub use prelude::*;
345351
self.find_enum("core:option:Option")
346352
}
347353

354+
pub fn core_default_Default(&self) -> Option<Trait> {
355+
self.find_trait("core:default:Default")
356+
}
357+
348358
pub fn core_iter_Iterator(&self) -> Option<Trait> {
349359
self.find_trait("core:iter:traits:iterator:Iterator")
350360
}

crates/completion/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ itertools = "0.9.0"
1414
log = "0.4.8"
1515
rustc-hash = "1.1.0"
1616

17+
assists = { path = "../assists", version = "0.0.0" }
1718
stdx = { path = "../stdx", version = "0.0.0" }
1819
syntax = { path = "../syntax", version = "0.0.0" }
1920
text_edit = { path = "../text_edit", version = "0.0.0" }

crates/completion/src/completions/record.rs

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,51 @@
11
//! Complete fields in record literals and patterns.
2-
use crate::{CompletionContext, Completions};
2+
use assists::utils::FamousDefs;
3+
use syntax::ast::Expr;
4+
5+
use crate::{
6+
item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
7+
};
38

49
pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
510
let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
611
(None, None) => return None,
712
(Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
813
(Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
9-
(_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
14+
(_, Some(record_lit)) => {
15+
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
16+
let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
17+
let impl_default_trait = default_trait
18+
.and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[])))
19+
.unwrap_or(false);
20+
21+
let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
22+
if impl_default_trait && !missing_fields.is_empty() {
23+
acc.add(
24+
CompletionItem::new(
25+
CompletionKind::Snippet,
26+
ctx.source_range(),
27+
"..Default::default()",
28+
)
29+
.insert_text("..Default::default()")
30+
.kind(CompletionItemKind::Field)
31+
.build(),
32+
);
33+
}
34+
35+
missing_fields
36+
}
1037
};
1138

1239
for (field, ty) in missing_fields {
13-
acc.add_field(ctx, field, &ty)
40+
acc.add_field(ctx, field, &ty);
1441
}
1542

1643
Some(())
1744
}
1845

1946
#[cfg(test)]
2047
mod tests {
48+
use assists::utils::FamousDefs;
2149
use expect_test::{expect, Expect};
2250

2351
use crate::{test_utils::completion_list, CompletionKind};
@@ -27,6 +55,80 @@ mod tests {
2755
expect.assert_eq(&actual);
2856
}
2957

58+
fn check_snippet(ra_fixture: &str, expect: Expect) {
59+
let actual = completion_list(
60+
&format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
61+
CompletionKind::Snippet,
62+
);
63+
expect.assert_eq(&actual);
64+
}
65+
66+
#[test]
67+
fn test_record_literal_field_default() {
68+
let test_code = r#"
69+
struct S { foo: u32, bar: usize }
70+
71+
impl core::default::Default for S {
72+
fn default() -> Self {
73+
S {
74+
foo: 0,
75+
bar: 0,
76+
}
77+
}
78+
}
79+
80+
fn process(f: S) {
81+
let other = S {
82+
foo: 5,
83+
.<|>
84+
};
85+
}
86+
"#;
87+
check(
88+
test_code,
89+
expect![[r#"
90+
fd bar usize
91+
"#]],
92+
);
93+
94+
check_snippet(
95+
test_code,
96+
expect![[r#"
97+
fd ..Default::default()
98+
sn pd
99+
sn ppd
100+
"#]],
101+
);
102+
}
103+
104+
#[test]
105+
fn test_record_literal_field_without_default() {
106+
let test_code = r#"
107+
struct S { foo: u32, bar: usize }
108+
109+
fn process(f: S) {
110+
let other = S {
111+
foo: 5,
112+
.<|>
113+
};
114+
}
115+
"#;
116+
check(
117+
test_code,
118+
expect![[r#"
119+
fd bar usize
120+
"#]],
121+
);
122+
123+
check_snippet(
124+
test_code,
125+
expect![[r#"
126+
sn pd
127+
sn ppd
128+
"#]],
129+
);
130+
}
131+
30132
#[test]
31133
fn test_record_pattern_field() {
32134
check(

0 commit comments

Comments
 (0)