Auto merge of #18164 - ShoyuVanilla:use-as-alias, r=Veykril

fix: Temporary fix for `remove_unused_imports` not handling import aliases correctly

Fixes #18129
This commit is contained in:
bors 2024-09-24 15:22:57 +00:00
commit 2f55a91552
2 changed files with 97 additions and 13 deletions

View file

@ -6,7 +6,10 @@ use ide_db::{
search::{FileReference, ReferenceCategory, SearchScope}, search::{FileReference, ReferenceCategory, SearchScope},
FxHashMap, RootDatabase, FxHashMap, RootDatabase,
}; };
use syntax::{ast, AstNode}; use syntax::{
ast::{self, Rename},
AstNode,
};
use text_edit::TextRange; use text_edit::TextRange;
use crate::{AssistContext, AssistId, AssistKind, Assists}; use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -100,19 +103,19 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)), hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
_ => None, _ => None,
}) })
.any(|d| used_once_in_scope(ctx, d, scope)) .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
{ {
return Some(u); return Some(u);
} }
} else if let Definition::Trait(ref t) = def { } else if let Definition::Trait(ref t) = def {
// If the trait or any item is used. // If the trait or any item is used.
if !std::iter::once(def) if !std::iter::once((def, u.rename()))
.chain(t.items(ctx.db()).into_iter().map(Definition::from)) .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
.any(|d| used_once_in_scope(ctx, d, scope)) .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
{ {
return Some(u); return Some(u);
} }
} else if !used_once_in_scope(ctx, def, scope) { } else if !used_once_in_scope(ctx, def, u.rename(), scope) {
return Some(u); return Some(u);
} }
@ -138,7 +141,12 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
} }
} }
fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec<SearchScope>) -> bool { fn used_once_in_scope(
ctx: &AssistContext<'_>,
def: Definition,
rename: Option<Rename>,
scopes: &Vec<SearchScope>,
) -> bool {
let mut found = false; let mut found = false;
for scope in scopes { for scope in scopes {
@ -151,7 +159,10 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec<Sea
false false
} }
}; };
def.usages(&ctx.sema).in_scope(scope).search(&mut search_non_import); def.usages(&ctx.sema)
.in_scope(scope)
.with_rename(rename.as_ref())
.search(&mut search_non_import);
if found { if found {
break; break;
} }
@ -330,7 +341,7 @@ fn w() {
} }
#[test] #[test]
fn ranamed_trait_item_use_is_use() { fn renamed_trait_item_use_is_use() {
check_assist_not_applicable( check_assist_not_applicable(
remove_unused_imports, remove_unused_imports,
r#" r#"
@ -356,7 +367,7 @@ fn w() {
} }
#[test] #[test]
fn ranamed_underscore_trait_item_use_is_use() { fn renamed_underscore_trait_item_use_is_use() {
check_assist_not_applicable( check_assist_not_applicable(
remove_unused_imports, remove_unused_imports,
r#" r#"
@ -942,6 +953,62 @@ pub struct X();
mod z { mod z {
mod foo; mod foo;
} }
"#,
);
}
#[test]
fn use_as_alias() {
check_assist_not_applicable(
remove_unused_imports,
r#"
mod foo {
pub struct Foo {}
}
use foo::Foo as Bar$0;
fn test(_: Bar) {}
"#,
);
check_assist(
remove_unused_imports,
r#"
mod foo {
pub struct Foo {}
pub struct Bar {}
pub struct Qux {}
pub trait Quux {
fn quxx(&self) {}
}
impl<T> Quxx for T {}
}
use foo::{Foo as Bar, Bar as Baz, Qux as _, Quxx as _}$0;
fn test(_: Bar) {
let a = ();
a.quxx();
}
"#,
r#"
mod foo {
pub struct Foo {}
pub struct Bar {}
pub struct Qux {}
pub trait Quux {
fn quxx(&self) {}
}
impl<T> Quxx for T {}
}
use foo::{Foo as Bar, Quxx as _};
fn test(_: Bar) {
let a = ();
a.quxx();
}
"#, "#,
); );
} }

View file

@ -19,7 +19,7 @@ use parser::SyntaxKind;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use span::EditionedFileId; use span::EditionedFileId;
use syntax::{ use syntax::{
ast::{self, HasName}, ast::{self, HasName, Rename},
match_ast, AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize, match_ast, AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize,
ToSmolStr, ToSmolStr,
}; };
@ -405,6 +405,7 @@ impl Definition {
pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> {
FindUsages { FindUsages {
def: self, def: self,
rename: None,
assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)), assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)),
sema, sema,
scope: None, scope: None,
@ -417,6 +418,7 @@ impl Definition {
#[derive(Clone)] #[derive(Clone)]
pub struct FindUsages<'a> { pub struct FindUsages<'a> {
def: Definition, def: Definition,
rename: Option<&'a Rename>,
sema: &'a Semantics<'a, RootDatabase>, sema: &'a Semantics<'a, RootDatabase>,
scope: Option<&'a SearchScope>, scope: Option<&'a SearchScope>,
/// The container of our definition should it be an assoc item /// The container of our definition should it be an assoc item
@ -447,6 +449,14 @@ impl<'a> FindUsages<'a> {
self self
} }
// FIXME: This is just a temporary fix for not handling import aliases like
// `use Foo as Bar`. We need to support them in a proper way.
// See issue #14079
pub fn with_rename(mut self, rename: Option<&'a Rename>) -> Self {
self.rename = rename;
self
}
pub fn at_least_one(&self) -> bool { pub fn at_least_one(&self) -> bool {
let mut found = false; let mut found = false;
self.search(&mut |_, _| { self.search(&mut |_, _| {
@ -884,9 +894,16 @@ impl<'a> FindUsages<'a> {
} }
}; };
let name = match self.def { let name = match (self.rename, self.def) {
(Some(rename), _) => {
if rename.underscore_token().is_some() {
None
} else {
rename.name().map(|n| n.to_smolstr())
}
}
// special case crate modules as these do not have a proper name // special case crate modules as these do not have a proper name
Definition::Module(module) if module.is_crate_root() => { (_, Definition::Module(module)) if module.is_crate_root() => {
// FIXME: This assumes the crate name is always equal to its display name when it // FIXME: This assumes the crate name is always equal to its display name when it
// really isn't // really isn't
// we should instead look at the dependency edge name and recursively search our way // we should instead look at the dependency edge name and recursively search our way