Skip to content

Commit ed8d1af

Browse files
committed
Refactor migration warnings
Now we have a single way to define migration warning versions and a single place to see all the deprecations we still need to address.
1 parent fab3f21 commit ed8d1af

File tree

9 files changed

+122
-90
lines changed

9 files changed

+122
-90
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dotty.tools
2+
package dotc
3+
package config
4+
5+
import SourceVersion.*
6+
import Feature.*
7+
import core.Contexts.Context
8+
9+
class MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion):
10+
assert(warnFrom.ordinal <= errorFrom.ordinal)
11+
def needsPatch(using Context): Boolean =
12+
sourceVersion.isMigrating && sourceVersion.isAtLeast(errorFrom)
13+
14+
object MigrationVersion:
15+
16+
val Scala2to3 = MigrationVersion(`3.0`, `3.0`)
17+
18+
val OverrideValParameter = MigrationVersion(`3.0`, future)
19+
20+
// we tighten for-comprehension without `case` to error in 3.4,
21+
// but we keep pat-defs as warnings for now ("@unchecked"),
22+
// until we propose an alternative way to assert exhaustivity to the typechecker.
23+
val ForComprehensionPatternWithoutCase = MigrationVersion(`3.2`, `3.4`)
24+
val ForComprehensionUncheckedPathDefs = MigrationVersion(`3.2`, future)
25+
26+
val NonLocalReturns = MigrationVersion(`3.2`, future)
27+
28+
val AscriptionAfterPattern = MigrationVersion(`3.3`, future)
29+
30+
val AlphanumericInfix = MigrationVersion(`3.4`, future)
31+
val RemoveThisQualifier = MigrationVersion(`3.4`, future)
32+
val UninitializedVars = MigrationVersion(`3.4`, future)
33+
val VarargSpliceAscription = MigrationVersion(`3.4`, future)
34+
val WildcardType = MigrationVersion(`3.4`, future)
35+
val WithOperator = MigrationVersion(`3.4`, future)
36+
val FunctionUnderscore = MigrationVersion(`3.4`, future)
37+
38+
val ImportWildcard = MigrationVersion(future, future)
39+
val ImportRename = MigrationVersion(future, future)
40+
val ParameterEnclosedByParenthesis = MigrationVersion(future, future)
41+
42+
end MigrationVersion

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+39-45
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import config.Feature
3333
import config.Feature.{sourceVersion, migrateTo3, globalOnlyImports}
3434
import config.SourceVersion.*
3535
import config.SourceVersion
36+
import dotty.tools.dotc.config.MigrationVersion
3637

