Repository: intellij-rust/intellij-rust
Issue: `impl`s from local macros are not accounted

## Environment

* **IntelliJ Rust plugin version:** 0.3.132.3360-202-nightly
* **Rust toolchain version:** 1.46.0 (04488afe3 2020-08-24) x86_64-unknown-linux-gnu
* **IDE name and version:** IntelliJ IDEA 2020.2.2 Ultimate Edition (IU-202.7319.50)
* **Operating system:** Linux 5.4.0-48-generic

## Problem description

```rust
macro_rules! as_is {
    ($($t:tt)*) => { $($t)* };
}

fn main() {
    struct S;
    as_is! {
        impl S {
            fn foo(&self) {}
        }
    }
    S.foo();
}   //^ unresolved
```

Changed files:
src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt, src/main/kotlin/org/rust/lang/core/types/Extensions.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeAlias.kt, src/main/kotlin/org/rust/lang/core/types/infer/TypeInference.kt, src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt, src/main/kotlin/org/rust/lang/core/resolve/RsCachedImplItem.kt, src/test/kotlin/org/rust/lang/core/resolve/RsProcMacroExpansionResolveTest.kt, src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsInferenceContextOwner.kt, src/test/kotlin/org/rust/lang/core/resolve/RsDoctestInjectionResolveTest.kt, src/test/kotlin/org/rust/lang/core/resolve/RsMacroExpansionResolveTest.kt, src/main/kotlin/org/rust/lang/core/psi/ext/RsImplItem.kt

diff --git a/src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt b/src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt
index 127cb44cf..fc58585ba 100644
--- a/src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt
+++ b/src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt
@@ -367,3 +367,6 @@ val RsFile.isDoctestInjection: Boolean
 
 val RsElement.isDoctestInjection: Boolean
     get() = (contextualFile as? RsFile)?.isDoctestInjection == true
+
+val RsElement.isInsideInjection: Boolean
+    get() = (contextualFile as? RsFile)?.virtualFile is VirtualFileWindow
diff --git a/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt b/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt
index 97afa1e62..d1395fe2e 100644
--- a/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt
+++ b/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt
@@ -42,7 +42,7 @@ object RsCommonCompletionProvider : RsCompletionProvider() {
         result: CompletionResultSet
     ) {
         // Use original position if possible to re-use caches of the real file
-        val position = parameters.position.safeGetOriginalOrSelf()
+        val position = parameters.position
         val element = position.parent as RsReferenceElement
         if (position !== element.referenceNameElement) return
 
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsImplItem.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsImplItem.kt
index ca430b8a8..07ca720c3 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsImplItem.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsImplItem.kt
@@ -13,13 +13,15 @@ import com.intellij.psi.util.CachedValue
 import com.intellij.psi.util.CachedValueProvider
 import com.intellij.psi.util.CachedValuesManager
 import com.intellij.util.CachedValueImpl
-import org.rust.cargo.project.workspace.PackageOrigin
 import org.rust.ide.icons.RsIcons
 import org.rust.ide.presentation.getPresentation
 import org.rust.lang.core.macros.RsExpandedElement
-import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.RsElementTypes.DEFAULT
 import org.rust.lang.core.psi.RsElementTypes.EXCL
+import org.rust.lang.core.psi.RsImplItem
+import org.rust.lang.core.psi.RsTraitItem
+import org.rust.lang.core.psi.RsTypeAlias
+import org.rust.lang.core.psi.rustStructureOrAnyPsiModificationTracker
 import org.rust.lang.core.resolve.RsCachedImplItem
 import org.rust.lang.core.resolve.knownItems
 import org.rust.lang.core.stubs.RsImplItemStub
@@ -84,12 +86,7 @@ abstract class RsImplItemImplMixin : RsStubbedElementImpl<RsImplItemStub>, RsImp
 
     val cachedImplItem: CachedValue<RsCachedImplItem> = CachedValueImpl {
         val cachedImpl = RsCachedImplItem(this)
-        val modTracker = if (cachedImpl.containingCrate?.origin == PackageOrigin.WORKSPACE) {
-            project.rustStructureModificationTracker
-        } else {
-            project.rustPsiManager.rustStructureModificationTrackerInDependencies
-        }
-        CachedValueProvider.Result(cachedImpl, modTracker)
+        RsCachedImplItem.toCachedResult(this, containingCrate, cachedImpl)
     }
 }
 
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsInferenceContextOwner.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsInferenceContextOwner.kt
index 966cfa2df..a3e4728f0 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsInferenceContextOwner.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsInferenceContextOwner.kt
@@ -5,6 +5,9 @@
 
 package org.rust.lang.core.psi.ext
 
+import com.intellij.injected.editor.VirtualFileWindow
+import com.intellij.psi.util.CachedValueProvider
+import com.intellij.psi.util.PsiModificationTracker
 import org.rust.lang.core.psi.*
 
 /**
@@ -28,3 +31,27 @@ val RsInferenceContextOwner.body: RsElement?
         is RsPath -> typeArgumentList
         else -> null
     }
+
+fun <T> RsInferenceContextOwner.createCachedResult(value: T): CachedValueProvider.Result<T> {
+    val structureModificationTracker = project.rustStructureModificationTracker
+
+    return when {
+        // The case of injected language. Injected PSI don't have its own event system, so can only
+        // handle evens from outer PSI. For example, Rust language is injected to Kotlin's string
+        // literal. If a user change the literal, we can only be notified that the literal is changed.
+        // So we have to invalidate the cached value on any PSI change
+        containingFile.virtualFile is VirtualFileWindow -> {
+            CachedValueProvider.Result.create(value, PsiModificationTracker.MODIFICATION_COUNT)
+        }
+
+        // Invalidate cached value of code fragment on any PSI change
+        this is RsCodeFragment -> CachedValueProvider.Result.create(value, PsiModificationTracker.MODIFICATION_COUNT)
+
+        // CachedValueProvider.Result can accept a ModificationTracker as a dependency, so the
+        // cached value will be invalidated if the modification counter is incremented.
+        else -> {
+            val modificationTracker = contextOrSelf<RsModificationTrackerOwner>()?.modificationTracker
+            CachedValueProvider.Result.create(value, listOfNotNull(structureModificationTracker, modificationTracker))
+        }
+    }
+}
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeAlias.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeAlias.kt
index 54f5b2302..a1ead0120 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeAlias.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsTypeAlias.kt
@@ -10,16 +10,13 @@ import com.intellij.psi.PsiElement
 import com.intellij.psi.search.SearchScope
 import com.intellij.psi.stubs.IStubElementType
 import com.intellij.psi.util.CachedValue
-import com.intellij.psi.util.CachedValueProvider
 import com.intellij.util.CachedValueImpl
-import org.rust.cargo.project.workspace.PackageOrigin
 import org.rust.ide.icons.RsIcons
 import org.rust.lang.core.macros.RsExpandedElement
 import org.rust.lang.core.psi.RsElementTypes.DEFAULT
 import org.rust.lang.core.psi.RsPsiImplUtil
 import org.rust.lang.core.psi.RsTypeAlias
-import org.rust.lang.core.psi.rustPsiManager
-import org.rust.lang.core.psi.rustStructureModificationTracker
+import org.rust.lang.core.resolve.RsCachedImplItem
 import org.rust.lang.core.resolve.RsCachedTypeAlias
 import org.rust.lang.core.stubs.RsTypeAliasStub
 import org.rust.lang.core.types.RsPsiTypeImplUtil
@@ -57,11 +54,6 @@ abstract class RsTypeAliasImplMixin : RsStubbedNamedElementImpl<RsTypeAliasStub>
 
     val cachedImplItem: CachedValue<RsCachedTypeAlias> = CachedValueImpl {
         val cachedTypeAlias = RsCachedTypeAlias(this)
-        val modTracker = if (cachedTypeAlias.containingCrate?.origin == PackageOrigin.WORKSPACE) {
-            project.rustStructureModificationTracker
-        } else {
-            project.rustPsiManager.rustStructureModificationTrackerInDependencies
-        }
-        CachedValueProvider.Result(cachedTypeAlias, modTracker)
+        RsCachedImplItem.toCachedResult(this, containingCrate, cachedTypeAlias)
     }
 }
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt b/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
index 550234667..1caa5333b 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ImplLookup.kt
@@ -6,8 +6,15 @@
 package org.rust.lang.core.resolve
 
 import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Key
+import com.intellij.psi.util.CachedValue
+import com.intellij.psi.util.CachedValueProvider
+import com.intellij.psi.util.CachedValuesManager
+import com.intellij.psi.util.PsiModificationTracker
 import com.intellij.util.SmartList
 import gnu.trove.THashMap
+import org.rust.cargo.project.workspace.PackageOrigin
+import org.rust.ide.injected.isInsideInjection
 import org.rust.lang.core.crate.Crate
 import org.rust.lang.core.crate.hasTransitiveDependencyOrSelf
 import org.rust.lang.core.psi.*
@@ -23,10 +30,7 @@ import org.rust.lang.core.types.infer.TypeInferenceMarks.WinnowParamCandidateWin
 import org.rust.lang.core.types.ty.*
 import org.rust.openapiext.hitOnTrue
 import org.rust.openapiext.testAssert
-import org.rust.stdext.buildList
-import org.rust.stdext.exhaustive
-import org.rust.stdext.swapRemoveAt
-import org.rust.stdext.withPrevious
+import org.rust.stdext.*
 import kotlin.LazyThreadSafetyMode.NONE
 import kotlin.LazyThreadSafetyMode.PUBLICATION
 
@@ -235,7 +239,8 @@ class ImplLookup(
     private val project: Project,
     private val containingCrate: Crate,
     val items: KnownItems,
-    private val paramEnv: ParamEnv = ParamEnv.EMPTY
+    private val paramEnv: ParamEnv,
+    context: RsElement? = null
 ) {
     // Non-concurrent HashMap and lazy(NONE) are safe here because this class isn't shared between threads
     private val traitSelectionCache: MutableMap<TraitRef, SelectionResult<SelectionCandidate>> = hashMapOf()
@@ -260,6 +265,17 @@ class ImplLookup(
         trait.findAssociatedType("Item")?.let { trait to it }
     }
 
+    private val implsFromNestedMacros: Map<TyFingerprint, List<RsCachedImplItem>> by lazy(NONE) {
+        @Suppress("NAME_SHADOWING")
+        val context = context ?: return@lazy emptyMap()
+        val ancestorItem = context.contexts.withPrevious().findLast { (it, prev) ->
+            it is RsFunction && (prev == null || prev is RsBlock)
+                || it is RsFile && it.isInsideInjection
+        }?.first as? RsElement
+            ?: return@lazy emptyMap()
+        ancestorItem.implsFromNestedMacros
+    }
+
     val ctx: RsInferenceContext by lazy(NONE) {
         RsInferenceContext(project, this, items)
     }
@@ -374,6 +390,7 @@ class ImplLookup(
         indexCache.findPotentialImpls(tyf)
             .asSequence()
             .filter { useImplsFromCrate(it.containingCrates) }
+            .plus(implsFromNestedMacros[tyf].orEmpty())
 
     private fun findPotentialAliases(tyf: TyFingerprint) =
         indexCache.findPotentialAliases(tyf)
@@ -1315,7 +1332,7 @@ class ImplLookup(
             } else {
                 ParamEnv.EMPTY
             }
-            return ImplLookup(psi.project, psi.containingCrate, psi.knownItems, paramEnv)
+            return ImplLookup(psi.project, psi.containingCrate, psi.knownItems, paramEnv, psi)
         }
     }
 }
@@ -1461,3 +1478,81 @@ private fun prepareSubstAndTraitRefRaw(
 
 private fun <T : Ty> T.withObligations(obligations: List<Obligation> = emptyList()): TyWithObligations<T> =
     TyWithObligations(this, obligations)
+
+/**
+ * The Rust plugin does not index impls expanded from macro calls located inside function bodies, so if we want to take
+ * them into account during type inference, we have to collect them manually
+ */
+private val RsElement.implsFromNestedMacros: Map<TyFingerprint, List<RsCachedImplItem>>
+    get() = CachedValuesManager.getCachedValue(this, IMPLS_FROM_NESTED_MACROS_KEY) {
+        val result = doGetImplsFromNestedMacros(this)
+        if (this is RsFunction) {
+            createCachedResult(result)
+        } else {
+            CachedValueProvider.Result(result, PsiModificationTracker.MODIFICATION_COUNT)
+        }
+    }
+
+private val IMPLS_FROM_NESTED_MACROS_KEY: Key<CachedValue<Map<TyFingerprint, List<RsCachedImplItem>>>> =
+    Key.create("IMPLS_FROM_NESTED_MACROS_KEY")
+
+private fun doGetImplsFromNestedMacros(element: RsElement): Map<TyFingerprint, List<RsCachedImplItem>> {
+    val body = when (element) {
+        is RsFunction -> element.block ?: return emptyMap()
+        is RsFile -> element
+        else -> error("Unexpected element type: $element")
+    }
+
+    val macroCalls = mutableListOf<RsPossibleMacroCall>()
+    val impls = mutableListOf<RsImplItem>()
+
+    collectNestedMacroCallsAndImpls(body, macroCalls, if (element.isInsideInjection) impls else null)
+
+    while (macroCalls.isNotEmpty()) {
+        val macroCall = macroCalls.removeLast()
+
+        // Optimization: skip hardcoded special macros
+        if (macroCall is RsMacroCall && macroCall.macroArgument == null) {
+            val macroDef = macroCall.resolveToMacro()
+            if (macroDef != null && macroDef.containingCrate.origin == PackageOrigin.STDLIB) {
+                continue
+            }
+        }
+
+        val expansion = macroCall.expansion ?: continue
+        for (expandedElement in expansion.elements) {
+            collectNestedMacroCallsAndImpls(expandedElement, macroCalls, impls)
+        }
+    }
+
+    val implMap = hashMapOf<TyFingerprint, MutableList<RsCachedImplItem>>()
+
+    for (impl in impls) {
+        val typeRef = impl.typeReference ?: continue
+        for (tyf in TyFingerprint.create(typeRef, impl.typeParameters.mapNotNull { it.name })) {
+            val list = implMap.getOrPut(tyf) { mutableListOf() }
+            list += RsCachedImplItem.forImpl(impl)
+        }
+    }
+
+    return implMap
+}
+
+private fun collectNestedMacroCallsAndImpls(
+    root: RsElement,
+    macroCalls: MutableList<RsPossibleMacroCall>,
+    impls: MutableList<RsImplItem>?,
+) {
+    val possibleMacroCallsOrImpls = root.descendantsOfTypeOrSelf<RsAttrProcMacroOwner>()
+    for (possibleMacroCall in possibleMacroCallsOrImpls) {
+        when (val attr = possibleMacroCall.procMacroAttributeWithDerives) {
+            is ProcMacroAttribute.Attr -> macroCalls += attr.attr
+            is ProcMacroAttribute.Derive -> macroCalls += attr.derives
+            ProcMacroAttribute.None -> when (possibleMacroCall) {
+                is RsMacroCall -> macroCalls += possibleMacroCall
+                is RsImplItem -> impls?.add(possibleMacroCall)
+            }
+        }
+    }
+}
+
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/RsCachedImplItem.kt b/src/main/kotlin/org/rust/lang/core/resolve/RsCachedImplItem.kt
index 3b32d3e29..aa01e4920 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/RsCachedImplItem.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/RsCachedImplItem.kt
@@ -5,15 +5,16 @@
 
 package org.rust.lang.core.resolve
 
