Skip to content

Commit 0c25979

Browse files
committed
SI-8731 warning if @switch is ignored
For matches with two or fewer cases, @switch is ignored. This should not happen silently.
1 parent a52db7f commit 0c25979

File tree

12 files changed

+93
-19
lines changed

12 files changed

+93
-19
lines changed

src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,24 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
550550
case _ => false
551551
}
552552
val suppression = Suppression(suppressExhaustive, supressUnreachable)
553+
val hasSwitchAnnotation = treeInfo.isSwitchAnnotation(tpt.tpe)
553554
// matches with two or fewer cases need not apply for switchiness (if-then-else will do)
554-
val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0
555+
// `case 1 | 2` is considered as two cases.
556+
def exceedsTwoCasesOrAlts = {
557+
// avoids traversing the entire list if there are more than 3 elements
558+
def lengthMax3[T](l: List[T]): Int = l match {
559+
case a :: b :: c :: _ => 3
560+
case cases =>
561+
cases.map({
562+
case AlternativesTreeMaker(_, alts, _) :: _ => lengthMax3(alts)
563+
case c => 1
564+
}).sum
565+
}
566+
lengthMax3(casesNoSubstOnly) > 2
567+
}
568+
val requireSwitch = hasSwitchAnnotation && exceedsTwoCasesOrAlts
569+
if (hasSwitchAnnotation && !requireSwitch)
570+
reporter.warning(scrut.pos, "matches with two cases or fewer are emitted using if-then-else instead of switch")
555571
(suppression, requireSwitch)
556572
case _ =>
557573
(Suppression.NoSuppression, false)

test/files/neg/t6902.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ object Test {
1616
// at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:50)
1717
// at scala.tools.nsc.Global.abort(Global.scala:249)
1818
// at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder$jcode$.emitSWITCH(GenASM.scala:1850)
19-
((1: Byte): @unchecked @annotation.switch) match {
19+
((1: Byte): @unchecked) match {
2020
case 1 => 2
2121
case 1 => 3 // crash
2222
}

test/files/neg/t8731.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
t8731.scala:5: warning: matches with two cases or fewer are emitted using if-then-else instead of switch
2+
def f(x: Int) = (x: @annotation.switch) match {
3+
^
4+
t8731.scala:10: warning: could not emit switch for @switch annotated match
5+
def g(x: Int) = (x: @annotation.switch) match {
6+
^
7+
error: No warnings can be incurred under -Xfatal-warnings.
8+
two warnings found
9+
one error found

test/files/neg/t8731.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xfatal-warnings

test/files/neg/t8731.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class C {
2+
// not a compile-time constant due to return type
3+
final val K: Int = 20
4+
5+
def f(x: Int) = (x: @annotation.switch) match {
6+
case K => 0
7+
case 2 => 1
8+
}
9+
10+
def g(x: Int) = (x: @annotation.switch) match {
11+
case K => 0
12+
case 2 => 1
13+
case 3 => 2
14+
}
15+
}

test/files/pos/switch-small.flags

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/files/pos/switch-small.scala

Lines changed: 0 additions & 8 deletions
This file was deleted.

test/files/run/t5830.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
a with oef
22
a with oef
3+
a with oef
34
a
45
def with oef
56
def

test/files/run/t5830.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import scala.annotation.switch
22

33
object Test extends App {
4-
// TODO: should not emit a switch
5-
// def noSwitch(ch: Char, eof: Boolean) = (ch: @switch) match {
6-
// case 'a' if eof => println("a with oef") // then branch
7-
// }
4+
def noSwitch(ch: Char, eof: Boolean) = ch match {
5+
case 'a' if eof => println("a with oef") // then branch
6+
}
87

9-
def onlyThen(ch: Char, eof: Boolean) = (ch: @switch) match {
8+
def onlyThen(ch: Char, eof: Boolean) = ch match {
109
case 'a' if eof => println("a with oef") // then branch
1110
case 'c' =>
1211
}
@@ -18,7 +17,7 @@ object Test extends App {
1817
case 'c' =>
1918
}
2019

21-
def defaultUnguarded(ch: Char, eof: Boolean) = (ch: @switch) match {
20+
def defaultUnguarded(ch: Char, eof: Boolean) = ch match {
2221
case ' ' if eof => println("spacey oef")
2322
case _ => println("default")
2423
}
@@ -44,7 +43,7 @@ object Test extends App {
4443
// case 'c' =>
4544
// }
4645

47-
// noSwitch('a', true)
46+
noSwitch('a', true)
4847
onlyThen('a', true) // 'a with oef'
4948
ifThenElse('a', true) // 'a with oef'
5049
ifThenElse('a', false) // 'a'

test/files/run/t6011c.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object Test extends App {
66
// at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:50)
77
// at scala.tools.nsc.Global.abort(Global.scala:249)
88
// at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder$jcode$.emitSWITCH(GenASM.scala:1850)
9-
((1: Byte): @unchecked @annotation.switch) match {
9+
((1: Byte): @unchecked) match {
1010
case 1 => 2
1111
case 1 => 3 // crash
1212
}

0 commit comments

Comments
 (0)