Unnamed repository; edit this file 'description' to name the repository.
fix: Fix edition handling for names in rename logic
Lukas Wirth 11 months ago
parent 42e8e4a · commit 96c4455
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/ide-db/src/rename.rs178
-rw-r--r--crates/ide/src/rename.rs94
3 files changed, 161 insertions, 115 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 09377df615..a75c56aa8f 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -659,7 +659,7 @@ impl<'db> SemanticsImpl<'db> {
/// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
/// and returns the conflicting locals.
- pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &str) -> Vec<Local> {
+ pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
let body = self.db.body(to_be_renamed.parent);
let resolver = to_be_renamed.parent.resolver(self.db);
let starting_expr =
@@ -668,7 +668,7 @@ impl<'db> SemanticsImpl<'db> {
body: &body,
conflicts: FxHashSet::default(),
db: self.db,
- new_name: Symbol::intern(new_name),
+ new_name: new_name.symbol().clone(),
old_name: to_be_renamed.name(self.db).symbol().clone(),
owner: to_be_renamed.parent,
to_be_renamed: to_be_renamed.binding_id,
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index e4d42146ca..4e737e27f0 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -20,7 +20,7 @@
//!
//! The correct behavior in such cases is probably to show a dialog to the user.
//! Our current behavior is ¯\_(ツ)_/¯.
-use std::fmt;
+use std::fmt::{self, Display};
use crate::{
source_change::ChangeAnnotation,
@@ -28,13 +28,12 @@ use crate::{
};
use base_db::AnchoredPathBuf;
use either::Either;
-use hir::{EditionedFileId, FieldSource, FileRange, InFile, ModuleSource, Semantics};
+use hir::{FieldSource, FileRange, InFile, ModuleSource, Name, Semantics, sym};
use span::{Edition, FileId, SyntaxContext};
use stdx::{TupleExt, never};
use syntax::{
AstNode, SyntaxKind, T, TextRange,
ast::{self, HasName},
- utils::is_raw_identifier,
};
use crate::{
@@ -87,13 +86,16 @@ impl Definition {
// self is a built-in attr, built-in type or tool module.
// it is not allowed for these defs to be renamed.
// cases where self.krate() is None is handled below.
- if let Some(krate) = self.krate(sema.db) {
+ let edition = if let Some(krate) = self.krate(sema.db) {
// Can we not rename non-local items?
// Then bail if non-local
if !krate.origin(sema.db).is_local() {
bail!("Cannot rename a non-local definition")
}
- }
+ krate.edition(sema.db)
+ } else {
+ Edition::LATEST
+ };
match *self {
Definition::Module(module) => rename_mod(sema, module, new_name),
@@ -108,9 +110,9 @@ impl Definition {
}
Definition::SelfType(_) => bail!("Cannot rename `Self`"),
Definition::Macro(mac) => {
- rename_reference(sema, Definition::Macro(mac), new_name, rename_definition)
+ rename_reference(sema, Definition::Macro(mac), new_name, rename_definition, edition)
}
- def => rename_reference(sema, def, new_name, rename_definition),
+ def => rename_reference(sema, def, new_name, rename_definition, edition),
}
}
@@ -243,10 +245,6 @@ fn rename_mod(
module: hir::Module,
new_name: &str,
) -> Result<SourceChange> {
- if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
- bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
- }
-
let mut source_change = SourceChange::default();
if module.is_crate_root() {
@@ -254,6 +252,14 @@ fn rename_mod(
}
let InFile { file_id, value: def_source } = module.definition_source(sema.db);
+ let edition = file_id.edition(sema.db);
+ let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
+ if kind != IdentifierKind::Ident {
+ bail!(
+ "Invalid name `{0}`: cannot rename module to {0}",
+ new_name.display(sema.db, edition)
+ );
+ }
if let ModuleSource::SourceFile(..) = def_source {
let anchor = file_id.original_file(sema.db).file_id(sema.db);
@@ -262,7 +268,7 @@ fn rename_mod(
// Module exists in a named file
if !is_mod_rs {
- let path = format!("{new_name}.rs");
+ let path = format!("{}.rs", new_name.as_str());
let dst = AnchoredPathBuf { anchor, path };
source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst })
}
@@ -273,11 +279,11 @@ fn rename_mod(
let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
// Go up one level since the anchor is inside the dir we're trying to rename
(true, _, Some(mod_name)) => {
- Some((format!("../{}", mod_name.as_str()), format!("../{new_name}")))
+ Some((format!("../{}", mod_name.as_str()), format!("../{}", new_name.as_str())))
}
// The anchor is on the same level as target dir
(false, true, Some(mod_name)) => {
- Some((mod_name.as_str().to_owned(), new_name.to_owned()))
+ Some((mod_name.as_str().to_owned(), new_name.as_str().to_owned()))
}
_ => None,
};
@@ -302,11 +308,7 @@ fn rename_mod(
.original_file_range_opt(sema.db)
.map(TupleExt::head)
{
- let new_name = if is_raw_identifier(new_name, file_id.edition(sema.db)) {
- format!("r#{new_name}")
- } else {
- new_name.to_owned()
- };
+ let new_name = new_name.display(sema.db, edition).to_string();
source_change.insert_source_edit(
file_id.file_id(sema.db),
TextEdit::replace(file_range.range, new_name),
@@ -320,9 +322,10 @@ fn rename_mod(
let def = Definition::Module(module);
let usages = def.usages(sema).all();
let ref_edits = usages.iter().map(|(file_id, references)| {
+ let edition = file_id.edition(sema.db);
(
file_id.file_id(sema.db),
- source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+ source_edit_from_references(sema.db, references, def, &new_name, edition),
)
});
source_change.extend(ref_edits);
@@ -335,8 +338,9 @@ fn rename_reference(
def: Definition,
new_name: &str,
rename_definition: RenameDefinition,
+ edition: Edition,
) -> Result<SourceChange> {
- let ident_kind = IdentifierKind::classify(new_name)?;
+ let (mut new_name, ident_kind) = IdentifierKind::classify(edition, new_name)?;
if matches!(
def,
@@ -344,18 +348,34 @@ fn rename_reference(
) {
match ident_kind {
IdentifierKind::Underscore => {
- bail!("Invalid name `{}`: not a lifetime identifier", new_name);
+ bail!(
+ "Invalid name `{}`: not a lifetime identifier",
+ new_name.display(sema.db, edition)
+ );
+ }
+ IdentifierKind::Ident => {
+ new_name = Name::new_lifetime(&format!("'{}", new_name.as_str()))
}
- _ => cov_mark::hit!(rename_lifetime),
+ IdentifierKind::Lifetime => (),
+ IdentifierKind::LowercaseSelf => bail!(
+ "Invalid name `{}`: not a lifetime identifier",
+ new_name.display(sema.db, edition)
+ ),
}
} else {
match ident_kind {
IdentifierKind::Lifetime => {
cov_mark::hit!(rename_not_an_ident_ref);
- bail!("Invalid name `{}`: not an identifier", new_name);
+ bail!("Invalid name `{}`: not an identifier", new_name.display(sema.db, edition));
}
IdentifierKind::Ident => cov_mark::hit!(rename_non_local),
IdentifierKind::Underscore => (),
+ IdentifierKind::LowercaseSelf => {
+ bail!(
+ "Invalid name `{}`: cannot rename to `self`",
+ new_name.display(sema.db, edition)
+ );
+ }
}
}
@@ -368,31 +388,29 @@ fn rename_reference(
}
let mut source_change = SourceChange::default();
source_change.extend(usages.iter().map(|(file_id, references)| {
+ let edition = file_id.edition(sema.db);
(
file_id.file_id(sema.db),
- source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+ source_edit_from_references(sema.db, references, def, &new_name, edition),
)
}));
if rename_definition == RenameDefinition::Yes {
// This needs to come after the references edits, because we change the annotation of existing edits
// if a conflict is detected.
- let (file_id, edit) = source_edit_from_def(sema, def, new_name, &mut source_change)?;
+ let (file_id, edit) = source_edit_from_def(sema, def, &new_name, &mut source_change)?;
source_change.insert_source_edit(file_id, edit);
}
Ok(source_change)
}
pub fn source_edit_from_references(
+ db: &RootDatabase,
references: &[FileReference],
def: Definition,
- new_name: &str,
+ new_name: &Name,
edition: Edition,
) -> TextEdit {
- let new_name = if is_raw_identifier(new_name, edition) {
- format!("r#{new_name}")
- } else {
- new_name.to_owned()
- };
+ let name_display = new_name.display(db, edition);
let mut edit = TextEdit::builder();
// macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
let mut edited_ranges = Vec::new();
@@ -403,23 +421,15 @@ pub fn source_edit_from_references(
// to make special rewrites like shorthand syntax and such, so just rename the node in
// the macro input
FileReferenceNode::NameRef(name_ref) if name_range == range => {
- source_edit_from_name_ref(&mut edit, name_ref, &new_name, def)
+ source_edit_from_name_ref(&mut edit, name_ref, &name_display, def)
}
FileReferenceNode::Name(name) if name_range == range => {
- source_edit_from_name(&mut edit, name, &new_name)
+ source_edit_from_name(&mut edit, name, &name_display)
}
_ => false,
};
if !has_emitted_edit && !edited_ranges.contains(&range.start()) {
- let (range, new_name) = match name {
- FileReferenceNode::Lifetime(_) => (
- TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
- new_name.strip_prefix('\'').unwrap_or(&new_name).to_owned(),
- ),
- _ => (range, new_name.to_owned()),
- };
-
- edit.replace(range, new_name);
+ edit.replace(range, name_display.to_string());
edited_ranges.push(range.start());
}
}
@@ -427,7 +437,11 @@ pub fn source_edit_from_references(
edit.finish()
}
-fn source_edit_from_name(edit: &mut TextEditBuilder, name: &ast::Name, new_name: &str) -> bool {
+fn source_edit_from_name(
+ edit: &mut TextEditBuilder,
+ name: &ast::Name,
+ new_name: &dyn Display,
+) -> bool {
if ast::RecordPatField::for_field_name(name).is_some() {
if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
cov_mark::hit!(rename_record_pat_field_name_split);
@@ -447,7 +461,7 @@ fn source_edit_from_name(edit: &mut TextEditBuilder, name: &ast::Name, new_name:
fn source_edit_from_name_ref(
edit: &mut TextEditBuilder,
name_ref: &ast::NameRef,
- new_name: &str,
+ new_name: &dyn Display,
def: Definition,
) -> bool {
if name_ref.super_token().is_some() {
@@ -460,6 +474,7 @@ fn source_edit_from_name_ref(
match &(rcf_name_ref, rcf_expr.and_then(|it| expr_as_name_ref(&it))) {
// field: init-expr, check if we can use a field init shorthand
(Some(field_name), Some(init)) => {
+ let new_name = new_name.to_string();
if field_name == name_ref {
if init.text() == new_name {
cov_mark::hit!(test_rename_field_put_init_shorthand);
@@ -515,6 +530,7 @@ fn source_edit_from_name_ref(
{
// field name is being renamed
if let Some(name) = pat.name() {
+ let new_name = new_name.to_string();
if name.text() == new_name {
cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
// Foo { field: ref mut local } -> Foo { ref mut field }
@@ -526,7 +542,7 @@ fn source_edit_from_name_ref(
let s = field_name.syntax().text_range().start();
let e = pat.syntax().text_range().start();
edit.delete(TextRange::new(s, e));
- edit.replace(name.syntax().text_range(), new_name.to_owned());
+ edit.replace(name.syntax().text_range(), new_name);
return true;
}
}
@@ -540,16 +556,9 @@ fn source_edit_from_name_ref(
fn source_edit_from_def(
sema: &Semantics<'_, RootDatabase>,
def: Definition,
- new_name: &str,
+ new_name: &Name,
source_change: &mut SourceChange,
) -> Result<(FileId, TextEdit)> {
- let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
- if is_raw_identifier(new_name, file_id.edition(sema.db)) {
- format!("r#{new_name}")
- } else {
- new_name.to_owned()
- }
- };
let mut edit = TextEdit::builder();
if let Definition::Local(local) = def {
let mut file_id = None;
@@ -581,7 +590,10 @@ fn source_edit_from_def(
{
Some(FileRange { file_id: file_id2, range }) => {
file_id = Some(file_id2);
- edit.replace(range, new_name_edition_aware(new_name, file_id2));
+ edit.replace(
+ range,
+ new_name.display(sema.db, file_id2.edition(sema.db)).to_string(),
+ );
continue;
}
None => {
@@ -595,7 +607,7 @@ fn source_edit_from_def(
// special cases required for renaming fields/locals in Record patterns
if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
if let Some(name_ref) = pat_field.name_ref() {
- if new_name == name_ref.text().as_str().trim_start_matches("r#")
+ if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#")
&& pat.at_token().is_none()
{
// Foo { field: ref mut local } -> Foo { ref mut field }
@@ -615,7 +627,9 @@ fn source_edit_from_def(
// ^^^^^ replace this with `new_name`
edit.replace(
name_range,
- new_name_edition_aware(new_name, source.file_id),
+ new_name
+ .display(sema.db, source.file_id.edition(sema.db))
+ .to_string(),
);
}
} else {
@@ -626,10 +640,16 @@ fn source_edit_from_def(
pat.syntax().text_range().start(),
format!("{}: ", pat_field.field_name().unwrap()),
);
- edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
+ edit.replace(
+ name_range,
+ new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
+ );
}
} else {
- edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
+ edit.replace(
+ name_range,
+ new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
+ );
}
}
}
@@ -647,16 +667,13 @@ fn source_edit_from_def(
.range_for_rename(sema)
.ok_or_else(|| format_err!("No identifier available to rename"))?;
let (range, new_name) = match def {
- Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) => (
- TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
- new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
+ Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => (
+ TextRange::empty(range.end()),
+ format!(" as {}", new_name.display(sema.db, file_id.edition(sema.db)),),
),
- Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => {
- (TextRange::empty(range.end()), format!(" as {new_name}"))
- }
- _ => (range, new_name.to_owned()),
+ _ => (range, new_name.display(sema.db, file_id.edition(sema.db)).to_string()),
};
- edit.replace(range, new_name_edition_aware(&new_name, file_id));
+ edit.replace(range, new_name);
Ok((file_id.file_id(sema.db), edit.finish()))
}
@@ -665,26 +682,27 @@ pub enum IdentifierKind {
Ident,
Lifetime,
Underscore,
+ LowercaseSelf,
}
impl IdentifierKind {
- pub fn classify(new_name: &str) -> Result<IdentifierKind> {
- let new_name = new_name.trim_start_matches("r#");
- match parser::LexedStr::single_token(Edition::LATEST, new_name) {
+ pub fn classify(edition: Edition, new_name: &str) -> Result<(Name, IdentifierKind)> {
+ match parser::LexedStr::single_token(edition, new_name) {
Some(res) => match res {
- (SyntaxKind::IDENT, _) => {
- if let Some(inner) = new_name.strip_prefix("r#") {
- if matches!(inner, "self" | "crate" | "super" | "Self") {
- bail!("Invalid name: `{}` cannot be a raw identifier", inner);
- }
- }
- Ok(IdentifierKind::Ident)
+ (SyntaxKind::IDENT, _) => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
+ (T![_], _) => {
+ Ok((Name::new_symbol_root(sym::underscore), IdentifierKind::Underscore))
}
- (T![_], _) => Ok(IdentifierKind::Underscore),
(SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
- Ok(IdentifierKind::Lifetime)
+ Ok((Name::new_lifetime(new_name), IdentifierKind::Lifetime))
}
- _ if is_raw_identifier(new_name, Edition::LATEST) => Ok(IdentifierKind::Ident),
+ _ if SyntaxKind::from_keyword(new_name, edition).is_some() => match new_name {
+ "self" => Ok((Name::new_root(new_name), IdentifierKind::LowercaseSelf)),
+ "crate" | "super" | "Self" => {
+ bail!("Invalid name `{}`: cannot rename to a keyword", new_name)
+ }
+ _ => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
+ },
(_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
(_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
},
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index e959a9bd76..fb84e8e6b4 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -4,7 +4,7 @@
//! tests. This module also implements a couple of magic tricks, like renaming
//! `self` and to `self` (to switch between associated function and method).
-use hir::{AsAssocItem, InFile, Name, Semantics};
+use hir::{AsAssocItem, InFile, Name, Semantics, sym};
use ide_db::{
FileId, FileRange, RootDatabase,
defs::{Definition, NameClass, NameRefClass},
@@ -33,7 +33,7 @@ pub(crate) fn prepare_rename(
let source_file = sema.parse_guess_edition(position.file_id);
let syntax = source_file.syntax();
- let res = find_definitions(&sema, syntax, position, "?")?
+ let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))?
.map(|(frange, kind, def, _, _)| {
// ensure all ranges are valid
@@ -88,22 +88,28 @@ pub(crate) fn rename(
let source_file = sema.parse(file_id);
let syntax = source_file.syntax();
- let defs = find_definitions(&sema, syntax, position, new_name)?;
- let alias_fallback = alias_fallback(syntax, position, new_name);
+ let edition = file_id.edition(db);
+ let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
+
+ let defs = find_definitions(&sema, syntax, position, &new_name)?;
+ let alias_fallback =
+ alias_fallback(syntax, position, &new_name.display(db, edition).to_string());
let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
Some(_) => defs
// FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
// properly find "direct" usages/references.
.map(|(.., def, new_name, _)| {
- match IdentifierKind::classify(&new_name)? {
+ match kind {
IdentifierKind::Ident => (),
IdentifierKind::Lifetime => {
bail!("Cannot alias reference to a lifetime identifier")
}
IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
+ IdentifierKind::LowercaseSelf => {
+ bail!("Cannot rename alias reference to `self`")
+ }
};
-
let mut usages = def.usages(&sema).all();
// FIXME: hack - removes the usage that triggered this rename operation.
@@ -120,7 +126,7 @@ pub(crate) fn rename(
source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
(
position.file_id,
- source_edit_from_references(refs, def, &new_name, file_id.edition(db)),
+ source_edit_from_references(db, refs, def, &new_name, edition),
)
}));
@@ -132,14 +138,14 @@ pub(crate) fn rename(
if let Definition::Local(local) = def {
if let Some(self_param) = local.as_self_param(sema.db) {
cov_mark::hit!(rename_self_to_param);
- return rename_self_to_param(&sema, local, self_param, &new_name);
+ return rename_self_to_param(&sema, local, self_param, &new_name, kind);
}
- if new_name == "self" {
+ if kind == IdentifierKind::LowercaseSelf {
cov_mark::hit!(rename_to_self);
return rename_to_self(&sema, local);
}
}
- def.rename(&sema, &new_name, rename_def)
+ def.rename(&sema, new_name.as_str(), rename_def)
})
.collect(),
};
@@ -200,8 +206,8 @@ fn find_definitions(
sema: &Semantics<'_, RootDatabase>,
syntax: &SyntaxNode,
FilePosition { file_id, offset }: FilePosition,
- new_name: &str,
-) -> RenameResult<impl Iterator<Item = (FileRange, SyntaxKind, Definition, String, RenameDefinition)>>
+ new_name: &Name,
+) -> RenameResult<impl Iterator<Item = (FileRange, SyntaxKind, Definition, Name, RenameDefinition)>>
{
let maybe_format_args =
syntax.token_at_offset(offset).find(|t| matches!(t.kind(), SyntaxKind::STRING));
@@ -213,7 +219,7 @@ fn find_definitions(
FileRange { file_id, range },
SyntaxKind::STRING,
Definition::from(resolution),
- new_name.to_owned(),
+ new_name.clone(),
RenameDefinition::Yes,
)]
.into_iter());
@@ -224,7 +230,13 @@ fn find_definitions(
.max_by_key(|t| {
t.kind().is_any_identifier() || matches!(t.kind(), SyntaxKind::LIFETIME_IDENT)
})
- .map(|t| Name::new_root(t.text()))
+ .map(|t| {
+ if t.kind() == SyntaxKind::LIFETIME_IDENT {
+ Name::new_lifetime(t.text())
+ } else {
+ Name::new_root(t.text())
+ }
+ })
.ok_or_else(|| format_err!("No references found at position"))?;
let symbols =
sema.find_namelike_at_offset_with_descend(syntax, offset).map(|name_like| {
@@ -299,13 +311,12 @@ fn find_definitions(
res.map(|def| {
let n = def.name(sema.db)?;
if n == original_ident {
- Some((range, kind, def, new_name.to_owned(), RenameDefinition::Yes))
+ Some((range, kind, def, new_name.clone(), RenameDefinition::Yes))
} else if let Some(suffix) = n.as_str().strip_prefix(original_ident.as_str()) {
- Some((range, kind, def, format!("{new_name}{suffix}"), RenameDefinition::No))
- } else if let Some(prefix) = n.as_str().strip_suffix(original_ident.as_str()) {
- Some((range, kind, def, format!("{prefix}{new_name}"), RenameDefinition::No))
+ Some((range, kind, def, Name::new_root(&format!("{}{suffix}", new_name.as_str())), RenameDefinition::No))
} else {
- None
+ n.as_str().strip_suffix(original_ident.as_str().trim_start_matches('\''))
+ .map(|prefix| (range, kind, def, Name::new_root(&format!("{prefix}{}", new_name.as_str())), RenameDefinition::No))
}
})
});
@@ -388,7 +399,13 @@ fn rename_to_self(
source_change.extend(usages.iter().map(|(file_id, references)| {
(
file_id.file_id(sema.db),
- source_edit_from_references(references, def, "self", file_id.edition(sema.db)),
+ source_edit_from_references(
+ sema.db,
+ references,
+ def,
+ &Name::new_symbol_root(sym::self_),
+ file_id.edition(sema.db),
+ ),
)
}));
source_change.insert_source_edit(
@@ -402,23 +419,25 @@ fn rename_self_to_param(
sema: &Semantics<'_, RootDatabase>,
local: hir::Local,
self_param: hir::SelfParam,
- new_name: &str,
+ new_name: &Name,
+ identifier_kind: IdentifierKind,
) -> RenameResult<SourceChange> {
- if new_name == "self" {
+ if identifier_kind == IdentifierKind::LowercaseSelf {
// Let's do nothing rather than complain.
cov_mark::hit!(rename_self_to_self);
return Ok(SourceChange::default());
}
- let identifier_kind = IdentifierKind::classify(new_name)?;
-
let InFile { file_id, value: self_param } =
sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
let def = Definition::Local(local);
let usages = def.usages(sema).all();
- let edit = text_edit_from_self_param(&self_param, new_name)
- .ok_or_else(|| format_err!("No target type found"))?;
+ let edit = text_edit_from_self_param(
+ &self_param,
+ new_name.display(sema.db, file_id.edition(sema.db)).to_string(),
+ )
+ .ok_or_else(|| format_err!("No target type found"))?;
if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
bail!("Cannot rename reference to `_` as it is being referenced multiple times");
}
@@ -427,13 +446,19 @@ fn rename_self_to_param(
source_change.extend(usages.iter().map(|(file_id, references)| {
(
file_id.file_id(sema.db),
- source_edit_from_references(references, def, new_name, file_id.edition(sema.db)),
+ source_edit_from_references(
+ sema.db,
+ references,
+ def,
+ new_name,
+ file_id.edition(sema.db),
+ ),
)
}));
Ok(source_change)
}
-fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
+fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> Option<TextEdit> {
fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
return Some(p.path()?.segment()?.name_ref()?.text().to_string());
@@ -445,7 +470,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
Some(impl_def) => {
let type_name = target_type_name(&impl_def)?;
- let mut replacement_text = String::from(new_name);
+ let mut replacement_text = new_name;
replacement_text.push_str(": ");
match (self_param.amp_token(), self_param.mut_token()) {
(Some(_), None) => replacement_text.push('&'),
@@ -458,7 +483,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
}
None => {
cov_mark::hit!(rename_self_outside_of_methods);
- let mut replacement_text = String::from(new_name);
+ let mut replacement_text = new_name;
replacement_text.push_str(": _");
Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
}
@@ -728,7 +753,7 @@ impl Foo {
check(
"super",
r#"fn main() { let i$0 = 1; }"#,
- "error: Invalid name `super`: not an identifier",
+ "error: Invalid name `super`: cannot rename to a keyword",
);
}
@@ -777,7 +802,11 @@ impl Foo {
#[test]
fn test_rename_mod_invalid_raw_ident() {
- check("r#self", r#"mod foo$0 {}"#, "error: Invalid name `self`: not an identifier");
+ check(
+ "r#self",
+ r#"mod foo$0 {}"#,
+ "error: Invalid name `self`: cannot rename module to self",
+ );
}
#[test]
@@ -2377,7 +2406,6 @@ fn foo(foo: Foo) {
#[test]
fn test_rename_lifetimes() {
- cov_mark::check!(rename_lifetime);
check(
"'yeeee",
r#"