Skip to content

Commit

Permalink
Merge pull request #10645 from som-snytt/issue/12690-import-commit
Browse files Browse the repository at this point in the history
Track import selector of implicit info in search
  • Loading branch information
lrytz committed Jan 15, 2024
2 parents 6b27646 + 5532ca5 commit aa99433
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package backend.jvm
import java.util.concurrent.ConcurrentHashMap

import scala.collection.mutable
import scala.reflect.internal.util.{NoPosition, Position, StringContextStripMarginOps}
import scala.reflect.internal.util.{NoPosition, Position}
import scala.reflect.io.AbstractFile
import scala.tools.asm.ClassWriter
import scala.tools.asm.tree.ClassNode
Expand Down
2 changes: 0 additions & 2 deletions src/compiler/scala/tools/nsc/plugins/PluginDescription.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
package scala.tools.nsc
package plugins

import scala.reflect.internal.util.StringContextStripMarginOps

/** A description of a compiler plugin, suitable for serialization
* to XML for inclusion in the plugin's .jar file.
*
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import java.util.zip.Deflater
import scala.annotation.{elidable, nowarn}
import scala.collection.mutable
import scala.language.existentials
import scala.reflect.internal.util.{ StatisticsStatics, StringContextStripMarginOps }
import scala.reflect.internal.util.StatisticsStatics
import scala.tools.nsc.util.DefaultJarFactory
import scala.tools.util.PathResolver.Defaults
import scala.util.chaining._
Expand Down
39 changes: 25 additions & 14 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ trait Contexts { self: Analyzer =>
def makeImportContext(tree: Import): Context =
make(tree).tap { ctx =>
if (settings.warnUnusedImport && openMacros.isEmpty && !ctx.isRootImport)
allImportInfos(ctx.unit) ::= ((ctx.importOrNull, ctx.owner))
allImportInfos(ctx.unit) ::= ctx.importOrNull -> ctx.owner
}

/** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/
Expand Down Expand Up @@ -770,6 +770,7 @@ trait Contexts { self: Analyzer =>
def makeImplicit(reportAmbiguousErrors: Boolean) = {
val c = makeSilent(reportAmbiguousErrors)
c(ImplicitsEnabled | EnrichmentEnabled) = false
c(InImplicitSearch) = true
c
}

Expand Down Expand Up @@ -1095,9 +1096,9 @@ trait Contexts { self: Analyzer =>
f(imported)
}

private def collectImplicits(syms: Scope, pre: Type, imported: Boolean = false): List[ImplicitInfo] =
for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported)) yield
new ImplicitInfo(sym.name, pre, sym)
private def collectImplicits(syms: Scope, pre: Type): List[ImplicitInfo] =
for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported = false))
yield new ImplicitInfo(sym.name, pre, sym)

private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = if (isExcludedRootImport(imp)) List() else {
val qual = imp.qual
Expand All @@ -1112,12 +1113,13 @@ trait Contexts { self: Analyzer =>
// I haven't been able to boil that down the an automated test yet.
// Looking up implicit members in the package, rather than package object, here is at least
// consistent with what is done just below for named imports.
collectImplicits(qual.tpe.implicitMembers, pre, imported = true)
for (sym <- qual.tpe.implicitMembers.toList if isQualifyingImplicit(sym.name, sym, pre, imported = true))
yield new ImplicitInfo(sym.name, pre, sym, imp, sel)
case (sel @ ImportSelector(from, _, to, _)) :: sels1 =>
var impls = collect(sels1).filter(_.name != from)
if (!sel.isMask)
withQualifyingImplicitAlternatives(imp, to, pre) { sym =>
impls = new ImplicitInfo(to, pre, sym) :: impls
impls = new ImplicitInfo(to, pre, sym, imp, sel) :: impls
}
impls
}
Expand Down Expand Up @@ -1353,7 +1355,6 @@ trait Contexts { self: Analyzer =>
}
}


private def isReplImportWrapperImport(tree: Tree): Boolean = {
tree match {
case Import(expr, selector :: Nil) =>
Expand Down Expand Up @@ -1697,10 +1698,15 @@ trait Contexts { self: Analyzer =>
}

// the choice has been made
imp1.recordUsage(impSel, impSym)
if (lookupError == null) {
// implicit searcher decides when import was used
if (thisContext.contextMode.inNone(InImplicitSearch))
imp1.recordUsage(impSel, impSym)

// optimization: don't write out package prefixes
finish(duplicateAndResetPos.transform(imp1.qual), impSym)
// optimization: don't write out package prefixes
finish(duplicateAndResetPos.transform(imp1.qual), impSym)
}
else finish(EmptyTree, NoSymbol)
}
else finish(EmptyTree, NoSymbol)
}
Expand Down Expand Up @@ -1911,7 +1917,7 @@ trait Contexts { self: Analyzer =>
def qual: Tree = tree.symbol.info match {
case ImportType(expr) => expr
case ErrorType => tree setType NoType // fix for #2870
case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug
case bad => throw new FatalError(s"symbol ${tree.symbol} has bad type: ${bad}")
}

/** Is name imported explicitly, not via wildcard? */
Expand Down Expand Up @@ -1978,7 +1984,7 @@ trait Contexts { self: Analyzer =>
if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)"
else s"(expr=${tree.expr}, ${result.fullLocationString})"
}")
if (settings.warnUnusedImport && result != NoSymbol && pos != NoPosition)
if (settings.warnUnusedImport && !isRootImport && result != NoSymbol && pos != NoPosition)
allUsedSelectors(this) += sel
}

