Skip to main content

hir_expand/
mod_path.rs

1//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
2
3use std::{
4    fmt::{self, Display as _},
5    iter::{self, Peekable},
6};
7
8use crate::{
9    hygiene::Transparency,
10    name::{AsName, Name},
11    tt,
12};
13use base_db::{Crate, SourceDatabase};
14use intern::{Symbol, sym};
15use parser::T;
16use smallvec::SmallVec;
17use span::{Edition, SyntaxContext};
18use syntax::{AstNode, SyntaxToken, ast};
19
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub struct ModPath {
22    pub kind: PathKind,
23    segments: SmallVec<[Name; 1]>,
24}
25
26intern::impl_internable!(ModPath);
27
28#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub enum PathKind {
30    Plain,
31    /// `self::` is `Super(0)`
32    Super(u8),
33    Crate,
34    /// Absolute path (::foo)
35    Abs,
36    // FIXME: Can we remove this somehow?
37    /// `$crate` from macro expansion
38    DollarCrate(Crate),
39}
40
41impl PathKind {
42    pub const SELF: PathKind = PathKind::Super(0);
43}
44
45impl ModPath {
46    pub fn from_src(
47        db: &dyn SourceDatabase,
48        path: ast::Path,
49        span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
50    ) -> Option<ModPath> {
51        convert_path(db, path, span_for_range)
52    }
53
54    pub fn from_tt(db: &dyn SourceDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> {
55        convert_path_tt(db, tt)
56    }
57
58    pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
59        let mut segments: SmallVec<_> = segments.into_iter().collect();
60        segments.shrink_to_fit();
61        ModPath { kind, segments }
62    }
63
64    /// Creates a `ModPath` from a `PathKind`, with no extra path segments.
65    pub const fn from_kind(kind: PathKind) -> ModPath {
66        ModPath { kind, segments: SmallVec::new_const() }
67    }
68
69    pub fn from_tokens(
70        db: &dyn SourceDatabase,
71        span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
72        is_abs: bool,
73        segments: impl Iterator<Item = SyntaxToken>,
74    ) -> Option<ModPath> {
75        let mut segments = segments.peekable();
76        let mut result = SmallVec::new_const();
77        let path_kind = if is_abs {
78            PathKind::Abs
79        } else {
80            let first = segments.next()?;
81            match first.kind() {
82                T![crate] => PathKind::Crate,
83                T![self] => PathKind::Super(handle_super(&mut segments)),
84                T![super] => PathKind::Super(1 + handle_super(&mut segments)),
85                T![ident] => {
86                    let first_text = first.text();
87                    if first_text == "$crate" {
88                        let ctxt = span_for_range(first.text_range());
89                        resolve_crate_root(db, ctxt)
90                            .map(PathKind::DollarCrate)
91                            .unwrap_or(PathKind::Crate)
92                    } else {
93                        result.push(Name::new_symbol_root(Symbol::intern(first_text)));
94                        PathKind::Plain
95                    }
96                }
97                _ => return None,
98            }
99        };
100        for segment in segments {
101            if segment.kind() != T![ident] {
102                return None;
103            }
104            result.push(Name::new_symbol_root(Symbol::intern(segment.text())));
105        }
106        if result.is_empty() {
107            return None;
108        }
109        result.shrink_to_fit();
110        return Some(ModPath { kind: path_kind, segments: result });
111
112        fn handle_super(segments: &mut Peekable<impl Iterator<Item = SyntaxToken>>) -> u8 {
113            let mut result = 0;
114            while segments.next_if(|it| it.kind() == T![super]).is_some() {
115                result += 1;
116            }
117            result
118        }
119    }
120
121    pub fn segments(&self) -> &[Name] {
122        &self.segments
123    }
124
125    pub fn push_segment(&mut self, segment: Name) {
126        self.segments.push(segment);
127    }
128
129    pub fn pop_segment(&mut self) -> Option<Name> {
130        self.segments.pop()
131    }
132
133    /// Returns the number of segments in the path (counting special segments like `$crate` and
134    /// `super`).
135    pub fn len(&self) -> usize {
136        self.segments.len()
137            + match self.kind {
138                PathKind::Plain => 0,
139                PathKind::Super(i) => i as usize,
140                PathKind::Crate => 1,
141                PathKind::Abs => 0,
142                PathKind::DollarCrate(_) => 1,
143            }
144    }
145
146    pub fn textual_len(&self) -> usize {
147        let base = match self.kind {
148            PathKind::Plain => 0,
149            PathKind::SELF => "self".len(),
150            PathKind::Super(i) => "super".len() * i as usize,
151            PathKind::Crate => "crate".len(),
152            PathKind::Abs => 0,
153            PathKind::DollarCrate(_) => "$crate".len(),
154        };
155        self.segments().iter().map(|segment| segment.as_str().len()).fold(base, core::ops::Add::add)
156    }
157
158    pub fn is_ident(&self) -> bool {
159        self.as_ident().is_some()
160    }
161
162    pub fn is_self(&self) -> bool {
163        self.kind == PathKind::SELF && self.segments.is_empty()
164    }
165
166    #[allow(non_snake_case)]
167    pub fn is_Self(&self) -> bool {
168        self.kind == PathKind::Plain && matches!(&*self.segments, [name] if *name == sym::Self_)
169    }
170
171    /// If this path is a single identifier, like `foo`, return its name.
172    pub fn as_ident(&self) -> Option<&Name> {
173        if self.kind != PathKind::Plain {
174            return None;
175        }
176
177        match &*self.segments {
178            [name] => Some(name),
179            _ => None,
180        }
181    }
182    pub fn display_verbatim<'a>(&'a self, db: &'a dyn SourceDatabase) -> impl fmt::Display + 'a {
183        Display { db, path: self, edition: None }
184    }
185
186    pub fn display<'a>(
187        &'a self,
188        db: &'a dyn SourceDatabase,
189        edition: Edition,
190    ) -> impl fmt::Display + 'a {
191        Display { db, path: self, edition: Some(edition) }
192    }
193}
194
195impl Extend<Name> for ModPath {
196    fn extend<T: IntoIterator<Item = Name>>(&mut self, iter: T) {
197        self.segments.extend(iter);
198    }
199}
200
201struct Display<'a> {
202    db: &'a dyn SourceDatabase,
203    path: &'a ModPath,
204    edition: Option<Edition>,
205}
206
207impl fmt::Display for Display<'_> {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        display_fmt_path(self.db, self.path, f, self.edition)
210    }
211}
212
213impl From<Name> for ModPath {
214    fn from(name: Name) -> ModPath {
215        ModPath::from_segments(PathKind::Plain, iter::once(name))
216    }
217}
218
219fn display_fmt_path(
220    db: &dyn SourceDatabase,
221    path: &ModPath,
222    f: &mut fmt::Formatter<'_>,
223    edition: Option<Edition>,
224) -> fmt::Result {
225    let mut first_segment = true;
226    let mut add_segment = |s| -> fmt::Result {
227        if !first_segment {
228            f.write_str("::")?;
229        }
230        first_segment = false;
231        f.write_str(s)?;
232        Ok(())
233    };
234    match path.kind {
235        PathKind::Plain => {}
236        PathKind::SELF => add_segment("self")?,
237        PathKind::Super(n) => {
238            for _ in 0..n {
239                add_segment("super")?;
240            }
241        }
242        PathKind::Crate => add_segment("crate")?,
243        PathKind::Abs => add_segment("")?,
244        PathKind::DollarCrate(_) => add_segment("$crate")?,
245    }
246    for segment in &path.segments {
247        if !first_segment {
248            f.write_str("::")?;
249        }
250        first_segment = false;
251        match edition {
252            Some(edition) => segment.display(db, edition).fmt(f)?,
253            None => fmt::Display::fmt(segment.as_str(), f)?,
254        };
255    }
256    Ok(())
257}
258
259fn convert_path(
260    db: &dyn SourceDatabase,
261    path: ast::Path,
262    span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
263) -> Option<ModPath> {
264    let mut segments = path.segments();
265
266    let segment = &segments.next()?;
267    let handle_super_kw = &mut |init_deg| {
268        let mut deg = init_deg;
269        let mut next_segment = None;
270        for segment in segments.by_ref() {
271            match segment.kind()? {
272                ast::PathSegmentKind::SuperKw => deg += 1,
273                ast::PathSegmentKind::Name(name) => {
274                    next_segment = Some(name.as_name());
275                    break;
276                }
277                ast::PathSegmentKind::Type { .. }
278                | ast::PathSegmentKind::SelfTypeKw
279                | ast::PathSegmentKind::SelfKw
280                | ast::PathSegmentKind::CrateKw => return None,
281            }
282        }
283
284        Some(ModPath::from_segments(PathKind::Super(deg), next_segment))
285    };
286
287    let mut mod_path = match segment.kind()? {
288        ast::PathSegmentKind::Name(name_ref) => {
289            if name_ref.text() == "$crate" {
290                ModPath::from_kind(
291                    resolve_crate_root(db, span_for_range(name_ref.syntax().text_range()))
292                        .map(PathKind::DollarCrate)
293                        .unwrap_or(PathKind::Crate),
294                )
295            } else {
296                let mut res = ModPath::from_kind(
297                    segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
298                );
299                res.segments.push(name_ref.as_name());
300                res
301            }
302        }
303        ast::PathSegmentKind::SelfTypeKw => {
304            ModPath::from_segments(PathKind::Plain, Some(Name::new_symbol_root(sym::Self_)))
305        }
306        ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
307        ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
308        ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
309        ast::PathSegmentKind::Type { .. } => {
310            // not allowed in imports
311            return None;
312        }
313    };
314
315    for segment in segments {
316        let name = match segment.kind()? {
317            ast::PathSegmentKind::Name(name) => name.as_name(),
318            _ => return None,
319        };
320        mod_path.segments.push(name);
321    }
322
323    // handle local_inner_macros :
324    // Basically, even in rustc it is quite hacky:
325    // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
326    // We follow what it did anyway :)
327    if mod_path.segments.len() == 1
328        && mod_path.kind == PathKind::Plain
329        && let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast)
330    {
331        let syn_ctx = span_for_range(segment.syntax().text_range());
332        if let Some(macro_call_id) = syn_ctx.outer_expn(db)
333            && crate::MacroCallId::from(macro_call_id).loc(db).def.local_inner
334        {
335            mod_path.kind = match resolve_crate_root(db, syn_ctx) {
336                Some(crate_root) => PathKind::DollarCrate(crate_root),
337                None => PathKind::Crate,
338            }
339        }
340    }
341
342    Some(mod_path)
343}
344
345fn convert_path_tt(db: &dyn SourceDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> {
346    let mut leaves = tt.iter().filter_map(|tt| match tt {
347        tt::TtElement::Leaf(leaf) => Some(leaf),
348        tt::TtElement::Subtree(..) => None,
349    });
350    let mut segments = smallvec::smallvec![];
351    let kind = match leaves.next()? {
352        tt::Leaf::Punct(tt::Punct { char: ':', .. }) => match leaves.next()? {
353            tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs,
354            _ => return None,
355        },
356        tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if text == sym::dollar_crate => {
357            resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
358        }
359        tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::self_ => PathKind::SELF,
360        tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::super_ => {
361            let mut deg = 1;
362            while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw: _ })) =
363                leaves.next()
364            {
365                if text != sym::super_ {
366                    segments.push(Name::new_symbol(text.clone(), span.ctx));
367                    break;
368                }
369                deg += 1;
370            }
371            PathKind::Super(deg)
372        }
373        tt::Leaf::Ident(tt::Ident { sym: text, .. }) if text == sym::crate_ => PathKind::Crate,
374        tt::Leaf::Ident(ident) => {
375            segments.push(Name::new_symbol(ident.sym.clone(), ident.span.ctx));
376            PathKind::Plain
377        }
378        _ => return None,
379    };
380    segments.extend(leaves.filter_map(|leaf| match leaf {
381        ::tt::Leaf::Ident(ident) => Some(Name::new_symbol(ident.sym.clone(), ident.span.ctx)),
382        _ => None,
383    }));
384    Some(ModPath { kind, segments })
385}
386
387pub fn resolve_crate_root(db: &dyn SourceDatabase, mut ctxt: SyntaxContext) -> Option<Crate> {
388    // When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
389    // we don't want to pretend that the `macro_rules!` definition is in the `macro`
390    // as described in `SyntaxContextId::apply_mark`, so we ignore prepended opaque marks.
391    // FIXME: This is only a guess and it doesn't work correctly for `macro_rules!`
392    // definitions actually produced by `macro` and `macro` definitions produced by
393    // `macro_rules!`, but at least such configurations are not stable yet.
394    ctxt = ctxt.normalize_to_macro_rules(db);
395    let mut iter = ctxt.marks_rev(db).peekable();
396    let mut result_mark = None;
397    // Find the last opaque mark from the end if it exists.
398    while let Some(&(mark, Transparency::Opaque)) = iter.peek() {
399        result_mark = Some(mark);
400        iter.next();
401    }
402    // Then find the last semi-opaque mark from the end if it exists.
403    while let Some((mark, Transparency::SemiOpaque)) = iter.next() {
404        result_mark = Some(mark);
405    }
406
407    result_mark.map(|call| crate::MacroCallId::from(call).loc(db).def.krate)
408}
409
410pub use crate::name as __name;
411
412#[macro_export]
413macro_rules! __known_path {
414    (core::iter::IntoIterator) => {};
415    (core::iter::Iterator) => {};
416    (core::result::Result) => {};
417    (core::option::Option) => {};
418    (core::ops::Range) => {};
419    (core::ops::RangeFrom) => {};
420    (core::ops::RangeFull) => {};
421    (core::ops::RangeTo) => {};
422    (core::ops::RangeToInclusive) => {};
423    (core::ops::RangeInclusive) => {};
424    (core::range::Range) => {};
425    (core::range::RangeFrom) => {};
426    (core::range::RangeInclusive) => {};
427    (core::range::RangeToInclusive) => {};
428    (core::async_iter::AsyncIterator) => {};
429    (core::future::Future) => {};
430    (core::future::IntoFuture) => {};
431    (core::fmt::Debug) => {};
432    (std::fmt::format) => {};
433    (core::ops::Try) => {};
434    (core::convert::From) => {};
435    (core::convert::TryFrom) => {};
436    (core::str::FromStr) => {};
437    ($path:path) => {
438        compile_error!("Please register your known path in the path module")
439    };
440}
441
442#[macro_export]
443macro_rules! __path {
444    ($start:ident $(:: $seg:ident)*) => ({
445        $crate::__known_path!($start $(:: $seg)*);
446        $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
447            $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
448        ])
449    });
450}
451
452pub use crate::__path as path;
453
454#[macro_export]
455macro_rules! __tool_path {
456    ($start:ident $(:: $seg:ident)*) => ({
457        $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
458            $crate::name::Name::new_symbol_root($crate::intern::sym::rust_analyzer), $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
459        ])
460    });
461}
462
463pub use crate::__tool_path as tool_path;