diff options
author | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
commit | c26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch) | |
tree | 7cff36c38234be0afb65273146d8247083a5cfeb /crates/hir_def/src/path.rs | |
parent | 3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff) | |
parent | f1f73649a686dc6e6449afc35e0fa6fed00e225d (diff) |
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/hir_def/src/path.rs')
-rw-r--r-- | crates/hir_def/src/path.rs | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs new file mode 100644 index 000000000..74d26f08b --- /dev/null +++ b/crates/hir_def/src/path.rs | |||
@@ -0,0 +1,351 @@ | |||
1 | //! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`. | ||
2 | mod lower; | ||
3 | |||
4 | use std::{ | ||
5 | fmt::{self, Display}, | ||
6 | iter, | ||
7 | sync::Arc, | ||
8 | }; | ||
9 | |||
10 | use crate::body::LowerCtx; | ||
11 | use base_db::CrateId; | ||
12 | use hir_expand::{ | ||
13 | hygiene::Hygiene, | ||
14 | name::{AsName, Name}, | ||
15 | }; | ||
16 | use syntax::ast; | ||
17 | |||
18 | use crate::{ | ||
19 | type_ref::{TypeBound, TypeRef}, | ||
20 | InFile, | ||
21 | }; | ||
22 | |||
23 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
24 | pub struct ModPath { | ||
25 | pub kind: PathKind, | ||
26 | pub segments: Vec<Name>, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
30 | pub enum PathKind { | ||
31 | Plain, | ||
32 | /// `self::` is `Super(0)` | ||
33 | Super(u8), | ||
34 | Crate, | ||
35 | /// Absolute path (::foo) | ||
36 | Abs, | ||
37 | /// `$crate` from macro expansion | ||
38 | DollarCrate(CrateId), | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
42 | pub enum ImportAlias { | ||
43 | /// Unnamed alias, as in `use Foo as _;` | ||
44 | Underscore, | ||
45 | /// Named alias | ||
46 | Alias(Name), | ||
47 | } | ||
48 | |||
49 | impl ModPath { | ||
50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | ||
51 | lower::lower_path(path, hygiene).map(|it| it.mod_path) | ||
52 | } | ||
53 | |||
54 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { | ||
55 | let segments = segments.into_iter().collect::<Vec<_>>(); | ||
56 | ModPath { kind, segments } | ||
57 | } | ||
58 | |||
59 | pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath { | ||
60 | name_ref.as_name().into() | ||
61 | } | ||
62 | |||
63 | /// Converts an `tt::Ident` into a single-identifier `Path`. | ||
64 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { | ||
65 | ident.as_name().into() | ||
66 | } | ||
67 | |||
68 | /// Calls `cb` with all paths, represented by this use item. | ||
69 | pub(crate) fn expand_use_item( | ||
70 | item_src: InFile<ast::Use>, | ||
71 | hygiene: &Hygiene, | ||
72 | mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>), | ||
73 | ) { | ||
74 | if let Some(tree) = item_src.value.use_tree() { | ||
75 | lower::lower_use_tree(None, tree, hygiene, &mut cb); | ||
76 | } | ||
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 is_ident(&self) -> bool { | ||
93 | self.kind == PathKind::Plain && self.segments.len() == 1 | ||
94 | } | ||
95 | |||
96 | pub fn is_self(&self) -> bool { | ||
97 | self.kind == PathKind::Super(0) && self.segments.is_empty() | ||
98 | } | ||
99 | |||
100 | /// If this path is a single identifier, like `foo`, return its name. | ||
101 | pub fn as_ident(&self) -> Option<&Name> { | ||
102 | if self.kind != PathKind::Plain || self.segments.len() > 1 { | ||
103 | return None; | ||
104 | } | ||
105 | self.segments.first() | ||
106 | } | ||
107 | } | ||
108 | |||
109 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
110 | pub struct Path { | ||
111 | /// Type based path like `<T>::foo`. | ||
112 | /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`. | ||
113 | type_anchor: Option<Box<TypeRef>>, | ||
114 | mod_path: ModPath, | ||
115 | /// Invariant: the same len as `self.mod_path.segments` | ||
116 | generic_args: Vec<Option<Arc<GenericArgs>>>, | ||
117 | } | ||
118 | |||
119 | /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This | ||
120 | /// also includes bindings of associated types, like in `Iterator<Item = Foo>`. | ||
121 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
122 | pub struct GenericArgs { | ||
123 | pub args: Vec<GenericArg>, | ||
124 | /// This specifies whether the args contain a Self type as the first | ||
125 | /// element. This is the case for path segments like `<T as Trait>`, where | ||
126 | /// `T` is actually a type parameter for the path `Trait` specifying the | ||
127 | /// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type | ||
128 | /// is left out. | ||
129 | pub has_self_type: bool, | ||
130 | /// Associated type bindings like in `Iterator<Item = T>`. | ||
131 | pub bindings: Vec<AssociatedTypeBinding>, | ||
132 | } | ||
133 | |||
134 | /// An associated type binding like in `Iterator<Item = T>`. | ||
135 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
136 | pub struct AssociatedTypeBinding { | ||
137 | /// The name of the associated type. | ||
138 | pub name: Name, | ||
139 | /// The type bound to this associated type (in `Item = T`, this would be the | ||
140 | /// `T`). This can be `None` if there are bounds instead. | ||
141 | pub type_ref: Option<TypeRef>, | ||
142 | /// Bounds for the associated type, like in `Iterator<Item: | ||
143 | /// SomeOtherTrait>`. (This is the unstable `associated_type_bounds` | ||
144 | /// feature.) | ||
145 | pub bounds: Vec<TypeBound>, | ||
146 | } | ||
147 | |||
148 | /// A single generic argument. | ||
149 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
150 | pub enum GenericArg { | ||
151 | Type(TypeRef), | ||
152 | // or lifetime... | ||
153 | } | ||
154 | |||
155 | impl Path { | ||
156 | /// Converts an `ast::Path` to `Path`. Works with use trees. | ||
157 | #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"] | ||
158 | pub fn from_ast(path: ast::Path) -> Option<Path> { | ||
159 | lower::lower_path(path, &Hygiene::new_unhygienic()) | ||
160 | } | ||
161 | |||
162 | /// Converts an `ast::Path` to `Path`. Works with use trees. | ||
163 | /// It correctly handles `$crate` based path from macro call. | ||
164 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> { | ||
165 | lower::lower_path(path, hygiene) | ||
166 | } | ||
167 | |||
168 | /// Converts a known mod path to `Path`. | ||
169 | pub(crate) fn from_known_path( | ||
170 | path: ModPath, | ||
171 | generic_args: Vec<Option<Arc<GenericArgs>>>, | ||
172 | ) -> Path { | ||
173 | Path { type_anchor: None, mod_path: path, generic_args } | ||
174 | } | ||
175 | |||
176 | pub fn kind(&self) -> &PathKind { | ||
177 | &self.mod_path.kind | ||
178 | } | ||
179 | |||
180 | pub fn type_anchor(&self) -> Option<&TypeRef> { | ||
181 | self.type_anchor.as_deref() | ||
182 | } | ||
183 | |||
184 | pub fn segments(&self) -> PathSegments<'_> { | ||
185 | PathSegments { | ||
186 | segments: self.mod_path.segments.as_slice(), | ||
187 | generic_args: self.generic_args.as_slice(), | ||
188 | } | ||
189 | } | ||
190 | |||
191 | pub fn mod_path(&self) -> &ModPath { | ||
192 | &self.mod_path | ||
193 | } | ||
194 | |||
195 | pub fn qualifier(&self) -> Option<Path> { | ||
196 | if self.mod_path.is_ident() { | ||
197 | return None; | ||
198 | } | ||
199 | let res = Path { | ||
200 | type_anchor: self.type_anchor.clone(), | ||
201 | mod_path: ModPath { | ||
202 | kind: self.mod_path.kind.clone(), | ||
203 | segments: self.mod_path.segments[..self.mod_path.segments.len() - 1].to_vec(), | ||
204 | }, | ||
205 | generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(), | ||
206 | }; | ||
207 | Some(res) | ||
208 | } | ||
209 | } | ||
210 | |||
211 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
212 | pub struct PathSegment<'a> { | ||
213 | pub name: &'a Name, | ||
214 | pub args_and_bindings: Option<&'a GenericArgs>, | ||
215 | } | ||
216 | |||
217 | pub struct PathSegments<'a> { | ||
218 | segments: &'a [Name], | ||
219 | generic_args: &'a [Option<Arc<GenericArgs>>], | ||
220 | } | ||
221 | |||
222 | impl<'a> PathSegments<'a> { | ||
223 | pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] }; | ||
224 | pub fn is_empty(&self) -> bool { | ||
225 | self.len() == 0 | ||
226 | } | ||
227 | pub fn len(&self) -> usize { | ||
228 | self.segments.len() | ||
229 | } | ||
230 | pub fn first(&self) -> Option<PathSegment<'a>> { | ||
231 | self.get(0) | ||
232 | } | ||
233 | pub fn last(&self) -> Option<PathSegment<'a>> { | ||
234 | self.get(self.len().checked_sub(1)?) | ||
235 | } | ||
236 | pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> { | ||
237 | assert_eq!(self.segments.len(), self.generic_args.len()); | ||
238 | let res = PathSegment { | ||
239 | name: self.segments.get(idx)?, | ||
240 | args_and_bindings: self.generic_args.get(idx).unwrap().as_ref().map(|it| &**it), | ||
241 | }; | ||
242 | Some(res) | ||
243 | } | ||
244 | pub fn skip(&self, len: usize) -> PathSegments<'a> { | ||
245 | assert_eq!(self.segments.len(), self.generic_args.len()); | ||
246 | PathSegments { segments: &self.segments[len..], generic_args: &self.generic_args[len..] } | ||
247 | } | ||
248 | pub fn take(&self, len: usize) -> PathSegments<'a> { | ||
249 | assert_eq!(self.segments.len(), self.generic_args.len()); | ||
250 | PathSegments { segments: &self.segments[..len], generic_args: &self.generic_args[..len] } | ||
251 | } | ||
252 | pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> { | ||
253 | self.segments.iter().zip(self.generic_args.iter()).map(|(name, args)| PathSegment { | ||
254 | name, | ||
255 | args_and_bindings: args.as_ref().map(|it| &**it), | ||
256 | }) | ||
257 | } | ||
258 | } | ||
259 | |||
260 | impl GenericArgs { | ||
261 | pub(crate) fn from_ast(lower_ctx: &LowerCtx, node: ast::GenericArgList) -> Option<GenericArgs> { | ||
262 | lower::lower_generic_args(lower_ctx, node) | ||
263 | } | ||
264 | |||
265 | pub(crate) fn empty() -> GenericArgs { | ||
266 | GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | impl From<Name> for Path { | ||
271 | fn from(name: Name) -> Path { | ||
272 | Path { | ||
273 | type_anchor: None, | ||
274 | mod_path: ModPath::from_segments(PathKind::Plain, iter::once(name)), | ||
275 | generic_args: vec![None], | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | impl From<Name> for ModPath { | ||
281 | fn from(name: Name) -> ModPath { | ||
282 | ModPath::from_segments(PathKind::Plain, iter::once(name)) | ||
283 | } | ||
284 | } | ||
285 | |||
286 | impl Display for ModPath { | ||
287 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
288 | let mut first_segment = true; | ||
289 | let mut add_segment = |s| -> fmt::Result { | ||
290 | if !first_segment { | ||
291 | f.write_str("::")?; | ||
292 | } | ||
293 | first_segment = false; | ||
294 | f.write_str(s)?; | ||
295 | Ok(()) | ||
296 | }; | ||
297 | match self.kind { | ||
298 | PathKind::Plain => {} | ||
299 | PathKind::Super(n) => { | ||
300 | if n == 0 { | ||
301 | add_segment("self")?; | ||
302 | } | ||
303 | for _ in 0..n { | ||
304 | add_segment("super")?; | ||
305 | } | ||
306 | } | ||
307 | PathKind::Crate => add_segment("crate")?, | ||
308 | PathKind::Abs => add_segment("")?, | ||
309 | PathKind::DollarCrate(_) => add_segment("$crate")?, | ||
310 | } | ||
311 | for segment in &self.segments { | ||
312 | if !first_segment { | ||
313 | f.write_str("::")?; | ||
314 | } | ||
315 | first_segment = false; | ||
316 | write!(f, "{}", segment)?; | ||
317 | } | ||
318 | Ok(()) | ||
319 | } | ||
320 | } | ||
321 | |||
322 | pub use hir_expand::name as __name; | ||
323 | |||
324 | #[macro_export] | ||
325 | macro_rules! __known_path { | ||
326 | (core::iter::IntoIterator) => {}; | ||
327 | (core::result::Result) => {}; | ||
328 | (core::ops::Range) => {}; | ||
329 | (core::ops::RangeFrom) => {}; | ||
330 | (core::ops::RangeFull) => {}; | ||
331 | (core::ops::RangeTo) => {}; | ||
332 | (core::ops::RangeToInclusive) => {}; | ||
333 | (core::ops::RangeInclusive) => {}; | ||
334 | (core::future::Future) => {}; | ||
335 | (core::ops::Try) => {}; | ||
336 | ($path:path) => { | ||
337 | compile_error!("Please register your known path in the path module") | ||
338 | }; | ||
339 | } | ||
340 | |||
341 | #[macro_export] | ||
342 | macro_rules! __path { | ||
343 | ($start:ident $(:: $seg:ident)*) => ({ | ||
344 | $crate::__known_path!($start $(:: $seg)*); | ||
345 | $crate::path::ModPath::from_segments($crate::path::PathKind::Abs, vec![ | ||
346 | $crate::path::__name![$start], $($crate::path::__name![$seg],)* | ||
347 | ]) | ||
348 | }); | ||
349 | } | ||
350 | |||
351 | pub use crate::__path as path; | ||