aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2021-02-28 08:32:15 +0000
committerKirill Bulatov <[email protected]>2021-03-08 21:59:20 +0000
commit89d410cef571f5fa7631b17e2fbe52a8f8f03990 (patch)
tree6c40354741dda2072ef08f1bab9920547277bdd0
parent9482353fa8e1e88cb720a029b9bb6304819c7399 (diff)
Do not propose already imported imports
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs4
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs23
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs43
4 files changed, 45 insertions, 27 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 945638cc5..69370ef3d 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -774,7 +774,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
774/// 774///
775/// Note that if you are wondering "what does this specific existing name mean?", 775/// Note that if you are wondering "what does this specific existing name mean?",
776/// you'd better use the `resolve_` family of methods. 776/// you'd better use the `resolve_` family of methods.
777#[derive(Debug)] 777#[derive(Debug, Clone)]
778pub struct SemanticsScope<'a> { 778pub struct SemanticsScope<'a> {
779 pub db: &'a dyn HirDatabase, 779 pub db: &'a dyn HirDatabase,
780 file_id: HirFileId, 780 file_id: HirFileId,
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index e9993a7cc..182547589 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -111,7 +111,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
111 Some(()) 111 Some(())
112} 112}
113 113
114pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> { 114pub(super) fn find_importable_node<'a>(
115 ctx: &'a AssistContext,
116) -> Option<(ImportAssets<'a>, SyntaxNode)> {
115 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { 117 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
116 ImportAssets::for_exact_path(&path_under_caret, &ctx.sema) 118 ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
117 .zip(Some(path_under_caret.syntax().clone())) 119 .zip(Some(path_under_caret.syntax().clone()))
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index af49fdd26..d6adf70b1 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -48,12 +48,11 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 49//! capability enabled.
50 50
51use hir::{AsAssocItem, ModPath, ScopeDef}; 51use hir::{AsAssocItem, ItemInNs, ModPath, ScopeDef};
52use ide_db::helpers::{ 52use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 53 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 54 insert_use::ImportScope,
55}; 55};
56use rustc_hash::FxHashSet;
57use syntax::{AstNode, SyntaxNode, T}; 56use syntax::{AstNode, SyntaxNode, T};
58 57
59use crate::{ 58use crate::{
@@ -92,19 +91,17 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
92 &ctx.sema, 91 &ctx.sema,
93 )?; 92 )?;
94 93
95 let scope_definitions = scope_definitions(ctx);
96 let mut all_mod_paths = import_assets 94 let mut all_mod_paths = import_assets
97 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) 95 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
98 .into_iter() 96 .into_iter()
99 .map(|import| { 97 .map(|import| {
100 let proposed_def = match import.item_to_display() { 98 let proposed_def = match import.item_to_display() {
101 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), 99 ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
102 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), 100 ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
103 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), 101 ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
104 }; 102 };
105 (import, proposed_def) 103 (import, proposed_def)
106 }) 104 })
107 .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def))
108 .collect::<Vec<_>>(); 105 .collect::<Vec<_>>();
109 all_mod_paths.sort_by_cached_key(|(import, _)| { 106 all_mod_paths.sort_by_cached_key(|(import, _)| {
110 compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased) 107 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)
125 Some(()) 122 Some(())
126} 123}
127 124
128fn 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
136pub(crate) fn position_for_import<'a>( 125pub(crate) fn position_for_import<'a>(
137 ctx: &'a CompletionContext, 126 ctx: &'a CompletionContext,
138 import_candidate: Option<&ImportCandidate>, 127 import_candidate: Option<&ImportCandidate>,
@@ -150,13 +139,14 @@ pub(crate) fn position_for_import<'a>(
150 }) 139 })
151} 140}
152 141
153fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { 142fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<ImportAssets<'a>> {
154 let current_module = ctx.scope.module()?; 143 let current_module = ctx.scope.module()?;
155 if let Some(dot_receiver) = &ctx.dot_receiver { 144 if let Some(dot_receiver) = &ctx.dot_receiver {
156 ImportAssets::for_fuzzy_method_call( 145 ImportAssets::for_fuzzy_method_call(
157 current_module, 146 current_module,
158 ctx.sema.type_of_expr(dot_receiver)?, 147 ctx.sema.type_of_expr(dot_receiver)?,
159 fuzzy_name, 148 fuzzy_name,
149 ctx.scope.clone(),
160 ) 150 )
161 } else { 151 } else {
162 let fuzzy_name_length = fuzzy_name.len(); 152 let fuzzy_name_length = fuzzy_name.len();
@@ -165,6 +155,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
165 ctx.path_qual.clone(), 155 ctx.path_qual.clone(),
166 fuzzy_name, 156 fuzzy_name,
167 &ctx.sema, 157 &ctx.sema,
158 ctx.scope.clone(),
168 )?; 159 )?;
169 160
170 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) 161 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 2e7a183d1..b25786928 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -2,7 +2,7 @@
2use either::Either; 2use either::Either;
3use hir::{ 3use hir::{
4 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, 4 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
5 ModuleDef, PathResolution, PrefixKind, Semantics, Type, 5 ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
6}; 6};
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode}; 8use syntax::{ast, AstNode};
@@ -62,33 +62,38 @@ impl NameToImport {
62} 62}
63 63
64#[derive(Debug)] 64#[derive(Debug)]
65pub struct ImportAssets { 65pub struct ImportAssets<'a> {
66 import_candidate: ImportCandidate, 66 import_candidate: ImportCandidate,
67 module_with_candidate: Module, 67 module_with_candidate: Module,
68 scope: SemanticsScope<'a>,
68} 69}
69 70
70impl ImportAssets { 71impl<'a> ImportAssets<'a> {
71 pub fn for_method_call( 72 pub fn for_method_call(
72 method_call: &ast::MethodCallExpr, 73 method_call: &ast::MethodCallExpr,
73 sema: &Semantics<RootDatabase>, 74 sema: &'a Semantics<RootDatabase>,
74 ) -> Option<Self> { 75 ) -> Option<Self> {
76 let scope = sema.scope(method_call.syntax());
75 Some(Self { 77 Some(Self {
76 import_candidate: ImportCandidate::for_method_call(sema, method_call)?, 78 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,
78 }) 81 })
79 } 82 }
80 83
81 pub fn for_exact_path( 84 pub fn for_exact_path(
82 fully_qualified_path: &ast::Path, 85 fully_qualified_path: &ast::Path,
83 sema: &Semantics<RootDatabase>, 86 sema: &'a Semantics<RootDatabase>,
84 ) -> Option<Self> { 87 ) -> Option<Self> {
85 let syntax_under_caret = fully_qualified_path.syntax(); 88 let syntax_under_caret = fully_qualified_path.syntax();
86 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 89 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
87 return None; 90 return None;
88 } 91 }
92 let scope = sema.scope(syntax_under_caret);
89 Some(Self { 93 Some(Self {
90 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, 94 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,
92 }) 97 })
93 } 98 }
94 99
@@ -97,10 +102,12 @@ impl ImportAssets {
97 qualifier: Option<ast::Path>, 102 qualifier: Option<ast::Path>,
98 fuzzy_name: String, 103 fuzzy_name: String,
99 sema: &Semantics<RootDatabase>, 104 sema: &Semantics<RootDatabase>,
105 scope: SemanticsScope<'a>,
100 ) -> Option<Self> { 106 ) -> Option<Self> {
101 Some(Self { 107 Some(Self {
102 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, 108 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
103 module_with_candidate, 109 module_with_candidate,
110 scope,
104 }) 111 })
105 } 112 }
106 113
@@ -108,6 +115,7 @@ impl ImportAssets {
108 module_with_method_call: Module, 115 module_with_method_call: Module,
109 receiver_ty: Type, 116 receiver_ty: Type,
110 fuzzy_method_name: String, 117 fuzzy_method_name: String,
118 scope: SemanticsScope<'a>,
111 ) -> Option<Self> { 119 ) -> Option<Self> {
112 Some(Self { 120 Some(Self {
113 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { 121 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
@@ -115,6 +123,7 @@ impl ImportAssets {
115 name: NameToImport::Fuzzy(fuzzy_method_name), 123 name: NameToImport::Fuzzy(fuzzy_method_name),
116 }), 124 }),
117 module_with_candidate: module_with_method_call, 125 module_with_candidate: module_with_method_call,
126 scope,
118 }) 127 })
119 } 128 }
120} 129}
@@ -155,7 +164,7 @@ impl LocatedImport {
155 } 164 }
156} 165}
157 166
158impl ImportAssets { 167impl<'a> ImportAssets<'a> {
159 pub fn import_candidate(&self) -> &ImportCandidate { 168 pub fn import_candidate(&self) -> &ImportCandidate {
160 &self.import_candidate 169 &self.import_candidate
161 } 170 }
@@ -189,6 +198,7 @@ impl ImportAssets {
189 prefixed: Option<PrefixKind>, 198 prefixed: Option<PrefixKind>,
190 ) -> Vec<LocatedImport> { 199 ) -> Vec<LocatedImport> {
191 let current_crate = self.module_with_candidate.krate(); 200 let current_crate = self.module_with_candidate.krate();
201 let scope_definitions = self.scope_definitions();
192 202
193 let imports_for_candidate_name = match self.name_to_import() { 203 let imports_for_candidate_name = match self.name_to_import() {
194 NameToImport::Exact(exact_name) => { 204 NameToImport::Exact(exact_name) => {
@@ -219,9 +229,25 @@ impl ImportAssets {
219 self.applicable_defs(sema.db, prefixed, imports_for_candidate_name) 229 self.applicable_defs(sema.db, prefixed, imports_for_candidate_name)
220 .into_iter() 230 .into_iter()
221 .filter(|import| import.import_path().len() > 1) 231 .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 })
222 .collect() 240 .collect()
223 } 241 }
224 242
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
225 fn applicable_defs( 251 fn applicable_defs(
226 &self, 252 &self,
227 db: &RootDatabase, 253 db: &RootDatabase,
@@ -297,7 +323,6 @@ fn path_applicable_imports(
297 Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier), 323 Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => (first_segment, qualifier),
298 }; 324 };
299 325
300 // TODO kb zz.syntax().ast_node() <- two options are now proposed despite the trait being imported
301 let unresolved_qualifier_string = unresolved_qualifier.to_string(); 326 let unresolved_qualifier_string = unresolved_qualifier.to_string();
302 let unresolved_first_segment_string = unresolved_first_segment.to_string(); 327 let unresolved_first_segment_string = unresolved_first_segment.to_string();
303 328