Simon Jakobi pushed to branch wip/sjakobi/multi-caret at Glasgow Haskell Compiler / GHC
Commits:
a1a2259b by Simon Jakobi at 2026-06-02T03:23:22+02:00
Add related locations to duplicate-declaration and duplicate-export errors
Attach diagnosticRelatedLocations (multi-caret) to:
* TcRnDuplicateDecls
* TcRnDuplicateExport
* TcRnDuplicateNamedDefaultExport
Enable -fdiagnostics-show-caret for the DuplicateExports and T25857
tests to exercise the new caret sections.
check_occs now threads LIE GhcPs so these export items can carry their
source spans. TcRnConflictingExports and TcRnDuplicateFieldExport were
also tried but reverted to IE GhcPs: their export items already appear
in the message text and name provenance is rendered too, so the carets
only duplicated the primary span.
Assisted-by: Claude Opus 4.8
- - - - -
9 changed files:
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- testsuite/tests/default/T25857.stderr
- testsuite/tests/default/all.T
- testsuite/tests/overloadedrecflds/should_fail/DuplicateExports.stderr
- testsuite/tests/overloadedrecflds/should_fail/all.T
- testsuite/tests/rename/should_fail/T7164.stderr
- testsuite/tests/rename/should_fail/all.T
Changes:
=====================================
compiler/GHC/Tc/Errors/Ppr.hs
=====================================
@@ -660,16 +660,16 @@ instance Diagnostic TcRnMessage where
$ formatExportItemError
(ppr export_item)
"attempts to export a default class declaration that is not visible here"
- TcRnDuplicateExport gre ie1 ie2
+ TcRnDuplicateExport gre lie1 lie2
-> mkSimpleDecorated $
hsep [ quotes (ppr $ greName gre)
- , text "is exported by", quotes (ppr ie1)
- , text "and", quotes (ppr ie2) ]
- TcRnDuplicateNamedDefaultExport nm ie1 ie2
- -> mkSimpleDecorated $
- hsep [ text "The named default declaration for" <+> quotes (ppr nm)
- , text "is exported by", quotes (ppr ie1)
- , text "and", quotes (ppr ie2) ]
+ , text "is exported by", quotes (ppr lie1)
+ , text "and", quotes (ppr lie2) ]
+ TcRnDuplicateNamedDefaultExport nm lie1 lie2
+ -> mkSimpleDecorated $
+ hsep [ text "The named default declaration for" <+> quotes (ppr nm)
+ , text "is exported by", quotes (ppr lie1)
+ , text "and", quotes (ppr lie2) ]
TcRnExportedParentChildMismatch parent_name ty_thing child parent_names
-> mkSimpleDecorated $
text "The type constructor" <+> quotes (ppr parent_name)
@@ -3464,6 +3464,12 @@ instance Diagnostic TcRnMessage where
-> diagnosticRelatedLocations msg'
TcRnBindingNameConflict _ locs
-> NE.tail locs
+ TcRnDuplicateDecls _ names
+ -> map nameSrcSpan (NE.init names)
+ TcRnDuplicateExport _ _lie1 lie2
+ -> [getLocA lie2]
+ TcRnDuplicateNamedDefaultExport _ _lie1 lie2
+ -> [getLocA lie2]
_ ->
[]
=====================================
compiler/GHC/Tc/Errors/Types.hs
=====================================
@@ -1607,14 +1607,14 @@ data TcRnMessage where
overloadedrecflds/should_fail/DuplicateExports
patsyn/should_compile/T11959
-}
- TcRnDuplicateExport :: GlobalRdrElt -> IE GhcPs -> IE GhcPs -> TcRnMessage
+ TcRnDuplicateExport :: GlobalRdrElt -> LIE GhcPs -> LIE GhcPs -> TcRnMessage
{-| TcRnDuplicateNamedDefaultExport is a warning (controlled by -Wduplicate-exports)
that occurs when a named default declaration appears in an export list
more than once.
-}
- TcRnDuplicateNamedDefaultExport :: TyCon -> IE GhcPs -> IE GhcPs -> TcRnMessage
+ TcRnDuplicateNamedDefaultExport :: TyCon -> LIE GhcPs -> LIE GhcPs -> TcRnMessage
{-| TcRnExportedParentChildMismatch is an error that occurs when an export is
bundled with a parent that it does not belong to
=====================================
compiler/GHC/Tc/Gen/Export.hs
=====================================
@@ -47,7 +47,7 @@ import GHC.Types.Id.Info
import GHC.Types.Name.Reader
import GHC.Types.Hint
-import Control.Arrow ( first )
+import Control.Arrow ( first, second )
import Control.Monad ( when )
import qualified Data.List.NonEmpty as NE
import Data.Traversable ( for )
@@ -142,7 +142,7 @@ data ExportAccum -- The type of the accumulating parameter of
= ExportAccum {
expacc_exp_occs :: ExportOccMap,
-- ^ Tracks exported occurrence names
- expacc_exp_dflts :: NameEnv (ClassDefaults, IE GhcPs),
+ expacc_exp_dflts :: NameEnv (ClassDefaults, LIE GhcPs),
-- ^ Tracks exported named default declarations
expacc_mods :: UniqMap ModuleName [Name],
-- ^ Tracks (re-)exported module names
@@ -186,7 +186,7 @@ accumExports f xs = do
where f' acc x
= fromMaybe (acc, Nothing) <$> attemptM (f acc x)
-type ExportOccMap = OccEnv (Name, IE GhcPs)
+type ExportOccMap = OccEnv (Name, LIE GhcPs)
-- Tracks what a particular exported OccName
-- in an export list refers to, and which item
-- it came from. It's illegal to export two distinct things
@@ -414,7 +414,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
; traceRn "efa" (ppr mod $$ ppr all_gres)
; addUsedGREs ExportDeprecationWarnings all_gres
- ; occs' <- check_occs occs ie new_gres
+ ; occs' <- check_occs occs (L loc ie) new_gres
-- This check_occs not only finds conflicts
-- between this item and others, but also
-- internally within this item. That is, if
@@ -484,7 +484,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
; traceRn "efa" (ppr mod $$ ppr all_gres)
; addUsedGREs ExportDeprecationWarnings all_gres
- ; occs' <- check_occs occs ie new_gres
+ ; occs' <- check_occs occs (L loc ie) new_gres
-- This check_occs not only finds conflicts
-- between this item and others, but also
-- internally within this item. That is, if
@@ -541,7 +541,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
name = greName gre
checkThLocalNameNoLift (ieLWrappedUserRdrName l name)
- occs' <- check_occs occs ie [gre]
+ occs' <- check_occs occs (L loc ie) [gre]
(export_warn_spans', dont_warn_export', warn_txt_rn)
<- process_warning export_warn_spans
dont_warn_export
@@ -579,14 +579,14 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
Just cls_dflts -> do
let cls = cd_class cls_dflts
case lookupNameEnv exp_dflts (className cls) of
- Just (_, ie') -> do
+ Just (_, lie') -> do
addDiagnostic $
- TcRnDuplicateNamedDefaultExport (classTyCon cls) ie ie'
+ TcRnDuplicateNamedDefaultExport (classTyCon cls) (L loc ie) lie'
return (Nothing, occs, exp_dflts)
Nothing ->
- return $ (Nothing, occs, extendNameEnv exp_dflts (className cls) (cls_dflts, ie))
+ return $ (Nothing, occs, extendNameEnv exp_dflts (className cls) (cls_dflts, L loc ie))
_ -> do
- occs' <- check_occs occs ie [gre]
+ occs' <- check_occs occs (L loc ie) [gre]
return (Just avail, occs', exp_dflts)
checkThLocalNameNoLift (ieLWrappedUserRdrName l name)
@@ -618,7 +618,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
all_names = map greName all_gres
checkThLocalNameNoLift (ieLWrappedUserRdrName l name)
- occs' <- check_occs occs ie all_gres
+ occs' <- check_occs occs (L loc ie) all_gres
(export_warn_spans', dont_warn_export', warn_txt_rn)
<- process_warning export_warn_spans
dont_warn_export
@@ -657,7 +657,7 @@ exports_from_avail (Just (L _ rdr_items)) rdr_env imports this_mod
all_names = map greName all_gres
checkThLocalNameNoLift (ieLWrappedUserRdrName l name)
- occs' <- check_occs occs ie all_gres
+ occs' <- check_occs occs (L loc ie) all_gres
(export_warn_spans', dont_warn_export', warn_txt_rn)
<- process_warning export_warn_spans
dont_warn_export
@@ -1044,12 +1044,13 @@ checkPatSynParent parent NoParent nm
-- | Insert the given 'GlobalRdrElt's into the 'ExportOccMap', checking that
-- each of the given 'GlobalRdrElt's does not appear multiple times in
-- the 'ExportOccMap', as per Note [Exporting duplicate declarations].
-check_occs :: ExportOccMap -> IE GhcPs -> [GlobalRdrElt] -> RnM ExportOccMap
-check_occs occs ie gres
+check_occs :: ExportOccMap -> LIE GhcPs -> [GlobalRdrElt] -> RnM ExportOccMap
+check_occs occs lie gres
-- 'gres' are the entities specified by 'ie'
= do { drf <- xoptM LangExt.DuplicateRecordFields
; foldlM (check drf) occs gres }
where
+ ie = unLoc lie
-- Check for distinct children exported with the same OccName (an error) or
-- for duplicate exports of the same child (a warning).
@@ -1067,20 +1068,21 @@ check_occs occs ie gres
| drf_enabled || not (isFieldOcc child_occ)
-> return occs'
| otherwise
- -> do { let flds = filter (\(_,ie') -> not $ dupFieldExport_ok ie ie')
+ -> do { let flds = filter (\(_,lie') -> not $ dupFieldExport_ok ie (unLoc lie'))
$ lookupFieldsOccEnv occs (occNameFS child_occ)
; case flds of { [] -> return occs'; clash1:clashes ->
- do { addDuplicateFieldExportErr (gre,ie) (clash1 NE.:| clashes)
+ do { addDuplicateFieldExportErr (gre, unLoc lie)
+ (fmap (second unLoc) (clash1 NE.:| clashes))
; return occs } } }
- Left (child', ie')
+ Left (child', lie')
| child == child' -- Duplicate export of a single Name: a warning.
- -> do { warnIf (not (dupExport_ok child ie ie')) (TcRnDuplicateExport gre ie ie')
+ -> do { warnIf (not (dupExport_ok child ie (unLoc lie'))) (TcRnDuplicateExport gre lie lie')
; return occs }
| otherwise -- Same OccName but different Name: an error.
-> do { global_env <- getGlobalRdrEnv
- ; addErr (exportClashErr global_env child' child ie' ie)
+ ; addErr (exportClashErr global_env child' child (unLoc lie') (unLoc lie))
; return occs }
where
child = greName gre
@@ -1088,10 +1090,10 @@ check_occs occs ie gres
-- Try to insert a child into the map, returning Left if there is something
-- already exported with the same OccName.
- try_insert :: ExportOccMap -> GlobalRdrElt -> Either (Name, IE GhcPs) ExportOccMap
+ try_insert :: ExportOccMap -> GlobalRdrElt -> Either (Name, LIE GhcPs) ExportOccMap
try_insert occs child
= case lookupOccEnv occs occ of
- Nothing -> Right (extendOccEnv occs occ (greName child, ie))
+ Nothing -> Right (extendOccEnv occs occ (greName child, lie))
Just x -> Left x
where
occ = greOccName child
=====================================
testsuite/tests/default/T25857.stderr
=====================================
@@ -1,3 +1,9 @@
T25857.hs:4:33: warning: [GHC-31584] [-Wduplicate-exports (in -Wdefault)]
The named default declaration for ‘IsString’ is exported by ‘default IsString’ and ‘default IsString’
+ |
+4 | IsString, default IsString, default IsString
+ | ^^^^^^^^^^^^^^^^
+ |
+4 | IsString, default IsString, default IsString
+ | ^^^^^^^^^^^^^^^^
=====================================
testsuite/tests/default/all.T
=====================================
@@ -32,7 +32,7 @@ test('default-fail07', normal, compile_fail, [''])
test('default-fail08', normal, compile_fail, [''])
test('T25775', normal, compile_fail, [''])
test('T25825', normal, compile, [''])
-test('T25857', normal, compile, [''])
+test('T25857', normal, compile, ['-fdiagnostics-show-caret'])
test('T25206', [extra_files(['T25206_helper.hs'])], multimod_compile, ['T25206', ''])
test('T25858', normal, compile_and_run, [''])
test('T25858v1', [extra_files(['T25858v1_helper.hs'])], multimod_compile_and_run, ['T25858v1', ''])
=====================================
testsuite/tests/overloadedrecflds/should_fail/DuplicateExports.stderr
=====================================
@@ -1,3 +1,9 @@
-
DuplicateExports.hs:6:29: error: [GHC-47854] [-Wduplicate-exports (in -Wdefault), Werror=duplicate-exports]
‘foo’ is exported by ‘foo’ and ‘T(foo, bar)’
+ |
+6 | module Export (T(foo, bar), foo, S(bar)) where
+ | ^^^^^^^^^^^
+ |
+6 | module Export (T(foo, bar), foo, S(bar)) where
+ | ^^^
+
=====================================
testsuite/tests/overloadedrecflds/should_fail/all.T
=====================================
@@ -35,7 +35,7 @@ test('T14953', [extra_files(['T14953_A.hs', 'T14953_B.hs'])],
multimod_compile_fail, ['T14953', ''])
test('T26480', extra_files(['T26480_aux1.hs', 'T26480_aux2.hs']), multimod_compile_fail, ['T26480', '-v0'])
test('T26480b', normal, compile_fail, [''])
-test('DuplicateExports', normal, compile_fail, [''])
+test('DuplicateExports', normal, compile_fail, ['-fdiagnostics-show-caret'])
test('T17420', [extra_files(['T17420A.hs'])], multimod_compile_fail,
['T17420', ''])
test('T17469', [extra_files(['T17469A.hs'])], multimod_compile_fail,
=====================================
testsuite/tests/rename/should_fail/T7164.stderr
=====================================
@@ -1,5 +1,11 @@
-
-T7164.hs:8:1: [GHC-29916]
+T7164.hs:8:1: error: [GHC-29916]
Multiple declarations of ‘derp’
Declared at: T7164.hs:5:5
T7164.hs:8:1
+ |
+5 | derp :: m a
+ | ^^^^^^^^^^^
+ |
+8 | derp = 123
+ | ^^^^
+
=====================================
testsuite/tests/rename/should_fail/all.T
=====================================
@@ -91,7 +91,7 @@ test('T6148a', normal, compile_fail, [''])
test('T6148b', normal, compile_fail, [''])
test('T6148c', normal, compile_fail, [''])
test('T6148d', normal, compile_fail, [''])
-test('T7164', normal, compile_fail, [''])
+test('T7164', normal, compile_fail, ['-fdiagnostics-show-caret'])
test('T7338', normal, compile_fail, [''])
test('T7338a', normal, compile_fail, [''])
test('T7454', normal, compile, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a1a2259b9a4cab30faa471a2060cb9ad...
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a1a2259b9a4cab30faa471a2060cb9ad...
You're receiving this email because of your account on gitlab.haskell.org.