aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/path/lower.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/path/lower.rs')
-rw-r--r--crates/hir_def/src/path/lower.rs215
1 files changed, 215 insertions, 0 deletions
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
new file mode 100644
index 000000000..07b9723ce
--- /dev/null
+++ b/crates/hir_def/src/path/lower.rs
@@ -0,0 +1,215 @@
1//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
2
3mod lower_use;
4
5use std::sync::Arc;
6
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName},
11};
12use syntax::ast::{self, AstNode, TypeBoundsOwner};
13
14use super::AssociatedTypeBinding;
15use crate::{
16 body::LowerCtx,
17 path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
18 type_ref::{TypeBound, TypeRef},
19};
20
21pub(super) use lower_use::lower_use_tree;
22
23/// Converts an `ast::Path` to `Path`. Works with use trees.
24/// It correctly handles `$crate` based path from macro call.
25pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
26 let mut kind = PathKind::Plain;
27 let mut type_anchor = None;
28 let mut segments = Vec::new();
29 let mut generic_args = Vec::new();
30 let ctx = LowerCtx::with_hygiene(hygiene);
31 loop {
32 let segment = path.segment()?;
33
34 if segment.coloncolon_token().is_some() {
35 kind = PathKind::Abs;
36 }
37
38 match segment.kind()? {
39 ast::PathSegmentKind::Name(name_ref) => {
40 // FIXME: this should just return name
41 match hygiene.name_ref_to_name(name_ref) {
42 Either::Left(name) => {
43 let args = segment
44 .generic_arg_list()
45 .and_then(|it| lower_generic_args(&ctx, it))
46 .or_else(|| {
47 lower_generic_args_from_fn_path(
48 &ctx,
49 segment.param_list(),
50 segment.ret_type(),
51 )
52 })
53 .map(Arc::new);
54 segments.push(name);
55 generic_args.push(args)
56 }
57 Either::Right(crate_id) => {
58 kind = PathKind::DollarCrate(crate_id);
59 break;
60 }
61 }
62 }
63 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
64 assert!(path.qualifier().is_none()); // this can only occur at the first segment
65
66 let self_type = TypeRef::from_ast(&ctx, type_ref?);
67
68 match trait_ref {
69 // <T>::foo
70 None => {
71 type_anchor = Some(Box::new(self_type));
72 kind = PathKind::Plain;
73 }
74 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
75 Some(trait_ref) => {
76 let path = Path::from_src(trait_ref.path()?, hygiene)?;
77 kind = path.mod_path.kind;
78
79 let mut prefix_segments = path.mod_path.segments;
80 prefix_segments.reverse();
81 segments.extend(prefix_segments);
82
83 let mut prefix_args = path.generic_args;
84 prefix_args.reverse();
85 generic_args.extend(prefix_args);
86
87 // Insert the type reference (T in the above example) as Self parameter for the trait
88 let last_segment = generic_args.last_mut()?;
89 if last_segment.is_none() {
90 *last_segment = Some(Arc::new(GenericArgs::empty()));
91 };
92 let args = last_segment.as_mut().unwrap();
93 let mut args_inner = Arc::make_mut(args);
94 args_inner.has_self_type = true;
95 args_inner.args.insert(0, GenericArg::Type(self_type));
96 }
97 }
98 }
99 ast::PathSegmentKind::CrateKw => {
100 kind = PathKind::Crate;
101 break;
102 }
103 ast::PathSegmentKind::SelfKw => {
104 kind = PathKind::Super(0);
105 break;
106 }
107 ast::PathSegmentKind::SuperKw => {
108 let nested_super_count = if let PathKind::Super(n) = kind { n } else { 0 };
109 kind = PathKind::Super(nested_super_count + 1);
110 }
111 }
112 path = match qualifier(&path) {
113 Some(it) => it,
114 None => break,
115 };
116 }
117 segments.reverse();
118 generic_args.reverse();
119
120 // handle local_inner_macros :
121 // Basically, even in rustc it is quite hacky:
122 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if macro_call.is_bang() {
127 if let Some(crate_id) = hygiene.local_inner_macros() {
128 kind = PathKind::DollarCrate(crate_id);
129 }
130 }
131 }
132 }
133
134 let mod_path = ModPath { kind, segments };
135 return Some(Path { type_anchor, mod_path, generic_args });
136
137 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
138 if let Some(q) = path.qualifier() {
139 return Some(q);
140 }
141 // FIXME: this bottom up traversal is not too precise.
142 // Should we handle do a top-down analysis, recording results?
143 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
144 let use_tree = use_tree_list.parent_use_tree();
145 use_tree.path()
146 }
147}
148
149pub(super) fn lower_generic_args(
150 lower_ctx: &LowerCtx,
151 node: ast::GenericArgList,
152) -> Option<GenericArgs> {
153 let mut args = Vec::new();
154 let mut bindings = Vec::new();
155 for generic_arg in node.generic_args() {
156 match generic_arg {
157 ast::GenericArg::TypeArg(type_arg) => {
158 let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
159 args.push(GenericArg::Type(type_ref));
160 }
161 ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
162 if let Some(name_ref) = assoc_type_arg.name_ref() {
163 let name = name_ref.as_name();
164 let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
165 let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
166 l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
167 } else {
168 Vec::new()
169 };
170 bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
171 }
172 }
173 // Lifetimes and constants are ignored for now.
174 ast::GenericArg::LifetimeArg(_) | ast::GenericArg::ConstArg(_) => (),
175 }
176 }
177
178 if args.is_empty() && bindings.is_empty() {
179 return None;
180 }
181 Some(GenericArgs { args, has_self_type: false, bindings })
182}
183
184/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
185/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
186fn lower_generic_args_from_fn_path(
187 ctx: &LowerCtx,
188 params: Option<ast::ParamList>,
189 ret_type: Option<ast::RetType>,
190) -> Option<GenericArgs> {
191 let mut args = Vec::new();
192 let mut bindings = Vec::new();
193 if let Some(params) = params {
194 let mut param_types = Vec::new();
195 for param in params.params() {
196 let type_ref = TypeRef::from_ast_opt(&ctx, param.ty());
197 param_types.push(type_ref);
198 }
199 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
200 args.push(arg);
201 }
202 if let Some(ret_type) = ret_type {
203 let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty());
204 bindings.push(AssociatedTypeBinding {
205 name: name![Output],
206 type_ref: Some(type_ref),
207 bounds: Vec::new(),
208 });
209 }
210 if args.is_empty() && bindings.is_empty() {
211 None
212 } else {
213 Some(GenericArgs { args, has_self_type: false, bindings })
214 }
215}