{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
module Tokstyle.C.Linter
    ( analyse
    , analyseProgram
    , analyseText
    , allWarnings
    , markdown
    , renderIO
    , renderPure
    , LinterError
    , DiagnosticLevel(..)
    , Doc
    , AnsiStyle
    , diagToText
    ) where

import           Control.Parallel.Strategies          (parMap, rpar)
import qualified Data.ByteString                      as BS
import           Data.Functor.Identity                (Identity, runIdentity)
import           Data.List                            (sortBy)
import           Data.Map.Strict                      (Map)
import qualified Data.Map.Strict                      as Map
import           Data.Text                            (Text)
import qualified Data.Text                            as Text
import qualified Data.Text.Encoding                   as Text
import           Language.C.Analysis.AstAnalysis      (analyseAST)
import           Language.C.Analysis.SemRep           (DeclEvent (..),
                                                       FunDef (..), GlobalDecls,
                                                       IdentDecl (..),
                                                       VarDecl (..),
                                                       VarName (..))
import           Language.C.Analysis.TravMonad        (CLanguage (..), Trav,
                                                       TravOptions (..),
                                                       TravState, modifyOptions,
                                                       modifyUserState, runTrav,
                                                       runTravTWithTravState,
                                                       userState,
                                                       withExtDeclHandler)
import           Language.C.Data.Error                (CError, errorPos)
import           Language.C.Data.Ident                (identToString)
import           Language.C.Data.Position             (isSourcePos)
import qualified Language.C.Data.Position             as C
import           Language.C.Syntax.AST                (CTranslUnit)
import           Language.Cimple.Diagnostics          (Diagnostic (..),
                                                       DiagnosticLevel (..),
                                                       IsPosition (..),
                                                       diagToText)
import qualified Language.Cimple.Diagnostics          as Diagnostics
import           Tokstyle.C.Analysis.Interval         (Program, emptyProgram)
import           Tokstyle.C.Analysis.Program          (Analysed, Observed,
                                                       observe, programFrom)

import           Prettyprinter                        (Doc, pretty)
import           Prettyprinter.Render.Terminal        (AnsiStyle)
import qualified Tokstyle.C.Env                       as Env
import           Tokstyle.C.Env                       (DiagnosticLevel (..),
                                                       Env, LinterError,
                                                       defaultEnv, linterErrors)

import           Data.Function                        (on)
import qualified Tokstyle.C.Linter.BoolConversion     as BoolConversion (descr)
import qualified Tokstyle.C.Linter.BorrowCheck        as BorrowCheck (descr)
import qualified Tokstyle.C.Linter.BufferSize         as BufferSize (descr,
                                                                     looksAt)
import qualified Tokstyle.C.Linter.CallbackData       as CallbackData (descr)
import qualified Tokstyle.C.Linter.CallbackDiscipline as CallbackDiscipline (descr)
import qualified Tokstyle.C.Linter.CallbackParams     as CallbackParams (descr)
import qualified Tokstyle.C.Linter.Cast               as Cast (descr)
import qualified Tokstyle.C.Linter.Conversion         as Conversion (descr)
import qualified Tokstyle.C.Linter.Memcpy             as Memcpy (descr)
import qualified Tokstyle.C.Linter.Memset             as Memset (descr)
import qualified Tokstyle.C.Linter.Memzero            as Memzero (descr)
import qualified Tokstyle.C.Linter.SilentFree         as SilentFree (descr)
import qualified Tokstyle.C.Linter.SizeArg            as SizeArg (descr)
import qualified Tokstyle.C.Linter.Sizeof             as Sizeof (descr)
import qualified Tokstyle.C.Linter.SizeofTarget       as SizeofTarget (descr)
import qualified Tokstyle.C.Linter.StrictTypedef      as StrictTypedef (descr)
import qualified Tokstyle.C.Linter.VoidCall           as VoidCall (descr)
import qualified Tokstyle.C.Linter.Wraparound         as Wraparound (descr,
                                                                     looksAt)


type Linter = (GlobalDecls -> Trav Env (), (Text, Text))

linters :: [Linter]
linters :: [Linter]
linters =
    [ Linter
BoolConversion.descr
    , Linter
BorrowCheck.descr
    , Linter
BufferSize.descr
    , Linter
CallbackData.descr
    , Linter
CallbackDiscipline.descr
    , Linter
CallbackParams.descr
    , Linter
Cast.descr
    , Linter
Conversion.descr
    , Linter
Memcpy.descr
    , Linter
Memset.descr
    , Linter
Memzero.descr
    , Linter
SilentFree.descr
    , Linter
SizeArg.descr
    , Linter
Sizeof.descr
    , Linter
SizeofTarget.descr
    , Linter
StrictTypedef.descr
    , Linter
VoidCall.descr
    , Linter
Wraparound.descr
    ]


-- | The linters built on the interval analysis, with the functions each looks
-- at.
intervalLinters :: [(Text, FunDef -> Bool)]
intervalLinters :: [(Text, FunDef -> Bool)]
intervalLinters =
    [ (Text
"buffer-size", FunDef -> Bool
BufferSize.looksAt)
    , (Text
"wraparound", FunDef -> Bool
Wraparound.looksAt)
    ]


runLinters :: [Text] -> GlobalDecls -> Trav Env ()
runLinters :: [Text] -> GlobalDecls -> Trav Env ()
runLinters [Text]
flags GlobalDecls
tu =
    (Linter -> Trav Env ()) -> [Linter] -> Trav Env ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\(GlobalDecls -> Trav Env ()
f, (Text
flag, Text
_)) -> do
        (Env -> Env) -> Trav Env ()
forall s. (s -> s) -> Trav s ()
modifyUserState ((Env -> Env) -> Trav Env ()) -> (Env -> Env) -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ \Env
env -> Env
env{Env.currentFlag = Just flag}
        GlobalDecls -> Trav Env ()
f GlobalDecls
tu
        (Env -> Env) -> Trav Env ()
forall s. (s -> s) -> Trav s ()
modifyUserState ((Env -> Env) -> Trav Env ()) -> (Env -> Env) -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ \Env
env -> Env
env{Env.currentFlag = Nothing}
    ) ([Linter] -> Trav Env ())
