Skip to content

Conversation

@jkeuhlen
Copy link

@jkeuhlen jkeuhlen commented Jul 14, 2025

  • Labeled the PR with patch, minor, or major to request a version bump when it's merged.
  • Updated the user manual in docs/.
  • Added integration / regression tests in tests/.

This PR adds support for a "Track warnings" feature which keeps track of warnings that might disappear due to GHC's recompilation processes:
image

GHC has a behavior that causes "ephemeral warnings" to disappear during development. When a file with warnings is recompiled due to dependency changes (without the file itself
changing), GHC only re-emits warnings for files that were directly modified. This means warnings from unchanged files vanish from the output, making it easy for developers to
lose track of warnings that still need to be addressed.

Example scenario:

  1. ModuleA.hs has an unused import warning
  2. ModuleB.hs (dependency) is modified
  3. GHC recompiles both modules but only shows warnings for ModuleB.hs
  4. The warning in ModuleA.hs disappears from output despite still being present

Solution

This PR introduces a --track-warnings flag that maintains an in-memory store of warnings per file. The system:

  • Captures warnings during compilation and stores them by file path
  • Persists warnings across dependency-driven recompilations when files haven't changed
  • Clears warnings when files are directly modified or removed
  • Prevents duplicates by filtering out warnings already shown in current compilation
  • Integrates with error logs so tracked warnings appear in editor-compatible output files

Usage

Enable warning tracking

ghciwatch --track-warnings

Or via environment variable

GHCIWATCH_TRACK_WARNINGS=1 ghciwatch

The feature is off by default to maintain backward compatibility. When enabled, developers get a complete view of all warnings across their codebase, eliminating the
frustrating experience of warnings disappearing during active development.

@github-actions github-actions bot added the patch Bug fixes or non-functional changes label Jul 14, 2025
if warning_count > 0 {
tracing::info!(
"{} Finished {} in {:.2?} ({} warning{} tracked)",
"Compilation succeeded".if_supports_color(Stdout, |text| text.yellow()),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One big design change here is that I purposefully picked a new phrase + color to signify "Compilation has finished successfully, but there are warnings present." Yellow + the succinct message here (distinct from the normal "All good!" is meant to signify that.

@jkeuhlen jkeuhlen marked this pull request as ready for review July 15, 2025 23:03
// Detect different types of lines and apply appropriate coloring

// Source code lines with line numbers (e.g., " 28 | import Data.Coerce (coerce)")
if let Some(pipe_pos) = line.find(" | ") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kinda hacky, there should at least be a comment here. Ideally we'd integrate this into the parser but I understand that's a more invasive change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah if there was a struct I could plug into with the various pieces of a GHC message, that would be really helpful. We could store original color information there as well.

I'd like to put that off for a follow up (and left one comment-based pointer for that at the top here). Do you think another comment here would suffice for now?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment here.

/// - Files that were directly changed: always update warnings (or clear if none)
/// - Files that were recompiled due to dependencies: only update if warnings exist
/// - Files that weren't recompiled: keep existing warnings
pub fn update_warnings_from_log(&mut self, log: &CompilationLog) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only method that uses current_changed_files, would it make sense to make that variable a parameter to this function instead of a field on the struct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could go either way? Do you feel strongly one way or the other?

My thinking here is that the struct keeps track of the things relevant for the feature: warnings and the files currently being changed (to determine which warnings are displayed). The latter could be thought of as an implementation detail but I wasn't sure if we'd want to surface the file list elsewhere externally too in the future.

@jkeuhlen jkeuhlen requested a review from 9999years July 18, 2025 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Bug fixes or non-functional changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants