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::expr_store::{
9    lower::{ExprCollector, generics::ImplTraitLowerFn},
10    path::NormalPath,
11};
12
13use hir_expand::{
14    mod_path::{ModPath, PathKind, resolve_crate_root},
15    name::{AsName, Name},
16};
17use intern::{Interned, sym};
18use syntax::{
19    AstPtr,
20    ast::{self, AstNode, HasGenericArgs},
21};
22
23use crate::{
24    expr_store::path::{GenericArg, GenericArgs, Path},
25    type_ref::TypeRef,
26};
27
28#[cfg(test)]
29thread_local! {
30    /// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
31    pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
32}
33
34/// Converts an `ast::Path` to `Path`. Works with use trees.
35/// It correctly handles `$crate` based path from macro call.
36// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
37// also needs an update.
38pub(super) fn lower_path(
39    collector: &mut ExprCollector<'_>,
40    mut path: ast::Path,
41    impl_trait_lower_fn: ImplTraitLowerFn<'_>,
42) -> Option<Path> {
43    let mut kind = PathKind::Plain;
44    let mut type_anchor = None;
45    let mut segments = Vec::new();
46    let mut generic_args = Vec::new();
47    #[cfg(test)]
48    let mut ast_segments = Vec::new();
49    #[cfg(test)]
50    let mut ast_segments_offset = 0;
51    #[allow(unused_mut)]
52    let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
53        #[cfg(test)]
54        ast_segments.push(_segment.clone());
55        segments.push(name);
56    };
57    loop {
58        let Some(segment) = path.segment() else {
59            segments.push(Name::missing());
60            // We can end up here if for `path::`
61            match qualifier(&path) {
62                Some(it) => {
63                    path = it;
64                    continue;
65                }
66                None => break,
67            }
68        };
69
70        if segment.coloncolon_token().is_some() {
71            debug_assert!(path.qualifier().is_none()); // this can only occur at the first segment
72            kind = PathKind::Abs;
73        }
74
75        match segment.kind()? {
76            ast::PathSegmentKind::Name(name_ref) => {
77                if name_ref.text() == "$crate" {
78                    if path.qualifier().is_some() {
79                        // FIXME: Report an error.
80                        return None;
81                    }
82                    break kind = resolve_crate_root(
83                        collector.db,
84                        collector.expander.ctx_for_range(name_ref.syntax().text_range()),
85                    )
86                    .map(PathKind::DollarCrate)
87                    .unwrap_or(PathKind::Crate);
88                }
89                let name = name_ref.as_name();
90                let args = segment
91                    .generic_arg_list()
92                    .and_then(|it| collector.lower_generic_args(it, impl_trait_lower_fn))
93                    .or_else(|| {
94                        collector.lower_generic_args_from_fn_path(
95                            segment.parenthesized_arg_list(),
96                            segment.ret_type(),
97                            impl_trait_lower_fn,
98                        )
99                    })
100                    .or_else(|| {
101                        segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
102                    });
103                if args.is_some() {
104                    generic_args.resize(segments.len(), None);
105                    generic_args.push(args);
106                }
107                push_segment(&segment, &mut segments, name);
108            }
109            ast::PathSegmentKind::SelfTypeKw => {
110                push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_));
111            }
112            ast::PathSegmentKind::Type { type_ref, trait_ref } => {
113                debug_assert!(path.qualifier().is_none()); // this can only occur at the first segment
114
115                let self_type = collector.lower_type_ref(type_ref?, impl_trait_lower_fn);
116
117                match trait_ref {
118                    // <T>::foo
119                    None => {
120                        type_anchor = Some(self_type);
121                        kind = PathKind::Plain;
122                    }
123                    // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
124                    Some(trait_ref) => {
125                        let path = collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)?;
126                        // FIXME: Unnecessary clone
127                        collector.alloc_type_ref(
128                            TypeRef::Path(path.clone()),
129                            AstPtr::new(&trait_ref).upcast(),
130                        );
131                        let mod_path = path.mod_path()?;
132                        let path_generic_args = path.generic_args();
133                        let num_segments = mod_path.segments().len();
134                        kind = mod_path.kind;
135
136                        segments.extend(mod_path.segments().iter().cloned().rev());
137                        #[cfg(test)]
138                        {
139                            ast_segments_offset = mod_path.segments().len();
140                        }
141                        if let Some(path_generic_args) = path_generic_args {
142                            generic_args.resize(segments.len() - num_segments, None);
143                            generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
144                        } else {
145                            generic_args.resize(segments.len(), None);
146                        }
147
148                        let self_type = GenericArg::Type(self_type);
149
150                        // Insert the type reference (T in the above example) as Self parameter for the trait
151                        let last_segment = generic_args.get_mut(segments.len() - num_segments)?;
152                        *last_segment = Some(match last_segment.take() {
153                            Some(it) => GenericArgs {
154                                args: iter::once(self_type)
155                                    .chain(it.args.iter().cloned())
156                                    .collect(),
157                                has_self_type: true,
158                                ..it
159                            },
160                            None => GenericArgs {
161                                args: Box::new([self_type]),
162                                has_self_type: true,
163                                ..GenericArgs::empty()
164                            },
165                        });
166                    }
167                }
168            }
169            ast::PathSegmentKind::CrateKw => {
170                if path.qualifier().is_some() {
171                    // FIXME: Report an error.
172                    return None;
173                }
174                kind = PathKind::Crate;
175                break;
176            }
177            ast::PathSegmentKind::SelfKw => {
178                if path.qualifier().is_some() {
179                    // FIXME: Report an error.
180                    return None;
181                }
182                // don't break out if `self` is the last segment of a path, this mean we got a
183                // use tree like `foo::{self}` which we want to resolve as `foo`
184                if !segments.is_empty() {
185                    kind = PathKind::SELF;
186                    break;
187                }
188            }
189            ast::PathSegmentKind::SuperKw => {
190                let nested_super_count = if let PathKind::Super(n) = kind { n } else { 0 };
191                kind = PathKind::Super(nested_super_count + 1);
192            }
193        }
194        path = match qualifier(&path) {
195            Some(it) => it,
196            None => break,
197        };
198    }
199    segments.reverse();
200    if !generic_args.is_empty() || type_anchor.is_some() {
201        generic_args.resize(segments.len(), None);
202        generic_args.reverse();
203    }
204
205    if segments.is_empty() && kind == PathKind::Plain && type_anchor.is_none() {
206        // plain empty paths don't exist, this means we got a single `self` segment as our path
207        kind = PathKind::SELF;
208    }
209
210    // handle local_inner_macros :
211    // Basically, even in rustc it is quite hacky:
212    // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
213    // We follow what it did anyway :)
214    if segments.len() == 1
215        && kind == PathKind::Plain
216        && let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast)
217    {
218        let syn_ctxt = collector.expander.ctx_for_range(path.segment()?.syntax().text_range());
219        if let Some(macro_call_id) = syn_ctxt.outer_expn(collector.db)
220            && collector.db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner
221        {
222            kind = match resolve_crate_root(collector.db, syn_ctxt) {
223                Some(crate_root) => PathKind::DollarCrate(crate_root),
224                None => PathKind::Crate,
225            }
226        }
227    }
228
229    #[cfg(test)]
230    {
231        ast_segments.reverse();
232        SEGMENT_LOWERING_MAP
233            .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
234    }
235
236    if let Some(last_segment_args @ Some(GenericArgs { has_self_type: true, .. })) =
237        generic_args.last_mut()
238    {
239        // Well-formed code cannot have `<T as Trait>` without an associated item after,
240        // and this causes panics in hir-ty lowering.
241        *last_segment_args = None;
242    }
243
244    let mod_path = Interned::new(ModPath::from_segments(kind, segments));
245    if type_anchor.is_none() && generic_args.is_empty() {
246        return Some(Path::BarePath(mod_path));
247    } else {
248        return Some(Path::Normal(Box::new(NormalPath {
249            type_anchor,
250            mod_path,
251            generic_args: generic_args.into_boxed_slice(),
252        })));
253    }
254
255    fn qualifier(path: &ast::Path) -> Option<ast::Path> {
256        if let Some(q) = path.qualifier() {
257            return Some(q);
258        }
259        // FIXME: this bottom up traversal is not too precise.
260        // Should we handle do a top-down analysis, recording results?
261        let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
262        let use_tree = use_tree_list.parent_use_tree();
263        use_tree.path()
264    }
265}
266
267/// This function finds the AST segment that corresponds to the HIR segment
268/// with index `segment_idx` on the path that is lowered from `path`.
269pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
270    // Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
271    // as keeping source maps for all paths segments will have a severe impact on memory usage.
272
273    let mut segments = path.segments();
274    if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
275        segments.clone().next().and_then(|it| it.kind())
276    {
277        segments.next();
278        return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
279    }
280    return find_segment(segments, segment_idx);
281
282    fn find_segment(
283        segments: impl Iterator<Item = ast::PathSegment>,
284        segment_idx: u32,
285    ) -> Option<ast::PathSegment> {
286        segments
287            .filter(|segment| match segment.kind() {
288                Some(
289                    ast::PathSegmentKind::CrateKw
290                    | ast::PathSegmentKind::SelfKw
291                    | ast::PathSegmentKind::SuperKw
292                    | ast::PathSegmentKind::Type { .. },
293                )
294                | None => false,
295                Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
296                Some(ast::PathSegmentKind::SelfTypeKw) => true,
297            })
298            .nth(segment_idx as usize)
299    }
300}