Skip to content

Commit 30fe761

Browse files
committed
fix: Removing all unused imports removes used imports for imports used for Derive macros
Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
1 parent 2bafe9d commit 30fe761

File tree

4 files changed

+257
-46
lines changed

4 files changed

+257
-46
lines changed

crates/hir/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ pub use crate::{
9797
diagnostics::*,
9898
has_source::HasSource,
9999
semantics::{
100-
PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits,
100+
PathResolution, PathResolutionPerNs, Semantics, SemanticsImpl, SemanticsScope, TypeInfo,
101+
VisibleTraits,
101102
},
102103
};
103104

crates/hir/src/semantics.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ impl PathResolution {
103103
}
104104
}
105105

106+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
107+
pub struct PathResolutionPerNs {
108+
pub type_ns: Option<PathResolution>,
109+
pub value_ns: Option<PathResolution>,
110+
pub macro_ns: Option<PathResolution>,
111+
}
112+
113+
impl PathResolutionPerNs {
114+
pub fn new(
115+
type_ns: Option<PathResolution>,
116+
value_ns: Option<PathResolution>,
117+
macro_ns: Option<PathResolution>,
118+
) -> Self {
119+
PathResolutionPerNs { type_ns, value_ns, macro_ns }
120+
}
121+
pub fn take_path(&self) -> Option<PathResolution> {
122+
self.type_ns.or(self.value_ns).or(self.macro_ns)
123+
}
124+
}
125+
106126
#[derive(Debug)]
107127
pub struct TypeInfo {
108128
/// The original type of the expression or pattern.
@@ -1606,6 +1626,10 @@ impl<'db> SemanticsImpl<'db> {
16061626
self.resolve_path_with_subst(path).map(|(it, _)| it)
16071627
}
16081628

1629+
pub fn resolve_path_per_ns(&self, path: &ast::Path) -> Option<PathResolutionPerNs> {
1630+
self.analyze(path.syntax())?.resolve_hir_path_per_ns(self.db, path)
1631+
}
1632+
16091633
pub fn resolve_path_with_subst(
16101634
&self,
16111635
path: &ast::Path,

crates/hir/src/source_analyzer.rs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use std::iter::{self, once};
1010
use crate::{
1111
Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
1212
Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
13-
TraitAlias, TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::PathResolution,
13+
TraitAlias, TupleField, Type, TypeAlias, Variant,
14+
db::HirDatabase,
15+
semantics::{PathResolution, PathResolutionPerNs},
1416
};
1517
use either::Either;
1618
use hir_def::{
@@ -1159,7 +1161,9 @@ impl<'db> SourceAnalyzer<'db> {
11591161
prefer_value_ns,
11601162
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
11611163
Some(&store),
1162-
)?;
1164+
false,
1165+
)
1166+
.take_path()?;
11631167
let subst = (|| {
11641168
let parent = parent()?;
11651169
let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) {
@@ -1209,6 +1213,26 @@ impl<'db> SourceAnalyzer<'db> {
12091213
}
12101214
}
12111215

1216+
pub(crate) fn resolve_hir_path_per_ns(
1217+
&self,
1218+
db: &dyn HirDatabase,
1219+
path: &ast::Path,
1220+
) -> Option<PathResolutionPerNs> {
1221+
let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
1222+
let hir_path =
1223+
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
1224+
let store = collector.store.finish();
1225+
Some(resolve_hir_path_(
1226+
db,
1227+
&self.resolver,
1228+
&hir_path,
1229+
false,
1230+
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
1231+
Some(&store),
1232+
true,
1233+
))
1234+
}
1235+
12121236
pub(crate) fn record_literal_missing_fields(
12131237
&self,
12141238
db: &'db dyn HirDatabase,
@@ -1532,7 +1556,7 @@ pub(crate) fn resolve_hir_path(
15321556
hygiene: HygieneId,
15331557
store: Option<&ExpressionStore>,
15341558
) -> Option<PathResolution> {
1535-
resolve_hir_path_(db, resolver, path, false, hygiene, store)
1559+
resolve_hir_path_(db, resolver, path, false, hygiene, store, false).take_path()
15361560
}
15371561

15381562
#[inline]
@@ -1554,7 +1578,8 @@ fn resolve_hir_path_(
15541578
prefer_value_ns: bool,
15551579
hygiene: HygieneId,
15561580
store: Option<&ExpressionStore>,
1557-
) -> Option<PathResolution> {
1581+
resolve_per_ns: bool,
1582+
) -> PathResolutionPerNs {
15581583
let types = || {
15591584
let (ty, unresolved) = match path.type_anchor() {
15601585
Some(type_ref) => resolver.generic_def().and_then(|def| {
@@ -1635,9 +1660,31 @@ fn resolve_hir_path_(
16351660
.map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into())))
16361661
};
16371662

1638-
if prefer_value_ns { values().or_else(types) } else { types().or_else(values) }
1639-
.or_else(items)
1640-
.or_else(macros)
1663+
if resolve_per_ns {
1664+
PathResolutionPerNs {
1665+
type_ns: types().or_else(items),
1666+
value_ns: values(),
1667+
macro_ns: macros(),
1668+
}
1669+
} else {
1670+
let res = if prefer_value_ns {
1671+
values()
1672+
.map(|value_ns| PathResolutionPerNs::new(None, Some(value_ns), None))
1673+
.unwrap_or_else(|| PathResolutionPerNs::new(types(), None, None))
1674+
} else {
1675+
types()
1676+
.map(|type_ns| PathResolutionPerNs::new(Some(type_ns), None, None))
1677+
.unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None))
1678+
};
1679+
1680+
if res.take_path().is_some() {
1681+
res
1682+
} else if let Some(type_ns) = items() {
1683+
PathResolutionPerNs::new(Some(type_ns), None, None)
1684+
} else {
1685+
PathResolutionPerNs::new(None, None, macros())
1686+
}
1687+
}
16411688
}
16421689

16431690
fn resolve_hir_value_path(

crates/ide-assists/src/handlers/remove_unused_imports.rs

Lines changed: 177 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::collections::hash_map::Entry;
22

3-
use hir::{FileRange, InFile, InRealFile, Module, ModuleSource};
3+
use hir::{
4+
FileRange, InFile, InRealFile, Module, ModuleDef, ModuleSource, PathResolution,
5+
PathResolutionPerNs,
6+
};
47
use ide_db::text_edit::TextRange;
58
use ide_db::{
69
FxHashMap, RootDatabase,
@@ -77,49 +80,52 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
7780
};
7881

7982
// Get the actual definition associated with this use item.
80-
let res = match ctx.sema.resolve_path(&path) {
81-
Some(x) => x,
82-
None => {
83+
let res = match ctx.sema.resolve_path_per_ns(&path) {
84+
Some(x) if x.take_path().is_some() => x,
85+
Some(_) | None => {
8386
return None;
8487
}
8588
};
86-
87-
let def = match res {
88-
hir::PathResolution::Def(d) => Definition::from(d),
89-
_ => return None,
90-
};
91-
92-
if u.star_token().is_some() {
93-
// Check if any of the children of this module are used
94-
let def_mod = match def {
95-
Definition::Module(module) => module,
96-
_ => return None,
97-
};
98-
99-
if !def_mod
100-
.scope(ctx.db(), Some(use_module))
101-
.iter()
102-
.filter_map(|(_, x)| match x {
103-
hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
104-
_ => None,
105-
})
106-
.any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
107-
{
108-
return Some(u);
89+
match res {
90+
PathResolutionPerNs { type_ns: Some(type_ns), .. } if u.star_token().is_some() => {
91+
// Check if any of the children of this module are used
92+
let def_mod = match type_ns {
93+
PathResolution::Def(ModuleDef::Module(module)) => module,
94+
_ => return None,
95+
};
96+
97+
if !def_mod
98+
.scope(ctx.db(), Some(use_module))
99+
.iter()
100+
.filter_map(|(_, x)| match x {
101+
hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
102+
_ => None,
103+
})
104+
.any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
105+
{
106+
Some(u)
107+
} else {
108+
None
109+
}
109110
}
110-
} else if let Definition::Trait(ref t) = def {
111-
// If the trait or any item is used.
112-
if !std::iter::once((def, u.rename()))
113-
.chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
114-
.any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
115-
{
116-
return Some(u);
111+
PathResolutionPerNs {
112+
type_ns: Some(PathResolution::Def(ModuleDef::Trait(ref t))),
113+
value_ns,
114+
macro_ns,
115+
} => {
116+
// If the trait or any item is used.
117+
if is_trait_unused_in_scope(ctx, &u, scope, t) {
118+
let path = [value_ns, macro_ns];
119+
is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
120+
} else {
121+
None
122+
}
123+
}
124+
PathResolutionPerNs { type_ns, value_ns, macro_ns } => {
125+
let path = [type_ns, value_ns, macro_ns];
126+
is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
117127
}
118-
} else if !used_once_in_scope(ctx, def, u.rename(), scope) {
119-
return Some(u);
120128
}
121-
122-
None
123129
})
124130
.peekable();
125131

@@ -141,6 +147,33 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
141147
}
142148
}
143149

150+
fn is_path_unused_in_scope(
151+
ctx: &AssistContext<'_>,
152+
u: &ast::UseTree,
153+
scope: &mut Vec<SearchScope>,
154+
path: &[Option<PathResolution>],
155+
) -> bool {
156+
!path
157+
.iter()
158+
.filter_map(|path| *path)
159+
.filter_map(|res| match res {
160+
PathResolution::Def(d) => Some(Definition::from(d)),
161+
_ => None,
162+
})
163+
.any(|def| used_once_in_scope(ctx, def, u.rename(), scope))
164+
}
165+
166+
fn is_trait_unused_in_scope(
167+
ctx: &AssistContext<'_>,
168+
u: &ast::UseTree,
169+
scope: &mut Vec<SearchScope>,
170+
t: &hir::Trait,
171+
) -> bool {
172+
!std::iter::once((Definition::Trait(*t), u.rename()))
173+
.chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
174+
.any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
175+
}
176+
144177
fn used_once_in_scope(
145178
ctx: &AssistContext<'_>,
146179
def: Definition,
@@ -1009,6 +1042,112 @@ fn test(_: Bar) {
10091042
let a = ();
10101043
a.quxx();
10111044
}
1045+
"#,
1046+
);
1047+
}
1048+
1049+
#[test]
1050+
fn test_unused_macro() {
1051+
check_assist(
1052+
remove_unused_imports,
1053+
r#"
1054+
//- /foo.rs crate:foo
1055+
#[macro_export]
1056+
macro_rules! m { () => {} }
1057+
1058+
//- /main.rs crate:main deps:foo
1059+
use foo::m;$0
1060+
fn main() {}
1061+
"#,
1062+
r#"
1063+
fn main() {}
1064+
"#,
1065+
);
1066+
1067+
check_assist_not_applicable(
1068+
remove_unused_imports,
1069+
r#"
1070+
//- /foo.rs crate:foo
1071+
#[macro_export]
1072+
macro_rules! m { () => {} }
1073+
1074+
//- /main.rs crate:main deps:foo
1075+
use foo::m;$0
1076+
fn main() {
1077+
m!();
1078+
}
1079+
"#,
1080+
);
1081+
1082+
check_assist_not_applicable(
1083+
remove_unused_imports,
1084+
r#"
1085+
//- /foo.rs crate:foo
1086+
#[macro_export]
1087+
macro_rules! m { () => {} }
1088+
1089+
//- /bar.rs crate:bar deps:foo
1090+
pub use foo::m;
1091+
fn m() {}
1092+
1093+
1094+
//- /main.rs crate:main deps:bar
1095+
use bar::m;$0
1096+
fn main() {
1097+
m!();
1098+
}
1099+
"#,
1100+
);
1101+
}
1102+
1103+
#[test]
1104+
fn test_conflict_derive_macro() {
1105+
check_assist_not_applicable(
1106+
remove_unused_imports,
1107+
r#"
1108+
//- proc_macros: derive_identity
1109+
//- minicore: derive
1110+
//- /bar.rs crate:bar
1111+
pub use proc_macros::DeriveIdentity;
1112+
pub trait DeriveIdentity {}
1113+
1114+
//- /main.rs crate:main deps:bar
1115+
$0use bar::DeriveIdentity;$0
1116+
#[derive(DeriveIdentity)]
1117+
struct S;
1118+
"#,
1119+
);
1120+
1121+
check_assist_not_applicable(
1122+
remove_unused_imports,
1123+
r#"
1124+
//- proc_macros: derive_identity
1125+
//- minicore: derive
1126+
//- /bar.rs crate:bar
1127+
pub use proc_macros::DeriveIdentity;
1128+
pub fn DeriveIdentity() {}
1129+
1130+
//- /main.rs crate:main deps:bar
1131+
$0use bar::DeriveIdentity;$0
1132+
#[derive(DeriveIdentity)]
1133+
struct S;
1134+
"#,
1135+
);
1136+
1137+
check_assist_not_applicable(
1138+
remove_unused_imports,
1139+
r#"
1140+
//- proc_macros: derive_identity
1141+
//- minicore: derive
1142+
//- /bar.rs crate:bar
1143+
pub use proc_macros::DeriveIdentity;
1144+
pub fn DeriveIdentity() {}
1145+
1146+
//- /main.rs crate:main deps:bar
1147+
$0use bar::DeriveIdentity;$0
1148+
fn main() {
1149+
DeriveIdentity();
1150+
}
10121151
"#,
10131152
);
10141153
}

0 commit comments

Comments
 (0)