diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 2e870f447893..5aeafde3fc2f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -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 @@ -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 @@ -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 = @@ -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 `.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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/tests/warn/i15503a.scala b/tests/warn/i15503a.scala index 78234e6e777b..8fc97888b584 100644 --- a/tests/warn/i15503a.scala +++ b/tests/warn/i15503a.scala @@ -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. @@ -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: @@ -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: diff --git a/tests/warn/i24034/circe.scala b/tests/warn/i24034/circe.scala deleted file mode 100644 index 5d2e646f67b2..000000000000 --- a/tests/warn/i24034/circe.scala +++ /dev/null @@ -1,97 +0,0 @@ - -// circe.scala - -package io.circe - -import scala.compiletime.* -import scala.deriving.Mirror -import scala.quoted.* - -trait Encoder[A]: - def encode(value: A): String - -object Encoder: - trait AsObject[A] extends Encoder[A] - given Encoder[String] = ??? - -trait Configuration -object Configuration: - val default: Configuration = ??? - -object Default: - given [A]: Default[A] = ??? -trait Default[T] - -trait Codec[A] extends Encoder[A] -object Codec: - trait AsObject[A] extends Encoder.AsObject[A] - object AsObject: - inline final def derived[A](using inline A: Mirror.Of[A]): Codec.AsObject[A] = - ConfiguredCodec.derived[A](using Configuration.default) - inline final def derivedConfigured[A](using - inline A: Mirror.Of[A], - inline conf: Configuration - ): Codec.AsObject[A] = ConfiguredCodec.derived[A] - -trait ConfiguredEncoder[A](using conf: Configuration) extends Encoder.AsObject[A] -trait ConfiguredCodec[A] extends Codec.AsObject[A], ConfiguredEncoder[A] -object ConfiguredCodec: - inline final def derive[A: Mirror.Of](): ConfiguredCodec[A] = - derived[A](using Configuration.default) - inline final def derived[A](using - conf: Configuration, - inline mirror: Mirror.Of[A] - ): ConfiguredCodec[A] = ${ derivedImpl[A]('conf, 'mirror) } - def ofProduct[A]( - encoders: => List[Encoder[?]] - )(using Configuration, Default[A]): ConfiguredCodec[A] = ??? - def derivedImpl[A: Type](conf: Expr[Configuration], mirror: Expr[Mirror.Of[A]])(using - q: Quotes - ): Expr[ConfiguredCodec[A]] = { - mirror match { - case '{ - ${ _ }: Mirror.ProductOf[A] { - type MirroredLabel = l - type MirroredElemLabels = el - type MirroredElemTypes = et - } - } => - '{ - ConfiguredCodec.ofProduct[A]( - derivation.summonEncoders[et & Tuple](false)(using $conf) - )(using $conf) - } - } - } - -object derivation: - sealed trait Inliner[A, Arg]: - inline def apply[T](inline arg: Arg): A - - class EncoderNotDeriveSum(using config: Configuration) extends Inliner[Encoder[?], Unit]: - inline def apply[T](inline arg: Unit): Encoder[?] = summonEncoder[T](false) - - inline final def loopUnrolled[A, Arg, T <: Tuple](f: Inliner[A, Arg], inline arg: Arg): List[A] = - inline erasedValue[T] match - case _: EmptyTuple => Nil - case _: (h *: ts) => f[h](arg) :: loopUnrolled[A, Arg, ts](f, arg) - - inline def loopUnrolledNoArg[A, T <: Tuple](f: Inliner[A, Unit]): List[A] = - loopUnrolled[A, Unit, T](f, ()) - - inline final def summonEncoders[T <: Tuple](inline derivingForSum: Boolean)(using - Configuration - ): List[Encoder[?]] = - loopUnrolledNoArg[Encoder[?], T]( - inline if (derivingForSum) compiletime.error("unreachable") - else new EncoderNotDeriveSum - ) - - private[circe] inline final def summonEncoder[A]( - inline derivingForSum: Boolean - )(using Configuration): Encoder[A] = summonFrom { - case encodeA: Encoder[A] => encodeA - case _: Mirror.Of[A] => - inline if (derivingForSum) compiletime.error("unreachable") - else error("Failed to find an instance of Encoder[]") - } diff --git a/tests/warn/i24034/iron.scala b/tests/warn/i24034/iron.scala deleted file mode 100644 index 17cb26bf6408..000000000000 --- a/tests/warn/i24034/iron.scala +++ /dev/null @@ -1,30 +0,0 @@ - -// iron.scala - -package iron - -import io.circe.* - -opaque type IronType[A, C] <: A = A -type :|[A, C] = IronType[A, C] -trait Constraint[A, C] - -package constraint: - object string: - final class StartWith[V <: String] - object StartWith: - inline given [V <: String]: Constraint[String, StartWith[V]] = ??? - -object circe: - inline given XXX[A, B](using inline encoder: Encoder[A]): Encoder[A :| B] = ??? - inline given YYY[A, B](using inline encoder: Encoder[A], dummy: scala.util.NotGiven[DummyImplicit]): Encoder[A :| B] = ??? - // inline given [T](using mirror: RefinedTypeOps.Mirror[T], ev: Encoder[mirror.IronType]): Encoder[T] = ??? - -// trait RefinedTypeOps[A, C, T]: -// inline given RefinedTypeOps.Mirror[T] = ??? -// object RefinedTypeOps: -// trait Mirror[T]: -// type BaseType -// type ConstraintType -// type IronType = BaseType :| ConstraintType - diff --git a/tests/warn/i24034/test.scala b/tests/warn/i24034/test.scala deleted file mode 100644 index f87be8da2c36..000000000000 --- a/tests/warn/i24034/test.scala +++ /dev/null @@ -1,10 +0,0 @@ -//> using options -Wunused:all - -import io.circe.Codec - -import iron.:| -import iron.circe.given -import iron.constraint.string.StartWith - -case class Alien(name: String :| StartWith["alen"]) derives Codec.AsObject -