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 <noreply@anthropic.com> - - - - - 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 <noreply@anthropic.com> - - - - - 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.