Expand Down Expand Up @@ -2101,12 +2107,17 @@ object ContextMode {
final val DiagUsedDefaults: ContextMode = 1 << 18

/** Are we currently typing the core or args of an annotation?
* When set, Java annotations may be instantiated directly.
*/
* When set, Java annotations may be instantiated directly.
*/
final val TypingAnnotation: ContextMode = 1 << 19

final val InPackageClauseName: ContextMode = 1 << 20

/** Context created with makeImplicit, for use in implicit search.
* Controls whether import elements are marked used on lookup.
*/
final val InImplicitSearch: ContextMode = 1 << 21

/** TODO: The "sticky modes" are EXPRmode, PATTERNmode, TYPEmode.
* To mimic the sticky mode behavior, when captain stickyfingers
* comes around we need to propagate those modes but forget the other
Expand Down
25 changes: 14 additions & 11 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,10 @@ trait Implicits extends splain.SplainData {
if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeImpl, subtypeStart)

if (result.isSuccess && settings.lintImplicitRecursion && result.tree.symbol != null) {
val s =
if (result.tree.symbol.isAccessor) result.tree.symbol.accessed
else if (result.tree.symbol.isModule) result.tree.symbol.moduleClass
else result.tree.symbol
val rts = result.tree.symbol
val s = if (rts.isAccessor) rts.accessed else if (rts.isModule) rts.moduleClass else rts
if (s != NoSymbol && context.owner.hasTransOwner(s))
context.warning(result.tree.pos, s"Implicit resolves to enclosing ${result.tree.symbol}", WarningCategory.WFlagSelfImplicit)
context.warning(result.tree.pos, s"Implicit resolves to enclosing $rts", WarningCategory.WFlagSelfImplicit)
}
implicitSearchContext.emitImplicitDictionary(result)
}
Expand Down Expand Up @@ -196,9 +194,8 @@ trait Implicits extends splain.SplainData {
* that were instantiated by the winning implicit.
* @param undetparams undetermined type parameters
*/
class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter, val undetparams: List[Symbol]) {
override def toString = "SearchResult(%s, %s)".format(tree,
if (subst.isEmpty) "" else subst)
class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter, val undetparams: List[Symbol], val implicitInfo: ImplicitInfo = null) {
override def toString = s"SearchResult($tree, ${if (subst.isEmpty) "" else subst})"

def isFailure = false
def isAmbiguousFailure = false
Expand All @@ -225,7 +222,7 @@ trait Implicits extends splain.SplainData {
* @param pre The prefix type of the implicit
* @param sym The symbol of the implicit
*/
class ImplicitInfo(val name: Name, val pre: Type, val sym: Symbol) {
class ImplicitInfo (val name: Name, val pre: Type, val sym: Symbol, val importInfo: ImportInfo = null, val importSelector: ImportSelector = null) {
private[this] var tpeCache: Type = null
private[this] var depolyCache: Type = null
private[this] var isErroneousCache: TriState = TriState.Unknown
Expand Down Expand Up @@ -1207,8 +1204,12 @@ trait Implicits extends splain.SplainData {
val savedInfos = undetParams.map(_.info)
val typedFirstPending = {
try {
if(isView || wildPtNotInstantiable || matchesPtInst(firstPending))
typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
if (isView || wildPtNotInstantiable || matchesPtInst(firstPending)) {
val res = typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
if (res.isFailure) res
else
new SearchResult(res.tree, res.subst, res.undetparams, firstPending)
}
else SearchFailure
} finally {
foreach2(undetParams, savedInfos){ (up, si) => up.setInfo(si) }
Expand Down Expand Up @@ -1268,6 +1269,8 @@ trait Implicits extends splain.SplainData {
s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type.${if (invalidImplicits.head.isModule) " An object can be written as a lazy val with an explicit type." else ""}"
)
}
else if (best.implicitInfo != null && best.implicitInfo.importInfo != null)
best.implicitInfo.importInfo.recordUsage(best.implicitInfo.importSelector, best.tree.symbol)

best
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import scala.annotation.{tailrec, unused}
import scala.collection.mutable
import mutable.ListBuffer
import scala.reflect.internal.{Chars, TypesStats}
import scala.reflect.internal.util.{CodeAction, FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps}
import scala.reflect.internal.util.{CodeAction, FreshNameCreator, ListOfNil, Statistics}
import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}, WarningCategory.Scala3Migration
import scala.util.chaining._
import symtab.Flags._
Expand Down
6 changes: 6 additions & 0 deletions test/files/neg/t12690.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
t12690.scala:10: warning: Unused import
import A._ // missing unused warning (order of imports is significant)
^
error: No warnings can be incurred under -Werror.
1 warning
1 error
12 changes: 12 additions & 0 deletions test/files/neg/t12690.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

//> using options -Werror -Wunused:imports

class X
class Y extends X
object A { implicit val x: X = new X }
object B { implicit val y: Y = new Y }
class C {
import B._
import A._ // missing unused warning (order of imports is significant)
def t = implicitly[X]
}
14 changes: 14 additions & 0 deletions test/files/neg/t12690b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
t12690b.scala:15: error: reference to v is ambiguous;
it is imported twice in the same scope by
import Y.v
and import X.v
v
^
t12690b.scala:11: warning: Unused import
import X.v
^
t12690b.scala:12: warning: Unused import
import Y.v
^
2 warnings
1 error
17 changes: 17 additions & 0 deletions test/files/neg/t12690b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

// scalac: -Wunused:imports -Werror

object X {
val v = 27
}
object Y {
val v = 42
}
object Main {
import X.v
import Y.v
def main(args: Array[String]) = println {
//"hello, world"
v
}
}
1 change: 1 addition & 0 deletions test/files/neg/warn-unused-imports/sample_1.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// scalac: -Werror -Wunused:imports

import language._

Expand Down

0 comments on commit aa99433

Please sign in to comment.