Unnamed repository; edit this file 'description' to name the repository.
Allow renaming when there are multiple definitions (due to macros) even when some cannot rename
Chayim Refael Friedman 10 months ago
parent 978393b · commit 47845d6
-rw-r--r--crates/ide/src/rename.rs124
1 files changed, 72 insertions, 52 deletions
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index aea4ae0fd9..8922a8eb48 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -27,6 +27,27 @@ pub use ide_db::rename::RenameError;
type RenameResult<T> = Result<T, RenameError>;
+/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item.
+fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
+ let mut err = None;
+ let oks = iter
+ .filter_map(|item| match item {
+ Ok(it) => Some(it),
+ Err(it) => {
+ err = Some(it);
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ if !oks.is_empty() {
+ Ok(oks)
+ } else if let Some(err) = err {
+ Err(err)
+ } else {
+ Ok(Vec::new())
+ }
+}
+
/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
/// being targeted for a rename.
pub(crate) fn prepare_rename(
@@ -95,58 +116,57 @@ pub(crate) fn rename(
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 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.
- match usages.references.get_mut(&file_id).and_then(|refs| {
- refs.iter()
- .position(|ref_| ref_.range.contains_inclusive(position.offset))
- .map(|idx| refs.remove(idx))
- }) {
- Some(_) => (),
- None => never!(),
- };
-
- let mut source_change = SourceChange::default();
- source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
- (
- position.file_id,
- source_edit_from_references(db, refs, def, &new_name, edition),
- )
- }));
-
- Ok(source_change)
- })
- .collect(),
- None => defs
- .map(|(.., def, new_name, rename_def)| {
- 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, kind);
- }
- if kind == IdentifierKind::LowercaseSelf {
- cov_mark::hit!(rename_to_self);
- return rename_to_self(&sema, local);
- }
+ Some(_) => ok_if_any(
+ 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 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.
+ match usages.references.get_mut(&file_id).and_then(|refs| {
+ refs.iter()
+ .position(|ref_| ref_.range.contains_inclusive(position.offset))
+ .map(|idx| refs.remove(idx))
+ }) {
+ Some(_) => (),
+ None => never!(),
+ };
+
+ let mut source_change = SourceChange::default();
+ source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
+ (
+ position.file_id,
+ source_edit_from_references(db, refs, def, &new_name, edition),
+ )
+ }));
+
+ Ok(source_change)
+ }),
+ ),
+ None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| {
+ 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, kind);
}
- def.rename(&sema, new_name.as_str(), rename_def)
- })
- .collect(),
+ if kind == IdentifierKind::LowercaseSelf {
+ cov_mark::hit!(rename_to_self);
+ return rename_to_self(&sema, local);
+ }
+ }
+ def.rename(&sema, new_name.as_str(), rename_def)
+ })),
};
ops?.into_iter()
@@ -320,7 +340,7 @@ fn find_definitions(
})
});
- let res: RenameResult<Vec<_>> = symbols.filter_map(Result::transpose).collect();
+ let res: RenameResult<Vec<_>> = ok_if_any(symbols.filter_map(Result::transpose));
match res {
Ok(v) => {
// remove duplicates, comparing `Definition`s