Inconsistent SrcSpan treatment in TH splices
(Spun off from a discussion in !7574 (comment 417656).)
If you compile this program with -ddump-rn-ast
enabled:
{-# LANGUAGE TemplateHaskell #-}
module Bug where
f = $([| True |])
You will see:
==================== Renamer ====================
(Just
((,,,)
(HsGroup
(NoExtField)
(XValBindsLR
(NValBinds
[((,)
(NonRecursive)
{Bag(LocatedA (HsBind Name)):
[(L
(SrcSpanAnn (EpAnn
(Anchor
{ Bug.hs:4:1-17 }
(UnchangedAnchor))
(AnnListItem
[])
(EpaComments
[])) { Bug.hs:4:1-17 })
(FunBind
{NameSet:
[]}
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:1 })
{Name: Bug.f})
(MG
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:1-17 })
[(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:1-17 })
(Match
(EpAnnNotUsed)
(FunRhs
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:1 })
{Name: Bug.f})
(Prefix)
(NoSrcStrict))
[]
(GRHSs
(EpaComments
[])
[(L
(SrcSpanAnn
(EpAnnNotUsed)
{ Bug.hs:4:3-17 })
(GRHS
(EpAnnNotUsed)
[]
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:5-17 })
(HsPar
(EpAnnNotUsed)
(L
(NoTokenLoc)
(HsTok))
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-17 })
(HsSpliceE
(EpAnnNotUsed)
(HsSpliced
(NoExtField)
(ThModFinalizers)
(HsSplicedExpr
(HsVar
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { <no location info> })
{Name: GHC.Types.True}))))))
(L
(NoTokenLoc)
(HsTok))))))]
(EmptyLocalBinds
(NoExtField)))))])
(FromSource))
[]))]})]
[]))
[]
[]
[]
[]
[]
[]
[]
[]
[]
[])
[(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:2:8-10 })
(ImportDecl
(NoExtField)
(NoSourceText)
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:2:8-10 })
{ModuleName: Prelude})
(NoPkgQual)
(NotBoot)
(False)
(NotQualified)
(True)
(Nothing)
(Nothing)))]
(Nothing)
(Nothing)))
The peculiar part is here:
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-17 })
(HsSpliceE
(EpAnnNotUsed)
(HsSpliced
(NoExtField)
(ThModFinalizers)
(HsSplicedExpr
(HsVar
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { <no location info> })
{Name: GHC.Types.True}))))))
This part corresponds to the spliced-in True
expression. Note that the outermost SrcSpan
has a source location of Bug.hs:4:6-17
, as expected. The inner SrcSpan
corresponding to the True
bit, however, has <no location info>
.
On its own, this might seem like a reasonable choice. After all, spliced-in expressions don't really have SrcSpan
s corresponding to the module in which they were spliced, so it would be a perfectly valid choice to just use <no location info>
for all spliced-in subexpressions. However, this is not the choice that GHC makes! Let's change the program slightly:
f = $([| True :: Bool |])
This has the following -ddump-rn-ast
output:
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-25 })
(HsSpliceE
(EpAnnNotUsed)
(HsSpliced
(NoExtField)
(ThModFinalizers)
(HsSplicedExpr
(ExprWithTySig
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-25 })
(HsVar
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { <no location info> })
{Name: GHC.Types.True})))
(HsWC
[]
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-25 })
(HsSig
(NoExtField)
(HsOuterImplicit
[])
(L
(SrcSpanAnn (EpAnnNotUsed) { Bug.hs:4:6-25 })
(HsTyVar
(EpAnnNotUsed)
(NotPromoted)
(L
(SrcSpanAnn (EpAnnNotUsed) { <no location info> })
{Name: GHC.Types.Bool})))))))))))
Notice that some parts of the HsSpliceE
, such as the True
and Bool
parts, have <no location info>
. On the other hand, other parts like the HsWC
, HsSig
, and HsTyVar
do have SrcSpan
s, which are copied from the Bug.hs:4:6-25
used on the outermost HsSpliceE
.
This is oddly inconsistent. TH splicing should consistently apply the same approach to source locations everywhere instead of whatever it is doing currently. Since TH is already threading around SrcSpan
s to use in some places, I propose that it simply use the same SrcSpan
s everywhere. This includes the True
and Bool
parts seen above.