Simon Jakobi pushed to branch wip/sjakobi/multi-caret at Glasgow Haskell Compiler / GHC
Commits:
b72ac939 by Simon Jakobi at 2026-06-10T17:52:09+02:00
Add support for related locations in Diagnostic
Diagnostics can now carry "related locations" -- additional source spans
beyond the primary error span. See the new Note [The source span model for
diagnostics] for the model and its correspondence to the LSP Diagnostic /
relatedInformation interfaces. This prepares the ground for #22637; the
diagnosticRelatedLocations name is inspired by #23414.
Implementation:
* Related spans are carried through the MCDiagnostic message class, the
single source of truth; decorateDiagnostic reads them from there.
* getCaretDiagnostics takes the show-caret flag and returns the real spans
that got no caret, splitting each span into 'Either SDoc RealSrcSpan' via
partitionEithers; decorateDiagnostic lists those under the "At:" heading.
The single-span getCaretDiagnostic lost its only caller and is removed
along with its exports and .hs-boot declaration.
* The renderer neither sorts nor deduplicates: locations are shown exactly
in the order the message author gives them, primary span first. Ordering
related spans deterministically (and not repeating the primary among
them) is the author's responsibility; the construction-site
leftmost_smallest sort is the sole authority on order.
JSON output:
* Emit related spans as a new "relatedSpans" field and bump the JSON schema
version 1.2 -> 1.3 (additive, backwards compatible). A changelog.d entry
records the GHC API changes.
Diagnostics converted:
* TcRnBindingNameConflict, TcRnDuplicateDecls, TcRnDuplicateExport and
TcRnDuplicateNamedDefaultExport carry related locations. The now-redundant
location prose is removed from TcRnBindingNameConflict ("Bound at:") and
TcRnDuplicateDecls ("Declared at:"), and TcRnDuplicateNamedDefaultExport
is reworded (its two export items are always identical). check_occs threads
LIE GhcPs so export items carry their source spans. TcRnConflictingExports
and TcRnDuplicateFieldExport were tried but reverted: their export items
and name provenance already appear in the message text, so carets only
duplicated the primary span.
* To keep multi-location output in source order, addDupDeclErr reports at
the first declaration; the next commit settles on the opposite
(rightmost-primary) convention.
Tests:
* Enable -fdiagnostics-show-caret in several tests to show multi-caret
output, and add MultiCaretFallback exercising a diagnostic whose related
locations are split between carets and the "At:" fallback.
Co-Authored-By: Claude Opus 4.8
- - - - -
4f01ec3c by Simon Jakobi at 2026-06-10T17:52:32+02:00
Diagnostics: adopt a rightmost-primary convention for duplicate diagnostics
When a diagnostic reports the same entity occurring at several sites
(TcRnDuplicateDecls, TcRnBindingNameConflict, TcRnDuplicateExport,
TcRnDuplicateNamedDefaultExport), make the primary span the last
occurrence in source order, with the earlier occurrences as related
locations in ascending order. Duplicates usually arise because the later
occurrence was just added, so the primary span -- the location the user
is sent to, and the one an editor marks -- is the occurrence they most
likely need to act on. This matches other compilers (clang's
"redefinition of 'x' / note: previous definition is here", and similarly
rustc and TypeScript), and it gives the unlabelled carets a reading: the
first caret is the site to fix, the following ones are the prior sites
in source order.
Concretely:
* addDupDeclErr reports at NE.last again (as on master), and dupNamesErr
now reports at the last occurrence rather than the first; the related
locations for both become NE.init. The sortedness invariant is
documented on the TcRnDuplicateDecls and TcRnBindingNameConflict
constructors, since diagnosticRelatedLocations silently relies on it.
* The duplicate-export warnings already comply: they are raised at the
second occurrence with the first as the related location.
* The author-facing guidance -- pick one real location rather than a
synthetic combined span, order related spans deterministically, and
the rightmost-primary convention itself, including a remark on how
rustc-style file-order display could be reintroduced once per-span
labels (#23414) exist -- is split out of Note [The source span model
for diagnostics] into a new Note [Choosing the primary and related
spans]. The emission sites and the diagnosticRelatedLocations
constructor list (now sorted alphabetically to reduce merge conflicts)
reference it.
* A changelog.d entry records the user-facing changes: related
locations, the JSON schema bump, and this convention.
* MultiCaretFallback's phantom file is renamed so that it sorts before
the real file, keeping the phantom span a related location and the
test exercising the "At:" fallback. The expected outputs of the
duplicate tests are updated; the "At:" lists are now in author order
(primary first).
Co-Authored-By: Claude Fable 5
- - - - -
88 changed files:
- + changelog.d/diagnostic-related-locations
- + changelog.d/ghc-api-diagnostic-related-locations
- compiler/GHC/Core/Opt/Monad.hs
- compiler/GHC/Driver/Errors.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Types/Error.hs
- compiler/GHC/Types/Error.hs-boot
- compiler/GHC/Utils/Error.hs
- compiler/GHC/Utils/Logger.hs
- + docs/users_guide/diagnostics-as-json-schema-1_3.json
- docs/users_guide/using.rst
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Exception.hs
- testsuite/tests/default/T25857.stderr
- testsuite/tests/default/all.T
- testsuite/tests/driver/T16167.stdout
- testsuite/tests/driver/json.stderr
- testsuite/tests/driver/json2.stderr
- testsuite/tests/driver/json_dump.stderr
- testsuite/tests/driver/json_warn.stderr
- testsuite/tests/ghci/scripts/T4127a.stderr
- testsuite/tests/ghci/scripts/ghci048.stderr
- testsuite/tests/haddock/haddock_examples/haddock.Test.stderr
- testsuite/tests/indexed-types/should_fail/SimpleFail6.stderr
- testsuite/tests/indexed-types/should_fail/T16110_Fail1.stderr
- testsuite/tests/mdo/should_fail/mdofail002.stderr
- testsuite/tests/mdo/should_fail/mdofail003.stderr
- testsuite/tests/module/mod128.stderr
- testsuite/tests/module/mod18.stderr
- testsuite/tests/module/mod19.stderr
- testsuite/tests/module/mod20.stderr
- testsuite/tests/module/mod21.stderr
- testsuite/tests/module/mod22.stderr
- testsuite/tests/module/mod23.stderr
- testsuite/tests/module/mod24.stderr
- testsuite/tests/module/mod38.stderr
- testsuite/tests/module/mod66.stderr
- testsuite/tests/overloadedrecflds/should_fail/DuplicateExports.stderr
- testsuite/tests/overloadedrecflds/should_fail/FieldSelectors.stderr
- testsuite/tests/overloadedrecflds/should_fail/NFSDuplicate.stderr
- testsuite/tests/overloadedrecflds/should_fail/T17965.stderr
- testsuite/tests/overloadedrecflds/should_fail/all.T
- testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail03.stderr
- testsuite/tests/patsyn/should_compile/T11959.stderr
- testsuite/tests/patsyn/should_compile/T9975a.stderr
- testsuite/tests/patsyn/should_fail/T14114.stderr
- testsuite/tests/patsyn/should_fail/all.T
- testsuite/tests/quasiquotation/qq006/qq006.stderr
- testsuite/tests/rename/should_compile/ExportWarnings6.stderr
- testsuite/tests/rename/should_compile/T23318.stderr
- + testsuite/tests/rename/should_fail/MultiCaretFallback.hs
- + testsuite/tests/rename/should_fail/MultiCaretFallback.stderr
- testsuite/tests/rename/should_fail/T22478b.stderr
- testsuite/tests/rename/should_fail/T7164.stderr
- testsuite/tests/rename/should_fail/all.T
- testsuite/tests/rename/should_fail/rn_dup.stderr
- testsuite/tests/rename/should_fail/rnfail001.stderr
- testsuite/tests/rename/should_fail/rnfail002.stderr
- testsuite/tests/rename/should_fail/rnfail003.stderr
- testsuite/tests/rename/should_fail/rnfail004.stderr
- testsuite/tests/rename/should_fail/rnfail009.stderr
- testsuite/tests/rename/should_fail/rnfail010.stderr
- testsuite/tests/rename/should_fail/rnfail011.stderr
- testsuite/tests/rename/should_fail/rnfail012.stderr
- testsuite/tests/rename/should_fail/rnfail013.stderr
- testsuite/tests/rename/should_fail/rnfail015.stderr
- testsuite/tests/rename/should_fail/rnfail043.stderr
- testsuite/tests/th/T8932.stderr
- testsuite/tests/th/TH_dupdecl.stderr
- testsuite/tests/th/TH_spliceD1.stderr
- testsuite/tests/type-data/should_fail/TDMultiple01.stderr
- testsuite/tests/type-data/should_fail/TDMultiple02.stderr
- testsuite/tests/type-data/should_fail/TDPunning.stderr
- testsuite/tests/typecheck/should_fail/T22560_fail_c.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearMultiAppPat.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearMultiPat.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearSinglePat.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_fail/tcfail038.stderr
- testsuite/tests/vdq-rta/should_fail/T22326_fail_nonlinear.stderr
- testsuite/tests/warnings/should_compile/T25901_exp_dup_wc_3.stderr
- testsuite/tests/warnings/should_compile/T25901_exp_dup_wc_4.stderr
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/672e43e1aa8161d3ad203cf8a2fa324...
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/672e43e1aa8161d3ad203cf8a2fa324...
You're receiving this email because of your account on gitlab.haskell.org.