diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 9dbe262b4e81..f8194a0ffb67 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -98,7 +98,7 @@ trait Warnings { |to prevent the shell from expanding patterns.""".stripMargin), prepend = true) - // Non-lint warnings. -- TODO turn into MultiChoiceEnumeration + // Non-lint warnings. val warnMacros = ChoiceSetting( name = "-Wmacros", helpArg = "mode", @@ -117,6 +117,20 @@ trait Warnings { val warnNumericWiden = BooleanSetting("-Wnumeric-widen", "Warn when numerics are widened.") withAbbreviation "-Ywarn-numeric-widen" val warnOctalLiteral = BooleanSetting("-Woctal-literal", "Warn on obsolete octal syntax.") withAbbreviation "-Ywarn-octal-literal" + object PerformanceWarnings extends MultiChoiceEnumeration { + val Captured = Choice("captured", "Modification of var in closure causes boxing.") + val NonlocalReturn = Choice("nonlocal-return", "A return statement used an exception for flow control.") + } + val warnPerformance = MultiChoiceSetting( + name = "-Wperformance", + helpArg = "warning", + descr = "Enable or disable specific lints for performance", + domain = PerformanceWarnings, + default = Some(List("_")) + ) + def warnCaptured = warnPerformance.contains(PerformanceWarnings.Captured) + def warnNonlocalReturn = warnPerformance.contains(PerformanceWarnings.NonlocalReturn) + object UnusedWarnings extends MultiChoiceEnumeration { val Imports = Choice("imports", "Warn if an import selector is not referenced.") val PatVars = Choice("patvars", "Warn if a variable bound in a pattern is unused.") @@ -215,7 +229,6 @@ trait Warnings { def warnStarsAlign = lint contains StarsAlign def warnConstant = lint contains Constant def lintUnused = lint contains Unused - def warnNonlocalReturn = lint contains NonlocalReturn def lintImplicitNotFound = lint contains ImplicitNotFound def warnSerialization = lint contains Serial def lintValPatterns = lint contains ValPattern @@ -239,6 +252,8 @@ trait Warnings { if (s contains Unused) warnUnused.enable(UnusedWarnings.Linted) else warnUnused.disable(UnusedWarnings.Linted) if (s.contains(Deprecation) && deprecation.isDefault) deprecation.value = true + if (s.contains(NonlocalReturn)) warnPerformance.enable(PerformanceWarnings.NonlocalReturn) + else warnPerformance.disable(PerformanceWarnings.NonlocalReturn) } // Backward compatibility. diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 4de4fe1c7b12..53c4870a5ad7 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -505,6 +505,9 @@ abstract class LambdaLift extends InfoTransform { } } + if (settings.warnCaptured) + reporter.warning(tree.pos, s"Modification of variable $name within a closure causes it to be boxed.") + treeCopy.ValDef(tree, mods, name, tpt1, factoryCall) } else tree case Return(Block(stats, value)) => diff --git a/test/files/neg/t10820-warn.scala b/test/files/neg/t10820-warn.scala index a04f62a5443d..97796aaa90d1 100644 --- a/test/files/neg/t10820-warn.scala +++ b/test/files/neg/t10820-warn.scala @@ -1,4 +1,4 @@ -// scalac: -Xfatal-warnings -Xlint:nonlocal-return +// scalac: -Werror -Wperformance // import util.Try diff --git a/test/files/neg/xlint-captured.check b/test/files/neg/xlint-captured.check new file mode 100644 index 000000000000..36584ac470c3 --- /dev/null +++ b/test/files/neg/xlint-captured.check @@ -0,0 +1,9 @@ +xlint-captured.scala:10: warning: return statement uses an exception to pass control to the caller of the enclosing named method f + def f(): Unit = List(42).foreach(i => if (i > 27) return) + ^ +xlint-captured.scala:5: warning: Modification of variable c within a closure causes it to be boxed. + var c = a // nok + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/xlint-captured.scala b/test/files/neg/xlint-captured.scala new file mode 100644 index 000000000000..e4acdf7bcc59 --- /dev/null +++ b/test/files/neg/xlint-captured.scala @@ -0,0 +1,11 @@ +// scalac: -Werror -Wperformance +object Test { + var a, b = 0 // ok + def mkStrangeCounter(): Int => Int = { + var c = a // nok + object _d { var d = b }; import _d._ // ok + e => { c += a; d += b; a *= b; b -= c; c ^ d } + } + + def f(): Unit = List(42).foreach(i => if (i > 27) return) +} diff --git a/test/files/run/nonlocalreturn.check b/test/files/run/nonlocalreturn.check deleted file mode 100644 index aeb2d5e2398d..000000000000 --- a/test/files/run/nonlocalreturn.check +++ /dev/null @@ -1 +0,0 @@ -Some(1) diff --git a/test/files/run/nonlocalreturn.scala b/test/files/run/nonlocalreturn.scala index 399cb0709c6e..0bc4a8d29a85 100644 --- a/test/files/run/nonlocalreturn.scala +++ b/test/files/run/nonlocalreturn.scala @@ -6,9 +6,7 @@ object Test { wrap({ return Some(1) ; None }) } - def main(args: Array[String]): Unit = { - println(f()) - } + def main(args: Array[String]): Unit = assert(f() == Some(1)) } // java.lang.ClassCastException: scala.Some cannot be cast to scala.None$ // at Test$$anonfun$f$1.apply(nonlocalreturn.scala:5) diff --git a/test/files/run/retclosure.check b/test/files/run/retclosure.check deleted file mode 100644 index 94c4971e4a42..000000000000 --- a/test/files/run/retclosure.check +++ /dev/null @@ -1 +0,0 @@ -check failed: some problem diff --git a/test/files/run/retclosure.scala b/test/files/run/retclosure.scala index 18d2aa937727..46e35074141f 100644 --- a/test/files/run/retclosure.scala +++ b/test/files/run/retclosure.scala @@ -17,7 +17,5 @@ object Test { } } - def main(args: Array[String]): Unit = { - Console.println(response) - } + def main(args: Array[String]): Unit = assert(response == "check failed: some problem") } diff --git a/test/files/run/spec-nlreturn.check b/test/files/run/spec-nlreturn.check deleted file mode 100644 index 26cff0736032..000000000000 --- a/test/files/run/spec-nlreturn.check +++ /dev/null @@ -1,2 +0,0 @@ -scala.runtime.NonLocalReturnControl$mcI$sp -16 diff --git a/test/files/run/spec-nlreturn.scala b/test/files/run/spec-nlreturn.scala index 8299901507d8..d9238a67863a 100644 --- a/test/files/run/spec-nlreturn.scala +++ b/test/files/run/spec-nlreturn.scala @@ -1,17 +1,13 @@ //scalac: -Xlint:-nonlocal-return object Test { def f(): Int = { - try { - val g = (1 to 10 map { i => return 16 ; i }).sum - g - } - catch { case x: runtime.NonLocalReturnControl[_] => - println(x.getClass.getName) - x.value.asInstanceOf[Int] + try (1 to 10).map { i => return 16 ; i }.sum + catch { + case x: runtime.NonLocalReturnControl[_] => + assert(x.getClass.getName == "scala.runtime.NonLocalReturnControl$mcI$sp") + x.value.asInstanceOf[Int] } } - def main(args: Array[String]): Unit = { - println(f()) - } + def main(args: Array[String]): Unit = assert(f() == 16) }