| //! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`. |
| mod lower; |
| |
| use std::{ |
| fmt::{self, Display}, |
| iter, |
| }; |
| |
| use crate::{ |
| lang_item::LangItemTarget, |
| lower::LowerCtx, |
| type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, |
| }; |
| use hir_expand::name::Name; |
| use intern::Interned; |
| use span::Edition; |
| use syntax::ast; |
| |
| pub use hir_expand::mod_path::{path, ModPath, PathKind}; |
| |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub enum ImportAlias { |
| /// Unnamed alias, as in `use Foo as _;` |
| Underscore, |
| /// Named alias |
| Alias(Name), |
| } |
| |
| impl ImportAlias { |
| pub fn display(&self, edition: Edition) -> impl Display + '_ { |
| ImportAliasDisplay { value: self, edition } |
| } |
| } |
| |
| struct ImportAliasDisplay<'a> { |
| value: &'a ImportAlias, |
| edition: Edition, |
| } |
| impl Display for ImportAliasDisplay<'_> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match self.value { |
| ImportAlias::Underscore => f.write_str("_"), |
| ImportAlias::Alias(name) => Display::fmt(&name.display_no_db(self.edition), f), |
| } |
| } |
| } |
| |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub enum Path { |
| /// A normal path |
| Normal { |
| /// Type based path like `<T>::foo`. |
| /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`. |
| type_anchor: Option<Interned<TypeRef>>, |
| mod_path: Interned<ModPath>, |
| /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. |
| generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>, |
| }, |
| /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these |
| /// links via a normal path since they might be private and not accessible in the usage place. |
| LangItem(LangItemTarget, Option<Name>), |
| } |
| |
| /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This |
| /// also includes bindings of associated types, like in `Iterator<Item = Foo>`. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub struct GenericArgs { |
| pub args: Box<[GenericArg]>, |
| /// This specifies whether the args contain a Self type as the first |
| /// element. This is the case for path segments like `<T as Trait>`, where |
| /// `T` is actually a type parameter for the path `Trait` specifying the |
| /// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type |
| /// is left out. |
| pub has_self_type: bool, |
| /// Associated type bindings like in `Iterator<Item = T>`. |
| pub bindings: Box<[AssociatedTypeBinding]>, |
| /// Whether these generic args were desugared from `Trait(Arg) -> Output` |
| /// parenthesis notation typically used for the `Fn` traits. |
| pub desugared_from_fn: bool, |
| } |
| |
| /// An associated type binding like in `Iterator<Item = T>`. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub struct AssociatedTypeBinding { |
| /// The name of the associated type. |
| pub name: Name, |
| /// The generic arguments to the associated type. e.g. For `Trait<Assoc<'a, T> = &'a T>`, this |
| /// would be `['a, T]`. |
| pub args: Option<Interned<GenericArgs>>, |
| /// The type bound to this associated type (in `Item = T`, this would be the |
| /// `T`). This can be `None` if there are bounds instead. |
| pub type_ref: Option<TypeRef>, |
| /// Bounds for the associated type, like in `Iterator<Item: |
| /// SomeOtherTrait>`. (This is the unstable `associated_type_bounds` |
| /// feature.) |
| pub bounds: Box<[Interned<TypeBound>]>, |
| } |
| |
| /// A single generic argument. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub enum GenericArg { |
| Type(TypeRef), |
| Lifetime(LifetimeRef), |
| Const(ConstRef), |
| } |
| |
| impl Path { |
| /// Converts an `ast::Path` to `Path`. Works with use trees. |
| /// It correctly handles `$crate` based path from macro call. |
| pub fn from_src(ctx: &LowerCtx<'_>, path: ast::Path) -> Option<Path> { |
| lower::lower_path(ctx, path) |
| } |
| |
| /// Converts a known mod path to `Path`. |
| pub fn from_known_path( |
| path: ModPath, |
| generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>, |
| ) -> Path { |
| let generic_args = generic_args.into(); |
| assert_eq!(path.len(), generic_args.len()); |
| Path::Normal { |
| type_anchor: None, |
| mod_path: Interned::new(path), |
| generic_args: Some(generic_args), |
| } |
| } |
| |
| /// Converts a known mod path to `Path`. |
| pub fn from_known_path_with_no_generic(path: ModPath) -> Path { |
| Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None } |
| } |
| |
| pub fn kind(&self) -> &PathKind { |
| match self { |
| Path::Normal { mod_path, .. } => &mod_path.kind, |
| Path::LangItem(..) => &PathKind::Abs, |
| } |
| } |
| |
| pub fn type_anchor(&self) -> Option<&TypeRef> { |
| match self { |
| Path::Normal { type_anchor, .. } => type_anchor.as_deref(), |
| Path::LangItem(..) => None, |
| } |
| } |
| |
| pub fn segments(&self) -> PathSegments<'_> { |
| match self { |
| Path::Normal { mod_path, generic_args, .. } => { |
| let s = PathSegments { |
| segments: mod_path.segments(), |
| generic_args: generic_args.as_deref(), |
| }; |
| if let Some(generic_args) = s.generic_args { |
| assert_eq!(s.segments.len(), generic_args.len()); |
| } |
| s |
| } |
| Path::LangItem(_, seg) => PathSegments { |
| segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)), |
| generic_args: None, |
| }, |
| } |
| } |
| |
| pub fn mod_path(&self) -> Option<&ModPath> { |
| match self { |
| Path::Normal { mod_path, .. } => Some(mod_path), |
| Path::LangItem(..) => None, |
| } |
| } |
| |
| pub fn qualifier(&self) -> Option<Path> { |
| let Path::Normal { mod_path, generic_args, type_anchor } = self else { |
| return None; |
| }; |
| if mod_path.is_ident() { |
| return None; |
| } |
| let res = Path::Normal { |
| type_anchor: type_anchor.clone(), |
| mod_path: Interned::new(ModPath::from_segments( |
| mod_path.kind, |
| mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(), |
| )), |
| generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), |
| }; |
| Some(res) |
| } |
| |
| pub fn is_self_type(&self) -> bool { |
| let Path::Normal { mod_path, generic_args, type_anchor } = self else { |
| return false; |
| }; |
| type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self() |
| } |
| } |
| |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub struct PathSegment<'a> { |
| pub name: &'a Name, |
| pub args_and_bindings: Option<&'a GenericArgs>, |
| } |
| |
| pub struct PathSegments<'a> { |
| segments: &'a [Name], |
| generic_args: Option<&'a [Option<Interned<GenericArgs>>]>, |
| } |
| |
| impl<'a> PathSegments<'a> { |
| pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: None }; |
| pub fn is_empty(&self) -> bool { |
| self.len() == 0 |
| } |
| pub fn len(&self) -> usize { |
| self.segments.len() |
| } |
| pub fn first(&self) -> Option<PathSegment<'a>> { |
| self.get(0) |
| } |
| pub fn last(&self) -> Option<PathSegment<'a>> { |
| self.get(self.len().checked_sub(1)?) |
| } |
| pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> { |
| let res = PathSegment { |
| name: self.segments.get(idx)?, |
| args_and_bindings: self.generic_args.and_then(|it| it.get(idx)?.as_deref()), |
| }; |
| Some(res) |
| } |
| pub fn skip(&self, len: usize) -> PathSegments<'a> { |
| PathSegments { |
| segments: self.segments.get(len..).unwrap_or(&[]), |
| generic_args: self.generic_args.and_then(|it| it.get(len..)), |
| } |
| } |
| pub fn take(&self, len: usize) -> PathSegments<'a> { |
| PathSegments { |
| segments: self.segments.get(..len).unwrap_or(self.segments), |
| generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)), |
| } |
| } |
| pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> { |
| self.segments |
| .iter() |
| .zip(self.generic_args.into_iter().flatten().chain(iter::repeat(&None))) |
| .map(|(name, args)| PathSegment { name, args_and_bindings: args.as_deref() }) |
| } |
| } |
| |
| impl GenericArgs { |
| pub(crate) fn from_ast( |
| lower_ctx: &LowerCtx<'_>, |
| node: ast::GenericArgList, |
| ) -> Option<GenericArgs> { |
| lower::lower_generic_args(lower_ctx, node) |
| } |
| |
| pub(crate) fn empty() -> GenericArgs { |
| GenericArgs { |
| args: Box::default(), |
| has_self_type: false, |
| bindings: Box::default(), |
| desugared_from_fn: false, |
| } |
| } |
| } |
| |
| impl From<Name> for Path { |
| fn from(name: Name) -> Path { |
| Path::Normal { |
| type_anchor: None, |
| mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))), |
| generic_args: None, |
| } |
| } |
| } |
| |
| impl From<Name> for Box<Path> { |
| fn from(name: Name) -> Box<Path> { |
| Box::new(Path::from(name)) |
| } |
| } |