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 | 69 |
1 files changed, 61 insertions, 8 deletions
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index d84d978cdb..51e449fe50 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -2,7 +2,7 @@ use std::{ fmt::{self, Display as _}, - iter, + iter::{self, Peekable}, }; use crate::{ @@ -12,10 +12,11 @@ use crate::{ tt, }; use base_db::Crate; -use intern::sym; +use intern::{Symbol, sym}; +use parser::T; use smallvec::SmallVec; use span::{Edition, SyntaxContext}; -use syntax::{AstNode, ast}; +use syntax::{AstNode, SyntaxToken, ast}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ModPath { @@ -64,6 +65,58 @@ impl ModPath { ModPath { kind, segments: SmallVec::new_const() } } + pub fn from_tokens( + db: &dyn ExpandDatabase, + span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext, + is_abs: bool, + segments: impl Iterator<Item = SyntaxToken>, + ) -> Option<ModPath> { + let mut segments = segments.peekable(); + let mut result = SmallVec::new_const(); + let path_kind = if is_abs { + PathKind::Abs + } else { + let first = segments.next()?; + match first.kind() { + T![crate] => PathKind::Crate, + T![self] => PathKind::Super(handle_super(&mut segments)), + T![super] => PathKind::Super(1 + handle_super(&mut segments)), + T![ident] => { + let first_text = first.text(); + if first_text == "$crate" { + let ctxt = span_for_range(first.text_range()); + resolve_crate_root(db, ctxt) + .map(PathKind::DollarCrate) + .unwrap_or(PathKind::Crate) + } else { + result.push(Name::new_symbol_root(Symbol::intern(first_text))); + PathKind::Plain + } + } + _ => return None, + } + }; + for segment in segments { + if segment.kind() != T![ident] { + return None; + } + result.push(Name::new_symbol_root(Symbol::intern(segment.text()))); + } + if result.is_empty() { + return None; + } + result.shrink_to_fit(); + return Some(ModPath { kind: path_kind, segments: result }); + + fn handle_super(segments: &mut Peekable<impl Iterator<Item = SyntaxToken>>) -> u8 { + let mut result = 0; + while segments.next_if(|it| it.kind() == T![super]).is_some() { + result += 1; + } + result + } + } + pub fn segments(&self) -> &[Name] { &self.segments } @@ -302,16 +355,16 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs, _ => return None, }, - tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if *text == sym::dollar_crate => { + tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if text == sym::dollar_crate => { resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate) } - tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF, - tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => { + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::self_ => PathKind::SELF, + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::super_ => { let mut deg = 1; while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw: _ })) = leaves.next() { - if *text != sym::super_ { + if text != sym::super_ { segments.push(Name::new_symbol(text.clone(), span.ctx)); break; } @@ -319,7 +372,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio } PathKind::Super(deg) } - tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate, + tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::crate_ => PathKind::Crate, tt::Leaf::Ident(ident) => { segments.push(Name::new_symbol(ident.sym.clone(), ident.span.ctx)); PathKind::Plain |