{-# LANGUAGE OverloadedStrings #-}
module Tokstyle.C.Linter
    ( analyse
    , allWarnings
    ) where

import           Data.Text                        (Text)
import qualified Data.Text                        as Text
import           Language.C.Analysis.AstAnalysis  (analyseAST)
import           Language.C.Analysis.SemRep       (GlobalDecls)
import           Language.C.Analysis.TravMonad    (CLanguage (..), Trav,
                                                   TravOptions (..),
                                                   modifyOptions, runTrav)
import           Language.C.Syntax.AST            (CTranslUnit)
import           Tokstyle.C.Env                   (Env, defaultEnv)

import qualified Tokstyle.C.Linter.BoolConversion as BoolConversion
import qualified Tokstyle.C.Linter.CallbackParams as CallbackParams
import qualified Tokstyle.C.Linter.Cast           as Cast
import qualified Tokstyle.C.Linter.Conversion     as Conversion
import qualified Tokstyle.C.Linter.Memset         as Memset
import qualified Tokstyle.C.Linter.SizeArg        as SizeArg
import qualified Tokstyle.C.Linter.Sizeof         as Sizeof
import qualified Tokstyle.C.Linter.VoidCall       as VoidCall


linters :: [(Text, GlobalDecls -> Trav Env ())]
linters :: [(Text, GlobalDecls -> Trav Env ())]
linters =
    [ (Text
"bool-conversion"    , GlobalDecls -> Trav Env ()
BoolConversion.analyse   )
    , (Text
"callback-params"    , GlobalDecls -> Trav Env ()
CallbackParams.analyse   )
    , (Text
"cast"               , GlobalDecls -> Trav Env ()
Cast.analyse             )
    , (Text
"conversion"         , GlobalDecls -> Trav Env ()
Conversion.analyse       )
    , (Text
"memset"             , GlobalDecls -> Trav Env ()
Memset.analyse           )
    , (Text
"size-arg"           , GlobalDecls -> Trav Env ()
SizeArg.analyse          )
    , (Text
"sizeof"             , GlobalDecls -> Trav Env ()
Sizeof.analyse           )
    , (Text
"void-call"          , GlobalDecls -> Trav Env ()
VoidCall.analyse         )
    ]

runLinters :: [Text] -> GlobalDecls -> Trav Env ()
runLinters :: [Text] -> GlobalDecls -> Trav Env ()
runLinters [Text]
flags GlobalDecls
tu =
    ((Text, GlobalDecls -> Trav Env ()) -> Trav Env ())
-> [(Text, GlobalDecls -> Trav Env ())] -> Trav Env ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\(Text
_, GlobalDecls -> Trav Env ()
f) -> GlobalDecls -> Trav Env ()
f GlobalDecls
tu) ([(Text, GlobalDecls -> Trav Env ())] -> Trav Env ())
-> ([(Text, GlobalDecls -> Trav Env ())]
    -> [(Text, GlobalDecls -> Trav Env ())])
-> [(Text, GlobalDecls -> Trav Env ())]
-> Trav Env ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Text, GlobalDecls -> Trav Env ()) -> Bool)
-> [(Text, GlobalDecls -> Trav Env ())]
-> [(Text, GlobalDecls -> Trav Env ())]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
flags) (Text -> Bool)
-> ((Text, GlobalDecls -> Trav Env ()) -> Text)
-> (Text, GlobalDecls -> Trav Env ())
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text, GlobalDecls -> Trav Env ()) -> Text
forall a b. (a, b) -> a
fst) ([(Text, GlobalDecls -> Trav Env ())] -> Trav Env ())
-> [(Text, GlobalDecls -> Trav Env ())] -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ [(Text, GlobalDecls -> Trav Env ())]
linters


analyse :: [Text] -> CTranslUnit -> [Text]
analyse :: [Text] -> CTranslUnit -> [Text]
analyse [Text]
enabled CTranslUnit
tu =
    case Either [CError] ((), TravState Identity Env)
analysis of
        Left [CError]
errs -> (CError -> Text) -> [CError] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Text
Text.pack (String -> Text) -> (CError -> String) -> CError -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CError -> String
forall a. Show a => a -> String
show) [CError]
errs
        Right ((), TravState Identity Env)
_   -> []
  where
    analysis :: Either [CError] ((), TravState Identity Env)
analysis = Env -> Trav Env () -> Either [CError] ((), TravState Identity Env)
forall s a.
s -> Trav s a -> Either [CError] (a, TravState Identity s)
runTrav Env
defaultEnv (Trav Env () -> Either [CError] ((), TravState Identity Env))
-> Trav Env () -> Either [CError] ((), TravState Identity Env)
forall a b. (a -> b) -> a -> b
$ do
        (TravOptions -> TravOptions) -> Trav Env ()
forall s. (TravOptions -> TravOptions) -> Trav s ()
modifyOptions (\TravOptions
opts -> TravOptions
opts { language :: CLanguage
language = CLanguage
GNU99 })
        GlobalDecls
decls <- CTranslUnit -> TravT Env Identity GlobalDecls
forall (m :: * -> *). MonadTrav m => CTranslUnit -> m GlobalDecls
analyseAST CTranslUnit
tu
        [Text] -> GlobalDecls -> Trav Env ()
runLinters [Text]
enabled GlobalDecls
decls


allWarnings :: [Text]
allWarnings :: [Text]
allWarnings = ((Text, GlobalDecls -> Trav Env ()) -> Text)
-> [(Text, GlobalDecls -> Trav Env ())] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, GlobalDecls -> Trav Env ()) -> Text
forall a b. (a, b) -> a
fst [(Text, GlobalDecls -> Trav Env ())]
linters