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.rs | 118 |
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` |