Repository: intellij-rust/intellij-rust
Issue: Impl function fails to resolve with new macro expansion engine

First off, THANK YOU for the new macro expansion engine announced in https://intellij-rust.github.io/2019/04/10/changelog-96.html. This bug report is for a minor issue with the new engine, but overall it has significantly improved my development experience!

## Environment

* **Intellij-Rust plugin version:** 0.2.96.2122-191
* **Rust toolchain version:** cargo 1.34.0 (6789d8a0a 2019-04-01)
* **IDE name and version:** IntelliJ 2019.1 Community Edition
* **Operating system:** MacOS 10.14.1

## Problem description

After enabling the new macro expansion engine, IntelliJ was, for the first time, able to navigate to the macros where many of my functions are defined

For some reason, one of my function calls still fails to resolve: https://github.com/ramosbugs/oauth2-rs/blob/0d306c115f62c87453404d448540f8080f6165a9/tests/lib.rs#L80

The call site is as follows:
```rust
#[test]
fn test_code_verifier_challenge() {
    // Example from https://tools.ietf.org/html/rfc7636#appendix-B
    let code_verifier =
        PkceCodeVerifierS256::new("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk".to_string());
    assert!(
        code_verifier.code_challenge().as_str() == "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
    );
}
```

For the call to `code_verifier.code_challenge()`, IntelliJ isn't able to find the definition of `code_challenge`, which is defined in the macro expansion at: https://github.com/ramosbugs/oauth2-rs/blob/0d306c115f62c87453404d448540f8080f6165a9/src/lib.rs#L617

Note that IntelliJ is able to resolve `PkceCodeVerifierS256::new()`, which is part of the same macro expansion. It seems to be able to find all of the functions in that expansion except for `code_challenge()` and `authorize_url_params()`.

Changed files:
src/test/kotlin/org/rust/lang/core/type/RsGenericExpressionTypeInferenceTest.kt, src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt, src/test/kotlin/org/rust/lang/core/resolve/RsPreciseTraitMatchingTest.kt, src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt, src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt, src/main/kotlin/org/rust/lang/core/resolve/ref/RsMethodCallReferenceImpl.kt, src/main/kotlin/org/rust/lang/core/types/infer/TypeInferenceWalker.kt, src/main/kotlin/org/rust/lang/core/resolve/Processors.kt

