aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/path.rs')
-rw-r--r--crates/hir_def/src/path.rs351
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`.
2mod lower;
3
4use std::{
5 fmt::{self, Display},
6 iter,
7 sync::Arc,
8};
9
10use crate::body::LowerCtx;
11use base_db::CrateId;
12use hir_expand::{
13 hygiene::Hygiene,
14 name::{AsName, Name},
15};
16use syntax::ast;
17
18use crate::{
19 type_ref::{TypeBound, TypeRef},
20 InFile,
21};
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct ModPath {
25 pub kind: PathKind,
26 pub segments: Vec<Name>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub 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)]
42pub enum ImportAlias {
43 /// Unnamed alias, as in `use Foo as _;`
44 Underscore,
45 /// Named alias
46 Alias(Name),
47}
48
49impl 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)]
110pub 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)]
122pub 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)]
136pub 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)]
150pub enum GenericArg {
151 Type(TypeRef),
152 // or lifetime...
153}
154
155impl 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)]
212pub struct PathSegment<'a> {
213 pub name: &'a Name,
214 pub args_and_bindings: Option<&'a GenericArgs>,
215}
216
217pub struct PathSegments<'a> {
218 segments: &'a [Name],
219 generic_args: &'a [Option<Arc<GenericArgs>>],
220}
221
222impl<'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
260impl 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
270impl 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
280impl From<Name> for ModPath {
281 fn from(name: Name) -> ModPath {
282 ModPath::from_segments(PathKind::Plain, iter::once(name))
283 }
284}
285
286impl 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
322pub use hir_expand::name as __name;
323
324#[macro_export]
325macro_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]
342macro_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
351pub use crate::__path as path;