Skip to content

Backport "In selector check, prefix of reference must match import qualifier" to 3.3 LTS #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 21, 2025
Merged
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
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {

methSymbol = dd.symbol
jMethodName = methSymbol.javaSimpleName
returnType = asmMethodType(dd.symbol).returnType
returnType = asmMethodType(methSymbol).returnType
isMethSymStaticCtor = methSymbol.isStaticConstructor

resetMethodBookkeeping(dd)
Expand Down Expand Up @@ -915,7 +915,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
for (p <- params) { emitLocalVarScope(p.symbol, veryFirstProgramPoint, onePastLastProgramPoint, force = true) }
}

if (isMethSymStaticCtor) { appendToStaticCtor(dd) }
if (isMethSymStaticCtor) { appendToStaticCtor() }
} // end of emitNormalMethodBody()

lineNumber(rhs)
Expand All @@ -936,7 +936,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
*
* TODO document, explain interplay with `fabricateStaticInitAndroid()`
*/
private def appendToStaticCtor(dd: DefDef): Unit = {
private def appendToStaticCtor(): Unit = {

def insertBefore(
location: asm.tree.AbstractInsnNode,
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/jvm/ClassfileWriters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import java.util.zip.{CRC32, Deflater, ZipEntry, ZipOutputStream}

import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Decorators.em
import dotty.tools.dotc.util.chaining.*
import dotty.tools.io.{AbstractFile, PlainFile, VirtualFile}
import dotty.tools.io.PlainFile.toPlainFile
import BTypes.InternalName
import scala.util.chaining._
import dotty.tools.io.JarArchive

import scala.language.unsafeNulls
Expand Down Expand Up @@ -180,7 +180,7 @@ class ClassfileWriters(frontendAccess: PostProcessorFrontendAccess) {
// important detail here, even on Windows, Zinc expects the separator within the jar
// to be the system default, (even if in the actual jar file the entry always uses '/').
// see https://github.com/sbt/zinc/blob/dcddc1f9cfe542d738582c43f4840e17c053ce81/internal/compiler-bridge/src/main/scala/xsbt/JarUtils.scala#L47
val pathInJar =
val pathInJar =
if File.separatorChar == '/' then relativePath
else relativePath.replace('/', File.separatorChar)
PlainFile.toPlainFile(Paths.get(s"${file.absolutePath}!$pathInJar"))
Expand Down
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import cc.CheckCaptures
import parsing.Parser
import Phases.Phase
import transform.*
import dotty.tools.backend
import backend.jvm.{CollectSuperCalls, GenBCode}
import localopt.StringInterpolatorOpt

Expand All @@ -35,8 +34,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 CheckShadowing) :: // Check shadowing elements
List(CheckUnused.PostTyper(), CheckShadowing()) :: // Check for unused, shadowed elements
List(new YCheckPositions) :: // YCheck positions
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
List(new semanticdb.ExtractSemanticDB.ExtractSemanticInfo) :: // Extract info into .semanticdb files
Expand All @@ -51,10 +49,10 @@ 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 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
List(new CheckUnused.PostInlining) :: // Check for unused elements
Nil

/** Phases dealing with the transformation from pickled trees to backend trees */
Expand Down
14 changes: 12 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ object desugar {
*/
val UntupledParam: Property.Key[Unit] = Property.StickyKey()

/** An attachment key to indicate that a ValDef originated from a pattern.
*/
val PatternVar: Property.Key[Unit] = Property.StickyKey()

/** An attachment key for Trees originating in for-comprehension, such as tupling of assignments.
*/
val ForArtifact: Property.Key[Unit] = Property.StickyKey()

/** What static check should be applied to a Match? */
enum MatchCheck {
case None, Exhaustive, IrrefutablePatDef, IrrefutableGenFrom
Expand Down Expand Up @@ -1221,7 +1229,7 @@ object desugar {
val matchExpr =
if (tupleOptimizable) rhs
else
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ()))
Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil)
vars match {
case Nil if !mods.is(Lazy) =>
Expand Down Expand Up @@ -1251,6 +1259,7 @@ object desugar {
ValDef(named.name.asTermName, tpt, selector(n))
.withMods(mods)
.withSpan(named.span)
.withAttachment(PatternVar, ())
)
flatTree(firstDef :: restDefs)
}
Expand Down Expand Up @@ -1542,6 +1551,7 @@ object desugar {
val vdef = ValDef(named.name.asTermName, tpt, rhs)
.withMods(mods)
.withSpan(original.span.withPoint(named.span.start))
.withAttachment(PatternVar, ())
val mayNeedSetter = valDef(vdef)
mayNeedSetter
}
Expand Down Expand Up @@ -1733,7 +1743,7 @@ object desugar {
case _ => Modifiers()
makePatDef(valeq, mods, defpat, rhs)
}
val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids)))
val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ())))
val allpats = gen.pat :: pats
val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore)
makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Trees {

/** Property key for backquoted identifiers and definitions */
val Backquoted: Property.StickyKey[Unit] = Property.StickyKey()

val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey()

/** Trees take a parameter indicating what the type of their `tpe` field
Expand Down Expand Up @@ -765,6 +765,7 @@ object Trees {
override def isEmpty: Boolean = !hasType
override def toString: String =
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
def isInferred = false
}

/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
Expand All @@ -787,6 +788,7 @@ object Trees {
*/
class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]:
type ThisTree[+T <: Untyped] <: InferredTypeTree[T]
override def isInferred = true

/** ref.type */
case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/CliCommand.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Settings.*
import core.Contexts.*
import printing.Highlighting

import scala.util.chaining.given
import dotty.tools.dotc.util.chaining.*
import scala.PartialFunction.cond

trait CliCommand:
Expand Down
22 changes: 6 additions & 16 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory}
import dotty.tools.backend.jvm.BackendUtils.classfileVersionMap
import Setting.ChoiceWithHelp

import scala.util.chaining.*
import dotty.tools.dotc.util.chaining.*

import java.util.zip.Deflater

Expand Down Expand Up @@ -179,28 +179,20 @@ private sealed trait WarningSettings:
choices = List(
ChoiceWithHelp("nowarn", ""),
ChoiceWithHelp("all", ""),
ChoiceWithHelp(
name = "imports",
description = "Warn if an import selector is not referenced.\n" +
"NOTE : overrided by -Wunused:strict-no-implicit-warn"),
ChoiceWithHelp("imports", "Warn if an import selector is not referenced."),
ChoiceWithHelp("privates", "Warn if a private member is unused"),
ChoiceWithHelp("locals", "Warn if a local definition is unused"),
ChoiceWithHelp("explicits", "Warn if an explicit parameter is unused"),
ChoiceWithHelp("implicits", "Warn if an implicit parameter is unused"),
ChoiceWithHelp("params", "Enable -Wunused:explicits,implicits"),
ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"),
//ChoiceWithHelp("inlined", "Apply -Wunused to inlined expansions"), // TODO
ChoiceWithHelp("linted", "Enable -Wunused:imports,privates,locals,implicits"),
ChoiceWithHelp(
name = "strict-no-implicit-warn",
description = "Same as -Wunused:import, only for imports of explicit named members.\n" +
"NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all"
),
// ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"),
ChoiceWithHelp(
name = "unsafe-warn-patvars",
description = "(UNSAFE) Warn if a variable bound in a pattern is unused.\n" +
"This warning can generate false positive, as warning cannot be\n" +
"suppressed yet."
)
),
default = Nil
)
Expand All @@ -212,7 +204,6 @@ private sealed trait WarningSettings:
// Is any choice set for -Wunused?
def any(using Context): Boolean = Wall.value || Wunused.value.nonEmpty

