1use std::{
4 fmt::{self, Display as _},
5 iter,
6};
7
8use crate::{
9 db::ExpandDatabase,
10 hygiene::{SyntaxContextExt, Transparency, marks_rev},
11 name::{AsName, Name},
12 tt,
13};
14use base_db::Crate;
15use intern::sym;
16use smallvec::SmallVec;
17use span::{Edition, SyntaxContext};
18use syntax::{AstNode, ast};
19
20#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub struct ModPath {
22 pub kind: PathKind,
23 segments: SmallVec<[Name; 1]>,
24}
25
26#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum PathKind {
28 Plain,
29 Super(u8),
31 Crate,
32 Abs,
34 DollarCrate(Crate),
37}
38
39impl PathKind {
40 pub const SELF: PathKind = PathKind::Super(0);
41}
42
43impl ModPath {
44 pub fn from_src(
45 db: &dyn ExpandDatabase,
46 path: ast::Path,
47 span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
48 ) -> Option<ModPath> {
49 convert_path(db, path, span_for_range)
50 }
51
52 pub fn from_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> {
53 convert_path_tt(db, tt)
54 }
55
56 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
57 let mut segments: SmallVec<_> = segments.into_iter().collect();
58 segments.shrink_to_fit();
59 ModPath { kind, segments }
60 }
61
62 pub const fn from_kind(kind: PathKind) -> ModPath {
64 ModPath { kind, segments: SmallVec::new_const() }
65 }
66
67 pub fn segments(&self) -> &[Name] {
68 &self.segments
69 }
70
71 pub fn push_segment(&mut self, segment: Name) {
72 self.segments.push(segment);
73 }
74
75 pub fn pop_segment(&mut self) -> Option<Name> {
76 self.segments.pop()
77 }
78
79 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
92 pub fn textual_len(&self) -> usize {
93 let base = match self.kind {
94 PathKind::Plain => 0,
95 PathKind::SELF => "self".len(),
96 PathKind::Super(i) => "super".len() * i as usize,
97 PathKind::Crate => "crate".len(),
98 PathKind::Abs => 0,
99 PathKind::DollarCrate(_) => "$crate".len(),
100 };
101 self.segments().iter().map(|segment| segment.as_str().len()).fold(base, core::ops::Add::add)
102 }
103
104 pub fn is_ident(&self) -> bool {
105 self.as_ident().is_some()
106 }
107
108 pub fn is_self(&self) -> bool {
109 self.kind == PathKind::SELF && self.segments.is_empty()
110 }
111
112 #[allow(non_snake_case)]
113 pub fn is_Self(&self) -> bool {
114 self.kind == PathKind::Plain
115 && matches!(&*self.segments, [name] if *name == sym::Self_.clone())
116 }
117
118 pub fn as_ident(&self) -> Option<&Name> {
120 if self.kind != PathKind::Plain {
121 return None;
122 }
123
124 match &*self.segments {
125 [name] => Some(name),
126 _ => None,
127 }
128 }
129 pub fn display_verbatim<'a>(
130 &'a self,
131 db: &'a dyn crate::db::ExpandDatabase,
132 ) -> impl fmt::Display + 'a {
133 Display { db, path: self, edition: None }
134 }
135
136 pub fn display<'a>(
137 &'a self,
138 db: &'a dyn crate::db::ExpandDatabase,
139 edition: Edition,
140 ) -> impl fmt::Display + 'a {
141 Display { db, path: self, edition: Some(edition) }
142 }
143}
144
145impl Extend<Name> for ModPath {
146 fn extend<T: IntoIterator<Item = Name>>(&mut self, iter: T) {
147 self.segments.extend(iter);
148 }
149}
150
151struct Display<'a> {
152 db: &'a dyn ExpandDatabase,
153 path: &'a ModPath,
154 edition: Option<Edition>,
155}
156
157impl fmt::Display for Display<'_> {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 display_fmt_path(self.db, self.path, f, self.edition)
160 }
161}
162
163impl From<Name> for ModPath {
164 fn from(name: Name) -> ModPath {
165 ModPath::from_segments(PathKind::Plain, iter::once(name))
166 }
167}
168
169fn display_fmt_path(
170 db: &dyn ExpandDatabase,
171 path: &ModPath,
172 f: &mut fmt::Formatter<'_>,
173 edition: Option<Edition>,
174) -> fmt::Result {
175 let mut first_segment = true;
176 let mut add_segment = |s| -> fmt::Result {
177 if !first_segment {
178 f.write_str("::")?;
179 }
180 first_segment = false;
181 f.write_str(s)?;
182 Ok(())
183 };
184 match path.kind {
185 PathKind::Plain => {}
186 PathKind::SELF => add_segment("self")?,
187 PathKind::Super(n) => {
188 for _ in 0..n {
189 add_segment("super")?;
190 }
191 }
192 PathKind::Crate => add_segment("crate")?,
193 PathKind::Abs => add_segment("")?,
194 PathKind::DollarCrate(_) => add_segment("$crate")?,
195 }
196 for segment in &path.segments {
197 if !first_segment {
198 f.write_str("::")?;
199 }
200 first_segment = false;
201 match edition {
202 Some(edition) => segment.display(db, edition).fmt(f)?,
203 None => fmt::Display::fmt(segment.as_str(), f)?,
204 };
205 }
206 Ok(())
207}
208
209fn convert_path(
210 db: &dyn ExpandDatabase,
211 path: ast::Path,
212 span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
213) -> Option<ModPath> {
214 let mut segments = path.segments();
215
216 let segment = &segments.next()?;
217 let handle_super_kw = &mut |init_deg| {
218 let mut deg = init_deg;
219 let mut next_segment = None;
220 for segment in segments.by_ref() {
221 match segment.kind()? {
222 ast::PathSegmentKind::SuperKw => deg += 1,
223 ast::PathSegmentKind::Name(name) => {
224 next_segment = Some(name.as_name());
225 break;
226 }
227 ast::PathSegmentKind::Type { .. }
228 | ast::PathSegmentKind::SelfTypeKw
229 | ast::PathSegmentKind::SelfKw
230 | ast::PathSegmentKind::CrateKw => return None,
231 }
232 }
233
234 Some(ModPath::from_segments(PathKind::Super(deg), next_segment))
235 };
236
237 let mut mod_path = match segment.kind()? {
238 ast::PathSegmentKind::Name(name_ref) => {
239 if name_ref.text() == "$crate" {
240 ModPath::from_kind(
241 resolve_crate_root(db, span_for_range(name_ref.syntax().text_range()))
242 .map(PathKind::DollarCrate)
243 .unwrap_or(PathKind::Crate),
244 )
245 } else {
246 let mut res = ModPath::from_kind(
247 segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
248 );
249 res.segments.push(name_ref.as_name());
250 res
251 }
252 }
253 ast::PathSegmentKind::SelfTypeKw => {
254 ModPath::from_segments(PathKind::Plain, Some(Name::new_symbol_root(sym::Self_.clone())))
255 }
256 ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
257 ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
258 ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
259 ast::PathSegmentKind::Type { .. } => {
260 return None;
262 }
263 };
264
265 for segment in segments {
266 let name = match segment.kind()? {
267 ast::PathSegmentKind::Name(name) => name.as_name(),
268 _ => return None,
269 };
270 mod_path.segments.push(name);
271 }
272
273 if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
278 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
279 let syn_ctx = span_for_range(segment.syntax().text_range());
280 if let Some(macro_call_id) = syn_ctx.outer_expn(db) {
281 if db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner {
282 mod_path.kind = match resolve_crate_root(db, syn_ctx) {
283 Some(crate_root) => PathKind::DollarCrate(crate_root),
284 None => PathKind::Crate,
285 }
286 }
287 }
288 }
289 }
290
291 Some(mod_path)
292}
293
294fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option<ModPath> {
295 let mut leaves = tt.iter().filter_map(|tt| match tt {
296 tt::TtElement::Leaf(leaf) => Some(leaf),
297 tt::TtElement::Subtree(..) => None,
298 });
299 let mut segments = smallvec::smallvec![];
300 let kind = match leaves.next()? {
301 tt::Leaf::Punct(tt::Punct { char: ':', .. }) => match leaves.next()? {
302 tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs,
303 _ => return None,
304 },
305 tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if *text == sym::dollar_crate => {
306 resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
307 }
308 tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF,
309 tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => {
310 let mut deg = 1;
311 while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw: _ })) =
312 leaves.next()
313 {
314 if *text != sym::super_ {
315 segments.push(Name::new_symbol(text.clone(), span.ctx));
316 break;
317 }
318 deg += 1;
319 }
320 PathKind::Super(deg)
321 }
322 tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate,
323 tt::Leaf::Ident(ident) => {
324 segments.push(Name::new_symbol(ident.sym.clone(), ident.span.ctx));
325 PathKind::Plain
326 }
327 _ => return None,
328 };
329 segments.extend(leaves.filter_map(|leaf| match leaf {
330 ::tt::Leaf::Ident(ident) => Some(Name::new_symbol(ident.sym.clone(), ident.span.ctx)),
331 _ => None,
332 }));
333 Some(ModPath { kind, segments })
334}
335
336pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> Option<Crate> {
337 ctxt = ctxt.normalize_to_macro_rules(db);
344 let mut iter = marks_rev(ctxt, db).peekable();
345 let mut result_mark = None;
346 while let Some(&(mark, Transparency::Opaque)) = iter.peek() {
348 result_mark = Some(mark);
349 iter.next();
350 }
351 while let Some((mark, Transparency::SemiTransparent)) = iter.next() {
353 result_mark = Some(mark);
354 }
355
356 result_mark.map(|call| db.lookup_intern_macro_call(call.into()).def.krate)
357}
358
359pub use crate::name as __name;
360
361#[macro_export]
362macro_rules! __known_path {
363 (core::iter::IntoIterator) => {};
364 (core::iter::Iterator) => {};
365 (core::result::Result) => {};
366 (core::option::Option) => {};
367 (core::ops::Range) => {};
368 (core::ops::RangeFrom) => {};
369 (core::ops::RangeFull) => {};
370 (core::ops::RangeTo) => {};
371 (core::ops::RangeToInclusive) => {};
372 (core::ops::RangeInclusive) => {};
373 (core::future::Future) => {};
374 (core::future::IntoFuture) => {};
375 (core::fmt::Debug) => {};
376 (std::fmt::format) => {};
377 (core::ops::Try) => {};
378 (core::convert::From) => {};
379 (core::convert::TryFrom) => {};
380 (core::str::FromStr) => {};
381 ($path:path) => {
382 compile_error!("Please register your known path in the path module")
383 };
384}
385
386#[macro_export]
387macro_rules! __path {
388 ($start:ident $(:: $seg:ident)*) => ({
389 $crate::__known_path!($start $(:: $seg)*);
390 $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
391 $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
392 ])
393 });
394}
395
396pub use crate::__path as path;
397
398#[macro_export]
399macro_rules! __tool_path {
400 ($start:ident $(:: $seg:ident)*) => ({
401 $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
402 $crate::name::Name::new_symbol_root($crate::intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
403 ])
404 });
405}
406
407pub use crate::__tool_path as tool_path;