mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
Merge #10076
10076: Use struct init shorthand when applicable in fill struct fields assist r=matklad a=nathanwhit This PR tweaks the fill struct fields assist to use the struct init shorthand when a local variable with a matching name and type is in scope. For example: ```rust struct Foo { a: usize, b: i32, c: char, } fn main() { let a = 1; let b = 2; let c = 3; let foo = Foo { <|> }; } ``` Before we would insert ```rust Foo { a: (), b: (), c: (), } ``` now we would insert ```rust Foo { a, b, c: () } ``` Co-authored-by: nathan.whitaker <nathan.whitaker01@gmail.com>
This commit is contained in:
commit
02a3d898e4
1 changed files with 110 additions and 3 deletions
|
@ -1,6 +1,7 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{db::AstDatabase, InFile};
|
use hir::{db::AstDatabase, InFile};
|
||||||
use ide_db::{assists::Assist, source_change::SourceChange};
|
use ide_db::{assists::Assist, source_change::SourceChange};
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
|
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
@ -54,9 +55,27 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
|
||||||
};
|
};
|
||||||
let old_field_list = field_list_parent.record_expr_field_list()?;
|
let old_field_list = field_list_parent.record_expr_field_list()?;
|
||||||
let new_field_list = old_field_list.clone_for_update();
|
let new_field_list = old_field_list.clone_for_update();
|
||||||
for f in d.missed_fields.iter() {
|
let mut locals = FxHashMap::default();
|
||||||
|
ctx.sema.scope(field_list_parent.syntax()).process_all_names(&mut |name, def| {
|
||||||
|
if let hir::ScopeDef::Local(local) = def {
|
||||||
|
locals.insert(name.clone(), local);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent);
|
||||||
|
for (f, ty) in missing_fields.iter() {
|
||||||
|
let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
|
||||||
|
cov_mark::hit!(field_shorthand);
|
||||||
|
let candidate_ty = local_candidate.ty(ctx.sema.db);
|
||||||
|
if ty.could_unify_with(ctx.sema.db, &candidate_ty) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(make::expr_unit())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(make::expr_unit())
|
||||||
|
};
|
||||||
let field =
|
let field =
|
||||||
make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
|
make::record_expr_field(make::name_ref(&f.name(ctx.sema.db).to_string()), field_expr)
|
||||||
.clone_for_update();
|
.clone_for_update();
|
||||||
new_field_list.add_field(field);
|
new_field_list.add_field(field);
|
||||||
}
|
}
|
||||||
|
@ -224,7 +243,7 @@ enum Expr {
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
|
fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
|
||||||
Expr::Bin { lhs: (), rhs: () }
|
Expr::Bin { lhs, rhs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -324,6 +343,94 @@ fn f() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fill_struct_fields_shorthand() {
|
||||||
|
cov_mark::check!(field_shorthand);
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
struct S { a: &'static str, b: i32 }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1i32;
|
||||||
|
S {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct S { a: &'static str, b: i32 }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1i32;
|
||||||
|
S {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fill_struct_fields_shorthand_ty_mismatch() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
struct S { a: &'static str, b: i32 }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1usize;
|
||||||
|
S {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct S { a: &'static str, b: i32 }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1usize;
|
||||||
|
S {
|
||||||
|
a,
|
||||||
|
b: (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fill_struct_fields_shorthand_unifies() {
|
||||||
|
check_fix(
|
||||||
|
r#"
|
||||||
|
struct S<T> { a: &'static str, b: T }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1i32;
|
||||||
|
S {
|
||||||
|
$0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct S<T> { a: &'static str, b: T }
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let a = "hello";
|
||||||
|
let b = 1i32;
|
||||||
|
S {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_extern_crate_clash_with_inner_item() {
|
fn import_extern_crate_clash_with_inner_item() {
|
||||||
// This is more of a resolver test, but doesn't really work with the hir_def testsuite.
|
// This is more of a resolver test, but doesn't really work with the hir_def testsuite.
|
||||||
|
|
Loading…
Reference in a new issue