Use shorthand field syntax in destructures

This commit is contained in:
Lukas Wirth 2020-11-14 19:57:47 +01:00
parent 924eecf4af
commit cb60708274
3 changed files with 84 additions and 29 deletions

View file

@ -622,7 +622,7 @@ fn foo() {
expect![[r#"
f RECORD_FIELD FileId(0) 15..21 15..16 Other
FileId(0) 55..56 Other Read
FileId(0) 55..56 RecordFieldExprOrPat Read
FileId(0) 68..69 Other Write
"#]],
);
@ -757,7 +757,7 @@ fn f() -> m::En {
expect![[r#"
field RECORD_FIELD FileId(0) 56..65 56..61 Other
FileId(0) 125..130 Other Read
FileId(0) 125..130 RecordFieldExprOrPat Read
"#]],
);
}

View file

@ -1,7 +1,7 @@
//! FIXME: write short doc here
use hir::{Module, ModuleDef, ModuleSource, Semantics};
use ide_db::base_db::SourceDatabaseExt;
use ide_db::base_db::{FileRange, SourceDatabaseExt};
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
RootDatabase,
@ -112,7 +112,6 @@ fn source_edit_from_reference(
new_name: &str,
) -> SourceFileEdit {
let mut replacement_text = String::new();
let file_id = reference.file_range.file_id;
let range = match reference.kind {
ReferenceKind::FieldShorthandForField => {
mark::hit!(test_rename_struct_field_for_shorthand);
@ -126,28 +125,49 @@ fn source_edit_from_reference(
replacement_text.push_str(new_name);
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
}
ReferenceKind::RecordExprField => {
ReferenceKind::RecordFieldExprOrPat => {
replacement_text.push_str(new_name);
let mut range = reference.file_range.range;
if let Some(field_expr) = syntax::algo::find_node_at_range::<ast::RecordExprField>(
sema.parse(file_id).syntax(),
reference.file_range.range,
) {
// use shorthand initializer if we were to write foo: foo
if let Some(name) = field_expr.expr().and_then(|e| e.name_ref()) {
if &name.to_string() == new_name {
range = field_expr.syntax().text_range();
}
}
}
range
edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name)
}
_ => {
replacement_text.push_str(new_name);
reference.file_range.range
}
};
SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) }
SourceFileEdit {
file_id: reference.file_range.file_id,
edit: TextEdit::replace(range, replacement_text),
}
}
fn edit_text_range_for_record_field_expr_or_pat(
sema: &Semantics<RootDatabase>,
file_range: FileRange,
new_name: &str,
) -> TextRange {
let mut range = file_range.range;
let source_file = sema.parse(file_range.file_id);
let file_syntax = source_file.syntax();
if let Some(field_expr) =
syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, range)
{
match field_expr.expr().and_then(|e| e.name_ref()) {
Some(name) if &name.to_string() == new_name => range = field_expr.syntax().text_range(),
_ => (),
}
} else if let Some(field_pat) =
syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, range)
{
match field_pat.pat() {
Some(ast::Pat::IdentPat(pat))
if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) =>
{
range = field_pat.syntax().text_range()
}
_ => (),
}
}
range
}
fn rename_mod(
@ -1189,6 +1209,29 @@ fn foo(foo: Foo) {
let Foo { i: bar } = foo;
let _ = bar;
}
"#,
);
}
#[test]
fn test_struct_field_destructure_into_shorthand() {
check(
"baz",
r#"
struct Foo { i<|>: i32 }
fn foo(foo: Foo) {
let Foo { i: baz } = foo;
let _ = baz;
}
"#,
r#"
struct Foo { baz: i32 }
fn foo(foo: Foo) {
let Foo { baz } = foo;
let _ = baz;
}
"#,
);
}

View file

@ -30,7 +30,7 @@ pub enum ReferenceKind {
FieldShorthandForField,
FieldShorthandForLocal,
StructLiteral,
RecordExprField,
RecordFieldExprOrPat,
Other,
}
@ -279,15 +279,13 @@ impl<'a> FindUsages<'a> {
) -> bool {
match NameRefClass::classify(self.sema, &name_ref) {
Some(NameRefClass::Definition(def)) if &def == self.def => {
let kind =
if name_ref.syntax().parent().and_then(ast::RecordExprField::cast).is_some() {
ReferenceKind::RecordExprField
} else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
{
ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
let kind = if is_record_field_expr_or_pat(&name_ref) {
ReferenceKind::RecordFieldExprOrPat
} else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
let reference = Reference {
file_range: self.sema.original_range(name_ref.syntax()),
@ -389,3 +387,17 @@ fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
.map(|p| p.name_ref().as_ref() == Some(name_ref))
.unwrap_or(false)
}
fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
if let Some(parent) = name_ref.syntax().parent() {
match_ast! {
match parent {
ast::RecordExprField(it) => true,
ast::RecordPatField(_it) => true,
_ => false,
}
}
} else {
false
}
}