3738
object Parsers {
3839

@@ -462,8 +463,8 @@ object Parsers {
462463
case t @ Typed(Ident(_), _) =>
463464
report.errorOrMigrationWarning(
464465
em"parentheses are required around the parameter of a lambda${rewriteNotice()}",
465-
in.sourcePos(), from = `3.0`)
466-
if sourceVersion.isMigrating then
466+
in.sourcePos(), MigrationVersion.Scala2to3)
467+
if MigrationVersion.Scala2to3.needsPatch then
467468
patch(source, t.span.startPos, "(")
468469
patch(source, t.span.endPos, ")")
469470
convertToParam(t, mods) :: Nil
@@ -1314,8 +1315,8 @@ object Parsers {
13141315
|or enclose in braces '{$name} if you want a quoted expression.
13151316
|For now, you can also `import language.deprecated.symbolLiterals` to accept
13161317
|the idiom, but this possibility might no longer be available in the future.""",
1317-
in.sourcePos(), from = `3.0`)
1318-
if sourceVersion.isMigrating then
1318+
in.sourcePos(), MigrationVersion.Scala2to3)
1319+
if MigrationVersion.Scala2to3.needsPatch then
13191320
patch(source, Span(in.offset, in.offset + 1), "Symbol(\"")
13201321
patch(source, Span(in.charOffset - 1), "\")")
13211322
atSpan(in.skipToken()) { SymbolLit(in.strVal) }
@@ -1412,8 +1413,8 @@ object Parsers {
14121413
em"""This opening brace will start a new statement in Scala 3.
14131414
|It needs to be indented to the right to keep being treated as
14141415
|an argument to the previous expression.${rewriteNotice()}""",
1415-
in.sourcePos(), from = `3.0`)
1416-
if sourceVersion.isMigrating then
1416+
in.sourcePos(), MigrationVersion.Scala2to3)
1417+
if MigrationVersion.Scala2to3.needsPatch then
14171418
patch(source, Span(in.offset), " ")
14181419

14191420
def possibleTemplateStart(isNew: Boolean = false): Unit =
@@ -1776,12 +1777,11 @@ object Parsers {
17761777
t
17771778
else
17781779
val withSpan = Span(withOffset, withOffset + 4)
1779-
report.gradualErrorOrMigrationWarning(
1780+
report.errorOrMigrationWarning(
17801781
DeprecatedWithOperator(rewriteNotice(`3.4-migration`)),
17811782
source.atSpan(withSpan),
1782-
warnFrom = `3.4`,
1783-
errorFrom = future)
1784-
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then
1783+
MigrationVersion.WithOperator)
1784+
if MigrationVersion.WithOperator.needsPatch then
17851785
patch(source, withSpan, "&")
17861786
atSpan(startOffset(t)) { makeAndType(t, withType()) }
17871787
else t
@@ -1882,12 +1882,11 @@ object Parsers {
18821882
Ident(tpnme.USCOREkw).withSpan(Span(start, in.lastOffset, start))
18831883
else
18841884
if !inTypeMatchPattern then
1885-
report.gradualErrorOrMigrationWarning(
1885+
report.errorOrMigrationWarning(
18861886
em"`_` is deprecated for wildcard arguments of types: use `?` instead${rewriteNotice(`3.4-migration`)}",
18871887
in.sourcePos(),
1888-
warnFrom = `3.4`,
1889-
errorFrom = future)
1890-
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then
1888+
MigrationVersion.WildcardType)
1889+
if MigrationVersion.WildcardType.needsPatch then
18911890
patch(source, Span(in.offset, in.offset + 1), "?")
18921891
end if
18931892
val start = in.skipToken()
@@ -2111,7 +2110,7 @@ object Parsers {
21112110
else if in.token == VIEWBOUND then
21122111
report.errorOrMigrationWarning(
21132112
em"view bounds `<%' are no longer supported, use a context bound `:' instead",
2114-
in.sourcePos(), from = `3.0`)
2113+
in.sourcePos(), MigrationVersion.Scala2to3)
21152114
atSpan(in.skipToken()) {
21162115
Function(Ident(pname) :: Nil, toplevelTyp())
21172116
} :: contextBounds(pname)
@@ -2260,15 +2259,15 @@ object Parsers {
22602259
report.errorOrMigrationWarning(
22612260
em"""`do <body> while <cond>` is no longer supported,
22622261
|use `while <body> ; <cond> do ()` instead.${rewriteNotice()}""",
2263-
in.sourcePos(), from = `3.0`)
2262+
in.sourcePos(), MigrationVersion.Scala2to3)
22642263
val start = in.skipToken()
22652264
atSpan(start) {
22662265
val body = expr()
22672266
if (isStatSep) in.nextToken()
22682267
val whileStart = in.offset
22692268
accept(WHILE)
22702269
val cond = expr()
2271-
if sourceVersion.isMigrating then
2270+
if MigrationVersion.Scala2to3.needsPatch then
22722271
patch(source, Span(start, start + 2), "while ({")
22732272
patch(source, Span(whileStart, whileStart + 5), ";")
22742273
cond match {
@@ -2370,18 +2369,17 @@ object Parsers {
23702369
val isVarargSplice = location.inArgs && followingIsVararg()
23712370
in.nextToken()
23722371
if isVarargSplice then
2373-
report.gradualErrorOrMigrationWarning(
2372+
report.errorOrMigrationWarning(
23742373
em"The syntax `x: _*` is no longer supported for vararg splices; use `x*` instead${rewriteNotice(`3.4-migration`)}",
23752374
in.sourcePos(uscoreStart),
2376-
warnFrom = `3.4`,
2377-
errorFrom = future)
2378-
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then
2375+
MigrationVersion.VarargSpliceAscription)
2376+
if MigrationVersion.VarargSpliceAscription.needsPatch then
23792377
patch(source, Span(t.span.end, in.lastOffset), "*")
23802378
else if opStack.nonEmpty then
23812379
report.errorOrMigrationWarning(
23822380
em"""`_*` can be used only for last argument of method application.
23832381
|It is no longer allowed in operands of infix operations.""",
2384-
in.sourcePos(uscoreStart), from = `3.0`)
2382+
in.sourcePos(uscoreStart), MigrationVersion.Scala2to3)
23852383
else
23862384
syntaxError(SeqWildcardPatternPos(), uscoreStart)
23872385
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
@@ -2448,13 +2446,12 @@ object Parsers {
24482446
report.errorOrMigrationWarning(
24492447
em"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice(`future-migration`)}",
24502448
source.atSpan(Span(start, in.lastOffset)),
2451-
from = future)
2449+
MigrationVersion.ParameterEnclosedByParenthesis)
24522450
in.nextToken()
24532451
val t = infixType()
2454-
if (sourceVersion == `future-migration`) {
2452+
if MigrationVersion.ParameterEnclosedByParenthesis.needsPatch then
24552453
patch(source, Span(start), "(")
24562454
patch(source, Span(in.lastOffset), ")")
2457-
}
24582455
t
24592456
}
24602457
else TypeTree()
@@ -2958,15 +2955,13 @@ object Parsers {
29582955
if in.isColon then
29592956
val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number]
29602957
if !isVariableOrNumber then
2961-
report.gradualErrorOrMigrationWarning(
2958+
report.errorOrMigrationWarning(
29622959
em"""Type ascriptions after patterns other than:
29632960
| * variable pattern, e.g. `case x: String =>`
29642961
| * number literal pattern, e.g. `case 10.5: Double =>`
29652962
|are no longer supported. Remove the type ascription or move it to a separate variable pattern.""",
29662963
in.sourcePos(),
2967-
warnFrom = `3.3`,
2968-
errorFrom = future
2969-
)
2964+
MigrationVersion.AscriptionAfterPattern)
29702965
in.nextToken()
29712966
ascription(p, location)
29722967
else p
@@ -3147,13 +3142,12 @@ object Parsers {
31473142
else mods.withPrivateWithin(ident().toTypeName)
31483143
}
31493144
if mods1.is(Local) then
3150-
report.gradualErrorOrMigrationWarning(
3145+
report.errorOrMigrationWarning(
31513146
em"""The [this] qualifier will be deprecated in the future; it should be dropped.
31523147
|See: https://docs.scala-lang.org/scala3/reference/dropped-features/this-qualifier.html${rewriteNotice(`3.4-migration`)}""",
31533148
in.sourcePos(),
3154-
warnFrom = `3.4`,
3155-
errorFrom = future)
3156-
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then
3149+
MigrationVersion.RemoveThisQualifier)
3150+
if MigrationVersion.RemoveThisQualifier.needsPatch then
31573151
patch(source, Span(startOffset, in.lastOffset), "")
31583152
mods1
31593153
}
@@ -3542,8 +3536,8 @@ object Parsers {
35423536
report.errorOrMigrationWarning(
35433537
em"`_` is no longer supported for a wildcard $exprName; use `*` instead${rewriteNotice(`future-migration`)}",
35443538
in.sourcePos(),
3545-
from = future)
3546-
if sourceVersion == `future-migration` then
3539+
MigrationVersion.ImportWildcard)
3540+
if MigrationVersion.ImportWildcard.needsPatch then
35473541
patch(source, Span(in.offset, in.offset + 1), "*")
35483542
ImportSelector(atSpan(in.skipToken()) { Ident(nme.WILDCARD) })
35493543

