packages feed

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

{-# LANGUAGE LambdaCase #-}

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

import qualified Data.Text as T
import GHC
import GHC.Builtin.Types (boolTy, falseDataCon, trueDataCon)
import GHC.Types.Name (getOccString)
import Test.Syd.Mutation.Plugin.Instrument (InstrM, MutationAlt (..), MutationOperator (..), MutationOperatorKind (..), SrcSpanDelta (..))

theOperator :: MutationOperator
theOperator =
  MutationOperator
    { operatorName = "BoolLit",
      operatorDescription = "Replace a boolean literal with its negation",
      operatorKind = ExpressionOperator $ fmap action . extractBoolLit
    }

-- | Extract the OccName of a boolean literal, unwrapping HsWrap and
-- ConLikeTc nodes that GHC inserts after type-checking.
extractBoolLit :: LHsExpr GhcTc -> Maybe String
extractBoolLit = \case
  L _ (HsVar _ (L _ v))
    | occ <- getOccString v, occ `elem` boolLits -> Just occ
  L _ (XExpr (ConLikeTc con _ _))
    | occ <- getOccString con, occ `elem` boolLits -> Just occ
  L _ (XExpr (WrapExpr (HsWrap _ e))) -> extractBoolLit (noLocA e)
  _ -> Nothing

boolLits :: [String]
boolLits = ["True", "False"]

action ::
  String ->
  InstrM [MutationAlt]
action origOcc =
  let repl = if origOcc == "True" then falseDataCon else trueDataCon
      replOcc = if origOcc == "True" then "False" else "True"
      replExpr = nlHsDataCon repl
   in pure
        [ MutationAlt
            { mutAltType = boolTy,
              mutAltExpr = replExpr,
              mutAltOriginal = origOcc,
              mutAltReplacement = replOcc,
              mutAltDelta = TokenReplace (T.pack replOcc),
              mutAltMitigation = Nothing
            }
        ]