Skip to content

Commit 3770894

Browse files
committed
Alternative: preserve : @unchecked message pre 3.8
patch and warning message text will still suggest/replace `: @unchecked` pre 3.8. from 3.8-migration onwards, the message/patch suggests `.runtimeChecked` This preserves behavior under older source versions.
1 parent 844e501 commit 3770894

10 files changed

+145
-12
lines changed

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,10 @@ trait Checking {
10851085
else em"pattern binding uses refutable extractor `$extractor`"
10861086

10871087
val fix =
1088-
if isPatDef then "adding `.runtimeChecked` after the expression"
1088+
if isPatDef then
1089+
val patchText =
1090+
if sourceVersion.isAtLeast(`3.8`) then ".runtimeChecked" else ": @unchecked"
1091+
s"adding `$patchText` after the expression"
10891092
else "adding the `case` keyword before the full pattern"
10901093
val addendum =
10911094
if isPatDef then "may result in a MatchError at runtime"
@@ -1098,7 +1101,9 @@ trait Checking {
10981101
case NonConforming => sel.srcPos
10991102
case RefutableExtractor => pat.source.atSpan(pat.span `union` sel.span)
11001103
else pat.srcPos
1101-
def rewriteMsg = Message.rewriteNotice("This patch", `3.2-migration`)
1104+
def rewriteMsg = Message.rewriteNotice("This patch",
1105+
if isPatDef && sourceVersion.isAtLeast(`3.8`) then `3.8-migration` else `3.2-migration`
1106+
)
11021107
report.errorOrMigrationWarning(
11031108
message.append(
11041109
i"""|
@@ -1107,7 +1112,7 @@ trait Checking {
11071112
|which $addendum.$rewriteMsg"""),
11081113
pos,
11091114
// we tighten for-comprehension without `case` to error in 3.4,
1110-
// but we keep pat-defs as warnings for now ("@unchecked"),
1115+
// but we keep pat-defs as warnings for now (".runtimeChecked"),
11111116
// until we propose an alternative way to assert exhaustivity to the typechecker.
11121117
if isPatDef then MigrationVersion.ForComprehensionUncheckedPathDefs
11131118
else MigrationVersion.ForComprehensionPatternWithoutCase

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,12 +2235,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
22352235
&& sourceVersion.isAtLeast(`3.2`)
22362236
&& sourceVersion.isMigrating
22372237
then
2238-
if isPatDef then runtimeCheckedBrackets(tree.selector) match
2238+
if isPatDef then
2239+
val patchText =
2240+
if sourceVersion.isAtLeast(`3.8`) then ".runtimeChecked"
2241+
else ": @unchecked"
2242+
runtimeCheckedBrackets(tree.selector) match
22392243
case None =>
2240-
patch(Span(tree.selector.span.end), ".runtimeChecked")
2244+
patch(Span(tree.selector.span.end), patchText)
22412245
case Some(bl, br) =>
22422246
patch(Span(tree.selector.span.start), s"$bl")
2243-
patch(Span(tree.selector.span.end), s"$br.runtimeChecked")
2247+
patch(Span(tree.selector.span.end), s"$br$patchText")
22442248
else
22452249
patch(Span(tree.span.start), "case ")
22462250

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ class CompilationTests {
7070
compileFile("tests/rewrites/private-this.scala", defaultOptions.and("-rewrite", "-source", "future-migration")),
7171
compileFile("tests/rewrites/alphanumeric-infix-operator.scala", defaultOptions.and("-rewrite", "-source", "future-migration")),
7272
compileFile("tests/rewrites/filtering-fors.scala", defaultOptions.and("-rewrite", "-source", "3.2-migration")),
73-
compileFile("tests/rewrites/refutable-pattern-bindings.scala", defaultOptions.and("-rewrite", "-source", "3.2-migration")),
73+
compileFile("tests/rewrites/refutable-pattern-bindings-old.scala", defaultOptions.and("-rewrite", "-source", "3.2-migration")),
74+
compileFile("tests/rewrites/refutable-pattern-bindings.scala", defaultOptions.and("-rewrite", "-source", "3.8-migration")),
7475
compileFile("tests/rewrites/i8982.scala", defaultOptions.and("-indent", "-rewrite")),
7576
compileFile("tests/rewrites/i9632.scala", defaultOptions.and("-indent", "-rewrite")),
7677
compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite")),
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
-- Error: tests/neg/refutable-pattern-binding-messages-old.scala:6:14 --------------------------------------------------
2+
6 | for Positive(i) <- List(1, 2, 3) do () // error: refutable extractor
3+
| ^^^^^^^^^^^
4+
| pattern binding uses refutable extractor `Test.Positive`
5+
|
6+
| If this usage is intentional, this can be communicated by adding the `case` keyword before the full pattern,
7+
| which will result in a filtering for expression (using `withFilter`).
8+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
9+
-- Error: tests/neg/refutable-pattern-binding-messages-old.scala:11:11 -------------------------------------------------
10+
11 | for ((x: String) <- xs) do () // error: pattern type more specialized
11+
| ^^^^^^
12+
| pattern's type String is more specialized than the right hand side expression's type AnyRef
13+
|
14+
| If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
15+
| which will result in a filtering for expression (using `withFilter`).
16+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
17+
-- Error: tests/neg/refutable-pattern-binding-messages-old.scala:15:13 -------------------------------------------------
18+
15 | for none @ None <- ys do () // error: pattern type does not match
19+
| ^^^^
20+
| pattern's type None.type does not match the right hand side expression's type (x$1 : Option[?])
21+
|
22+
| If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
23+
| which will result in a filtering for expression (using `withFilter`).
24+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
25+
-- Warning: tests/neg/refutable-pattern-binding-messages-old.scala:5:14 ------------------------------------------------
26+
5 | val Positive(p) = 5 // warn: refutable extractor
27+
| ^^^^^^^^^^^^^^^
28+
| pattern binding uses refutable extractor `Test.Positive`
29+
|
30+
| If this usage is intentional, this can be communicated by adding `: @unchecked` after the expression,
31+
| which may result in a MatchError at runtime.
32+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
33+
-- Warning: tests/neg/refutable-pattern-binding-messages-old.scala:10:20 -----------------------------------------------
34+
10 | val i :: is = List(1, 2, 3) // warn: pattern type more specialized
35+
| ^^^^^^^^^^^^^
36+
| pattern's type ::[Int] is more specialized than the right hand side expression's type List[Int]
37+
|
38+
| If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
39+
| which may result in a MatchError at runtime.
40+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
41+
-- Warning: tests/neg/refutable-pattern-binding-messages-old.scala:16:10 -----------------------------------------------
42+
16 | val 1 = 2 // warn: pattern type does not match
43+
| ^
44+
| pattern's type (1 : Int) does not match the right hand side expression's type (2 : Int)
45+
|
46+
| If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
47+
| which may result in a MatchError at runtime.
48+
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//> using options -source 3.7
2+
object Test {
3+
// refutable extractor
4+
object Positive { def unapply(i: Int): Option[Int] = Some(i).filter(_ > 0) }
5+
val Positive(p) = 5 // warn: refutable extractor
6+
for Positive(i) <- List(1, 2, 3) do () // error: refutable extractor
7+
8+
// more specialized
9+
val xs: List[AnyRef] = ???
10+
val i :: is = List(1, 2, 3) // warn: pattern type more specialized
11+
for ((x: String) <- xs) do () // error: pattern type more specialized
12+
13+
// does not match
14+
val ys: List[Option[?]] = ???
15+
for none @ None <- ys do () // error: pattern type does not match
16+
val 1 = 2 // warn: pattern type does not match
17+
}

tests/neg/refutable-pattern-binding-messages.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@
2929
|
3030
| If this usage is intentional, this can be communicated by adding `.runtimeChecked` after the expression,
3131
| which may result in a MatchError at runtime.
32-
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
32+
| This patch can be rewritten automatically under -rewrite -source 3.8-migration.
3333
-- Warning: tests/neg/refutable-pattern-binding-messages.scala:10:20 ---------------------------------------------------
3434
10 | val i :: is = List(1, 2, 3) // warn: pattern type more specialized
3535
| ^^^^^^^^^^^^^
3636
| pattern's type ::[Int] is more specialized than the right hand side expression's type List[Int]
3737
|
3838
| If the narrowing is intentional, this can be communicated by adding `.runtimeChecked` after the expression,
3939
| which may result in a MatchError at runtime.
40-
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
40+
| This patch can be rewritten automatically under -rewrite -source 3.8-migration.
4141
-- Warning: tests/neg/refutable-pattern-binding-messages.scala:16:10 ---------------------------------------------------
4242
16 | val 1 = 2 // warn: pattern type does not match
4343
| ^
4444
| pattern's type (1 : Int) does not match the right hand side expression's type (2 : Int)
4545
|
4646
| If the narrowing is intentional, this can be communicated by adding `.runtimeChecked` after the expression,
4747
| which may result in a MatchError at runtime.
48-
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
48+
| This patch can be rewritten automatically under -rewrite -source 3.8-migration.

tests/neg/refutable-pattern-binding-messages.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
//> using options -source 3.8
22
object Test {
33
// refutable extractor
44
object Positive { def unapply(i: Int): Option[Int] = Some(i).filter(_ > 0) }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// NOTE: this file is compiled under -source 3.2-migration (i.e. it will rewrite to `: @unchecked`)
2+
// see refutable-pattern-bindings.scala for the version compiled under -source 3.8-migration (which rewrites to `.runtimeChecked`)
3+
val xs: List[Any] = ???
4+
5+
val hd :: tl = (xs match
6+
case Nil => null :: xs
7+
case _ => xs): @unchecked
8+
9+
val h :: t = xs: @unchecked
10+
11+
val a :: b =
12+
(if xs.isEmpty then null :: xs
13+
else xs): @unchecked
14+
15+
val c :: d =
16+
(try xs.head :: xs
17+
catch case _: NoSuchElementException => null :: xs): @unchecked
18+
19+
val e :: f =
20+
{val zero = null :: Nil
21+
if xs.isEmpty then zero
22+
else xs}: @unchecked
23+
24+
val j :: k =
25+
(for
26+
case (x: String) <- xs
27+
yield x): @unchecked
28+
29+
val (_: Int | _: AnyRef) = (??? : AnyRef): @unchecked
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// NOTE: this file is compiled under -source 3.2-migration (i.e. it will rewrite to `: @unchecked`)
2+
// see refutable-pattern-bindings.scala for the version compiled under -source 3.8-migration (which rewrites to `.runtimeChecked`)
3+
val xs: List[Any] = ???
4+
5+
val hd :: tl = xs match
6+
case Nil => null :: xs
7+
case _ => xs
8+
9+
val h :: t = xs
10+
11+
val a :: b =
12+
if xs.isEmpty then null :: xs
13+
else xs
14+
15+
val c :: d =
16+
try xs.head :: xs
17+
catch case _: NoSuchElementException => null :: xs
18+
19+
val e :: f =
20+
val zero = null :: Nil
21+
if xs.isEmpty then zero
22+
else xs
23+
24+
val j :: k =
25+
for
26+
(x: String) <- xs
27+
yield x
28+
29+
val (_: Int | _: AnyRef) = ??? : AnyRef

tests/rewrites/refutable-pattern-bindings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ val e :: f =
2121

2222
val j :: k =
2323
for
24-
(x: String) <- xs
24+
case (x: String) <- xs
2525
yield x
2626

2727
val (_: Int | _: AnyRef) = ??? : AnyRef

0 commit comments

Comments
 (0)