{-# LANGUAGE ViewPatterns, PatternGuards #-}
module Hint.Pattern(patternHint) where
import Hint.Type
import Data.Function
import Data.List.Extra
import Data.Tuple
import Data.Maybe
import Data.Either
import Refact.Types hiding (RType(Pattern, Match))
import qualified Refact.Types as R (RType(Pattern, Match), SrcSpan)
patternHint :: DeclHint
patternHint _ modu x =
concatMap (uncurry hints . swap) (asPattern x) ++
concatMap (patHint strict False) (universeBi [p | PatBind _ p _ _ <- universe x]) ++
concatMap (patHint strict True) (universeBi $ transform noPatBind x) ++
concatMap expHint (universeBi x)
where
noPatBind (PatBind a _ b c) = PatBind a (PWildCard a) b c
noPatBind x = x
strict = "Strict" `elem` [n | LanguagePragma _ ns <- modulePragmas modu, Ident _ n <- ns]
hints :: (String -> Pattern -> [Refactoring R.SrcSpan] -> Idea) -> Pattern -> [Idea]
hints gen (Pattern l rtype pat (UnGuardedRhs d bod) bind)
| length guards > 2 = [gen "Use guards" (Pattern l rtype pat (GuardedRhss d guards) bind) [refactoring]]
where rawGuards = asGuards bod
mkGuard a = GuardedRhs an [Qualifier an a]
guards = map (uncurry mkGuard) rawGuards
(lhs, rhs) = unzip rawGuards
mkTemplate c ps =
let checkAn p v = if ann p == an then Left p else Right ( c ++ [v], toSS p)
in zipWith checkAn ps ['1' .. '9']
patSubts = case pat of
[p] -> [Left p]
ps -> mkTemplate "p100" ps
guardSubts = mkTemplate "g100" lhs
exprSubts = mkTemplate "e100" rhs
templateGuards = zipWith (mkGuard `on` toString) guardSubts exprSubts
toString (Left e) = e
toString (Right (v, _)) = toNamed v
template = fromMaybe "" $ ideaTo (gen "" (Pattern l rtype (map toString patSubts) (GuardedRhss d templateGuards) bind) [])
f :: [Either a (String, R.SrcSpan)] -> [(String, R.SrcSpan)]
f = rights
refactoring = Replace rtype (toRefactSrcSpan $ srcInfoSpan l) (f patSubts ++ f guardSubts ++ f exprSubts) template
hints gen (Pattern l t pats (GuardedRhss _ [GuardedRhs _ [test] bod]) bind)
| prettyPrint test `elem` ["otherwise","True"]
= [gen "Redundant guard" (Pattern l t pats (UnGuardedRhs an bod) bind) [Delete Stmt (toSS test)]]
hints gen (Pattern l t pats bod (Just bind)) | f bind
= [gen "Redundant where" (Pattern l t pats bod Nothing) []]
where
f (BDecls _ x) = null x
f (IPBinds _ x) = null x
hints gen (Pattern l t pats (GuardedRhss _ (unsnoc -> Just (gs, GuardedRhs _ [test] bod))) bind)
| prettyPrint test == "True"
= [gen "Use otherwise" (Pattern l t pats (GuardedRhss an $ gs ++ [GuardedRhs an [Qualifier an $ toNamed "otherwise"] bod]) bind) [Replace Expr (toSS test) [] "otherwise"]]
hints _ _ = []
asGuards :: Exp_ -> [(Exp S, Exp S)]
asGuards (Paren _ x) = asGuards x
asGuards (If _ a b c) = (a, b) : asGuards c
asGuards x = [(toNamed "otherwise", x)]
data Pattern = Pattern S R.RType [Pat_] (Rhs S) (Maybe (Binds S))
asPattern :: Decl_ -> [(Pattern, String -> Pattern -> [Refactoring R.SrcSpan] -> Idea)]
asPattern x = concatMap decl (universeBi x) ++ concatMap alt (universeBi x)
where
decl o@(PatBind a pat rhs bind) = [(Pattern a Bind [pat] rhs bind, \msg (Pattern _ _ [pat] rhs bind) rs -> suggest msg o (PatBind a pat rhs bind) rs)]
decl (FunBind _ xs) = map match xs
decl _ = []
match o@(Match a b pat rhs bind) = (Pattern a R.Match pat rhs bind, \msg (Pattern _ _ pat rhs bind) rs -> suggest msg o (Match a b pat rhs bind) rs)
match o@(InfixMatch a p b ps rhs bind) = (Pattern a R.Match (p:ps) rhs bind, \msg (Pattern _ _ (p:ps) rhs bind) rs -> suggest msg o (InfixMatch a p b ps rhs bind) rs)
alt o@(Alt a pat rhs bind) = [(Pattern a R.Match [pat] rhs bind, \msg (Pattern _ _ [pat] rhs bind) rs -> suggest msg o (Alt a pat rhs bind) [])]
patHint :: Bool -> Bool -> Pat_ -> [Idea]
patHint lang strict o@(PApp _ name args) | length args >= 3 && all isPWildCard args =
[suggest "Use record patterns" o (PRec an name []) [Replace R.Pattern (toSS o) [] (prettyPrint $ PRec an name [])] ]
patHint lang strict o@(PVar _ v) | prettyPrint v == "otherwise" = [warn "Used otherwise as a pattern" o (PWildCard an) []]
patHint lang strict o@(PBangPat _ x) | strict, f x = [warn "Redundant bang pattern" o x [r]]
where f (PParen _ x) = f x
f (PAsPat _ _ x) = f x
f PLit{} = True
f PApp{} = True
f PInfixApp{} = True
f PTuple{} = True
f PList{} = True
f PRec{} = True
f (PatTypeSig _ x _) = f x
f _ = False
r = Replace R.Pattern (toSS o) [("x", toSS x)] "x"
patHint False strict o@(PIrrPat _ x) | f x = [warn "Redundant irrefutable pattern" o x [r]]
where f (PParen _ x) = f x
f (PAsPat _ _ x) = f x
f PWildCard{} = True
f PVar{} = True
f _ = False
r = Replace R.Pattern (toSS o) [("x", toSS x)] "x"
patHint _ _ _ = []
expHint :: Exp_ -> [Idea]
expHint o@(Case _ _ [Alt _ PWildCard{} (UnGuardedRhs _ e) Nothing]) =
[suggest "Redundant case" o e [r]]
where
r = Replace Expr (toSS o) [("x", toSS e)] "x"
expHint o@(Case _ (Var _ x) [Alt _ (PVar _ y) (UnGuardedRhs _ e) Nothing])
| x =~= UnQual an y =
[suggest "Redundant case" o e [r]]
where
r = Replace Expr (toSS o) [("x", toSS e)] "x"
expHint _ = []