Skip to content

Commit

Permalink
Make fatal warnings not fail compilation early & aggregate warns (#19245
Browse files Browse the repository at this point in the history
)

Final PR. Adds functionality that changes the behaviour of
fatal-warnings - fixes #18634
PR 5/5 (merge consecutively, per [Nicolas'
suggestion](#18829 (review))

Split up version of #18829
  • Loading branch information
szymon-rd authored Jan 31, 2024
2 parents 0859ee9 + 35b0509 commit 62f0324
Show file tree
Hide file tree
Showing 160 changed files with 1,067 additions and 390 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
runCtx.withProgressCallback: cb =>
_progress = Progress(cb, this, fusedPhases.map(_.traversals).sum)
runPhases(allPhases = fusedPhases)(using runCtx)
ctx.reporter.finalizeReporting()
if (!ctx.reporter.hasErrors)
Rewrites.writeBack()
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
Expand Down
13 changes: 6 additions & 7 deletions compiler/src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,19 +172,14 @@ abstract class Reporter extends interfaces.ReporterResult {
end issueUnconfigured

def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit =
def toErrorIfFatal(dia: Diagnostic) = dia match
case w: Warning if ctx.settings.silentWarnings.value => dia
case w: ConditionalWarning if w.isSummarizedConditional => dia
case w: Warning if ctx.settings.XfatalWarnings.value => w.toError
case _ => dia

def go() =
import Action.*
dia match
case w: Warning => WConf.parsed.action(dia) match
case Error => issueUnconfigured(w.toError)
case Warning => issueUnconfigured(toErrorIfFatal(w))
case Verbose => issueUnconfigured(toErrorIfFatal(w.setVerbose()))
case Warning => issueUnconfigured(w)
case Verbose => issueUnconfigured(w.setVerbose())
case Info => issueUnconfigured(w.toInfo)
case Silent =>
case _ => issueUnconfigured(dia)
Expand Down Expand Up @@ -214,6 +209,10 @@ abstract class Reporter extends interfaces.ReporterResult {
def incomplete(dia: Diagnostic)(using Context): Unit =
incompleteHandler(dia, ctx)

def finalizeReporting()(using Context) =
if (hasWarnings && ctx.settings.XfatalWarnings.value)
report(new Error("No warnings can be incurred under -Werror (or -Xfatal-warnings)", NoSourcePosition))

/** Summary of warnings and errors */
def summary: String = {
val b = new mutable.ListBuffer[String]
Expand Down
13 changes: 8 additions & 5 deletions compiler/test-resources/repl/rewrite-messages
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
//> using options -source:future-migration -deprecation -Werror
scala> import scala.util._
-- Error: ----------------------------------------------------------------------
-- Migration Warning: ----------------------------------------------------------
1 | import scala.util._
| ^
| `_` is no longer supported for a wildcard import; use `*` instead

No warnings can be incurred under -Werror (or -Xfatal-warnings)
1 warning found
1 error found
scala> extension (x: Int) def foo(y: Int) = x + y
def foo(x: Int)(y: Int): Int

scala> 2 foo 4
-- Error: ----------------------------------------------------------------------
-- Migration Warning: ----------------------------------------------------------
1 | 2 foo 4
| ^^^
|Alphanumeric method foo is not declared infix; it should not be used as infix operator.
|Instead, use method syntax .foo(...) or backticked identifier `foo`.
1 error found
No warnings can be incurred under -Werror (or -Xfatal-warnings)
1 warning found
1 error found
4 changes: 1 addition & 3 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,8 @@ class CompilationTests {
// initialization tests
@Test def checkInitGlobal: Unit = {
implicit val testGroup: TestGroup = TestGroup("checkInitGlobal")
val options = defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings")
compileFilesInDir("tests/init-global/neg", options, FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyBlacklisted)).checkExpectedErrors()
compileFilesInDir("tests/init-global/warn", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyBlacklisted)).checkWarnings()
compileFilesInDir("tests/init-global/pos", options, FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyBlacklisted)).checkCompile()
compileFilesInDir("tests/init-global/pos", defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyBlacklisted)).checkCompile()
}

// initialization tests
Expand Down
2 changes: 0 additions & 2 deletions tests/init-global/neg/i11262.scala

This file was deleted.

2 changes: 0 additions & 2 deletions tests/init-global/neg/i15883.scala

This file was deleted.

10 changes: 10 additions & 0 deletions tests/init-global/warn/context-sensitivity.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Warning: tests/init-global/warn/context-sensitivity.scala:9:21 ------------------------------------------------------
9 | def foo(): Int = A.m // warn
| ^^^
| Access uninitialized field value m. Calling trace:
| ├── object A: [ context-sensitivity.scala:14 ]
| │ ^
| ├── val m: Int = box1.value.foo() [ context-sensitivity.scala:17 ]
| │ ^^^^^^^^^^^^^^^^
| └── def foo(): Int = A.m // warn [ context-sensitivity.scala:9 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class C(var x: Int) extends Foo {
}

class D(var y: Int) extends Foo {
def foo(): Int = A.m // error
def foo(): Int = A.m // warn
}

class Box(var value: Foo)
Expand All @@ -15,3 +15,4 @@ object A:
val box1: Box = new Box(new C(5))
val box2: Box = new Box(new D(10))
val m: Int = box1.value.foo()

Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
-- Error: tests/init-global/neg/global-cycle1.scala:1:7 ----------------------------------------------------------------
1 |object A { // error
-- Warning: tests/init-global/warn/global-cycle1.scala:1:7 -------------------------------------------------------------
1 |object A { // warn
| ^
| Cyclic initialization: object A -> object B -> object A. Calling trace:
| ├── object A { // error [ global-cycle1.scala:1 ]
| ├── object A { // warn [ global-cycle1.scala:1 ]
| │ ^
| ├── val a: Int = B.b [ global-cycle1.scala:2 ]
| │ ^
| ├── object B { [ global-cycle1.scala:5 ]
| │ ^
| └── val b: Int = A.a // error [ global-cycle1.scala:6 ]
| └── val b: Int = A.a // warn [ global-cycle1.scala:6 ]
| ^
-- Error: tests/init-global/neg/global-cycle1.scala:6:17 ---------------------------------------------------------------
6 | val b: Int = A.a // error
-- Warning: tests/init-global/warn/global-cycle1.scala:6:17 ------------------------------------------------------------
6 | val b: Int = A.a // warn
| ^^^
| Access uninitialized field value a. Calling trace:
| ├── object B { [ global-cycle1.scala:5 ]
| │ ^
| └── val b: Int = A.a // error [ global-cycle1.scala:6 ]
| └── val b: Int = A.a // warn [ global-cycle1.scala:6 ]
| ^^^
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
object A { // error
object A { // warn
val a: Int = B.b
}

object B {
val b: Int = A.a // error
val b: Int = A.a // warn
}

@main
Expand Down
20 changes: 20 additions & 0 deletions tests/init-global/warn/global-cycle14.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- Warning: tests/init-global/warn/global-cycle14.scala:8:7 ------------------------------------------------------------
8 |object A { // warn
| ^
| Cyclic initialization: object A -> object B -> object A. Calling trace:
| ├── object A { // warn [ global-cycle14.scala:8 ]
| │ ^
| ├── val n: Int = B.m [ global-cycle14.scala:9 ]
| │ ^
| ├── object B { [ global-cycle14.scala:12 ]
| │ ^
| └── val m: Int = A.n // warn [ global-cycle14.scala:13 ]
| ^
-- Warning: tests/init-global/warn/global-cycle14.scala:13:17 ----------------------------------------------------------
13 | val m: Int = A.n // warn
| ^^^
| Access uninitialized field value n. Calling trace:
| ├── object B { [ global-cycle14.scala:12 ]
| │ ^
| └── val m: Int = A.n // warn [ global-cycle14.scala:13 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ object O {
val d = Data(3)
}

object A { // error
object A { // warn
val n: Int = B.m
}

object B {
val m: Int = A.n // error
val m: Int = A.n // warn
}
10 changes: 10 additions & 0 deletions tests/init-global/warn/global-cycle2.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Warning: tests/init-global/warn/global-cycle2.scala:6:21 ------------------------------------------------------------
6 | def foo(): Int = A.a * 2 // warn
| ^^^
| Access uninitialized field value a. Calling trace:
| ├── object A { [ global-cycle2.scala:1 ]
| │ ^
| ├── val a: Int = B.foo() [ global-cycle2.scala:2 ]
| │ ^^^^^^^
| └── def foo(): Int = A.a * 2 // warn [ global-cycle2.scala:6 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ object A {
}

object B {
def foo(): Int = A.a * 2 // error
def foo(): Int = A.a * 2 // warn
}
10 changes: 10 additions & 0 deletions tests/init-global/warn/global-cycle3.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Warning: tests/init-global/warn/global-cycle3.scala:2:21 ------------------------------------------------------------
2 | def foo(): Int = B.a + 10 // warn
| ^^^
| Access uninitialized field value a. Calling trace:
| ├── object B { [ global-cycle3.scala:5 ]
| │ ^
| ├── val a: Int = A(4).foo() [ global-cycle3.scala:6 ]
| │ ^^^^^^^^^^
| └── def foo(): Int = B.a + 10 // warn [ global-cycle3.scala:2 ]
| ^^^
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class A(x: Int) {
def foo(): Int = B.a + 10 // error
def foo(): Int = B.a + 10 // warn
}

object B {
Expand Down
10 changes: 10 additions & 0 deletions tests/init-global/warn/global-cycle4.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Warning: tests/init-global/warn/global-cycle4.scala:10:21 -----------------------------------------------------------
10 | def foo(): Int = O.a + 10 // warn
| ^^^
| Access uninitialized field value a. Calling trace:
| ├── object O { [ global-cycle4.scala:17 ]
| │ ^
| ├── val a: Int = D(5).bar().foo() [ global-cycle4.scala:18 ]
| │ ^^^^^^^^^^^^^^^^
| └── def foo(): Int = O.a + 10 // warn [ global-cycle4.scala:10 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class B extends A {
}

class C extends A {
def foo(): Int = O.a + 10 // error
def foo(): Int = O.a + 10 // warn
}

class D(x: Int) {
Expand Down
9 changes: 9 additions & 0 deletions tests/init-global/warn/global-cycle5.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Warning: tests/init-global/warn/global-cycle5.scala:10:17 -----------------------------------------------------------
10 | val b: Int = A.a.foo() // warn
| ^^^
|Reading mutable state of object A during initialization of object B.
|Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. Calling trace:
|├── object B { [ global-cycle5.scala:9 ]
|│ ^
|└── val b: Int = A.a.foo() // warn [ global-cycle5.scala:10 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object A {
}

object B {
val b: Int = A.a.foo() // error
val b: Int = A.a.foo() // warn
}

class Y extends X {
Expand Down
28 changes: 28 additions & 0 deletions tests/init-global/warn/global-cycle6.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Warning: tests/init-global/warn/global-cycle6.scala:1:7 -------------------------------------------------------------
1 |object A { // warn
| ^
| Cyclic initialization: object A -> object B -> object A. Calling trace:
| ├── object A { // warn [ global-cycle6.scala:1 ]
| │ ^
| ├── val n: Int = B.m [ global-cycle6.scala:2 ]
| │ ^
| ├── object B { [ global-cycle6.scala:8 ]
| │ ^
| ├── val a = new A.Inner [ global-cycle6.scala:9 ]
| │ ^^^^^^^^^^^
| ├── class Inner { [ global-cycle6.scala:3 ]
| │ ^
| └── println(n) // warn [ global-cycle6.scala:4 ]
| ^
-- Warning: tests/init-global/warn/global-cycle6.scala:4:12 ------------------------------------------------------------
4 | println(n) // warn
| ^
| Access uninitialized field value n. Calling trace:
| ├── object B { [ global-cycle6.scala:8 ]
| │ ^
| ├── val a = new A.Inner [ global-cycle6.scala:9 ]
| │ ^^^^^^^^^^^
| ├── class Inner { [ global-cycle6.scala:3 ]
| │ ^
| └── println(n) // warn [ global-cycle6.scala:4 ]
| ^
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
object A { // error
object A { // warn
val n: Int = B.m
class Inner {
println(n) // error
println(n) // warn
}
}

Expand Down
20 changes: 20 additions & 0 deletions tests/init-global/warn/global-cycle7.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- Warning: tests/init-global/warn/global-cycle7.scala:1:7 -------------------------------------------------------------
1 |object A { // warn
| ^
| Cyclic initialization: object A -> object B -> object A. Calling trace:
| ├── object A { // warn [ global-cycle7.scala:1 ]
| │ ^
| ├── val n: Int = B.m [ global-cycle7.scala:2 ]
| │ ^
| ├── object B { [ global-cycle7.scala:5 ]
| │ ^
| └── val m: Int = A.n // warn [ global-cycle7.scala:6 ]
| ^
-- Warning: tests/init-global/warn/global-cycle7.scala:6:17 ------------------------------------------------------------
6 | val m: Int = A.n // warn
| ^^^
| Access uninitialized field value n. Calling trace:
| ├── object B { [ global-cycle7.scala:5 ]
| │ ^
| └── val m: Int = A.n // warn [ global-cycle7.scala:6 ]
| ^^^
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
object A { // error
object A { // warn
val n: Int = B.m
}

object B {
val m: Int = A.n // error
val m: Int = A.n // warn
}

abstract class TokensCommon {
Expand Down
16 changes: 16 additions & 0 deletions tests/init-global/warn/global-cycle8.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Warning: tests/init-global/warn/global-cycle8.scala:9:7 -------------------------------------------------------------
9 |object O { // warn
| ^
| Cyclic initialization: object O -> object P -> object O. Calling trace:
| ├── object O { // warn [ global-cycle8.scala:9 ]
| │ ^
| ├── println(P.m) [ global-cycle8.scala:11 ]
| │ ^
| ├── object P { [ global-cycle8.scala:14 ]
| │ ^
| ├── val m = Q.bar(new B) [ global-cycle8.scala:15 ]
| │ ^^^^^^^^^^^^
| ├── def bar(b: B) = b.a.foo() [ global-cycle8.scala:19 ]
| │ ^^^^^^^^^
| └── def foo() = println(O.n) [ global-cycle8.scala:2 ]
| ^
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class B {
val a = new A
}

object O { // error
object O { // warn
val n: Int = 10
println(P.m)
}
Expand Down
9 changes: 9 additions & 0 deletions tests/init-global/warn/global-irrelevance1.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Warning: tests/init-global/warn/global-irrelevance1.scala:5:12 ------------------------------------------------------
5 | var y = A.x * 2 // warn
| ^^^
|Reading mutable state of object A during initialization of object B.
|Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. Calling trace:
|├── object B: [ global-irrelevance1.scala:4 ]
|│ ^
|└── var y = A.x * 2 // warn [ global-irrelevance1.scala:5 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ object A:
var x = 6

object B:
var y = A.x * 2 // error
var y = A.x * 2 // warn
13 changes: 13 additions & 0 deletions tests/init-global/warn/global-irrelevance2.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Warning: tests/init-global/warn/global-irrelevance2.scala:5:6 -------------------------------------------------------
5 | A.x = b * 2 // warn
| ^^^^^^^^^^^^
| Mutating object A during initialization of object B.
| Mutating other static objects during the initialization of one static object is forbidden. Calling trace:
| ├── object B: [ global-irrelevance2.scala:7 ]
| │ ^
| ├── new B(10) [ global-irrelevance2.scala:8 ]
| │ ^^^^^^^^^
| ├── class B(b: Int): [ global-irrelevance2.scala:4 ]
| │ ^
| └── A.x = b * 2 // warn [ global-irrelevance2.scala:5 ]
| ^^^^^^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ object A:
var x = 6

class B(b: Int):
A.x = b * 2 // error
A.x = b * 2 // warn

object B:
new B(10)
Loading

0 comments on commit 62f0324

Please sign in to comment.