-> ([Linter] -> [Linter]) -> [Linter] -> Trav Env ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Linter -> Bool) -> [Linter] -> [Linter]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
flags) (Text -> Bool) -> (Linter -> Text) -> Linter -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text, Text) -> Text
forall a b. (a, b) -> a
fst ((Text, Text) -> Text)
-> (Linter -> (Text, Text)) -> Linter -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Linter -> (Text, Text)
forall a b. (a, b) -> b
snd) ([Linter] -> Trav Env ()) -> [Linter] -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ [Linter]
linters


analyse :: [Text] -> CTranslUnit -> [LinterError]
analyse :: [Text] -> CTranslUnit -> [LinterError]
analyse [Text]
enabled CTranslUnit
tu = [[LinterError]] -> [LinterError]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[LinterError]] -> [LinterError])
-> [[LinterError]] -> [LinterError]
forall a b. (a -> b) -> a -> b
$ [Text] -> [(FilePath, CTranslUnit)] -> [[LinterError]]
analyseProgram [Text]
enabled [(FilePath
"test.c", CTranslUnit
tu)]


analyseProgram :: [Text] -> [(FilePath, CTranslUnit)] -> [[LinterError]]
analyseProgram :: [Text] -> [(FilePath, CTranslUnit)] -> [[LinterError]]
analyseProgram [Text]
enabled [(FilePath, CTranslUnit)]
units = Strategy [LinterError]
-> (((FilePath,
      Either [CError] (GlobalDecls, TravState Identity Env)),
     ([Observation], [Analysed]))
    -> [LinterError])
-> [((FilePath,
      Either [CError] (GlobalDecls, TravState Identity Env)),
     ([Observation], [Analysed]))]
-> [[LinterError]]
forall b a. Strategy b -> (a -> b) -> [a] -> [b]
parMap Strategy [LinterError]
forall a. Strategy a
rpar ((FilePath, Either [CError] (GlobalDecls, TravState Identity Env)),
 ([Observation], [Analysed]))
-> [LinterError]
forall {a} {a}.
((a, Either [CError] (GlobalDecls, TravState Identity Env)),
 (a, [Analysed]))
-> [LinterError]
check ([(FilePath, Either [CError] (GlobalDecls, TravState Identity Env))]
-> [([Observation], [Analysed])]
-> [((FilePath,
      Either [CError] (GlobalDecls, TravState Identity Env)),
     ([Observation], [Analysed]))]
forall a b. [a] -> [b] -> [(a, b)]
zip [(FilePath, Either [CError] (GlobalDecls, TravState Identity Env))]
analysed [([Observation], [Analysed])]
observed)
  where
    analysed :: [(FilePath, Either [CError] (GlobalDecls, TravState Identity Env))]
