Skip to content

Commit 62f0324

Browse files
authored
Make fatal warnings not fail compilation early & aggregate warns (#19245)
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
2 parents 0859ee9 + 35b0509 commit 62f0324

File tree

160 files changed

+1067
-390
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+1067
-390
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
348348
runCtx.withProgressCallback: cb =>
349349
_progress = Progress(cb, this, fusedPhases.map(_.traversals).sum)
350350
runPhases(allPhases = fusedPhases)(using runCtx)
351+
ctx.reporter.finalizeReporting()
351352
if (!ctx.reporter.hasErrors)
352353
Rewrites.writeBack()
353354
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

+6-7
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,14 @@ abstract class Reporter extends interfaces.ReporterResult {
172172
end issueUnconfigured
173173

174174
def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit =
175-
def toErrorIfFatal(dia: Diagnostic) = dia match
176-
case w: Warning if ctx.settings.silentWarnings.value => dia
177-
case w: ConditionalWarning if w.isSummarizedConditional => dia
178-
case w: Warning if ctx.settings.XfatalWarnings.value => w.toError
179-
case _ => dia
180175

181176
def go() =
182177
import Action.*
183178
dia match
184179
case w: Warning => WConf.parsed.action(dia) match
185180
case Error => issueUnconfigured(w.toError)
186-
case Warning => issueUnconfigured(toErrorIfFatal(w))
187-
case Verbose => issueUnconfigured(toErrorIfFatal(w.setVerbose()))
181+
case Warning => issueUnconfigured(w)
182+
case Verbose => issueUnconfigured(w.setVerbose())
188183
case Info => issueUnconfigured(w.toInfo)
189184
case Silent =>
190185
case _ => issueUnconfigured(dia)
@@ -214,6 +209,10 @@ abstract class Reporter extends interfaces.ReporterResult {
214209
def incomplete(dia: Diagnostic)(using Context): Unit =
215210
incompleteHandler(dia, ctx)
216211

212+
def finalizeReporting()(using Context) =
213+
if (hasWarnings && ctx.settings.XfatalWarnings.value)
214+
report(new Error("No warnings can be incurred under -Werror (or -Xfatal-warnings)", NoSourcePosition))
215+
217216
/** Summary of warnings and errors */
218217
def summary: String = {
219218
val b = new mutable.ListBuffer[String]
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
//> using options -source:future-migration -deprecation -Werror
22
scala> import scala.util._
3-
-- Error: ----------------------------------------------------------------------
3+
-- Migration Warning: ----------------------------------------------------------
44
1 | import scala.util._
55
| ^
66
| `_` is no longer supported for a wildcard import; use `*` instead
7-
7+
No warnings can be incurred under -Werror (or -Xfatal-warnings)
8+
1 warning found
9+
1 error found
810
scala> extension (x: Int) def foo(y: Int) = x + y
911
def foo(x: Int)(y: Int): Int
10-
1112
scala> 2 foo 4
12-
-- Error: ----------------------------------------------------------------------
13+
-- Migration Warning: ----------------------------------------------------------
1314
1 | 2 foo 4
1415
| ^^^
1516
|Alphanumeric method foo is not declared infix; it should not be used as infix operator.
1617
|Instead, use method syntax .foo(...) or backticked identifier `foo`.
17-
1 error found
18+
No warnings can be incurred under -Werror (or -Xfatal-warnings)
19+
1 warning found
20+
1 error found

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,8 @@ class CompilationTests {
215215
// initialization tests
216216
@Test def checkInitGlobal: Unit = {
217217
implicit val testGroup: TestGroup = TestGroup("checkInitGlobal")
218-
val options = defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings")
219-
compileFilesInDir("tests/init-global/neg", options, FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyBlacklisted)).checkExpectedErrors()
220218
compileFilesInDir("tests/init-global/warn", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyBlacklisted)).checkWarnings()
221-
compileFilesInDir("tests/init-global/pos", options, FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyBlacklisted)).checkCompile()
219+
compileFilesInDir("tests/init-global/pos", defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyBlacklisted)).checkCompile()
222220
}
223221

224222
// initialization tests

tests/init-global/neg/i11262.scala

-2
This file was deleted.

tests/init-global/neg/i15883.scala

-2
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/init-global/warn/context-sensitivity.scala:9:21 ------------------------------------------------------
2+
9 | def foo(): Int = A.m // warn
3+
| ^^^
4+
| Access uninitialized field value m. Calling trace:
5+
| ├── object A: [ context-sensitivity.scala:14 ]
6+
| │ ^
7+
| ├── val m: Int = box1.value.foo() [ context-sensitivity.scala:17 ]
8+
| │ ^^^^^^^^^^^^^^^^
9+
| └── def foo(): Int = A.m // warn [ context-sensitivity.scala:9 ]
10+
| ^^^

tests/init-global/neg/context-sensitivity.scala renamed to tests/init-global/warn/context-sensitivity.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class C(var x: Int) extends Foo {
66
}
77

88
class D(var y: Int) extends Foo {
9-
def foo(): Int = A.m // error
9+
def foo(): Int = A.m // warn
1010
}
1111

1212
class Box(var value: Foo)
@@ -15,3 +15,4 @@ object A:
1515
val box1: Box = new Box(new C(5))
1616
val box2: Box = new Box(new D(10))
1717
val m: Int = box1.value.foo()
18+
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
-- Error: tests/init-global/neg/global-cycle1.scala:1:7 ----------------------------------------------------------------
2-
1 |object A { // error
1+
-- Warning: tests/init-global/warn/global-cycle1.scala:1:7 -------------------------------------------------------------
2+
1 |object A { // warn
33
| ^
44
| Cyclic initialization: object A -> object B -> object A. Calling trace:
5-
| ├── object A { // error [ global-cycle1.scala:1 ]
5+
| ├── object A { // warn [ global-cycle1.scala:1 ]
66
| │ ^
77
| ├── val a: Int = B.b [ global-cycle1.scala:2 ]
88
| │ ^
99
| ├── object B { [ global-cycle1.scala:5 ]
1010
| │ ^
11-
| └── val b: Int = A.a // error [ global-cycle1.scala:6 ]
11+
| └── val b: Int = A.a // warn [ global-cycle1.scala:6 ]
1212
| ^
13-
-- Error: tests/init-global/neg/global-cycle1.scala:6:17 ---------------------------------------------------------------
14-
6 | val b: Int = A.a // error
13+
-- Warning: tests/init-global/warn/global-cycle1.scala:6:17 ------------------------------------------------------------
14+
6 | val b: Int = A.a // warn
1515
| ^^^
1616
| Access uninitialized field value a. Calling trace:
1717
| ├── object B { [ global-cycle1.scala:5 ]
1818
| │ ^
19-
| └── val b: Int = A.a // error [ global-cycle1.scala:6 ]
19+
| └── val b: Int = A.a // warn [ global-cycle1.scala:6 ]
2020
| ^^^

tests/init-global/neg/global-cycle1.scala renamed to tests/init-global/warn/global-cycle1.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
object A { // error
1+
object A { // warn
22
val a: Int = B.b
33
}
44

55
object B {
6-
val b: Int = A.a // error
6+
val b: Int = A.a // warn
77
}
88

99
@main
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Warning: tests/init-global/warn/global-cycle14.scala:8:7 ------------------------------------------------------------
2+
8 |object A { // warn
3+
| ^
4+
| Cyclic initialization: object A -> object B -> object A. Calling trace:
5+
| ├── object A { // warn [ global-cycle14.scala:8 ]
6+
| │ ^
7+
| ├── val n: Int = B.m [ global-cycle14.scala:9 ]
8+
| │ ^
9+
| ├── object B { [ global-cycle14.scala:12 ]
10+
| │ ^
11+
| └── val m: Int = A.n // warn [ global-cycle14.scala:13 ]
12+
| ^
13+
-- Warning: tests/init-global/warn/global-cycle14.scala:13:17 ----------------------------------------------------------
14+
13 | val m: Int = A.n // warn
15+
| ^^^
16+
| Access uninitialized field value n. Calling trace:
17+
| ├── object B { [ global-cycle14.scala:12 ]
18+
| │ ^
19+
| └── val m: Int = A.n // warn [ global-cycle14.scala:13 ]
20+
| ^^^

tests/init-global/neg/global-cycle14.scala renamed to tests/init-global/warn/global-cycle14.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ object O {
55
val d = Data(3)
66
}
77

8-
object A { // error
8+
object A { // warn
99
val n: Int = B.m
1010
}
1111

1212
object B {
13-
val m: Int = A.n // error
13+
val m: Int = A.n // warn
1414
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/init-global/warn/global-cycle2.scala:6:21 ------------------------------------------------------------
2+
6 | def foo(): Int = A.a * 2 // warn
3+
| ^^^
4+
| Access uninitialized field value a. Calling trace:
5+
| ├── object A { [ global-cycle2.scala:1 ]
6+
| │ ^
7+
| ├── val a: Int = B.foo() [ global-cycle2.scala:2 ]
8+
| │ ^^^^^^^
9+
| └── def foo(): Int = A.a * 2 // warn [ global-cycle2.scala:6 ]
10+
| ^^^

tests/init-global/neg/global-cycle2.scala renamed to tests/init-global/warn/global-cycle2.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ object A {
33
}
44

55
object B {
6-
def foo(): Int = A.a * 2 // error
6+
def foo(): Int = A.a * 2 // warn
77
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/init-global/warn/global-cycle3.scala:2:21 ------------------------------------------------------------
2+
2 | def foo(): Int = B.a + 10 // warn
3+
| ^^^
4+
| Access uninitialized field value a. Calling trace:
5+
| ├── object B { [ global-cycle3.scala:5 ]
6+
| │ ^
7+
| ├── val a: Int = A(4).foo() [ global-cycle3.scala:6 ]
8+
| │ ^^^^^^^^^^
9+
| └── def foo(): Int = B.a + 10 // warn [ global-cycle3.scala:2 ]
10+
| ^^^

tests/init-global/neg/global-cycle3.scala renamed to tests/init-global/warn/global-cycle3.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class A(x: Int) {
2-
def foo(): Int = B.a + 10 // error
2+
def foo(): Int = B.a + 10 // warn
33
}
44

55
object B {
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/init-global/warn/global-cycle4.scala:10:21 -----------------------------------------------------------
2+
10 | def foo(): Int = O.a + 10 // warn
3+
| ^^^
4+
| Access uninitialized field value a. Calling trace:
5+
| ├── object O { [ global-cycle4.scala:17 ]
6+
| │ ^
7+
| ├── val a: Int = D(5).bar().foo() [ global-cycle4.scala:18 ]
8+
| │ ^^^^^^^^^^^^^^^^
9+
| └── def foo(): Int = O.a + 10 // warn [ global-cycle4.scala:10 ]
10+
| ^^^

tests/init-global/neg/global-cycle4.scala renamed to tests/init-global/warn/global-cycle4.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class B extends A {
77
}
88

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

1313
class D(x: Int) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Warning: tests/init-global/warn/global-cycle5.scala:10:17 -----------------------------------------------------------
2+
10 | val b: Int = A.a.foo() // warn
3+
| ^^^
4+
|Reading mutable state of object A during initialization of object B.
5+
|Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. Calling trace:
6+
|├── object B { [ global-cycle5.scala:9 ]
7+
|│ ^
8+
|└── val b: Int = A.a.foo() // warn [ global-cycle5.scala:10 ]
9+
| ^^^

tests/init-global/neg/global-cycle5.scala renamed to tests/init-global/warn/global-cycle5.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object A {
77
}
88

99
object B {
10-
val b: Int = A.a.foo() // error
10+
val b: Int = A.a.foo() // warn
1111
}
1212

1313
class Y extends X {
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Warning: tests/init-global/warn/global-cycle6.scala:1:7 -------------------------------------------------------------
2+
1 |object A { // warn
3+
| ^
4+
| Cyclic initialization: object A -> object B -> object A. Calling trace:
5+
| ├── object A { // warn [ global-cycle6.scala:1 ]
6+
| │ ^
7+
| ├── val n: Int = B.m [ global-cycle6.scala:2 ]
8+
| │ ^
9+
| ├── object B { [ global-cycle6.scala:8 ]
10+
| │ ^
11+
| ├── val a = new A.Inner [ global-cycle6.scala:9 ]
12+
| │ ^^^^^^^^^^^
13+
| ├── class Inner { [ global-cycle6.scala:3 ]
14+
| │ ^
15+
| └── println(n) // warn [ global-cycle6.scala:4 ]
16+
| ^
17+
-- Warning: tests/init-global/warn/global-cycle6.scala:4:12 ------------------------------------------------------------
18+
4 | println(n) // warn
19+
| ^
20+
| Access uninitialized field value n. Calling trace:
21+
| ├── object B { [ global-cycle6.scala:8 ]
22+
| │ ^
23+
| ├── val a = new A.Inner [ global-cycle6.scala:9 ]
24+
| │ ^^^^^^^^^^^
25+
| ├── class Inner { [ global-cycle6.scala:3 ]
26+
| │ ^
27+
| └── println(n) // warn [ global-cycle6.scala:4 ]
28+
| ^

tests/init-global/neg/global-cycle6.scala renamed to tests/init-global/warn/global-cycle6.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
object A { // error
1+
object A { // warn
22
val n: Int = B.m
33
class Inner {
4-
println(n) // error
4+
println(n) // warn
55
}
66
}
77

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Warning: tests/init-global/warn/global-cycle7.scala:1:7 -------------------------------------------------------------
2+
1 |object A { // warn
3+
| ^
4+
| Cyclic initialization: object A -> object B -> object A. Calling trace:
5+
| ├── object A { // warn [ global-cycle7.scala:1 ]
6+
| │ ^
7+
| ├── val n: Int = B.m [ global-cycle7.scala:2 ]
8+
| │ ^
9+
| ├── object B { [ global-cycle7.scala:5 ]
10+
| │ ^
11+
| └── val m: Int = A.n // warn [ global-cycle7.scala:6 ]
12+
| ^
13+
-- Warning: tests/init-global/warn/global-cycle7.scala:6:17 ------------------------------------------------------------
14+
6 | val m: Int = A.n // warn
15+
| ^^^
16+
| Access uninitialized field value n. Calling trace:
17+
| ├── object B { [ global-cycle7.scala:5 ]
18+
| │ ^
19+
| └── val m: Int = A.n // warn [ global-cycle7.scala:6 ]
20+
| ^^^

tests/init-global/neg/global-cycle7.scala renamed to tests/init-global/warn/global-cycle7.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
object A { // error
1+
object A { // warn
22
val n: Int = B.m
33
}
44

55
object B {
6-
val m: Int = A.n // error
6+
val m: Int = A.n // warn
77
}
88

99
abstract class TokensCommon {
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- Warning: tests/init-global/warn/global-cycle8.scala:9:7 -------------------------------------------------------------
2+
9 |object O { // warn
3+
| ^
4+
| Cyclic initialization: object O -> object P -> object O. Calling trace:
5+
| ├── object O { // warn [ global-cycle8.scala:9 ]
6+
| │ ^
7+
| ├── println(P.m) [ global-cycle8.scala:11 ]
8+
| │ ^
9+
| ├── object P { [ global-cycle8.scala:14 ]
10+
| │ ^
11+
| ├── val m = Q.bar(new B) [ global-cycle8.scala:15 ]
12+
| │ ^^^^^^^^^^^^
13+
| ├── def bar(b: B) = b.a.foo() [ global-cycle8.scala:19 ]
14+
| │ ^^^^^^^^^
15+
| └── def foo() = println(O.n) [ global-cycle8.scala:2 ]
16+
| ^

tests/init-global/neg/global-cycle8.scala renamed to tests/init-global/warn/global-cycle8.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class B {
66
val a = new A
77
}
88

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

tests/init-global/neg/global-irrelevance1.scala renamed to tests/init-global/warn/global-irrelevance1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ object A:
22
var x = 6
33

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

tests/init-global/neg/global-irrelevance2.scala renamed to tests/init-global/warn/global-irrelevance2.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object A:
22
var x = 6
33

44
class B(b: Int):
5-
A.x = b * 2 // error
5+
A.x = b * 2 // warn
66

77
object B:
88
new B(10)

0 commit comments

Comments
 (0)