1 //! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
15 use smallvec::SmallVec;
16 use syntax::{ast, AstNode};
18 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21 segments: SmallVec<[Name; 1]>,
24 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25 pub struct EscapedModPath<'a>(&'a ModPath);
27 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30 /// `self::` is `Super(0)`
33 /// Absolute path (::foo)
35 /// `$crate` from macro expansion
40 pub fn from_src(db: &dyn AstDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
41 convert_path(db, None, path, hygiene)
44 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
45 let segments = segments.into_iter().collect();
46 ModPath { kind, segments }
49 /// Creates a `ModPath` from a `PathKind`, with no extra path segments.
50 pub const fn from_kind(kind: PathKind) -> ModPath {
51 ModPath { kind, segments: SmallVec::new_const() }
54 pub fn segments(&self) -> &[Name] {
58 pub fn push_segment(&mut self, segment: Name) {
59 self.segments.push(segment);
62 pub fn pop_segment(&mut self) -> Option<Name> {
66 /// Returns the number of segments in the path (counting special segments like `$crate` and
68 pub fn len(&self) -> usize {
72 PathKind::Super(i) => i as usize,
75 PathKind::DollarCrate(_) => 1,
79 pub fn is_ident(&self) -> bool {
80 self.as_ident().is_some()
83 pub fn is_self(&self) -> bool {
84 self.kind == PathKind::Super(0) && self.segments.is_empty()
87 #[allow(non_snake_case)]
88 pub fn is_Self(&self) -> bool {
89 self.kind == PathKind::Plain
90 && matches!(&*self.segments, [name] if *name == known::SELF_TYPE)
93 /// If this path is a single identifier, like `foo`, return its name.
94 pub fn as_ident(&self) -> Option<&Name> {
95 if self.kind != PathKind::Plain {
99 match &*self.segments {
100 [name] => Some(name),
105 pub fn escaped(&self) -> EscapedModPath<'_> {
109 fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
110 let mut first_segment = true;
111 let mut add_segment = |s| -> fmt::Result {
115 first_segment = false;
120 PathKind::Plain => {}
121 PathKind::Super(0) => add_segment("self")?,
122 PathKind::Super(n) => {
124 add_segment("super")?;
127 PathKind::Crate => add_segment("crate")?,
128 PathKind::Abs => add_segment("")?,
129 PathKind::DollarCrate(_) => add_segment("$crate")?,
131 for segment in &self.segments {
135 first_segment = false;
137 segment.escaped().fmt(f)?
146 impl Display for ModPath {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 impl<'a> Display for EscapedModPath<'a> {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 impl From<Name> for ModPath {
159 fn from(name: Name) -> ModPath {
160 ModPath::from_segments(PathKind::Plain, iter::once(name))
165 db: &dyn AstDatabase,
166 prefix: Option<ModPath>,
169 ) -> Option<ModPath> {
170 let prefix = match path.qualifier() {
171 Some(qual) => Some(convert_path(db, prefix, qual, hygiene)?),
175 let segment = path.segment()?;
176 let mut mod_path = match segment.kind()? {
177 ast::PathSegmentKind::Name(name_ref) => {
178 match hygiene.name_ref_to_name(db, name_ref) {
179 Either::Left(name) => {
180 // no type args in use
181 let mut res = prefix.unwrap_or_else(|| {
183 segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs),
186 res.segments.push(name);
189 Either::Right(crate_id) => {
190 return Some(ModPath::from_segments(
191 PathKind::DollarCrate(crate_id),
197 ast::PathSegmentKind::SelfTypeKw => {
198 if prefix.is_some() {
201 ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
203 ast::PathSegmentKind::CrateKw => {
204 if prefix.is_some() {
207 ModPath::from_segments(PathKind::Crate, iter::empty())
209 ast::PathSegmentKind::SelfKw => {
210 if prefix.is_some() {
213 ModPath::from_segments(PathKind::Super(0), iter::empty())
215 ast::PathSegmentKind::SuperKw => {
216 let nested_super_count = match prefix.map(|p| p.kind) {
217 Some(PathKind::Super(n)) => n,
218 Some(_) => return None,
222 ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
224 ast::PathSegmentKind::Type { .. } => {
225 // not allowed in imports
230 // handle local_inner_macros :
231 // Basically, even in rustc it is quite hacky:
232 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
233 // We follow what it did anyway :)
234 if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
235 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
236 if let Some(crate_id) = hygiene.local_inner_macros(db, path) {
237 mod_path.kind = PathKind::DollarCrate(crate_id);
245 pub use crate::name as __name;
248 macro_rules! __known_path {
249 (core::iter::IntoIterator) => {};
250 (core::iter::Iterator) => {};
251 (core::result::Result) => {};
252 (core::option::Option) => {};
253 (core::ops::Range) => {};
254 (core::ops::RangeFrom) => {};
255 (core::ops::RangeFull) => {};
256 (core::ops::RangeTo) => {};
257 (core::ops::RangeToInclusive) => {};
258 (core::ops::RangeInclusive) => {};
259 (core::future::Future) => {};
260 (core::ops::Try) => {};
262 compile_error!("Please register your known path in the path module")
267 macro_rules! __path {
268 ($start:ident $(:: $seg:ident)*) => ({
269 $crate::__known_path!($start $(:: $seg)*);
270 $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
271 $crate::mod_path::__name![$start], $($crate::mod_path::__name![$seg],)*
276 pub use crate::__path as path;