analysed = Strategy
  (FilePath, Either [CError] (GlobalDecls, TravState Identity Env))
-> ((FilePath, CTranslUnit)
    -> (FilePath,
        Either [CError] (GlobalDecls, TravState Identity Env)))
-> [(FilePath, CTranslUnit)]
-> [(FilePath,
     Either [CError] (GlobalDecls, TravState Identity Env))]
forall b a. Strategy b -> (a -> b) -> [a] -> [b]
parMap Strategy
  (FilePath, Either [CError] (GlobalDecls, TravState Identity Env))
forall a. Strategy a
rpar (\(FilePath
file, CTranslUnit
tu) -> (FilePath
file, CTranslUnit
-> Either [CError] (GlobalDecls, TravState Identity Env)
analyseUnit CTranslUnit
tu)) [(FilePath, CTranslUnit)]
units

    looksAt :: [FunDef -> Bool]
looksAt = [ FunDef -> Bool
p | (Text
flag, FunDef -> Bool
p) <- [(Text, FunDef -> Bool)]
intervalLinters, Text
flag Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
enabled ]
    observed :: [([Observation], [Analysed])]
observed
        | [FunDef -> Bool] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FunDef -> Bool]
looksAt = ((FilePath, Either [CError] (GlobalDecls, TravState Identity Env))
 -> ([Observation], [Analysed]))
-> [(FilePath,
     Either [CError] (GlobalDecls, TravState Identity Env))]
-> [([Observation], [Analysed])]
forall a b. (a -> b) -> [a] -> [b]
map (([Observation], [Analysed])
-> (FilePath,
    Either [CError] (GlobalDecls, TravState Identity Env))
-> ([Observation], [Analysed])
forall a b. a -> b -> a
const ([], [])) [(FilePath, Either [CError] (GlobalDecls, TravState Identity Env))]
analysed
        | Bool
otherwise = Strategy ([Observation], [Analysed])
-> ((FilePath,
     Either [CError] (GlobalDecls, TravState Identity Env))
    -> ([Observation], [Analysed]))
-> [(FilePath,
     Either [CError] (GlobalDecls, TravState Identity Env))]
-> [([Observation], [Analysed])]
forall b a. Strategy b -> (a -> b) -> [a] -> [b]
parMap Strategy ([Observation], [Analysed])
forall a. Strategy a
rpar ((FunDef -> Bool)
-> (FilePath,
    Either [CError] (GlobalDecls, TravState Identity Env))
-> ([Observation], [Analysed])
observeUnit (\FunDef
fd -> ((FunDef -> Bool) -> Bool) -> [FunDef -> Bool] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((FunDef -> Bool) -> FunDef -> Bool
forall a b. (a -> b) -> a -> b
$ FunDef
fd) [FunDef -> Bool]
looksAt)) [(FilePath, Either [CError] (GlobalDecls, TravState Identity Env))]
analysed
    prog :: Program
prog = [Observation] -> Program
programFrom ((([Observation], [Analysed]) -> [Observation])
-> [([Observation], [Analysed])] -> [Observation]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([Observation], [Analysed]) -> [Observation]
forall a b. (a, b) -> a
fst [([Observation], [Analysed])]
observed)

    check :: ((a, Either [CError] (GlobalDecls, TravState Identity Env)),
 (a, [Analysed]))
-> [LinterError]
check ((a
_, Left [CError]
errs), (a, [Analysed])
_) = (CError -> LinterError) -> [CError] -> [LinterError]
forall a b. (a -> b) -> [a] -> [b]
map CError -> LinterError
toLinterError [CError]
errs
    check ((a
_, Right (GlobalDecls
decls, TravState Identity Env
st)), (a
_, [Analysed]
fns)) = case TravState Identity Env
-> Program
-> [Analysed]
-> Trav Env ()
-> Either [CError] ((), TravState Identity Env)
forall a.
TravState Identity Env
-> Program
-> [Analysed]
-> Trav Env a
-> Either [CError] (a, TravState Identity Env)
resume TravState Identity Env
st Program
prog [Analysed]
fns ([Text] -> GlobalDecls -> Trav Env ()
runLinters [Text]
enabled GlobalDecls
decls) of
        Left [CError]
