diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index ae5251db98..7f44f396bf 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2060,6 +2060,11 @@ impl SemanticsScope<'_> { ) } + pub fn resolve_mod_path(&self, path: &ModPath) -> impl Iterator { + let items = self.resolver.resolve_module_path_in_items(self.db.upcast(), path); + items.iter_items().map(|(item, _)| item.into()) + } + /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). pub fn assoc_type_shorthand_candidates( diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index db909874df..c436528425 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -7,7 +7,7 @@ mod tests; use std::{iter, ops::ControlFlow}; use hir::{ - db::DefDatabase, HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, + HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ @@ -758,15 +758,43 @@ impl<'a> CompletionContext<'a> { }); let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db)) - // `BlockExpr` modules are not count as module depth + // `BlockExpr` modules do not count towards module depth .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_))) .count() // 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); + let exclude_traits: FxHashSet<_> = config + .exclude_traits + .iter() + .filter_map(|path| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .find_map(|it| match it { + hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t), + _ => None, + }) + }) + .collect(); + + let mut exclude_flyimport_traits: FxHashSet<_> = config + .exclude_flyimport_traits + .iter() + .filter_map(|path| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .find_map(|it| match it { + hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t), + _ => None, + }) + }) + .collect(); exclude_flyimport_traits.extend(exclude_traits.iter().copied()); let complete_semicolon = if config.add_semicolon_to_unit { @@ -841,71 +869,6 @@ impl<'a> CompletionContext<'a> { } } -fn resolve_exclude_traits_list(db: &RootDatabase, traits: &[String]) -> FxHashSet { - 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", diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 5265aa8515..04bb178c65 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -100,9 +100,9 @@ // } // ---- +use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; use itertools::Itertools; -use syntax::{ast, AstNode, GreenNode, SyntaxNode}; use crate::context::CompletionContext; @@ -123,10 +123,7 @@ pub struct Snippet { pub scope: SnippetScope, pub description: Option>, snippet: String, - // These are `ast::Path`'s but due to SyntaxNodes not being Send we store these - // and reconstruct them on demand instead. This is cheaper than reparsing them - // from strings - requires: Box<[GreenNode]>, + requires: Box<[ModPath]>, } impl Snippet { @@ -143,7 +140,6 @@ impl Snippet { } let (requires, snippet, description) = validate_snippet(snippet, description, requires)?; Some(Snippet { - // Box::into doesn't work as that has a Copy bound 😒 postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(), prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(), scope, @@ -167,15 +163,11 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option> { +fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { let import_cfg = ctx.config.import_path_config(); - let resolve = |import: &GreenNode| { - let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?; - let item = match ctx.scope.speculative_resolve(&path)? { - hir::PathResolution::Def(def) => def.into(), - _ => return None, - }; + let resolve = |import| { + let item = ctx.scope.resolve_mod_path(import).next()?; let path = ctx.module.find_use_path( ctx.db, item, @@ -198,19 +190,14 @@ fn validate_snippet( snippet: &[String], description: &str, requires: &[String], -) -> Option<(Box<[GreenNode]>, String, Option>)> { +) -> Option<(Box<[ModPath]>, String, Option>)> { let mut imports = Vec::with_capacity(requires.len()); for path in requires.iter() { - let use_path = - ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT_FIXME) - .syntax_node() - .descendants() - .find_map(ast::Path::cast)?; - if use_path.syntax().text() != path.as_str() { - return None; - } - let green = use_path.syntax().green().into_owned(); - imports.push(green); + let use_path = ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + ); + imports.push(use_path); } let snippet = snippet.iter().join("\n"); let description = (!description.is_empty())