@@ -3562,8 +3556,8 @@ object Parsers {
35623556
report.errorOrMigrationWarning(
35633557
em"The $exprName renaming `a => b` is no longer supported ; use `a as b` instead${rewriteNotice(`future-migration`)}",
35643558
in.sourcePos(),
3565-
from = future)
3566-
if sourceVersion == `future-migration` then
3559+
MigrationVersion.ImportRename)
3560+
if MigrationVersion.ImportRename.needsPatch then
35673561
patch(source, Span(in.offset, in.offset + 2),
35683562
if testChar(in.offset - 1, ' ') && testChar(in.offset + 2, ' ') then "as"
35693563
else " as ")
@@ -3674,13 +3668,12 @@ object Parsers {
36743668
subExpr() match
36753669
case rhs0 @ Ident(name) if placeholderParams.nonEmpty && name == placeholderParams.head.name
36763670
&& !tpt.isEmpty && mods.is(Mutable) && lhs.forall(_.isInstanceOf[Ident]) =>
3677-
report.gradualErrorOrMigrationWarning(
3671+
report.errorOrMigrationWarning(
36783672
em"""`= _` has been deprecated; use `= uninitialized` instead.
36793673
|`uninitialized` can be imported with `scala.compiletime.uninitialized`.${rewriteNotice(`3.4-migration`)}""",
36803674
in.sourcePos(rhsOffset),
3681-
warnFrom = `3.4`,
3682-
errorFrom = future)
3683-
if sourceVersion.isMigrating && sourceVersion.isAtLeast(`3.4-migration`) then
3675+
MigrationVersion.UninitializedVars)
3676+
if MigrationVersion.UninitializedVars.needsPatch then
36843677
patch(source, Span(rhsOffset, rhsOffset + 1), "scala.compiletime.uninitialized")
36853678
placeholderParams = placeholderParams.tail
36863679
atSpan(rhs0.span) { Ident(nme.WILDCARD) }
@@ -3722,9 +3715,10 @@ object Parsers {
37223715
else ": Unit " // trailing space ensures that `def f()def g()` works.
37233716
if migrateTo3 then
37243717
report.errorOrMigrationWarning(
3725-
em"Procedure syntax no longer supported; `$toInsert` should be inserted here",
3726-
in.sourcePos(), from = `3.0`)
3727-
patch(source, Span(in.lastOffset), toInsert)
3718+
em"Procedure syntax no longer supported; `$toInsert` should be inserted here${rewriteNotice()}",
3719+
in.sourcePos(), MigrationVersion.Scala2to3)
3720+
if MigrationVersion.Scala2to3.needsPatch then
3721+
patch(source, Span(in.lastOffset), toInsert)
37283722
true
37293723
else
37303724
false
@@ -4150,7 +4144,7 @@ object Parsers {
41504144
if (in.token == LBRACE || in.token == COLONeol) {
41514145
report.errorOrMigrationWarning(
41524146
em"`extends` must be followed by at least one parent",
4153-
in.sourcePos(), from = `3.0`)
4147+
in.sourcePos(), MigrationVersion.Scala2to3)
41544148
Nil
41554149
}
41564150
else constrApps()

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import rewrites.Rewrites.patch
1919
import config.Feature
2020
import config.Feature.{migrateTo3, fewerBracesEnabled}
2121
import config.SourceVersion.{`3.0`, `3.0-migration`}
22+
import config.MigrationVersion
2223
import reporting.{NoProfile, Profile, Message}
2324

2425
import java.util.Objects
@@ -257,8 +258,8 @@ object Scanners {
257258
report.errorOrMigrationWarning(
258259
em"$what is now a keyword, write `$what` instead of $what to keep it as an identifier${rewriteNotice("This", `3.0-migration`)}",
259260
sourcePos(),
260-
from = `3.0`)
261-
if sourceVersion.isMigrating then
261+
MigrationVersion.Scala2to3)
262+
if MigrationVersion.Scala2to3.needsPatch then
262263
patch(source, Span(offset), "`")
263264
patch(source, Span(offset + identifier.length), "`")
264265
IDENTIFIER
@@ -470,7 +471,7 @@ object Scanners {
470471
em"""$what starts with an operator;
471472
|it is now treated as a continuation of the $previous,
472473
|not as a separate statement.""",
473-
sourcePos(), from = `3.0`)
474+
sourcePos(), MigrationVersion.Scala2to3)
474475
true
475476
}
476477

