Repository: rust-analyzer/rust-analyzer
Pull request #1817 (2019-09-11 18:41:53)

Support path starting with a type
The path syntax `<Ty>::foo`

matklad: Note though that https://github.com/rust-analyzer/rust-analyzer/pull/1833
also breaks a lot of things

On Fri, 13 Sep 2019 at 13:15, uHOOCCOOHu <notifications@github.com> wrote:

> *@uHOOCCOOHu* commented on this pull request.
> ------------------------------
>
> In crates/ra_hir/src/path.rs
> <https://github.com/rust-analyzer/rust-analyzer/pull/1817#discussion_r324126888>
> :
>
> > @@ -10,6 +10,7 @@ use crate::{name, type_ref::TypeRef, AsName, Name};
>  #[derive(Debug, Clone, PartialEq, Eq, Hash)]
>  pub struct Path {
>      pub kind: PathKind,
> +    pub type_ref: Option<Box<TypeRef>>,
>
> It should be really. But it will break many more things and make PathKind
> no longer Copy. I don't know if we should do this.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/rust-analyzer/rust-analyzer/pull/1817?email_source=notifications&email_token=AANB3M4JNS2LHYNZGSKUYZDQJNR47A5CNFSM4IVXQHDKYY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCEUXCCY#discussion_r324126888>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AANB3M7Q6SHMDWMD4JKTFDTQJNR47ANCNFSM4IVXQHDA>
> .
>


Patch:
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 1158adbbc0..5aae985461 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -504,7 +504,7 @@ fn apply_auto_import(
     }
 }
 
-pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
+pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
     let mut ps = Vec::<SmolStr>::with_capacity(10);
     match path.kind {
         hir::PathKind::Abs => ps.push("".into()),
@@ -512,11 +512,12 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
         hir::PathKind::Plain => {}
         hir::PathKind::Self_ => ps.push("self".into()),
         hir::PathKind::Super => ps.push("super".into()),
+        hir::PathKind::Type(_) => return None,
     }
     for s in path.segments.iter() {
         ps.push(s.name.to_string().into());
     }
-    ps
+    Some(ps)
 }
 
 // This function produces sequence of text edits into edit
@@ -552,7 +553,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
     }
 
     let hir_path = hir::Path::from_ast(path.clone())?;
-    let segments = collect_hir_path_segments(&hir_path);
+    let segments = collect_hir_path_segments(&hir_path)?;
     if segments.len() < 2 {
         return None;
     }
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs
index 030ec53a29..f06e5ec07e 100644
--- a/crates/ra_hir/src/expr/validation.rs
+++ b/crates/ra_hir/src/expr/validation.rs
@@ -7,8 +7,7 @@ use crate::{
     db::HirDatabase,
     diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
     expr::AstPtr,
-    name,
-    path::{PathKind, PathSegment},
+    path::known,
     ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
     Adt, Function, Name, Path,
 };
@@ -108,14 +107,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             None => return,
         };
 
-        let std_result_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::RESULT_MOD, args_and_bindings: None },
-                PathSegment { name: name::RESULT_TYPE, args_and_bindings: None },
-            ],
-        };
+        let std_result_path = known::std_result_result();
 
         let resolver = self.func.resolver(db);
         let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index abdfec296f..1bf993ffb0 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -85,6 +85,7 @@ impl AsName for ra_db::Dependency {
     }
 }
 
+// Primitives
 pub(crate) const ISIZE: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"isize"));
 pub(crate) const I8: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"i8"));
 pub(crate) const I16: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i16"));
@@ -102,24 +103,30 @@ pub(crate) const F64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"f64")
 pub(crate) const BOOL: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"bool"));
 pub(crate) const CHAR: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"char"));
 pub(crate) const STR: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"str"));
+
+// Special names
 pub(crate) const SELF_PARAM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"self"));
 pub(crate) const SELF_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Self"));
 pub(crate) const MACRO_RULES: Name = Name::new(SmolStr::new_inline_from_ascii(11, b"macro_rules"));
+
+// Components of known path (value or mod name)
 pub(crate) const STD: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"std"));
 pub(crate) const ITER: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"iter"));
-pub(crate) const INTO_ITERATOR: Name =
-    Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
-pub(crate) const ITEM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
 pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops"));
-pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
-pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
-pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
+pub(crate) const FUTURE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
+pub(crate) const RESULT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
+pub(crate) const BOXED: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"boxed"));
+
+// Components of known path (type name)
+pub(crate) const INTO_ITERATOR_TYPE: Name =
+    Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
+pub(crate) const ITEM_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
+pub(crate) const TRY_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
+pub(crate) const OK_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
 pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future"));
-pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
 pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result"));
-pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
-pub(crate) const TARGET: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target"));
-pub(crate) const BOXED_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"boxed"));
+pub(crate) const OUTPUT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
+pub(crate) const TARGET_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target"));
 pub(crate) const BOX_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Box"));
 
 fn resolve_name(text: &SmolStr) -> SmolStr {
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 7922e3f7e6..b808a0c36c 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -382,6 +382,11 @@ impl CrateDefMap {
                     return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                 }
             }
+            PathKind::Type(_) => {
+                // This is handled in `infer::infer_path_expr`
+                // The result returned here does not matter
+                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
+            }
         };
 
         for (i, segment) in segments {
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index d6c78593bc..a61161b63a 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -1,4 +1,4 @@
-use std::sync::Arc;
+use std::{iter, sync::Arc};
 
 use ra_syntax::{
     ast::{self, NameOwner, TypeAscriptionOwner},
@@ -42,7 +42,7 @@ pub enum GenericArg {
     // or lifetime...
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum PathKind {
     Plain,
     Self_,
@@ -50,6 +50,8 @@ pub enum PathKind {
     Crate,
     // Absolute path
     Abs,
+    // Type based path like `<T>::foo`
+    Type(Box<TypeRef>),
 }
 
 impl Path {
@@ -63,6 +65,16 @@ impl Path {
         }
     }
 
+    pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
+        Path {
+            kind,
+            segments: segments
+                .into_iter()
+                .map(|name| PathSegment { name, args_and_bindings: None })
+                .collect(),
+        }
+    }
+
     /// Converts an `ast::Path` to `Path`. Works with use trees.
     pub fn from_ast(mut path: ast::Path) -> Option<Path> {
         let mut kind = PathKind::Plain;
@@ -92,24 +104,32 @@ impl Path {
                 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
                     assert!(path.qualifier().is_none()); // this can only occur at the first segment
 
-                    // FIXME: handle <T> syntax (type segments without trait)
-
-                    // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
-                    let path = Path::from_ast(trait_ref?.path()?)?;
-                    kind = path.kind;
-                    let mut prefix_segments = path.segments;
-                    prefix_segments.reverse();
-                    segments.extend(prefix_segments);
-                    // Insert the type reference (T in the above example) as Self parameter for the trait
                     let self_type = TypeRef::from_ast(type_ref?);
-                    let mut last_segment = segments.last_mut()?;
-                    if last_segment.args_and_bindings.is_none() {
-                        last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
-                    };
-                    let args = last_segment.args_and_bindings.as_mut().unwrap();
-                    let mut args_inner = Arc::make_mut(args);
-                    args_inner.has_self_type = true;
-                    args_inner.args.insert(0, GenericArg::Type(self_type));
+
+                    match trait_ref {
+                        // <T>::foo
+                        None => {
+                            kind = PathKind::Type(Box::new(self_type));
+                        }
+                        // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
+                        Some(trait_ref) => {
+                            let path = Path::from_ast(trait_ref.path()?)?;
+                            kind = path.kind;
+                            let mut prefix_segments = path.segments;
+                            prefix_segments.reverse();
+                            segments.extend(prefix_segments);
+                            // Insert the type reference (T in the above example) as Self parameter for the trait
+                            let mut last_segment = segments.last_mut()?;
+                            if last_segment.args_and_bindings.is_none() {
+                                last_segment.args_and_bindings =
+                                    Some(Arc::new(GenericArgs::empty()));
+                            };
+                            let args = last_segment.args_and_bindings.as_mut().unwrap();
+                            let mut args_inner = Arc::make_mut(args);
+                            args_inner.has_self_type = true;
+                            args_inner.args.insert(0, GenericArg::Type(self_type));
+                        }
+                    }
                 }
                 ast::PathSegmentKind::CrateKw => {
                     kind = PathKind::Crate;
@@ -214,7 +234,7 @@ impl GenericArgs {
         }
         if let Some(ret_type) = ret_type {
             let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
-            bindings.push((name::OUTPUT, type_ref))
+            bindings.push((name::OUTPUT_TYPE, type_ref))
         }
         if args.is_empty() && bindings.is_empty() {
             None
@@ -230,10 +250,7 @@ impl GenericArgs {
 
 impl From<Name> for Path {
     fn from(name: Name) -> Path {
-        Path {
-            kind: PathKind::Plain,
-            segments: vec![PathSegment { name, args_and_bindings: None }],
-        }
+        Path::from_simple_segments(PathKind::Plain, iter::once(name))
     }
 }
 
@@ -287,6 +304,7 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
     let segment = path.segment()?;
     let res = match segment.kind()? {
         ast::PathSegmentKind::Name(name) => {
+            // no type args in use
             let mut res = prefix
                 .unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
             res.segments.push(PathSegment {
@@ -299,19 +317,19 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
             if prefix.is_some() {
                 return None;
             }
-            Path { kind: PathKind::Crate, segments: Vec::new() }
+            Path::from_simple_segments(PathKind::Crate, iter::empty())
         }
         ast::PathSegmentKind::SelfKw => {
             if prefix.is_some() {
                 return None;
             }
-            Path { kind: PathKind::Self_, segments: Vec::new() }
+            Path::from_simple_segments(PathKind::Self_, iter::empty())
         }
         ast::PathSegmentKind::SuperKw => {
             if prefix.is_some() {
                 return None;
             }
-            Path { kind: PathKind::Super, segments: Vec::new() }
+            Path::from_simple_segments(PathKind::Super, iter::empty())
         }
         ast::PathSegmentKind::Type { .. } => {
             // not allowed in imports
@@ -320,3 +338,31 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
     };
     Some(res)
 }
+
+pub mod known {
+    use super::{Path, PathKind};
+    use crate::name;
+
+    pub fn std_iter_into_iterator() -> Path {
+        Path::from_simple_segments(
+            PathKind::Abs,
+            vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
+        )
+    }
+
+    pub fn std_ops_try() -> Path {
+        Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
+    }
+
+    pub fn std_result_result() -> Path {
+        Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
+    }
+
+    pub fn std_future_future() -> Path {
+        Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
+    }
+
+    pub fn std_boxed_box() -> Path {
+        Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
+    }
+}
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index 3207b6626d..bb69159010 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -15,6 +15,7 @@ use crate::{
     name::{Name, SELF_PARAM, SELF_TYPE},
     nameres::{CrateDefMap, CrateModuleId, PerNs},
     path::{Path, PathKind},
+    type_ref::TypeRef,
     Adt, BuiltinType, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct,
     Trait, TypeAlias,
 };
@@ -64,9 +65,10 @@ pub enum TypeNs {
 }
 
 #[derive(Debug)]
-pub enum ValueOrPartial {
+pub enum ResolveValueResult<'a> {
     ValueNs(ValueNs),
     Partial(TypeNs, usize),
+    TypeRef(&'a TypeRef),
 }
 
 #[derive(Debug)]
@@ -183,11 +185,15 @@ impl Resolver {
         Some(res)
     }
 
-    pub(crate) fn resolve_path_in_value_ns(
+    pub(crate) fn resolve_path_in_value_ns<'p>(
         &self,
         db: &impl HirDatabase,
-        path: &Path,
-    ) -> Option<ValueOrPartial> {
+        path: &'p Path,
+    ) -> Option<ResolveValueResult<'p>> {
+        if let PathKind::Type(type_ref) = &path.kind {
+            return Some(ResolveValueResult::TypeRef(type_ref));
+        }
+
         let n_segments = path.segments.len();
         let tmp = SELF_PARAM;
         let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
@@ -208,7 +214,7 @@ impl Resolver {
                         .find(|entry| entry.name() == first_name);
 
                     if let Some(e) = entry {
-                        return Some(ValueOrPartial::ValueNs(ValueNs::LocalBinding(e.pat())));
+                        return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.pat())));
                     }
                 }
                 Scope::ExprScope(_) => continue,
@@ -216,7 +222,7 @@ impl Resolver {
                 Scope::GenericParams(params) if n_segments > 1 => {
                     if let Some(param) = params.find_by_name(first_name) {
                         let ty = TypeNs::GenericParam(param.idx);
-                        return Some(ValueOrPartial::Partial(ty, 1));
+                        return Some(ResolveValueResult::Partial(ty, 1));
                     }
                 }
                 Scope::GenericParams(_) => continue,
@@ -224,7 +230,7 @@ impl Resolver {
                 Scope::ImplBlockScope(impl_) if n_segments > 1 => {
                     if first_name == &SELF_TYPE {
                         let ty = TypeNs::SelfType(*impl_);
-                        return Some(ValueOrPartial::Partial(ty, 1));
+                        return Some(ResolveValueResult::Partial(ty, 1));
                     }
                 }
                 Scope::ImplBlockScope(_) => continue,
@@ -247,7 +253,7 @@ impl Resolver {
                                 | ModuleDef::BuiltinType(_)
                                 | ModuleDef::Module(_) => return None,
                             };
-                            Some(ValueOrPartial::ValueNs(value))
+                            Some(ResolveValueResult::ValueNs(value))
                         }
                         Some(idx) => {
                             let ty = match module_def.take_types()? {
@@ -262,7 +268,7 @@ impl Resolver {
                                 | ModuleDef::Const(_)
                                 | ModuleDef::Static(_) => return None,
                             };
-                            Some(ValueOrPartial::Partial(ty, idx))
+                            Some(ResolveValueResult::Partial(ty, idx))
                         }
                     };
                 }
@@ -277,8 +283,8 @@ impl Resolver {
         path: &Path,
     ) -> Option<ValueNs> {
         match self.resolve_path_in_value_ns(db, path)? {
-            ValueOrPartial::ValueNs(it) => Some(it),
-            ValueOrPartial::Partial(..) => None,
+            ResolveValueResult::ValueNs(it) => Some(it),
+            ResolveValueResult::Partial(..) | ResolveValueResult::TypeRef(_) => None,
         }
     }
 
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index cff55b6403..2a907c9f1c 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -25,8 +25,7 @@ use crate::{
         BodySourceMap,
     },
     ids::LocationCtx,
-    name,
-    path::{PathKind, PathSegment},
+    path::known,
     resolve::{ScopeDef, TypeNs, ValueNs},
     ty::method_resolution::implements_trait,
     AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirFileId, MacroDef,
@@ -433,14 +432,7 @@ impl SourceAnalyzer {
     /// Checks that particular type `ty` implements `std::future::Future`.
     /// This function is used in `.await` syntax completion.
     pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
-        let std_future_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
-                PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
-            ],
-        };
+        let std_future_path = known::std_future_future();
 
         let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) {
             Some(it) => it,
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs
index 5ba7cf2e0f..94f8ecdc9f 100644
--- a/crates/ra_hir/src/ty/autoderef.rs
+++ b/crates/ra_hir/src/ty/autoderef.rs
@@ -42,7 +42,7 @@ fn deref_by_trait(
         crate::lang_item::LangItemTarget::Trait(t) => t,
         _ => return None,
     };
-    let target = deref_trait.associated_type_by_name(db, &name::TARGET)?;
+    let target = deref_trait.associated_type_by_name(db, &name::TARGET_TYPE)?;
 
     if target.generic_params(db).count_params_including_parent() != 1 {
         // the Target type + Deref trait should only have one generic parameter,
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index bf57bb3b70..785499f776 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -44,11 +44,12 @@ use crate::{
     generics::{GenericParams, HasGenericParams},
     name,
     nameres::Namespace,
-    path::{GenericArg, GenericArgs, PathKind, PathSegment},
-    resolve::{Resolver, TypeNs, ValueNs, ValueOrPartial},
+    path::{known, GenericArg, GenericArgs},
+    resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
     ty::infer::diagnostics::InferenceDiagnostic,
     type_ref::{Mutability, TypeRef},
-    Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, Name, Path, StructField,
+    Adt, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path,
+    StructField,
 };
 
 mod unify;
@@ -470,9 +471,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
 
         let (value, self_subst) = match value_or_partial {
-            ValueOrPartial::ValueNs(it) => (it, None),
-            ValueOrPartial::Partial(def, remaining_index) => {
-                self.resolve_assoc_item(def, path, remaining_index, id)?
+            ResolveValueResult::ValueNs(it) => (it, None),
+            ResolveValueResult::Partial(def, remaining_index) => {
+                self.resolve_assoc_item(Either::A(def), path, remaining_index, id)?
+            }
+            ResolveValueResult::TypeRef(type_ref) => {
+                let ty = self.make_ty(type_ref);
+                self.resolve_assoc_item(Either::B(ty), path, 0, id)?
             }
         };
 
@@ -503,7 +508,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
     fn resolve_assoc_item(
         &mut self,
-        mut def: TypeNs,
+        mut def_or_ty: Either<TypeNs, Ty>,
         path: &Path,
         remaining_index: usize,
         id: ExprOrPatId,
@@ -516,30 +521,33 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         // resolve intermediate segments
         for (i, segment) in path.segments[remaining_index..].iter().enumerate() {
             let is_last_segment = i == path.segments[remaining_index..].len() - 1;
-            ty = {
-                let typable: TypableDef = match def {
-                    TypeNs::Adt(it) => it.into(),
-                    TypeNs::TypeAlias(it) => it.into(),
-                    TypeNs::BuiltinType(it) => it.into(),
-                    // FIXME associated item of traits, generics, and Self
-                    TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => {
-                        return None;
-                    }
-                    // FIXME: report error here
-                    TypeNs::EnumVariant(_) => return None,
-                };
-
-                let ty = self.db.type_for_def(typable, Namespace::Types);
+            ty = match def_or_ty {
+                Either::A(def) => {
+                    let typable: TypableDef = match def {
+                        TypeNs::Adt(it) => it.into(),
+                        TypeNs::TypeAlias(it) => it.into(),
+                        TypeNs::BuiltinType(it) => it.into(),
+                        // FIXME associated item of traits, generics, and Self
+                        TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => {
+                            return None;
+                        }
+                        // FIXME: report error here
+                        TypeNs::EnumVariant(_) => return None,
+                    };
 
-                // For example, this substs will take `Gen::*<u32>*::make`
-                assert!(remaining_index > 0);
-                let substs = Ty::substs_from_path_segment(
-                    self.db,
-                    &self.resolver,
-                    &path.segments[remaining_index + i - 1],
-                    typable,
-                );
-                ty.subst(&substs)
+                    let ty = self.db.type_for_def(typable, Namespace::Types);
+
+                    // For example, this substs will take `Gen::*<u32>*::make`
+                    assert!(remaining_index > 0);
+                    let substs = Ty::substs_from_path_segment(
+                        self.db,
+                        &self.resolver,
+                        &path.segments[remaining_index + i - 1],
+                        typable,
+                    );
+                    ty.subst(&substs)
+                }
+                Either::B(ty) => ty,
             };
             if is_last_segment {
                 break;
@@ -550,15 +558,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             log::debug!("looking for path segment: {:?}", segment);
 
             let ty = mem::replace(&mut ty, Ty::Unknown);
-            def = ty.iterate_impl_items(self.db, krate, |item| {
+            def_or_ty = ty.iterate_impl_items(self.db, krate, |item| {
                 match item {
                     crate::ImplItem::Method(_) => None,
                     crate::ImplItem::Const(_) => None,
 
                     // FIXME: Resolve associated types
                     crate::ImplItem::TypeAlias(_) => {
-                        // Some(TypeNs::TypeAlias(..))
-                        None::<TypeNs>
+                        // Some(Either::A(TypeNs::TypeAlias(..)))
+                        None
                     }
                 }
             })?;
@@ -1434,57 +1442,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
     }
 
     fn resolve_into_iter_item(&self) -> Option<TypeAlias> {
-        let into_iter_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::ITER, args_and_bindings: None },
-                PathSegment { name: name::INTO_ITERATOR, args_and_bindings: None },
-            ],
-        };
-
-        let trait_ = self.resolver.resolve_known_trait(self.db, &into_iter_path)?;
-        trait_.associated_type_by_name(self.db, &name::ITEM)
+        let path = known::std_iter_into_iterator();
+        let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
+        trait_.associated_type_by_name(self.db, &name::ITEM_TYPE)
     }
 
     fn resolve_ops_try_ok(&self) -> Option<TypeAlias> {
-        let ops_try_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::OPS, args_and_bindings: None },
-                PathSegment { name: name::TRY, args_and_bindings: None },
-            ],
-        };
-
-        let trait_ = self.resolver.resolve_known_trait(self.db, &ops_try_path)?;
-        trait_.associated_type_by_name(self.db, &name::OK)
+        let path = known::std_ops_try();
+        let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
+        trait_.associated_type_by_name(self.db, &name::OK_TYPE)
     }
 
     fn resolve_future_future_output(&self) -> Option<TypeAlias> {
-        let future_future_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
-                PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
-            ],
-        };
-
-        let trait_ = self.resolver.resolve_known_trait(self.db, &future_future_path)?;
-        trait_.associated_type_by_name(self.db, &name::OUTPUT)
+        let path = known::std_future_future();
+        let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
+        trait_.associated_type_by_name(self.db, &name::OUTPUT_TYPE)
     }
 
     fn resolve_boxed_box(&self) -> Option<Adt> {
-        let boxed_box_path = Path {
-            kind: PathKind::Abs,
-            segments: vec![
-                PathSegment { name: name::STD, args_and_bindings: None },
-                PathSegment { name: name::BOXED_MOD, args_and_bindings: None },
-                PathSegment { name: name::BOX_TYPE, args_and_bindings: None },
-            ],
-        };
-        let struct_ = self.resolver.resolve_known_struct(self.db, &boxed_box_path)?;
+        let path = known::std_boxed_box();
+        let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
         Some(Adt::Struct(struct_))
     }
 }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 591ac8ca3d..dc1f8c82cb 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -2385,6 +2385,9 @@ impl PathSegment {
     pub fn ret_type(&self) -> Option<RetType> {
         AstChildren::new(&self.syntax).next()
     }
+    pub fn path_type(&self) -> Option<PathType> {
+        AstChildren::new(&self.syntax).next()
+    }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct PathType {
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 08cc6eeea7..da9de2214b 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -689,7 +689,7 @@ Grammar(
             ]
         ),
         "PathSegment": (
-            options: [ "NameRef", "TypeArgList", "ParamList", "RetType" ]
+            options: [ "NameRef", "TypeArgList", "ParamList", "RetType", "PathType" ]
         ),
         "TypeArgList": (collections: [
             ("type_args", "TypeArg"),


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index f6a2a658f1..1bd677cab7 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -281,6 +281,64 @@ fn test() {
     );
 }
 
+#[test]
+fn infer_path_type() {
+    assert_snapshot!(
+        infer(r#"
+struct S;
+
+impl S {
+    fn foo() -> i32 { 1 }
+}
+
+fn test() {
+    S::foo();
+    <S>::foo();
+}
+"#),
+        @r###"
+    [41; 46) '{ 1 }': i32
+    [43; 44) '1': i32
+    [60; 93) '{     ...o(); }': ()
+    [66; 72) 'S::foo': fn foo() -> i32
+    [66; 74) 'S::foo()': i32
+    [80; 88) '<S>::foo': fn foo() -> i32
+    [80; 90) '<S>::foo()': i32
+"###
+    );
+}
+
+#[test]
+fn infer_slice_method() {
+    assert_snapshot!(
+        infer(r#"
+#[lang = "slice"]
+impl<T> [T] {
+    fn foo(&self) -> T {
+        loop {}
+    }
+}
+
+#[lang = "slice_alloc"]
+impl<T> [T] {}
+
+fn test() {
+    <[_]>::foo(b"foo");
+}
+"#),
+        @r###"
+    [45; 49) 'self': &[T]
+    [56; 79) '{     ...     }': !
+    [66; 73) 'loop {}': !
+    [71; 73) '{}': ()
+    [133; 160) '{     ...o"); }': ()
+    [139; 149) '<[_]>::foo': fn foo<u8>(&[T]) -> T
+    [139; 157) '<[_]>:..."foo")': u8
+    [150; 156) 'b"foo"': &[u8]
+"###
+    );
+}
+
 #[test]
 fn infer_struct() {
     assert_snapshot!(