errs        -> (CError -> LinterError) -> [CError] -> [LinterError]
forall a b. (a -> b) -> [a] -> [b]
map CError -> LinterError
toLinterError [CError]
errs
        Right (()
_, TravState Identity Env
state) -> [LinterError] -> [LinterError]
forall a. [a] -> [a]
reverse ([LinterError] -> [LinterError])
-> (Env -> [LinterError]) -> Env -> [LinterError]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Env -> [LinterError]
linterErrors (Env -> [LinterError]) -> Env -> [LinterError]
forall a b. (a -> b) -> a -> b
$ TravState Identity Env -> Env
forall (m :: * -> *) s. TravState m s -> s
userState TravState Identity Env
state

    toLinterError :: CError -> LinterError
    toLinterError :: CError -> LinterError
toLinterError CError
err = CPosition
-> Int
-> DiagnosticLevel
-> Doc AnsiStyle
-> Maybe Text
-> [DiagnosticSpan CPosition]
-> [(DiagnosticLevel, Doc AnsiStyle)]
-> LinterError
forall pos.
pos
-> Int
-> DiagnosticLevel
-> Doc AnsiStyle
-> Maybe Text
-> [DiagnosticSpan pos]
-> [(DiagnosticLevel, Doc AnsiStyle)]
-> Diagnostic pos
Diagnostic (Position -> CPosition
Env.CPosition (CError -> Position
forall e. Error e => e -> Position
errorPos CError
err)) Int
1 DiagnosticLevel
ErrorLevel (FilePath -> Doc AnsiStyle
forall ann. FilePath -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty (CError -> FilePath
forall a. Show a => a -> FilePath
show CError
err)) Maybe Text
forall a. Maybe a
Nothing [] []


-- | The declarations of a translation unit, with the state of their analysis
-- to go on from.
analyseUnit :: CTranslUnit -> Either [CError] (GlobalDecls, TravState Identity Env)
analyseUnit :: CTranslUnit
-> Either [CError] (GlobalDecls, TravState Identity Env)
analyseUnit CTranslUnit
tu = Env
-> Trav Env GlobalDecls
-> Either [CError] (GlobalDecls, TravState Identity Env)
forall s a.
s -> Trav s a -> Either [CError] (a, TravState Identity s)
runTrav Env
defaultEnv (Trav Env GlobalDecls
 -> Either [CError] (GlobalDecls, TravState Identity Env))
-> Trav Env GlobalDecls
-> Either [CError] (GlobalDecls, 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 = GNU99 })
    CTranslUnit -> Trav Env GlobalDecls
forall (m :: * -> *). MonadTrav m => CTranslUnit -> m GlobalDecls
analyseAST CTranslUnit
tu Trav Env GlobalDecls
-> (DeclEvent -> Trav Env ()) -> Trav Env GlobalDecls
forall (m :: * -> *) s a.
Monad m =>
TravT s m a -> (DeclEvent -> TravT s m ()) -> TravT s m a
`withExtDeclHandler` DeclEvent -> Trav Env ()
recordLocals

-- | Remembers the locals each function declares, which language-c forgets
-- once it leaves the function.
recordLocals :: DeclEvent -> Trav Env ()
recordLocals :: DeclEvent -> Trav Env ()
recordLocals = \case
    LocalEvent IdentDecl
d -> (Env -> Env) -> Trav Env ()
forall s. (s -> s) -> Trav s ()
modifyUserState ((Env -> Env) -> Trav Env ()) -> (Env -> Env) -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ \Env
env -> Env
env{Env.pendingLocals = d : Env.pendingLocals env}
    DeclEvent (FunctionDef (FunDef (VarDecl (VarName Ident
i Maybe AsmName
_) DeclAttrs
_ Type
_) Stmt
_ NodeInfo
_)) -> (Env -> Env) -> Trav Env ()
forall s. (s -> s) -> Trav s ()
modifyUserState ((Env -> Env) -> Trav Env ()) -> (Env -> Env) -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ \Env
env -> Env
env
        { Env.localsOf = Map.insert (identToString i) (reverse (Env.pendingLocals env)) (Env.localsOf env)
        , Env.pendingLocals = []
        }
    DeclEvent