+import com.intellij.injected.editor.VirtualFileWindow
+import com.intellij.openapi.util.ModificationTracker
+import com.intellij.psi.util.CachedValueProvider
 import com.intellij.util.SmartList
 import com.intellij.util.recursionSafeLazy
 import gnu.trove.THashMap
+import org.rust.cargo.project.workspace.PackageOrigin
 import org.rust.lang.core.crate.Crate
-import org.rust.lang.core.psi.RsImplItem
-import org.rust.lang.core.psi.RsTraitItem
-import org.rust.lang.core.psi.RsTraitRef
+import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
-import org.rust.lang.core.psi.isValidProjectMemberAndContainingCrate
 import org.rust.lang.core.types.BoundElement
 import org.rust.lang.core.types.consts.CtConstParameter
 import org.rust.lang.core.types.infer.constGenerics
@@ -75,5 +76,21 @@ class RsCachedImplItem(
         fun forImpl(impl: RsImplItem): RsCachedImplItem {
             return (impl as RsImplItemImplMixin).cachedImplItem.value
         }
+
+        fun <T> toCachedResult(psi: RsElement, containingCrate: Crate?, cachedImpl: T): CachedValueProvider.Result<T> {
+            val containingFile = psi.containingFile
+            val modTracker = if (containingCrate?.origin == PackageOrigin.WORKSPACE) {
+                containingFile.project.rustStructureModificationTracker
+            } else {
+                containingFile.project.rustPsiManager.rustStructureModificationTrackerInDependencies
+            }
+            val deps = if (!containingFile.isPhysical || containingFile.virtualFile is VirtualFileWindow) {
+                // Non-physical PSI does not have event system, but we can track the file changes
+                listOf(modTracker, ModificationTracker { containingFile.modificationStamp })
+            } else {
+                listOf(modTracker)
+            }
+            return CachedValueProvider.Result.create(cachedImpl, deps)
+        }
     }
 }
