Repository: rust-analyzer/rust-analyzer
Pull request #1862 (2019-09-17 21:15:56)

Assoc item resolution refactoring (again)
This is #1849, with the associated type selection code removed for now. Handling cycles there will need some more thought.


Patch:
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs
index 8f4ce42da5..6b1e44a2c5 100644
--- a/crates/ra_cli/src/analysis_stats.rs
+++ b/crates/ra_cli/src/analysis_stats.rs
@@ -1,7 +1,7 @@
 use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
 
 use ra_db::SourceDatabase;
-use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty, TypeWalk};
+use ra_hir::{AssocItem, Crate, HasBodySource, HasSource, HirDisplay, ModuleDef, Ty, TypeWalk};
 use ra_syntax::AstNode;
 
 use crate::{Result, Verbosity};
@@ -47,7 +47,7 @@ pub fn run(
             for impl_block in module.impl_blocks(db) {
                 for item in impl_block.items(db) {
                     num_decls += 1;
-                    if let ImplItem::Method(f) = item {
+                    if let AssocItem::Function(f) = item {
                         funcs.push(f);
                     }
                 }
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 7aacf515d1..ca9275cd45 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -29,7 +29,7 @@ impl Verbosity {
 }
 
 fn main() -> Result<()> {
-    Logger::with_env_or_str("error").start()?;
+    Logger::with_env().start()?;
 
     let subcommand = match std::env::args_os().nth(1) {
         None => {
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 84e15385c5..706d24c325 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -23,7 +23,7 @@ use crate::{
     },
     nameres::{CrateModuleId, ImportId, ModuleScope, Namespace},
     resolve::{Resolver, TypeNs},
-    traits::{TraitData, TraitItem},
+    traits::TraitData,
     ty::{
         primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
         InferenceResult, TraitRef,
@@ -269,7 +269,7 @@ impl Module {
 
         for impl_block in self.impl_blocks(db) {
             for item in impl_block.items(db) {
-                if let crate::ImplItem::Method(f) = item {
+                if let AssocItem::Function(f) = item {
                     f.diagnostics(db, sink);
                 }
             }
@@ -749,6 +749,10 @@ impl Const {
         db.const_data(self)
     }
 
+    pub fn name(&self, db: &impl HirDatabase) -> Option<Name> {
+        self.data(db).name().cloned()
+    }
+
     pub fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
         db.infer(self.into())
     }
@@ -849,7 +853,7 @@ impl Trait {
         self.trait_data(db).name().clone()
     }
 
-    pub fn items(self, db: &impl DefDatabase) -> Vec<TraitItem> {
+    pub fn items(self, db: &impl DefDatabase) -> Vec<AssocItem> {
         self.trait_data(db).items().to_vec()
     }
 
@@ -902,7 +906,7 @@ impl Trait {
             .items()
             .iter()
             .filter_map(|item| match item {
-                TraitItem::TypeAlias(t) => Some(*t),
+                AssocItem::TypeAlias(t) => Some(*t),
                 _ => None,
             })
             .find(|t| &t.name(db) == name)
@@ -1019,3 +1023,15 @@ impl Container {
         }
     }
 }
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum AssocItem {
+    Function(Function),
+    Const(Const),
+    TypeAlias(TypeAlias),
+}
+// FIXME: not every function, ... is actually an assoc item. maybe we should make
+// sure that you can only turn actual assoc items into AssocItems. This would
+// require not implementing From, and instead having some checked way of
+// casting them, and somehow making the constructors private, which would be annoying.
+impl_froms!(AssocItem: Function, Const, TypeAlias);
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index d26a024ed5..c463d351cf 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -15,7 +15,7 @@ use crate::{
     resolve::Resolver,
     ty::Ty,
     type_ref::TypeRef,
-    Const, Function, HasSource, HirFileId, Source, TraitRef, TypeAlias,
+    AssocItem, Const, Function, HasSource, HirFileId, Source, TraitRef, TypeAlias,
 };
 
 #[derive(Debug, Default, PartialEq, Eq)]
@@ -56,7 +56,7 @@ impl HasSource for ImplBlock {
 impl ImplBlock {
     pub(crate) fn containing(
         module_impl_blocks: Arc<ModuleImplBlocks>,
-        item: ImplItem,
+        item: AssocItem,
     ) -> Option<ImplBlock> {
         let impl_id = *module_impl_blocks.impls_by_def.get(&item)?;
         Some(ImplBlock { module: module_impl_blocks.module, impl_id })
@@ -91,7 +91,7 @@ impl ImplBlock {
         TraitRef::from_hir(db, &self.resolver(db), &self.target_trait(db)?, Some(target_ty))
     }
 
-    pub fn items(&self, db: &impl DefDatabase) -> Vec<ImplItem> {
+    pub fn items(&self, db: &impl DefDatabase) -> Vec<AssocItem> {
         db.impls_in_module(self.module).impls[self.impl_id].items().to_vec()
     }
 
@@ -113,7 +113,7 @@ impl ImplBlock {
 pub struct ImplData {
     target_trait: Option<TypeRef>,
     target_type: TypeRef,
-    items: Vec<ImplItem>,
+    items: Vec<AssocItem>,
     negative: bool,
 }
 
@@ -151,27 +151,11 @@ impl ImplData {
         &self.target_type
     }
 
-    pub fn items(&self) -> &[ImplItem] {
+    pub fn items(&self) -> &[AssocItem] {
         &self.items
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-//FIXME: rename to ImplDef?
-pub enum ImplItem {
-    Method(Function),
-    Const(Const),
-    TypeAlias(TypeAlias),
-    // Existential
-}
-impl_froms!(ImplItem: Const, TypeAlias);
-
-impl From<Function> for ImplItem {
-    fn from(func: Function) -> ImplItem {
-        ImplItem::Method(func)
-    }
-}
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct ImplId(pub RawId);
 impl_arena_id!(ImplId);
@@ -185,7 +169,7 @@ impl_arena_id!(ImplId);
 pub struct ModuleImplBlocks {
     pub(crate) module: Module,
     pub(crate) impls: Arena<ImplId, ImplData>,
-    impls_by_def: FxHashMap<ImplItem, ImplId>,
+    impls_by_def: FxHashMap<AssocItem, ImplId>,
 }
 
 impl ModuleImplBlocks {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 80cf8d9c02..e7a576aa0a 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -69,7 +69,7 @@ pub use self::{
     expr::ExprScopes,
     generics::{GenericParam, GenericParams, HasGenericParams},
     ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
-    impl_block::{ImplBlock, ImplItem},
+    impl_block::ImplBlock,
     name::Name,
     nameres::{ImportId, Namespace, PerNs},
     path::{Path, PathKind},
@@ -85,7 +85,7 @@ pub use self::{
 pub use self::code_model::{
     docs::{DocDef, Docs, Documentation},
     src::{HasBodySource, HasSource, Source},
-    Adt, BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
-    EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef, ModuleSource,
-    Static, Struct, StructField, Trait, TypeAlias, Union,
+    Adt, AssocItem, BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody,
+    Enum, EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
+    ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
 };
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index a6b9b41d02..1446c49a02 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -114,7 +114,7 @@ where
                 _ => i += 1,
             }
             if i == 1000 {
-                log::error!("name resolution is stuck");
+                log::error!("diverging name resolution");
                 break;
             }
         }
@@ -444,6 +444,36 @@ where
                 return false;
             }
 
+            if resolved_res.reached_fixedpoint != ReachedFixedPoint::Yes {
+                let crate_name = &path.segments[0].name;
+
+                // FIXME:
+                // $crate are not handled in resolver right now
+                if crate_name.to_string() == "$crate" {
+                    return true;
+                }
+
+                // FIXME:
+                // Currently `#[cfg(test)]` are ignored and cargo-metadata do not insert
+                // dev-dependencies of dependencies. For example,
+                // if we depend on parking lot, and parking lot has a dev-dependency on lazy_static.
+                // Then `lazy_static` wil not included in `CrateGraph`
+                // We can fix that by proper handling `cfg(test)`.
+                //
+                // So right now we set the fixpoint to No only if its crate is in CrateGraph
+                // See issue #1282 for details
+                let krate =
+                    match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() {
+                        Some(ModuleDef::Module(m)) => m.krate(self.db),
+                        _ => return true,
+                    };
+                if krate.is_none() {
+                    return true;
+                }
+
+                res = resolved_res.reached_fixedpoint;
+            }
+
             true
         });
 
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index a61161b63a..39d1b7e46f 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -190,6 +190,13 @@ impl Path {
     pub fn expand_macro_expr(&self) -> Option<Name> {
         self.as_ident().and_then(|name| Some(name.clone()))
     }
+
+    pub fn is_type_relative(&self) -> bool {
+        match self.kind {
+            PathKind::Type(_) => true,
+            _ => false,
+        }
+    }
 }
 
 impl GenericArgs {
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index bb69159010..a23c8792ad 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -15,7 +15,6 @@ 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,
 };
@@ -50,7 +49,7 @@ pub(crate) enum Scope {
     ExprScope(ExprScope),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum TypeNs {
     SelfType(ImplBlock),
     GenericParam(u32),
@@ -59,19 +58,18 @@ pub enum TypeNs {
     TypeAlias(TypeAlias),
     BuiltinType(BuiltinType),
     Trait(Trait),
-    // Module belong to type ns, but the resovler is used when all module paths
+    // Module belong to type ns, but the resolver is used when all module paths
     // are fully resolved.
     // Module(Module)
 }
 
-#[derive(Debug)]
-pub enum ResolveValueResult<'a> {
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ResolveValueResult {
     ValueNs(ValueNs),
     Partial(TypeNs, usize),
-    TypeRef(&'a TypeRef),
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ValueNs {
     LocalBinding(PatId),
     Function(Function),
@@ -131,6 +129,9 @@ impl Resolver {
         db: &impl HirDatabase,
         path: &Path,
     ) -> Option<(TypeNs, Option<usize>)> {
+        if path.is_type_relative() {
+            return None;
+        }
         let first_name = &path.segments.first()?.name;
         let skip_to_mod = path.kind != PathKind::Plain;
         for scope in self.scopes.iter().rev() {
@@ -189,11 +190,10 @@ impl Resolver {
         &self,
         db: &impl HirDatabase,
         path: &'p Path,
-    ) -> Option<ResolveValueResult<'p>> {
-        if let PathKind::Type(type_ref) = &path.kind {
-            return Some(ResolveValueResult::TypeRef(type_ref));
+    ) -> Option<ResolveValueResult> {
+        if path.is_type_relative() {
+            return None;
         }
-
         let n_segments = path.segments.len();
         let tmp = SELF_PARAM;
         let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
@@ -284,7 +284,7 @@ impl Resolver {
     ) -> Option<ValueNs> {
         match self.resolve_path_in_value_ns(db, path)? {
             ResolveValueResult::ValueNs(it) => Some(it),
-            ResolveValueResult::Partial(..) | ResolveValueResult::TypeRef(_) => None,
+            ResolveValueResult::Partial(..) => None,
         }
     }
 
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 2a907c9f1c..4d895f0a19 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -190,7 +190,7 @@ pub enum PathResolution {
     GenericParam(u32),
     SelfType(crate::ImplBlock),
     Macro(MacroDef),
-    AssocItem(crate::ImplItem),
+    AssocItem(crate::AssocItem),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/ra_hir/src/traits.rs b/crates/ra_hir/src/traits.rs
index 4c2f7980dd..e39511518d 100644
--- a/crates/ra_hir/src/traits.rs
+++ b/crates/ra_hir/src/traits.rs
@@ -9,13 +9,13 @@ use crate::{
     db::{AstDatabase, DefDatabase},
     ids::LocationCtx,
     name::AsName,
-    Const, Function, HasSource, Module, Name, Trait, TypeAlias,
+    AssocItem, Const, Function, HasSource, Module, Name, Trait, TypeAlias,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TraitData {
     name: Option<Name>,
-    items: Vec<TraitItem>,
+    items: Vec<AssocItem>,
     auto: bool,
 }
 
@@ -48,7 +48,7 @@ impl TraitData {
         &self.name
     }
 
-    pub(crate) fn items(&self) -> &[TraitItem] {
+    pub(crate) fn items(&self) -> &[AssocItem] {
         &self.items
     }
 
@@ -57,22 +57,9 @@ impl TraitData {
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum TraitItem {
-    Function(Function),
-    Const(Const),
-    TypeAlias(TypeAlias),
-    // Existential
-}
-// FIXME: not every function, ... is actually a trait item. maybe we should make
-// sure that you can only turn actual trait items into TraitItems. This would
-// require not implementing From, and instead having some checked way of
-// casting them.
-impl_froms!(TraitItem: Function, Const, TypeAlias);
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TraitItemsIndex {
-    traits_by_def: FxHashMap<TraitItem, Trait>,
+    traits_by_def: FxHashMap<AssocItem, Trait>,
 }
 
 impl TraitItemsIndex {
@@ -88,7 +75,7 @@ impl TraitItemsIndex {
         index
     }
 
-    pub(crate) fn get_parent_trait(&self, item: TraitItem) -> Option<Trait> {
+    pub(crate) fn get_parent_trait(&self, item: AssocItem) -> Option<Trait> {
         self.traits_by_def.get(&item).cloned()
     }
 }
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index bf9609d8c7..70da7f3113 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -48,8 +48,7 @@ use crate::{
     resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
     ty::infer::diagnostics::InferenceDiagnostic,
     type_ref::{Mutability, TypeRef},
-    Adt, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path,
-    StructField,
+    Adt, AssocItem, ConstData, DefWithBody, FnData, Function, HasBody, Name, Path, StructField,
 };
 
 mod unify;
@@ -121,7 +120,7 @@ pub struct InferenceResult {
     /// For each struct literal, records the variant it resolves to.
     variant_resolutions: FxHashMap<ExprOrPatId, VariantDef>,
     /// For each associated item record what it resolves to
-    assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
+    assoc_resolutions: FxHashMap<ExprOrPatId, AssocItem>,
     diagnostics: Vec<InferenceDiagnostic>,
     pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
     pub(super) type_of_pat: ArenaMap<PatId, Ty>,
@@ -141,10 +140,10 @@ impl InferenceResult {
     pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantDef> {
         self.variant_resolutions.get(&id.into()).copied()
     }
-    pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<ImplItem> {
+    pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItem> {
         self.assoc_resolutions.get(&id.into()).copied()
     }
-    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
+    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItem> {
         self.assoc_resolutions.get(&id.into()).copied()
     }
     pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
@@ -235,7 +234,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         self.result.variant_resolutions.insert(id, variant);
     }
 
-    fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) {
+    fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItem) {
         self.result.assoc_resolutions.insert(id, item);
     }
 
@@ -468,16 +467,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
     }
 
     fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option<Ty> {
-        let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
-
-        let (value, self_subst) = match value_or_partial {
-            ResolveValueResult::ValueNs(it) => (it, None),
-            ResolveValueResult::Partial(def, remaining_index) => {
-                self.resolve_assoc_item(Either::A(def), path, remaining_index, id)?
+        let (value, self_subst) = if let crate::PathKind::Type(type_ref) = &path.kind {
+            if path.segments.is_empty() {
+                // This can't actually happen syntax-wise
+                return None;
             }
-            ResolveValueResult::TypeRef(type_ref) => {
-                let ty = self.make_ty(type_ref);
-                self.resolve_assoc_item(Either::B(ty), path, 0, id)?
+            let ty = self.make_ty(type_ref);
+            let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1];
+            let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
+            self.resolve_ty_assoc_item(
+                ty,
+                path.segments.last().expect("path had at least one segment"),
+                id,
+            )?
+        } else {
+            let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
+
+            match value_or_partial {
+                ResolveValueResult::ValueNs(it) => (it, None),
+                ResolveValueResult::Partial(def, remaining_index) => {
+                    self.resolve_assoc_item(def, path, remaining_index, id)?
+                }
             }
         };
 
@@ -508,101 +518,91 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
     fn resolve_assoc_item(
         &mut self,
-        mut def_or_ty: Either<TypeNs, Ty>,
+        def: TypeNs,
         path: &Path,
         remaining_index: usize,
         id: ExprOrPatId,
     ) -> Option<(ValueNs, Option<Substs>)> {
         assert!(remaining_index < path.segments.len());
-        let krate = self.resolver.krate()?;
-
-        let mut ty = Ty::Unknown;
-
-        // 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 = 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,
-                    };
-
-                    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;
+        // there may be more intermediate segments between the resolved one and
+        // the end. Only the last segment needs to be resolved to a value; from
+        // the segments before that, we need to get either a type or a trait ref.
+
+        let resolved_segment = &path.segments[remaining_index - 1];
+        let remaining_segments = &path.segments[remaining_index..];
+        let is_before_last = remaining_segments.len() == 1;
+
+        match (def, is_before_last) {
+            (TypeNs::Trait(_trait), true) => {
+                // FIXME Associated item of trait, e.g. `Default::default`
+                None
             }
+            (def, _) => {
+                // Either we already have a type (e.g. `Vec::new`), or we have a
+                // trait but it's not the last segment, so the next segment
+                // should resolve to an associated type of that trait (e.g. `<T
+                // as Iterator>::Item::default`)
+                let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1];
+                let ty = Ty::from_partly_resolved_hir_path(
+                    self.db,
+                    &self.resolver,
+                    def,
+                    resolved_segment,
+                    remaining_segments_for_ty,
+                );
+                if let Ty::Unknown = ty {
+                    return None;
+                }
 
-            // Attempt to find an impl_item for the type which has a name matching
-            // the current segment
-            log::debug!("looking for path segment: {:?}", segment);
+                let segment =
+                    remaining_segments.last().expect("there should be at least one segment here");
 
-            let ty = mem::replace(&mut ty, Ty::Unknown);
-            def_or_ty = ty.iterate_impl_items(self.db, krate, |item| {
-                match item {
-                    crate::ImplItem::Method(_) => None,
-                    crate::ImplItem::Const(_) => None,
+                self.resolve_ty_assoc_item(ty, segment, id)
+            }
+        }
+    }
 
-                    // FIXME: Resolve associated types
-                    crate::ImplItem::TypeAlias(_) => {
-                        // Some(Either::A(TypeNs::TypeAlias(..)))
-                        None
-                    }
-                }
-            })?;
+    fn resolve_ty_assoc_item(
+        &mut self,
+        ty: Ty,
+        segment: &crate::path::PathSegment,
+        id: ExprOrPatId,
+    ) -> Option<(ValueNs, Option<Substs>)> {
+        if let Ty::Unknown = ty {
+            return None;
         }
 
-        let segment = path.segments.last().unwrap();
-        let def = ty.clone().iterate_impl_items(self.db, krate, |item| {
-            let matching_def: Option<ValueNs> = match item {
-                crate::ImplItem::Method(func) => {
-                    if segment.name == func.name(self.db) {
-                        Some(ValueNs::Function(func))
-                    } else {
-                        None
-                    }
-                }
+        let krate = self.resolver.krate()?;
 
-                crate::ImplItem::Const(konst) => {
-                    let data = konst.data(self.db);
-                    if Some(&segment.name) == data.name() {
-                        Some(ValueNs::Const(konst))
-                    } else {
-                        None
-                    }
+        // Find impl
+        // FIXME: consider trait candidates
+        let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
+            AssocItem::Function(func) => {
+                if segment.name == func.name(self.db) {
+                    Some(AssocItem::Function(func))
+                } else {
+                    None
                 }
-                crate::ImplItem::TypeAlias(_) => None,
-            };
-            match matching_def {
-                Some(_) => {
-                    self.write_assoc_resolution(id, item);
-                    matching_def
+            }
+
+            AssocItem::Const(konst) => {
+                if konst.name(self.db).map_or(false, |n| n == segment.name) {
+                    Some(AssocItem::Const(konst))
+                } else {
+                    None
                 }
-                None => None,
             }
+            AssocItem::TypeAlias(_) => None,
         })?;
-        let self_types = self.find_self_types(&def, ty);
-        Some((def, self_types))
+        let def = match item {
+            AssocItem::Function(f) => ValueNs::Function(f),
+            AssocItem::Const(c) => ValueNs::Const(c),
+            AssocItem::TypeAlias(_) => unreachable!(),
+        };
+        let substs = self.find_self_types(&def, ty);
+
+        self.write_assoc_resolution(id, item);
+        Some((def, substs))
     }
 
     fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index 6ead3846a8..a83842b0f3 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -86,78 +86,125 @@ impl Ty {
         }
     }
 
-    pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
-        // Resolve the path (in type namespace)
-        let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
-            Some(it) => it,
-            None => return Ty::Unknown,
-        };
+    pub(crate) fn from_type_relative_path(
+        db: &impl HirDatabase,
+        resolver: &Resolver,
+        ty: Ty,
+        remaining_segments: &[PathSegment],
+    ) -> Ty {
+        if remaining_segments.len() == 1 {
+            // resolve unselected assoc types
+            let segment = &remaining_segments[0];
+            Ty::select_associated_type(db, resolver, ty, segment)
+        } else if remaining_segments.len() > 1 {
+            // FIXME report error (ambiguous associated type)
+            Ty::Unknown
+        } else {
+            ty
+        }
+    }
 
-        let typable: TypableDef = match resolution {
+    pub(crate) fn from_partly_resolved_hir_path(
+        db: &impl HirDatabase,
+        resolver: &Resolver,
+        resolution: TypeNs,
+        resolved_segment: &PathSegment,
+        remaining_segments: &[PathSegment],
+    ) -> Ty {
+        let ty = match resolution {
             TypeNs::Trait(trait_) => {
-                let segment = match remaining_index {
-                    None => path.segments.last().expect("resolved path has at least one element"),
-                    Some(i) => &path.segments[i - 1],
-                };
-                let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
-                return if let Some(remaining_index) = remaining_index {
-                    if remaining_index == path.segments.len() - 1 {
-                        let segment = &path.segments[remaining_index];
-                        match trait_ref
-                            .trait_
-                            .associated_type_by_name_including_super_traits(db, &segment.name)
-                        {
-                            Some(associated_ty) => {
-                                // FIXME handle type parameters on the segment
-                                Ty::Projection(ProjectionTy {
-                                    associated_ty,
-                                    parameters: trait_ref.substs,
-                                })
-                            }
-                            None => {
-                                // associated type not found
-                                Ty::Unknown
-                            }
+                let trait_ref =
+                    TraitRef::from_resolved_path(db, resolver, trait_, resolved_segment, None);
+                return if remaining_segments.len() == 1 {
+                    let segment = &remaining_segments[0];
+                    match trait_ref
+                        .trait_
+                        .associated_type_by_name_including_super_traits(db, &segment.name)
+                    {
+                        Some(associated_ty) => {
+                            // FIXME handle type parameters on the segment
+                            Ty::Projection(ProjectionTy {
+                                associated_ty,
+                                parameters: trait_ref.substs,
+                            })
+                        }
+                        None => {
+                            // FIXME: report error (associated type not found)
+                            Ty::Unknown
                         }
-                    } else {
-                        // FIXME more than one segment remaining, is this possible?
-                        Ty::Unknown
                     }
+                } else if remaining_segments.len() > 1 {
+                    // FIXME report error (ambiguous associated type)
+                    Ty::Unknown
                 } else {
                     Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
                 };
             }
             TypeNs::GenericParam(idx) => {
-                if remaining_index.is_some() {
-                    // e.g. T::Item
-                    return Ty::Unknown;
-                }
-                return Ty::Param {
-                    idx,
-                    // FIXME: maybe return name in resolution?
-                    name: path
-                        .as_ident()
-                        .expect("generic param should be single-segment path")
-                        .clone(),
-                };
-            }
-            TypeNs::SelfType(impl_block) => {
-                if remaining_index.is_some() {
-                    // e.g. Self::Item
-                    return Ty::Unknown;
-                }
-                return impl_block.target_ty(db);
+                // FIXME: maybe return name in resolution?
+                let name = resolved_segment.name.clone();
+                Ty::Param { idx, name }
             }
+            TypeNs::SelfType(impl_block) => impl_block.target_ty(db),
 
-            TypeNs::Adt(it) => it.into(),
-            TypeNs::BuiltinType(it) => it.into(),
-            TypeNs::TypeAlias(it) => it.into(),
+            TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()),
+            TypeNs::BuiltinType(it) => {
+                Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into())
+            }
+            TypeNs::TypeAlias(it) => {
+                Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into())
+            }
             // FIXME: report error
             TypeNs::EnumVariant(_) => return Ty::Unknown,
         };
 
+        Ty::from_type_relative_path(db, resolver, ty, remaining_segments)
+    }
+
+    pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
+        // Resolve the path (in type namespace)
+        if let crate::PathKind::Type(type_ref) = &path.kind {
+            let ty = Ty::from_hir(db, resolver, &type_ref);
+            let remaining_segments = &path.segments[..];
+            return Ty::from_type_relative_path(db, resolver, ty, remaining_segments);
+        }
+        let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
+            Some(it) => it,
+            None => return Ty::Unknown,
+        };
+        let (resolved_segment, remaining_segments) = match remaining_index {
+            None => (
+                path.segments.last().expect("resolved path has at least one element"),
+                &[] as &[PathSegment],
+            ),
+            Some(i) => (&path.segments[i - 1], &path.segments[i..]),
+        };
+        Ty::from_partly_resolved_hir_path(
+            db,
+            resolver,
+            resolution,
+            resolved_segment,
+            remaining_segments,
+        )
+    }
+
+    fn select_associated_type(
+        _db: &impl HirDatabase,
+        _resolver: &Resolver,
+        _self_ty: Ty,
+        _segment: &PathSegment,
+    ) -> Ty {
+        Ty::Unknown
+    }
+
+    fn from_hir_path_inner(
+        db: &impl HirDatabase,
+        resolver: &Resolver,
+        segment: &PathSegment,
+        typable: TypableDef,
+    ) -> Ty {
         let ty = db.type_for_def(typable, Namespace::Types);
-        let substs = Ty::substs_from_path(db, resolver, path, typable);
+        let substs = Ty::substs_from_path_segment(db, resolver, segment, typable);
         ty.subst(&substs)
     }
 
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index be63806d43..8b46b11a9c 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -11,13 +11,12 @@ use super::{autoderef, lower, Canonical, InEnvironment, TraitEnvironment, TraitR
 use crate::{
     db::HirDatabase,
     generics::HasGenericParams,
-    impl_block::{ImplBlock, ImplId, ImplItem},
+    impl_block::{ImplBlock, ImplId},
     nameres::CrateModuleId,
     resolve::Resolver,
-    traits::TraitItem,
     ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy},
     ty::{Ty, TypeCtor},
-    Crate, Function, Module, Name, Trait,
+    AssocItem, Crate, Function, Module, Name, Trait,
 };
 
 /// This is used as a key for indexing impls.
@@ -232,7 +231,7 @@ fn iterate_trait_method_candidates<T>(
         // iteration
         let mut known_implemented = inherently_implemented;
         for item in data.items() {
-            if let TraitItem::Function(m) = *item {
+            if let AssocItem::Function(m) = *item {
                 let data = m.data(db);
                 if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
                     if !known_implemented {
@@ -264,7 +263,7 @@ fn iterate_inherent_methods<T>(
 
         for impl_block in impls.lookup_impl_blocks(&ty.value) {
             for item in impl_block.items(db) {
-                if let ImplItem::Method(f) = item {
+                if let AssocItem::Function(f) = item {
                     let data = f.data(db);
                     if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
                         if let Some(result) = callback(&ty.value, f) {
@@ -304,7 +303,7 @@ impl Ty {
         self,
         db: &impl HirDatabase,
         krate: Crate,
-        mut callback: impl FnMut(ImplItem) -> Option<T>,
+        mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
         for krate in def_crates(db, krate, &self)? {
             let impls = db.impls_in_crate(krate);
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index c748e9d84a..693d9b28fd 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -21,7 +21,7 @@ use crate::{
         ApplicationTy, CallableDef, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
         TypeWalk,
     },
-    Crate, HasGenericParams, ImplBlock, ImplItem, Trait, TypeAlias,
+    AssocItem, Crate, HasGenericParams, ImplBlock, Trait, TypeAlias,
 };
 
 /// This represents a trait whose name we could not resolve.
@@ -496,7 +496,7 @@ pub(crate) fn trait_datum_query(
         .items(db)
         .into_iter()
         .filter_map(|trait_item| match trait_item {
-            crate::traits::TraitItem::TypeAlias(type_alias) => Some(type_alias),
+            crate::AssocItem::TypeAlias(type_alias) => Some(type_alias),
             _ => None,
         })
         .map(|type_alias| type_alias.to_chalk(db))
@@ -616,7 +616,7 @@ pub(crate) fn impl_datum_query(
         .items(db)
         .into_iter()
         .filter_map(|item| match item {
-            ImplItem::TypeAlias(t) => Some(t),
+            AssocItem::TypeAlias(t) => Some(t),
             _ => None,
         })
         .filter_map(|t| {
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs
index e1c8d6ab0e..e9fec54d86 100644
--- a/crates/ra_ide_api/src/completion/complete_path.rs
+++ b/crates/ra_ide_api/src/completion/complete_path.rs
@@ -52,14 +52,14 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
             if let Some(krate) = krate {
                 ty.iterate_impl_items(ctx.db, krate, |item| {
                     match item {
-                        hir::ImplItem::Method(func) => {
+                        hir::AssocItem::Function(func) => {
                             let data = func.data(ctx.db);
                             if !data.has_self_param() {
                                 acc.add_function(ctx, func);
                             }
                         }
-                        hir::ImplItem::Const(ct) => acc.add_const(ctx, ct),
-                        hir::ImplItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
+                        hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
+                        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
                     }
                     None::<()>
                 });
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 03382ab3cb..11f73ccfd0 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,4 +1,4 @@
-use hir::{FieldSource, HasSource, ImplItem, ModuleSource};
+use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
 use ra_db::{FileId, SourceDatabase};
 use ra_syntax::{
     algo::visit::{visitor, Visitor},
@@ -221,11 +221,14 @@ impl NavigationTarget {
         )
     }
 
-    pub(crate) fn from_impl_item(db: &RootDatabase, impl_item: hir::ImplItem) -> NavigationTarget {
-        match impl_item {
-            ImplItem::Method(it) => NavigationTarget::from_def_source(db, it),
-            ImplItem::Const(it) => NavigationTarget::from_def_source(db, it),
-            ImplItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
+    pub(crate) fn from_assoc_item(
+        db: &RootDatabase,
+        assoc_item: hir::AssocItem,
+    ) -> NavigationTarget {
+        match assoc_item {
+            AssocItem::Function(it) => NavigationTarget::from_def_source(db, it),
+            AssocItem::Const(it) => NavigationTarget::from_def_source(db, it),
+            AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
         }
     }
 
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 28529a2de0..503dcacff4 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -60,7 +60,7 @@ pub(crate) fn reference_definition(
     match classify_name_ref(db, &analyzer, name_ref) {
         Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)),
         Some(FieldAccess(field)) => return Exact(NavigationTarget::from_field(db, field)),
-        Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_impl_item(db, assoc)),
+        Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_assoc_item(db, assoc)),
         Some(Method(func)) => return Exact(NavigationTarget::from_def_source(db, func)),
         Some(Def(def)) => match NavigationTarget::from_def(db, def) {
             Some(nav) => return Exact(nav),
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 28a6bef12d..655bcdb16d 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -117,9 +117,9 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
                 }
             }
             Some(AssocItem(it)) => res.extend(match it {
-                hir::ImplItem::Method(it) => from_def_source(db, it),
-                hir::ImplItem::Const(it) => from_def_source(db, it),
-                hir::ImplItem::TypeAlias(it) => from_def_source(db, it),
+                hir::AssocItem::Function(it) => from_def_source(db, it),
+                hir::AssocItem::Const(it) => from_def_source(db, it),
+                hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
             }),
             Some(Def(it)) => {
                 match it {
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs
index 6c2a7b260b..aff03464ae 100644
--- a/crates/ra_ide_api/src/name_ref_kind.rs
+++ b/crates/ra_ide_api/src/name_ref_kind.rs
@@ -8,7 +8,7 @@ pub enum NameRefKind {
     Method(hir::Function),
     Macro(hir::MacroDef),
     FieldAccess(hir::StructField),
-    AssocItem(hir::ImplItem),
+    AssocItem(hir::AssocItem),
     Def(hir::ModuleDef),
     SelfType(hir::Ty),
     Pat(AstPtr<ast::BindPat>),
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 86ab3a2605..3d7f91c1de 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -102,9 +102,9 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                         Some(Method(_)) => "function",
                         Some(Macro(_)) => "macro",
                         Some(FieldAccess(_)) => "field",
-                        Some(AssocItem(hir::ImplItem::Method(_))) => "function",
-                        Some(AssocItem(hir::ImplItem::Const(_))) => "constant",
-                        Some(AssocItem(hir::ImplItem::TypeAlias(_))) => "type",
+                        Some(AssocItem(hir::AssocItem::Function(_))) => "function",
+                        Some(AssocItem(hir::AssocItem::Const(_))) => "constant",
+                        Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type",
                         Some(Def(hir::ModuleDef::Module(_))) => "module",
                         Some(Def(hir::ModuleDef::Function(_))) => "function",
                         Some(Def(hir::ModuleDef::Adt(_))) => "type",


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 1bd677cab7..09c17fdf4d 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -2613,18 +2613,18 @@ fn infer_call_trait_method_on_generic_param_1() {
     assert_snapshot!(
         infer(r#"
 trait Trait {
-    fn method() -> u32;
+    fn method(&self) -> u32;
 }
 fn test<T: Trait>(t: T) {
     t.method();
 }
 "#),
         @r###"
-
-    [59; 60) 't': T
-    [65; 84) '{     ...d(); }': ()
-    [71; 72) 't': T
-    [71; 81) 't.method()': {unknown}
+    [30; 34) 'self': &Self
+    [64; 65) 't': T
+    [70; 89) '{     ...d(); }': ()
+    [76; 77) 't': T
+    [76; 86) 't.method()': u32
     "###
     );
 }
@@ -2634,18 +2634,18 @@ fn infer_call_trait_method_on_generic_param_2() {
     assert_snapshot!(
         infer(r#"
 trait Trait<T> {
-    fn method() -> T;
+    fn method(&self) -> T;
 }
 fn test<U, T: Trait<U>>(t: T) {
     t.method();
 }
 "#),
         @r###"
-
-    [66; 67) 't': T
-    [72; 91) '{     ...d(); }': ()
-    [78; 79) 't': T
-    [78; 88) 't.method()': {unknown}
+    [33; 37) 'self': &Self
+    [71; 72) 't': T
+    [77; 96) '{     ...d(); }': ()
+    [83; 84) 't': T
+    [83; 93) 't.method()': [missing name]
     "###
     );
 }
@@ -2685,6 +2685,7 @@ fn test() {
 
 #[test]
 fn infer_project_associated_type() {
+    // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234
     assert_snapshot!(
         infer(r#"
 trait Iterable {
@@ -2696,16 +2697,19 @@ fn test<T: Iterable>() {
     let x: <S as Iterable>::Item = 1;
     let y: <T as Iterable>::Item = no_matter;
     let z: T::Item = no_matter;
+    let a: <T>::Item = no_matter;
 }
 "#),
         @r###"
-    [108; 227) '{     ...ter; }': ()
+    [108; 261) '{     ...ter; }': ()
     [118; 119) 'x': u32
     [145; 146) '1': u32
     [156; 157) 'y': {unknown}
     [183; 192) 'no_matter': {unknown}
     [202; 203) 'z': {unknown}
     [215; 224) 'no_matter': {unknown}
+    [234; 235) 'a': {unknown}
+    [249; 258) 'no_matter': {unknown}
     "###
     );
 }
@@ -2721,9 +2725,11 @@ struct S;
 impl Iterable for S { type Item = u32; }
 fn foo1<T: Iterable>(t: T) -> T::Item {}
 fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {}
+fn foo3<T: Iterable>(t: T) -> <T>::Item {}
 fn test() {
     let x = foo1(S);
     let y = foo2(S);
+    let z = foo3(S);
 }
 "#),
         @r###"
@@ -2731,15 +2737,21 @@ fn test() {
     [123; 125) '{}': ()
     [147; 148) 't': T
     [178; 180) '{}': ()
-    [191; 236) '{     ...(S); }': ()
-    [201; 202) 'x': {unknown}
-    [205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
-    [205; 212) 'foo1(S)': {unknown}
-    [210; 211) 'S': S
-    [222; 223) 'y': u32
-    [226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
-    [226; 233) 'foo2(S)': u32
-    [231; 232) 'S': S
+    [202; 203) 't': T
+    [221; 223) '{}': ()
+    [234; 300) '{     ...(S); }': ()
+    [244; 245) 'x': {unknown}
+    [248; 252) 'foo1': fn foo1<S>(T) -> {unknown}
+    [248; 255) 'foo1(S)': {unknown}
+    [253; 254) 'S': S
+    [265; 266) 'y': u32
+    [269; 273) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
+    [269; 276) 'foo2(S)': u32
+    [274; 275) 'S': S
+    [286; 287) 'z': {unknown}
+    [290; 294) 'foo3': fn foo3<S>(T) -> {unknown}
+    [290; 297) 'foo3(S)': {unknown}
+    [295; 296) 'S': S
     "###
     );
 }
@@ -4050,6 +4062,48 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
     );
 }
 
+#[test]
+fn unselected_projection_in_trait_env() {
+    let t = type_at(
+        r#"
+//- /main.rs
+trait Trait {
+    type Item;
+}
+
+trait Trait2 {
+    fn foo(&self) -> u32;
+}
+
+fn test<T: Trait>() where T::Item: Trait2 {
+    let x: T::Item = no_matter;
+    x.foo()<|>;
+}
+"#,
+    );
+    assert_eq!(t, "u32");
+}
+
+#[test]
+fn unselected_projection_in_trait_env_cycle() {
+    let t = type_at(
+        r#"
+//- /main.rs
+trait Trait {
+    type Item;
+}
+
+trait Trait2<T> {}
+
+fn test<T: Trait>() where T: Trait2<T::Item> {
+    let x: T::Item = no_matter<|>;
+}
+"#,
+    );
+    // this is a legitimate cycle
+    assert_eq!(t, "{unknown}");
+}
+
 fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
     let file = db.parse(pos.file_id).ok().unwrap();
     let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();