diff --git a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt
index 5065d375b..2254dd627 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt
@@ -998,7 +998,7 @@ private fun processFieldDeclarations(struct: RsFieldsOwner, processor: RsResolve
 
 private fun processMethodDeclarationsWithDeref(lookup: ImplLookup, receiver: Ty, processor: RsMethodResolveProcessor): Boolean {
     return lookup.coercionSequence(receiver).withIndex().any { (i, ty) ->
-        val methodProcessor: (AssocItemScopeEntry) -> Boolean = { (name, element, _, source) ->
+        val methodProcessor: (AssocItemScopeEntry) -> Boolean = { (name, element, _, _, source) ->
             element is RsFunction && element.isMethod && processor(MethodResolveVariant(name, element, ty, i, source))
         }
         processAssociatedItems(lookup, ty, VALUES, methodProcessor)
@@ -1014,7 +1014,7 @@ private fun processAssociatedItems(
     val traitBounds = (type as? TyTypeParameter)?.let { lookup.getEnvBoundTransitivelyFor(it).toList() }
     val visitedInherent = mutableSetOf<String>()
     fun processTraitOrImpl(traitOrImpl: TraitImplSource, inherent: Boolean): Boolean {
-        fun inherentProcessor(entry: RsNamedElement): Boolean {
+        fun inherentProcessor(entry: RsAbstractable): Boolean {
             val name = entry.name ?: return false
             if (inherent) visitedInherent.add(name)
             if (!inherent && name in visitedInherent) return false
@@ -1028,14 +1028,14 @@ private fun processAssociatedItems(
             } else {
                 listOf(emptySubstitution)
             }
-            return subst.any { processor(AssocItemScopeEntry(name, entry, it, traitOrImpl)) }
+            return subst.any { processor(AssocItemScopeEntry(name, entry, it, type, traitOrImpl)) }
         }
 
         /**
          * For `impl T for Foo`, this'll walk impl members and trait `T` members,
          * which are not implemented.
          */
-        fun processMembersWithDefaults(accessor: (RsMembers) -> List<RsNamedElement>): Boolean {
+        fun processMembersWithDefaults(accessor: (RsMembers) -> List<RsAbstractable>): Boolean {
             val directlyImplemented = traitOrImpl.value.members?.let { accessor(it) }.orEmpty()
             if (directlyImplemented.any { inherentProcessor(it) }) return true
 
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt b/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt
index 23ee604b5..9ad0e2d3f 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt
@@ -81,6 +81,23 @@ fun collectResolveVariants(referenceName: String, f: (RsResolveProcessor) -> Uni
     return result
 }
 
+fun <T : ScopeEntry> collectResolveVariantsAsScopeEntries(referenceName: String, f: ((T) -> Boolean) -> Unit): List<T> {
+    val result = mutableListOf<T>()
+    f { e ->
+        if ((e == ScopeEvent.STAR_IMPORTS) && result.isNotEmpty()) {
+            return@f true
+        }
+
+        if (e.name == referenceName) {
+            // de-lazying. See `RsResolveProcessor.lazy`
+            e.element ?: return@f false
+            result += e
+        }
+        false
+    }
+    return result
+}
+
 fun pickFirstResolveVariant(referenceName: String, f: (RsResolveProcessor) -> Unit): RsElement? {
     var result: RsElement? = null
     f { e ->
@@ -118,12 +135,19 @@ private data class SimpleScopeEntry(
     override val subst: Substitution = emptySubstitution
 ) : ScopeEntry
 
+interface AssocItemScopeEntryBase<out T: RsAbstractable> : ScopeEntry {
+    override val element: T
+    val selfTy: Ty
+    val source: TraitImplSource
+}
+
 data class AssocItemScopeEntry(
     override val name: String,
-    override val element: RsElement,
+    override val element: RsAbstractable,
     override val subst: Substitution = emptySubstitution,
-    val source: TraitImplSource
-) : ScopeEntry
+    override val selfTy: Ty,
+    override val source: TraitImplSource
+) : AssocItemScopeEntryBase<RsAbstractable>
 
 private class LazyScopeEntry(
     override val name: String,
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMethodCallReferenceImpl.kt b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMethodCallReferenceImpl.kt
index 1f4c85ea3..0e4e0c243 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMethodCallReferenceImpl.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsMethodCallReferenceImpl.kt
@@ -56,7 +56,7 @@ fun resolveMethodCallReferenceWithReceiverType(
     receiverType: Ty,
     methodCall: RsMethodCall
 ): List<MethodResolveVariant> {
-    return collectResolveVariants(methodCall.referenceName) {
+    return collectResolveVariantsAsScopeEntries(methodCall.referenceName) {
         processMethodCallExprResolveVariants(lookup, receiverType, it)
     }
 }
@@ -66,7 +66,7 @@ fun resolveFieldLookupReferenceWithReceiverType(
     receiverType: Ty,
     expr: RsFieldLookup
 ): List<FieldResolveVariant> {
-    return collectResolveVariants(expr.referenceName) {
+    return collectResolveVariantsAsScopeEntries(expr.referenceName) {
         processFieldExprResolveVariants(lookup, receiverType, it)
     }
 }
@@ -95,16 +95,5 @@ data class MethodResolveVariant(
      * trait definition, this contains the impl of the actual trait for the receiver type.
      * Otherwise it's just a trait the method defined in
      */
-    val source: TraitImplSource
-) : DotExprResolveVariant
-
-private fun <T : ScopeEntry> collectResolveVariants(referenceName: String, f: ((T) -> Boolean) -> Unit): List<T> {
-    val result = mutableListOf<T>()
-    f { e ->
-        if (e.name == referenceName) {
-            result += e
-        }
-        false
-    }
-    return result
-}
+    override val source: TraitImplSource
+) : DotExprResolveVariant, AssocItemScopeEntryBase<RsFunction>
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt
index 7df933584..5f6ba1556 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ref/RsPathReferenceImpl.kt
@@ -65,11 +65,25 @@ class RsPathReferenceImpl(
     }
 }
 
+fun resolvePathRaw(path: RsPath, lookup: ImplLookup): List<ScopeEntry> {
+    return collectResolveVariantsAsScopeEntries(path.referenceName) {
+        processPathResolveVariants(lookup, path, false, it)
+    }
+}
+
 fun resolvePath(path: RsPath, lookup: ImplLookup = ImplLookup.relativeTo(path)): List<BoundElement<RsElement>> {
     val result = collectPathResolveVariants(path.referenceName) {
         processPathResolveVariants(lookup, path, false, it)
     }
 
+    return instantiatePathGenerics(path, result, lookup)
+}
+
+private fun <T: RsElement> instantiatePathGenerics(
+    path: RsPath,
+    resolved: List<BoundElement<T>>,
+    lookup: ImplLookup
+): List<BoundElement<T>> {
     val typeArguments: List<Ty>? = run {
         val inAngles = path.typeArgumentList
         val fnSugar = path.valueParameterList
@@ -86,7 +100,7 @@ fun resolvePath(path: RsPath, lookup: ImplLookup = ImplLookup.relativeTo(path)):
 
     val outputArg = path.retType?.typeReference?.type
 
-    return result.map { boundElement ->
+    return resolved.map { boundElement ->
         val (element, subst) = boundElement.downcast<RsGenericDeclaration>() ?: return@map boundElement
 
         val assocTypes = run {
@@ -119,10 +133,16 @@ fun resolvePath(path: RsPath, lookup: ImplLookup = ImplLookup.relativeTo(path)):
         val typeSubst = typeParameters.zip(typeArguments ?: typeParameters).toMap()
         val regionSubst = regionParameters.zip(regionArguments ?: regionParameters).toMap()
         val newSubst = Substitution(typeSubst, regionSubst)
-        BoundElement(element, subst + newSubst, assocTypes)
+        BoundElement(boundElement.element, subst + newSubst, assocTypes)
     }
 }
 
+fun <T: RsElement> instantiatePathGenerics(
+    path: RsPath,
+    resolved: BoundElement<T>,
+    lookup: ImplLookup
+): BoundElement<T> = instantiatePathGenerics(path, listOf(resolved), lookup).single()
+
 private fun resolveAssocTypeBinding(trait: RsTraitItem, binding: RsAssocTypeBinding): RsTypeAlias? =
     collectResolveVariants(binding.referenceName) { processAssocTypeVariants(trait, it) }
         .singleOrNull() as? RsTypeAlias?
diff --git a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
index 2935dfe88..cc338b241 100644
--- a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt
@@ -309,8 +309,8 @@ class RsInferenceContext(
         expectedDotExprTypes[psi] = ty
     }
 
-    fun writePath(path: RsPathExpr, resolved: List<BoundElement<RsElement>>) {
-        resolvedPaths[path] = resolved.map { it.element }
+    fun writePath(path: RsPathExpr, resolved: List<RsElement>) {
+        resolvedPaths[path] = resolved
     }
 
     fun writeResolvedMethod(call: RsMethodCall, resolvedTo: List<MethodResolveVariant>) {
diff --git a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInferenceWalker.kt b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInferenceWalker.kt
index 1faae3940..b3465b60e 100644
--- a/src/main/kotlin/org/rust/lang/core/types/infer/TypeInferenceWalker.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/infer/TypeInferenceWalker.kt
@@ -11,9 +11,7 @@ import com.intellij.util.containers.isNullOrEmpty
 import org.rust.lang.core.macros.MacroExpansion
 import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
-import org.rust.lang.core.resolve.KnownItems
-import org.rust.lang.core.resolve.TraitImplSource
-import org.rust.lang.core.resolve.knownItems
+import org.rust.lang.core.resolve.*
 import org.rust.lang.core.resolve.ref.*
 import org.rust.lang.core.stubs.RsStubLiteralKind
 import org.rust.lang.core.types.*
@@ -286,19 +284,40 @@ class RsTypeInferenceWalker(
     }
 
     private fun inferPathExprType(expr: RsPathExpr): Ty {
-        val variants = resolvePath(expr.path, lookup).mapNotNull { it.downcast<RsNamedElement>() }
-        ctx.writePath(expr, variants)
-        val fnVariants = variants.mapNotNull { it.downcast<RsFunction>() }
-        val qualifier = expr.path.path
-        if (variants.size > 1 && fnVariants.size == variants.size && qualifier != null) {
-            val resolved = collapseToTrait(fnVariants.map { it.element })
-            if (resolved != null) {
-                val subst = collapseSubst(resolved, fnVariants)
-                return instantiatePath(BoundElement(resolved, subst), expr, tryRefinePath = true)
+        val path = expr.path
+        val resolveVariants = resolvePathRaw(path, lookup)
+        val assocVariants = resolveVariants.filterIsInstance<AssocItemScopeEntry>()
+
+        val filteredVariants = if (resolveVariants.size == assocVariants.size) {
+            val variants = filterAssocItems(assocVariants, expr)
+            val fnVariants = variants.mapNotNull { it.element as? RsFunction }
+            if (variants.size > 1 && fnVariants.size == variants.size && path.path != null) {
+                val resolved = collapseToTrait(fnVariants)
+                if (resolved != null) {
+                    ctx.writePath(expr, variants.map { it.element })
+                    val subst = collapseSubst(
+                        resolved,
+                        variants.mapNotNull { e ->
+                            (e.element as? RsGenericDeclaration)?.let { BoundElement(it, e.subst) }
+                        }
+                    )
+                    val scopeEntry = variants.first().copy(
+                        element = resolved,
+                        subst = subst,
+                        source = TraitImplSource.Collapsed((resolved.owner as RsAbstractableOwner.Trait).trait)
+                    )
+                    return instantiatePath(resolved, scopeEntry, expr)
+                }
             }
+            variants
+        } else {
+            resolveVariants
         }
-        val first = variants.singleOrNull() ?: return TyUnknown
-        return instantiatePath(first, expr, tryRefinePath = variants.size == 1)
+
+        ctx.writePath(expr, filteredVariants.mapNotNull { it.element })
+
+        val first = filteredVariants.singleOrNull() ?: return TyUnknown
+        return instantiatePath(first.element as RsNamedElement, first, expr)
     }
 
     /** This works for `String::from` where multiple impls of `From` trait found for `String` */
@@ -323,7 +342,7 @@ class RsTypeInferenceWalker(
     }
 
     /** See test `test type arguments remap on collapse to trait` */
-    private fun collapseSubst(parentFn: RsFunction, variants: List<BoundElement<RsFunction>>): Substitution {
+    private fun collapseSubst(parentFn: RsFunction, variants: List<BoundElement<RsGenericDeclaration>>): Substitution {
         //TODO remap lifetimes
         val collapsed = mutableMapOf<TyTypeParameter, Ty>()
         val generics = parentFn.generics
@@ -340,11 +359,11 @@ class RsTypeInferenceWalker(
     }
 
     private fun instantiatePath(
-        boundElement: BoundElement<RsNamedElement>,
-        pathExpr: RsPathExpr? = null,
-        tryRefinePath: Boolean = false
+        element: RsNamedElement,
+        scopeEntry: ScopeEntry,
+        pathExpr: RsPathExpr
     ): Ty {
-        val (element, subst) = boundElement
+        val subst = instantiatePathGenerics(pathExpr.path, BoundElement(element, scopeEntry.subst), lookup).subst
         val type = when (element) {
             is RsPatBinding -> ctx.getBindingType(element)
             is RsTypeDeclarationElement -> element.declaredType
@@ -355,10 +374,10 @@ class RsTypeInferenceWalker(
             else -> return TyUnknown
         }
 
-        val typeParameters = when (element) {
-            is RsFunction -> {
+        val typeParameters = when {
+            scopeEntry is AssocItemScopeEntry && element is RsAbstractable -> {
                 val owner = element.owner
-                var (typeParameters, selfTy) = when (owner) {
+                val (typeParameters, selfTy) = when (owner) {
                     is RsAbstractableOwner.Impl -> {
                         val typeParameters = instantiateBounds(owner.impl)
                         val selfTy = owner.impl.typeReference?.type?.substitute(typeParameters) ?: TyUnknown
@@ -374,17 +393,24 @@ class RsTypeInferenceWalker(
                             .substitute(typeParameters)
                         val traitRef = TraitRef(selfTy, boundTrait)
                         fulfill.registerPredicateObligation(Obligation(Predicate.Trait(traitRef)))
-                        if (pathExpr != null && tryRefinePath) ctx.registerPathRefinement(pathExpr, traitRef)
+                        when (scopeEntry.source) {
+                            is TraitImplSource.Object, is TraitImplSource.Collapsed -> {
+                                ctx.registerPathRefinement(pathExpr, traitRef)
+                            }
+                        }
                         typeParameters to selfTy
                     }
                     else -> emptySubstitution to null
                 }
 
-                typeParameters = instantiateBounds(element, selfTy, typeParameters)
-                typeParameters
+                if (element is RsGenericDeclaration) {
+                    instantiateBounds(element, selfTy, typeParameters)
+                } else {
+                    typeParameters
+                }
             }
-            is RsEnumVariant -> instantiateBounds(element.parentEnum)
-            is RsGenericDeclaration -> instantiateBounds(element)
+            element is RsEnumVariant -> instantiateBounds(element.parentEnum)
+            element is RsGenericDeclaration -> instantiateBounds(element)
             else -> emptySubstitution
         }
 
@@ -610,12 +636,12 @@ class RsTypeInferenceWalker(
         return methodType.retType
     }
 
-    private fun pickSingleMethod(receiver: Ty, variants: List<MethodResolveVariant>, methodCall: RsMethodCall): MethodResolveVariant? {
-        val filtered = variants.singleOrLet { list ->
+    private fun <T: AssocItemScopeEntryBase<E>, E> filterAssocItems(variants: List<T>, context: RsElement): List<T> {
+        return variants.singleOrLet { list ->
             // 1. filter traits that are not imported
             TypeInferenceMarks.methodPickTraitScope.hit()
-            val traitToCallee = hashMapOf<RsTraitItem, MutableList<MethodResolveVariant>>()
-            val filtered = mutableListOf<MethodResolveVariant>()
+            val traitToCallee = hashMapOf<RsTraitItem, MutableList<T>>()
+            val filtered = mutableListOf<T>()
             for (callee in list) {
                 val trait = callee.source.impl?.traitRef?.resolveToTrait()
                 if (trait != null) {
@@ -624,7 +650,7 @@ class RsTypeInferenceWalker(
                     filtered.add(callee) // inherent impl
                 }
             }
-            traitToCallee.keys.filterInScope(methodCall).forEach {
+            traitToCallee.keys.filterInScope(context).forEach {
                 filtered += traitToCallee.getValue(it)
             }
             if (filtered.isNotEmpty()) {
@@ -638,7 +664,11 @@ class RsTypeInferenceWalker(
             TypeInferenceMarks.methodPickCheckBounds.hit()
             val impl = callee.source.impl ?: return@singleOrFilter true
             ctx.canEvaluateBounds(impl, callee.selfTy)
-        }.singleOrLet { list ->
+        }
+    }
+
+    private fun pickSingleMethod(receiver: Ty, variants: List<MethodResolveVariant>, methodCall: RsMethodCall): MethodResolveVariant? {
+        val filtered = filterAssocItems(variants, methodCall).singleOrLet { list ->
             // 3. Pick results matching receiver type
             TypeInferenceMarks.methodPickDerefOrder.hit()
 
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsPreciseTraitMatchingTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsPreciseTraitMatchingTest.kt
index 5963b8a8b..0ae109539 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsPreciseTraitMatchingTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsPreciseTraitMatchingTest.kt
@@ -362,4 +362,88 @@ class RsPreciseTraitMatchingTest : RsResolveTestBase() {
                //^
         }
     """, TypeInferenceMarks.methodPickTraitsOutOfScope)
+
+    fun `test filter associated functions by trait visibility`() = checkByCode("""
+        struct S;
+
+        mod foo {
+            pub trait Foo {
+                fn new() {}
+            }    //X
+            impl Foo for super::S {}
+        }
+        mod bar {
+            pub trait Bar {
+                fn new() {}
+            }
+            impl Bar for super::S {}
+        }
+        use foo::Foo;
+
+        fn main () {
+            S::new();
+        }    //^
+    """)
+
+    fun `test filter associated constants by trait visibility`() = checkByCode("""
+        struct S;
+
+        mod foo {
+            pub trait Foo {
+                const C: i32 = 0;
+            }       //X
+            impl Foo for super::S {}
+        }
+        mod bar {
+            pub trait Bar {
+                const C: i32 = 0;
+            }
+            impl Bar for super::S {}
+        }
+        use foo::Foo;
+
+        fn main () {
+            let a = S::C;
+        }            //^
+    """)
+
+    fun `test filter associated functions by trait bounds`() = checkByCode("""
+        struct S;
+        trait Bound1 {}
+        trait Bound2 {}
+        impl Bound1 for S {}
+
+        pub trait Foo {
+            fn new() {}
+        }    //X
+        pub trait Bar {
+            fn new() {}
+        }
+        impl<T: Bound1> Foo for T {}
+        impl<T: Bound2> Bar for T {}
+
+        fn main () {
+            S::new();
+        }    //^
+    """)
+
+    fun `test filter associated constants by trait bounds`() = checkByCode("""
+        struct S;
+        trait Bound1 {}
+        trait Bound2 {}
+        impl Bound1 for S {}
+
+        pub trait Foo {
+            const C: i32 = 0;
+        }       //X
+        pub trait Bar {
+            const C: i32 = 0;
+        }
+        impl<T: Bound1> Foo for T {}
+        impl<T: Bound2> Bar for T {}
+
+        fn main () {
+            let a = S::C;
+        }            //^
+    """)
 }
diff --git a/src/test/kotlin/org/rust/lang/core/type/RsGenericExpressionTypeInferenceTest.kt b/src/test/kotlin/org/rust/lang/core/type/RsGenericExpressionTypeInferenceTest.kt
index 875fd73ee..e37ca93a3 100644
--- a/src/test/kotlin/org/rust/lang/core/type/RsGenericExpressionTypeInferenceTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/type/RsGenericExpressionTypeInferenceTest.kt
@@ -1559,4 +1559,20 @@ class RsGenericExpressionTypeInferenceTest : RsTypificationTestBase() {
             let _: Box<[&Bar; 1]> = (Box::new([&Foo]));
         }                         //^ Box<[&Bar; 1]>
     """)
+
+    fun `test associated constant with complex type`() = testExpr("""
+        trait Tr<A> { const C: A; }
+        struct S<T>(T);
+        impl<T: Tr<T>> Tr<T> for S<T> {
+            const C: T = T::C;
+        }
+        struct X;
+        impl Tr<X> for X {
+            const C: X = X;
+        }
+        fn main() {
+            let a = S::<X>::C;
+            a;
+        } //^ X
+    """)
 }