Skip to content

Commit

Permalink
Align warnings / semantics with -Xsource:3 / 3-X
Browse files Browse the repository at this point in the history
  • Loading branch information
lrytz committed Nov 20, 2023
1 parent f2e7cca commit bec5686
Show file tree
Hide file tree
Showing 25 changed files with 495 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,7 @@ self =>
// Scala 2 allowed uprooted Ident for purposes of virtualization
val t1 =
if (currentRun.isScala3X) atPos(o2p(start)) { Select(Select(Ident(nme.ROOTPKG), nme.scala_), nme.StringContextName) }
else atPos(o2p(start)) { Ident(nme.StringContextName) }
else atPos(o2p(start)) { Ident(nme.StringContextName).updateAttachment(VirtualStringContext) }
val t2 = atPos(start) { Apply(t1, partsBuf.toList) } updateAttachment InterpolatedString
t2 setPos t2.pos.makeTransparent
val t3 = Select(t2, interpolator) setPos t2.pos
Expand Down
10 changes: 7 additions & 3 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -970,11 +970,15 @@ trait Scanners extends ScannersCommon {
val diffPosition = processed.zip(strVal).zipWithIndex.collectFirst{ case ((r, o), i) if r != o => i}.getOrElse(processed.length - 1)
val pos = offset + 3 + diffPosition
def msg(what: String) = s"Unicode escapes in triple quoted strings are $what; use the literal character instead"
if (!currentRun.isScala3) {
deprecationWarning(pos, msg("deprecated"), since="2.13.2")
if (currentRun.isScala3X)
()
else {
if (currentRun.isScala3)
warning(pos, msg("ignored in Scala 3"), WarningCategory.Scala3Migration)
else
deprecationWarning(pos, msg("deprecated"), since="2.13.2")
strVal = processed
}
else warning(pos, msg("ignored under -Xsource:3"), WarningCategory.Scala3Migration)
}
} catch {
case ue: StringContext.InvalidUnicodeEscapeException =>
Expand Down
25 changes: 14 additions & 11 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,31 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "")
val reporter = StringSetting ("-Xreporter", "classname", "Specify a custom subclass of FilteringReporter for compiler messages.", "scala.tools.nsc.reporters.ConsoleReporter")
private val XsourceHelp =
sm"""|-Xsource:3 issues migration warnings in category `cat=Scala3Migration`,
sm"""|-Xsource:3 is for migrating a codebase, -Xsource:3-X is for cross-building.
|
|-Xsource:3 issues migration warnings in category `cat=scala3-migration`,
| which by default are promoted to errors under the `-Wconf` configuration.
| Certain benign syntax features are enabled:
| Examples of promoted warnings:
| * Implicit definitions must have an explicit type.
| * (x: Any) + "" is deprecated.
| * Args not adapted to unit value.
| * Member classes cannot shadow a same-named class defined in a parent.
| * Presence or absence of parentheses in overrides must match exactly.
|
|Certain benign syntax features are enabled:
| * case C(xs*) =>.
| * A & B type intersection.
| * import p.*.
| * import p.m as n.
| * import p.{given, *}
|
|-Xsource:3-X enables certain Scala 3 syntax and behavior:
| * Ignore Unicode escapes in raw interpolations and triple-quoted strings.
|The following constructs emit a migration warning under -Xsource:3. With
|-Xsource:3-X the semantics change to match Scala 3 and no warning is issued.
| * Unicode escapes in raw interpolations and triple-quoted strings.
| * Leading infix operators continue the previous line.
| * Interpolator must be selectable from `scala.StringContext`.
| * Case class copy and apply have the same access modifier as the constructor.
| * The inferred type of an override is taken from the member it overrides.
|
|Promoted warnings:
| * Implicit definitions must have an explicit type.
| * (x: Any) + "" is deprecated.
| * Args not adapted to unit value.
| * Member classes cannot override.
| * Presence or absence of parentheses in overrides must match exactly.
|"""
@nowarn("cat=deprecation")
val source = ScalaVersionSetting ("-Xsource", "version", "Enable warnings and features for a future version.", initial = ScalaVersion("2.13"), helpText = Some(XsourceHelp)).withPostSetHook { s =>
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ trait Contexts { self: Analyzer =>
!(
// [eed3si9n] ideally I'd like to do this: val fd = currentRun.isScala3 && sym.isDeprecated
// but implicit caching currently does not report sym.isDeprecated correctly.
currentRun.isScala3 && (sym == currentRun.runDefinitions.Predef_any2stringaddMethod)
currentRun.isScala3X && (sym == currentRun.runDefinitions.Predef_any2stringaddMethod)
) &&
!(imported && {
val e = scope.lookupEntry(name)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ trait Namers extends MethodSynthesis {
}
}
val legacy = dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt))
if (inferOverridden && currentRun.isScala3 && !(legacy =:= pt)) {
if (inferOverridden && currentRun.isScala3 && !currentRun.isScala3X && !(legacy =:= pt)) {
val pts = pt.toString
val leg = legacy.toString
val help = if (pts != leg) s" instead of $leg" else ""
Expand Down
20 changes: 11 additions & 9 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3417,18 +3417,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
for (sym <- scope) context.unit.synthetics.get(sym) match {
// OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
case Some(tree) if shouldAdd(sym) =>
def isSyntheticCaseApplyTweaked = tree match {
case DefDef(mods, nme.apply, _, _, _, _) =>
sym.owner.sourceModule.companionSymbol.isCaseClass && {
mods.hasFlag(PRIVATE) || mods.privateWithin != tpnme.EMPTY
}
case _ => false
}
// if the completer set the IS_ERROR flag, retract the stat (currently only used by applyUnapplyMethodCompleter)
if (!sym.initialize.hasFlag(IS_ERROR)) {
newStats += typedStat(tree) // might add even more synthetics to the scope
if (currentRun.isScala3 && !sym.owner.isCaseClass && isSyntheticCaseApplyTweaked)
runReporting.warning(tree.pos, "access modifiers for `apply` method are copied from the case class constructor", Scala3Migration, sym)
tree.getAndRemoveAttachment[CaseApplyInheritAccess.type].foreach(_ =>
runReporting.warning(tree.pos, "access modifiers for `apply` method are copied from the case class constructor", Scala3Migration, sym))
}
context.unit.synthetics -= sym
case _ => ()
Expand Down Expand Up @@ -5597,6 +5590,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val fix = runReporting.codeAction("make reference explicit", tree.pos.focusStart, w.fix, w.msg)
runReporting.warning(tree.pos, w.msg, cat, context.owner, fix)
})
if (currentRun.isScala3) {
tree.getAndRemoveAttachment[VirtualStringContext.type].foreach(_ =>
if (sym != definitions.StringContextModule)
runReporting.warning(
tree.pos,
s"String interpolations always use scala.StringContext in Scala 3 (${sym.fullNameString} is used here)",
Scala3Migration,
context.owner))
}
(// this -> Foo.this
if (sym.isThisSym)
typed1(This(sym.owner) setPos tree.pos, mode, pt)
Expand Down
40 changes: 26 additions & 14 deletions src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,16 @@ trait Unapplies extends ast.TreeDSL {
}
}

private def applyShouldInheritAccess(mods: Modifiers) =
currentRun.isScala3 && (mods.hasFlag(PRIVATE) || (!mods.hasFlag(PROTECTED) && mods.hasAccessBoundary))
sealed private trait ApplyAccess
private object Default extends ApplyAccess
private object Warn extends ApplyAccess
private object Inherit extends ApplyAccess
private def applyAccess(mods: Modifiers): ApplyAccess = {
val changeModsIn3 = mods.hasFlag(PRIVATE) || (!mods.hasFlag(PROTECTED) && mods.hasAccessBoundary)
if (currentRun.isScala3X && changeModsIn3) Inherit
else if (currentRun.isScala3 && changeModsIn3) Warn
else Default
}

/** The module corresponding to a case class; overrides toString to show the module's name
*/
Expand All @@ -108,7 +116,7 @@ trait Unapplies extends ast.TreeDSL {
def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && (params match {
case List(ps) if ps.length <= MaxFunctionArity => true
case _ => false
}) && !applyShouldInheritAccess(constrMods(cdef))
}) && applyAccess(constrMods(cdef)) != Inherit
def createFun = {
def primaries = params.head map (_.tpt)
gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true)
Expand Down Expand Up @@ -153,12 +161,12 @@ trait Unapplies extends ast.TreeDSL {
*/
def caseModuleApplyMeth(cdef: ClassDef): DefDef = {
val inheritedMods = constrMods(cdef)
val access = applyAccess(inheritedMods)
val mods =
if (applyShouldInheritAccess(inheritedMods))
(caseMods | (inheritedMods.flags & PRIVATE)).copy(privateWithin = inheritedMods.privateWithin)
else
caseMods
factoryMeth(mods, nme.apply, cdef)
if (access == Inherit) (caseMods | (inheritedMods.flags & PRIVATE)).copy(privateWithin = inheritedMods.privateWithin)
else caseMods
factoryMeth(mods, nme.apply, cdef).tap(m =>
if (access == Warn) m.updateAttachment(CaseApplyInheritAccess))
}

/** The unapply method corresponding to a case class
Expand Down Expand Up @@ -272,16 +280,20 @@ trait Unapplies extends ast.TreeDSL {
val classTpe = classType(cdef, tparams)
val argss = mmap(paramss)(toIdent)
val body: Tree = New(classTpe, argss)
val synth = Modifiers(SYNTHETIC)
val copyMods =
if (currentRun.isScala3) {
val inheritedMods = constrMods(cdef)
Modifiers(SYNTHETIC | (inheritedMods.flags & AccessFlags), inheritedMods.privateWithin)
.tap { mods =>
if (currentRun.isScala3 && mods != Modifiers(SYNTHETIC))
runReporting.warning(cdef.pos, "access modifiers for `copy` method are copied from the case class constructor", Scala3Migration, cdef.symbol)
}
val mods3 = Modifiers(SYNTHETIC | (inheritedMods.flags & AccessFlags), inheritedMods.privateWithin)
if (currentRun.isScala3X)
mods3
else {
if (mods3 != synth)
runReporting.warning(cdef.pos, "access modifiers for `copy` method are copied from the case class constructor", Scala3Migration, cdef.symbol)
synth
}
}
else Modifiers(SYNTHETIC)
else synth
val copyDefDef = atPos(cdef.pos.focus)(
DefDef(copyMods, nme.copy, tparams, paramss, TypeTree(), body)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ trait FastStringInterpolator extends FormatInterpolator {
lit.pos.withShift(diffindex)
}
def msg(fate: String) = s"Unicode escapes in raw interpolations are $fate; use literal characters instead"
if (currentRun.isScala3X) {
runReporting.warning(pos, msg("ignored under -Xsource:3"), Scala3Migration, c.internal.enclosingOwner)
if (currentRun.isScala3X)
stringVal
}
else if (currentRun.isScala3) {
runReporting.warning(pos, msg("ignored in Scala 3"), Scala3Migration, c.internal.enclosingOwner)
processed
Expand Down
4 changes: 4 additions & 0 deletions src/reflect/scala/reflect/internal/StdAttachments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ trait StdAttachments {

case object InterpolatedString extends PlainAttachment

case object VirtualStringContext extends PlainAttachment

case object CaseApplyInheritAccess extends PlainAttachment

// Use of _root_ is in correct leading position of selection
case object RootSelection extends PlainAttachment

Expand Down
2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.NullaryOverrideAdapted
this.ChangeOwnerAttachment
this.InterpolatedString
this.VirtualStringContext
this.CaseApplyInheritAccess
this.RootSelection
this.TypedExpectingUnitAttachment
this.FieldTypeInferred
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/caseclass_private_constructor.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// scalac: -Xsource:3 -Wconf:cat=scala3-migration:s
// scalac: -Xsource:3-X -Wconf:cat=scala3-migration:s

case class A private (i: Int)
object A
Expand Down
33 changes: 33 additions & 0 deletions test/files/neg/deprecationsFor3.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
deprecationsFor3.scala:4: warning: Unicode escapes in triple quoted strings are deprecated; use the literal character instead
def inTripleQuoted = """\u0041""" // deprecation
^
deprecationsFor3.scala:16: warning: Line starts with an operator that in future
will be taken as an infix expression continued from the previous line.
To force the previous interpretation as a separate statement,
add an explicit `;`, add an empty line, or remove spaces after the operator.
`x` (42) // migration
^
deprecationsFor3.scala:5: warning: Unicode escapes in raw interpolations are deprecated; use literal characters instead
def inRawInterpolation = raw"\u0041" // deprecation
^
deprecationsFor3.scala:6: warning: Unicode escapes in raw interpolations are deprecated; use literal characters instead
def inRawTripleQuoted = raw"""\u0041""" // deprecation
^
deprecationsFor3.scala:29: warning: Implicit definition should have explicit type (inferred String => Option[Int]) [quickfixable]
implicit def b = _.toIntOption // error
^
deprecationsFor3.scala:31: warning: Implicit definition should have explicit type (inferred String) [quickfixable]
implicit def s = "" // error
^
deprecationsFor3.scala:30: warning: Implicit definition should have explicit type (inferred Int) [quickfixable]
implicit val i = 0 // error
^
deprecationsFor3.scala:34: warning: method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
object AnyPlus { def f(xs: List[Int]) = xs + ";" }
^
deprecationsFor3.scala:38: warning: shadowing a nested class of a parent is deprecated but class X shadows class X defined in class A; rename the class to something else
class B extends A { class X; def f = new X }
^
error: No warnings can be incurred under -Werror.
9 warnings
1 error
39 changes: 39 additions & 0 deletions test/files/neg/deprecationsFor3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//> using options -deprecation -Xmigration -Werror

object UnicodeEscapes {
def inTripleQuoted = """\u0041""" // deprecation
def inRawInterpolation = raw"\u0041" // deprecation
def inRawTripleQuoted = raw"""\u0041""" // deprecation
}

object InfixNewline extends App {
class K { def x(y: Int) = 0 }

def x(a: Int) = 1

def ok = {
(new K)
`x` (42) // migration
}
}

case class CaseCompanionMods private (x: Int) // nothing

trait InferredBase { def f: Object }
object InferredSub extends InferredBase { def f = "a" } // nothing

trait ExplicitImplicitsBase {
implicit def b: String => Option[Int]
}
object ExplicitImplicits extends ExplicitImplicitsBase {
implicit def b = _.toIntOption // error
implicit val i = 0 // error
implicit def s = "" // error
}

object AnyPlus { def f(xs: List[Int]) = xs + ";" }

object NameShadowing {
class A { class X }
class B extends A { class X; def f = new X }
}
2 changes: 1 addition & 1 deletion test/files/neg/implicit-any2stringadd.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// scalac: -Xsource:3 -Vimplicits
// scalac: -Xsource:3-X -Vimplicits
//
object Test {
true + "what"
Expand Down
22 changes: 22 additions & 0 deletions test/files/neg/source3Xneg.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
source3Xneg.scala:47: error: value + is not a member of List[Int]
object AnyPlus { def f(xs: List[Int]) = xs + ";" }
^
source3Xneg.scala:56: error: method copy in class CaseCompanionMods cannot be accessed as a member of CaseCompanionMods from object Test
CaseCompanionMods.i.copy(CaseCompanionMods(2).x) // 2 errors
^
source3Xneg.scala:56: error: method apply in object CaseCompanionMods cannot be accessed as a member of object CaseCompanionMods from object Test
error after rewriting to CaseCompanionMods.<apply: error>
possible cause: maybe a wrong Dynamic method signature?
CaseCompanionMods.i.copy(CaseCompanionMods(2).x) // 2 errors
^
source3Xneg.scala:60: error: value toUpperCase is not a member of Object
InferredSub.f.toUpperCase // error
^
source3Xneg.scala:44: warning: Implicit definition must have explicit type (inferred String) [quickfixable]
implicit def s = "" // error
^
source3Xneg.scala:43: warning: Implicit definition must have explicit type (inferred Int) [quickfixable]
implicit val i = 0 // error
^
2 warnings
4 errors

0 comments on commit bec5686

Please sign in to comment.