Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/context.rs')
| -rw-r--r-- | crates/ide-completion/src/context.rs | 91 |
1 files changed, 87 insertions, 4 deletions
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index f8d403122d..db909874df 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -7,8 +7,8 @@ mod tests; use std::{iter, ops::ControlFlow}; use hir::{ - HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, - Symbol, Type, TypeInfo, + db::DefDatabase, HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, + SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition, @@ -429,7 +429,7 @@ pub(crate) struct CompletionContext<'a> { pub(crate) sema: Semantics<'a, RootDatabase>, pub(crate) scope: SemanticsScope<'a>, pub(crate) db: &'a RootDatabase, - pub(crate) config: &'a CompletionConfig, + pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, /// The token before the cursor, in the original file. @@ -462,6 +462,17 @@ pub(crate) struct CompletionContext<'a> { /// Here depth will be 2 pub(crate) depth_from_crate_root: usize, + /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest + /// importing those traits. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_flyimport_traits: FxHashSet<hir::Trait>, + /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`). + /// They will *not* be excluded, however, if they are available as a generic bound. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_traits: FxHashSet<hir::Trait>, + /// Whether and how to complete semicolon for unit-returning functions. pub(crate) complete_semicolon: CompleteSemicolon, } @@ -670,7 +681,7 @@ impl<'a> CompletionContext<'a> { pub(crate) fn new( db: &'a RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, - config: &'a CompletionConfig, + config: &'a CompletionConfig<'a>, ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); @@ -753,6 +764,11 @@ impl<'a> CompletionContext<'a> { // exclude `m` itself .saturating_sub(1); + let exclude_traits = resolve_exclude_traits_list(db, config.exclude_traits); + let mut exclude_flyimport_traits = + resolve_exclude_traits_list(db, config.exclude_flyimport_traits); + exclude_flyimport_traits.extend(exclude_traits.iter().copied()); + let complete_semicolon = if config.add_semicolon_to_unit { let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| { match_ast! { @@ -817,12 +833,79 @@ impl<'a> CompletionContext<'a> { qualifier_ctx, locals, depth_from_crate_root, + exclude_flyimport_traits, + exclude_traits, complete_semicolon, }; Some((ctx, analysis)) } } +fn resolve_exclude_traits_list(db: &RootDatabase, traits: &[String]) -> FxHashSet<hir::Trait> { + let _g = tracing::debug_span!("resolve_exclude_trait_list", ?traits).entered(); + let crate_graph = db.crate_graph(); + let mut crate_name_to_def_map = FxHashMap::default(); + let mut result = FxHashSet::default(); + 'process_traits: for trait_ in traits { + let mut segments = trait_.split("::").peekable(); + let Some(crate_name) = segments.next() else { + tracing::error!( + ?trait_, + "error resolving trait from traits exclude list: invalid path" + ); + continue; + }; + let Some(def_map) = crate_name_to_def_map.entry(crate_name).or_insert_with(|| { + let krate = crate_graph + .iter() + .find(|&krate| crate_graph[krate].display_name.as_deref() == Some(crate_name)); + let def_map = krate.map(|krate| db.crate_def_map(krate)); + if def_map.is_none() { + tracing::error!( + "error resolving `{trait_}` from trait exclude lists: crate could not be found" + ); + } + def_map + }) else { + // Do not report more than one error for the same crate. + continue; + }; + let mut module = &def_map[hir::DefMap::ROOT]; + let trait_name = 'lookup_mods: { + while let Some(segment) = segments.next() { + if segments.peek().is_none() { + break 'lookup_mods segment; + } + + let Some(&inner) = + module.children.get(&Name::new_symbol_root(hir::Symbol::intern(segment))) + else { + tracing::error!( + "error resolving `{trait_}` from trait exclude lists: could not find module `{segment}`" + ); + continue 'process_traits; + }; + module = &def_map[inner]; + } + + tracing::error!("error resolving `{trait_}` from trait exclude lists: invalid path"); + continue 'process_traits; + }; + let resolved_trait = module + .scope + .trait_by_name(&Name::new_symbol_root(hir::Symbol::intern(trait_name))) + .map(hir::Trait::from); + let Some(resolved_trait) = resolved_trait else { + tracing::error!( + "error resolving `{trait_}` from trait exclude lists: trait could not be found" + ); + continue; + }; + result.insert(resolved_trait); + } + result +} + const OP_TRAIT_LANG_NAMES: &[&str] = &[ "add_assign", "add", |