Skip to content

Commit

Permalink
Fix messages leaking via suspended messages
Browse files Browse the repository at this point in the history
It isn't clear what StoreReporter's purpose is.  Is it simply a store of
all messages, or is it a store of "real" messages, i.e. messages that
aren't suppressed with `@nowarn` or -Wconf (i.e. configurable warnings)?
I believe StoreReporter is often used as a way to get programmatic
access to the real messages.

Typer, with its TyperState, uses StoreReporter as a more general buffer
while making typing attempts, such as when trying to apply an implicit
conversion.  But with configurable warnings, we don't know if a warning
is real or not until we've typed all the `@nowarn` annotation in the
code, which is why we suspend the messages, on Run.

So we add an override for TyperState, so that StoreReporter is used as
simply a buffer.  When it then unbuffers its messages in flush to the
parent context's reporter, it will run through the regular
"issueIfNotSuppressed" code (assuming it's not another store reporter).
  • Loading branch information
dwijnand committed Nov 8, 2021
1 parent 483bf2c commit 95f0d5d
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 4 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TyperState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class TyperState() {
this

/** A fresh typer state with the same constraint as this one. */
def fresh(reporter: Reporter = StoreReporter(this.reporter),
def fresh(reporter: Reporter = StoreReporter(this.reporter, fromTyperState = true),
committable: Boolean = this.isCommittable): TyperState =
util.Stats.record("TyperState.fresh")
TyperState().init(this, this.constraint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import core.Contexts.Context
import Diagnostic._

/** A re-usable Reporter used in Contexts#test */
class ExploringReporter extends StoreReporter(null):
class ExploringReporter extends StoreReporter(null, fromTyperState = false):
infos = new mutable.ListBuffer[Diagnostic]

override def hasUnreportedErrors: Boolean =
Expand Down
9 changes: 8 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Diagnostic._
* - The reporter is not flushed and the message containers capture a
* `Context` (about 4MB)
*/
class StoreReporter(outer: Reporter = Reporter.NoReporter) extends Reporter {
class StoreReporter(outer: Reporter = Reporter.NoReporter, fromTyperState: Boolean = false) extends Reporter {

protected var infos: mutable.ListBuffer[Diagnostic] = null

Expand All @@ -40,4 +40,11 @@ class StoreReporter(outer: Reporter = Reporter.NoReporter) extends Reporter {
override def pendingMessages(using Context): List[Diagnostic] = if (infos != null) infos.toList else Nil

override def errorsReported: Boolean = hasErrors || (outer != null && outer.errorsReported)

// If this is a TyperState buffering reporter then buffer the messages,
// so that then only when the messages are unbuffered (when the reporter if flushed)
// do they go through -Wconf, and possibly then buffered on the Run as a suspended message
override def report(dia: Diagnostic)(using Context): Unit =
if fromTyperState then doReport(dia)
else super.report(dia)
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/reporting/TestReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import collection.mutable
import Diagnostic._

/** A re-usable Reporter used in Contexts#test */
class TestingReporter extends StoreReporter(null):
class TestingReporter extends StoreReporter(null, fromTyperState = false):
infos = new mutable.ListBuffer[Diagnostic]
override def hasUnreportedErrors: Boolean = infos.exists(_.isInstanceOf[Error])
def reset(): Unit = infos.clear()

0 comments on commit 95f0d5d

Please sign in to comment.