_ -> () -> Trav Env ()
forall a. a -> TravT Env Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Continues analysing a translation unit, knowing what the whole program
-- shows, with its functions as analysed without that knowledge.
resume :: TravState Identity Env -> Program -> [Analysed] -> Trav Env a -> Either [CError] (a, TravState Identity Env)
resume :: forall a.
TravState Identity Env
-> Program
-> [Analysed]
-> Trav Env a
-> Either [CError] (a, TravState Identity Env)
resume TravState Identity Env
st Program
p [Analysed]
fns Trav Env a
act = Identity (Either [CError] (a, TravState Identity Env))
-> Either [CError] (a, TravState Identity Env)
forall a. Identity a -> a
runIdentity (Identity (Either [CError] (a, TravState Identity Env))
 -> Either [CError] (a, TravState Identity Env))
-> (Trav Env a
    -> Identity (Either [CError] (a, TravState Identity Env)))
-> Trav Env a
-> Either [CError] (a, TravState Identity Env)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TravState Identity Env
-> Trav Env a
-> Identity (Either [CError] (a, TravState Identity Env))
forall s (m :: * -> *) a.
Monad m =>
TravState m s
-> TravT s m a -> m (Either [CError] (a, TravState m s))
runTravTWithTravState TravState Identity Env
st (Trav Env a -> Either [CError] (a, TravState Identity Env))
-> Trav Env a -> Either [CError] (a, TravState Identity Env)
forall a b. (a -> b) -> a -> b
$ do
    (Env -> Env) -> Trav Env ()
forall s. (s -> s) -> Trav s ()
modifyUserState ((Env -> Env) -> Trav Env ()) -> (Env -> Env) -> Trav Env ()
forall a b. (a -> b) -> a -> b
$ \Env
env -> Env
env{Env.program = p, Env.analysed = fns}
    Trav Env a
act

-- | 'observe' run on a translation unit, knowing nothing about the rest of the
-- program.
observeUnit :: (FunDef -> Bool) -> (FilePath, Either [CError] (GlobalDecls, TravState Identity Env)) -> (Observed, [Analysed])
observeUnit :: (FunDef -> Bool)
-> (FilePath,
    Either [CError] (GlobalDecls, TravState Identity Env))
-> ([Observation], [Analysed])
observeUnit FunDef -> Bool
wanted = \case
    (FilePath
file, Right (GlobalDecls
decls, TravState Identity Env
st)) | Right (([Observation], [Analysed])
o, TravState Identity Env
_) <- TravState Identity Env
-> Program
-> [Analysed]
-> Trav Env ([Observation], [Analysed])
-> Either
     [CError] (([Observation], [Analysed]), TravState Identity Env)
forall a.
TravState Identity Env
-> Program
-> [Analysed]
-> Trav Env a
-> Either [CError] (a, TravState Identity Env)
resume TravState Identity Env
st Program
emptyProgram [] ((FunDef -> Bool)
-> FilePath
-> Map FilePath [IdentDecl]
-> GlobalDecls
-> Trav Env ([Observation], [Analysed])
forall (m :: * -> *).
MonadTrav m =>
(FunDef -> Bool)
-> FilePath
-> Map FilePath [IdentDecl]
-> GlobalDecls
-> m ([Observation], [Analysed])
observe FunDef -> Bool
wanted FilePath
file (Env -> Map FilePath [IdentDecl]
Env.localsOf (TravState Identity Env -> Env
forall (m :: * -> *) s. TravState m s -> s
userState TravState Identity Env
st)) GlobalDecls
decls) -> ([Observation], [Analysed])
o
    (FilePath, Either [CError] (GlobalDecls, TravState Identity Env))
_ -> ([], [])


analyseText :: [Text] -> CTranslUnit -> [Text]
analyseText :: [Text] -> CTranslUnit -> [Text]
analyseText [Text]
enabled CTranslUnit
tu = (LinterError -> Text) -> [LinterError] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map LinterError -> Text
forall pos. IsPosition pos => Diagnostic pos -> Text
diagToText ([Text] -> CTranslUnit -> [LinterError]
analyse [Text]
enabled CTranslUnit
tu)


renderIO :: [LinterError] -> IO [Doc AnsiStyle]
renderIO :: [LinterError] -> IO [Doc AnsiStyle]
renderIO [LinterError]
errors = do
    let files :: [FilePath]
