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