mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-01 04:18:20 +00:00
Fix import search not discarding rawness
This commit is contained in:
parent
20ff27e2ba
commit
a06606c802
8 changed files with 65 additions and 29 deletions
|
|
@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
rust-version = "1.82"
|
rust-version = "1.83"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
authors = ["rust-analyzer team"]
|
authors = ["rust-analyzer team"]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use syntax::utils::is_raw_identifier;
|
||||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||||
/// not there yet!
|
/// not there yet!
|
||||||
///
|
///
|
||||||
/// Note that the rawness (`r#`) of names does not depend on whether they are written raw.
|
/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix.
|
||||||
/// This is because we want to show (in completions etc.) names as raw depending on the needs
|
/// This is because we want to show (in completions etc.) names as raw depending on the needs
|
||||||
/// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
|
/// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
|
||||||
/// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
|
/// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
|
||||||
|
|
@ -77,6 +77,7 @@ impl Name {
|
||||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||||
/// future, and to switch to interned representation of names.
|
/// future, and to switch to interned representation of names.
|
||||||
fn new_text(text: &str) -> Name {
|
fn new_text(text: &str) -> Name {
|
||||||
|
debug_assert!(!text.starts_with("r#"));
|
||||||
Name { symbol: Symbol::intern(text), ctx: () }
|
Name { symbol: Symbol::intern(text), ctx: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,15 +92,34 @@ impl Name {
|
||||||
|
|
||||||
pub fn new_root(text: &str) -> Name {
|
pub fn new_root(text: &str) -> Name {
|
||||||
// The edition doesn't matter for hygiene.
|
// The edition doesn't matter for hygiene.
|
||||||
Self::new(text, SyntaxContextId::root(Edition::Edition2015))
|
Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_tuple_field(idx: usize) -> Name {
|
pub fn new_tuple_field(idx: usize) -> Name {
|
||||||
Name { symbol: Symbol::intern(&idx.to_string()), ctx: () }
|
let symbol = match idx {
|
||||||
|
0 => sym::INTEGER_0.clone(),
|
||||||
|
1 => sym::INTEGER_1.clone(),
|
||||||
|
2 => sym::INTEGER_2.clone(),
|
||||||
|
3 => sym::INTEGER_3.clone(),
|
||||||
|
4 => sym::INTEGER_4.clone(),
|
||||||
|
5 => sym::INTEGER_5.clone(),
|
||||||
|
6 => sym::INTEGER_6.clone(),
|
||||||
|
7 => sym::INTEGER_7.clone(),
|
||||||
|
8 => sym::INTEGER_8.clone(),
|
||||||
|
9 => sym::INTEGER_9.clone(),
|
||||||
|
10 => sym::INTEGER_10.clone(),
|
||||||
|
11 => sym::INTEGER_11.clone(),
|
||||||
|
12 => sym::INTEGER_12.clone(),
|
||||||
|
13 => sym::INTEGER_13.clone(),
|
||||||
|
14 => sym::INTEGER_14.clone(),
|
||||||
|
15 => sym::INTEGER_15.clone(),
|
||||||
|
_ => Symbol::intern(&idx.to_string()),
|
||||||
|
};
|
||||||
|
Name { symbol, ctx: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
|
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
|
||||||
Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () }
|
Self::new_text(lt.text().as_str().trim_start_matches("r#"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a name from the text of token.
|
/// Resolve a name from the text of token.
|
||||||
|
|
@ -142,15 +162,18 @@ impl Name {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the text this name represents if it isn't a tuple field.
|
/// Returns the text this name represents if it isn't a tuple field.
|
||||||
|
///
|
||||||
|
/// Do not use this for user-facing text, use `display` instead to handle editions properly.
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.symbol.as_str()
|
self.symbol.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove this
|
||||||
pub fn unescaped(&self) -> UnescapedName<'_> {
|
pub fn unescaped(&self) -> UnescapedName<'_> {
|
||||||
UnescapedName(self)
|
UnescapedName(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_escaped(&self, edition: Edition) -> bool {
|
pub fn needs_escape(&self, edition: Edition) -> bool {
|
||||||
is_raw_identifier(self.symbol.as_str(), edition)
|
is_raw_identifier(self.symbol.as_str(), edition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,16 +196,19 @@ impl Name {
|
||||||
&self.symbol
|
&self.symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
|
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
|
||||||
|
debug_assert!(!symbol.as_str().starts_with("r#"));
|
||||||
_ = ctx;
|
_ = ctx;
|
||||||
Self { symbol, ctx: () }
|
Self { symbol, ctx: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This needs to go once we have hygiene
|
// FIXME: This needs to go once we have hygiene
|
||||||
pub const fn new_symbol_root(sym: Symbol) -> Self {
|
pub fn new_symbol_root(sym: Symbol) -> Self {
|
||||||
|
debug_assert!(!sym.as_str().starts_with("r#"));
|
||||||
Self { symbol: sym, ctx: () }
|
Self { symbol: sym, ctx: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove this
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn eq_ident(&self, ident: &str) -> bool {
|
pub fn eq_ident(&self, ident: &str) -> bool {
|
||||||
self.as_str() == ident.trim_start_matches("r#")
|
self.as_str() == ident.trim_start_matches("r#")
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ use stdx::TupleExt;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::skip_trivia_token,
|
algo::skip_trivia_token,
|
||||||
ast::{self, HasAttrs as _, HasGenericParams},
|
ast::{self, HasAttrs as _, HasGenericParams},
|
||||||
AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
|
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
||||||
TextRange, TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
|
@ -1587,14 +1587,11 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
pub fn resolve_mod_path_relative(
|
pub fn resolve_mod_path_relative(
|
||||||
&self,
|
&self,
|
||||||
to: Module,
|
to: Module,
|
||||||
segments: impl IntoIterator<Item = SmolStr>,
|
segments: impl IntoIterator<Item = Name>,
|
||||||
) -> Option<impl Iterator<Item = ItemInNs>> {
|
) -> Option<impl Iterator<Item = ItemInNs>> {
|
||||||
let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
|
let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
|
||||||
self.db.upcast(),
|
self.db.upcast(),
|
||||||
&ModPath::from_segments(
|
&ModPath::from_segments(hir_def::path::PathKind::Plain, segments),
|
||||||
hir_def::path::PathKind::Plain,
|
|
||||||
segments.into_iter().map(|it| Name::new_root(&it)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Some(items.iter_items().map(|(item, _)| item.into()))
|
Some(items.iter_items().map(|(item, _)| item.into()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use ide_db::imports::{
|
||||||
insert_use::ImportScope,
|
insert_use::ImportScope,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T};
|
use syntax::{ast, AstNode, SyntaxNode, ToSmolStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::AutoImportExclusionType,
|
config::AutoImportExclusionType,
|
||||||
|
|
@ -403,10 +403,11 @@ fn import_on_the_fly_method(
|
||||||
|
|
||||||
fn import_name(ctx: &CompletionContext<'_>) -> String {
|
fn import_name(ctx: &CompletionContext<'_>) -> String {
|
||||||
let token_kind = ctx.token.kind();
|
let token_kind = ctx.token.kind();
|
||||||
if matches!(token_kind, T![.] | T![::]) {
|
|
||||||
String::new()
|
if token_kind.is_any_identifier() {
|
||||||
} else {
|
|
||||||
ctx.token.to_string()
|
ctx.token.to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,7 @@ fn render_resolution_path(
|
||||||
|
|
||||||
let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
|
let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
|
||||||
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
|
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
|
||||||
if local_name.is_escaped(completion.edition) {
|
if local_name.needs_escape(completion.edition) {
|
||||||
item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
|
item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
|
||||||
}
|
}
|
||||||
// Add `<>` for generic types
|
// Add `<>` for generic types
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ use std::ops::ControlFlow;
|
||||||
|
|
||||||
use hir::{
|
use hir::{
|
||||||
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
|
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
|
||||||
ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics,
|
ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
|
||||||
SemanticsScope, Trait, TyFingerprint, Type,
|
SemanticsScope, Trait, TyFingerprint, Type,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make, HasName},
|
ast::{self, make, HasName},
|
||||||
AstNode, SmolStr, SyntaxNode,
|
AstNode, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -53,7 +53,7 @@ pub struct TraitImportCandidate {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PathImportCandidate {
|
pub struct PathImportCandidate {
|
||||||
/// Optional qualifier before name.
|
/// Optional qualifier before name.
|
||||||
pub qualifier: Vec<SmolStr>,
|
pub qualifier: Vec<Name>,
|
||||||
/// The name the item (struct, trait, enum, etc.) should have.
|
/// The name the item (struct, trait, enum, etc.) should have.
|
||||||
pub name: NameToImport,
|
pub name: NameToImport,
|
||||||
}
|
}
|
||||||
|
|
@ -72,10 +72,18 @@ pub enum NameToImport {
|
||||||
|
|
||||||
impl NameToImport {
|
impl NameToImport {
|
||||||
pub fn exact_case_sensitive(s: String) -> NameToImport {
|
pub fn exact_case_sensitive(s: String) -> NameToImport {
|
||||||
|
let s = match s.strip_prefix("r#") {
|
||||||
|
Some(s) => s.to_owned(),
|
||||||
|
None => s,
|
||||||
|
};
|
||||||
NameToImport::Exact(s, true)
|
NameToImport::Exact(s, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fuzzy(s: String) -> NameToImport {
|
pub fn fuzzy(s: String) -> NameToImport {
|
||||||
|
let s = match s.strip_prefix("r#") {
|
||||||
|
Some(s) => s.to_owned(),
|
||||||
|
None => s,
|
||||||
|
};
|
||||||
// unless all chars are lowercase, we do a case sensitive search
|
// unless all chars are lowercase, we do a case sensitive search
|
||||||
let case_sensitive = s.chars().any(|c| c.is_uppercase());
|
let case_sensitive = s.chars().any(|c| c.is_uppercase());
|
||||||
NameToImport::Fuzzy(s, case_sensitive)
|
NameToImport::Fuzzy(s, case_sensitive)
|
||||||
|
|
@ -359,7 +367,7 @@ fn path_applicable_imports(
|
||||||
[first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
|
[first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
|
||||||
sema,
|
sema,
|
||||||
current_crate,
|
current_crate,
|
||||||
NameToImport::Exact(first_qsegment.to_string(), true),
|
NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
|
||||||
AssocSearchMode::Exclude,
|
AssocSearchMode::Exclude,
|
||||||
)
|
)
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
|
|
@ -389,7 +397,7 @@ fn validate_resolvable(
|
||||||
scope_filter: impl Fn(ItemInNs) -> bool,
|
scope_filter: impl Fn(ItemInNs) -> bool,
|
||||||
candidate: &NameToImport,
|
candidate: &NameToImport,
|
||||||
resolved_qualifier: ItemInNs,
|
resolved_qualifier: ItemInNs,
|
||||||
unresolved_qualifier: &[SmolStr],
|
unresolved_qualifier: &[Name],
|
||||||
) -> Option<LocatedImport> {
|
) -> Option<LocatedImport> {
|
||||||
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
|
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
|
||||||
|
|
||||||
|
|
@ -722,7 +730,7 @@ fn path_import_candidate(
|
||||||
if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
|
if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
|
||||||
let qualifier = qualifier
|
let qualifier = qualifier
|
||||||
.segments()
|
.segments()
|
||||||
.map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
|
.map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
|
||||||
.collect::<Option<Vec<_>>>()?;
|
.collect::<Option<Vec<_>>>()?;
|
||||||
ImportCandidate::Path(PathImportCandidate { qualifier, name })
|
ImportCandidate::Path(PathImportCandidate { qualifier, name })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -141,10 +141,11 @@ fn find_items<'a>(
|
||||||
// Query the local crate using the symbol index.
|
// Query the local crate using the symbol index.
|
||||||
let mut local_results = Vec::new();
|
let mut local_results = Vec::new();
|
||||||
local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| {
|
local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| {
|
||||||
ControlFlow::<()>::Continue(local_results.push(match local_candidate.def {
|
local_results.push(match local_candidate.def {
|
||||||
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
|
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
|
||||||
def => ItemInNs::from(def),
|
def => ItemInNs::from(def),
|
||||||
}))
|
});
|
||||||
|
ControlFlow::<()>::Continue(())
|
||||||
});
|
});
|
||||||
local_results.into_iter().chain(external_importables)
|
local_results.into_iter().chain(external_importables)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,10 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
query.search::<()>(&indices, |f| ControlFlow::Continue(res.push(f.clone())));
|
query.search::<()>(&indices, |f| {
|
||||||
|
res.push(f.clone());
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
});
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue