Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/remove_unused_imports.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/remove_unused_imports.rs | 190 |
1 files changed, 166 insertions, 24 deletions
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index 1baf814ca6..16debc4d72 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -1,6 +1,9 @@ use std::collections::hash_map::Entry; -use hir::{FileRange, InFile, InRealFile, Module, ModuleSource}; +use hir::{ + FileRange, InFile, InRealFile, Module, ModuleDef, ModuleSource, PathResolution, + PathResolutionPerNs, +}; use ide_db::text_edit::TextRange; use ide_db::{ FxHashMap, RootDatabase, @@ -77,22 +80,17 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) }; // Get the actual definition associated with this use item. - let res = match ctx.sema.resolve_path(&path) { - Some(x) => x, - None => { + let res = match ctx.sema.resolve_path_per_ns(&path) { + Some(x) if x.any().is_some() => x, + Some(_) | None => { return None; } }; - let def = match res { - hir::PathResolution::Def(d) => Definition::from(d), - _ => return None, - }; - if u.star_token().is_some() { // Check if any of the children of this module are used - let def_mod = match def { - Definition::Module(module) => module, + let def_mod = match res.type_ns { + Some(PathResolution::Def(ModuleDef::Module(module))) => module, _ => return None, }; @@ -105,21 +103,13 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) }) .any(|d| used_once_in_scope(ctx, d, u.rename(), scope)) { - return Some(u); - } - } else if let Definition::Trait(ref t) = def { - // If the trait or any item is used. - if !std::iter::once((def, u.rename())) - .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) - .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) - { - return Some(u); + Some(u) + } else { + None } - } else if !used_once_in_scope(ctx, def, u.rename(), scope) { - return Some(u); + } else { + is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u) } - - None }) .peekable(); @@ -141,6 +131,52 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } } +fn is_path_per_ns_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec<SearchScope>, + path: &PathResolutionPerNs, +) -> bool { + if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns { + if is_trait_unused_in_scope(ctx, u, scope, t) { + let path = [path.value_ns, path.macro_ns]; + is_path_unused_in_scope(ctx, u, scope, &path) + } else { + false + } + } else { + let path = [path.type_ns, path.value_ns, path.macro_ns]; + is_path_unused_in_scope(ctx, u, scope, &path) + } +} + +fn is_path_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec<SearchScope>, + path: &[Option<PathResolution>], +) -> bool { + !path + .iter() + .filter_map(|path| *path) + .filter_map(|res| match res { + PathResolution::Def(d) => Some(Definition::from(d)), + _ => None, + }) + .any(|def| used_once_in_scope(ctx, def, u.rename(), scope)) +} + +fn is_trait_unused_in_scope( + ctx: &AssistContext<'_>, + u: &ast::UseTree, + scope: &mut Vec<SearchScope>, + t: &hir::Trait, +) -> bool { + !std::iter::once((Definition::Trait(*t), u.rename())) + .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) + .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) +} + fn used_once_in_scope( ctx: &AssistContext<'_>, def: Definition, @@ -1012,4 +1048,110 @@ fn test(_: Bar) { "#, ); } + + #[test] + fn test_unused_macro() { + check_assist( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /main.rs crate:main deps:foo +use foo::m;$0 +fn main() {} +"#, + r#" +fn main() {} +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /main.rs crate:main deps:foo +use foo::m;$0 +fn main() { + m!(); +} +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- /foo.rs crate:foo +#[macro_export] +macro_rules! m { () => {} } + +//- /bar.rs crate:bar deps:foo +pub use foo::m; +fn m() {} + + +//- /main.rs crate:main deps:bar +use bar::m;$0 +fn main() { + m!(); +} +"#, + ); + } + + #[test] + fn test_conflict_derive_macro() { + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub trait DeriveIdentity {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +#[derive(DeriveIdentity)] +struct S; +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub fn DeriveIdentity() {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +#[derive(DeriveIdentity)] +struct S; +"#, + ); + + check_assist_not_applicable( + remove_unused_imports, + r#" +//- proc_macros: derive_identity +//- minicore: derive +//- /bar.rs crate:bar +pub use proc_macros::DeriveIdentity; +pub fn DeriveIdentity() {} + +//- /main.rs crate:main deps:bar +$0use bar::DeriveIdentity;$0 +fn main() { + DeriveIdentity(); +} +"#, + ); + } } |