From 183ba483a93fa8968ffbd5828c5e4c2684f0094c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 3 Mar 2021 18:23:38 +0100 Subject: Reorder functions in rename.rs --- crates/ide/src/references/rename.rs | 376 ++++++++++++++++++------------------ 1 file changed, 187 insertions(+), 189 deletions(-) diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 22ddeeae3..878ca1afc 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -122,7 +122,7 @@ fn check_identifier(new_name: &str) -> RenameResult { Ok(IdentifierKind::Lifetime) } (SyntaxKind::LIFETIME_IDENT, _) => { - bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name) + bail!("Invalid name `{}`: not a lifetime identifier", new_name) } (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), @@ -162,119 +162,6 @@ fn find_definition( .ok_or_else(|| format_err!("No references found at position")) } -fn source_edit_from_references( - _sema: &Semantics, - file_id: FileId, - references: &[FileReference], - def: Definition, - new_name: &str, -) -> (FileId, TextEdit) { - let mut edit = TextEdit::builder(); - for reference in references { - let (range, replacement) = match &reference.name { - // if the ranges differ then the node is inside a macro call, we can't really attempt - // to make special rewrites like shorthand syntax and such, so just rename the node in - // the macro input - ast::NameLike::NameRef(name_ref) - if name_ref.syntax().text_range() == reference.range => - { - source_edit_from_name_ref(name_ref, new_name, def) - } - ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { - source_edit_from_name(name, new_name) - } - _ => None, - } - .unwrap_or_else(|| (reference.range, new_name.to_string())); - edit.replace(range, replacement); - } - (file_id, edit.finish()) -} - -fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { - if let Some(_) = ast::RecordPatField::for_field_name(name) { - if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { - return Some(( - TextRange::empty(ident_pat.syntax().text_range().start()), - format!("{}: ", new_name), - )); - } - } - None -} - -fn source_edit_from_name_ref( - name_ref: &ast::NameRef, - new_name: &str, - def: Definition, -) -> Option<(TextRange, String)> { - if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { - let rcf_name_ref = record_field.name_ref(); - let rcf_expr = record_field.expr(); - match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { - // field: init-expr, check if we can use a field init shorthand - (Some(field_name), Some(init)) => { - if field_name == *name_ref { - if init.text() == new_name { - mark::hit!(test_rename_field_put_init_shorthand); - // same names, we can use a shorthand here instead. - // we do not want to erase attributes hence this range start - let s = field_name.syntax().text_range().start(); - let e = record_field.syntax().text_range().end(); - return Some((TextRange::new(s, e), new_name.to_owned())); - } - } else if init == *name_ref { - if field_name.text() == new_name { - mark::hit!(test_rename_local_put_init_shorthand); - // same names, we can use a shorthand here instead. - // we do not want to erase attributes hence this range start - let s = field_name.syntax().text_range().start(); - let e = record_field.syntax().text_range().end(); - return Some((TextRange::new(s, e), new_name.to_owned())); - } - } - None - } - // init shorthand - // FIXME: instead of splitting the shorthand, recursively trigger a rename of the - // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 - (None, Some(_)) if matches!(def, Definition::Field(_)) => { - mark::hit!(test_rename_field_in_field_shorthand); - let s = name_ref.syntax().text_range().start(); - Some((TextRange::empty(s), format!("{}: ", new_name))) - } - (None, Some(_)) if matches!(def, Definition::Local(_)) => { - mark::hit!(test_rename_local_in_field_shorthand); - let s = name_ref.syntax().text_range().end(); - Some((TextRange::empty(s), format!(": {}", new_name))) - } - _ => None, - } - } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { - let rcf_name_ref = record_field.name_ref(); - let rcf_pat = record_field.pat(); - match (rcf_name_ref, rcf_pat) { - // field: rename - (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { - // field name is being renamed - if pat.name().map_or(false, |it| it.text() == new_name) { - mark::hit!(test_rename_field_put_init_shorthand_pat); - // same names, we can use a shorthand here instead/ - // we do not want to erase attributes hence this range start - let s = field_name.syntax().text_range().start(); - let e = record_field.syntax().text_range().end(); - Some((TextRange::new(s, e), pat.to_string())) - } else { - None - } - } - _ => None, - } - } else { - None - } -} - fn rename_mod( sema: &Semantics, module: Module, @@ -308,18 +195,75 @@ fn rename_mod( TextEdit::replace(name.syntax().text_range(), new_name.to_string()), ), _ => unreachable!(), - }; + } } let def = Definition::ModuleDef(ModuleDef::Module(module)); let usages = def.usages(sema).all(); let ref_edits = usages.iter().map(|(&file_id, references)| { - source_edit_from_references(sema, file_id, references, def, new_name) + (file_id, source_edit_from_references(references, def, new_name)) }); source_change.extend(ref_edits); Ok(source_change) } +fn rename_reference( + sema: &Semantics, + def: Definition, + new_name: &str, +) -> RenameResult { + let ident_kind = check_identifier(new_name)?; + + let def_is_lbl_or_lt = matches!( + def, + Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) + ); + match (ident_kind, def) { + (IdentifierKind::ToSelf, _) + | (IdentifierKind::Underscore, _) + | (IdentifierKind::Ident, _) + if def_is_lbl_or_lt => + { + mark::hit!(rename_not_a_lifetime_ident_ref); + bail!("Invalid name `{}`: not a lifetime identifier", new_name) + } + (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), + (IdentifierKind::Lifetime, _) => { + mark::hit!(rename_not_an_ident_ref); + bail!("Invalid name `{}`: not an identifier", new_name) + } + (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { + // no-op + mark::hit!(rename_self_to_self); + return Ok(SourceChange::default()); + } + (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { + mark::hit!(rename_self_to_param); + return rename_self_to_param(sema, local, new_name, ident_kind); + } + (IdentifierKind::ToSelf, Definition::Local(local)) => { + mark::hit!(rename_to_self); + return rename_to_self(sema, local); + } + (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), + (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), + } + + let usages = def.usages(sema).all(); + if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { + mark::hit!(rename_underscore_multiple); + bail!("Cannot rename reference to `_` as it is being referenced multiple times"); + } + let mut source_change = SourceChange::default(); + source_change.extend(usages.iter().map(|(&file_id, references)| { + (file_id, source_edit_from_references(&references, def, new_name)) + })); + + let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; + source_change.insert_source_edit(file_id, edit); + Ok(source_change) +} + fn rename_to_self(sema: &Semantics, local: hir::Local) -> RenameResult { if never!(local.is_self(sema.db)) { bail!("rename_to_self invoked on self"); @@ -384,7 +328,7 @@ fn rename_to_self(sema: &Semantics, local: hir::Local) -> RenameRe let usages = def.usages(sema).all(); let mut source_change = SourceChange::default(); source_change.extend(usages.iter().map(|(&file_id, references)| { - source_edit_from_references(sema, file_id, references, def, "self") + (file_id, source_edit_from_references(references, def, "self")) })); source_change.insert_source_edit( file_id.original_file(sema.db), @@ -394,29 +338,6 @@ fn rename_to_self(sema: &Semantics, local: hir::Local) -> RenameRe Ok(source_change) } -fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option { - fn target_type_name(impl_def: &ast::Impl) -> Option { - if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { - return Some(p.path()?.segment()?.name_ref()?.text().to_string()); - } - None - } - - let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; - let type_name = target_type_name(&impl_def)?; - - let mut replacement_text = String::from(new_name); - replacement_text.push_str(": "); - match (self_param.amp_token(), self_param.mut_token()) { - (None, None) => (), - (Some(_), None) => replacement_text.push('&'), - (_, Some(_)) => replacement_text.push_str("&mut "), - }; - replacement_text.push_str(type_name.as_str()); - - Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) -} - fn rename_self_to_param( sema: &Semantics, local: hir::Local, @@ -441,66 +362,143 @@ fn rename_self_to_param( let mut source_change = SourceChange::default(); source_change.insert_source_edit(file_id.original_file(sema.db), edit); source_change.extend(usages.iter().map(|(&file_id, references)| { - source_edit_from_references(sema, file_id, &references, def, new_name) + (file_id, source_edit_from_references(&references, def, new_name)) })); Ok(source_change) } -fn rename_reference( - sema: &Semantics, +fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option { + fn target_type_name(impl_def: &ast::Impl) -> Option { + if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { + return Some(p.path()?.segment()?.name_ref()?.text().to_string()); + } + None + } + + let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; + let type_name = target_type_name(&impl_def)?; + + let mut replacement_text = String::from(new_name); + replacement_text.push_str(": "); + match (self_param.amp_token(), self_param.mut_token()) { + (Some(_), None) => replacement_text.push('&'), + (Some(_), Some(_)) => replacement_text.push_str("&mut "), + (_, _) => (), + }; + replacement_text.push_str(type_name.as_str()); + + Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) +} + +fn source_edit_from_references( + references: &[FileReference], def: Definition, new_name: &str, -) -> RenameResult { - let ident_kind = check_identifier(new_name)?; - - let def_is_lbl_or_lt = matches!( - def, - Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) - ); - match (ident_kind, def) { - (IdentifierKind::ToSelf, _) - | (IdentifierKind::Underscore, _) - | (IdentifierKind::Ident, _) - if def_is_lbl_or_lt => - { - mark::hit!(rename_not_a_lifetime_ident_ref); - bail!("Invalid name `{}`: not a lifetime identifier", new_name) - } - (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), - (IdentifierKind::Lifetime, _) => { - mark::hit!(rename_not_an_ident_ref); - bail!("Invalid name `{}`: not an identifier", new_name) - } - (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { - // no-op - mark::hit!(rename_self_to_self); - return Ok(SourceChange::default()); - } - (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { - mark::hit!(rename_self_to_param); - return rename_self_to_param(sema, local, new_name, ident_kind); - } - (IdentifierKind::ToSelf, Definition::Local(local)) => { - mark::hit!(rename_to_self); - return rename_to_self(sema, local); +) -> TextEdit { + let mut edit = TextEdit::builder(); + for reference in references { + let (range, replacement) = match &reference.name { + // if the ranges differ then the node is inside a macro call, we can't really attempt + // to make special rewrites like shorthand syntax and such, so just rename the node in + // the macro input + ast::NameLike::NameRef(name_ref) + if name_ref.syntax().text_range() == reference.range => + { + source_edit_from_name_ref(name_ref, new_name, def) + } + ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { + source_edit_from_name(name, new_name) + } + _ => None, } - (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), - (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), + .unwrap_or_else(|| (reference.range, new_name.to_string())); + edit.replace(range, replacement); } + edit.finish() +} - let usages = def.usages(sema).all(); - if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { - mark::hit!(rename_underscore_multiple); - bail!("Cannot rename reference to `_` as it is being referenced multiple times"); +fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { + if let Some(_) = ast::RecordPatField::for_field_name(name) { + if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { + return Some(( + TextRange::empty(ident_pat.syntax().text_range().start()), + [new_name, ": "].concat(), + )); + } } - let mut source_change = SourceChange::default(); - source_change.extend(usages.iter().map(|(&file_id, references)| { - source_edit_from_references(sema, file_id, &references, def, new_name) - })); + None +} - let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; - source_change.insert_source_edit(file_id, edit); - Ok(source_change) +fn source_edit_from_name_ref( + name_ref: &ast::NameRef, + new_name: &str, + def: Definition, +) -> Option<(TextRange, String)> { + if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { + let rcf_name_ref = record_field.name_ref(); + let rcf_expr = record_field.expr(); + match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { + // field: init-expr, check if we can use a field init shorthand + (Some(field_name), Some(init)) => { + if field_name == *name_ref { + if init.text() == new_name { + mark::hit!(test_rename_field_put_init_shorthand); + // same names, we can use a shorthand here instead. + // we do not want to erase attributes hence this range start + let s = field_name.syntax().text_range().start(); + let e = record_field.syntax().text_range().end(); + return Some((TextRange::new(s, e), new_name.to_owned())); + } + } else if init == *name_ref { + if field_name.text() == new_name { + mark::hit!(test_rename_local_put_init_shorthand); + // same names, we can use a shorthand here instead. + // we do not want to erase attributes hence this range start + let s = field_name.syntax().text_range().start(); + let e = record_field.syntax().text_range().end(); + return Some((TextRange::new(s, e), new_name.to_owned())); + } + } + None + } + // init shorthand + // FIXME: instead of splitting the shorthand, recursively trigger a rename of the + // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 + (None, Some(_)) if matches!(def, Definition::Field(_)) => { + mark::hit!(test_rename_field_in_field_shorthand); + let s = name_ref.syntax().text_range().start(); + Some((TextRange::empty(s), format!("{}: ", new_name))) + } + (None, Some(_)) if matches!(def, Definition::Local(_)) => { + mark::hit!(test_rename_local_in_field_shorthand); + let s = name_ref.syntax().text_range().end(); + Some((TextRange::empty(s), format!(": {}", new_name))) + } + _ => None, + } + } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { + let rcf_name_ref = record_field.name_ref(); + let rcf_pat = record_field.pat(); + match (rcf_name_ref, rcf_pat) { + // field: rename + (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { + // field name is being renamed + if pat.name().map_or(false, |it| it.text() == new_name) { + mark::hit!(test_rename_field_put_init_shorthand_pat); + // same names, we can use a shorthand here instead/ + // we do not want to erase attributes hence this range start + let s = field_name.syntax().text_range().start(); + let e = record_field.syntax().text_range().end(); + Some((TextRange::new(s, e), pat.to_string())) + } else { + None + } + } + _ => None, + } + } else { + None + } } fn source_edit_from_def( -- cgit v1.2.3 From e194f2edc4275f592c247e26a460b025c69c6cea Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 3 Mar 2021 19:48:53 +0100 Subject: Prevent renaming SelfType and BuiltinType --- crates/ide/src/references/rename.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 878ca1afc..1919639a3 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -88,6 +88,8 @@ pub(crate) fn rename_with_semantics( let def = find_definition(sema, syntax, position)?; match def { Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), + Definition::SelfType(_) => bail!("Cannot rename `Self`"), + Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), def => rename_reference(sema, def, new_name), } } -- cgit v1.2.3