Florian Ragwitz pushed to branch wip/rafl/hpc-viewpatterns at Glasgow Haskell Compiler / GHC
Commits:
-
1abc1855
by Florian Ragwitz at 2026-10-13T19:31:03-07:00
5 changed files:
- + changelog.d/hpc-viewpats
- compiler/GHC/HsToCore/Ticks.hs
- testsuite/tests/hpc/all.T
- + testsuite/tests/hpc/viewpats.hs
- + testsuite/tests/hpc/viewpats.stdout
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Support coverage reporting for view patterns
|
|
| 3 | +issues: #18590
|
|
| 4 | +mrs: !16805
|
|
| 5 | +description:
|
|
| 6 | + Coverage reporting (:ghc-flag:`-fhpc`) now traverses into patterns and
|
|
| 7 | + creates ticks for expressions contained within :extension:`ViewPatterns`.
|
|
| 8 | + |
|
| 9 | + This provides normal coverage semantics for those embedded expressions,
|
|
| 10 | + which were previously entirely ignored in coverage reporting.
|
|
| 11 | + |
|
| 12 | + Since tick insertion is shared by coverage reporting, source notes,
|
|
| 13 | + breakpoints, and profiling, this also allows those other modes to instrument
|
|
| 14 | + expressions inside patterns where applicable. |
| ... | ... | @@ -401,8 +401,9 @@ addTickLHsBind (L pos (pat@(PatBind { pat_lhs = lhs |
| 401 | 401 | -- TODO: better name for rhs's for non-simple patterns?
|
| 402 | 402 | let name = maybe "(...)" getOccString simplePatId
|
| 403 | 403 | |
| 404 | + lhs' <- addTickLPat lhs
|
|
| 404 | 405 | (fvs, rhs') <- getFreeVars $ addPathEntry name $ addTickGRHSs False False False rhs
|
| 405 | - let pat' = pat { pat_rhs = rhs'}
|
|
| 406 | + let pat' = pat { pat_lhs = lhs', pat_rhs = rhs'}
|
|
| 406 | 407 | |
| 407 | 408 | -- Should create ticks here?
|
| 408 | 409 | density <- getDensity
|
| ... | ... | @@ -752,11 +753,12 @@ addTickMatchGroup is_lam mg@(MG { mg_alts = L l matches, mg_ext = ctxt }) = do |
| 752 | 753 | |
| 753 | 754 | addTickMatch :: Bool -> Bool -> Bool {-Is this Do Expansion-} -> Match GhcTc (LHsExpr GhcTc)
|
| 754 | 755 | -> TM (Match GhcTc (LHsExpr GhcTc))
|
| 755 | -addTickMatch isOneOfMany isLambda isDoExp match@(Match { m_pats = L _ pats
|
|
| 756 | +addTickMatch isOneOfMany isLambda isDoExp match@(Match { m_pats = L l pats
|
|
| 756 | 757 | , m_grhss = gRHSs }) =
|
| 757 | 758 | bindLocals pats $ do
|
| 759 | + pats' <- mapM addTickLPat pats
|
|
| 758 | 760 | gRHSs' <- addTickGRHSs isOneOfMany isLambda isDoExp gRHSs
|
| 759 | - return $ match { m_grhss = gRHSs' }
|
|
| 761 | + return $ match { m_pats = L l pats', m_grhss = gRHSs' }
|
|
| 760 | 762 | |
| 761 | 763 | addTickGRHSs :: Bool -> Bool -> Bool -> GRHSs GhcTc (LHsExpr GhcTc)
|
| 762 | 764 | -> TM (GRHSs GhcTc (LHsExpr GhcTc))
|
| ... | ... | @@ -936,9 +938,60 @@ addTickSyntaxExpr pos syn@(SyntaxExprTc { syn_expr = x }) = do |
| 936 | 938 | return $ syn { syn_expr = x' }
|
| 937 | 939 | addTickSyntaxExpr _ NoSyntaxExprTc = return NoSyntaxExprTc
|
| 938 | 940 | |
| 939 | --- we do not walk into patterns.
|
|
| 941 | +-- ViewPat contains an LHsExpr, so we have to walk into patterns.
|
|
| 940 | 942 | addTickLPat :: LPat GhcTc -> TM (LPat GhcTc)
|
| 941 | -addTickLPat pat = return pat
|
|
| 943 | +addTickLPat = traverse addTickPat
|
|
| 944 | + |
|
| 945 | +addTickPat :: Pat GhcTc -> TM (Pat GhcTc)
|
|
| 946 | +addTickPat (ViewPat x expr pat) =
|
|
| 947 | + ViewPat x <$> addTickLHsExpr expr <*> addTickLPat pat
|
|
| 948 | +addTickPat (LazyPat x pat) = LazyPat x <$> addTickLPat pat
|
|
| 949 | +addTickPat (AsPat x var pat) = AsPat x var <$> addTickLPat pat
|
|
| 950 | +addTickPat (ParPat x pat) = ParPat x <$> addTickLPat pat
|
|
| 951 | +addTickPat (BangPat x pat) = BangPat x <$> addTickLPat pat
|
|
| 952 | +addTickPat (SigPat x pat sig) = SigPat x <$> addTickLPat pat <*> pure sig
|
|
| 953 | +addTickPat (ModifiedPat x mods pat) = ModifiedPat x mods <$> addTickLPat pat
|
|
| 954 | +addTickPat (ListPat x pats) = ListPat x <$> mapM addTickLPat pats
|
|
| 955 | +addTickPat (TuplePat x pats boxity) =
|
|
| 956 | + TuplePat x <$> mapM addTickLPat pats <*> pure boxity
|
|
| 957 | +addTickPat (OrPat x pats) = OrPat x <$> mapM addTickLPat pats
|
|
| 958 | +addTickPat (SumPat x pat alt arity) =
|
|
| 959 | + SumPat x <$> addTickLPat pat <*> pure alt <*> pure arity
|
|
| 960 | +addTickPat pat@ConPat{ pat_args } = do
|
|
| 961 | + pat_args' <- addTickConPatDetails pat_args
|
|
| 962 | + pure pat{ pat_args = pat_args' }
|
|
| 963 | + where
|
|
| 964 | + addTickConPatDetails :: HsConPatDetails GhcTc -> TM (HsConPatDetails GhcTc)
|
|
| 965 | + addTickConPatDetails (PrefixCon x pats) =
|
|
| 966 | + PrefixCon x <$> mapM addTickLPat pats
|
|
| 967 | + addTickConPatDetails (InfixCon x pat1 pat2) =
|
|
| 968 | + InfixCon x <$> addTickLPat pat1 <*> addTickLPat pat2
|
|
| 969 | + addTickConPatDetails (RecCon x fields) =
|
|
| 970 | + RecCon x <$> addTickRecFields fields
|
|
| 971 | + where
|
|
| 972 | + addTickRecFields flds@HsRecFields{ rec_flds } = do
|
|
| 973 | + rec_flds' <- mapM (traverse addTickRecField) rec_flds
|
|
| 974 | + pure flds{ rec_flds = rec_flds' }
|
|
| 975 | + addTickRecField fld = do
|
|
| 976 | + rhs' <- addTickLPat (hfbRHS fld)
|
|
| 977 | + pure fld{ hfbRHS = rhs' }
|
|
| 978 | +addTickPat (NPat x lit neg eq) =
|
|
| 979 | + NPat x lit <$> traverse (addTickSyntaxExpr hpcSrcSpan) neg
|
|
| 980 | + <*> addTickSyntaxExpr hpcSrcSpan eq
|
|
| 981 | +addTickPat (NPlusKPat x n k lit ge minus) =
|
|
| 982 | + NPlusKPat x n k lit <$> addTickSyntaxExpr hpcSrcSpan ge
|
|
| 983 | + <*> addTickSyntaxExpr hpcSrcSpan minus
|
|
| 984 | +addTickPat (XPat ext@CoPat{ co_pat_inner }) =
|
|
| 985 | + XPat . (\pat -> ext{ co_pat_inner = pat }) <$> addTickPat co_pat_inner
|
|
| 986 | +addTickPat (XPat (ExpansionPat orig expansion)) =
|
|
| 987 | + XPat . ExpansionPat orig <$> addTickPat expansion
|
|
| 988 | +addTickPat p@WildPat{} = pure p
|
|
| 989 | +addTickPat p@VarPat{} = pure p
|
|
| 990 | +addTickPat p@LitPat{} = pure p
|
|
| 991 | +addTickPat p@QualLitPat{} = pure p
|
|
| 992 | +addTickPat p@SplicePat{} = pure p
|
|
| 993 | +addTickPat p@EmbTyPat{} = pure p
|
|
| 994 | +addTickPat p@InvisPat{} = pure p
|
|
| 942 | 995 | |
| 943 | 996 | addTickHsCmdTop :: HsCmdTop GhcTc -> TM (HsCmdTop GhcTc)
|
| 944 | 997 | addTickHsCmdTop (HsCmdTop x cmd) =
|
| ... | ... | @@ -1012,10 +1065,11 @@ addTickCmdMatchGroup mg@(MG { mg_alts = (L l matches) }) = do |
| 1012 | 1065 | return $ mg { mg_alts = L l matches' }
|
| 1013 | 1066 | |
| 1014 | 1067 | addTickCmdMatch :: Match GhcTc (LHsCmd GhcTc) -> TM (Match GhcTc (LHsCmd GhcTc))
|
| 1015 | -addTickCmdMatch match@(Match { m_pats = L _ pats, m_grhss = gRHSs }) =
|
|
| 1068 | +addTickCmdMatch match@(Match { m_pats = L l pats, m_grhss = gRHSs }) =
|
|
| 1016 | 1069 | bindLocals pats $ do
|
| 1070 | + pats' <- mapM addTickLPat pats
|
|
| 1017 | 1071 | gRHSs' <- addTickCmdGRHSs gRHSs
|
| 1018 | - return $ match { m_grhss = gRHSs' }
|
|
| 1072 | + return $ match { m_pats = L l pats', m_grhss = gRHSs' }
|
|
| 1019 | 1073 | |
| 1020 | 1074 | addTickCmdGRHSs :: GRHSs GhcTc (LHsCmd GhcTc) -> TM (GRHSs GhcTc (LHsCmd GhcTc))
|
| 1021 | 1075 | addTickCmdGRHSs (GRHSs x guarded local_binds) =
|
| ... | ... | @@ -7,6 +7,8 @@ test('T10138', [extra_files(['.keepme.hpc.T10138/']), |
| 7 | 7 | |
| 8 | 8 | test('T11798', normal, makefile_test, [])
|
| 9 | 9 | |
| 10 | +test('viewpats', normal, compile_and_run, ['-fhpc'])
|
|
| 11 | + |
|
| 10 | 12 | # Run tests below only for the hpc way.
|
| 11 | 13 | #
|
| 12 | 14 | # Do not explicitly specify '-fhpc' in extra_hc_opts, unless also setting
|
| 1 | +{-# LANGUAGE ViewPatterns, Modifiers, OrPatterns, UnboxedSums, NPlusKPatterns, TypeFamilies, OverloadedLists, QualifiedStrings, LambdaCase, Arrows #-}
|
|
| 2 | + |
|
| 3 | +module Main where
|
|
| 4 | + |
|
| 5 | +import Control.Exception
|
|
| 6 | +import Data.List
|
|
| 7 | +import Data.Tree
|
|
| 8 | +import Trace.Hpc.Mix
|
|
| 9 | +import Trace.Hpc.Reflect
|
|
| 10 | +import Trace.Hpc.Tix
|
|
| 11 | +import Trace.Hpc.Util
|
|
| 12 | +import qualified Data.String as S
|
|
| 13 | +import Control.Arrow
|
|
| 14 | + |
|
| 15 | +testView :: Int -> Bool
|
|
| 16 | +testView (id -> 1) = True
|
|
| 17 | +testView _ = False
|
|
| 18 | + |
|
| 19 | +testLazyForced :: Int -> Int
|
|
| 20 | +testLazyForced ~(id -> x) = x + 1
|
|
| 21 | + |
|
| 22 | +testLazyUnforced :: Int -> Int
|
|
| 23 | +testLazyUnforced ~(id -> x) = 42
|
|
| 24 | + |
|
| 25 | +testAsCovered :: Int -> Int
|
|
| 26 | +testAsCovered x@(id -> y) = x + y
|
|
| 27 | + |
|
| 28 | +testAsUncovered :: Int -> Int
|
|
| 29 | +testAsUncovered ~(_x@(id -> _y)) = 42
|
|
| 30 | + |
|
| 31 | +testBangCovered :: Int -> Int
|
|
| 32 | +testBangCovered !(id -> x) = x + 1
|
|
| 33 | + |
|
| 34 | +testBangUncovered :: Int -> Int
|
|
| 35 | +testBangUncovered ~(!(id -> x)) = 42
|
|
| 36 | + |
|
| 37 | +testSigCovered :: Int -> Int
|
|
| 38 | +testSigCovered ((id -> x) :: Int) = x + 1
|
|
| 39 | + |
|
| 40 | +testSigUncovered :: Int -> Int
|
|
| 41 | +testSigUncovered ((id -> x) :: Int) = 42
|
|
| 42 | + |
|
| 43 | +testModifiedCovered :: Int -> Int
|
|
| 44 | +testModifiedCovered (%() (id -> x)) = x + 1
|
|
| 45 | + |
|
| 46 | +testModifiedUncovered :: Int -> Int
|
|
| 47 | +testModifiedUncovered (%() (id -> x)) = 42
|
|
| 48 | + |
|
| 49 | +testList :: [Int] -> Int
|
|
| 50 | +testList [id -> x] = x + 1
|
|
| 51 | +testList [] = 42
|
|
| 52 | + |
|
| 53 | +testTuple :: (Int, Int) -> Int
|
|
| 54 | +testTuple (id -> x, id -> _y) = x + 1
|
|
| 55 | + |
|
| 56 | +testOr :: [Int] -> Bool
|
|
| 57 | +testOr (id -> []; reverse -> [_]) = True
|
|
| 58 | +testOr _ = False
|
|
| 59 | + |
|
| 60 | +testSum :: (# Int | Int #) -> Int
|
|
| 61 | +testSum (# (id -> x) | #) = x + 1
|
|
| 62 | +testSum (# | (id -> y) #) = y + 2
|
|
| 63 | + |
|
| 64 | +testNPat :: Int -> Int
|
|
| 65 | +testNPat 1 = 10
|
|
| 66 | +testNPat (-1) = 20
|
|
| 67 | +testNPat _ = 30
|
|
| 68 | + |
|
| 69 | +testNPlusKPat :: Int -> Int
|
|
| 70 | +testNPlusKPat (n+3) = n * 2
|
|
| 71 | +testNPlusKPat _ = 0
|
|
| 72 | + |
|
| 73 | +type family CoList a where
|
|
| 74 | + CoList Bool = [Int]
|
|
| 75 | + |
|
| 76 | +testCoPat :: CoList Bool -> Int
|
|
| 77 | +testCoPat [id -> x] = x + 1
|
|
| 78 | +testCoPat [] = 0
|
|
| 79 | + |
|
| 80 | +testQualifiedString :: String -> Int
|
|
| 81 | +testQualifiedString S."hello" = 1
|
|
| 82 | +testQualifiedString _ = 0
|
|
| 83 | + |
|
| 84 | +testPrefix :: Maybe Int -> Int
|
|
| 85 | +testPrefix (Just (id -> x)) = x + 1
|
|
| 86 | +testPrefix _ = 0
|
|
| 87 | + |
|
| 88 | +testInfix :: [Int] -> Int
|
|
| 89 | +testInfix ((id -> x) : _) = x + 1
|
|
| 90 | +testInfix _ = 0
|
|
| 91 | + |
|
| 92 | +data Record = Record { recVal :: Int }
|
|
| 93 | + |
|
| 94 | +testRec :: Record -> Int
|
|
| 95 | +testRec Record{ recVal = (id -> x) } = x + 1
|
|
| 96 | + |
|
| 97 | +testLambda :: Int -> Int
|
|
| 98 | +testLambda = \(id -> x) -> x + 1
|
|
| 99 | + |
|
| 100 | +testLambdaCase :: Int -> Int
|
|
| 101 | +testLambdaCase = \case
|
|
| 102 | + (id -> 5) -> 1
|
|
| 103 | + _ -> 0
|
|
| 104 | + |
|
| 105 | +testLambdaCases :: Int -> Int -> Int
|
|
| 106 | +testLambdaCases = \cases
|
|
| 107 | + (id -> 5) (id -> y) -> y + 1
|
|
| 108 | + _ _ -> 0
|
|
| 109 | + |
|
| 110 | +testCase :: Int -> Int
|
|
| 111 | +testCase n = case n of
|
|
| 112 | + (id -> 5) -> 1
|
|
| 113 | + _ -> 0
|
|
| 114 | + |
|
| 115 | +testDoExpansion :: Maybe Int -> Maybe Int
|
|
| 116 | +testDoExpansion m = do
|
|
| 117 | + (id -> 5) <- m
|
|
| 118 | + pure 42
|
|
| 119 | + |
|
| 120 | +testPatBindLet :: Int -> Int
|
|
| 121 | +testPatBindLet n =
|
|
| 122 | + let (id -> x) = n
|
|
| 123 | + (id -> _) = undefined
|
|
| 124 | + in x + 1
|
|
| 125 | + |
|
| 126 | +testPatBindWhere :: Int -> Int
|
|
| 127 | +testPatBindWhere n = x + 1
|
|
| 128 | + where (id -> x) = n
|
|
| 129 | + |
|
| 130 | +testPatBindComp :: Int -> Int
|
|
| 131 | +testPatBindComp n =
|
|
| 132 | + head [ x + 1
|
|
| 133 | + | let (id -> x) = n
|
|
| 134 | + , let (id -> _) = undefined ]
|
|
| 135 | + |
|
| 136 | +testPatBindGuard :: Int -> Int
|
|
| 137 | +testPatBindGuard n
|
|
| 138 | + | let (id -> x) = n, x > 0 = x + 1
|
|
| 139 | + | otherwise = 0
|
|
| 140 | + |
|
| 141 | +testPatBindDo :: Int -> Maybe Int
|
|
| 142 | +testPatBindDo n = do
|
|
| 143 | + let (id -> x) = n
|
|
| 144 | + pure (x + 1)
|
|
| 145 | + |
|
| 146 | +testPatBindCmdLet :: Int -> Int
|
|
| 147 | +testPatBindCmdLet = proc n ->
|
|
| 148 | + let (id -> x) = n
|
|
| 149 | + in returnA -< x + 1
|
|
| 150 | + |
|
| 151 | +(id -> testTopValue) = (5 :: Int)
|
|
| 152 | + |
|
| 153 | +testPatBindTopLevel :: Int
|
|
| 154 | +testPatBindTopLevel = testTopValue + 1
|
|
| 155 | + |
|
| 156 | +testCmdLam :: Int -> Int
|
|
| 157 | +testCmdLam = proc n ->
|
|
| 158 | + (\(id -> x) -> returnA -< x + 1) n
|
|
| 159 | + |
|
| 160 | +testCmdCase :: Int -> Int
|
|
| 161 | +testCmdCase = proc n ->
|
|
| 162 | + case n of
|
|
| 163 | + (id -> 5) -> returnA -< 1
|
|
| 164 | + _ -> returnA -< 0
|
|
| 165 | + |
|
| 166 | +testCmdLambdaCase :: Int -> Int
|
|
| 167 | +testCmdLambdaCase = proc n ->
|
|
| 168 | + (\case
|
|
| 169 | + (id -> 5) -> returnA -< 1
|
|
| 170 | + _ -> returnA -< 0) n
|
|
| 171 | + |
|
| 172 | +testCmdLambdaCases :: Int -> Int
|
|
| 173 | +testCmdLambdaCases = proc n ->
|
|
| 174 | + (\cases
|
|
| 175 | + (id -> 5) (id -> y) -> returnA -< y + 1
|
|
| 176 | + _ _ -> returnA -< 0) n n
|
|
| 177 | + |
|
| 178 | +main :: IO ()
|
|
| 179 | +main = do
|
|
| 180 | + evaluate (testView 1)
|
|
| 181 | + evaluate (testLazyForced 5)
|
|
| 182 | + evaluate (testLazyUnforced undefined)
|
|
| 183 | + evaluate (testAsCovered 5)
|
|
| 184 | + evaluate (testAsUncovered undefined)
|
|
| 185 | + evaluate (testBangCovered 5)
|
|
| 186 | + evaluate (testBangUncovered undefined)
|
|
| 187 | + evaluate (testSigCovered 5)
|
|
| 188 | + evaluate (testSigUncovered undefined)
|
|
| 189 | + evaluate (testModifiedCovered 5)
|
|
| 190 | + evaluate (testModifiedUncovered undefined)
|
|
| 191 | + evaluate (testList [5])
|
|
| 192 | + evaluate (testList [])
|
|
| 193 | + evaluate (testTuple (5, undefined))
|
|
| 194 | + evaluate (testOr [])
|
|
| 195 | + evaluate (testOr [5])
|
|
| 196 | + evaluate (testOr [5, 6])
|
|
| 197 | + evaluate (testSum (# 5 | #))
|
|
| 198 | + evaluate (testNPat 1)
|
|
| 199 | + evaluate (testNPat (-1))
|
|
| 200 | + evaluate (testNPlusKPat 5)
|
|
| 201 | + evaluate (testNPlusKPat 2)
|
|
| 202 | + evaluate (testCoPat [5])
|
|
| 203 | + evaluate (testCoPat [])
|
|
| 204 | + evaluate (testQualifiedString "hello")
|
|
| 205 | + evaluate (testQualifiedString "world")
|
|
| 206 | + evaluate (testPrefix (Just 5))
|
|
| 207 | + evaluate (testPrefix Nothing)
|
|
| 208 | + evaluate (testInfix [5])
|
|
| 209 | + evaluate (testInfix [])
|
|
| 210 | + evaluate (testRec (Record 5))
|
|
| 211 | + evaluate (testLambda 5)
|
|
| 212 | + evaluate (testLambdaCase 5)
|
|
| 213 | + evaluate (testLambdaCase 6)
|
|
| 214 | + evaluate (testLambdaCases 5 7)
|
|
| 215 | + evaluate (testLambdaCases 6 7)
|
|
| 216 | + evaluate (testCase 5)
|
|
| 217 | + evaluate (testCase 6)
|
|
| 218 | + evaluate (maybe 0 id (testDoExpansion (Just 5)))
|
|
| 219 | + evaluate (maybe 0 id (testDoExpansion (Just 6)))
|
|
| 220 | + evaluate (maybe 0 id (testDoExpansion Nothing))
|
|
| 221 | + evaluate (testPatBindLet 5)
|
|
| 222 | + evaluate (testPatBindWhere 5)
|
|
| 223 | + evaluate (testPatBindComp 5)
|
|
| 224 | + evaluate (testPatBindGuard 5)
|
|
| 225 | + evaluate (testPatBindGuard (-5))
|
|
| 226 | + evaluate (maybe 0 id (testPatBindDo 5))
|
|
| 227 | + evaluate (testPatBindCmdLet 5)
|
|
| 228 | + evaluate testPatBindTopLevel
|
|
| 229 | + evaluate (testCmdLam 5)
|
|
| 230 | + evaluate (testCmdCase 5)
|
|
| 231 | + evaluate (testCmdCase 6)
|
|
| 232 | + evaluate (testCmdLambdaCase 5)
|
|
| 233 | + evaluate (testCmdLambdaCase 6)
|
|
| 234 | + evaluate (testCmdLambdaCases 5)
|
|
| 235 | + evaluate (testCmdLambdaCases 6)
|
|
| 236 | + dumpCoverage
|
|
| 237 | + |
|
| 238 | +dumpCoverage :: IO ()
|
|
| 239 | +dumpCoverage = do
|
|
| 240 | + Mix path _ _ _ entries <- readMix [".hpc"] (Left "Main")
|
|
| 241 | + Tix [TixModule "Main" _ _ counts] <- examineTix
|
|
| 242 | + src <- readFileUtf8 path
|
|
| 243 | + putStr . compactForest . map (fmap (describe src)) . filter interesting $
|
|
| 244 | + createMixEntryDom [ (pos, (label, count)) | ((pos, label), count) <- zip entries counts ]
|
|
| 245 | + where
|
|
| 246 | + interesting (Node (_, boxes) _) = any (interestingBox . fst) boxes
|
|
| 247 | + interestingBox (TopLevelBox [name]) = "test" `isPrefixOf` name
|
|
| 248 | + interestingBox ExpBox{} = True
|
|
| 249 | + interestingBox _ = False
|
|
| 250 | + describe src (pos, boxes) =
|
|
| 251 | + intercalate ", " [ describeBox label ++ ": " ++ show count | (label, count) <- boxes ]
|
|
| 252 | + ++ if any (hasSource . fst) boxes then " " ++ show (sourceAt src pos) else ""
|
|
| 253 | + hasSource (ExpBox{}; BinBox{}) = True
|
|
| 254 | + hasSource _ = False
|
|
| 255 | + describeBox (TopLevelBox names) = intercalate "." names
|
|
| 256 | + describeBox (ExpBox False) = "exp"
|
|
| 257 | + describeBox (ExpBox True) = "exp-alt"
|
|
| 258 | + describeBox (BinBox kind side) = show kind ++ " " ++ show side
|
|
| 259 | + compactForest = unlines . filter (not . all (`elem` " |")) . lines . drawForest
|
|
| 260 | + |
|
| 261 | +-- candidate for HPC.Utils, maybe?
|
|
| 262 | +sourceAt :: String -> HpcPos -> String
|
|
| 263 | +sourceAt src pos
|
|
| 264 | + | l1 == l2 = take (c2 - c1 + 1) . drop (c1 - 1) $ ls !! (l1 - 1)
|
|
| 265 | + | otherwise = intercalate "\n" $ first : (middle ++ [last])
|
|
| 266 | + where
|
|
| 267 | + (l1, c1, l2, c2) = fromHpcPos pos
|
|
| 268 | + ls = lines src
|
|
| 269 | + first = drop (c1 - 1) $ ls !! (l1 - 1)
|
|
| 270 | + middle = take (l2 - l1 - 1) $ drop l1 ls
|
|
| 271 | + last = take c2 $ ls !! (l2 - 1) |
| 1 | +testCmdLambdaCases: 1
|
|
| 2 | +`- exp: 1 "proc n ->\n (\\cases\n (id -> 5) (id -> y) -> returnA -< y + 1\n _ _ -> returnA -< 0) n n"
|
|
| 3 | + +- exp: 2 "id"
|
|
| 4 | + +- exp: 1 "id"
|
|
| 5 | + +- exp: 1 "returnA"
|
|
| 6 | + +- exp: 1 "y + 1"
|
|
| 7 | + | +- exp: 1 "y"
|
|
| 8 | + | `- exp: 1 "1"
|
|
| 9 | + +- exp: 1 "returnA"
|
|
| 10 | + +- exp: 1 "0"
|
|
| 11 | + +- exp: 2 "n"
|
|
| 12 | + `- exp: 1 "n"
|
|
| 13 | +testCmdLambdaCase: 1
|
|
| 14 | +`- exp: 1 "proc n ->\n (\\case\n (id -> 5) -> returnA -< 1\n _ -> returnA -< 0) n"
|
|
| 15 | + +- exp: 2 "id"
|
|
| 16 | + +- exp: 1 "returnA"
|
|
| 17 | + +- exp: 1 "1"
|
|
| 18 | + +- exp: 1 "returnA"
|
|
| 19 | + +- exp: 1 "0"
|
|
| 20 | + `- exp: 2 "n"
|
|
| 21 | +testCmdCase: 1
|
|
| 22 | +`- exp: 1 "proc n ->\n case n of\n (id -> 5) -> returnA -< 1\n _ -> returnA -< 0"
|
|
| 23 | + +- exp: 2 "n"
|
|
| 24 | + +- exp: 2 "id"
|
|
| 25 | + +- exp: 1 "returnA"
|
|
| 26 | + +- exp: 1 "1"
|
|
| 27 | + +- exp: 1 "returnA"
|
|
| 28 | + `- exp: 1 "0"
|
|
| 29 | +testCmdLam: 1
|
|
| 30 | +`- exp: 1 "proc n ->\n (\\(id -> x) -> returnA -< x + 1) n"
|
|
| 31 | + +- exp: 1 "id"
|
|
| 32 | + +- exp: 1 "returnA"
|
|
| 33 | + +- exp: 1 "x + 1"
|
|
| 34 | + | +- exp: 1 "x"
|
|
| 35 | + | `- exp: 1 "1"
|
|
| 36 | + `- exp: 1 "n"
|
|
| 37 | +exp: 1 "id"
|
|
| 38 | +exp: 1 "(5 :: Int)"
|
|
| 39 | +testPatBindTopLevel: 1
|
|
| 40 | +`- exp: 1 "testTopValue + 1"
|
|
| 41 | + +- exp: 1 "testTopValue"
|
|
| 42 | + `- exp: 1 "1"
|
|
| 43 | +testPatBindCmdLet: 1
|
|
| 44 | +`- exp: 1 "proc n ->\n let (id -> x) = n\n in returnA -< x + 1"
|
|
| 45 | + +- exp: 1 "id"
|
|
| 46 | + +- exp: 1 "n"
|
|
| 47 | + +- exp: 1 "returnA"
|
|
| 48 | + `- exp: 1 "x + 1"
|
|
| 49 | + +- exp: 1 "x"
|
|
| 50 | + `- exp: 1 "1"
|
|
| 51 | +testPatBindDo: 1
|
|
| 52 | +`- exp: 1 "do\n let (id -> x) = n\n pure (x + 1)"
|
|
| 53 | + +- exp: 1 "pure (x + 1)"
|
|
| 54 | + | `- exp: 1 "(x + 1)"
|
|
| 55 | + | +- exp: 1 "x"
|
|
| 56 | + | `- exp: 1 "1"
|
|
| 57 | + `- exp: 1 "let (id -> x) = n"
|
|
| 58 | + +- exp: 1 "id"
|
|
| 59 | + `- exp: 1 "n"
|
|
| 60 | +testPatBindGuard: 2
|
|
| 61 | ++- exp: 2 "id"
|
|
| 62 | ++- exp: 2 "n"
|
|
| 63 | ++- GuardBinBox True: 1, GuardBinBox False: 1, exp: 2 "x > 0"
|
|
| 64 | +| +- exp: 2 "x"
|
|
| 65 | +| `- exp: 2 "0"
|
|
| 66 | ++- exp-alt: 1 "x + 1"
|
|
| 67 | +| +- exp: 1 "x"
|
|
| 68 | +| `- exp: 1 "1"
|
|
| 69 | ++- GuardBinBox True: 1, GuardBinBox False: 0, exp: 1 "otherwise"
|
|
| 70 | +`- exp-alt: 1 "0"
|
|
| 71 | +testPatBindComp: 1
|
|
| 72 | +`- exp: 1 "head [ x + 1\n | let (id -> x) = n\n , let (id -> _) = undefined ]"
|
|
| 73 | + `- exp: 1 "[ x + 1\n | let (id -> x) = n\n , let (id -> _) = undefined ]"
|
|
| 74 | + +- exp: 1 "id"
|
|
| 75 | + +- exp: 1 "n"
|
|
| 76 | + +- exp: 0 "id"
|
|
| 77 | + +- exp: 0 "undefined"
|
|
| 78 | + `- exp: 1 "x + 1"
|
|
| 79 | + +- exp: 1 "x"
|
|
| 80 | + `- exp: 1 "1"
|
|
| 81 | +testPatBindWhere: 1
|
|
| 82 | ++- exp: 1 "id"
|
|
| 83 | ++- exp: 1 "n"
|
|
| 84 | +`- exp: 1 "x + 1"
|
|
| 85 | + +- exp: 1 "x"
|
|
| 86 | + `- exp: 1 "1"
|
|
| 87 | +testPatBindLet: 1
|
|
| 88 | +`- exp: 1 "let (id -> x) = n\n (id -> _) = undefined\n in x + 1"
|
|
| 89 | + +- exp: 0 "id"
|
|
| 90 | + +- exp: 0 "undefined"
|
|
| 91 | + +- exp: 1 "id"
|
|
| 92 | + +- exp: 1 "n"
|
|
| 93 | + +- exp: 1 "x"
|
|
| 94 | + `- exp: 1 "1"
|
|
| 95 | +testDoExpansion: 3
|
|
| 96 | +`- exp: 3 "do\n (id -> 5) <- m\n pure 42"
|
|
| 97 | + +- exp: 1 "pure 42"
|
|
| 98 | + | `- exp: 1 "42"
|
|
| 99 | + `- exp: 3 "(id -> 5) <- m"
|
|
| 100 | + +- exp: 3 "m"
|
|
| 101 | + `- exp: 2 "id"
|
|
| 102 | +testCase: 2
|
|
| 103 | +`- exp: 2 "case n of\n (id -> 5) -> 1\n _ -> 0"
|
|
| 104 | + +- exp: 2 "n"
|
|
| 105 | + +- exp: 2 "id"
|
|
| 106 | + +- exp-alt: 1 "1"
|
|
| 107 | + `- exp-alt: 1 "0"
|
|
| 108 | +testLambdaCases: 1
|
|
| 109 | +`- exp: 1 "\\cases\n (id -> 5) (id -> y) -> y + 1\n _ _ -> 0"
|
|
| 110 | + +- exp: 2 "id"
|
|
| 111 | + +- exp: 1 "id"
|
|
| 112 | + +- exp-alt: 1 "y + 1"
|
|
| 113 | + | +- exp: 1 "y"
|
|
| 114 | + | `- exp: 1 "1"
|
|
| 115 | + `- exp-alt: 1 "0"
|
|
| 116 | +testLambdaCase: 1
|
|
| 117 | +`- exp: 1 "\\case\n (id -> 5) -> 1\n _ -> 0"
|
|
| 118 | + +- exp: 2 "id"
|
|
| 119 | + +- exp-alt: 1 "1"
|
|
| 120 | + `- exp-alt: 1 "0"
|
|
| 121 | +testLambda: 1
|
|
| 122 | +`- exp: 1 "\\(id -> x) -> x + 1"
|
|
| 123 | + +- exp: 1 "id"
|
|
| 124 | + `- exp: 1 "x + 1"
|
|
| 125 | + +- exp: 1 "x"
|
|
| 126 | + `- exp: 1 "1"
|
|
| 127 | +testRec: 1
|
|
| 128 | ++- exp: 1 "id"
|
|
| 129 | +`- exp: 1 "x + 1"
|
|
| 130 | + +- exp: 1 "x"
|
|
| 131 | + `- exp: 1 "1"
|
|
| 132 | +testInfix: 2
|
|
| 133 | ++- exp: 1 "id"
|
|
| 134 | ++- exp-alt: 1 "x + 1"
|
|
| 135 | +| +- exp: 1 "x"
|
|
| 136 | +| `- exp: 1 "1"
|
|
| 137 | +`- exp-alt: 1 "0"
|
|
| 138 | +testPrefix: 2
|
|
| 139 | ++- exp: 1 "id"
|
|
| 140 | ++- exp-alt: 1 "x + 1"
|
|
| 141 | +| +- exp: 1 "x"
|
|
| 142 | +| `- exp: 1 "1"
|
|
| 143 | +`- exp-alt: 1 "0"
|
|
| 144 | +testQualifiedString: 2
|
|
| 145 | ++- exp-alt: 1 "1"
|
|
| 146 | +`- exp-alt: 1 "0"
|
|
| 147 | +testCoPat: 2
|
|
| 148 | ++- exp: 1 "id"
|
|
| 149 | ++- exp-alt: 1 "x + 1"
|
|
| 150 | +| +- exp: 1 "x"
|
|
| 151 | +| `- exp: 1 "1"
|
|
| 152 | +`- exp-alt: 1 "0"
|
|
| 153 | +testNPlusKPat: 2
|
|
| 154 | ++- exp-alt: 1 "n * 2"
|
|
| 155 | +| +- exp: 1 "n"
|
|
| 156 | +| `- exp: 1 "2"
|
|
| 157 | +`- exp-alt: 1 "0"
|
|
| 158 | +testNPat: 2
|
|
| 159 | ++- exp-alt: 1 "10"
|
|
| 160 | ++- exp-alt: 1 "20"
|
|
| 161 | +`- exp-alt: 0 "30"
|
|
| 162 | +testSum: 1
|
|
| 163 | ++- exp: 1 "id"
|
|
| 164 | ++- exp-alt: 1 "x + 1"
|
|
| 165 | +| +- exp: 1 "x"
|
|
| 166 | +| `- exp: 1 "1"
|
|
| 167 | ++- exp: 0 "id"
|
|
| 168 | +`- exp-alt: 0 "y + 2"
|
|
| 169 | + +- exp: 0 "y"
|
|
| 170 | + `- exp: 0 "2"
|
|
| 171 | +testOr: 3
|
|
| 172 | ++- exp: 3 "id"
|
|
| 173 | ++- exp: 2 "reverse"
|
|
| 174 | ++- exp-alt: 2 "True"
|
|
| 175 | +`- exp-alt: 1 "False"
|
|
| 176 | +testTuple: 1
|
|
| 177 | ++- exp: 1 "id"
|
|
| 178 | ++- exp: 0 "id"
|
|
| 179 | +`- exp: 1 "x + 1"
|
|
| 180 | + +- exp: 1 "x"
|
|
| 181 | + `- exp: 1 "1"
|
|
| 182 | +testList: 2
|
|
| 183 | ++- exp: 1 "id"
|
|
| 184 | ++- exp-alt: 1 "x + 1"
|
|
| 185 | +| +- exp: 1 "x"
|
|
| 186 | +| `- exp: 1 "1"
|
|
| 187 | +`- exp-alt: 1 "42"
|
|
| 188 | +testModifiedUncovered: 1
|
|
| 189 | ++- exp: 0 "id"
|
|
| 190 | +`- exp: 1 "42"
|
|
| 191 | +testModifiedCovered: 1
|
|
| 192 | ++- exp: 1 "id"
|
|
| 193 | +`- exp: 1 "x + 1"
|
|
| 194 | + +- exp: 1 "x"
|
|
| 195 | + `- exp: 1 "1"
|
|
| 196 | +testSigUncovered: 1
|
|
| 197 | ++- exp: 0 "id"
|
|
| 198 | +`- exp: 1 "42"
|
|
| 199 | +testSigCovered: 1
|
|
| 200 | ++- exp: 1 "id"
|
|
| 201 | +`- exp: 1 "x + 1"
|
|
| 202 | + +- exp: 1 "x"
|
|
| 203 | + `- exp: 1 "1"
|
|
| 204 | +testBangUncovered: 1
|
|
| 205 | ++- exp: 0 "id"
|
|
| 206 | +`- exp: 1 "42"
|
|
| 207 | +testBangCovered: 1
|
|
| 208 | ++- exp: 1 "id"
|
|
| 209 | +`- exp: 1 "x + 1"
|
|
| 210 | + +- exp: 1 "x"
|
|
| 211 | + `- exp: 1 "1"
|
|
| 212 | +testAsUncovered: 1
|
|
| 213 | ++- exp: 0 "id"
|
|
| 214 | +`- exp: 1 "42"
|
|
| 215 | +testAsCovered: 1
|
|
| 216 | ++- exp: 1 "id"
|
|
| 217 | +`- exp: 1 "x + y"
|
|
| 218 | + +- exp: 1 "x"
|
|
| 219 | + `- exp: 1 "y"
|
|
| 220 | +testLazyUnforced: 1
|
|
| 221 | ++- exp: 0 "id"
|
|
| 222 | +`- exp: 1 "42"
|
|
| 223 | +testLazyForced: 1
|
|
| 224 | ++- exp: 1 "id"
|
|
| 225 | +`- exp: 1 "x + 1"
|
|
| 226 | + +- exp: 1 "x"
|
|
| 227 | + `- exp: 1 "1"
|
|
| 228 | +testView: 1
|
|
| 229 | ++- exp: 1 "id"
|
|
| 230 | ++- exp-alt: 1 "True"
|
|
| 231 | +`- exp-alt: 0 "False" |