hir_def/expr_store/lower/
path.rs1#[cfg(test)]
4mod tests;
5
6use std::iter;
7
8use crate::{
9 expr_store::{
10 lower::{ExprCollector, generics::ImplTraitLowerFn},
11 path::NormalPath,
12 },
13 type_ref::LifetimeRef,
14};
15
16use hir_expand::{
17 mod_path::{ModPath, PathKind, resolve_crate_root},
18 name::{AsName, Name},
19};
20use intern::{Interned, sym};
21use syntax::{
22 AstPtr,
23 ast::{self, AstNode, HasGenericArgs},
24};
25
26use crate::{
27 expr_store::path::{GenericArg, GenericArgs, Path},
28 type_ref::TypeRef,
29};
30
31#[cfg(test)]
32thread_local! {
33 pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
35}
36
37pub(super) fn lower_path(
42 collector: &mut ExprCollector<'_>,
43 mut path: ast::Path,
44 impl_trait_lower_fn: ImplTraitLowerFn<'_>,
45) -> Option<Path> {
46 let mut kind = PathKind::Plain;
47 let mut type_anchor = None;
48 let mut segments = Vec::new();
49 let mut generic_args = Vec::new();
50 #[cfg(test)]
51 let mut ast_segments = Vec::new();
52 #[cfg(test)]
53 let mut ast_segments_offset = 0;
54 #[allow(unused_mut)]
55 let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
56 #[cfg(test)]
57 ast_segments.push(_segment.clone());
58 segments.push(name);
59 };
60
61 let old_lifetimes_constrained_by_input = if collector.is_argument_lt_bound_scope() {
62 Some(std::mem::take(&mut collector.named_lifetime_store.lifetimes_constrained_by_input))
63 } else {
64 None
65 };
66
67 loop {
68 let Some(segment) = path.segment() else {
69 segments.push(Name::missing());
70 match qualifier(&path) {
72 Some(it) => {
73 path = it;
74 continue;
75 }
76 None => break,
77 }
78 };
79
80 if segment.coloncolon_token().is_some() {
81 debug_assert!(path.qualifier().is_none()); kind = PathKind::Abs;
83 }
84
85 match segment.kind()? {
86 ast::PathSegmentKind::Name(name_ref) => {
87 if name_ref.text() == "$crate" {
88 if path.qualifier().is_some() {
89 return None;
91 }
92 break kind = resolve_crate_root(
93 collector.db,
94 collector.expander.ctx_for_range(name_ref.syntax().text_range()),
95 )
96 .map(PathKind::DollarCrate)
97 .unwrap_or(PathKind::Crate);
98 }
99 let name = name_ref.as_name();
100 let args = segment
101 .generic_arg_list()
102 .and_then(|it| collector.lower_generic_args(it, impl_trait_lower_fn))
103 .or_else(|| {
104 collector.lower_generic_args_from_fn_path(
105 segment.parenthesized_arg_list(),
106 segment.ret_type(),
107 impl_trait_lower_fn,
108 )
109 })
110 .or_else(|| {
111 segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
112 });
113 if args.is_some() {
114 generic_args.resize(segments.len(), None);
115 generic_args.push(args);
116 }
117 push_segment(&segment, &mut segments, name);
118 }
119 ast::PathSegmentKind::SelfTypeKw => {
120 push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_));
121 }
122 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
123 debug_assert!(path.qualifier().is_none()); let type_ref = type_ref?;
126 let self_type = collector.for_path_type_projection(|collector| {
127 collector.lower_type_ref(type_ref, impl_trait_lower_fn)
128 });
129
130 match trait_ref {
131 None => {
133 type_anchor = Some(self_type);
134 kind = PathKind::Plain;
135 }
136 Some(trait_ref) => {
138 let path = collector.for_path_type_projection(|collector| {
139 collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)
140 })?;
141 collector.alloc_type_ref(
143 TypeRef::Path(path.clone()),
144 AstPtr::new(&trait_ref).upcast(),
145 );
146 let mod_path = path.mod_path()?;
147 let path_generic_args = path.generic_args();
148 let num_segments = mod_path.segments().len();
149 kind = mod_path.kind;
150
151 segments.extend(mod_path.segments().iter().cloned().rev());
152 #[cfg(test)]
153 {
154 ast_segments_offset = mod_path.segments().len();
155 }
156 if let Some(path_generic_args) = path_generic_args {
157 generic_args.resize(segments.len() - num_segments, None);
158 generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
159 } else {
160 generic_args.resize(segments.len(), None);
161 }
162
163 let self_type = GenericArg::Type(self_type);
164
165 let last_segment = generic_args.get_mut(segments.len() - num_segments)?;
167 *last_segment = Some(match last_segment.take() {
168 Some(it) => GenericArgs {
169 args: iter::once(self_type)
170 .chain(it.args.iter().cloned())
171 .collect(),
172 has_self_type: true,
173 ..it
174 },
175 None => GenericArgs {
176 args: Box::new([self_type]),
177 has_self_type: true,
178 ..GenericArgs::empty()
179 },
180 });
181 }
182 }
183 }
184 ast::PathSegmentKind::CrateKw => {
185 if path.qualifier().is_some() {
186 return None;
188 }
189 kind = PathKind::Crate;
190 break;
191 }
192 ast::PathSegmentKind::SelfKw => {
193 if path.qualifier().is_some() {
194 return None;
196 }
197 if !segments.is_empty() {
200 kind = PathKind::SELF;
201 break;
202 }
203 }
204 ast::PathSegmentKind::SuperKw => {
205 let nested_super_count = if let PathKind::Super(n) = kind { n } else { 0 };
206 kind = PathKind::Super(nested_super_count + 1);
207 }
208 }
209 path = match qualifier(&path) {
210 Some(it) => it,
211 None => break,
212 };
213 }
214 segments.reverse();
215 if !generic_args.is_empty() || type_anchor.is_some() {
216 generic_args.resize(segments.len(), None);
217 generic_args.reverse();
218 }
219
220 if segments.is_empty() && kind == PathKind::Plain && type_anchor.is_none() {
221 kind = PathKind::SELF;
223 }
224
225 if segments.len() == 1
230 && kind == PathKind::Plain
231 && let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast)
232 {
233 let syn_ctxt = collector.expander.ctx_for_range(path.segment()?.syntax().text_range());
234 if let Some(macro_call_id) = syn_ctxt.outer_expn(collector.db)
235 && hir_expand::MacroCallId::from(macro_call_id).loc(collector.db).def.local_inner
236 {
237 kind = match resolve_crate_root(collector.db, syn_ctxt) {
238 Some(crate_root) => PathKind::DollarCrate(crate_root),
239 None => PathKind::Crate,
240 }
241 }
242 }
243
244 #[cfg(test)]
245 {
246 ast_segments.reverse();
247 SEGMENT_LOWERING_MAP
248 .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
249 }
250
251 if let Some(last_segment_args @ Some(GenericArgs { has_self_type: true, .. })) =
252 generic_args.last_mut()
253 {
254 *last_segment_args = None;
257 }
258
259 let mod_path = Interned::new(ModPath::from_segments(kind, segments));
260
261 if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input {
262 let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias(
263 &mod_path,
264 generic_args.last().and_then(|g| g.as_ref()),
265 );
266 if let Some(lifetimes) = type_alias_constrained_lifetimes {
267 collector.named_lifetime_store.lifetimes_constrained_by_input =
268 old_lifetimes_constrained_by_input;
269 collector.named_lifetime_store.lifetimes_constrained_by_input.extend(
270 lifetimes.filter_map(|lt_ref| match &collector.store.lifetimes[lt_ref] {
271 LifetimeRef::Named(name) => Some(name.clone()),
272 _ => None,
273 }),
274 );
275 } else {
276 collector
277 .named_lifetime_store
278 .lifetimes_constrained_by_input
279 .extend(old_lifetimes_constrained_by_input);
280 }
281 }
282
283 if type_anchor.is_none() && generic_args.is_empty() {
284 return Some(Path::BarePath(mod_path));
285 } else {
286 return Some(Path::Normal(Box::new(NormalPath {
287 type_anchor,
288 mod_path,
289 generic_args: generic_args.into_boxed_slice(),
290 })));
291 }
292
293 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
294 if let Some(q) = path.qualifier() {
295 return Some(q);
296 }
297 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
300 let use_tree = use_tree_list.parent_use_tree();
301 use_tree.path()
302 }
303}
304
305pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
308 let mut segments = path.segments();
312 if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
313 segments.clone().next().and_then(|it| it.kind())
314 {
315 segments.next();
316 return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
317 }
318 return find_segment(segments, segment_idx);
319
320 fn find_segment(
321 segments: impl Iterator<Item = ast::PathSegment>,
322 segment_idx: u32,
323 ) -> Option<ast::PathSegment> {
324 segments
325 .filter(|segment| match segment.kind() {
326 Some(
327 ast::PathSegmentKind::CrateKw
328 | ast::PathSegmentKind::SelfKw
329 | ast::PathSegmentKind::SuperKw
330 | ast::PathSegmentKind::Type { .. },
331 )
332 | None => false,
333 Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
334 Some(ast::PathSegmentKind::SelfTypeKw) => true,
335 })
336 .nth(segment_idx as usize)
337 }
338}