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#" expect![[r#"
f RECORD_FIELD FileId(0) 15..21 15..16 Other 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 FileId(0) 68..69 Other Write
"#]], "#]],
); );
@ -757,7 +757,7 @@ fn f() -> m::En {
expect![[r#" expect![[r#"
field RECORD_FIELD FileId(0) 56..65 56..61 Other 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 //! FIXME: write short doc here
use hir::{Module, ModuleDef, ModuleSource, Semantics}; use hir::{Module, ModuleDef, ModuleSource, Semantics};
use ide_db::base_db::SourceDatabaseExt; use ide_db::base_db::{FileRange, SourceDatabaseExt};
use ide_db::{ use ide_db::{
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
@ -112,7 +112,6 @@ fn source_edit_from_reference(
new_name: &str, new_name: &str,
) -> SourceFileEdit { ) -> SourceFileEdit {
let mut replacement_text = String::new(); let mut replacement_text = String::new();
let file_id = reference.file_range.file_id;
let range = match reference.kind { let range = match reference.kind {
ReferenceKind::FieldShorthandForField => { ReferenceKind::FieldShorthandForField => {
mark::hit!(test_rename_struct_field_for_shorthand); mark::hit!(test_rename_struct_field_for_shorthand);
@ -126,28 +125,49 @@ fn source_edit_from_reference(
replacement_text.push_str(new_name); replacement_text.push_str(new_name);
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
} }
ReferenceKind::RecordExprField => { ReferenceKind::RecordFieldExprOrPat => {
replacement_text.push_str(new_name); replacement_text.push_str(new_name);
let mut range = reference.file_range.range; edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name)
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
} }
_ => { _ => {
replacement_text.push_str(new_name); replacement_text.push_str(new_name);
reference.file_range.range 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( fn rename_mod(
@ -1189,6 +1209,29 @@ fn foo(foo: Foo) {
let Foo { i: bar } = foo; let Foo { i: bar } = foo;
let _ = bar; 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, FieldShorthandForField,
FieldShorthandForLocal, FieldShorthandForLocal,
StructLiteral, StructLiteral,
RecordExprField, RecordFieldExprOrPat,
Other, Other,
} }
@ -279,15 +279,13 @@ fn found_name_ref(
) -> bool { ) -> bool {
match NameRefClass::classify(self.sema, &name_ref) { match NameRefClass::classify(self.sema, &name_ref) {
Some(NameRefClass::Definition(def)) if &def == self.def => { Some(NameRefClass::Definition(def)) if &def == self.def => {
let kind = let kind = if is_record_field_expr_or_pat(&name_ref) {
if name_ref.syntax().parent().and_then(ast::RecordExprField::cast).is_some() { ReferenceKind::RecordFieldExprOrPat
ReferenceKind::RecordExprField } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
} else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) ReferenceKind::StructLiteral
{ } else {
ReferenceKind::StructLiteral ReferenceKind::Other
} else { };
ReferenceKind::Other
};
let reference = Reference { let reference = Reference {
file_range: self.sema.original_range(name_ref.syntax()), 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)) .map(|p| p.name_ref().as_ref() == Some(name_ref))
.unwrap_or(false) .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
}
}