Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/mod_path.rs')
-rw-r--r--crates/hir-expand/src/mod_path.rs118
1 files changed, 80 insertions, 38 deletions
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 47a587e407..0eb1fc1eb5 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -10,6 +10,7 @@ use crate::{
hygiene::{marks_rev, SyntaxContextExt, Transparency},
name::{known, AsName, Name},
span_map::SpanMapRef,
+ tt,
};
use base_db::CrateId;
use smallvec::SmallVec;
@@ -39,7 +40,7 @@ pub enum PathKind {
Crate,
/// Absolute path (::foo)
Abs,
- // FIXME: Remove this
+ // FIXME: Can we remove this somehow?
/// `$crate` from macro expansion
DollarCrate(CrateId),
}
@@ -50,11 +51,16 @@ impl ModPath {
path: ast::Path,
span_map: SpanMapRef<'_>,
) -> Option<ModPath> {
- convert_path(db, None, path, span_map)
+ convert_path(db, path, span_map)
+ }
+
+ pub fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> {
+ convert_path_tt(db, tt)
}
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
- let segments = segments.into_iter().collect();
+ let mut segments: SmallVec<_> = segments.into_iter().collect();
+ segments.shrink_to_fit();
ModPath { kind, segments }
}
@@ -193,22 +199,15 @@ fn display_fmt_path(
fn convert_path(
db: &dyn ExpandDatabase,
- prefix: Option<ModPath>,
path: ast::Path,
span_map: SpanMapRef<'_>,
) -> Option<ModPath> {
- let prefix = match path.qualifier() {
- Some(qual) => Some(convert_path(db, prefix, qual, span_map)?),
- None => prefix,
- };
+ let mut segments = path.segments();
- let segment = path.segment()?;
+ let segment = &segments.next()?;
let mut mod_path = match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => {
if name_ref.text() == "$crate" {
- if prefix.is_some() {
- return None;
- }
ModPath::from_kind(
resolve_crate_root(
db,
@@ -218,41 +217,36 @@ fn convert_path(
.unwrap_or(PathKind::Crate),
)
} else {
- let mut res = prefix.unwrap_or_else(|| {
- ModPath::from_kind(
- segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
- )
- });
+ let mut res = ModPath::from_kind(
+ segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
+ );
res.segments.push(name_ref.as_name());
res
}
}
ast::PathSegmentKind::SelfTypeKw => {
- if prefix.is_some() {
- return None;
- }
ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
}
- ast::PathSegmentKind::CrateKw => {
- if prefix.is_some() {
- return None;
- }
- ModPath::from_segments(PathKind::Crate, iter::empty())
- }
- ast::PathSegmentKind::SelfKw => {
- if prefix.is_some() {
- return None;
- }
- ModPath::from_segments(PathKind::Super(0), iter::empty())
- }
+ ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
+ ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()),
ast::PathSegmentKind::SuperKw => {
- let nested_super_count = match prefix.map(|p| p.kind) {
- Some(PathKind::Super(n)) => n,
- Some(_) => return None,
- None => 0,
- };
+ let mut deg = 1;
+ let mut next_segment = None;
+ while let Some(segment) = segments.next() {
+ match segment.kind()? {
+ ast::PathSegmentKind::SuperKw => deg += 1,
+ ast::PathSegmentKind::Name(name) => {
+ next_segment = Some(name.as_name());
+ break;
+ }
+ ast::PathSegmentKind::Type { .. }
+ | ast::PathSegmentKind::SelfTypeKw
+ | ast::PathSegmentKind::SelfKw
+ | ast::PathSegmentKind::CrateKw => return None,
+ }
+ }
- ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
+ ModPath::from_segments(PathKind::Super(deg), next_segment)
}
ast::PathSegmentKind::Type { .. } => {
// not allowed in imports
@@ -260,6 +254,14 @@ fn convert_path(
}
};
+ for segment in segments {
+ let name = match segment.kind()? {
+ ast::PathSegmentKind::Name(name) => name.as_name(),
+ _ => return None,
+ };
+ mod_path.segments.push(name);
+ }
+
// handle local_inner_macros :
// Basically, even in rustc it is quite hacky:
// https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
@@ -281,6 +283,46 @@ fn convert_path(
Some(mod_path)
}
+fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModPath> {
+ let mut leafs = tt.iter().filter_map(|tt| match tt {
+ tt::TokenTree::Leaf(leaf) => Some(leaf),
+ tt::TokenTree::Subtree(_) => None,
+ });
+ let mut segments = smallvec::smallvec![];
+ let kind = match leafs.next()? {
+ tt::Leaf::Punct(tt::Punct { char: ':', .. }) => match leafs.next()? {
+ tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs,
+ _ => return None,
+ },
+ tt::Leaf::Ident(tt::Ident { text, span }) if text == "$crate" => {
+ resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
+ }
+ tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::Super(0),
+ tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => {
+ let mut deg = 1;
+ while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leafs.next() {
+ if text != "super" {
+ segments.push(Name::new_text_dont_use(text.clone()));
+ break;
+ }
+ deg += 1;
+ }
+ PathKind::Super(deg)
+ }
+ tt::Leaf::Ident(tt::Ident { text, .. }) if text == "crate" => PathKind::Crate,
+ tt::Leaf::Ident(ident) => {
+ segments.push(Name::new_text_dont_use(ident.text.clone()));
+ PathKind::Plain
+ }
+ _ => return None,
+ };
+ segments.extend(leafs.filter_map(|leaf| match leaf {
+ ::tt::Leaf::Ident(ident) => Some(Name::new_text_dont_use(ident.text.clone())),
+ _ => None,
+ }));
+ Some(ModPath { kind, segments })
+}
+
pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<CrateId> {
// When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
// we don't want to pretend that the `macro_rules!` definition is in the `macro`