diff --git a/src/main/kotlin/org/rust/lang/core/types/Extensions.kt b/src/main/kotlin/org/rust/lang/core/types/Extensions.kt
index 1706604d6..e9ee4fca7 100644
--- a/src/main/kotlin/org/rust/lang/core/types/Extensions.kt
+++ b/src/main/kotlin/org/rust/lang/core/types/Extensions.kt
@@ -5,13 +5,10 @@
 
 package org.rust.lang.core.types
 
-import com.intellij.injected.editor.VirtualFileWindow
 import com.intellij.openapi.util.Key
 import com.intellij.psi.PsiElement
 import com.intellij.psi.util.CachedValue
-import com.intellij.psi.util.CachedValueProvider.Result
 import com.intellij.psi.util.CachedValuesManager
-import com.intellij.psi.util.PsiModificationTracker
 import org.rust.lang.core.dfa.Cmt
 import org.rust.lang.core.dfa.ControlFlowGraph
 import org.rust.lang.core.dfa.MemoryCategorizationContext
@@ -32,28 +29,6 @@ import org.rust.lang.core.types.ty.TyUnknown
 import org.rust.stdext.withNext
 
 
-private fun <T> RsInferenceContextOwner.createResult(value: T): Result<T> {
-    val structureModificationTracker = project.rustStructureModificationTracker
-
-    return when {
-        // The case of injected language. Injected PSI don't have it's own event system, so can only
-        // handle evens from outer PSI. For example, Rust language is injected to Kotlin's string
-        // literal. If a user change the literal, we can only be notified that the literal is changed.
-        // So we have to invalidate the cached value on any PSI change
-        containingFile.virtualFile is VirtualFileWindow -> Result.create(value, PsiModificationTracker.MODIFICATION_COUNT)
-
-        // Invalidate cached value of code fragment on any PSI change
-        this is RsCodeFragment -> Result.create(value, PsiModificationTracker.MODIFICATION_COUNT)
-
-        // CachedValueProvider.Result can accept a ModificationTracker as a dependency, so the
-        // cached value will be invalidated if the modification counter is incremented.
-        else -> {
-            val modificationTracker = contextOrSelf<RsModificationTrackerOwner>()?.modificationTracker
-            Result.create(value, listOfNotNull(structureModificationTracker, modificationTracker))
-        }
-    }
-}
-
 /**
  * A [Ty]pe of the type reference without normalization of normalizable associated type projections
  * ([org.rust.lang.core.types.ty.TyProjection]).
@@ -118,7 +93,7 @@ val RsInferenceContextOwner.selfInferenceResult: RsInferenceResult
         return CachedValuesManager.getCachedValue(this, TYPE_INFERENCE_KEY) {
             val inferred = inferTypesIn(this)
 
-            createResult(inferred)
+            createCachedResult(inferred)
         }
     }
 
@@ -203,10 +178,10 @@ private val BORROW_CHECKER_KEY: Key<CachedValue<BorrowCheckResult>> = Key.create
 
 val RsInferenceContextOwner.borrowCheckResult: BorrowCheckResult?
     get() = CachedValuesManager.getCachedValue(this, BORROW_CHECKER_KEY) {
-        if (!existsAfterExpansion) return@getCachedValue createResult(null)
+        if (!existsAfterExpansion) return@getCachedValue createCachedResult(null)
         val bccx = BorrowCheckContext.buildFor(this)
         val borrowCheckResult = bccx?.check()
-        createResult(borrowCheckResult)
+        createCachedResult(borrowCheckResult)
     }
 
 fun RsNamedElement?.asTy(): Ty =
@@ -226,18 +201,18 @@ private val CONTROL_FLOW_KEY: Key<CachedValue<ControlFlowGraph>> = Key.create("C
 
 val RsInferenceContextOwner.controlFlowGraph: ControlFlowGraph?
     get() = CachedValuesManager.getCachedValue(this, CONTROL_FLOW_KEY) {
-        if (!existsAfterExpansion) return@getCachedValue createResult(null)
+        if (!existsAfterExpansion) return@getCachedValue createCachedResult(null)
         val regionScopeTree = getRegionScopeTree(this)
         val cfg = (body as? RsBlock)?.let { ControlFlowGraph.buildFor(it, regionScopeTree) }
-        createResult(cfg)
+        createCachedResult(cfg)
     }
 
 private val LIVENESS_KEY: Key<CachedValue<Liveness>> = Key.create("LIVENESS_KEY")
 
 val RsInferenceContextOwner.liveness: Liveness?
     get() = CachedValuesManager.getCachedValue(this, LIVENESS_KEY) {
-        if (!existsAfterExpansion) return@getCachedValue createResult(null)
+        if (!existsAfterExpansion) return@getCachedValue createCachedResult(null)
         val livenessContext = LivenessContext.buildFor(this)
         val livenessResult = livenessContext?.check()
-        createResult(livenessResult)
+        createCachedResult(livenessResult)
     }
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 1f8ed2f20..b219a6242 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
@@ -36,7 +36,7 @@ import org.rust.stdext.RsResult.Ok
 fun inferTypesIn(element: RsInferenceContextOwner): RsInferenceResult {
     val items = element.knownItems
     val paramEnv = if (element is RsItemElement) ParamEnv.buildFor(element) else ParamEnv.EMPTY
-    val lookup = ImplLookup(element.project, element.containingCrate, items, paramEnv)
+    val lookup = ImplLookup(element.project, element.containingCrate, items, paramEnv, element)
     return recursionGuard(element, { lookup.ctx.infer(element) }, memoize = false)
         ?: error("Can not run nested type inference")
 }
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsDoctestInjectionResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsDoctestInjectionResolveTest.kt
index 8f1b1bde2..102069703 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsDoctestInjectionResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsDoctestInjectionResolveTest.kt
@@ -21,13 +21,12 @@ class RsDoctestInjectionResolveTest : RsResolveTestBase() {
          //X
     """, "lib.rs")
 
-    // BACKCOMPAT: Rust 1.50. Vec struct was moved into `vec/mod.rs` since Rust 1.51
     @ProjectDescriptor(WithStdlibAndDependencyRustProjectDescriptor::class)
     fun `test resolve std element`() = stubOnlyResolve("""
     //- lib.rs
         /// ```
         /// Vec::new()
-        /// //^ ...vec.rs|...vec/mod.rs
+        /// //^ ...vec/mod.rs
         /// ```
         pub fn foo() {}
     """)
@@ -191,4 +190,40 @@ class RsDoctestInjectionResolveTest : RsResolveTestBase() {
         /// ```
         fn foo() {}
     """)
+
+    @ProjectDescriptor(WithDependencyRustProjectDescriptor::class)
+    fun `test impl`() = checkByCode("""
+        /// ```
+        /// use test_package::Bar;
+        /// struct Foo;
+        /// impl Foo {
+        ///      fn foo(&self) -> Bar {}
+        /// }
+        /// Foo.foo().bar();
+        ///         //^
+        /// ```
+        pub struct Bar;
+        impl Bar {
+            pub fn bar(&self) {}
+        }        //X
+    """, "lib.rs")
+
+    @ProjectDescriptor(WithDependencyRustProjectDescriptor::class)
+    fun `test impl outside of main`() = checkByCode("""
+        /// ```
+        /// use test_package::Bar;
+        /// struct Foo;
+        /// impl Foo {
+        ///      fn foo(&self) -> Bar {}
+        /// }
+        /// fn main() {
+        ///     Foo.foo().bar();
+        ///             //^
+        /// }
+        /// ```
+        pub struct Bar;
+        impl Bar {
+            pub fn bar(&self) {}
+        }        //X
+    """, "lib.rs")
 }
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsMacroExpansionResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsMacroExpansionResolveTest.kt
index 0b5596733..e20fcf41b 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsMacroExpansionResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsMacroExpansionResolveTest.kt
@@ -666,6 +666,46 @@ class RsMacroExpansionResolveTest : RsResolveTestBase() {
         }           //^
     """)
 
+    fun `test impl defined by macro inside a function body`() = checkByCode("""
+        macro_rules! foo {
+            ($ i:ident) => {
+                impl $ i {
+                    fn foo(&self) -> Bar {}
+                }
+            }
+        }
+
+        struct Foo;
+        struct Bar;
+        fn main() {
+            foo!(Foo);
+            impl Bar { fn bar(&self) {} }
+                        //X
+            Foo.foo().bar();
+        }           //^
+    """)
+
+    fun `test impl defined by macro inside a parent function body`() = checkByCode("""
+        macro_rules! foo {
+            ($ i:ident) => {
+                impl $ i {
+                    fn foo(&self) -> Bar {}
+                }
+            }
+        }
+
+        struct Foo;
+        struct Bar;
+        fn main() {
+            foo!(Foo);
+            fn bar() {
+                impl Bar { fn bar(&self) {} }
+                            //X
+                Foo.foo().bar();
+            }           //^
+        }
+    """)
+
     fun `test mod declared with macro`() = stubOnlyResolve("""
     //- main.rs
         macro_rules! foo {
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsProcMacroExpansionResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsProcMacroExpansionResolveTest.kt
index b61328e5c..a2bf76dc6 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsProcMacroExpansionResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsProcMacroExpansionResolveTest.kt
@@ -579,7 +579,7 @@ class RsProcMacroExpansionResolveTest : RsResolveTestBase() {
         }     //X
     """)
 
-    fun `test custom derive inside a function body`() = checkByCode("""
+    fun `test custom derive expanding to a struct inside a function body`() = checkByCode("""
         use test_proc_macros::DeriveStructFooDeclaration;
 
         fn main() {
@@ -594,6 +594,37 @@ class RsProcMacroExpansionResolveTest : RsResolveTestBase() {
         }     //^
     """)
 
+    fun `test custom derive expanding to an impl inside a function body`() = checkByCode("""
+        use test_proc_macros::DeriveImplForFoo;
+
+        fn main() {
+            #[derive(DeriveImplForFoo)] // impl Foo { fn foo(&self) -> Bar {} }
+            struct Foo;
+            struct Bar;
+            impl Bar {
+                fn bar(&self) {}
+            }     //X
+
+            Foo.foo().bar()
+        }           //^
+    """)
+
+    fun `test attr expanded to an impl inside a function body`() = checkByCode("""
+        use test_proc_macros::attr_replace_with_attr;
+
+        fn main() {
+            #[attr_replace_with_attr(impl Foo { fn foo(&self) -> Bar {} })]
+            foobar!();
+            struct Foo;
+            struct Bar;
+            impl Bar {
+                fn bar(&self) {}
+            }     //X
+
+            Foo.foo().bar()
+        }           //^
+    """)
+
     // Issue https://github.com/intellij-rust/intellij-rust/issues/9531
     fun `test no stack overflow with derives in included file`() = stubOnlyResolve("""
     //- main.rs