about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/path.rs52
-rw-r--r--crates/hir-def/src/path/lower.rs27
2 files changed, 52 insertions, 27 deletions
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 25a23fcd61a..f53bd4f6186 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -38,11 +38,11 @@ impl Display for ImportAlias {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Path {
     /// Type based path like `<T>::foo`.
-    /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::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`
-    generic_args: Box<[Option<Interned<GenericArgs>>]>,
+    /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
+    generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
 }
 
 /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -102,7 +102,7 @@ impl Path {
     ) -> Path {
         let generic_args = generic_args.into();
         assert_eq!(path.len(), generic_args.len());
-        Path { type_anchor: None, mod_path: Interned::new(path), generic_args }
+        Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
     }
 
     pub fn kind(&self) -> &PathKind {
@@ -114,7 +114,14 @@ impl Path {
     }
 
     pub fn segments(&self) -> PathSegments<'_> {
-        PathSegments { segments: self.mod_path.segments(), generic_args: &self.generic_args }
+        let s = PathSegments {
+            segments: self.mod_path.segments(),
+            generic_args: self.generic_args.as_deref(),
+        };
+        if let Some(generic_args) = s.generic_args {
+            assert_eq!(s.segments.len(), generic_args.len());
+        }
+        s
     }
 
     pub fn mod_path(&self) -> &ModPath {
@@ -131,13 +138,15 @@ impl Path {
                 self.mod_path.kind,
                 self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
             )),
-            generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec().into(),
+            generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
         };
         Some(res)
     }
 
     pub fn is_self_type(&self) -> bool {
-        self.type_anchor.is_none() && *self.generic_args == [None] && self.mod_path.is_Self()
+        self.type_anchor.is_none()
+            && self.generic_args.as_deref().is_none()
+            && self.mod_path.is_Self()
     }
 }
 
@@ -149,11 +158,11 @@ pub struct PathSegment<'a> {
 
 pub struct PathSegments<'a> {
     segments: &'a [Name],
-    generic_args: &'a [Option<Interned<GenericArgs>>],
+    generic_args: Option<&'a [Option<Interned<GenericArgs>>]>,
 }
 
 impl<'a> PathSegments<'a> {
-    pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] };
+    pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: None };
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
@@ -167,26 +176,29 @@ impl<'a> PathSegments<'a> {
         self.get(self.len().checked_sub(1)?)
     }
     pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
-        assert_eq!(self.segments.len(), self.generic_args.len());
         let res = PathSegment {
             name: self.segments.get(idx)?,
-            args_and_bindings: self.generic_args.get(idx).unwrap().as_ref().map(|it| &**it),
+            args_and_bindings: self.generic_args.and_then(|it| it.get(idx)?.as_deref()),
         };
         Some(res)
     }
     pub fn skip(&self, len: usize) -> PathSegments<'a> {
-        assert_eq!(self.segments.len(), self.generic_args.len());
-        PathSegments { segments: &self.segments[len..], generic_args: &self.generic_args[len..] }
+        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> {
-        assert_eq!(self.segments.len(), self.generic_args.len());
-        PathSegments { segments: &self.segments[..len], generic_args: &self.generic_args[..len] }
+        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.iter()).map(|(name, args)| PathSegment {
-            name,
-            args_and_bindings: args.as_ref().map(|it| &**it),
-        })
+        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() })
     }
 }
 
@@ -213,7 +225,7 @@ impl From<Name> for Path {
         Path {
             type_anchor: None,
             mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
-            generic_args: Box::new([None]),
+            generic_args: None,
         }
     }
 }
diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index d570191595b..334750a5915 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -45,8 +45,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
                                 )
                             })
                             .map(Interned::new);
+                        if let Some(_) = args {
+                            generic_args.resize(segments.len(), None);
+                            generic_args.push(args);
+                        }
                         segments.push(name);
-                        generic_args.push(args)
                     }
                     Either::Right(crate_id) => {
                         kind = PathKind::DollarCrate(crate_id);
@@ -56,7 +59,6 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
             }
             ast::PathSegmentKind::SelfTypeKw => {
                 segments.push(name![Self]);
-                generic_args.push(None)
             }
             ast::PathSegmentKind::Type { type_ref, trait_ref } => {
                 assert!(path.qualifier().is_none()); // this can only occur at the first segment
@@ -77,11 +79,15 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
                         kind = mod_path.kind;
 
                         segments.extend(mod_path.segments().iter().cloned().rev());
-                        generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
+                        if let Some(path_generic_args) = path_generic_args {
+                            generic_args.resize(segments.len() - num_segments, None);
+                            generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
+                        } else {
+                            generic_args.resize(segments.len(), None);
+                        }
 
                         // Insert the type reference (T in the above example) as Self parameter for the trait
-                        let last_segment =
-                            generic_args.iter_mut().rev().nth(num_segments.saturating_sub(1))?;
+                        let last_segment = generic_args.get_mut(segments.len() - num_segments)?;
                         let mut args_inner = match last_segment {
                             Some(it) => it.as_ref().clone(),
                             None => GenericArgs::empty(),
@@ -115,7 +121,10 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
         };
     }
     segments.reverse();
-    generic_args.reverse();
+    if !generic_args.is_empty() {
+        generic_args.resize(segments.len(), None);
+        generic_args.reverse();
+    }
 
     if segments.is_empty() && kind == PathKind::Plain && type_anchor.is_none() {
         // plain empty paths don't exist, this means we got a single `self` segment as our path
@@ -135,7 +144,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
     }
 
     let mod_path = Interned::new(ModPath::from_segments(kind, segments));
-    return Some(Path { type_anchor, mod_path, generic_args: generic_args.into() });
+    return Some(Path {
+        type_anchor,
+        mod_path,
+        generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
+    });
 
     fn qualifier(path: &ast::Path) -> Option<ast::Path> {
         if let Some(q) = path.qualifier() {