Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
-
40681d2c
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
50d5df6d
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
94a10668
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
bb8f91e5
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
98d4c514
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
1eb8f430
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
988521cd
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
8350e6a1
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
9e958e61
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
-
d4c60f39
by Florian Ragwitz at 2026-09-04T18:19:40-07:00
10 changed files:
- + changelog.d/hpc-classinsts
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- + testsuite/tests/hpc/instmeths/instmeths.hs
- + testsuite/tests/hpc/instmeths/instmeths.stdout
- + testsuite/tests/hpc/instmeths/test.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Improve coverage reporting for class instances and their methods
|
|
| 3 | +description:
|
|
| 4 | + |
|
| 5 | + Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
|
|
| 6 | + naturally.
|
|
| 7 | + |
|
| 8 | + Compiler-generated instance methods, such as inherited default methods, no
|
|
| 9 | + longer receive separate top-level coverage boxes. Explicitly implemented
|
|
| 10 | + instance methods retain their own declaration coverage.
|
|
| 11 | + |
|
| 12 | + This means that it is no longer necessary to exercise all of a class' methods
|
|
| 13 | + in order for it to appear "covered".
|
|
| 14 | + |
|
| 15 | + Additionally, top-level coverage boxes are now created for all instances of
|
|
| 16 | + classes with methods, and ticked by the use of *any* of the class' methods.
|
|
| 17 | + This indicates that an instance was in fact used at run-time.
|
|
| 18 | + |
|
| 19 | + Note that classes without any instance methods are currently excluded from
|
|
| 20 | + this, and won't have any coverage boxes created.
|
|
| 21 | + |
|
| 22 | +mrs: !16632
|
|
| 23 | +issues: #17155 |
| ... | ... | @@ -147,6 +147,7 @@ deSugar hsc_env |
| 147 | 147 | tcg_tcs = tcs,
|
| 148 | 148 | tcg_default_exports = defaults,
|
| 149 | 149 | tcg_insts = insts,
|
| 150 | + tcg_inst_meths = inst_meths,
|
|
| 150 | 151 | tcg_fam_insts = fam_insts,
|
| 151 | 152 | tcg_complete_matches = complete_matches,
|
| 152 | 153 | tcg_self_boot = self_boot
|
| ... | ... | @@ -170,7 +171,8 @@ deSugar hsc_env |
| 170 | 171 | (hsc_logger hsc_env)
|
| 171 | 172 | (initTicksConfig (hsc_dflags hsc_env))
|
| 172 | 173 | mod mod_loc
|
| 173 | - export_set (typeEnvTyCons type_env) binds
|
|
| 174 | + export_set (typeEnvTyCons type_env)
|
|
| 175 | + insts inst_meths binds
|
|
| 174 | 176 | else return (binds, Nothing)
|
| 175 | 177 | ; let modBreaks
|
| 176 | 178 | | Just (_, _, breakpointSpecs) <- m_tickInfo
|
| ... | ... | @@ -24,6 +24,8 @@ import GHC.Unit |
| 24 | 24 | |
| 25 | 25 | import GHC.Core.Type
|
| 26 | 26 | import GHC.Core.TyCon
|
| 27 | +import GHC.Core.InstEnv
|
|
| 28 | +import GHC.Core.Class
|
|
| 27 | 29 | |
| 28 | 30 | import GHC.Data.Maybe
|
| 29 | 31 | import GHC.Data.FastString
|
| ... | ... | @@ -100,11 +102,13 @@ addTicksToBinds |
| 100 | 102 | -- isExportedId doesn't work yet (the desugarer
|
| 101 | 103 | -- hasn't set it), so we have to work from this set.
|
| 102 | 104 | -> [TyCon] -- ^ Type constructors in this module
|
| 105 | + -> [ClsInst]
|
|
| 106 | + -> IdEnv DFunId
|
|
| 103 | 107 | -> LHsBinds GhcTc
|
| 104 | 108 | -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
|
| 105 | 109 | |
| 106 | 110 | addTicksToBinds logger cfg
|
| 107 | - mod mod_loc exports tyCons binds
|
|
| 111 | + mod mod_loc exports tyCons insts inst_meths binds
|
|
| 108 | 112 | | let passes = ticks_passes cfg
|
| 109 | 113 | , not (null passes)
|
| 110 | 114 | , Just orig_file <- ml_hs_file mod_loc = do
|
| ... | ... | @@ -129,8 +133,13 @@ addTicksToBinds logger cfg |
| 129 | 133 | , this_mod = mod
|
| 130 | 134 | , tickishType = tickish
|
| 131 | 135 | , recSelBinds = emptyVarEnv
|
| 136 | + , instMeths = inst_meths
|
|
| 137 | + , instTicks = emptyVarEnv
|
|
| 132 | 138 | }
|
| 133 | - (binds',_,st') = unTM (addTickLHsBinds binds) env st
|
|
| 139 | + addTick = do
|
|
| 140 | + instTicks <- allocInstTicks insts
|
|
| 141 | + withEnv (\e -> e{ instTicks }) $ addTickLHsBinds binds
|
|
| 142 | + (binds',_,st') = unTM addTick env st
|
|
| 134 | 143 | in (binds', st')
|
| 135 | 144 | |
| 136 | 145 | (binds1,st) = foldr tickPass (binds, initTTState) passes
|
| ... | ... | @@ -231,7 +240,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc) |
| 231 | 240 | addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
|
| 232 | 241 | , abs_exports = abs_exports
|
| 233 | 242 | }))) =
|
| 234 | - withEnv (add_rec_sels . add_inlines . add_exports) $ do
|
|
| 243 | + withEnv (add_inst_meths . add_rec_sels . add_inlines . add_exports) $ do
|
|
| 235 | 244 | binds' <- addTickLHsBinds binds
|
| 236 | 245 | return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
|
| 237 | 246 | where
|
| ... | ... | @@ -259,19 +268,26 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds |
| 259 | 268 | | ABE{ abe_poly, abe_mono } <- abs_exports
|
| 260 | 269 | , RecSelId{} <- [idDetails abe_poly] ] }
|
| 261 | 270 | |
| 271 | + add_inst_meths env =
|
|
| 272 | + env{ instMeths = instMeths env `extendVarEnvList`
|
|
| 273 | + [ (abe_mono, dfun)
|
|
| 274 | + | ABE{ abe_poly, abe_mono } <- abs_exports
|
|
| 275 | + , Just dfun <- [lookupVarEnv (instMeths env) abe_poly] ] }
|
|
| 276 | + |
|
| 262 | 277 | addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
|
| 263 | 278 | let name = getOccString id
|
| 264 | 279 | decl_path <- getPathEntry
|
| 265 | 280 | density <- getDensity
|
| 281 | + env <- getEnv
|
|
| 266 | 282 | |
| 267 | - inline_ids <- liftM inlines getEnv
|
|
| 268 | 283 | -- See Note [inline sccs]
|
| 269 | 284 | let inline = isInlinePragma (idInlinePragma id)
|
| 270 | - || id `elemVarSet` inline_ids
|
|
| 285 | + || id `elemVarSet` inlines env
|
|
| 271 | 286 | |
| 272 | 287 | -- See Note [inline sccs]
|
| 273 | - tickish <- tickishType `liftM` getEnv
|
|
| 274 | - case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
|
|
| 288 | + case tickishType env of { ProfNotes | inline -> return (L pos funBind); _ -> do
|
|
| 289 | + |
|
| 290 | + instTick <- instMethTick id
|
|
| 275 | 291 | |
| 276 | 292 | -- See Note [Record-selector ticks]
|
| 277 | 293 | selTicks <- recSelTick id
|
| ... | ... | @@ -294,8 +310,12 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches |
| 294 | 310 | toplev = null decl_path
|
| 295 | 311 | exported = idName id `elemNameSet` exported_names
|
| 296 | 312 | |
| 297 | - tick <- if not blackListed &&
|
|
| 298 | - shouldTickBind density toplev exported simple inline
|
|
| 313 | + let generatedInstMeth = id `elemVarEnv` instMeths env
|
|
| 314 | + && isGenerated (mg_origin (mg_ext matches))
|
|
| 315 | + |
|
| 316 | + tick <- if not generatedInstMeth
|
|
| 317 | + && not blackListed
|
|
| 318 | + && shouldTickBind density toplev exported simple inline
|
|
| 299 | 319 | then
|
| 300 | 320 | bindTick density name (locA pos) fvs
|
| 301 | 321 | else
|
| ... | ... | @@ -303,7 +323,8 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches |
| 303 | 323 | |
| 304 | 324 | let mbCons = maybe Prelude.id (:)
|
| 305 | 325 | return $ L pos $ funBind { fun_matches = mg
|
| 306 | - , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
|
|
| 326 | + , fun_ext = second ((instTick `mbCons`) . (tick `mbCons`))
|
|
| 327 | + (fun_ext funBind) }
|
|
| 307 | 328 | } }
|
| 308 | 329 | where
|
| 309 | 330 | -- See Note [Record-selector ticks]
|
| ... | ... | @@ -1098,6 +1119,8 @@ data TickTransEnv = TTE { fileName :: FastString |
| 1098 | 1119 | , this_mod :: Module
|
| 1099 | 1120 | , tickishType :: TickishType
|
| 1100 | 1121 | , recSelBinds :: IdEnv DVarSet
|
| 1122 | + , instMeths :: IdEnv DFunId
|
|
| 1123 | + , instTicks :: IdEnv CoreTickish
|
|
| 1101 | 1124 | }
|
| 1102 | 1125 | |
| 1103 | 1126 | -- deriving Show
|
| ... | ... | @@ -1238,6 +1261,21 @@ isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList |
| 1238 | 1261 | isBlackListed GeneratedSrcSpan{} = return False
|
| 1239 | 1262 | isBlackListed UnhelpfulSpan{} = return False
|
| 1240 | 1263 | |
| 1264 | +allocInstTicks :: [ClsInst] -> TM (IdEnv CoreTickish)
|
|
| 1265 | +allocInstTicks insts =
|
|
| 1266 | + ifDensity TickForCoverage (mkVarEnv . catMaybes <$> mapM alloc insts) (pure emptyVarEnv)
|
|
| 1267 | + where
|
|
| 1268 | + alloc inst
|
|
| 1269 | + | null (classMethods (is_cls inst)) = pure Nothing
|
|
| 1270 | + | otherwise = do
|
|
| 1271 | + let dfun = is_dfun inst
|
|
| 1272 | + fmap (dfun,) <$> allocATickBox (TopLevelBox [getOccString dfun])
|
|
| 1273 | + False True (getSrcSpan dfun) noFVs
|
|
| 1274 | + |
|
| 1275 | +instMethTick :: Id -> TM (Maybe CoreTickish)
|
|
| 1276 | +instMethTick id = getEnv >>= \e ->
|
|
| 1277 | + pure $ lookupVarEnv (instMeths e) id >>= lookupVarEnv (instTicks e)
|
|
| 1278 | + |
|
| 1241 | 1279 | -- the tick application inherits the source position of its
|
| 1242 | 1280 | -- expression argument to support nested box allocations
|
| 1243 | 1281 | allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)
|
| ... | ... | @@ -1770,7 +1770,7 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls, |
| 1770 | 1770 | -- Second pass over class and instance declarations,
|
| 1771 | 1771 | -- now using the kind-checked decls
|
| 1772 | 1772 | traceTc "Tc6" empty ;
|
| 1773 | - inst_binds <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
|
|
| 1773 | + (inst_binds, inst_meths) <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
|
|
| 1774 | 1774 | |
| 1775 | 1775 | -- Foreign exports
|
| 1776 | 1776 | traceTc "Tc7" empty ;
|
| ... | ... | @@ -1802,8 +1802,9 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls, |
| 1802 | 1802 | , tcg_anns = tcg_anns tcg_env ++ annotations
|
| 1803 | 1803 | , tcg_ann_env = extendAnnEnvList (tcg_ann_env tcg_env) annotations
|
| 1804 | 1804 | , tcg_fords = tcg_fords tcg_env ++ foe_decls ++ fi_decls
|
| 1805 | - , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs } } ;
|
|
| 1805 | + , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs
|
|
| 1806 | 1806 | -- tcg_dus: see Note [Newtype constructor usage in foreign declarations]
|
| 1807 | + , tcg_inst_meths = tcg_inst_meths tcg_env `plusVarEnv` inst_meths } } ;
|
|
| 1807 | 1808 | |
| 1808 | 1809 | -- See Note [Newtype constructor usage in foreign declarations]
|
| 1809 | 1810 | addUsedGREs NoDeprecationWarnings (bagToList fo_gres) ;
|
| ... | ... | @@ -1282,7 +1282,7 @@ takes a slightly different approach. |
| 1282 | 1282 | ********************************************************************* -}
|
| 1283 | 1283 | |
| 1284 | 1284 | tcInstDecls2 :: [LTyClDecl GhcRn] -> [InstInfo GhcRn]
|
| 1285 | - -> TcM (LHsBinds GhcTc)
|
|
| 1285 | + -> TcM (LHsBinds GhcTc, IdEnv DFunId)
|
|
| 1286 | 1286 | -- (a) From each class declaration,
|
| 1287 | 1287 | -- generate any default-method bindings
|
| 1288 | 1288 | -- (b) From each instance decl
|
| ... | ... | @@ -1299,11 +1299,11 @@ tcInstDecls2 tycl_decls inst_decls |
| 1299 | 1299 | -- Add the default method Ids (again)
|
| 1300 | 1300 | -- (they were already added in GHC.Tc.TyCl.Utils.tcAddImplicits)
|
| 1301 | 1301 | -- See Note [Default methods in the type environment]
|
| 1302 | - ; inst_binds_s <- tcExtendGlobalValEnv dm_ids $
|
|
| 1303 | - mapM tcInstDecl2 inst_decls
|
|
| 1302 | + ; (inst_binds_s, inst_meths_s) <- unzip <$> (tcExtendGlobalValEnv dm_ids $
|
|
| 1303 | + mapM tcInstDecl2 inst_decls)
|
|
| 1304 | 1304 | |
| 1305 | 1305 | -- Done
|
| 1306 | - ; return (dm_binds ++ concat inst_binds_s) }
|
|
| 1306 | + ; return (dm_binds ++ concat inst_binds_s, plusVarEnvList inst_meths_s) }
|
|
| 1307 | 1307 | |
| 1308 | 1308 | {- Note [Default methods in the type environment]
|
| 1309 | 1309 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -1319,12 +1319,12 @@ So right here in tcInstDecls2 we must re-extend the type envt with |
| 1319 | 1319 | the default method Ids replete with their INLINE pragmas. Urk.
|
| 1320 | 1320 | -}
|
| 1321 | 1321 | |
| 1322 | -tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc)
|
|
| 1322 | +tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc, IdEnv DFunId)
|
|
| 1323 | 1323 | -- Returns a binding for the dfun
|
| 1324 | 1324 | tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
|
| 1325 | - = recoverM (return emptyLHsBinds) $
|
|
| 1326 | - setSrcSpan loc $
|
|
| 1327 | - addErrCtxt (instDeclCtxt2 dfun_ty) $
|
|
| 1325 | + = recoverM (return (emptyLHsBinds, emptyVarEnv)) $
|
|
| 1326 | + setSrcSpan loc $
|
|
| 1327 | + addErrCtxt (instDeclCtxt2 dfun_ty) $
|
|
| 1328 | 1328 | do { -- Instantiate the instance decl with skolem constants
|
| 1329 | 1329 | (skol_info, inst_tyvars, dfun_theta, clas, inst_tys) <- tcSkolDFunType dfun_ty
|
| 1330 | 1330 | ; dfun_ev_vars <- newEvVars dfun_theta
|
| ... | ... | @@ -1342,7 +1342,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds }) |
| 1342 | 1342 | -- See Note [Typechecking plan for instance declarations]
|
| 1343 | 1343 | ; dfun_ev_binds_var <- newTcEvBinds
|
| 1344 | 1344 | ; let dfun_ev_binds = TcEvBinds dfun_ev_binds_var
|
| 1345 | - ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics))
|
|
| 1345 | + ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics, meth_ids))
|
|
| 1346 | 1346 | <- pushTcLevelM $
|
| 1347 | 1347 | do { (sc_ids, sc_binds, sc_implics)
|
| 1348 | 1348 | <- tcSuperClasses skol_info dfun_id clas inst_tyvars
|
| ... | ... | @@ -1356,7 +1356,8 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds }) |
| 1356 | 1356 | |
| 1357 | 1357 | ; return ( sc_ids ++ meth_ids
|
| 1358 | 1358 | , sc_binds ++ meth_binds
|
| 1359 | - , sc_implics `unionBags` meth_implics ) }
|
|
| 1359 | + , sc_implics `unionBags` meth_implics
|
|
| 1360 | + , meth_ids ) }
|
|
| 1360 | 1361 | |
| 1361 | 1362 | ; imp <- newImplication
|
| 1362 | 1363 | ; emitImplication $
|
| ... | ... | @@ -1413,7 +1414,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds }) |
| 1413 | 1414 | , abs_binds = [dict_bind]
|
| 1414 | 1415 | , abs_sig = True }
|
| 1415 | 1416 | |
| 1416 | - ; return (L loc' main_bind : sc_meth_binds)
|
|
| 1417 | + ; return (L loc' main_bind : sc_meth_binds, mkVarEnv $ (,dfun_id) <$> meth_ids)
|
|
| 1417 | 1418 | }
|
| 1418 | 1419 | where
|
| 1419 | 1420 | dfun_id = instanceDFunId ispec
|
| ... | ... | @@ -157,6 +157,7 @@ import GHC.Types.Name.Env |
| 157 | 157 | import GHC.Types.Name.Set
|
| 158 | 158 | import GHC.Types.Avail
|
| 159 | 159 | import GHC.Types.Var
|
| 160 | +import GHC.Types.Var.Env
|
|
| 160 | 161 | import GHC.Types.TypeEnv
|
| 161 | 162 | import GHC.Types.SourceFile
|
| 162 | 163 | import GHC.Types.SrcLoc
|
| ... | ... | @@ -528,6 +529,7 @@ data TcGblEnv |
| 528 | 529 | -- ^ Instance envt for all /home-package/ modules;
|
| 529 | 530 | -- Includes the dfuns in tcg_insts
|
| 530 | 531 | -- NB. BangPattern is to fix a leak, see #15111
|
| 532 | + tcg_inst_meths :: IdEnv DFunId,
|
|
| 531 | 533 | tcg_fam_inst_env :: !FamInstEnv, -- ^ Ditto for family instances
|
| 532 | 534 | -- NB. BangPattern is to fix a leak, see #15111
|
| 533 | 535 | tcg_ann_env :: AnnEnv, -- ^ And for annotations
|
| ... | ... | @@ -409,6 +409,7 @@ initTcGblEnv hsc_env hsc_src keep_rn_syntax mod loc = |
| 409 | 409 | , tcg_knot_vars = hsc_type_env_vars hsc_env
|
| 410 | 410 | , tcg_known_key_maps = known_key_maps_var
|
| 411 | 411 | , tcg_inst_env = emptyInstEnv
|
| 412 | + , tcg_inst_meths = emptyVarEnv
|
|
| 412 | 413 | , tcg_fam_inst_env = emptyFamInstEnv
|
| 413 | 414 | , tcg_ann_env = emptyAnnEnv
|
| 414 | 415 | , tcg_complete_match_env = []
|
| 1 | +import Data.Bifunctor
|
|
| 2 | +import Data.List
|
|
| 3 | +import Data.Maybe
|
|
| 4 | +import Trace.Hpc.Mix
|
|
| 5 | +import Trace.Hpc.Reflect
|
|
| 6 | +import Trace.Hpc.Tix
|
|
| 7 | +import Trace.Hpc.Util
|
|
| 8 | + |
|
| 9 | +class Foo a where
|
|
| 10 | + defMeth, defMeth', reqMeth :: a -> String
|
|
| 11 | + defMeth = const "class default"
|
|
| 12 | + defMeth' = const "class default"
|
|
| 13 | + |
|
| 14 | +newtype T a = T a deriving (Show, Eq, Functor)
|
|
| 15 | +instance Foo (T a) where
|
|
| 16 | + reqMeth = const "Foo (T a) instance"
|
|
| 17 | + |
|
| 18 | +newtype T' a = T' a
|
|
| 19 | + |
|
| 20 | +instance Foo (T' a) where
|
|
| 21 | + reqMeth = const "Foo (T' a) instance"
|
|
| 22 | + defMeth = const "Foo (T' a) instance"
|
|
| 23 | + |
|
| 24 | +-- no method use could "cover" this instance, so we (for now) omit the top-level box for it
|
|
| 25 | +class Marker a
|
|
| 26 | +instance Marker (T Bool)
|
|
| 27 | + |
|
| 28 | +interesting :: (Int, MixEntry) -> Maybe (Int, HpcPos, String)
|
|
| 29 | +interesting (n, (pos, TopLevelBox [name])) | isInteresting name = Just (n, pos, name)
|
|
| 30 | + where isInteresting = (||) <$> isInst <*> isMeth
|
|
| 31 | + isInst = ("$f" `isPrefixOf`)
|
|
| 32 | + isMeth = (||) <$> (`elem` ["(==)", "(/=)", "show", "showList", "showsPrec", "fmap", "(<$)"])
|
|
| 33 | + <*> ("Meth" `isSubsequenceOf`)
|
|
| 34 | +interesting _ = Nothing
|
|
| 35 | + |
|
| 36 | +-- candidate for HPC.Utils, maybe?
|
|
| 37 | +sourceAt :: String -> HpcPos -> String
|
|
| 38 | +sourceAt src pos
|
|
| 39 | + | l1 == l2 = take (c2 - c1 + 1) . drop (c1 - 1) $ ls !! (l1 - 1)
|
|
| 40 | + | otherwise = intercalate "\n" $ first : (middle ++ [last])
|
|
| 41 | + where
|
|
| 42 | + (l1, c1, l2, c2) = fromHpcPos pos
|
|
| 43 | + ls = lines src
|
|
| 44 | + first = drop (c1 - 1) $ ls !! (l1 - 1)
|
|
| 45 | + middle = take (l2 - l1 - 1) $ drop l1 ls
|
|
| 46 | + last = take c2 $ ls !! (l2 - 1)
|
|
| 47 | + |
|
| 48 | +main :: IO ()
|
|
| 49 | +main = do
|
|
| 50 | + print (T 23 == (succ <$> T 22)) -- tick Eq and Functor by using any of their methods
|
|
| 51 | + print (reqMeth (T' 1)) -- tick `Foo (T' a)` and that instance's `reqMeth`
|
|
| 52 | + |
|
| 53 | + Mix source _ _ _ mixEntries <- readMix [".hpc"] (Left "Main")
|
|
| 54 | + src <- readFileUtf8 source
|
|
| 55 | + let boxes = mapMaybe interesting $ zip [0 ..] mixEntries
|
|
| 56 | + Tix [TixModule "Main" _ _ counts] <- examineTix
|
|
| 57 | + mapM_ print [ (counts !! n, path, sourceAt src pos)
|
|
| 58 | + | (n, pos, path) <- sortOn (\(_, _, s) -> s) boxes ] |
| 1 | +True
|
|
| 2 | +"Foo (T' a) instance"
|
|
| 3 | +(1,"$fEqT","Eq")
|
|
| 4 | +(0,"$fFooT","Foo (T a)")
|
|
| 5 | +(1,"$fFooT'","Foo (T' a)")
|
|
| 6 | +(1,"$fFunctorT","Functor")
|
|
| 7 | +(0,"$fShowT","Show")
|
|
| 8 | +(0,"defMeth","defMeth = const \"class default\"")
|
|
| 9 | +(0,"defMeth","defMeth = const \"Foo (T' a) instance\"")
|
|
| 10 | +(0,"defMeth'","defMeth' = const \"class default\"")
|
|
| 11 | +(0,"reqMeth","reqMeth = const \"Foo (T a) instance\"")
|
|
| 12 | +(1,"reqMeth","reqMeth = const \"Foo (T' a) instance\"") |
| 1 | +setTestOpts([omit_ghci, when(fast(), skip), js_skip])
|
|
| 2 | + |
|
| 3 | +test('instmeths',
|
|
| 4 | + [ignore_extension],
|
|
| 5 | + compile_and_run, ['-fhpc']) |