packages feed

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

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

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

import GHC
import GHC.Hs.Syn.Type (lhsExprType)
import GHC.Types.Name (getOccString)
import Test.Syd.Mutation.Plugin.Instrument (InstrM, MutationAlt (..), MutationOperator (..), MutationOperatorKind (..), SrcSpanDelta (..))
import Test.Syd.Mutation.Plugin.Operator.Util (mkNothingExpr)

theOperator :: MutationOperator
theOperator =
  MutationOperator
    { operatorName = "MaybeOp",
      operatorDescription = "Replace Just e with Nothing",
      operatorKind = ExpressionOperator $ \case
        le@(L _ (HsApp _ f _))
          | Just occ <- funOccName f,
            occ == "Just" ->
              Just (action le)
        _ -> Nothing
    }

-- | Extract the OccName string of the function in an application, handling
-- typechecker wrappers.  After typechecking, data constructors like @Just@
-- are rewritten from @HsVar@ to @XExpr (ConLikeTc ...)@, so we have to
-- inspect that form too — mirroring 'BoolLit.extractBoolLit'.
funOccName :: LHsExpr GhcTc -> Maybe String
funOccName = \case
  L _ (HsVar _ (L _ v)) -> Just (getOccString v)
  L _ (XExpr (ConLikeTc con _ _)) -> Just (getOccString con)
  L _ (XExpr (WrapExpr (HsWrap _ e))) -> funOccName (noLocA e)
  _ -> Nothing

action ::
  LHsExpr GhcTc ->
  InstrM [MutationAlt]
action le =
  -- lhsExprType gives Maybe a, which is the type for the ifMutation wrapper.
  -- 'mkNothingExpr' instantiates Nothing's type variable to that element type;
  -- a bare 'nlHsDataCon nothingDataCon' would be @forall a. Maybe a@ and make
  -- the surrounding @ifMutation \@ty@ wrapper ill-typed (see 'mkNothingExpr').
  let mayTy = lhsExprType le
      nothingExpr = mkNothingExpr mayTy
   in pure
        [ MutationAlt
            { mutAltType = mayTy,
              mutAltExpr = nothingExpr,
              mutAltOriginal = "Just e",
              mutAltReplacement = "Nothing",
              mutAltDelta = TokenReplace "Nothing",
              mutAltMitigation = Nothing
            }
        ]