diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index a906d52ccd4e..d0776bae9719 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -28,6 +28,8 @@ class CompilationUnit protected (val source: SourceFile) { var tpdTree: tpd.Tree = tpd.EmptyTree + val eventLog: EventLog = new EventLog + /** Is this the compilation unit of a Java file */ def isJava: Boolean = source.file.name.endsWith(".java") diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..ec2c6abe4835 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckUnused.AfterTyper) :: // Check for unused elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files @@ -50,7 +50,7 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code - List(new CheckUnused.PostInlining) :: // Check for unused elements + List(new CheckUnused.AfterInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures diff --git a/compiler/src/dotty/tools/dotc/EventLog.scala b/compiler/src/dotty/tools/dotc/EventLog.scala new file mode 100644 index 000000000000..f82a5f4cdaa9 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/EventLog.scala @@ -0,0 +1,52 @@ +package dotty.tools.dotc + +import dotty.tools.dotc.core.SymDenotations.NoDenotation.exists +import scala.collection.mutable +import core.* +import ast.* +import Symbols.* +import EventLog.* +import Contexts.* +import dotty.tools.dotc.transform.CheckUnused + +class EventLog { + private val entries: mutable.ListBuffer[Entry] = mutable.ListBuffer() + private var state = 0 + + inline def appendEntry(entry: Entry)(using Context): Unit = { + state match { + case Disabled => () + case Enabled => entries += entry + case Uninitialized => + val enabled = ctx.base.allPhases.exists { phase => + logReaderPhasesNames.contains(phase.phaseName) && phase.isRunnable + } + state = + if enabled then + entries += entry + Enabled + else Disabled + } + } + + def toSeq: Seq[Entry] = entries.toSeq + +} + +object EventLog { + + private val logReaderPhasesNames = Set( + CheckUnused.phaseNamePrefix + CheckUnused.afterTyperSuffix, + CheckUnused.phaseNamePrefix + CheckUnused.afterInliningSuffix + ) + + private val Uninitialized: Int = 0 + private val Enabled = 1 + private val Disabled = 2 + + sealed trait Entry + case class MatchedImportSelector(importSym: Symbol, selector: untpd.ImportSelector) extends Entry + case class ImplicitIntroducedViaImport(importSym: Symbol, tree: Tree, ref: TermRef) extends Entry + case object FindRefFinished extends Entry + +} diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 8a7f2ff4e051..0687f64cd9a2 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -210,7 +210,7 @@ object Contexts { private var implicitsCache: ContextualImplicits | Null = null def implicits: ContextualImplicits = { if (implicitsCache == null) - implicitsCache = { + implicitsCache = {d val implicitRefs: List[ImplicitRef] = if (isClassDefContext) try owner.thisType.implicitMembers @@ -357,6 +357,8 @@ object Contexts { final def isAfterTyper = base.isAfterTyper(phase) final def isTyper = base.isTyper(phase) + final def eventLog: EventLog = compilationUnit.eventLog + /** Is this a context for the members of a class definition? */ def isClassDefContext: Boolean = owner.isClass && (owner ne outer.owner) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0eeec0f3cbec..bc00795c2b42 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -25,7 +25,11 @@ import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.EventLog import scala.math.Ordering +import scala.annotation.tailrec +import dotty.tools.dotc.util.Spans.Coord +import dotty.tools.dotc.util.Spans.Span /** @@ -55,6 +59,8 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def prepareForUnit(tree: tpd.Tree)(using Context): Context = val data = UnusedData() + data.usedImports = collectUsedImports + println("Used imports: " + data.usedImports) tree.getAttachment(_key).foreach(oldData => data.unusedAggregate = oldData.unusedAggregate ) @@ -62,6 +68,34 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke tree.putAttachment(_key, data) fresh + private def collectUsedImports(using Context): Map[Span, Set[ImportSelector]] = Map.empty + // @tailrec + // def collectUsedImportsFromEventLog( + // acc: Set[(Symbol, ImportSelector)], + // lastSelector: ImportSelector, + // eventLog: Seq[EventLog.Entry] + // ): Set[(Symbol, ImportSelector)] = + // eventLog match + // case EventLog.MatchedImportSelector(_, selector) :: tail => + // println("Selector") + // collectUsedImportsFromEventLog(acc, selector, tail) + // case EventLog.RefFoundInImport(importSym) :: tail => + // println("Adding") + // collectUsedImportsFromEventLog(acc + ((importSym, lastSelector)), lastSelector, tail) + // case Nil => + // acc + + // println("Log: " + ctx.eventLog.toSeq) + // val eventLog = ctx.eventLog.toSeq.dropWhile(!_.isInstanceOf[EventLog.MatchedImportSelector]) + // println("Reduced: " + eventLog) + // eventLog match + // case EventLog.MatchedImportSelector(_, selector) :: tail => + // collectUsedImportsFromEventLog(Set.empty, selector, tail) + // .groupBy(_._1.span).view.mapValues(_.map(_._2)).toMap + // case _ => Map.empty + + + // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = @@ -305,6 +339,8 @@ end CheckUnused object CheckUnused: val phaseNamePrefix: String = "checkUnused" val description: String = "check for unused elements" + val afterTyperSuffix: String = "AfterTyper" + val afterInliningSuffix: String = "AfterInlining" enum PhaseMode: case Aggregate @@ -326,9 +362,9 @@ object CheckUnused: */ private val _key = Property.StickyKey[UnusedData] - class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + class AfterTyper extends CheckUnused(PhaseMode.Aggregate, afterTyperSuffix, _key) - class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining", _key) + class AfterInlining extends CheckUnused(PhaseMode.Report, afterInliningSuffix, _key) /** * A stateful class gathering the infos on : @@ -343,6 +379,7 @@ object CheckUnused: /** The current scope during the tree traversal */ val currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) + var usedImports = Map[Span, Set[ImportSelector]]() var unusedAggregate: Option[UnusedResult] = None /* IMPORTS */ @@ -423,7 +460,8 @@ object CheckUnused: if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then impInScope.top += imp unusedImport ++= imp.selectors.filter { s => - !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) + !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) && + usedImports.get(imp.expr.span).forall(!_.contains(s)) } /** Register (or not) some `val` or `def` according to the context, scope and flags */ @@ -463,36 +501,6 @@ object CheckUnused: def popScope()(using Context): Unit = // used symbol in this scope val used = usedInScope.pop().toSet - // used imports in this scope - val imports = impInScope.pop() - val kept = used.filterNot { (sym, isAccessible, optName, isDerived) => - // keep the symbol for outer scope, if it matches **no** import - // This is the first matching wildcard selector - var selWildCard: Option[ImportSelector] = None - - val matchedExplicitImport = imports.exists { imp => - sym.isInImport(imp, isAccessible, optName, isDerived) match - case None => false - case optSel@Some(sel) if sel.isWildcard => - if selWildCard.isEmpty then selWildCard = optSel - // We keep wildcard symbol for the end as they have the least precedence - false - case Some(sel) => - unusedImport -= sel - true - } - if !matchedExplicitImport && selWildCard.isDefined then - unusedImport -= selWildCard.get - true // a matching import exists so the symbol won't be kept for outer scope - else - matchedExplicitImport - } - - // if there's an outer scope - if usedInScope.nonEmpty then - // we keep the symbols not referencing an import in this scope - // as it can be the only reference to an outer import - usedInScope.top ++= kept // register usage in this scope for other warnings at the end of the phase usedDef ++= used.map(_._1) // retrieve previous scope type @@ -654,28 +662,6 @@ object CheckUnused: && c.owner.thisType.member(sym.name).alternatives.contains(sym) } - /** Given an import and accessibility, return selector that matches import<->symbol */ - private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = - val tpd.Import(qual, sels) = imp - val dealiasedSym = dealias(sym) - val simpleSelections = qual.tpe.member(sym.name).alternatives - val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) - val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val selectionsToDealias = typeSelections ::: termSelections - val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) - def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { - case (sel, sym) if dealias(sym) == dealiasedSym => sel - }.headOption else None - def givenSelector = if sym.is(Given) || sym.is(Implicit) - then sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info) - else None - def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then - selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given) - else - None - private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) = sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index ac6cc701be87..7428768cc4e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -44,7 +44,7 @@ object Implicits: /** An implicit definition `implicitRef` that is visible under a different name, `alias`. * Gets generated if an implicit ref is imported via a renaming import. */ - class RenamedImplicitRef(val underlyingRef: TermRef, val alias: TermName) extends ImplicitRef { + class RenamedImplicitRef(val underlyingRef: TermRef, val alias: TermName, val fromImport: Option[ImportInfo] = None) extends ImplicitRef { def implicitName(using Context): TermName = alias } @@ -422,7 +422,7 @@ object Implicits: * @param isExtension Whether the result is an extension method application * @param tstate The typer state to be committed if this alternative is chosen */ - case class SearchSuccess(tree: Tree, ref: TermRef, level: Int, isExtension: Boolean = false)(val tstate: TyperState, val gstate: GadtConstraint) + case class SearchSuccess(tree: Tree, ref: TermRef, level: Int, isExtension: Boolean = false, fromCandidate: Option[Candidate] = None)(val tstate: TyperState, val gstate: GadtConstraint) extends SearchResult with RefAndLevel with Showable /** A failed search */ @@ -877,7 +877,7 @@ trait Implicits: val inferred = inferImplicit(adjust(to), from, from.span) inferred match { - case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) => + case SearchSuccess(_, ref, _, false, _) if isOldStyleFunctionConversion(ref.underlying) => report.migrationWarning( em"The conversion ${ref} will not be applied implicitly here in Scala 3 because only implicit methods and instances of Conversion class will continue to work as implicit views.", from @@ -1207,7 +1207,7 @@ trait Implicits: ctx.reporter.removeBufferedMessages res else - SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) + SearchSuccess(adapted, ref, cand.level, cand.isExtension, Some(cand))(ctx.typerState, ctx.gadt) } /** An implicit search; parameters as in `inferImplicit` */ @@ -1586,11 +1586,15 @@ trait Implicits: // effectively in a more inner context than any other definition provided by // explicit definitions. Consequently these terms have the highest priority and no // other candidates need to be considered. - recursiveRef match + val result = recursiveRef match case ref: TermRef => SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt) case _ => searchImplicit(contextual = true) + result.fromCandidate.foreach { cand => + ctx.eventLog.appendLog(cand.) + } + result end bestImplicit def implicitScope(tp: Type): OfTypeImplicits = ctx.run.nn.implicitScope(tp) diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index ba05cba229ae..508eb312eab9 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -143,7 +143,7 @@ class ImportInfo(symf: Context ?=> Symbol, if isEligible && ref.denot.asSingleDenotation.matchesImportBound(bound) then ref :: Nil else Nil else if renamed == ref.name then ref :: Nil - else RenamedImplicitRef(ref, renamed) :: Nil + else RenamedImplicitRef(ref, renamed, Some(this)) :: Nil } else for @@ -153,7 +153,7 @@ class ImportInfo(symf: Context ?=> Symbol, val original = reverseMapping(renamed).nn val ref = TermRef(pre, original, denot) if renamed == original then ref - else RenamedImplicitRef(ref, renamed) + else RenamedImplicitRef(ref, renamed, Some(this)) /** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden. * Note: this computation needs to work even for un-initialized import infos, and diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6accc49b2766..b7ec3ddc6887 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -52,14 +52,10 @@ import Nullables._ import NullOpsDecorator._ import cc.CheckCaptures import config.Config -import java.io.File +import EventLog._ import scala.annotation.constructorOnly import dotty.tools.dotc.rewrites.Rewrites -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.nio.file.StandardOpenOption object Typer { @@ -72,32 +68,6 @@ object Typer { def isImportPrec = this == NamedImport || this == WildImport } - - sealed trait TypeResolution: - def result: Type - def exists = result.exists - def isError(using Context) = result.isError - def prec: BindingPrec - - import BindingPrec._ - case object NoTypeResolution extends TypeResolution: - def result = NoType - def prec = NothingBound - - case class NamedImportedType(result: Type, from: ImportInfo) extends TypeResolution: - def prec = NamedImport - - case class WildImportedType(result: Type, from: ImportInfo) extends TypeResolution: - def prec = WildImport - - case class DefinitionType(result: Type) extends TypeResolution: - def prec = Definition - - case class PackageClauseType(result: Type) extends TypeResolution: - def prec = PackageClause - - - /** Assert tree has a position, unless it is empty or a typed splice */ def assertPositioned(tree: untpd.Tree)(using Context): Unit = if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) @@ -197,22 +167,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * a reference for `m` is searched. `null` in all other situations. */ def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos, - altImports: mutable.ListBuffer[TermRef] | Null = null)(using Context): Type = - findRefResolution(name, pt, required, excluded, pos, altImports).result - - /** Find the type of an identifier with given `name` in given context `ctx`. - * @param name the name of the identifier - * @param pt the expected type - * @param required flags the result's symbol must have - * @param excluded flags the result's symbol must not have - * @param pos indicates position to use for error reporting - * @param altImports a ListBuffer in which alternative imported references are - * collected in case `findRef` is called from an expansion of - * an extension method, i.e. when `e.m` is expanded to `m(e)` and - * a reference for `m` is searched. `null` in all other situations. - */ - def findRefResolution(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos, - altImports: mutable.ListBuffer[TermRef] | Null = null)(using Context): TypeResolution = { + altImports: mutable.ListBuffer[TermRef] | Null = null)(using Context): Type = { val refctx = ctx val noImports = ctx.mode.is(Mode.InPackageClauseName) def suppressErrors = excluded.is(ConstructorProxy) @@ -257,7 +212,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * @param prevCtx The context of the previous denotation, * or else `NoContext` if nothing was found yet. */ - def findRefRecur(previous: TypeResolution, prevCtx: Context)(using Context): TypeResolution = { + def findRefRecur(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = { import BindingPrec._ /** Check that any previously found result from an inner context @@ -269,18 +224,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * previous and new contexts do not have the same scope, we select * the previous (inner) definition. This models what scalac does. */ - def checkNewOrShadowed(found: TypeResolution, scala2pkg: Boolean = false)(using Context): TypeResolution = - if !previous.exists || TypeComparer.isSameRef(previous.result, found.result) then + def checkNewOrShadowed(found: Type, newPrec: BindingPrec, scala2pkg: Boolean = false)(using Context): Type = + if !previous.exists || TypeComparer.isSameRef(previous, found) then found else if (prevCtx.scope eq ctx.scope) - && (found.prec == Definition || found.prec == NamedImport && previous.prec == WildImport) + && (newPrec == Definition || newPrec == NamedImport && prevPrec == WildImport) then // special cases: definitions beat imports, and named imports beat // wildcard imports, provided both are in contexts with same scope found else if !scala2pkg && !previous.isError && !found.isError then - fail(AmbiguousReference(name, found.prec, previous.prec, prevCtx)) + fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx)) previous /** Assemble and check alternatives to an imported reference. This implies: @@ -299,10 +254,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * @param prevCtx the context in which the reference was found * @param using_Context the outer context of `precCtx` */ - def checkImportAlternatives(previous: TypeResolution, prevCtx: Context)(using Context): TypeResolution = - + def checkImportAlternatives(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = def addAltImport(altImp: TermRef) = - if !TypeComparer.isSameRef(previous.result, altImp) + if !TypeComparer.isSameRef(previous, altImp) && !altImports.uncheckedNN.exists(TypeComparer.isSameRef(_, altImp)) then altImports.uncheckedNN += altImp @@ -311,22 +265,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val curImport = ctx.importInfo.uncheckedNN namedImportRef(curImport) match case altImp: TermRef => - if previous.prec == WildImport then + if prevPrec == WildImport then // Discard all previously found references and continue with `altImp` altImports.clear() - checkImportAlternatives(NamedImportedType(altImp, curImport), ctx)(using ctx.outer) + checkImportAlternatives(altImp, NamedImport, ctx)(using ctx.outer) else addAltImport(altImp) - checkImportAlternatives(previous, prevCtx)(using ctx.outer) + checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer) case _ => - if previous.prec == WildImport then + if prevPrec == WildImport then wildImportRef(curImport) match case altImp: TermRef => addAltImport(altImp) case _ => - checkImportAlternatives(previous, prevCtx)(using ctx.outer) + checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer) else - val found = findRefRecur(previous, prevCtx) - if found eq previous then checkNewOrShadowed(previous)(using prevCtx) + val found = findRefRecur(previous, prevPrec, prevCtx) + if found eq previous then checkNewOrShadowed(found, prevPrec)(using prevCtx) else found end checkImportAlternatives @@ -373,6 +327,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val other = recur(selectors.tail) if other.exists && found.exists && found != other then fail(em"reference to `$name` is ambiguous; it is imported twice") + ctx.eventLog.appendEntry(MatchedImportSelector(imp.importSym, selector)) found if selector.rename == termName && !selector.isUnimport then @@ -415,12 +370,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Would import of kind `prec` be not shadowed by a nested higher-precedence definition? */ def isPossibleImport(prec: BindingPrec)(using Context) = !noImports && - (previous.prec.ordinal < prec.ordinal || previous.prec == prec && (prevCtx.scope eq ctx.scope)) + (prevPrec.ordinal < prec.ordinal || prevPrec == prec && (prevCtx.scope eq ctx.scope)) - @tailrec def loop(lastCtx: Context)(using Context): TypeResolution = + @tailrec def loop(lastCtx: Context)(using Context): Type = if (ctx.scope eq EmptyScope) previous else { - var result: TypeResolution = NoTypeResolution + var result: Type = NoType val curOwner = ctx.owner /** Is curOwner a package object that should be skipped? @@ -515,7 +470,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer effectiveOwner.thisType.select(name, defDenot) } if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then - result = checkNewOrShadowed(DefinitionType(found)) // no need to go further out, we found highest prec entry + result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry found match case found: NamedType if curOwner.isClass && isInherited(found.denot) && !ctx.compilationUnit.isJava => @@ -523,11 +478,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => else if migrateTo3 && !foundUnderScala2.exists then - foundUnderScala2 = checkNewOrShadowed(DefinitionType(found), scala2pkg = true).result + foundUnderScala2 = checkNewOrShadowed(found, Definition, scala2pkg = true) if (defDenot.symbol.is(Package)) - result = checkNewOrShadowed(PackageClauseType(previous.result orElse found)) - else if (previous.prec.ordinal < PackageClause.ordinal) - result = findRefRecur(PackageClauseType(found), ctx)(using ctx.outer) + result = checkNewOrShadowed(previous orElse found, PackageClause) + else if (prevPrec.ordinal < PackageClause.ordinal) + result = findRefRecur(found, PackageClause, ctx)(using ctx.outer) } if result.exists then result @@ -539,14 +494,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (curOwner.is(Package) && curImport != null && curImport.isRootImport && previous.exists) previous // no more conflicts possible in this case else if (isPossibleImport(NamedImport) && (curImport nen outer.importInfo)) { - val curImportNN = curImport.uncheckedNN - val namedImp = namedImportRef(curImportNN) + val namedImp = namedImportRef(curImport.uncheckedNN) if (namedImp.exists) - checkImportAlternatives(NamedImportedType(namedImp, curImportNN), ctx)(using outer) + checkImportAlternatives(namedImp, NamedImport, ctx)(using outer) else if (isPossibleImport(WildImport) && !curImport.nn.importSym.isCompleting) { - val wildImp = wildImportRef(curImportNN) + val wildImp = wildImportRef(curImport.uncheckedNN) if (wildImp.exists) - checkImportAlternatives(WildImportedType(wildImp, curImportNN), ctx)(using outer) + checkImportAlternatives(wildImp, WildImport, ctx)(using outer) else { updateUnimported() loop(ctx)(using outer) @@ -565,7 +519,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer loop(NoContext) } - findRefRecur(NoTypeResolution, NoContext) + val result = findRefRecur(NoType, BindingPrec.NothingBound, NoContext) + ctx.eventLog.appendEntry(FindRefFinished) + result } /** If `tree`'s type is a `TermRef` identified by flow typing to be non-null, then @@ -620,20 +576,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer checkLegalValue(tree2, pt) return tree2 - val (rawType, directlyImported) = + val rawType = val saved1 = unimported val saved2 = foundUnderScala2 unimported = Set.empty foundUnderScala2 = NoType try - val found = findRefResolution(name, pt, EmptyFlags, EmptyFlags, tree.srcPos) - if foundUnderScala2.exists && !(foundUnderScala2 =:= found.result) then + val found = findRef(name, pt, EmptyFlags, EmptyFlags, tree.srcPos) + if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then report.migrationWarning( em"""Name resolution will change. | currently selected : $foundUnderScala2 - | in the future, without -source 3.0-migration: ${found.result}""", tree.srcPos) - (foundUnderScala2, false) - else (found.result, found.prec == BindingPrec.NamedImport) + | in the future, without -source 3.0-migration: $found""", tree.srcPos) + foundUnderScala2 + else found finally unimported = saved1 foundUnderScala2 = saved2 @@ -665,10 +621,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => ownType - def setType(ownType: Type, directlyImported: Boolean): Tree = + def setType(ownType: Type): Tree = val checkedType = checkNotShadowed(ownType) val tree1 = checkedType match - case checkedType: NamedType if !prefixIsElidable(checkedType) && !directlyImported => + case checkedType: NamedType if !prefixIsElidable(checkedType) => ref(checkedType).withSpan(tree.span) case _ => tree.withType(checkedType) @@ -696,13 +652,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val selection = untpd.cpy.Select(tree)(qualifier, name) typed(selection, pt) else if rawType.exists then - setType(ensureAccessible(rawType, superAccess = false, tree.srcPos), directlyImported) + setType(ensureAccessible(rawType, superAccess = false, tree.srcPos)) else if name == nme._scope then // gross hack to support current xml literals. // awaiting a better implicits based solution for library-supported xml ref(defn.XMLTopScopeModule.termRef) else if name.toTermName == nme.ERROR then - setType(UnspecifiedErrorType, false) + setType(UnspecifiedErrorType) else if ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor && ctx.owner.owner.unforcedDecls.lookup(tree.name).exists then // we are in the arguments of a this(...) constructor call