packages feed

sydtest-mutation-plugin-0.4.4.0: src/Test/Syd/Mutation/Plugin/Operator/ListLit.hs

{-# LANGUAGE LambdaCase #-}

module Test.Syd.Mutation.Plugin.Operator.ListLit (theOperator) where

import GHC
import GHC.Builtin.Types (mkListTy)
import Test.Syd.Mutation.Plugin.Instrument (InstrM, MutationAlt (..), MutationOperator (..), MutationOperatorKind (..), SrcSpanDelta (..))

theOperator :: MutationOperator
theOperator =
  MutationOperator
    { operatorName = "ListLit",
      operatorDescription = "Shrink a list literal by removing elements or emptying it",
      operatorKind = ExpressionOperator $ \case
        (L ann (ExplicitList elTy es))
          | length es >= 2 ->
              Just (action ann elTy es)
        _ -> Nothing
    }

action ::
  SrcSpanAnnA ->
  Type ->
  [LHsExpr GhcTc] ->
  InstrM [MutationAlt]
action ann elTy es =
  let listTy = mkListTy elTy
      n = length es
      toRss e = case getLocA e of
        RealSrcSpan rss _ -> [rss]
        UnhelpfulSpan _ -> []
      mkList xs delta =
        MutationAlt
          { mutAltType = listTy,
            mutAltExpr = L ann (ExplicitList elTy xs),
            mutAltOriginal = show n ++ " elements",
            mutAltReplacement = show (length xs) ++ " elements",
            mutAltDelta = delta,
            mutAltMitigation = Nothing
          }
      -- Always produce: empty list, drop-head.
      -- Only add drop-last if it gives a different length than drop-head
      -- (i.e. n > 2; when n == 2 both give one element).
      lastE = reverse es
      repls = case es of
        [] -> []
        (firstE : restEs) ->
          mkList [] (SpanRemoval (concatMap toRss es))
            : mkList restEs (SpanRemoval (toRss firstE))
            : case lastE of
              [] -> []
              (le : _) -> [mkList (take (n - 1) es) (SpanRemoval (toRss le)) | n > 2]
   in pure repls