Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 41 additions & 39 deletions compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package dotty.tools.dotc
package transform

import ast.*, desugar.{ForArtifact, PatternVar}, tpd.*, untpd.ImportSelector
import config.ScalaSettings
import core.*, Contexts.*, Flags.*
import Names.{Name, SimpleName, DerivedName, TermName, termName}
import NameKinds.{BodyRetainerName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName}
import NameOps.{isAnonymousFunctionName, isReplWrapperName, setterName}
import Scopes.newScope
import StdNames.nme
import Symbols.{ClassSymbol, NoSymbol, Symbol, defn, isDeprecated, requiredClass, requiredModule}
import Types.*
import reporting.{CodeAction, UnusedSymbol}
import rewrites.Rewrites

import MegaPhase.MiniPhase
import typer.{ImportInfo, Typer}
import typer.Deriving.OriginalTypeClass
import typer.Implicits.{ContextualImplicits, RenamedImplicitRef}
import util.{Property, Spans, SrcPos}, Spans.Span
import util.Chars.{isLineBreakChar, isWhitespace}
import util.chaining.*
package dotty.tools.dotc.transform

import dotty.tools.dotc.ast.desugar.{ForArtifact, PatternVar}
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.ast.untpd, untpd.ImportSelector
import dotty.tools.dotc.config.ScalaSettings
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, termName}
import dotty.tools.dotc.core.NameOps.{isAnonymousFunctionName, isReplWrapperName, setterName}
import dotty.tools.dotc.core.NameKinds.{
BodyRetainerName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName}
import dotty.tools.dotc.core.StdNames.nme
import dotty.tools.dotc.core.Symbols.{ClassSymbol, NoSymbol, Symbol, defn, isDeprecated, requiredClass, requiredModule}
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.report
import dotty.tools.dotc.reporting.{CodeAction, UnusedSymbol}
import dotty.tools.dotc.rewrites.Rewrites
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
import dotty.tools.dotc.typer.{ImportInfo, Typer}
import dotty.tools.dotc.typer.Deriving.OriginalTypeClass
import dotty.tools.dotc.util.{Property, Spans, SrcPos}, Spans.Span
import dotty.tools.dotc.util.Chars.{isLineBreakChar, isWhitespace}
import dotty.tools.dotc.util.chaining.*

import java.util.IdentityHashMap

Expand Down Expand Up @@ -55,16 +56,17 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
if tree.symbol.exists then
// if in an inline expansion, resolve at summonInline (synthetic pos) or in an enclosing call site
val resolving =
tree.srcPos.isUserCode
|| tree.srcPos.isZeroExtentSynthetic // take as summonInline
if !ignoreTree(tree) then
refInfos.inlined.isEmpty
|| tree.srcPos.isZeroExtentSynthetic
|| refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos))
if resolving && !ignoreTree(tree) then
def loopOverPrefixes(prefix: Type, depth: Int): Unit =
if depth < 10 && prefix.exists && !prefix.classSymbol.isEffectiveRoot then
resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix, imports = resolving)
resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix)
loopOverPrefixes(prefix.normalizedPrefix, depth + 1)
if tree.srcPos.isZeroExtentSynthetic then
loopOverPrefixes(tree.typeOpt.normalizedPrefix, depth = 0)
resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject, imports = resolving)
resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
else if tree.hasType then
resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
refInfos.isAssignment = false
Expand Down Expand Up @@ -140,8 +142,14 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
case _ =>
tree

override def prepareForInlined(tree: Inlined)(using Context): Context =
refInfos.inlined.push(tree.call.srcPos)
ctx
override def transformInlined(tree: Inlined)(using Context): tree.type =
transformAllDeep(tree.call)
//transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs
val _ = refInfos.inlined.pop()
if !tree.call.isEmpty && phaseMode.eq(PhaseMode.Aggregate) then
transformAllDeep(tree.call)
tree

override def prepareForBind(tree: Bind)(using Context): Context =
Expand Down Expand Up @@ -289,11 +297,8 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
* e.g., in `scala.Int`, `scala` is in scope for typer, but here we reverse-engineer the attribution.
* For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`.
* For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence.
*
* The `imports` flag is whether an identifier can mark an import as used: the flag is false
* for inlined code, except for `summonInline` (and related constructs) which are resolved at inlining.
*/
def resolveUsage(sym0: Symbol, name: Name, prefix: Type, imports: Boolean = true)(using Context): Unit =
def resolveUsage(sym0: Symbol, name: Name, prefix: Type)(using Context): Unit =
import PrecedenceLevels.*
val sym = sym0.userSymbol

Expand Down Expand Up @@ -387,7 +392,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
// record usage and possibly an import
if !enclosed then
refInfos.addRef(sym)
if imports && candidate != NoContext && candidate.isImportContext && importer != null then
if candidate != NoContext && candidate.isImportContext && importer != null then
refInfos.sels.put(importer, ())
end resolveUsage
end CheckUnused
Expand Down Expand Up @@ -427,7 +432,7 @@ object CheckUnused:
val nowarn = mutable.Set.empty[Symbol] // marked @nowarn
val imps = new IdentityHashMap[Import, Unit] // imports
val sels = new IdentityHashMap[ImportSelector, Unit] // matched selectors
def register(tree: Tree)(using Context): Unit = if tree.srcPos.isUserCode then
def register(tree: Tree)(using Context): Unit = if inlined.isEmpty then
tree match
case imp: Import =>
if inliners == 0
Expand Down Expand Up @@ -457,6 +462,7 @@ object CheckUnused:
if tree.symbol ne NoSymbol then
defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path

val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code (expansions)
var inliners = 0 // depth of inline def (not inlined yet)

// instead of refs.addOne, use addRef to distinguish a read from a write to var
Expand Down Expand Up @@ -998,10 +1004,6 @@ object CheckUnused:
extension (pos: SrcPos)
def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.isZeroExtent
def isSynthetic: Boolean = pos.span.isSynthetic && pos.span.exists
def isUserCode(using Context): Boolean =
val inlineds = enclosingInlineds // per current context
inlineds.isEmpty
|| inlineds.last.srcPos.sourcePos.contains(pos.sourcePos)

extension [A <: AnyRef](arr: Array[A])
// returns `until` if not satisfied
Expand Down
13 changes: 7 additions & 6 deletions tests/warn/i15503a.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ object FooGiven:

val foo = summon[Int]

object SomeGivenImports:
given Int = 0
given String = "foo"

/**
* Import used as type name are considered
* as used.
Expand Down Expand Up @@ -73,7 +69,7 @@ object InlineChecks:
object InlinedBar:
import collection.mutable.Set // warn (don't be fooled by inline expansion)
import collection.mutable.Map // warn
val a = InlineFoo.getSet // expansion is attributed mutable.Set.apply(1)
val a = InlineFoo.getSet

object MacroChecks:
object StringInterpol:
Expand All @@ -95,7 +91,12 @@ object IgnoreExclusion:
def check =
val a = Set(1)
val b = Map(1 -> 2)
def c = Seq(42)
/**
* Some given values for the test
*/
object SomeGivenImports:
given Int = 0
given String = "foo"

/* BEGIN : Check on packages*/
package nestedpackageimport:
Expand Down
97 changes: 0 additions & 97 deletions tests/warn/i24034/circe.scala

This file was deleted.

30 changes: 0 additions & 30 deletions tests/warn/i24034/iron.scala

This file was deleted.

10 changes: 0 additions & 10 deletions tests/warn/i24034/test.scala

This file was deleted.

Loading