Repository: intellij-rust/intellij-rust
Issue: Struct is not shadowed for a path in a type argument position

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **IntelliJ Rust plugin version:** 0.4.185.5058-223-nightly
* **Rust toolchain version:** 1.65.0 (897e37553 2022-11-02) x86_64-unknown-linux-gnu
* **IDE name and version:** IntelliJ IDEA 2022.3 Ultimate Edition (IU-223.7571.182)
* **Operating system:** Linux 5.15.0-56-generic
* **Macro expansion:** enabled
* **Additional experimental features:** org.rust.cargo.emulate.terminal

## Problem description

#7479 introduced a bug with name shadowing:

```rust
mod a {
    pub struct S;
}
use a::*;
struct S {}

trait Trait<T> {}
impl Trait<S> for S {}
         //^ Should be resolved to `struct S {}`, but actually multiresolved
```

(btw write the test in `RsConstArgumentResolveTest`)

Changed files:
src/main/kotlin/org/rust/lang/core/resolve/RsPathResolveResult.kt, src/test/kotlin/org/rust/lang/core/completion/RsLookupElementTest.kt, src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt, src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.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/resolve2/FacadeResolve.kt, src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt, src/test/kotlin/org/rust/lang/core/resolve/RsConstArgumentResolveTest.kt, src/main/kotlin/org/rust/lang/core/resolve/Processors.kt, src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt

diff --git a/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt b/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
index 6bf2884c0..bd6690dcd 100644
--- a/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
+++ b/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
@@ -721,7 +721,7 @@ class ImportingPsiRenderer(
         val elementToName = mutableMapOf<RsElement, String>()
         processNestedScopesUpwards(context, TYPES_N_VALUES, createProcessor {
             val element = it.element as? RsNamedElement ?: return@createProcessor false
-            for (namespace in element.namespaces) {
+            for (namespace in it.namespaces) {
                 nameToElement[it.name to namespace] = element
                 if (it.name != "_" && element !in elementToName) {
                     elementToName[element] = it.name
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 2fdbc2217..5808a2594 100644
--- a/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt
+++ b/src/main/kotlin/org/rust/lang/core/completion/RsCommonCompletionProvider.kt
@@ -209,7 +209,7 @@ object RsCommonCompletionProvider : RsCompletionProvider() {
 
         for (candidate in candidates) {
             val item = candidate.item
-            val scopeEntry = SimpleScopeEntry(candidate.itemName, item)
+            val scopeEntry = SimpleScopeEntry(candidate.itemName, item, TYPES_N_VALUES_N_MACROS)
 
             if (item is RsEnumItem
                 && (context.expectedTy?.ty?.stripReferences() as? TyAdt)?.item == (item.declaredType as? TyAdt)?.item) {
@@ -529,7 +529,7 @@ fun collectVariantsForEnumCompletion(
         val variantName = enumVariant.name ?: return@mapNotNull null
 
         return@mapNotNull createLookupElement(
-            scopeEntry = SimpleScopeEntry("${enumName}::${variantName}", enumVariant, substitution),
+            scopeEntry = SimpleScopeEntry("${enumName}::${variantName}", enumVariant, ENUM_VARIANT_NS, substitution),
             context = context,
             null,
             object : RsDefaultInsertHandler() {
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt b/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
index fb470e3bd..431cccc62 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
@@ -28,18 +28,18 @@ fun processItemOrEnumVariantDeclarations(
             val (item, subst) = (scope.typeReference?.skipParens() as? RsPathType)
                 ?.path?.reference?.advancedDeepResolve() ?: return false
             if (item is RsEnumItem) {
-                if (processAllWithSubst(item.variants, subst, processor)) return true
+                if (processAllWithSubst(item.variants, subst, ENUM_VARIANT_NS, processor)) return true
             }
         }
         is RsImplItem -> {
             val (item, subst) = (scope.typeReference?.skipParens() as? RsPathType)
                 ?.path?.reference?.advancedDeepResolve() ?: return false
             if (item is RsEnumItem) {
-                if (processAllWithSubst(item.variants, subst, processor)) return true
+                if (processAllWithSubst(item.variants, subst, ENUM_VARIANT_NS, processor)) return true
             }
         }
         is RsEnumItem -> {
-            if (processAll(scope.variants, processor)) return true
+            if (processor.processAll(scope.variants, ENUM_VARIANT_NS)) return true
         }
         is RsMod -> {
             val ipm = if (withPrivateImports()) {
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 aca93b9d0..acedecffe 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/NameResolution.kt
@@ -198,7 +198,7 @@ fun processModDeclResolveVariants(modDecl: RsModDeclItem, processor: RsResolvePr
         val mod = psiMgr.findFile(vFile)?.rustFile ?: return false
 
         val name = modDecl.name ?: return false
-        return processor.process(name, mod)
+        return processor.process(name, TYPES, mod)
     }
     if (ownedDirectory == null) return false
     if (modDecl.isLocal) return false
@@ -216,13 +216,13 @@ fun processModDeclResolveVariants(modDecl: RsModDeclItem, processor: RsResolvePr
     for (vFile in files) {
         val rawFileName = vFile.name
         if (vFile == originalFileOriginal || rawFileName == RsConstants.MOD_RS_FILE) continue
-        if (processor.lazy(fileName(rawFileName)) { psiMgr.findFile(vFile)?.rustFile }) return true
+        if (processor.lazy(fileName(rawFileName), TYPES) { psiMgr.findFile(vFile)?.rustFile }) return true
     }
 
     for (vDir in dirs) {
         val mod = vDir.findChild(RsConstants.MOD_RS_FILE)
         if (mod != null) {
-            if (processor.lazy(vDir.name) { psiMgr.findFile(mod)?.rustFile }) return true
+            if (processor.lazy(vDir.name, TYPES) { psiMgr.findFile(mod)?.rustFile }) return true
         }
 
         // We shouldn't search possible module files in subdirectories
@@ -246,7 +246,7 @@ fun processModDeclResolveVariants(modDecl: RsModDeclItem, processor: RsResolvePr
                 if (vFile.isDirectory) continue
                 val rawFileName = vFile.name
                 if (rawFileName == RsConstants.MOD_RS_FILE) continue
-                if (processor.lazy(fileName(rawFileName)) { psiMgr.findFile(vFile)?.rustFile }) return true
+                if (processor.lazy(fileName(rawFileName), TYPES) { psiMgr.findFile(vFile)?.rustFile }) return true
             }
         }
     }
@@ -262,7 +262,7 @@ fun processExternPreludeResolveVariants(ctx: PathResolutionContext, processor: R
     val (project, defMap) = ctx.containingModInfo ?: return false
     for ((name, externCrateDefMap) in defMap.externPrelude.entriesWithNames(processor.names)) {
         val externCrateRoot = externCrateDefMap.rootAsRsMod(project) ?: continue
-        if (processor.process(name, externCrateRoot)) return true
+        if (processor.process(name, TYPES, externCrateRoot)) return true
     }
     return false
 }
@@ -290,13 +290,13 @@ fun processExternCrateResolveVariants(
         if (crate.origin == PackageOrigin.STDLIB && dependencyName in visitedDeps) return false
         visitedDeps.add(dependencyName)
 
-        return processor.lazy(dependencyName) {
+        return processor.lazy(dependencyName, TYPES) {
             crate.rootModFile?.toPsiFile(element.project)?.rustFile
         }
     }
 
     if (withSelf) {
-        if (processor.lazy("self") { crate.rootMod }) return true
+        if (processor.lazy("self", TYPES) { crate.rootMod }) return true
     }
     val explicitDepsFirst = crate.dependenciesWithCyclic.sortedBy {
         when (it.crate.origin) {
@@ -434,10 +434,7 @@ private fun processQualifiedPathResolveVariants(
         val shadowingProcessor = if (primitiveType != null) {
             processor.wrapWithBeforeProcessingHandler { e ->
                 if (processor.acceptsName(e.name)) {
-                    val element = e.element
-                    if (element is RsNamedElement) {
-                        prevScope[e.name] = element.namespaces
-                    }
+                    prevScope[e.name] = e.namespaces
                 }
             }
         } else {
@@ -481,11 +478,11 @@ private fun processQualifiedPathResolveVariants1(
 
     if (parent is RsUseSpeck && path.path == null) {
         SelfInGroup.hit()
-        if (processor.process("self", base)) return true
+        if (processor.process("self", TYPES, base)) return true
     }
 
     if (base is RsMod) {
-        val result = processor.lazy("super") {
+        val result = processor.lazy("super", TYPES) {
             // `super` is allowed only after `self` and `super`
             // so we add `super` in completion only when it produces valid path
             if (!isCompletion || isSuperChain(qualifier)) {
@@ -598,15 +595,15 @@ private fun processSelfSuperCrate(
     processor: RsResolveProcessor
 ): Boolean {
     if (Namespace.Types in ns) {
-        if (processor.lazy("self") { ctx.containingMod }) return true
-        if (processor.lazy("super") { ctx.containingMod.`super` }) return true
-        if (ctx.crateRoot != null && processor.process("crate", ctx.crateRoot)) return true
+        if (processor.lazy("self", TYPES) { ctx.containingMod }) return true
+        if (processor.lazy("super", TYPES) { ctx.containingMod.`super` }) return true
+        if (ctx.crateRoot != null && processor.process("crate", TYPES, ctx.crateRoot)) return true
     }
     return false
 }
 
 private fun processMacroDollarCrateResolveVariants(path: RsPath, processor: RsResolveProcessor): Boolean {
-    return processor.lazy(MACRO_DOLLAR_CRATE_IDENTIFIER) {
+    return processor.lazy(MACRO_DOLLAR_CRATE_IDENTIFIER, TYPES) {
         path.resolveDollarCrateIdentifier()?.rootMod
     }
 }
@@ -680,14 +677,12 @@ private fun processTraitMembers(
     source: TraitImplSource.Trait,
     processor: RsResolveProcessor
 ): Boolean {
+    val nsFilter = assocMembersNsFilter(ns) ?: return false
     for (item in trait.members?.expandedMembers.orEmpty()) {
-        val itemNs = when (item) {
-            is RsTypeAlias -> Namespace.Types
-            else -> Namespace.Values // RsFunction, RsConstant
-        }
-        if (itemNs !in ns) continue
+        if (!nsFilter(item)) continue
         val name = item.name ?: continue
-        if (processor.process(AssocItemScopeEntry(name, item, subst, selfTy, source))) return true
+        val namespaces = assocMemberNamespaces(item)
+        if (processor.process(AssocItemScopeEntry(name, item, namespaces, subst, selfTy, source))) return true
     }
     return false
 }
@@ -728,7 +723,10 @@ fun processLabelResolveVariants(label: RsLabel, processor: RsResolveProcessor):
         if (scope is RsLambdaExpr || scope is RsFunction) return false
         if (scope is RsLabeledExpression) {
             val labelDecl = scope.labelDecl ?: continue
-            if (processWithShadowingAndUpdateScope(prevScope, LIFETIMES, processor) { it.process(labelDecl) }) return true
+            val stop = processWithShadowingAndUpdateScope(prevScope, LIFETIMES, processor) {
+                it.process(labelDecl, LIFETIMES)
+            }
+            if (stop) return true
         }
     }
     return false
@@ -744,7 +742,7 @@ fun processLifetimeResolveVariants(lifetime: RsLifetime, processor: RsResolvePro
             is RsPolybound -> scope.forLifetimes?.lifetimeParameterList.orEmpty()
             else -> continue@loop
         }
-        if (processAll(lifetimeParameters, processor)) return true
+        if (processor.processAll(lifetimeParameters, LIFETIMES)) return true
     }
 
     return false
@@ -809,7 +807,7 @@ fun processMacroReferenceVariants(ref: RsMacroReference, processor: RsResolvePro
     val simple = definition.macroPattern.descendantsOfType<RsMacroBinding>()
         .toList()
 
-    return simple.any { processor.process(it) }
+    return simple.any { processor.process(it, TYPES) }
 }
 
 fun processProcMacroResolveVariants(
@@ -834,11 +832,11 @@ fun processDeriveTraitResolveVariants(element: RsPath, traitName: String, proces
     // FIXME: support custom items with the same name as known derivable traits (i.e. `foo::bar::Clone`)
     val knownDerive = KNOWN_DERIVABLE_TRAITS[traitName]?.findTrait(element.knownItems)
     return if (knownDerive != null) {
-        processor.process(knownDerive)
+        processor.process(knownDerive, TYPES)
     } else {
         val traits = RsNamedElementIndex.findElementsByName(element.project, traitName)
             .filterIsInstance<RsTraitItem>()
-        processAll(traits, processor)
+        processor.processAll(traits, TYPES)
     }
 }
 
@@ -853,7 +851,7 @@ fun processBinaryOpVariants(element: RsBinaryOp, operator: OverloadableBinaryOpe
         .functions
         .find { it.name == operator.fnName }
         ?: return false
-    return processor.process(function)
+    return processor.process(function, VALUES)
 }
 
 fun processAssocTypeVariants(element: RsAssocTypeBinding, processor: RsResolveProcessor): Boolean {
@@ -862,7 +860,7 @@ fun processAssocTypeVariants(element: RsAssocTypeBinding, processor: RsResolvePr
 }
 
 fun processAssocTypeVariants(trait: RsTraitItem, processor: RsResolveProcessor): Boolean {
-    if (trait.associatedTypesTransitively.any { processor.process(it) }) return true
+    if (trait.associatedTypesTransitively.any { processor.process(it, TYPES) }) return true
     return false
 }
 
@@ -898,7 +896,7 @@ fun processMacroCallPathResolveVariants(path: RsPath, isCompletion: Boolean, pro
             } else {
                 val def = call.resolveToMacroUsingNewResolve() ?: return false
                 val name = processor.names?.singleOrNull() ?: def.name ?: return false
-                processor.process(name, def)
+                processor.process(name, MACROS, def)
             }
         } else {
             // Allowed only 1 or 2-segment paths: `foo!()` or `foo::bar!()`, but not foo::bar::baz!();
@@ -943,7 +941,9 @@ private class MacroResolver private constructor(
     ignoreLegacyMacros: Boolean,
     private val path: RsPath,
 ) : RsVisitor() {
-    private val visitor = MacroResolvingVisitor(reverse = true, ignoreLegacyMacros) { processor.process(it) }
+    private val visitor = MacroResolvingVisitor(reverse = true, ignoreLegacyMacros) {
+        processor.process(it, MACROS)
+    }
 
     private fun processMacrosInLexicalOrderUpward(startElement: PsiElement): MacroResolveResult {
         val result = processScopesInLexicalOrderUpward(startElement)
@@ -1038,7 +1038,7 @@ private class MacroResolver private constructor(
         if (expandedFrom.path.qualifier == null) {
             val resolved = (expandedFrom.path.reference as? RsMacroPathReferenceImpl)?.resolveIfCached()
                 ?: return false
-            if (processor.process(expandedFrom.macroName, resolved)) return true
+            if (processor.process(expandedFrom.macroName, MACROS, resolved)) return true
         }
         return false
     }
@@ -1224,11 +1224,11 @@ private fun exportedMacrosInternal(scope: RsFile): List<ScopeEntry> {
 
 private fun asProcMacroDefinition(item: RsFunction): ScopeEntry? {
     val macroName = item.procMacroName ?: return null
-    return SimpleScopeEntry(macroName, item)
+    return SimpleScopeEntry(macroName, item, MACROS)
 }
 
 private fun List<RsMacro>.toScopeEntries(): List<ScopeEntry> =
-    mapNotNull { item -> item.name?.let { SimpleScopeEntry(it, item) } }
+    mapNotNull { item -> item.name?.let { SimpleScopeEntry(it, item, MACROS) } }
 
 /**
  * Returns list of re-exported macros via `[macro_reexport]` attribute from given extern crate
@@ -1320,7 +1320,7 @@ private fun collect2segmentPaths(rootSpeck: RsUseSpeck): List<TwoSegmentPath> {
 private fun processFieldDeclarations(struct: RsFieldsOwner, processor: RsResolveProcessor): Boolean =
     struct.fields.any { field ->
         val name = field.name ?: return@any false
-        processor.process(name, field)
+        processor.process(name, VALUES, field)
     }
 
 private fun processMethodDeclarationsWithDeref(
@@ -1334,7 +1334,7 @@ private fun processMethodDeclarationsWithDeref(
         val tyIsSuitableForMethodCall = ty != TyUnknown && ty !is TyInfer.TyVar
         val withAutoBorrow = (prevTy !is TyReference || prevTy.mutability == MUTABLE) && tyIsSuitableForMethodCall
         val withAutoBorrowMut = (prevTy !is TyReference || prevTy.mutability == IMMUTABLE) && tyIsSuitableForMethodCall
-        val methodProcessor = createProcessorGeneric<AssocItemScopeEntry>(processor.names) { (name, element, _, selfTy, source) ->
+        val methodProcessor = createProcessorGeneric<AssocItemScopeEntry>(processor.names) { (name, element, _, _, selfTy, source) ->
             val autoBorrow = selfTy is TyReference && selfTy.referenced === ty
             // We intentionally use `hasSelfParameters` instead of `isMethod` because we already know that
             // it is an associated item and so if it is a function with self parameter - it is a method.
@@ -1356,18 +1356,7 @@ private fun processAssociatedItems(
     context: RsElement,
     processor: RsResolveProcessorBase<AssocItemScopeEntry>
 ): Boolean {
-    val nsFilter: (RsAbstractable) -> Boolean = when {
-        Namespace.Types in ns && Namespace.Values in ns -> {
-            { true }
-        }
-        Namespace.Types in ns -> {
-            { it is RsTypeAlias }
-        }
-        Namespace.Values in ns -> {
-            { it !is RsTypeAlias }
-        }
-        else -> return false
-    }
+    val nsFilter = assocMembersNsFilter(ns) ?: return false
 
     val traitBounds = (type as? TyTypeParameter)?.let { lookup.getEnvBoundTransitivelyFor(it).toList() }
     val visitedInherent = hashMapOf<String, RsAbstractable>()
@@ -1387,15 +1376,16 @@ private fun processAssociatedItems(
                 continue
             }
 
+            val namespaces = assocMemberNamespaces(member)
             val result = if (traitBounds != null && traitOrImpl is TraitImplSource.TraitBound) {
                 // Retrieve trait subst for associated type like
                 // trait SliceIndex<T> { type Output; }
                 // fn get<I: : SliceIndex<S>>(index: I) -> I::Output
                 // Resulting subst will contain mapping T => S
                 val bounds = traitBounds.filter { it.element == traitOrImpl.value }
-                bounds.any { processor.process(AssocItemScopeEntry(name, member, it.subst, type, traitOrImpl)) }
+                bounds.any { processor.process(AssocItemScopeEntry(name, member, namespaces, it.subst, type, traitOrImpl)) }
             } else {
-                processor.process(AssocItemScopeEntry(name, member, emptySubstitution, type, traitOrImpl))
+                processor.process(AssocItemScopeEntry(name, member, namespaces, emptySubstitution, type, traitOrImpl))
             }
             if (result) return true
         }
@@ -1403,6 +1393,31 @@ private fun processAssociatedItems(
     return false
 }
 
+private fun assocMembersNsFilter(ns: Set<Namespace>): ((RsAbstractable) -> Boolean)? {
+    return when {
+        Namespace.Types in ns && Namespace.Values in ns -> {
+            { true }
+        }
+
+        Namespace.Types in ns -> {
+            { it is RsTypeAlias }
+        }
+
+        Namespace.Values in ns -> {
+            { it !is RsTypeAlias }
+        }
+
+        else -> null
+    }
+}
+
+private fun assocMemberNamespaces(member: RsAbstractable): Set<Namespace> {
+    return when (member) {
+        is RsTypeAlias -> TYPES
+        else -> VALUES // RsFunction, RsConstant
+    }
+}
+
 private fun Map<String, List<RsAbstractable>>.entriesWithNames(names: Set<String>?): Sequence<Pair<String, RsAbstractable>> {
     return if (names.isNullOrEmpty()) {
         entries.asSequence()
@@ -1461,7 +1476,7 @@ private fun processLexicalDeclarations(
 
         return PsiTreeUtil.findChildrenOfType(pattern, RsPatBinding::class.java).any { binding ->
             val name = binding.name ?: return@any false
-            patternProcessor.lazy(name) {
+            patternProcessor.lazy(name, VALUES) {
                 binding.takeIf { (it.parent is RsPatField || !it.isReferenceToConstant) && hygieneFilter(it) }
             }
         }
@@ -1504,33 +1519,33 @@ private fun processLexicalDeclarations(
         }
 
         is RsTypeAlias -> {
-            if (processAll(scope.typeParameters, processor)) return true
-            if (Namespace.Values in ns && processAll(scope.constParameters, processor)) return true
+            if (Namespace.Types in ns && processor.processAll(scope.typeParameters, TYPES)) return true
+            if (Namespace.Values in ns && processor.processAll(scope.constParameters, VALUES)) return true
         }
 
         is RsStructItem,
         is RsEnumItem,
         is RsTraitOrImpl -> {
             scope as RsGenericDeclaration
-            if (processAll(scope.typeParameters, processor)) return true
-            if (Namespace.Values in ns && processAll(scope.constParameters, processor)) return true
-            if (processor.process("Self", scope)) return true
+            if (Namespace.Types in ns && processor.processAll(scope.typeParameters, TYPES)) return true
+            if (Namespace.Values in ns && processor.processAll(scope.constParameters, VALUES)) return true
+            if (processor.process("Self", TYPES_N_VALUES, scope)) return true
             if (scope is RsImplItem) {
                 scope.traitRef?.let { traitRef ->
                     // really should be unnamed, but "_" is not a valid name in rust, so I think it's ok
-                    if (processor.lazy("_") { traitRef.resolveToTrait() }) return true
+                    if (processor.lazy("_", TYPES) { traitRef.resolveToTrait() }) return true
                 }
             }
         }
 
         is RsFunction -> {
-            if (Namespace.Types in ns && processAll(scope.typeParameters, processor)) return true
-            if (Namespace.Values in ns && processAll(scope.constParameters, processor)) return true
+            if (Namespace.Types in ns && processor.processAll(scope.typeParameters, TYPES)) return true
+            if (Namespace.Values in ns && processor.processAll(scope.constParameters, VALUES)) return true
             // XXX: `cameFrom is RsBlock` prevents switches to AST in cases like `fn foo(a: usize, b: [u8; SIZE])`.
             // Note that rustc really process them and show [E0435] on this: `fn foo(a: usize, b: [u8; a])`.
             if (Namespace.Values in ns && cameFrom is RsBlock) {
                 val selfParam = scope.selfParameter
-                if (selfParam != null && processor.process("self", selfParam)) return true
+                if (selfParam != null && processor.process("self", VALUES, selfParam)) return true
 
                 for (parameter in scope.valueParameters) {
                     val pat = parameter.pat ?: continue
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 5c38a5ad7..543041e7e 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/Processors.kt
@@ -14,7 +14,6 @@ import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
 import org.rust.lang.core.resolve.ref.MethodResolveVariant
 import org.rust.lang.core.resolve2.RsModInfo
-import org.rust.lang.core.types.BoundElement
 import org.rust.lang.core.types.Substitution
 import org.rust.lang.core.types.emptySubstitution
 import org.rust.lang.core.types.infer.TypeFolder
@@ -32,9 +31,14 @@ import org.rust.stdext.intersects
 interface ScopeEntry {
     val name: String
     val element: RsElement
+    val namespaces: Set<Namespace>
     val subst: Substitution get() = emptySubstitution
+    fun doCopyWithNs(namespaces: Set<Namespace>): ScopeEntry
 }
 
+@Suppress("UNCHECKED_CAST")
+private fun <T: ScopeEntry> T.copyWithNs(namespaces: Set<Namespace>): T = doCopyWithNs(namespaces) as T
+
 typealias RsProcessor<T> = (T) -> Boolean
 
 interface RsResolveProcessorBase<in T : ScopeEntry> {
@@ -154,8 +158,8 @@ private class ShadowingProcessor<in T : ScopeEntry>(
     override fun process(entry: T): Boolean {
         val prevNs = prevScope[entry.name]
         if (entry.name == "_" || prevNs == null) return originalProcessor.process(entry)
-        val newNs = (entry.element as? RsNamedElement)?.namespaces
-        return (newNs == null || ns.intersects(newNs.minus(prevNs))) && originalProcessor.process(entry)
+        val restNs = entry.namespaces.minus(prevNs)
+        return ns.intersects(restNs) && originalProcessor.process(entry.copyWithNs(restNs))
     }
     override fun toString(): String = "ShadowingProcessor($originalProcessor, ns = $ns)"
 }
@@ -176,20 +180,23 @@ private class ShadowingAndUpdateScopeProcessor<in T : ScopeEntry>(
 ) : RsResolveProcessorBase<T> {
     override val names: Set<String>? = originalProcessor.names
     override fun process(entry: T): Boolean {
-        val prevNs = if (entry.name != "_") prevScope[entry.name] else null
-        if (prevNs != null) {
-            val newNs = (entry.element as? RsNamedElement)?.namespaces
-            if (newNs != null && !ns.intersects(newNs.minus(prevNs))) {
-                return false
-            }
+        if (!originalProcessor.acceptsName(entry.name) || entry.name == "_") {
+            return originalProcessor.process(entry)
         }
-        if (originalProcessor.acceptsName(entry.name) && entry.name != "_") {
-            val newNs = (entry.element as? RsNamedElement)?.namespaces
-            if (newNs != null) {
-                currScope[entry.name] = prevNs?.let { it + newNs } ?: newNs
+        val prevNs = prevScope[entry.name]
+        val newNs = entry.namespaces
+        val entryWithIntersectedNs = if (prevNs != null) {
+            val restNs = newNs.minus(prevNs)
+            if (ns.intersects(restNs)) {
+                entry.copyWithNs(restNs)
+            } else {
+                return false
             }
+        } else {
+            entry
         }
-        return originalProcessor.process(entry)
+        currScope[entry.name] = prevNs?.let { it + newNs } ?: newNs
+        return originalProcessor.process(entryWithIntersectedNs)
     }
     override fun toString(): String = "ShadowingAndUpdateScopeProcessor($originalProcessor, ns = $ns)"
 }
@@ -270,7 +277,14 @@ private fun collectPathScopeEntry(
         val visibilityStatus = e.getVisibilityStatusFrom(ctx.context, ctx.lazyContainingModInfo)
         if (visibilityStatus != VisibilityStatus.CfgDisabled) {
             val isVisible = visibilityStatus == VisibilityStatus.Visible
-            result += RsPathResolveResult(element, e.subst.foldTyInferWithTyPlaceholder(), isVisible)
+            // Canonicalize namespaces to consume less memory by the resolve cache
+            val namespaces = when (e.namespaces) {
+                TYPES -> TYPES
+                VALUES -> VALUES
+                TYPES_N_VALUES -> TYPES_N_VALUES
+                else -> e.namespaces
+            }
+            result += RsPathResolveResult(element, e.subst.foldTyInferWithTyPlaceholder(), isVisible, namespaces)
         }
     }
 }
@@ -374,16 +388,22 @@ fun collectCompletionVariants(
 data class SimpleScopeEntry(
     override val name: String,
     override val element: RsElement,
+    override val namespaces: Set<Namespace>,
     override val subst: Substitution = emptySubstitution
-) : ScopeEntry
+) : ScopeEntry {
+    override fun doCopyWithNs(namespaces: Set<Namespace>): ScopeEntry = copy(namespaces = namespaces)
+}
 
 data class ScopeEntryWithVisibility(
     override val name: String,
     override val element: RsElement,
+    override val namespaces: Set<Namespace>,
     /** Given a [RsElement] (usually [RsPath]) checks if this item is visible in `containingMod` of that element */
     val visibilityFilter: VisibilityFilter,
     override val subst: Substitution = emptySubstitution,
-) : ScopeEntry
+) : ScopeEntry {
+    override fun doCopyWithNs(namespaces: Set<Namespace>): ScopeEntry = copy(namespaces = namespaces)
+}
 
 typealias VisibilityFilter = (RsElement, Lazy<RsModInfo?>?) -> VisibilityStatus
 
@@ -412,39 +432,38 @@ interface AssocItemScopeEntryBase<out T : RsAbstractable> : ScopeEntry {
 data class AssocItemScopeEntry(
     override val name: String,
     override val element: RsAbstractable,
-    override val subst: Substitution = emptySubstitution,
+    override val namespaces: Set<Namespace>,
+    override val subst: Substitution,
     override val selfTy: Ty,
     override val source: TraitImplSource
-) : AssocItemScopeEntryBase<RsAbstractable>
+) : AssocItemScopeEntryBase<RsAbstractable> {
+    override fun doCopyWithNs(namespaces: Set<Namespace>): ScopeEntry = copy(namespaces = namespaces)
+}
 
 
-fun RsResolveProcessor.process(name: String, e: RsElement): Boolean =
-    process(SimpleScopeEntry(name, e))
+fun RsResolveProcessor.process(name: String, namespaces: Set<Namespace>, e: RsElement): Boolean =
+    process(SimpleScopeEntry(name, e, namespaces))
 
 fun RsResolveProcessor.process(
     name: String,
     e: RsElement,
+    namespaces: Set<Namespace>,
     visibilityFilter: VisibilityFilter
-): Boolean = process(ScopeEntryWithVisibility(name, e, visibilityFilter))
+): Boolean = process(ScopeEntryWithVisibility(name, e, namespaces, visibilityFilter))
 
-inline fun RsResolveProcessor.lazy(name: String, e: () -> RsElement?): Boolean {
+inline fun RsResolveProcessor.lazy(name: String, namespaces: Set<Namespace>, e: () -> RsElement?): Boolean {
     if (!acceptsName(name)) return false
     val element = e() ?: return false
-    return process(name, element)
+    return process(name, namespaces, element)
 }
 
-fun RsResolveProcessor.process(e: RsNamedElement): Boolean {
+fun RsResolveProcessor.process(e: RsNamedElement, namespaces: Set<Namespace>): Boolean {
     val name = e.name ?: return false
-    return process(name, e)
-}
-
-fun RsResolveProcessor.process(e: BoundElement<RsNamedElement>): Boolean {
-    val name = e.element.name ?: return false
-    return process(SimpleScopeEntry(name, e.element, e.subst))
+    return process(name, namespaces, e)
 }
 
-fun processAll(elements: List<RsNamedElement>, processor: RsResolveProcessor): Boolean {
-    return elements.any { processor.process(it) }
+fun RsResolveProcessor.processAll(elements: List<RsNamedElement>, namespaces: Set<Namespace>): Boolean {
+    return elements.any { process(it, namespaces) }
 }
 
 fun processAllScopeEntries(elements: List<ScopeEntry>, processor: RsResolveProcessor): Boolean {
@@ -454,10 +473,12 @@ fun processAllScopeEntries(elements: List<ScopeEntry>, processor: RsResolveProce
 fun processAllWithSubst(
     elements: Collection<RsNamedElement>,
     subst: Substitution,
+    namespaces: Set<Namespace>,
     processor: RsResolveProcessor
 ): Boolean {
     for (e in elements) {
-        if (processor.process(BoundElement(e, subst))) return true
+        val name = e.name ?: continue
+        if (processor.process(SimpleScopeEntry(name, e, namespaces, subst))) return true
     }
     return false
 }
diff --git a/src/main/kotlin/org/rust/lang/core/resolve/RsPathResolveResult.kt b/src/main/kotlin/org/rust/lang/core/resolve/RsPathResolveResult.kt
index 07c3f57fc..c6c299b23 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/RsPathResolveResult.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/RsPathResolveResult.kt
@@ -18,17 +18,9 @@ data class RsPathResolveResult<T : RsElement>(
     val element: T,
     val resolvedSubst: Substitution = emptySubstitution,
     val isVisible: Boolean,
+    val namespaces: Set<Namespace> = emptySet(),
 ) : ResolveResult {
     override fun getElement(): PsiElement = element
 
     override fun isValidResult(): Boolean = true
-
-    inline fun mapSubst(
-        f: (Substitution) -> Substitution
-    ): RsPathResolveResult<T> {
-        if (resolvedSubst === emptySubstitution) {
-            return this
-        }
-        return RsPathResolveResult(element, f(resolvedSubst), isVisible)
-    }
 }
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 22966e266..bbf02750c 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
@@ -71,6 +71,11 @@ interface DotExprResolveVariant : ScopeEntry {
     val selfTy: Ty
     /** The number of `*` dereferences should be performed on receiver to match `selfTy` */
     val derefCount: Int
+
+    override val namespaces: Set<Namespace>
+        get() = VALUES // Namespace does not matter in the case of dot expression
+
+    override fun doCopyWithNs(namespaces: Set<Namespace>): ScopeEntry = this
 }
 
 data class FieldResolveVariant(
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 5d5c13bef..1a2621e7e 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
@@ -395,8 +395,7 @@ private fun filterResolveResults(
             1 -> result
             else -> {
                 val types = result.filter {
-                    val element = it.element as? RsNamedElement ?: return@filter false
-                    Namespace.Types in element.namespaces
+                    Namespace.Types in it.namespaces
                 }
                 types.ifEmpty { result }
             }
diff --git a/src/main/kotlin/org/rust/lang/core/resolve2/FacadeResolve.kt b/src/main/kotlin/org/rust/lang/core/resolve2/FacadeResolve.kt
index ffeb28b18..99944995c 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve2/FacadeResolve.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve2/FacadeResolve.kt
@@ -33,6 +33,8 @@ import org.rust.lang.core.resolve.ref.RsResolveCache
 import org.rust.openapiext.testAssert
 import org.rust.openapiext.toPsiFile
 import org.rust.stdext.RsResult
+import org.rust.stdext.enumSetOf
+import java.util.*
 
 fun processItemDeclarations(
     scope: RsItemsOwner,
@@ -52,10 +54,8 @@ fun processItemDeclarationsUsingModInfo(
     ipm: ItemProcessingMode
 ): Boolean {
     val (_, defMap, modData) = info
+    val elements = hashMapOf<RsNamedElement, Pair<EnumSet<Namespace>, VisibilityFilter>>()
     for ((name, perNs) in modData.visibleItems.entriesWithNames(processor.names)) {
-        // We need a `Set` here because item could belong to multiple namespaces (e.g. unit struct)
-        // Also we need to distinguish unit struct and e.g. mod and function with same name in one module
-        val elements = hashSetOf<RsNamedElement>()
         for ((visItems, namespace) in perNs.getVisItemsByNamespace()) {
             if (namespace !in ns) continue
             for (visItem in visItems) {
@@ -63,11 +63,17 @@ fun processItemDeclarationsUsingModInfo(
                 if (namespace == Namespace.Types && visItem.visibility.isInvisible && name in defMap.externPrelude) continue
                 val visibilityFilter = visItem.visibility.createFilter()
                 for (element in visItem.toPsi(info, namespace)) {
-                    if (!elements.add(element)) continue
-                    if (processor.process(name, element, visibilityFilter)) return true
+                    elements.getOrPut(element) { enumSetOf<Namespace>() to visibilityFilter }
+                        .first
+                        .add(namespace)
                 }
             }
         }
+        for ((element, namespaceAndFilter) in elements) {
+            val (namespaces, visibilityFilter) = namespaceAndFilter
+            if (processor.process(name, element, namespaces, visibilityFilter)) return true
+        }
+        elements.clear()
     }
 
     if (processor.names == null && Namespace.Types in ns) {
@@ -75,7 +81,7 @@ fun processItemDeclarationsUsingModInfo(
             val trait = VisItem(traitPath, traitVisibility)
             val visibilityFilter = traitVisibility.createFilter()
             for (traitPsi in trait.toPsi(info, Namespace.Types)) {
-                if (processor.process("_", traitPsi, visibilityFilter)) return true
+                if (processor.process("_", traitPsi, TYPES, visibilityFilter)) return true
             }
         }
     }
@@ -86,7 +92,7 @@ fun processItemDeclarationsUsingModInfo(
             if (existingItemInScope != null && existingItemInScope.types.any { !it.visibility.isInvisible }) continue
 
             val externCrateRoot = externCrateDefMap.rootAsRsMod(info.project) ?: continue
-            processor.process(name, externCrateRoot) && return true
+            processor.process(name, TYPES, externCrateRoot) && return true
         }
     }
 
@@ -134,7 +140,7 @@ private fun ModData.processMacros(
             val visItem = VisItem(macroInfo.path, Visibility.Public)
             val macroContainingScope = visItem.containingMod.toScope(info).singleOrNull() ?: continue
             val macro = macroInfo.legacyMacroToPsi(macroContainingScope, info) ?: continue
-            if (processor.process(name, macro)) return true
+            if (processor.process(name, MACROS, macro)) return true
         }
 
         if (!isHanging) {
@@ -157,7 +163,7 @@ private fun ModData.processScopedMacros(
             if (!filter(name)) continue
             val macro = visItem.scopedMacroToPsi(info) ?: continue
             val visibilityFilter = visItem.visibility.createFilter()
-            if (processor.process(name, macro, visibilityFilter)) return true
+            if (processor.process(name, macro, MACROS, visibilityFilter)) return true
         }
     }
     return false
diff --git a/src/test/kotlin/org/rust/lang/core/completion/RsLookupElementTest.kt b/src/test/kotlin/org/rust/lang/core/completion/RsLookupElementTest.kt
index 3c2249a80..be6f6c941 100644
--- a/src/test/kotlin/org/rust/lang/core/completion/RsLookupElementTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/completion/RsLookupElementTest.kt
@@ -23,6 +23,8 @@ import org.rust.lang.core.psi.ext.RsMethodOrField
 import org.rust.lang.core.psi.ext.RsNamedElement
 import org.rust.lang.core.psi.ext.RsReferenceElement
 import org.rust.lang.core.resolve.SimpleScopeEntry
+import org.rust.lang.core.resolve.TYPES
+import org.rust.lang.core.resolve.TYPES_N_VALUES
 
 class RsLookupElementTest : RsTestBase() {
     fun `test fn`() = check("""
@@ -157,7 +159,7 @@ class RsLookupElementTest : RsTestBase() {
     fun `test mod`() {
         myFixture.configureByText("foo.rs", "")
         val lookup = createLookupElement(
-            SimpleScopeEntry("foo", myFixture.file as RsFile),
+            SimpleScopeEntry("foo", myFixture.file as RsFile, TYPES),
             RsCompletionContext()
         )
         val presentation = LookupElementPresentation()
@@ -331,7 +333,7 @@ class RsLookupElementTest : RsTestBase() {
 
         val element = findElementInEditor<T>()
         val lookup = createLookupElement(
-            SimpleScopeEntry(element.name!!, element as RsElement),
+            SimpleScopeEntry(element.name!!, element as RsElement, TYPES_N_VALUES),
             RsCompletionContext()
         )
 
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsConstArgumentResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsConstArgumentResolveTest.kt
index 1da3188ff..2333a64f8 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsConstArgumentResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsConstArgumentResolveTest.kt
@@ -123,6 +123,29 @@ class RsConstArgumentResolveTest : RsResolveTestBase() {
         }       //^
     """)
 
+    fun `test type namespace is shadowed 1`() = checkByCode("""
+        mod a {
+            pub struct S;
+        }
+        use a::*;
+        struct S {}
+             //X
+        trait Trait<T> {}
+        impl Trait<S> for S {}
+                 //^
+    """)
+
+    fun `test type namespace is shadowed 2`() = checkByCode("""
+        pub struct S;
+        fn main() {
+            struct S {}
+                 //X
+            trait Trait<T> {}
+            impl Trait<S> for S {}
+                     //^
+        }
+    """)
+
     // Method call arguments:
 
     fun `test disambiguate type argument in method call`() = checkByCode("""