compiler/src/dotty/tools/dotc/report.scala

+6-9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import config.SourceVersion
99
import ast.*
1010
import config.Feature.sourceVersion
1111
import java.lang.System.currentTimeMillis
12+
import dotty.tools.dotc.config.MigrationVersion
1213

1314
object report:
1415

@@ -80,15 +81,11 @@ object report:
8081
if ctx.settings.YdebugError.value then Thread.dumpStack()
8182
if ctx.settings.YdebugTypeError.value then ex.printStackTrace()
8283

83-
def errorOrMigrationWarning(msg: Message, pos: SrcPos, from: SourceVersion)(using Context): Unit =
84-
if sourceVersion.isAtLeast(from) then
85-
if sourceVersion.isMigrating && sourceVersion.ordinal <= from.ordinal then
86-
if ctx.settings.rewrite.value.isEmpty then migrationWarning(msg, pos)
87-
else error(msg, pos)
88-
89-
def gradualErrorOrMigrationWarning(msg: Message, pos: SrcPos, warnFrom: SourceVersion, errorFrom: SourceVersion)(using Context): Unit =
90-
if sourceVersion.isAtLeast(errorFrom) then errorOrMigrationWarning(msg, pos, errorFrom)
91-
else if sourceVersion.isAtLeast(warnFrom) then warning(msg, pos)
84+
def errorOrMigrationWarning(msg: Message, pos: SrcPos, migrationVersion: MigrationVersion)(using Context): Unit =
85+
if sourceVersion.isAtLeast(migrationVersion.errorFrom) then
86+
if !sourceVersion.isMigrating then error(msg, pos)
87+
else if ctx.settings.rewrite.value.isEmpty then migrationWarning(msg, pos)
88+
else if sourceVersion.isAtLeast(migrationVersion.warnFrom) then warning(msg, pos)
9289

9390
def restrictionError(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit =
9491
error(msg.mapMsg("Implementation restriction: " + _), pos)

compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import MegaPhase.*
77
import NameKinds.NonLocalReturnKeyName
88
import config.SourceVersion.*
99
import Decorators.em
10+
import dotty.tools.dotc.config.MigrationVersion
1011

1112
object NonLocalReturns {
1213
import ast.tpd.*
@@ -96,11 +97,10 @@ class NonLocalReturns extends MiniPhase {
9697

9798
override def transformReturn(tree: Return)(using Context): Tree =
9899
if isNonLocalReturn(tree) then
99-
report.gradualErrorOrMigrationWarning(
100+
report.errorOrMigrationWarning(
100101
em"Non local returns are no longer supported; use `boundary` and `boundary.break` in `scala.util` instead",
101102
tree.srcPos,
102-
warnFrom = `3.2`,
103-
errorFrom = future)
103+
MigrationVersion.NonLocalReturns)
104104
nonLocalReturnThrow(tree.expr, tree.from.symbol).withSpan(tree.span)
105105
else tree
106106
}

0 commit comments

Comments
 (0)