{-# 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
]
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 [] []
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
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 ()
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
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"