From b2c23180f155a1ba43a4e2d98dfebe4dbcf57d2a Mon Sep 17 00:00:00 2001
From: Tomasz Godzik <tgodzik@virtuslab.com>
Date: Tue, 1 Apr 2025 17:46:59 +0200
Subject: [PATCH] improvement: Rework IndexedContext to reuse the previously
 calculated scopes

It turns out the work being done in IndexedContext was already done in Completions, but better, since it doesn't try to read files as the separate logic does.

There is still some improvement to be done to not calculate it twice, but in order to keep this PR as simple as possible I will skip that for now.
---
 .../tools/dotc/interactive/Completion.scala   |  62 +++--
 .../src/main/dotty/tools/pc/AutoImports.scala |  31 ++-
 .../dotty/tools/pc/AutoImportsProvider.scala  |   6 +-
 .../tools/pc/ExtractMethodProvider.scala      |   2 +-
 .../main/dotty/tools/pc/HoverProvider.scala   |   6 +-
 .../main/dotty/tools/pc/IndexedContext.scala  | 239 +++++-------------
 .../dotty/tools/pc/InferExpectedType.scala    |   2 +-
 .../dotty/tools/pc/InferredTypeProvider.scala |   2 +-
 .../dotty/tools/pc/PcDefinitionProvider.scala |   2 +-
 .../dotty/tools/pc/PcInlayHintsProvider.scala |   4 +-
 .../tools/pc/PcInlineValueProvider.scala      |  16 +-
 .../tools/pc/SignatureHelpProvider.scala      |   2 +-
 .../pc/completions/CompletionProvider.scala   |   3 +-
 .../tools/pc/completions/Completions.scala    |   1 +
 .../pc/completions/MatchCaseCompletions.scala |   9 +-
 .../pc/completions/NamedArgCompletions.scala  |  43 ++--
 .../pc/completions/OverrideCompletions.scala  |   2 +-
 .../pc/printer/ShortenedTypePrinter.scala     |  42 +--
 .../pc/utils/InteractiveEnrichments.scala     |   3 +
 .../tests/completion/CompletionArgSuite.scala |   2 +-
 .../completion/CompletionCaseSuite.scala      |   6 +-
 .../pc/tests/inlayHints/InlayHintsSuite.scala |   2 +-
 22 files changed, 207 insertions(+), 280 deletions(-)

diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala
index cef5de613920..36f6fa18e94b 100644
--- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala
+++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala
@@ -48,6 +48,16 @@ case class Completion(label: String, description: String, symbols: List[Symbol])
 
 object Completion:
 
+  def scopeContext(pos: SourcePosition)(using Context): CompletionResult =
+    val tpdPath = Interactive.pathTo(ctx.compilationUnit.tpdTree, pos.span)
+    val completionContext = Interactive.contextOfPath(tpdPath).withPhase(Phases.typerPhase)
+    inContext(completionContext):
+      val untpdPath = Interactive.resolveTypedOrUntypedPath(tpdPath, pos)
+      val mode = completionMode(untpdPath, pos, forSymbolSearch = true)
+      val rawPrefix = completionPrefix(untpdPath, pos)
+      val completer = new Completer(mode, pos, untpdPath, _ => true)
+      completer.scopeCompletions
+
   /** Get possible completions from tree at `pos`
    *
    *  @return offset and list of symbols for possible completions
@@ -60,7 +70,6 @@ object Completion:
       val mode = completionMode(untpdPath, pos)
       val rawPrefix = completionPrefix(untpdPath, pos)
       val completions = rawCompletions(pos, mode, rawPrefix, tpdPath, untpdPath)
-
       postProcessCompletions(untpdPath, completions, rawPrefix)
 
   /** Get possible completions from tree at `pos`
@@ -89,7 +98,7 @@ object Completion:
    *
    * Otherwise, provide no completion suggestion.
    */
-  def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = path match
+  def completionMode(path: List[untpd.Tree], pos: SourcePosition, forSymbolSearch: Boolean = false): Mode = path match
     // Ignore `package foo@@` and `package foo.bar@@`
     case ((_: tpd.Select) | (_: tpd.Ident)):: (_ : tpd.PackageDef) :: _  => Mode.None
     case GenericImportSelector(sel) =>
@@ -102,11 +111,14 @@ object Completion:
     case untpd.Literal(Constants.Constant(_: String)) :: _ => Mode.Term | Mode.Scope // literal completions
     case (ref: untpd.RefTree) :: _ =>
       val maybeSelectMembers = if ref.isInstanceOf[untpd.Select] then Mode.Member else Mode.Scope
-
-      if (ref.name.isTermName) Mode.Term | maybeSelectMembers
+      if (forSymbolSearch) then Mode.Term | Mode.Type | maybeSelectMembers
+      else if (ref.name.isTermName) Mode.Term | maybeSelectMembers
       else if (ref.name.isTypeName) Mode.Type | maybeSelectMembers
       else Mode.None
 
+    case (_: tpd.TypeTree | _: tpd.MemberDef) :: _ if forSymbolSearch => Mode.Type | Mode.Term
+    case (_: tpd.CaseDef) :: _ if forSymbolSearch => Mode.Type | Mode.Term
+    case Nil if forSymbolSearch =>  Mode.Type | Mode.Term
     case _ => Mode.None
 
   /** When dealing with <errors> in varios palces we check to see if they are
@@ -174,12 +186,12 @@ object Completion:
         case _ => None
 
   private object StringContextApplication:
-    def unapply(path: List[tpd.Tree]): Option[tpd.Apply] = 
+    def unapply(path: List[tpd.Tree]): Option[tpd.Apply] =
       path match
         case tpd.Select(qual @ tpd.Apply(tpd.Select(tpd.Select(_, StdNames.nme.StringContext), _), _), _) :: _ =>
           Some(qual)
         case _ => None
-      
+
 
   /** Inspect `path` to determine the offset where the completion result should be inserted. */
   def completionOffset(untpdPath: List[untpd.Tree]): Int =
