mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
add suggestion ..Default::default() for remaining struct fields in a constructor #6492
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
parent
9beec7c263
commit
e73d140b51
4 changed files with 118 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -253,6 +253,7 @@ dependencies = [
|
||||||
name = "completion"
|
name = "completion"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assists",
|
||||||
"base_db",
|
"base_db",
|
||||||
"expect-test",
|
"expect-test",
|
||||||
"hir",
|
"hir",
|
||||||
|
|
|
@ -257,6 +257,12 @@ pub mod convert {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod default {
|
||||||
|
pub trait Default: Sized {
|
||||||
|
fn default() -> Self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod iter {
|
pub mod iter {
|
||||||
pub use self::traits::{collect::IntoIterator, iterator::Iterator};
|
pub use self::traits::{collect::IntoIterator, iterator::Iterator};
|
||||||
mod traits {
|
mod traits {
|
||||||
|
@ -327,7 +333,7 @@ pub mod option {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}};
|
pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
|
||||||
}
|
}
|
||||||
#[prelude_import]
|
#[prelude_import]
|
||||||
pub use prelude::*;
|
pub use prelude::*;
|
||||||
|
@ -345,6 +351,10 @@ pub use prelude::*;
|
||||||
self.find_enum("core:option:Option")
|
self.find_enum("core:option:Option")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn core_default_Default(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:default:Default")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn core_iter_Iterator(&self) -> Option<Trait> {
|
pub fn core_iter_Iterator(&self) -> Option<Trait> {
|
||||||
self.find_trait("core:iter:traits:iterator:Iterator")
|
self.find_trait("core:iter:traits:iterator:Iterator")
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ itertools = "0.9.0"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
|
|
||||||
|
assists = { path = "../assists", version = "0.0.0" }
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
syntax = { path = "../syntax", version = "0.0.0" }
|
syntax = { path = "../syntax", version = "0.0.0" }
|
||||||
text_edit = { path = "../text_edit", version = "0.0.0" }
|
text_edit = { path = "../text_edit", version = "0.0.0" }
|
||||||
|
|
|
@ -1,16 +1,43 @@
|
||||||
//! Complete fields in record literals and patterns.
|
//! Complete fields in record literals and patterns.
|
||||||
use crate::{CompletionContext, Completions};
|
use assists::utils::FamousDefs;
|
||||||
|
use syntax::ast::Expr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
||||||
let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
|
let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
|
||||||
(None, None) => return None,
|
(None, None) => return None,
|
||||||
(Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
|
(Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
|
||||||
(Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
|
(Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
|
||||||
(_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit),
|
(_, Some(record_lit)) => {
|
||||||
|
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
|
||||||
|
let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
|
||||||
|
let impl_default_trait = default_trait
|
||||||
|
.and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[])))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
|
||||||
|
if impl_default_trait && !missing_fields.is_empty() {
|
||||||
|
acc.add(
|
||||||
|
CompletionItem::new(
|
||||||
|
CompletionKind::Snippet,
|
||||||
|
ctx.source_range(),
|
||||||
|
"..Default::default()",
|
||||||
|
)
|
||||||
|
.insert_text("..Default::default()")
|
||||||
|
.kind(CompletionItemKind::Field)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
missing_fields
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (field, ty) in missing_fields {
|
for (field, ty) in missing_fields {
|
||||||
acc.add_field(ctx, field, &ty)
|
acc.add_field(ctx, field, &ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
|
@ -18,6 +45,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use assists::utils::FamousDefs;
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
|
||||||
use crate::{test_utils::completion_list, CompletionKind};
|
use crate::{test_utils::completion_list, CompletionKind};
|
||||||
|
@ -27,6 +55,80 @@ mod tests {
|
||||||
expect.assert_eq(&actual);
|
expect.assert_eq(&actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_snippet(ra_fixture: &str, expect: Expect) {
|
||||||
|
let actual = completion_list(
|
||||||
|
&format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
|
||||||
|
CompletionKind::Snippet,
|
||||||
|
);
|
||||||
|
expect.assert_eq(&actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_record_literal_field_default() {
|
||||||
|
let test_code = r#"
|
||||||
|
struct S { foo: u32, bar: usize }
|
||||||
|
|
||||||
|
impl core::default::Default for S {
|
||||||
|
fn default() -> Self {
|
||||||
|
S {
|
||||||
|
foo: 0,
|
||||||
|
bar: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(f: S) {
|
||||||
|
let other = S {
|
||||||
|
foo: 5,
|
||||||
|
.<|>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
check(
|
||||||
|
test_code,
|
||||||
|
expect![[r#"
|
||||||
|
fd bar usize
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check_snippet(
|
||||||
|
test_code,
|
||||||
|
expect![[r#"
|
||||||
|
fd ..Default::default()
|
||||||
|
sn pd
|
||||||
|
sn ppd
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_record_literal_field_without_default() {
|
||||||
|
let test_code = r#"
|
||||||
|
struct S { foo: u32, bar: usize }
|
||||||
|
|
||||||
|
fn process(f: S) {
|
||||||
|
let other = S {
|
||||||
|
foo: 5,
|
||||||
|
.<|>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
check(
|
||||||
|
test_code,
|
||||||
|
expect![[r#"
|
||||||
|
fd bar usize
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check_snippet(
|
||||||
|
test_code,
|
||||||
|
expect![[r#"
|
||||||
|
sn pd
|
||||||
|
sn ppd
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_record_pattern_field() {
|
fn test_record_pattern_field() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Reference in a new issue