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