Unnamed repository; edit this file 'description' to name the repository.
Simplify completion config path resolutions
Lukas Wirth 2025-01-01
parent 45954eb · commit c5bda0d
-rw-r--r--crates/hir/src/semantics.rs5
-rw-r--r--crates/ide-completion/src/context.rs103
-rw-r--r--crates/ide-completion/src/snippet.rs35
3 files changed, 49 insertions, 94 deletions
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<Item = ItemInNs> {
+ 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<R>(
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<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",
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<Box<str>>,
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<Vec<LocatedImport>> {
+fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> {
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<Box<str>>)> {
+) -> Option<(Box<[ModPath]>, String, Option<Box<str>>)> {
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())