]> git.lizzy.rs Git - rust.git/blob - crates/hir-expand/src/mod_path.rs
fea09521e87cb6f96aeac38730a65df3739d7e5b
[rust.git] / crates / hir-expand / src / mod_path.rs
1 //! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
2
3 use std::{
4     fmt::{self, Display},
5     iter,
6 };
7
8 use crate::{
9     db::AstDatabase,
10     hygiene::Hygiene,
11     name::{known, Name},
12 };
13 use base_db::CrateId;
14 use either::Either;
15 use smallvec::SmallVec;
16 use syntax::{ast, AstNode};
17
18 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19 pub struct ModPath {
20     pub kind: PathKind,
21     segments: SmallVec<[Name; 1]>,
22 }
23
24 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25 pub struct EscapedModPath<'a>(&'a ModPath);
26
27 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28 pub enum PathKind {
29     Plain,
30     /// `self::` is `Super(0)`
31     Super(u8),
32     Crate,
33     /// Absolute path (::foo)
34     Abs,
35     /// `$crate` from macro expansion
36     DollarCrate(CrateId),
37 }
38
39 impl ModPath {
40     pub fn from_src(db: &dyn AstDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
41         convert_path(db, None, path, hygiene)
42     }
43
44     pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
45         let segments = segments.into_iter().collect();
46         ModPath { kind, segments }
47     }
48
49     /// Creates a `ModPath` from a `PathKind`, with no extra path segments.
50     pub const fn from_kind(kind: PathKind) -> ModPath {
51         ModPath { kind, segments: SmallVec::new_const() }
52     }
53
54     pub fn segments(&self) -> &[Name] {
55         &self.segments
56     }
57
58     pub fn push_segment(&mut self, segment: Name) {
59         self.segments.push(segment);
60     }
61
62     pub fn pop_segment(&mut self) -> Option<Name> {
63         self.segments.pop()
64     }
65
66     /// Returns the number of segments in the path (counting special segments like `$crate` and
67     /// `super`).
68     pub fn len(&self) -> usize {
69         self.segments.len()
70             + match self.kind {
71                 PathKind::Plain => 0,
72                 PathKind::Super(i) => i as usize,
73                 PathKind::Crate => 1,
74                 PathKind::Abs => 0,
75                 PathKind::DollarCrate(_) => 1,
76             }
77     }
78
79     pub fn is_ident(&self) -> bool {
80         self.as_ident().is_some()
81     }
82
83     pub fn is_self(&self) -> bool {
84         self.kind == PathKind::Super(0) && self.segments.is_empty()
85     }
86
87     #[allow(non_snake_case)]
88     pub fn is_Self(&self) -> bool {
89         self.kind == PathKind::Plain
90             && matches!(&*self.segments, [name] if *name == known::SELF_TYPE)
91     }
92
93     /// If this path is a single identifier, like `foo`, return its name.
94     pub fn as_ident(&self) -> Option<&Name> {
95         if self.kind != PathKind::Plain {
96             return None;
97         }
98
99         match &*self.segments {
100             [name] => Some(name),
101             _ => None,
102         }
103     }
104
105     pub fn escaped(&self) -> EscapedModPath<'_> {
106         EscapedModPath(self)
107     }
108
109     fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
110         let mut first_segment = true;
111         let mut add_segment = |s| -> fmt::Result {
112             if !first_segment {
113                 f.write_str("::")?;
114             }
115             first_segment = false;
116             f.write_str(s)?;
117             Ok(())
118         };
119         match self.kind {
120             PathKind::Plain => {}
121             PathKind::Super(0) => add_segment("self")?,
122             PathKind::Super(n) => {
123                 for _ in 0..n {
124                     add_segment("super")?;
125                 }
126             }
127             PathKind::Crate => add_segment("crate")?,
128             PathKind::Abs => add_segment("")?,
129             PathKind::DollarCrate(_) => add_segment("$crate")?,
130         }
131         for segment in &self.segments {
132             if !first_segment {
133                 f.write_str("::")?;
134             }
135             first_segment = false;
136             if escaped {
137                 segment.escaped().fmt(f)?
138             } else {
139                 segment.fmt(f)?
140             };
141         }
142         Ok(())
143     }
144 }
145
146 impl Display for ModPath {
147     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148         self._fmt(f, false)
149     }
150 }
151
152 impl<'a> Display for EscapedModPath<'a> {
153     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154         self.0._fmt(f, true)
155     }
156 }
157
158 impl From<Name> for ModPath {
159     fn from(name: Name) -> ModPath {
160         ModPath::from_segments(PathKind::Plain, iter::once(name))
161     }
162 }
163
164 fn convert_path(
165     db: &dyn AstDatabase,
166     prefix: Option<ModPath>,
167     path: ast::Path,
168     hygiene: &Hygiene,
169 ) -> Option<ModPath> {
170     let prefix = match path.qualifier() {
171         Some(qual) => Some(convert_path(db, prefix, qual, hygiene)?),
172         None => prefix,
173     };
174
175     let segment = path.segment()?;
176     let mut mod_path = match segment.kind()? {
177         ast::PathSegmentKind::Name(name_ref) => {
178             match hygiene.name_ref_to_name(db, name_ref) {
179                 Either::Left(name) => {
180                     // no type args in use
181                     let mut res = prefix.unwrap_or_else(|| {
182                         ModPath::from_kind(
183                             segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
184                         )
185                     });
186                     res.segments.push(name);
187                     res
188                 }
189                 Either::Right(crate_id) => {
190                     return Some(ModPath::from_segments(
191                         PathKind::DollarCrate(crate_id),
192                         iter::empty(),
193                     ))
194                 }
195             }
196         }
197         ast::PathSegmentKind::SelfTypeKw => {
198             if prefix.is_some() {
199                 return None;
200             }
201             ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
202         }
203         ast::PathSegmentKind::CrateKw => {
204             if prefix.is_some() {
205                 return None;
206             }
207             ModPath::from_segments(PathKind::Crate, iter::empty())
208         }
209         ast::PathSegmentKind::SelfKw => {
210             if prefix.is_some() {
211                 return None;
212             }
213             ModPath::from_segments(PathKind::Super(0), iter::empty())
214         }
215         ast::PathSegmentKind::SuperKw => {
216             let nested_super_count = match prefix.map(|p| p.kind) {
217                 Some(PathKind::Super(n)) => n,
218                 Some(_) => return None,
219                 None => 0,
220             };
221
222             ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
223         }
224         ast::PathSegmentKind::Type { .. } => {
225             // not allowed in imports
226             return None;
227         }
228     };
229
230     // handle local_inner_macros :
231     // Basically, even in rustc it is quite hacky:
232     // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
233     // We follow what it did anyway :)
234     if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
235         if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
236             if let Some(crate_id) = hygiene.local_inner_macros(db, path) {
237                 mod_path.kind = PathKind::DollarCrate(crate_id);
238             }
239         }
240     }
241
242     Some(mod_path)
243 }
244
245 pub use crate::name as __name;
246
247 #[macro_export]
248 macro_rules! __known_path {
249     (core::iter::IntoIterator) => {};
250     (core::iter::Iterator) => {};
251     (core::result::Result) => {};
252     (core::option::Option) => {};
253     (core::ops::Range) => {};
254     (core::ops::RangeFrom) => {};
255     (core::ops::RangeFull) => {};
256     (core::ops::RangeTo) => {};
257     (core::ops::RangeToInclusive) => {};
258     (core::ops::RangeInclusive) => {};
259     (core::future::Future) => {};
260     (core::ops::Try) => {};
261     ($path:path) => {
262         compile_error!("Please register your known path in the path module")
263     };
264 }
265
266 #[macro_export]
267 macro_rules! __path {
268     ($start:ident $(:: $seg:ident)*) => ({
269         $crate::__known_path!($start $(:: $seg)*);
270         $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
271             $crate::mod_path::__name![$start], $($crate::mod_path::__name![$seg],)*
272         ])
273     });
274 }
275
276 pub use crate::__path as path;