Skip to content

Commit 89d410c

Browse files
Do not propose already imported imports
1 parent 9482353 commit 89d410c

File tree

4 files changed

+45
-27
lines changed

4 files changed

+45
-27
lines changed

crates/hir/src/semantics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
774774
///
775775
/// Note that if you are wondering "what does this specific existing name mean?",
776776
/// you'd better use the `resolve_` family of methods.
777-
#[derive(Debug)]
777+
#[derive(Debug, Clone)]
778778
pub struct SemanticsScope<'a> {
779779
pub db: &'a dyn HirDatabase,
780780
file_id: HirFileId,

crates/ide_assists/src/handlers/auto_import.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
111111
Some(())
112112
}
113113

114-
pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
114+
pub(super) fn find_importable_node<'a>(
115+
ctx: &'a AssistContext,
116+
) -> Option<(ImportAssets<'a>, SyntaxNode)> {
115117
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
116118
ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
117119
.zip(Some(path_under_caret.syntax().clone()))

crates/ide_completion/src/completions/flyimport.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@
4848
//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
4949
//! capability enabled.
5050
51-
use hir::{AsAssocItem, ModPath, ScopeDef};
51+
use hir::{AsAssocItem, ItemInNs, ModPath, ScopeDef};
5252
use ide_db::helpers::{
5353
import_assets::{ImportAssets, ImportCandidate},
5454
insert_use::ImportScope,
5555
};
56-
use rustc_hash::FxHashSet;
5756
use syntax::{AstNode, SyntaxNode, T};
5857

5958
use crate::{
@@ -92,19 +91,17 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
9291
&ctx.sema,
9392
)?;
9493

95-
let scope_definitions = scope_definitions(ctx);
9694
let mut all_mod_paths = import_assets
9795
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
9896
.into_iter()
9997
.map(|import| {
10098
let proposed_def = match import.item_to_display() {
101-
hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
102-
hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
103-
hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
99+
ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
100+
ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
101+
ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
104102
};
105103
(import, proposed_def)
106104
})
107-
.filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def))
108105
.collect::<Vec<_>>();
109106
all_mod_paths.sort_by_cached_key(|(import, _)| {
110107
compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
@@ -125,14 +122,6 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
125122
Some(())
126123
}
127124

128-
fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
129-
let mut scope_definitions = FxHashSet::default();
130-
ctx.scope.process_all_names(&mut |_, scope_def| {
131-
scope_definitions.insert(scope_def);
132-
});
133-
scope_definitions
134-
}
135-
136125
pub(crate) fn position_for_import<'a>(
137126
ctx: &'a CompletionContext,
138127
import_candidate: Option<&ImportCandidate>,
@@ -150,13 +139,14 @@ pub(crate) fn position_for_import<'a>(
150139
})
151140
}
152141

153-
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
142+
fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<ImportAssets<'a>> {
154143
let current_module = ctx.scope.module()?;
155144
if let Some(dot_receiver) = &ctx.dot_receiver {
156145
ImportAssets::for_fuzzy_method_call(
157146
current_module,
158147
ctx.sema.type_of_expr(dot_receiver)?,
159148
fuzzy_name,
149+
ctx.scope.clone(),
160150
)
161151
} else {
162152
let fuzzy_name_length = fuzzy_name.len();
@@ -165,6 +155,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
165155
ctx.path_qual.clone(),
166156
fuzzy_name,
167157
&ctx.sema,
158+
ctx.scope.clone(),
168159
)?;
169160

170161
if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))

crates/ide_db/src/helpers/import_assets.rs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use either::Either;
33
use hir::{
44
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
5-
ModuleDef, PathResolution, PrefixKind, Semantics, Type,
5+
ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
66
};
77
use rustc_hash::FxHashSet;
88
use syntax::{ast, AstNode};
@@ -62,33 +62,38 @@ impl NameToImport {
6262
}
6363

6464
#[derive(Debug)]
65-
pub struct ImportAssets {
65+
pub struct ImportAssets<'a> {
6666
import_candidate: ImportCandidate,
6767
module_with_candidate: Module,
68+
scope: SemanticsScope<'a>,
6869
}
6970