@@ -230,14 +242,14 @@ object Completion:
     val result = adjustedPath match
       // Ignore synthetic select from `This` because in code it was `Ident`
       // See example in dotty.tools.languageserver.CompletionTest.syntheticThis
-      case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic      => completer.scopeCompletions
+      case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic      => completer.scopeCompletions.names
       case StringContextApplication(qual) =>
-        completer.scopeCompletions ++ completer.selectionCompletions(qual) 
-      case tpd.Select(qual, _) :: _               if qual.typeOpt.hasSimpleKind => 
+        completer.scopeCompletions.names ++ completer.selectionCompletions(qual)
+      case tpd.Select(qual, _) :: _               if qual.typeOpt.hasSimpleKind =>
         completer.selectionCompletions(qual)
       case tpd.Select(qual, _) :: _                                             => Map.empty
       case (tree: tpd.ImportOrExport) :: _                                      => completer.directMemberCompletions(tree.expr)
-      case _                                                                    => completer.scopeCompletions
+      case _                                                                    => completer.scopeCompletions.names
 
     interactiv.println(i"""completion info with pos    = $pos,
                           |                     term   = ${completer.mode.is(Mode.Term)},
@@ -338,6 +350,7 @@ object Completion:
          (completionMode.is(Mode.Term) && (sym.isTerm || sym.is(ModuleClass))
       || (completionMode.is(Mode.Type) && (sym.isType || sym.isStableMember)))
     )
+  end isValidCompletionSymbol
 
   given ScopeOrdering(using Context): Ordering[Seq[SingleDenotation]] with
     val order =
@@ -371,7 +384,7 @@ object Completion:
      *    (even if the import follows it syntactically)
      *  - a more deeply nested import shadowing a member or a local definition causes an ambiguity
      */
-    def scopeCompletions(using context: Context): CompletionMap =
+    def scopeCompletions(using context: Context): CompletionResult =
 
       /** Temporary data structure representing denotations with the same name introduced in a given scope
        *  as a member of a type, by a local definition or by an import clause
@@ -382,14 +395,19 @@ object Completion:
           ScopedDenotations(denots.filter(includeFn), ctx)
 
       val mappings = collection.mutable.Map.empty[Name, List[ScopedDenotations]].withDefaultValue(List.empty)
+      val renames = collection.mutable.Map.empty[Symbol, Name]
       def addMapping(name: Name, denots: ScopedDenotations) =
         mappings(name) = mappings(name) :+ denots
 
       ctx.outersIterator.foreach { case ctx @ given Context =>
         if ctx.isImportContext then
-          importedCompletions.foreach { (name, denots) =>
+          val imported = importedCompletions
+          imported.names.foreach { (name, denots) =>
             addMapping(name, ScopedDenotations(denots, ctx, include(_, name)))
           }
+          imported.renames.foreach { (name, newName) =>
+            renames(name) = newName
+          }
         else if ctx.owner.isClass then
           accessibleMembers(ctx.owner.thisType)
             .groupByName.foreach { (name, denots) =>
@@ -433,7 +451,6 @@ object Completion:
           // most deeply nested member or local definition if not shadowed by an import
           case Some(local) if local.ctx.scope == first.ctx.scope =>
             resultMappings += name -> local.denots
-
           case None if isSingleImport || isImportedInDifferentScope || isSameSymbolImportedDouble =>
             resultMappings += name -> first.denots
           case None if notConflictingWithDefaults =>
@@ -443,7 +460,7 @@ object Completion:
         }
       }
 
-      resultMappings
+      CompletionResult(resultMappings, renames.toMap)
     end scopeCompletions
 
     /** Widen only those types which are applied or are exactly nothing
@@ -485,15 +502,20 @@ object Completion:
     /** Completions introduced by imports directly in this context.
      *  Completions from outer contexts are not included.
      */
-    private def importedCompletions(using Context): CompletionMap =
+    private def importedCompletions(using Context): CompletionResult =
       val imp = ctx.importInfo
+      val renames = collection.mutable.Map.empty[Symbol, Name]
 
       if imp == null then
-        Map.empty
+        CompletionResult(Map.empty, Map.empty)
       else
         def fromImport(name: Name, nameInScope: Name): Seq[(Name, SingleDenotation)] =
           imp.site.member(name).alternatives
-            .collect { case denot if include(denot, nameInScope) => nameInScope -> denot }
+            .collect { case denot if include(denot, nameInScope) =>
+               if name != nameInScope then
+                 renames(denot.symbol) = nameInScope
+               nameInScope -> denot
+            }
 
         val givenImports = imp.importedImplicits
           .map { ref => (ref.implicitName: Name, ref.underlyingRef.denot.asSingleDenotation) }
@@ -519,7 +541,8 @@ object Completion:
               fromImport(original.toTypeName, nameInScope.toTypeName)
             }.toSeq.groupByName
 
-        givenImports ++ wildcardMembers ++ explicitMembers
+        val results = givenImports ++ wildcardMembers ++ explicitMembers
+        CompletionResult(results, renames.toMap)
     end importedCompletions
 
     /** Completions from implicit conversions including old style extensions using implicit classes */
@@ -597,7 +620,7 @@ object Completion:
 
       // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference.
       val termCompleter = new Completer(Mode.Term, pos, untpdPath, matches)
-      val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap:
+      val extMethodsInScope = termCompleter.scopeCompletions.names.toList.flatMap:
         case (name, denots) => denots.collect:
           case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName)
 
@@ -699,6 +722,7 @@ object Completion:
 
   private type CompletionMap = Map[Name, Seq[SingleDenotation]]
 
+  case class CompletionResult(names: Map[Name, Seq[SingleDenotation]], renames: Map[Symbol, Name])
   /**
    * The completion mode: defines what kinds of symbols should be included in the completion
    * results.
diff --git a/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala b/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala
index 1b44dce8c642..7b30c745e3ed 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala
@@ -40,7 +40,7 @@ object AutoImports:
     case class Select(qual: SymbolIdent, name: String) extends SymbolIdent:
       def value: String = s"${qual.value}.$name"
 
-    def direct(name: String): SymbolIdent = Direct(name)
+    def direct(name: String)(using Context): SymbolIdent = Direct(name)
 
     def fullIdent(symbol: Symbol)(using Context): SymbolIdent =
       val symbols = symbol.ownersIterator.toList
@@ -70,7 +70,7 @@ object AutoImports:
       importSel: Option[ImportSel]
   ):
 
-    def name: String = ident.value
+    def name(using Context): String = ident.value
 
   object SymbolImport:
 
@@ -189,10 +189,13 @@ object AutoImports:
                 ownerImport.importSel,
               )
             else
-              (
-                SymbolIdent.direct(symbol.nameBackticked),
-                Some(ImportSel.Direct(symbol)),
-              )
+              renames(symbol) match
+                case Some(rename) => (SymbolIdent.direct(rename), None)
+                case None =>
+                  (
+                    SymbolIdent.direct(symbol.nameBackticked),
+                    Some(ImportSel.Direct(symbol)),
+                  )
           end val
 
           SymbolImport(
@@ -223,9 +226,13 @@ object AutoImports:
                 importSel
               )
             case None =>
+              val reverse = symbol.ownersIterator.toList.reverse
+              val fullName = reverse.drop(1).foldLeft(SymbolIdent.direct(reverse.head.nameBackticked)){
+                case (acc, sym) => SymbolIdent.Select(acc, sym.nameBackticked(false))
+              }
               SymbolImport(
                 symbol,
-                SymbolIdent.direct(symbol.fullNameBackticked),
+                SymbolIdent.Direct(symbol.fullNameBackticked),
                 None
               )
           end match
@@ -252,7 +259,6 @@ object AutoImports:
         val topPadding =
           if importPosition.padTop then "\n"
           else ""
-
         val formatted = imports
           .map {
             case ImportSel.Direct(sym) => importName(sym)
@@ -267,15 +273,16 @@ object AutoImports:
     end renderImports
 
     private def importName(sym: Symbol): String =
-      if indexedContext.importContext.toplevelClashes(sym) then
+      if indexedContext.toplevelClashes(sym, inImportScope = true) then
         s"_root_.${sym.fullNameBackticked(false)}"
       else
         sym.ownersIterator.zipWithIndex.foldLeft((List.empty[String], false)) { case ((acc, isDone), (sym, idx)) =>
           if(isDone || sym.isEmptyPackage || sym.isRoot) (acc, true)
           else indexedContext.rename(sym) match
-            case Some(renamed) => (renamed :: acc, true)
-            case None if !sym.isPackageObject => (sym.nameBackticked(false) :: acc, false)
-            case None => (acc, false)
+            // we can't import first part
+            case Some(renamed) if idx != 0 => (renamed :: acc, true)
+            case _ if !sym.isPackageObject => (sym.nameBackticked(false) :: acc, false)
+            case _ => (acc, false)
         }._1.mkString(".")
   end AutoImportsGenerator
 
diff --git a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala
index d30ab813f9f2..0a6178eae106 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala
@@ -44,8 +44,8 @@ final class AutoImportsProvider(
     val path =
       Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx)
 
-    val indexedContext = IndexedContext(
-      Interactive.contextOfPath(path)(using newctx)
+    val indexedContext = IndexedContext(pos)(
+      using Interactive.contextOfPath(path)(using newctx)
     )
     import indexedContext.ctx
 
@@ -96,7 +96,7 @@ final class AutoImportsProvider(
                 text,
                 tree,
                 unit.comments,
-                indexedContext.importContext,
+                indexedContext,
                 config
               )
             (sym: Symbol) => generator.forSymbol(sym)
diff --git a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala
index c72a0602f1ce..00cde67873d5 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala
@@ -51,7 +51,7 @@ final class ExtractMethodProvider(
     given locatedCtx: Context =
       val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
       Interactive.contextOfPath(path)(using newctx)
-    val indexedCtx = IndexedContext(locatedCtx)
+    val indexedCtx = IndexedContext(pos)(using locatedCtx)
     val printer =
       ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedCtx)
     def prettyPrint(tpe: Type) =
diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala
index 3b2f4d2aa9b0..746be65155d9 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala
@@ -49,7 +49,7 @@ object HoverProvider:
     val path = unit
       .map(unit => Interactive.pathTo(unit.tpdTree, pos.span))
       .getOrElse(Interactive.pathTo(driver.openedTrees(uri), pos))
-    val indexedContext = IndexedContext(ctx)
+    val indexedContext = IndexedContext(pos)(using ctx)
 
     def typeFromPath(path: List[Tree]) =
       if path.isEmpty then NoType else path.head.typeOpt
@@ -96,7 +96,7 @@ object HoverProvider:
 
       val printerCtx = Interactive.contextOfPath(path)
       val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Include)(
-        using IndexedContext(printerCtx)
+        using IndexedContext(pos)(using printerCtx)
       )
       MetalsInteractive.enclosingSymbolsWithExpressionType(
         enclosing,
@@ -134,7 +134,7 @@ object HoverProvider:
             .map(_.docstring())
             .mkString("\n")
 
-          val expresionTypeOpt = 
+          val expresionTypeOpt =
             if symbol.name == StdNames.nme.??? then
               InferExpectedType(search, driver, params).infer()
             else printer.expressionType(exprTpw)
diff --git a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala
index 7c2c34cf5ebb..e457edfe1798 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala
@@ -4,64 +4,45 @@ import scala.annotation.tailrec
 import scala.util.control.NonFatal
 
 import dotty.tools.dotc.core.Contexts.*
+import dotty.tools.dotc.core.Denotations.PreDenotation
+import dotty.tools.dotc.core.Denotations.SingleDenotation
 import dotty.tools.dotc.core.Flags.*
-import dotty.tools.dotc.core.NameOps.moduleClassName
+import dotty.tools.dotc.core.NameOps.*
 import dotty.tools.dotc.core.Names.*
 import dotty.tools.dotc.core.Scopes.EmptyScope
 import dotty.tools.dotc.core.Symbols.*
 import dotty.tools.dotc.core.Types.*
+import dotty.tools.dotc.interactive.Completion
 import dotty.tools.dotc.interactive.Interactive
 import dotty.tools.dotc.typer.ImportInfo
+import dotty.tools.dotc.util.SourcePosition
 import dotty.tools.pc.IndexedContext.Result
 import dotty.tools.pc.utils.InteractiveEnrichments.*
 
 sealed trait IndexedContext:
   given ctx: Context
   def scopeSymbols: List[Symbol]
-  def names: IndexedContext.Names
   def rename(sym: Symbol): Option[String]
-  def outer: IndexedContext
-
-  def findSymbol(name: String): Option[List[Symbol]]
-
-  final def findSymbol(name: Name): Option[List[Symbol]] =
-    findSymbol(name.decoded)
+  def findSymbol(name: Name): Option[List[Symbol]]
+  def findSymbolInLocalScope(name: String): Option[List[Symbol]]
 
   final def lookupSym(sym: Symbol): Result =
-    findSymbol(sym.decodedName) match
-      case Some(symbols) if symbols.exists(_ == sym) =>
-        Result.InScope
-      case Some(symbols)
-          if symbols.exists(s => isNotConflictingWithDefault(s, sym) || isTypeAliasOf(s, sym) || isTermAliasOf(s, sym)) =>
-            Result.InScope
-      // when all the conflicting symbols came from an old version of the file
+    def all(symbol: Symbol): Set[Symbol] = Set(symbol, symbol.companionModule, symbol.companionClass, symbol.companion).filter(_ != NoSymbol)
+    val isRelated = all(sym) ++ all(sym.dealiasType)
+    findSymbol(sym.name) match
+      case Some(symbols) if symbols.exists(isRelated) => Result.InScope
+      case Some(symbols) if symbols.exists(isTermAliasOf(_, sym)) => Result.InScope
+      case Some(symbols) if symbols.map(_.dealiasType).exists(isRelated) => Result.InScope
       case Some(symbols) if symbols.nonEmpty && symbols.forall(_.isStale) => Result.Missing
-      case Some(symbols) if symbols.exists(rename(_).isEmpty) => Result.Conflict
+      case Some(symbols) if symbols.exists(rename(_).isEmpty) && rename(sym).isEmpty => Result.Conflict
+      case Some(symbols) => Result.InScope
       case _ => Result.Missing
   end lookupSym
 
-  /**
-   * Scala by default imports following packages:
-   * https://scala-lang.org/files/archive/spec/3.4/02-identifiers-names-and-scopes.html
-   * import java.lang.*
-   * {
-   *   import scala.*
-   *   {
-   *     import Predef.*
-   *     { /* source */ }
-   *   }
-   * }
-   *
-   * This check is necessary for proper scope resolution, because when we compare symbols from
-   * index including the underlying type like scala.collection.immutable.List it actually
-   * is in current scope in form of type forwarder imported from Predef.
-   */
-  private def isNotConflictingWithDefault(sym: Symbol, queriedSym: Symbol): Boolean =
-    sym.info.widenDealias =:= queriedSym.info.widenDealias && (Interactive.isImportedByDefault(sym))
-
   final def hasRename(sym: Symbol, as: String): Boolean =
     rename(sym) match
-      case Some(v) => v == as
+      case Some(v) =>
+        v == as
       case None => false
 
   // detects import scope aliases like
@@ -74,73 +55,71 @@ sealed trait IndexedContext:
         case _ => false
     )
 
-  private def isTypeAliasOf(alias: Symbol, queriedSym: Symbol): Boolean =
-    alias.isAliasType && alias.info.deepDealias.typeSymbol  == queriedSym
-
-  final def isEmpty: Boolean = this match
-    case IndexedContext.Empty => true
-    case _ => false
-
-  final def importContext: IndexedContext =
-    this match
-      case IndexedContext.Empty => this
-      case _ if ctx.owner.is(Package) => this
-      case _ => outer.importContext
-
   @tailrec
-  final def toplevelClashes(sym: Symbol): Boolean =
+  final def toplevelClashes(sym: Symbol, inImportScope: Boolean): Boolean =
     if sym == NoSymbol || sym.owner == NoSymbol || sym.owner.isRoot then
-      lookupSym(sym) match
-        case IndexedContext.Result.Conflict => true
+      // we need to fix the import only if the toplevel package conflicts with the imported one
+      findSymbolInLocalScope(sym.name.show) match
+        case Some(symbols) if !symbols.contains(sym) && (symbols.exists(_.owner.is(Package)) || !inImportScope) => true
         case _ => false
-    else toplevelClashes(sym.owner)
+    else toplevelClashes(sym.owner, inImportScope)
 
 end IndexedContext
 
 object IndexedContext:
 
-  def apply(ctx: Context): IndexedContext =
+  def apply(pos: SourcePosition)(using Context): IndexedContext =
     ctx match
       case NoContext => Empty
-      case _ => LazyWrapper(using ctx)
+      case _ => LazyWrapper(pos)(using ctx)
 
   case object Empty extends IndexedContext:
     given ctx: Context = NoContext
-    def findSymbol(name: String): Option[List[Symbol]] = None
+    def findSymbol(name: Name): Option[List[Symbol]] = None
+    def findSymbolInLocalScope(name: String): Option[List[Symbol]] = None
     def scopeSymbols: List[Symbol] = List.empty
-    val names: Names = Names(Map.empty, Map.empty)
     def rename(sym: Symbol): Option[String] = None
-    def outer: IndexedContext = this
-
-  class LazyWrapper(using val ctx: Context) extends IndexedContext:
-    val outer: IndexedContext = IndexedContext(ctx.outer)
-    val names: Names = extractNames(ctx)
 
-    def findSymbol(name: String): Option[List[Symbol]] =
-      names.symbols
-        .get(name)
-        .map(_.toList)
-        .orElse(outer.findSymbol(name))
+  class LazyWrapper(pos: SourcePosition)(using val ctx: Context) extends IndexedContext:
+
+    val completionContext = Completion.scopeContext(pos)
+    val names: Map[String, Seq[SingleDenotation]] = completionContext.names.toList.groupBy(_._1.show).map{
+      case (name, denotations) =>
+        val denots = denotations.flatMap(_._2)
+        val nonRoot = denots.filter(!_.symbol.owner.isRoot)
+        def hasImportedByDefault = denots.exists(denot => Interactive.isImportedByDefault(denot.symbol))
+        def hasConflictingValue = denots.exists(denot => !Interactive.isImportedByDefault(denot.symbol))
+        if hasImportedByDefault && hasConflictingValue then
+          name.trim -> nonRoot.filter(denot => !Interactive.isImportedByDefault(denot.symbol))
+        else
+          name.trim -> nonRoot
+    }
+    val renames = completionContext.renames
+
+    def defaultScopes(name: Name): Option[List[Symbol]] =
+      val fromPredef = defn.ScalaPredefModuleClass.membersNamed(name)
+      val fromScala = defn.ScalaPackageClass.membersNamed(name)
+      val fromJava = defn.JavaLangPackageClass.membersNamed(name)
+      val predefList = if fromPredef.exists then List(fromPredef.first.symbol) else Nil
+      val scalaList = if fromScala.exists then List(fromScala.first.symbol) else Nil
+      val javaList = if fromJava.exists then List(fromJava.first.symbol) else Nil
+      val combined = predefList ++ scalaList ++ javaList
+      if combined.nonEmpty then Some(combined) else None
+
+    override def findSymbolInLocalScope(name: String): Option[List[Symbol]] =
+      names.get(name).map(_.map(_.symbol).toList).filter(_.nonEmpty)
+    def findSymbol(name: Name): Option[List[Symbol]] =
+      names
+        .get(name.show)
+        .map(_.map(_.symbol).toList)
+        .orElse(defaultScopes(name))
 
     def scopeSymbols: List[Symbol] =
-      val acc = Set.newBuilder[Symbol]
-      (this :: outers).foreach { ref =>
-        acc ++= ref.names.symbols.values.flatten
-      }
-      acc.result.toList
+      names.values.flatten.map(_.symbol).toList
 
     def rename(sym: Symbol): Option[String] =
-      names.renames
-        .get(sym)
-        .orElse(outer.rename(sym))
-
-    private def outers: List[IndexedContext] =
-      val builder = List.newBuilder[IndexedContext]
-      var curr = outer
-      while !curr.isEmpty do
-        builder += curr
-        curr = curr.outer
-      builder.result
+      renames.get(sym).orElse(renames.get(sym.companion)).map(_.decoded)
+
   end LazyWrapper
 
   enum Result:
@@ -149,97 +128,5 @@ object IndexedContext:
       case InScope | Conflict => true
       case Missing => false
 
-  case class Names(
-      symbols: Map[String, List[Symbol]],
-      renames: Map[Symbol, String]
-  )
-
-  private def extractNames(ctx: Context): Names =
-    def isAccessibleFromSafe(sym: Symbol, site: Type): Boolean =
-      try sym.isAccessibleFrom(site, superAccess = false)
-      catch
-        case NonFatal(e) =>
-          false
-
-    def accessibleSymbols(site: Type, tpe: Type)(using
-        Context
-    ): List[Symbol] =
-      tpe.decls.toList.filter(sym => isAccessibleFromSafe(sym, site))
-
-    def accesibleMembers(site: Type)(using Context): List[Symbol] =
-      site.allMembers
-        .filter(denot =>
-          try isAccessibleFromSafe(denot.symbol, site)
-          catch
-            case NonFatal(e) =>
-              false
-        )
-        .map(_.symbol)
-        .toList
-
-    def allAccessibleSymbols(
-        tpe: Type,
-        filter: Symbol => Boolean = _ => true
-    )(using Context): List[Symbol] =
-      val initial = accessibleSymbols(tpe, tpe).filter(filter)
-      val fromPackageObjects =
-        initial
-          .filter(_.isPackageObject)
-          .flatMap(sym => accessibleSymbols(tpe, sym.thisType))
-      initial ++ fromPackageObjects
-
-    def fromImport(site: Type, name: Name)(using Context): List[Symbol] =
-      List(
-        site.member(name.toTypeName),
-        site.member(name.toTermName),
-        site.member(name.moduleClassName),
-      )
-        .flatMap(_.alternatives)
-        .map(_.symbol)
-
-    def fromImportInfo(
-        imp: ImportInfo
-    )(using Context): List[(Symbol, Option[TermName])] =
-      val excludedNames = imp.excluded.map(_.decoded)
-
-      if imp.isWildcardImport then
-        allAccessibleSymbols(
-          imp.site,
-          sym => !excludedNames.contains(sym.name.decoded)
-        ).map((_, None))
-      else
-        imp.forwardMapping.toList.flatMap { (name, rename) =>
-          val isRename = name != rename
-          if !isRename && !excludedNames.contains(name.decoded) then
-            fromImport(imp.site, name).map((_, None))
-          else if isRename then
-            fromImport(imp.site, name).map((_, Some(rename)))
-          else Nil
-        }
-      end if
-    end fromImportInfo
-
-    given Context = ctx
-    val (symbols, renames) =
-      if ctx.isImportContext then
-        val (syms, renames) =
-          fromImportInfo(ctx.importInfo.nn)
-            .map((sym, rename) => (sym, rename.map(r => sym -> r.decoded)))
-            .unzip
-        (syms, renames.flatten.toMap)
-      else if ctx.owner.isClass then
-        val site = ctx.owner.thisType
-        (accesibleMembers(site), Map.empty)
-      else if ctx.scope != EmptyScope then (ctx.scope.toList, Map.empty)
-      else (List.empty, Map.empty)
-
-    val initial = Map.empty[String, List[Symbol]]
-    val values =
-      symbols.foldLeft(initial) { (acc, sym) =>
-        val name = sym.decodedName
-        val syms = acc.getOrElse(name, List.empty)
-        acc.updated(name, sym :: syms)
-      }
-    Names(values, renames)
-  end extractNames
+
 end IndexedContext
diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala
index 3d65f69621e1..075167f3f5c1 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala
@@ -51,7 +51,7 @@ class InferExpectedType(
           )
         val locatedCtx =
           Interactive.contextOfPath(tpdPath)(using newctx)
-        val indexedCtx = IndexedContext(locatedCtx)
+        val indexedCtx = IndexedContext(pos)(using locatedCtx)
         val printer =
           ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using indexedCtx)
         InterCompletionType.inferType(path)(using newctx).map{
diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala
index a0d726d5f382..2006774ae19b 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala
@@ -75,7 +75,7 @@ final class InferredTypeProvider(
       Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx)
 
     given locatedCtx: Context = driver.localContext(params)
-    val indexedCtx = IndexedContext(locatedCtx)
+    val indexedCtx = IndexedContext(pos)(using locatedCtx)
     val autoImportsGen = AutoImports.generator(
       pos,
       sourceText,
diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala
index 9fbaa8dcd16d..ca5a36cefad0 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala
@@ -51,7 +51,7 @@ class PcDefinitionProvider(
       Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx)
 
     given ctx: Context = driver.localContext(params)
-    val indexedContext = IndexedContext(ctx)
+    val indexedContext = IndexedContext(pos)(using ctx)
     val result =
       if findTypeDef then findTypeDefinitions(path, pos, indexedContext, uri)
       else findDefinitions(path, pos, indexedContext, uri)
diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala
index cf4929dfc91d..8718eaf58a88 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala
@@ -125,7 +125,7 @@ class PcInlayHintsProvider(
     val tpdPath =
       Interactive.pathTo(unit.tpdTree, pos.span)
 
-    val indexedCtx = IndexedContext(Interactive.contextOfPath(tpdPath))
+    val indexedCtx = IndexedContext(pos)(using Interactive.contextOfPath(tpdPath))
     val printer = ShortenedTypePrinter(
       symbolSearch
     )(using indexedCtx)
@@ -149,7 +149,7 @@ class PcInlayHintsProvider(
     InlayHints.makeLabelParts(parts, tpeStr)
   end toLabelParts
 
-  private val definitions = IndexedContext(ctx).ctx.definitions
+  private val definitions = IndexedContext(pos)(using ctx).ctx.definitions
   private def syntheticTupleApply(tree: Tree): Boolean =
     tree match
       case sel: Select =>
diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala
index eafac513c7e1..edd556c53a2f 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala
@@ -110,8 +110,8 @@ final class PcInlineValueProvider(
         }
         .toRight(Errors.didNotFindDefinition)
       path = Interactive.pathTo(unit.tpdTree, definition.tree.rhs.span)(using newctx)
-      indexedContext = IndexedContext(Interactive.contextOfPath(path)(using newctx))
-      symbols = symbolsUsedInDefn(definition.tree.rhs).filter(indexedContext.lookupSym(_) == Result.InScope)
+      indexedContext = IndexedContext(definition.tree.namePos)(using Interactive.contextOfPath(path)(using newctx))
+      symbols = symbolsUsedInDefn(definition.tree.rhs, indexedContext)
       references <- getReferencesToInline(definition, allOccurences, symbols)
     yield
       val (deleteDefinition, refsEdits) = references
@@ -184,15 +184,19 @@ final class PcInlineValueProvider(
     val adjustedEnd = extend(pos.end - 1, ')', 1) + 1
     text.slice(adjustedStart, adjustedEnd).mkString
 
-  private def symbolsUsedInDefn(rhs: Tree): Set[Symbol] =
+  private def symbolsUsedInDefn(rhs: Tree, indexedContext: IndexedContext): Set[Symbol] =
     def collectNames(
         symbols: Set[Symbol],
         tree: Tree
     ): Set[Symbol] =
       tree match
-        case id: (Ident | Select)
+        case id: Ident
             if !id.symbol.is(Synthetic) && !id.symbol.is(Implicit) =>
           symbols + tree.symbol
+        case sel: Select =>
+          indexedContext.lookupSym(sel.symbol) match
+            case IndexedContext.Result.InScope => symbols + sel.symbol
+            case _ => symbols
         case _ => symbols
 
     val traverser = new DeepFolder[Set[Symbol]](collectNames)
@@ -247,8 +251,8 @@ final class PcInlineValueProvider(
     def buildRef(occurrence: Occurence): Either[String, Reference] =
       val path =
         Interactive.pathTo(unit.tpdTree, occurrence.pos.span)(using newctx)
-      val indexedContext = IndexedContext(
-        Interactive.contextOfPath(path)(using newctx)
+      val indexedContext = IndexedContext(pos)(
+        using Interactive.contextOfPath(path)(using newctx)
       )
       import indexedContext.ctx
       val conflictingSymbols = symbols
diff --git a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala
index bd16d2ce2aa9..5f925ea80ee7 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala
@@ -37,7 +37,7 @@ object SignatureHelpProvider:
         val path = Interactive.pathTo(unit.tpdTree, pos.span)(using driver.currentCtx)
 
         val localizedContext = Interactive.contextOfPath(path)(using driver.currentCtx)
-        val indexedContext = IndexedContext(driver.currentCtx)
+        val indexedContext = IndexedContext(pos)(using driver.currentCtx)
 
         given Context = localizedContext.fresh
           .setCompilationUnit(unit)
diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala
index 2a63d6a92a81..ef9f77eb58fc 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala
@@ -107,7 +107,7 @@ class CompletionProvider(
 
 
         val locatedCtx = Interactive.contextOfPath(tpdPath)(using newctx)
-        val indexedCtx = IndexedContext(locatedCtx)
+        val indexedCtx = IndexedContext(pos)(using locatedCtx)
 
         val completionPos = CompletionPos.infer(pos, params, adjustedPath, wasCursorApplied)(using locatedCtx)
 
@@ -222,7 +222,6 @@ class CompletionProvider(
       if config.isDetailIncludedInLabel then completion.labelWithDescription(printer)
       else completion.label
     val ident = underlyingCompletion.insertText.getOrElse(underlyingCompletion.label)
-
     lazy val isInStringInterpolation =
       path match
         // s"My name is $name"
diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala
index 05dbe1ef5a43..4b2e76807895 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala
@@ -569,6 +569,7 @@ class Completions(
         then
           indexedContext.lookupSym(sym) match
             case IndexedContext.Result.InScope => false
+            case IndexedContext.Result.Missing if indexedContext.rename(sym).isDefined => false
             case _ if completionMode.is(Mode.ImportOrExport) =>
               visit(
                 CompletionValue.Workspace(
diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala
index 2efcba48e82d..4fbf22e2294c 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala
@@ -147,7 +147,7 @@ object CaseKeywordCompletion:
             definitions.NullClass,
             definitions.NothingClass,
           )
-          val tpes = Set(selectorSym, selectorSym.companion)
+          val tpes = Set(selectorSym, selectorSym.companion).filter(_ != NoSymbol)
           def isSubclass(sym: Symbol) = tpes.exists(par => sym.isSubClass(par))
 
           def visit(symImport: SymbolImport): Unit =
@@ -174,8 +174,9 @@ object CaseKeywordCompletion:
 
           indexedContext.scopeSymbols
             .foreach(s =>
-              val ts = s.info.deepDealias.typeSymbol
-              if isValid(ts) then visit(autoImportsGen.inferSymbolImport(ts))
+              val ts = if s.is(Flags.Module) then s.info.typeSymbol else s.dealiasType
+              if isValid(ts) then
+                visit(autoImportsGen.inferSymbolImport(ts))
             )
           // Step 2: walk through known subclasses of sealed types.
           val sealedDescs = subclassesForType(
@@ -185,6 +186,7 @@ object CaseKeywordCompletion:
             val symbolImport = autoImportsGen.inferSymbolImport(sym)
             visit(symbolImport)
           }
+
           val res = result.result().flatMap {
             case si @ SymbolImport(sym, name, importSel) =>
               completionGenerator.labelForCaseMember(sym, name.value).map {
@@ -293,7 +295,6 @@ object CaseKeywordCompletion:
 
     val (labels, imports) =
       sortedSubclasses.map((si, label) => (label, si.importSel)).unzip
-
     val (obracket, cbracket) = if noIndent then (" {", "}") else ("", "")
     val basicMatch = CompletionValue.MatchCompletion(
       "match",
diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala
index a21706b9e36e..c72dd3008256 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala
@@ -30,6 +30,9 @@ import dotty.tools.dotc.core.Types.WildcardType
 import dotty.tools.pc.IndexedContext
 import dotty.tools.pc.utils.InteractiveEnrichments.*
 import scala.annotation.tailrec
+import dotty.tools.dotc.core.Denotations.Denotation
+import dotty.tools.dotc.core.Denotations.MultiDenotation
+import dotty.tools.dotc.core.Denotations.SingleDenotation
 
 object NamedArgCompletions:
 
@@ -138,44 +141,40 @@ object NamedArgCompletions:
     def fallbackFindMatchingMethods() =
       def maybeNameAndIndexedContext(
           method: Tree
-      ): Option[(Name, IndexedContext)] =
+      ): List[Symbol] =
         method match
-          case Ident(name) => Some((name, indexedContext))
-          case Select(This(_), name) => Some((name, indexedContext))
-          case Select(from, name) =>
+          case Ident(name) => indexedContext.findSymbol(name).getOrElse(Nil)
+          case Select(This(_), name) => indexedContext.findSymbol(name).getOrElse(Nil)
+          case sel @ Select(from, name) =>
             val symbol = from.symbol
             val ownerSymbol =
               if symbol.is(Method) && symbol.owner.isClass then
                 Some(symbol.owner)
               else Try(symbol.info.classSymbol).toOption
-            ownerSymbol.map(sym =>
-              (name, IndexedContext(context.localContext(from, sym)))
-            )
+            ownerSymbol.map(sym =>  sym.info.member(name)).collect{
+              case single: SingleDenotation => List(single.symbol)
+              case multi: MultiDenotation => multi.allSymbols
+            }.getOrElse(Nil)
           case Apply(fun, _) => maybeNameAndIndexedContext(fun)
-          case _ => None
+          case _ => Nil
       val matchingMethods =
         for
-          (name, indexedContext) <- maybeNameAndIndexedContext(method)
-          potentialMatches <- indexedContext.findSymbol(name)
-        yield
-          potentialMatches.collect {
-            case m
-              if m.is(Flags.Method) &&
-                m.vparamss.length >= argss.length &&
-                Try(m.isAccessibleFrom(apply.symbol.info)).toOption
+          potentialMatch <- maybeNameAndIndexedContext(method)
+          if potentialMatch.is(Flags.Method) &&
+                potentialMatch.vparamss.length >= argss.length &&
+                Try(potentialMatch.isAccessibleFrom(apply.symbol.info)).toOption
                   .getOrElse(false) &&
-                m.vparamss
+                potentialMatch.vparamss
                   .zip(argss)
                   .reverse
                   .zipWithIndex
                   .forall { case (pair, index) =>
-                    FuzzyArgMatcher(m.tparams)
+                    FuzzyArgMatcher(potentialMatch.tparams)
                       .doMatch(allArgsProvided = index != 0, ident)
                       .tupled(pair)
-                  } =>
-            m
-        }
-      matchingMethods.getOrElse(Nil)
+                    }
+        yield potentialMatch
+      matchingMethods
     end fallbackFindMatchingMethods
 
     val matchingMethods: List[Symbols.Symbol] =
diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala
index f5c15ca6df0e..8123bc8fa216 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala
@@ -191,7 +191,7 @@ object OverrideCompletions:
               template :: path
             case path => path
 
-        val indexedContext = IndexedContext(
+        val indexedContext = IndexedContext(pos)(using
           Interactive.contextOfPath(path)(using newctx)
         )
         import indexedContext.ctx
diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala
index b66fbe56fb9b..ff58a1b01960 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala
@@ -133,11 +133,16 @@ class ShortenedTypePrinter(
     prefixIterator.flatMap { owner =>
       val prefixAfterRename = ownersAfterRename(owner)
       val ownerRename = indexedCtx.rename(owner)
-        ownerRename.foreach(rename => foundRenames += owner -> rename)
+      ownerRename.foreach(rename => foundRenames += owner -> rename)
       val currentRenamesSearchResult =
-        ownerRename.map(Found(owner, _, prefixAfterRename))
+      ownerRename.map(rename => Found(owner, rename, prefixAfterRename))
       lazy val configRenamesSearchResult =
-        renameConfigMap.get(owner).map(Missing(owner, _, prefixAfterRename))
+        renameConfigMap.get(owner).flatMap{rename =>
+          // if the rename is taken, we don't want to use it
+          indexedCtx.findSymbolInLocalScope(rename) match
+            case Some(symbols) => None
+            case None => Some(Missing(owner, rename, prefixAfterRename))
+        }
       currentRenamesSearchResult orElse configRenamesSearchResult
     }.nextOption
 
@@ -150,28 +155,24 @@ class ShortenedTypePrinter(
   private def optionalRootPrefix(sym: Symbol): Text =
     // If the symbol has toplevel clash we need to prepend `_root_.` to the symbol to disambiguate
     // it from the local symbol. It is only required when we are computing text for text edit.
-    if isTextEdit && indexedCtx.toplevelClashes(sym) then
+    if isTextEdit && indexedCtx.toplevelClashes(sym, inImportScope = false) then
       Str("_root_.")
     else
       Text()
 
   private def findRename(tp: NamedType): Option[Text] =
     val maybePrefixRename = findPrefixRename(tp.symbol.maybeOwner)
+    maybePrefixRename.map {
+      case res: Found => res.toPrefixText
+      case res: Missing =>
+        val importSel =
+          if res.owner.name.decoded == res.rename then
+            ImportSel.Direct(res.owner)
+          else ImportSel.Rename(res.owner, res.rename)
 
-    if maybePrefixRename.exists(importRename => indexedCtx.findSymbol(importRename.rename).isDefined) then
-      Some(super.toTextPrefixOf(tp))
-    else
-      maybePrefixRename.map {
-        case res: Found => res.toPrefixText
-        case res: Missing =>
-          val importSel =
-            if res.owner.name.toString == res.rename then
-              ImportSel.Direct(res.owner)
-            else ImportSel.Rename(res.owner, res.rename)
-
-          missingImports += importSel
-          res.toPrefixText
-      }
+        missingImports += importSel
+        res.toPrefixText
+    }
 
 
   override def toTextPrefixOf(tp: NamedType): Text = controlled {
@@ -184,7 +185,10 @@ class ShortenedTypePrinter(
           // symbol is missing and is accessible statically, we can import it and add proper prefix
           case Result.Missing if isAccessibleStatically(tp.symbol) =>
             maybeRenamedPrefix.getOrElse:
-              missingImports += ImportSel.Direct(tp.symbol)
+              indexedCtx.rename(tp.symbol) match
+                case None =>
+                  missingImports += ImportSel.Direct(tp.symbol)
+                case _ =>
               Text()
           // the symbol is in scope, we can omit the prefix
           case Result.InScope => Text()
diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala
index b65f23fae40f..02d384cccf0a 100644
--- a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala
+++ b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala
@@ -177,6 +177,9 @@ object InteractiveEnrichments extends CommonMtagsEnrichments:
     def companion: Symbol =
       if sym.is(Module) then sym.companionClass else sym.companionModule
 
+    def dealiasType: Symbol =
+      if sym.isType then sym.info.deepDealias.typeSymbol else sym
+
     def nameBackticked: String = nameBackticked(Set.empty[String])
 
     def nameBackticked(backtickSoftKeyword: Boolean = true): String =
diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala
index e5f2d31ad808..044b5456d31d 100644
--- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala
+++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala
@@ -826,8 +826,8 @@ class CompletionArgSuite extends BaseCompletionSuite:
          |""".stripMargin,
       """|aaa = : Int
          |aaa = g : Int
-         |abb = : Option[Int]
          |abb = : Int
+         |abb = : Option[Int]
          |abb = g : Int
          |""".stripMargin,
       topLines = Some(5),
diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala
index e72ee5221d91..edc6cb2b4fb2 100644
--- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala
+++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala
@@ -37,7 +37,7 @@ class CompletionCaseSuite extends BaseCompletionSuite:
         |class Cat extends Animal
         |class Dog extends Animal
         |object Elephant extends Animal
-        |class HasFeet[A, B](e: T, f: B) extends Animal
+        |class HasFeet[A, B](e: A, f: B) extends Animal
         |class HasMouth[T](e: T) extends Animal
         |case class HasWings[T](e: T) extends Animal
         |case object Seal extends Animal
@@ -146,14 +146,12 @@ class CompletionCaseSuite extends BaseCompletionSuite:
          |""".stripMargin
     )
 
-  // TODO: `Left` has conflicting name in Scope, we should fix it so the result is the same as for scala 2
-  // Issue: https://github.com/scalameta/metals/issues/4368
   @Test def `sealed-conflict` =
     check(
       """
         |object A {
         |  val e: Either[Int, String] = ???
-        |  type Left = String
+        |  val Left = 123
         |  e match {
         |    case@@
         |  }
diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala
index 84002e54dcb7..bf917f05669b 100644
--- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala
+++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala
@@ -655,7 +655,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite {
         |    val y/*: S<<scala/collection/Set#>>[Char<<scala/Char#>>]*/ = f
         |    ???
         |  }
-        |  val x/*: AB<<scala/collection/AbstractMap#>>[Int<<scala/Int#>>, String<<scala/Predef.String#>>]*/ = test(Set/*[Int<<scala/Int#>>]*/(1), Set/*[Char<<scala/Char#>>]*/('a'))
+        |  val x/*: AB<<scala/collection/AbstractMap#>>[Int<<scala/Int#>>, String<<java/lang/String#>>]*/ = test(Set/*[Int<<scala/Int#>>]*/(1), Set/*[Char<<scala/Char#>>]*/('a'))
         |}
         |""".stripMargin,
     )