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