files = Map FilePath () -> [FilePath]
forall k a. Map k a -> [k]
Map.keys (Map FilePath () -> [FilePath])
-> ([(FilePath, ())] -> Map FilePath ())
-> [(FilePath, ())]
-> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(FilePath, ())] -> Map FilePath ()
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(FilePath, ())] -> [FilePath]) -> [(FilePath, ())] -> [FilePath]
forall a b. (a -> b) -> a -> b
$ [ (CPosition -> FilePath
forall p. IsPosition p => p -> FilePath
posFile (LinterError -> CPosition
forall pos. Diagnostic pos -> pos
diagPos LinterError
e), ()) | LinterError
e <- [LinterError]
errors, CPosition -> Bool
forall p. IsPosition p => p -> Bool
isRealPos (LinterError -> CPosition
forall pos. Diagnostic pos -> pos
diagPos LinterError
e) ]
    Map FilePath [Text]
cache <- [(FilePath, [Text])] -> Map FilePath [Text]
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(FilePath, [Text])] -> Map FilePath [Text])
-> IO [(FilePath, [Text])] -> IO (Map FilePath [Text])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (FilePath -> IO (FilePath, [Text]))
-> [FilePath] -> IO [(FilePath, [Text])]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (\FilePath
f -> do
        [Text]
ls <- Text -> [Text]
Text.lines (Text -> [Text]) -> (ByteString -> Text) -> ByteString -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
Text.decodeUtf8 (ByteString -> [Text]) -> IO ByteString -> IO [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO ByteString
BS.readFile FilePath
f
        (FilePath, [Text]) -> IO (FilePath, [Text])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath
f, [Text]
ls)) [FilePath]
files
    [Doc AnsiStyle] -> IO [Doc AnsiStyle]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Doc AnsiStyle] -> IO [Doc AnsiStyle])
-> [Doc AnsiStyle] -> IO [Doc AnsiStyle]
forall a b. (a -> b) -> a -> b
$ Map FilePath [Text] -> [LinterError] -> [Doc AnsiStyle]
renderPure Map FilePath [Text]
cache [LinterError]
errors


renderPure :: Map FilePath [Text] -> [LinterError] -> [Doc AnsiStyle]
renderPure :: Map FilePath [Text] -> [LinterError] -> [Doc AnsiStyle]
renderPure = Map FilePath [Text] -> [LinterError] -> [Doc AnsiStyle]
forall pos.
IsPosition pos =>
Map FilePath [Text] -> [Diagnostic pos] -> [Doc AnsiStyle]
Diagnostics.renderPure


allWarnings :: [Text]
allWarnings :: [Text]
allWarnings = (Linter -> Text) -> [Linter] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map ((Text, Text) -> Text
forall a b. (a, b) -> a
fst ((Text, Text) -> Text)
-> (Linter -> (Text, Text)) -> Linter -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Linter -> (Text, Text)
forall a b. (a, b) -> b
snd) [Linter]
linters


markdown :: Text
markdown :: Text
markdown = Text -> [Text] -> Text
Text.intercalate Text
"\n" ([Text] -> Text) -> ([Linter] -> [Text]) -> [Linter] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Text]
prelude [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++) ([Text] -> [Text]) -> ([Linter] -> [Text]) -> [Linter] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Linter -> Text) -> [Linter] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Linter -> Text
forall {a}. (a, (Text, Text)) -> Text
mkDoc ([Linter] -> [Text])
-> ([Linter] -> [Linter]) -> [Linter] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Linter -> Linter -> Ordering) -> [Linter] -> [Linter]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (Text -> Text -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Text -> Text -> Ordering)
-> (Linter -> Text) -> Linter -> Linter -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` ((Text, Text) -> Text
forall a b. (a, b) -> a
fst ((Text, Text) -> Text)
-> (Linter -> (Text, Text)) -> Linter -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Linter -> (Text, Text)
forall a b. (a, b) -> b
snd)) ([Linter] -> Text) -> [Linter] -> Text
forall a b. (a -> b) -> a -> b
$ [Linter]
linters
  where
    prelude :: [Text]
prelude =
        [ Text
"# C-based linters (`check-c`)"
        , Text
""
        , Text
"There are currently " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Text
Text.pack (Int -> FilePath
forall a. Show a => a -> FilePath
show (Int -> FilePath) -> Int -> FilePath
forall a b. (a -> b) -> a -> b
$ [Linter] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Linter]
linters) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" linters implemented."
        , Text
""
        ]
    mkDoc :: (a, (Text, Text)) -> Text
mkDoc (a
_, (Text
flag, Text
doc)) = Text
"## `-W" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
flag Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"`\n\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
Text.stripEnd Text
doc Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n"