{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Strict #-} module Tokstyle.Linter.MemcpyStructs (descr) where import Control.Monad.State.Strict (State) import qualified Control.Monad.State.Strict as State import Data.Fix (Fix (..)) import Data.Text (Text) import qualified Data.Text as Text import Language.Cimple (Lexeme (..), Node, NodeF (..)) import Language.Cimple.Diagnostics (CimplePos, Diagnostic) import Language.Cimple.Pretty (ppNode) import Language.Cimple.TraverseAst (AstActions, astActions, doNode, traverseAst) import Prettyprinter (pretty) import Tokstyle.Common (warnDoc) exemptions :: [Text] exemptions :: [Text] exemptions = [Text "IP_Port" , Text "IP4" , Text "IP6" ] checkSize :: Text -> Text -> FilePath -> Node (Lexeme Text) -> State [Diagnostic CimplePos] () checkSize :: Text -> Text -> FilePath -> Node (Lexeme Text) -> State [Diagnostic CimplePos] () checkSize Text fname Text instead FilePath file Node (Lexeme Text) size = case Node (Lexeme Text) -> NodeF (Lexeme Text) (Node (Lexeme Text)) forall (f :: * -> *). Fix f -> f (Fix f) unFix Node (Lexeme Text) size of SizeofType ty :: Node (Lexeme Text) ty@(Fix (TyUserDefined (L AlexPosn _ LexemeClass _ Text name))) | Text name Text -> [Text] -> Bool forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool `notElem` [Text] exemptions -> FilePath -> Node (Lexeme Text) -> Doc AnsiStyle -> State [Diagnostic CimplePos] () forall diags at. (HasDiagnosticsRich diags CimplePos, HasDiagnosticInfo at CimplePos) => FilePath -> at -> Doc AnsiStyle -> DiagnosticsT diags () warnDoc FilePath file Node (Lexeme Text) size (Doc AnsiStyle -> State [Diagnostic CimplePos] ()) -> Doc AnsiStyle -> State [Diagnostic CimplePos] () forall a b. (a -> b) -> a -> b $ Doc AnsiStyle "`" Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Text -> Doc AnsiStyle forall a ann. Pretty a => a -> Doc ann pretty Text fname Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Doc AnsiStyle "` should not be used for structs like `" Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Node (Lexeme Text) -> Doc AnsiStyle forall a. Pretty a => Node (Lexeme a) -> Doc AnsiStyle ppNode Node (Lexeme Text) ty Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Doc AnsiStyle "`; use " Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Text -> Doc AnsiStyle forall a ann. Pretty a => a -> Doc ann pretty Text instead Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle forall a. Semigroup a => a -> a -> a <> Doc AnsiStyle " instead" NodeF (Lexeme Text) (Node (Lexeme Text)) _ -> () -> State [Diagnostic CimplePos] () forall (m :: * -> *) a. Monad m => a -> m a return () linter :: AstActions (State [Diagnostic CimplePos]) Text linter :: AstActions (State [Diagnostic CimplePos]) Text linter = AstActions (State [Diagnostic CimplePos]) Text forall (f :: * -> *) text. Applicative f => AstActions f text astActions { doNode :: FilePath -> Node (Lexeme Text) -> State [Diagnostic CimplePos] () -> State [Diagnostic CimplePos] () doNode = \FilePath file Node (Lexeme Text) node State [Diagnostic CimplePos] () act -> case Node (Lexeme Text) -> NodeF (Lexeme Text) (Node (Lexeme Text)) forall (f :: * -> *). Fix f -> f (Fix f) unFix Node (Lexeme Text) node of FunctionCall (Fix (VarExpr (L AlexPosn _ LexemeClass _ Text "memset"))) [Node (Lexeme Text) _, Node (Lexeme Text) _, Node (Lexeme Text) size] -> Text -> Text -> FilePath -> Node (Lexeme Text) -> State [Diagnostic CimplePos] () checkSize Text "memset" Text "`(Type) {0}`" FilePath file Node (Lexeme Text) size FunctionCall (Fix (VarExpr (L AlexPosn _ LexemeClass _ Text "memcpy"))) [Node (Lexeme Text) _, Node (Lexeme Text) _, Node (Lexeme Text) size] -> Text -> Text -> FilePath -> Node (Lexeme Text) -> State [Diagnostic CimplePos] () checkSize Text "memcpy" Text "assignment" FilePath file Node (Lexeme Text) size NodeF (Lexeme Text) (Node (Lexeme Text)) _ -> State [Diagnostic CimplePos] () act } analyse :: (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos] analyse :: (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos] analyse = [Diagnostic CimplePos] -> [Diagnostic CimplePos] forall a. [a] -> [a] reverse ([Diagnostic CimplePos] -> [Diagnostic CimplePos]) -> ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos]) -> (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos] forall b c a. (b -> c) -> (a -> b) -> a -> c . (State [Diagnostic CimplePos] () -> [Diagnostic CimplePos] -> [Diagnostic CimplePos]) -> [Diagnostic CimplePos] -> State [Diagnostic CimplePos] () -> [Diagnostic CimplePos] forall a b c. (a -> b -> c) -> b -> a -> c flip State [Diagnostic CimplePos] () -> [Diagnostic CimplePos] -> [Diagnostic CimplePos] forall s a. State s a -> s -> s State.execState [] (State [Diagnostic CimplePos] () -> [Diagnostic CimplePos]) -> ((FilePath, [Node (Lexeme Text)]) -> State [Diagnostic CimplePos] ()) -> (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos] forall b c a. (b -> c) -> (a -> b) -> a -> c . AstActions (State [Diagnostic CimplePos]) Text -> (FilePath, [Node (Lexeme Text)]) -> State [Diagnostic CimplePos] () forall text a (f :: * -> *). (TraverseAst text a, Applicative f) => AstActions f text -> a -> f () traverseAst AstActions (State [Diagnostic CimplePos]) Text linter descr :: ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos], (Text, Text)) descr :: ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos], (Text, Text)) descr = ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos] analyse, (Text "memcpy-structs", [Text] -> Text Text.unlines [Text "Checks that `memcpy` and `memset` aren't used for struct pointers." , Text "" , Text "Exemptions are:" , Text "" , Text -> [Text] -> Text Text.intercalate Text "\n" ([Text] -> Text) -> ([Text] -> [Text]) -> [Text] -> Text forall b c a. (b -> c) -> (a -> b) -> a -> c . (Text -> Text) -> [Text] -> [Text] forall a b. (a -> b) -> [a] -> [b] map (\Text x -> Text "- `" Text -> Text -> Text forall a. Semigroup a => a -> a -> a <> Text x Text -> Text -> Text forall a. Semigroup a => a -> a -> a <> Text "`") ([Text] -> Text) -> [Text] -> Text forall a b. (a -> b) -> a -> b $ [Text] exemptions , Text "" , Text "**Reason:** structs can contain pointers, so `memset` is risky (it can create" , Text "invalid null pointer representations) and `memcpy` should be replaced by an" , Text "assignment, possibly in a loop, to avoid messing up the size argument of the" , Text "`memcpy` call." ]))