Skip to main content

hir_def/expr_store/lower/
path.rs

1//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
2
3#[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    /// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
34    pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
35}
36
37/// Converts an `ast::Path` to `Path`. Works with use trees.
38/// It correctly handles `$crate` based path from macro call.
39// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
40// also needs an update.
41pub(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            // We can end up here if for `path::`
71            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()); // this can only occur at the first segment
82            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                        // FIXME: Report an error.
90                        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()); // this can only occur at the first segment
124
125                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                    // <T>::foo
132                    None => {
133                        type_anchor = Some(self_type);
134                        kind = PathKind::Plain;
135                    }
136                    // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
137                    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                        // FIXME: Unnecessary clone
142                        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                        // Insert the type reference (T in the above example) as Self parameter for the trait
166                        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                    // FIXME: Report an error.
187                    return None;
188                }
189                kind = PathKind::Crate;
190                break;
191            }
192            ast::PathSegmentKind::SelfKw => {
193                if path.qualifier().is_some() {
194                    // FIXME: Report an error.
195                    return None;
196                }
197                // don't break out if `self` is the last segment of a path, this mean we got a
198                // use tree like `foo::{self}` which we want to resolve as `foo`
199                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        // plain empty paths don't exist, this means we got a single `self` segment as our path
222        kind = PathKind::SELF;
223    }
224
225    // handle local_inner_macros :
226    // Basically, even in rustc it is quite hacky:
227    // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
228    // We follow what it did anyway :)
229    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        // Well-formed code cannot have `<T as Trait>` without an associated item after,
255        // and this causes panics in hir-ty lowering.
256        *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        // FIXME: this bottom up traversal is not too precise.
298        // Should we handle do a top-down analysis, recording results?
299        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
305/// This function finds the AST segment that corresponds to the HIR segment
306/// with index `segment_idx` on the path that is lowered from `path`.
307pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
308    // Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
309    // as keeping source maps for all paths segments will have a severe impact on memory usage.
310
311    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}