Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir/src/symbols.rs44
-rw-r--r--crates/ide-completion/src/item.rs12
-rw-r--r--crates/ide-completion/src/snippet.rs2
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs26
-rw-r--r--crates/ide-db/src/imports/import_assets.rs202
-rw-r--r--crates/ide-db/src/items_locator.rs8
-rw-r--r--crates/ide-db/src/symbol_index.rs2
-rw-r--r--crates/ide-db/src/test_data/test_doc_alias.txt7
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt29
-rw-r--r--crates/syntax/src/ast/node_ext.rs13
-rw-r--r--crates/syntax/src/utils.rs42
11 files changed, 196 insertions, 191 deletions
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index 03112f6de5..a2a30edeb0 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -23,6 +23,7 @@ pub struct FileSymbol {
pub loc: DeclarationLocation,
pub container_name: Option<SmolStr>,
pub is_alias: bool,
+ pub is_assoc: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -121,34 +122,34 @@ impl<'a> SymbolCollector<'a> {
match module_def_id {
ModuleDefId::ModuleId(id) => self.push_module(id),
ModuleDefId::FunctionId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
self.collect_from_body(id);
}
- ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id),
- ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id),
- ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id),
+ ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false),
+ ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false),
+ ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false),
ModuleDefId::ConstId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
self.collect_from_body(id);
}
ModuleDefId::StaticId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
self.collect_from_body(id);
}
ModuleDefId::TraitId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
self.collect_from_trait(id);
}
ModuleDefId::TraitAliasId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
}
ModuleDefId::TypeAliasId(id) => {
- self.push_decl(id);
+ self.push_decl(id, false);
}
ModuleDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => self.push_decl(id),
- MacroId::MacroRulesId(id) => self.push_decl(id),
- MacroId::ProcMacroId(id) => self.push_decl(id),
+ MacroId::Macro2Id(id) => self.push_decl(id, false),
+ MacroId::MacroRulesId(id) => self.push_decl(id, false),
+ MacroId::ProcMacroId(id) => self.push_decl(id, false),
},
// Don't index these.
ModuleDefId::BuiltinType(_) => {}
@@ -190,6 +191,7 @@ impl<'a> SymbolCollector<'a> {
container_name: self.current_container_name.clone(),
loc: dec_loc,
is_alias: false,
+ is_assoc: false,
});
});
}
@@ -202,9 +204,9 @@ impl<'a> SymbolCollector<'a> {
for &id in id {
if id.module(self.db.upcast()) == module_id {
match id {
- MacroId::Macro2Id(id) => self.push_decl(id),
- MacroId::MacroRulesId(id) => self.push_decl(id),
- MacroId::ProcMacroId(id) => self.push_decl(id),
+ MacroId::Macro2Id(id) => self.push_decl(id, false),
+ MacroId::MacroRulesId(id) => self.push_decl(id, false),
+ MacroId::ProcMacroId(id) => self.push_decl(id, false),
}
}
}
@@ -266,13 +268,13 @@ impl<'a> SymbolCollector<'a> {
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
match assoc_item_id {
- AssocItemId::FunctionId(id) => self.push_decl(id),
- AssocItemId::ConstId(id) => self.push_decl(id),
- AssocItemId::TypeAliasId(id) => self.push_decl(id),
+ AssocItemId::FunctionId(id) => self.push_decl(id, true),
+ AssocItemId::ConstId(id) => self.push_decl(id, true),
+ AssocItemId::TypeAliasId(id) => self.push_decl(id, true),
}
}
- fn push_decl<L>(&mut self, id: L)
+ fn push_decl<L>(&mut self, id: L, is_assoc: bool)
where
L: Lookup + Into<ModuleDefId>,
<L as Lookup>::Data: HasSource,
@@ -296,6 +298,7 @@ impl<'a> SymbolCollector<'a> {
loc: dec_loc.clone(),
container_name: self.current_container_name.clone(),
is_alias: true,
+ is_assoc,
});
}
}
@@ -306,6 +309,7 @@ impl<'a> SymbolCollector<'a> {
container_name: self.current_container_name.clone(),
loc: dec_loc,
is_alias: false,
+ is_assoc,
});
}
@@ -331,6 +335,7 @@ impl<'a> SymbolCollector<'a> {
loc: dec_loc.clone(),
container_name: self.current_container_name.clone(),
is_alias: true,
+ is_assoc: false,
});
}
}
@@ -341,6 +346,7 @@ impl<'a> SymbolCollector<'a> {
container_name: self.current_container_name.clone(),
loc: dec_loc,
is_alias: false,
+ is_assoc: false,
});
}
}
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index b982322a73..de41a5bd70 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -458,13 +458,11 @@ impl Builder {
}
if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one
- if let Some(original_path) = import_edit.original_path.as_ref() {
- label_detail.replace(SmolStr::from(format!(
- "{} (use {})",
- label_detail.as_deref().unwrap_or_default(),
- original_path.display(db)
- )));
- }
+ label_detail.replace(SmolStr::from(format!(
+ "{} (use {})",
+ label_detail.as_deref().unwrap_or_default(),
+ import_edit.import_path.display(db)
+ )));
} else if let Some(trait_name) = self.trait_name {
label_detail.replace(SmolStr::from(format!(
"{} (as {trait_name})",
diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs
index 50618296ee..e667e2e016 100644
--- a/crates/ide-completion/src/snippet.rs
+++ b/crates/ide-completion/src/snippet.rs
@@ -181,7 +181,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<V
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
)?;
- Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item, None)))
+ Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item)))
};
let mut res = Vec::with_capacity(requires.len());
for import in requires {
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 9a4a94a245..c58374f2e8 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -597,8 +597,8 @@ fn main() {
}
"#,
expect![[r#"
- ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
+ ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
"#]],
);
}
@@ -717,7 +717,7 @@ fn main() {
check(
fixture,
expect![[r#"
- st Item (use foo::bar::baz::Item) Item
+ st Item (use foo::bar) Item
"#]],
);
@@ -725,19 +725,19 @@ fn main() {
"Item",
fixture,
r#"
- use foo::bar;
+use foo::bar;
- mod foo {
- pub mod bar {
- pub mod baz {
- pub struct Item;
- }
- }
+mod foo {
+ pub mod bar {
+ pub mod baz {
+ pub struct Item;
}
+ }
+}
- fn main() {
- bar::baz::Item
- }"#,
+fn main() {
+ bar::baz::Item
+}"#,
);
}
@@ -803,7 +803,7 @@ fn main() {
check(
fixture,
expect![[r#"
- ct TEST_ASSOC (use foo::bar::Item) usize
+ ct TEST_ASSOC (use foo::bar) usize
"#]],
);
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 04263d15d0..a4f0a6df78 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -1,14 +1,14 @@
//! Look up accessible paths for items.
+
use hir::{
- AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef,
+ AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name,
PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
};
-use itertools::Itertools;
-use rustc_hash::FxHashSet;
+use itertools::{EitherOrBoth, Itertools};
+use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{self, make, HasName},
- utils::path_to_string_stripping_turbo_fish,
- AstNode, SyntaxNode,
+ AstNode, SmolStr, SyntaxNode,
};
use crate::{
@@ -51,18 +51,11 @@ pub struct TraitImportCandidate {
#[derive(Debug)]
pub struct PathImportCandidate {
/// Optional qualifier before name.
- pub qualifier: Option<FirstSegmentUnresolved>,
+ pub qualifier: Option<Vec<SmolStr>>,
/// The name the item (struct, trait, enum, etc.) should have.
pub name: NameToImport,
}
-/// A qualifier that has a first segment and it's unresolved.
-#[derive(Debug)]
-pub struct FirstSegmentUnresolved {
- fist_segment: ast::NameRef,
- full_qualifier: ast::Path,
-}
-
/// A name that will be used during item lookups.
#[derive(Debug, Clone)]
pub enum NameToImport {
@@ -195,18 +188,11 @@ pub struct LocatedImport {
/// the original item is the associated constant, but the import has to be a trait that
/// defines this constant.
pub original_item: ItemInNs,
- /// A path of the original item.
- pub original_path: Option<ModPath>,
}
impl LocatedImport {
- pub fn new(
- import_path: ModPath,
- item_to_import: ItemInNs,
- original_item: ItemInNs,
- original_path: Option<ModPath>,
- ) -> Self {
- Self { import_path, item_to_import, original_item, original_path }
+ pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self {
+ Self { import_path, item_to_import, original_item }
}
}
@@ -351,64 +337,75 @@ fn path_applicable_imports(
)
.filter_map(|item| {
let mod_path = mod_path(item)?;
- Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path)))
- })
- .collect()
- }
- Some(first_segment_unresolved) => {
- let unresolved_qualifier =
- path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier);
- let unresolved_first_segment = first_segment_unresolved.fist_segment.text();
- items_locator::items_with_name(
- sema,
- current_crate,
- path_candidate.name.clone(),
- AssocSearchMode::Include,
- Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
- )
- .filter_map(|item| {
- import_for_item(
- sema.db,
- mod_path,
- &unresolved_first_segment,
- &unresolved_qualifier,
- item,
- )
+ Some(LocatedImport::new(mod_path, item, item))
})
.collect()
}
+ Some(qualifier) => items_locator::items_with_name(
+ sema,
+ current_crate,
+ path_candidate.name.clone(),
+ AssocSearchMode::Include,
+ Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
+ )
+ .filter_map(|item| import_for_item(sema.db, mod_path, &qualifier, item))
+ .collect(),
}
}
fn import_for_item(
db: &RootDatabase,
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
- unresolved_first_segment: &str,
- unresolved_qualifier: &str,
+ unresolved_qualifier: &[SmolStr],
original_item: ItemInNs,
) -> Option<LocatedImport> {
let _p = profile::span("import_assets::import_for_item");
+ let [first_segment, ..] = unresolved_qualifier else { return None };
- let original_item_candidate = item_for_path_search(db, original_item)?;
+ let item_as_assoc = item_as_assoc(db, original_item);
+
+ let (original_item_candidate, trait_item_to_import) = match item_as_assoc {
+ Some(assoc_item) => match assoc_item.container(db) {
+ AssocItemContainer::Trait(trait_) => {
+ let trait_ = ItemInNs::from(ModuleDef::from(trait_));
+ (trait_, Some(trait_))
+ }
+ AssocItemContainer::Impl(impl_) => {
+ (ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), None)
+ }
+ },
+ None => (original_item, None),
+ };
let import_path_candidate = mod_path(original_item_candidate)?;
- let import_path_string = import_path_candidate.display(db).to_string();
- let expected_import_end = if item_as_assoc(db, original_item).is_some() {
- unresolved_qualifier.to_string()
- } else {
- format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db))
+ let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev();
+ let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it {
+ // segments match, check next one
+ EitherOrBoth::Both(a, b) if b.as_str() == Some(&**a) => None,
+ // segments mismatch / qualifier is longer than the path, bail out
+ EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false),
+ // all segments match and we have exhausted the qualifier, proceed
+ EitherOrBoth::Right(_) => Some(true),
};
- if !import_path_string.contains(unresolved_first_segment)
- || !import_path_string.ends_with(&expected_import_end)
- {
+ if item_as_assoc.is_none() {
+ let item_name = item_name(db, original_item)?.as_text()?;
+ let last_segment = import_path_candidate_segments.next()?;
+ if last_segment.as_str() != Some(&*item_name) {
+ return None;
+ }
+ }
+ let ends_with = unresolved_qualifier
+ .iter()
+ .rev()
+ .zip_longest(import_path_candidate_segments)
+ .find_map(predicate)
+ .unwrap_or(true);
+ if !ends_with {
return None;
}
- let segment_import =
- find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?;
- let trait_item_to_import = item_as_assoc(db, original_item)
- .and_then(|assoc| assoc.containing_trait(db))
- .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
+ let segment_import = find_import_for_segment(db, original_item_candidate, first_segment)?;
+
Some(match (segment_import == original_item_candidate, trait_item_to_import) {
(true, Some(_)) => {
// FIXME we should be able to import both the trait and the segment,
@@ -416,42 +413,37 @@ fn import_for_item(
// especially in case of lazy completion edit resolutions.
return None;
}
- (false, Some(trait_to_import)) => LocatedImport::new(
- mod_path(trait_to_import)?,
- trait_to_import,
- original_item,
- mod_path(original_item),
- ),
- (true, None) => LocatedImport::new(
- import_path_candidate,
- original_item_candidate,
- original_item,
- mod_path(original_item),
- ),
- (false, None) => LocatedImport::new(
- mod_path(segment_import)?,
- segment_import,
- original_item,
- mod_path(original_item),
- ),
+ (false, Some(trait_to_import)) => {
+ LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
+ }
+ (true, None) => {
+ LocatedImport::new(import_path_candidate, original_item_candidate, original_item)
+ }
+ (false, None) => {
+ LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
+ }
})
}
pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
Some(match item {
ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
- Some(assoc_item) => match assoc_item.container(db) {
- AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
- AssocItemContainer::Impl(impl_) => {
- ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
- }
- },
+ Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?,
None => item,
},
ItemInNs::Macros(_) => item,
})
}
+fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option<ItemInNs> {
+ Some(match assoc_item.container(db) {
+ AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
+ AssocItemContainer::Impl(impl_) => {
+ ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?))
+ }
+ })
+}
+
fn find_import_for_segment(
db: &RootDatabase,
original_item: ItemInNs,
@@ -528,6 +520,7 @@ fn trait_applicable_items(
.collect();
let mut located_imports = FxHashSet::default();
+ let mut trait_import_paths = FxHashMap::default();
if trait_assoc_item {
trait_candidate.receiver_ty.iterate_path_candidates(
@@ -545,12 +538,14 @@ fn trait_applicable_items(
}
let located_trait = assoc.containing_trait(db)?;
let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
- let original_item = assoc_to_item(assoc);
+ let import_path = trait_import_paths
+ .entry(trait_item)
+ .or_insert_with(|| mod_path(trait_item))
+ .clone()?;
located_imports.insert(LocatedImport::new(
- mod_path(trait_item)?,
+ import_path,
trait_item,
- original_item,
- mod_path(original_item),
+ assoc_to_item(assoc),
));
}
None::<()>
@@ -568,12 +563,14 @@ fn trait_applicable_items(
if required_assoc_items.contains(&assoc) {
let located_trait = assoc.containing_trait(db)?;
let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
- let original_item = assoc_to_item(assoc);
+ let import_path = trait_import_paths
+ .entry(trait_item)
+ .or_insert_with(|| mod_path(trait_item))
+ .clone()?;
located_imports.insert(LocatedImport::new(
- mod_path(trait_item)?,
+ import_path,
trait_item,
- original_item,
- mod_path(original_item),
+ assoc_to_item(assoc),
));
}
None::<()>
@@ -671,18 +668,13 @@ fn path_import_candidate(
Some(match qualifier {
Some(qualifier) => match sema.resolve_path(&qualifier) {
None => {
- let qualifier_start =
- qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
- let qualifier_start_path =
- qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
- if sema.resolve_path(&qualifier_start_path).is_none() {
- ImportCandidate::Path(PathImportCandidate {
- qualifier: Some(FirstSegmentUnresolved {
- fist_segment: qualifier_start,
- full_qualifier: qualifier,
- }),
- name,
- })
+ if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) {
+ let mut qualifier = qualifier
+ .segments_of_this_path_only_rev()
+ .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
+ .collect::<Option<Vec<_>>>()?;
+ qualifier.reverse();
+ ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name })
} else {
return None;
}
diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs
index 67ed44f08b..022b167d49 100644
--- a/crates/ide-db/src/items_locator.rs
+++ b/crates/ide-db/src/items_locator.rs
@@ -3,13 +3,13 @@
//! The main reason for this module to exist is the fact that project's items and dependencies' items
//! are located in different caches, with different APIs.
use either::Either;
-use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics};
+use hir::{import_map, Crate, ItemInNs, Semantics};
use limit::Limit;
use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
/// A value to use, when uncertain which limit to pick.
-pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
+pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
pub use import_map::AssocSearchMode;
@@ -101,8 +101,8 @@ fn find_items<'a>(
.into_iter()
.filter(move |candidate| match assoc_item_search {
AssocSearchMode::Include => true,
- AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(),
- AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(),
+ AssocSearchMode::Exclude => !candidate.is_assoc,
+ AssocSearchMode::AssocItemsOnly => candidate.is_assoc,
})
.map(|local_candidate| match local_candidate.def {
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index 3e89159c2c..be8566b759 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -50,7 +50,7 @@ enum SearchMode {
Prefix,
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Query {
query: String,
lowercased: String,
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index 4a72881fe5..7c01ac0693 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -36,6 +36,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Struct",
@@ -65,6 +66,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "mul1",
@@ -94,6 +96,7 @@
},
container_name: None,
is_alias: true,
+ is_assoc: false,
},
FileSymbol {
name: "mul2",
@@ -123,6 +126,7 @@
},
container_name: None,
is_alias: true,
+ is_assoc: false,
},
FileSymbol {
name: "s1",
@@ -152,6 +156,7 @@
},
container_name: None,
is_alias: true,
+ is_assoc: false,
},
FileSymbol {
name: "s1",
@@ -181,6 +186,7 @@
},
container_name: None,
is_alias: true,
+ is_assoc: false,
},
FileSymbol {
name: "s2",
@@ -210,6 +216,7 @@
},
container_name: None,
is_alias: true,
+ is_assoc: false,
},
],
),
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index da1f3167d7..c9875c7f8f 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -34,6 +34,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "CONST",
@@ -61,6 +62,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "CONST_WITH_INNER",
@@ -88,6 +90,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Enum",
@@ -117,6 +120,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "ItemLikeMacro",
@@ -146,6 +150,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Macro",
@@ -175,6 +180,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "STATIC",
@@ -202,6 +208,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Struct",
@@ -231,6 +238,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "StructFromMacro",
@@ -260,6 +268,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "StructInFn",
@@ -291,6 +300,7 @@
"main",
),
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "StructInNamedConst",
@@ -322,6 +332,7 @@
"CONST_WITH_INNER",
),
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "StructInUnnamedConst",
@@ -351,6 +362,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Trait",
@@ -378,6 +390,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Trait",
@@ -407,6 +420,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "Union",
@@ -436,6 +450,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "a_mod",
@@ -465,6 +480,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "b_mod",
@@ -494,6 +510,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "define_struct",
@@ -523,6 +540,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "impl_fn",
@@ -550,6 +568,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: true,
},
FileSymbol {
name: "macro_rules_macro",
@@ -579,6 +598,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "main",
@@ -606,6 +626,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "really_define_struct",
@@ -635,6 +656,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "trait_fn",
@@ -664,6 +686,7 @@
"Trait",
),
is_alias: false,
+ is_assoc: true,
},
],
),
@@ -704,6 +727,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
],
),
@@ -744,6 +768,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "StructInModB",
@@ -773,6 +798,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "SuperItemLikeMacro",
@@ -802,6 +828,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "ThisStruct",
@@ -831,6 +858,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
FileSymbol {
name: "ThisStruct",
@@ -860,6 +888,7 @@
},
container_name: None,
is_alias: false,
+ is_assoc: false,
},
],
),
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index be5b954ad3..f81dff8840 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -275,10 +275,19 @@ impl ast::Path {
successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
}
+ pub fn first_qualifier(&self) -> Option<ast::Path> {
+ successors(self.qualifier(), ast::Path::qualifier).last()
+ }
+
pub fn first_segment(&self) -> Option<ast::PathSegment> {
self.first_qualifier_or_self().segment()
}
+ // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function
+ pub fn segments_of_this_path_only_rev(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
+ self.qualifiers_and_self().filter_map(|it| it.segment())
+ }
+
pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
successors(self.first_segment(), |p| {
p.parent_path().parent_path().and_then(|p| p.segment())
@@ -289,6 +298,10 @@ impl ast::Path {
successors(self.qualifier(), |p| p.qualifier())
}
+ pub fn qualifiers_and_self(&self) -> impl Iterator<Item = ast::Path> + Clone {
+ successors(Some(self.clone()), |p| p.qualifier())
+ }
+
pub fn top_path(&self) -> ast::Path {
let mut this = self.clone();
while let Some(path) = this.parent_path() {
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
index 25f34ea9d3..a38f8b2b55 100644
--- a/crates/syntax/src/utils.rs
+++ b/crates/syntax/src/utils.rs
@@ -1,48 +1,8 @@
//! A set of utils methods to reuse on other abstraction levels
-use itertools::Itertools;
-
-use crate::{ast, match_ast, AstNode, SyntaxKind};
-
-pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
- path.syntax()
- .children()
- .filter_map(|node| {
- match_ast! {
- match node {
- ast::PathSegment(it) => {
- Some(it.name_ref()?.to_string())
- },
- ast::Path(it) => {
- Some(path_to_string_stripping_turbo_fish(&it))
- },
- _ => None,
- }
- }
- })
- .join("::")
-}
+use crate::SyntaxKind;
pub fn is_raw_identifier(name: &str) -> bool {
let is_keyword = SyntaxKind::from_keyword(name).is_some();
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
}
-
-#[cfg(test)]
-mod tests {
- use super::path_to_string_stripping_turbo_fish;
- use crate::ast::make;
-
- #[test]
- fn turbofishes_are_stripped() {
- assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
- assert_eq!(
- "Vec::new",
- path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
- );
- assert_eq!(
- "Vec::new",
- path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
- );
- }
-}