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