hir_def/expr_store/lower/
path.rs
1#[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 pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
30}
31
32pub(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 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()); 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 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()); let self_type = collector.lower_type_ref(type_ref?, impl_trait_lower_fn);
114
115 match trait_ref {
116 None => {
118 type_anchor = Some(self_type);
119 kind = PathKind::Plain;
120 }
121 Some(trait_ref) => {
123 let path = collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)?;
124 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 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 return None;
171 }
172 kind = PathKind::Crate;
173 break;
174 }
175 ast::PathSegmentKind::SelfKw => {
176 if path.qualifier().is_some() {
177 return None;
179 }
180 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 kind = PathKind::SELF;
206 }
207
208 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 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
256pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
259 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}