// overrided by strict-no-implicit-warn
def imports(using Context) =
(allOr("imports") || allOr("linted")) && !(strictNoImplicitWarn)
def locals(using Context) =
Expand All @@ -226,9 +217,8 @@ private sealed trait WarningSettings:
def params(using Context) = allOr("params")
def privates(using Context) =
allOr("privates") || allOr("linted")
def patvars(using Context) =
isChoiceSet("unsafe-warn-patvars") // not with "all"
// allOr("patvars") // todo : rename once fixed
def patvars(using Context) = allOr("patvars")
def inlined(using Context) = isChoiceSet("inlined")
def linted(using Context) =
allOr("linted")
def strictNoImplicitWarn(using Context) =
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@ class Definitions {
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass

@tu lazy val SameTypeClass: ClassSymbol = requiredClass("scala.=:=")
@tu lazy val SameType_refl: Symbol = SameTypeClass.companionModule.requiredMethod(nme.refl)
@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
@tu lazy val SubType_refl: Symbol = SubTypeClass.companionModule.requiredMethod(nme.refl)

Expand Down Expand Up @@ -816,6 +818,7 @@ class Definitions {
@tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr")

@tu lazy val QuotesClass: ClassSymbol = requiredClass("scala.quoted.Quotes")
@tu lazy val Quotes_reflectModule: Symbol = QuotesClass.requiredClass("reflectModule")
@tu lazy val Quotes_reflect: Symbol = QuotesClass.requiredValue("reflect")
@tu lazy val Quotes_reflect_asTerm: Symbol = Quotes_reflect.requiredMethod("asTerm")
@tu lazy val Quotes_reflect_Apply: Symbol = Quotes_reflect.requiredValue("Apply")
Expand Down Expand Up @@ -941,6 +944,7 @@ class Definitions {
def NonEmptyTupleClass(using Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass
lazy val NonEmptyTuple_tail: Symbol = NonEmptyTupleClass.requiredMethod("tail")
@tu lazy val PairClass: ClassSymbol = requiredClass("scala.*:")
@tu lazy val PairClass_unapply: Symbol = PairClass.companionModule.requiredMethod("unapply")

@tu lazy val TupleXXLClass: ClassSymbol = requiredClass("scala.runtime.TupleXXL")
def TupleXXLModule(using Context): Symbol = TupleXXLClass.companionModule
Expand Down Expand Up @@ -1031,6 +1035,7 @@ class Definitions {
@tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures")
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
@tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns")
@tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature")
@tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter")
@tu lazy val BeanSetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanSetter")
@tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field")
Expand Down
15 changes: 8 additions & 7 deletions compiler/src/dotty/tools/dotc/report.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package dotty.tools.dotc

import reporting.*
import Diagnostic.*
import util.{SourcePosition, NoSourcePosition, SrcPos}
import core.*
import Contexts.*, Flags.*, Symbols.*, Decorators.*
import config.SourceVersion
import ast.*
import config.Feature.sourceVersion
import core.*, Contexts.*, Flags.*, Symbols.*, Decorators.*
import config.Feature.sourceVersion, config.SourceVersion
import reporting.*, Diagnostic.*
import util.{SourcePosition, NoSourcePosition, SrcPos}

import java.lang.System.currentTimeMillis

object report:
Expand Down Expand Up @@ -54,6 +52,9 @@ object report:
else issueWarning(new FeatureWarning(msg, pos.sourcePos))
end featureWarning

def warning(msg: Message, pos: SrcPos, origin: String)(using Context): Unit =
issueWarning(LintWarning(msg, addInlineds(pos), origin))

def warning(msg: Message, pos: SrcPos)(using Context): Unit =
issueWarning(new Warning(msg, addInlineds(pos)))

Expand Down
24 changes: 16 additions & 8 deletions compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import dotty.tools.dotc.config.Settings.Setting
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING}
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.util.chaining.*

import java.util.{Collections, Optional, List => JList}
import scala.util.chaining.*
import core.Decorators.toMessage

object Diagnostic:
Expand All @@ -36,6 +36,18 @@ object Diagnostic:
pos: SourcePosition
) extends Error(msg, pos)

/** A Warning with an origin. The semantics of `origin` depend on the warning.
* For example, an unused import warning has an origin that specifies the unused selector.
* The origin of a deprecation is the deprecated element.
*/
trait OriginWarning(val origin: String):
self: Warning =>

/** Lints are likely to be filtered. Provide extra axes for filtering by `-Wconf`.
*/
class LintWarning(msg: Message, pos: SourcePosition, origin: String)
extends Warning(msg, pos), OriginWarning(origin)

class Warning(
msg: Message,
pos: SourcePosition
Expand Down Expand Up @@ -73,13 +85,9 @@ object Diagnostic:
def enablingOption(using Context): Setting[Boolean] = ctx.settings.unchecked
}

class DeprecationWarning(
msg: Message,
pos: SourcePosition,
val origin: String
) extends ConditionalWarning(msg, pos) {
class DeprecationWarning(msg: Message, pos: SourcePosition, origin: String)
extends ConditionalWarning(msg, pos), OriginWarning(origin):
def enablingOption(using Context): Setting[Boolean] = ctx.settings.deprecation
}

class MigrationWarning(
msg: Message,
Expand All @@ -104,5 +112,5 @@ class Diagnostic(
override def diagnosticRelatedInformation: JList[interfaces.DiagnosticRelatedInformation] =
Collections.emptyList()

override def toString: String = s"$getClass at $pos: $message"
override def toString: String = s"$getClass at $pos L${pos.line+1}: $message"
end Diagnostic
24 changes: 13 additions & 11 deletions compiler/src/dotty/tools/dotc/reporting/WConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import scala.annotation.internal.sharable
import scala.util.matching.Regex

enum MessageFilter:
def matches(message: Diagnostic): Boolean = this match
def matches(message: Diagnostic): Boolean =
import Diagnostic.*
this match
case Any => true
case Deprecated => message.isInstanceOf[Diagnostic.DeprecationWarning]
case Feature => message.isInstanceOf[Diagnostic.FeatureWarning]
case Unchecked => message.isInstanceOf[Diagnostic.UncheckedWarning]
case Deprecated => message.isInstanceOf[DeprecationWarning]
case Feature => message.isInstanceOf[FeatureWarning]
case Unchecked => message.isInstanceOf[UncheckedWarning]
case MessageID(errorId) => message.msg.errorId == errorId
case MessagePattern(pattern) =>
val noHighlight = message.msg.message.replaceAll("\\e\\[[\\d;]*[^\\d;]","")
Expand All @@ -31,7 +33,7 @@ enum MessageFilter:
pattern.findFirstIn(path).nonEmpty
case Origin(pattern) =>
message match
case message: Diagnostic.DeprecationWarning => pattern.findFirstIn(message.origin).nonEmpty
case message: OriginWarning => pattern.findFirstIn(message.origin).nonEmpty
case _ => false
case None => false

Expand All @@ -56,12 +58,12 @@ object WConf:
private type Conf = (List[MessageFilter], Action)

def parseAction(s: String): Either[List[String], Action] = s match
case "error" | "e" => Right(Error)
case "warning" | "w" => Right(Warning)
case "verbose" | "v" => Right(Verbose)
case "info" | "i" => Right(Info)
case "silent" | "s" => Right(Silent)
case _ => Left(List(s"unknown action: `$s`"))
case "error" | "e" => Right(Error)
case "warning" | "w" => Right(Warning)
case "verbose" | "v" => Right(Verbose)
case "info" | "i" => Right(Info)
case "silent" | "s" => Right(Silent)
case _ => Left(List(s"unknown action: `$s`"))

private def regex(s: String) =
try Right(s.r)
Expand Down
Loading
Loading