Skip to content

Commit

Permalink
chore: preserve reported messages in MessageLog (#6307)
Browse files Browse the repository at this point in the history
Fixes #4460 (and similar future changes) making prior messages
inaccessible to metaprograms such as linters
  • Loading branch information
Kha authored Dec 11, 2024
1 parent 8709ca3 commit b862e2d
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 27 deletions.
10 changes: 5 additions & 5 deletions src/Lean/CoreM.lean
Original file line number Diff line number Diff line change
Expand Up @@ -612,14 +612,14 @@ instance : MonadRuntimeException CoreM where

/--
Returns `true` if the given message kind has not been reported in the message log,
and then mark it as reported. Otherwise, returns `false`.
We use this API to ensure we don't report the same kind of warning multiple times.
and then mark it as logged. Otherwise, returns `false`.
We use this API to ensure we don't log the same kind of warning multiple times.
-/
def reportMessageKind (kind : Name) : CoreM Bool := do
if (← get).messages.reportedKinds.contains kind then
def logMessageKind (kind : Name) : CoreM Bool := do
if (← get).messages.loggedKinds.contains kind then
return false
else
modify fun s => { s with messages.reportedKinds := s.messages.reportedKinds.insert kind }
modify fun s => { s with messages.loggedKinds := s.messages.loggedKinds.insert kind }
return true

end Lean
42 changes: 21 additions & 21 deletions src/Lean/Message.lean
Original file line number Diff line number Diff line change
Expand Up @@ -391,23 +391,14 @@ end Message
A persistent array of messages.
In the Lean elaborator, we use a fresh message log per command but may also report diagnostics at
various points inside a command, which will empty `unreported` and updated `hadErrors` accordingly
(see `CoreM.getAndEmptyMessageLog`).
various points inside a command, which will empty `unreported` and move its messages to `reported`.
Reported messages are preserved for some specific "lookback" operations such as `hasError` that
should consider the entire message history of the current command; most other functions such as
`add` and `toList` will only operate on unreported messages.
-/
structure MessageLog where
/--
If true, there was an error in the log previously that has already been reported to the user and
removed from the log. Thus we say that in the current context (usually the current command), we
"have errors" if either this flag is set or there is an error in `msgs`; see
`MessageLog.hasErrors`. If we have errors, we suppress some error messages that are often the
result of a previous error.
-/
/-
Design note: We considered introducing a `hasErrors` field instead that already includes the
presence of errors in `msgs` but this would not be compatible with e.g.
`MessageLog.errorsToWarnings`.
-/
hadErrors : Bool := false
/-- The list of messages already reported (i.e. saved in a `Snapshot`), in insertion order. -/
reported : PersistentArray Message := {}
/-- The list of messages not already reported, in insertion order. -/
unreported : PersistentArray Message := {}
/--
Expand All @@ -416,7 +407,7 @@ structure MessageLog where
the configuration option `exponentiation.threshold`.
We don't produce a warning if the kind is already in the following set.
-/
reportedKinds : NameSet := {}
loggedKinds : NameSet := {}
deriving Inhabited

namespace MessageLog
Expand All @@ -426,24 +417,33 @@ def empty : MessageLog := {}
using `MessageLog.toList/toArray`" (since := "2024-05-22")]
def msgs : MessageLog → PersistentArray Message := unreported

def reportedPlusUnreported : MessageLog → PersistentArray Message
| { reported := r, unreported := u, .. } => r ++ u

def hasUnreported (log : MessageLog) : Bool :=
!log.unreported.isEmpty

def add (msg : Message) (log : MessageLog) : MessageLog :=
{ log with unreported := log.unreported.push msg }

protected def append (l₁ l₂ : MessageLog) : MessageLog :=
{ hadErrors := l₁.hadErrors || l₂.hadErrors, unreported := l₁.unreported ++ l₂.unreported }
protected def append (l₁ l₂ : MessageLog) : MessageLog where
reported := l₁.reported ++ l₂.reported
unreported := l₁.unreported ++ l₂.unreported
loggedKinds := l₁.loggedKinds.union l₂.loggedKinds

instance : Append MessageLog :=
⟨MessageLog.append⟩

/--
Checks if either of `reported` or `unreported` contains an error, i.e. whether the current command
has errored yet.
-/
def hasErrors (log : MessageLog) : Bool :=
log.hadErrors || log.unreported.any (·.severity matches .error)
log.reported.any (·.severity matches .error) || log.unreported.any (·.severity matches .error)

/-- Clears unreported messages while preserving `hasErrors`. -/
/-- Moves `unreported` messages to `reported`. -/
def markAllReported (log : MessageLog) : MessageLog :=
{ log with unreported := {}, hadErrors := log.hasErrors }
{ log with unreported := {}, reported := log.reported ++ log.unreported }

def errorsToWarnings (log : MessageLog) : MessageLog :=
{ unreported := log.unreported.map (fun m => match m.severity with | MessageSeverity.error => { m with severity := MessageSeverity.warning } | _ => m) }
Expand Down
2 changes: 1 addition & 1 deletion src/Lean/Util/SafeExponentiation.lean
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This method ensures there is at most one warning message of this kind in the mes
def checkExponent (n : Nat) : CoreM Bool := do
let threshold := exponentiation.threshold.get (← getOptions)
if n > threshold then
if (← reportMessageKind `unsafe.exponentiation) then
if (← logMessageKind `unsafe.exponentiation) then
logWarning s!"exponent {n} exceeds the threshold {threshold}, exponentiation operation was not evaluated, use `set_option {exponentiation.threshold.name} <num>` to set a new threshold"
return false
else
Expand Down

0 comments on commit b862e2d

Please sign in to comment.