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