70-
impl ImportAssets {
71+
impl<'a> ImportAssets<'a> {
7172
pub fn for_method_call(
7273
method_call: &ast::MethodCallExpr,
73-
sema: &Semantics<RootDatabase>,
74+
sema: &'a Semantics<RootDatabase>,
7475
) -> Option<Self> {
76+
let scope = sema.scope(method_call.syntax());
7577
Some(Self {
7678
import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
77-
module_with_candidate: sema.scope(method_call.syntax()).module()?,
79+
module_with_candidate: scope.module()?,
80+
scope,
7881
})
7982
}
8083

8184
pub fn for_exact_path(
8285
fully_qualified_path: &ast::Path,
83-
sema: &Semantics<RootDatabase>,
86+
sema: &'a Semantics<RootDatabase>,
8487
) -> Option<Self> {
8588
let syntax_under_caret = fully_qualified_path.syntax();
8689
if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
8790
return None;
8891
}
92+
let scope = sema.scope(syntax_under_caret);
8993
Some(Self {
9094
import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
91-
module_with_candidate: sema.scope(syntax_under_caret).module()?,
95+
module_with_candidate: scope.module()?,
96+
scope,
9297
})
9398
}
9499

@@ -97,24 +102,28 @@ impl ImportAssets {
97102
qualifier: Option<ast::Path>,
98103
fuzzy_name: String,
99104
sema: &Semantics<RootDatabase>,
105+
scope: SemanticsScope<'a>,
100106
) -> Option<Self> {
101107
Some(Self {
102108
import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
103109
module_with_candidate,
110+
scope,
104111
})
105112
}
106113

107114
pub fn for_fuzzy_method_call(
108115
module_with_method_call: Module,
109116
receiver_ty: Type,
110117
fuzzy_method_name: String,
118+
scope: SemanticsScope<'a>,
111119
) -> Option<Self> {
112120
Some(Self {
113121
import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
114122
receiver_ty,
115123
name: NameToImport::Fuzzy(fuzzy_method_name),
116124
}),
117125
module_with_candidate: module_with_method_call,
126+
scope,
118127
})
119128
}
120129
}
@@ -155,7 +164,7 @@ impl LocatedImport {
155164
}
156165
}
157166

158-
impl ImportAssets {
167+
impl<'a> ImportAssets<'a> {
159168
pub fn import_candidate(&self) -> &ImportCandidate {
160169
&self.import_candidate
161170
}
@@ -189,6 +198,7 @@ impl ImportAssets {
189198
prefixed: Option<PrefixKind>,
190199
) -> Vec<LocatedImport> {
191200
let current_crate = self.module_with_candidate.krate();
201+
let scope_definitions = self.scope_definitions();
192202

193203
let imports_for_candidate_name = match self.name_to_import() {
194204
NameToImport::Exact(exact_name) => {
@@ -219,9 +229,25 @@ impl ImportAssets {
219229
self.applicable_defs(sema.db, prefixed, imports_for_candidate_name)
220230
.into_iter()
221231
.filter(|import| import.import_path().len() > 1)
232+
.filter(|import| {
233+
let proposed_def = match import.item_to_import() {
234+
ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
235+
ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
236+
ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
237+
};
238+
!scope_definitions.contains(&proposed_def)
239+
})
222240
.collect()
223241
}
224242

243+
fn scope_definitions(&self) -> FxHashSet<ScopeDef> {
244+
let mut scope_definitions = FxHashSet::default();
245+
self.scope.process_all_names(&mut |_, scope_def| {
246+
scope_definitions.insert(scope_def);
247+
});
248+
scope_definitions
249+
}
250+
225251
fn applicable_defs(
226252
&self,
227253
db: &RootDatabase,
@@ -297,7 +323,6 @@ fn path_applicable_imports(
297323
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier),
298324
};
299325

300-
// TODO kb zz.syntax().ast_node() <- two options are now proposed despite the trait being imported
301326
let unresolved_qualifier_string = unresolved_qualifier.to_string();
302327
let unresolved_first_segment_string = unresolved_first_segment.to_string();
303328

0 commit comments

Comments
 (0)