Allow to customize the TODO marker string

This commit is contained in:
Alexander Kobjolke 2022-03-07 12:48:33 +01:00
parent 461f34a100
commit 4514d0f120
5 changed files with 61 additions and 28 deletions

View file

@ -44,13 +44,15 @@ Usage: annotator [OPTIONS] files...
A tool to semi-automatically add Coverity source-code annotations based on found defects. A tool to semi-automatically add Coverity source-code annotations based on found defects.
-v --verbose be more verbose, pass multiple times to increase verbosity -v --verbose be more verbose, pass multiple times to increase verbosity
-i --inplace replace source-file after inserting annotations -i --inplace replace source-file after inserting annotations
-V --version show version -V --version show full version information
--short-version show version --short-version show just the version number
-h --help show usage information -h --help show usage information
-a --all handle all defects not just Newest -a --all handle all defects not just Newest
-C[NUM] --context[=NUM] specify how much context should be shown around a violation -C[NUM] --context[=NUM] specify how much context should be shown around a violation
-t[STRING] --todo-marker[=STRING] override the default TODO marker with a custom string
-A FILE --annotations=FILE load automatic annotation rules
``` ```
### In-place annotations ### In-place annotations
@ -65,3 +67,18 @@ By default the annotator will only handle *Newest* violations and not those that
are already contained within the Coverity database for some reason. However, are already contained within the Coverity database for some reason. However,
it's still possible to process all found violations by passing `-a` or `--all` it's still possible to process all found violations by passing `-a` or `--all`
on the command-line. on the command-line.
### Insert annotations automatically
In case you have violations that always result in the same annotation over and
over again, you can supply one or more files that contain automatic decisions.
Each line may be one of the following:
```
Intentional (Rule "autosar_cpp14_a18_9_1") "reason why it's intentional"
FalsePositive (Rule "autosar_cpp14_a18_9_1") "reason why it's a false-positive"
ToDo (Rule "autosar_cpp14_a18_9_1")
```
Lines may be disabled by prefixing them with `#` or `--`.

View file

@ -1 +1,4 @@
Intentional (Rule "autosar_cpp14_a18_9_1") "Test auto" Intentional (Rule "autosar_cpp14_a18_9_1") "Test intentional auto"
--FalsePositive (Rule "autosar_cpp14_a18_9_2") "Test false auto"
ToDo (Rule "autosar_cpp14_a18_9_3")
#ToDo (Rule "autosar_cpp14_a18_9_4")

View file

@ -30,7 +30,7 @@ import Annotator.Rule
import Annotator.Annotation import Annotator.Annotation
version :: Vsn.Version version :: Vsn.Version
version = Vsn.makeVersion [0,0,3,0] version = Vsn.makeVersion [0,0,4,0]
type App a = ReaderT Options IO a type App a = ReaderT Options IO a
@ -54,6 +54,7 @@ data Options = Options
, onlyNewest :: Bool , onlyNewest :: Bool
, annotationFiles :: [FilePath] , annotationFiles :: [FilePath]
, autoAnnotations :: [Annotation] , autoAnnotations :: [Annotation]
, todoMarker :: String
} }
deriving (Show, Eq) deriving (Show, Eq)
@ -67,6 +68,7 @@ defaultOptions = Options
, onlyNewest = True , onlyNewest = True
, annotationFiles = [] , annotationFiles = []
, autoAnnotations = [] , autoAnnotations = []
, todoMarker = "TODO"
} }
options :: [OptDescr (Options -> Options)] options :: [OptDescr (Options -> Options)]
@ -90,11 +92,22 @@ options =
(NoArg (\opts -> opts { onlyNewest = False})) (NoArg (\opts -> opts { onlyNewest = False}))
"handle all defects not just Newest" "handle all defects not just Newest"
, Option ['C'] ["context"] , Option ['C'] ["context"]
(OptArg ((\f opts -> opts { contextLines = fromMaybe 0 $ readMaybe f }) . fromMaybe "0") "NUM") (OptArg (\f opts -> opts { contextLines = fromMaybe 0 (f >>= readMaybe) }) "NUM")
"specify how much context should be shown around a violation" "specify how much context should be shown around a violation"
, Option ['t'] ["todo-marker"]
(OptArg (\f opts -> opts { todoMarker = fromMaybe "TODO" f }) "STRING")
"override the default TODO marker with a custom string"
, Option ['A'] ["annotations"] , Option ['A'] ["annotations"]
(ReqArg (\f opts -> opts { annotationFiles = (nub $ annotationFiles opts ++ [f]) }) "FILE") (ReqArg (\f opts -> opts { annotationFiles = (nub $ annotationFiles opts ++ [f]) }) "FILE")
"load automatic annotation rules" (unlines [ "load automatic annotation rules"
, " some examples:"
, " " <> show (Intentional (Rule "rule_1") "some reason")
, " " <> show (FalsePositive (Rule "rule_1") "some reason")
, " " <> show (ToDo (Rule "rule_1"))
, " # a comment"
, " -- another comment"
]
)
] ]
readMaybe :: (Read a) => String -> Maybe a readMaybe :: (Read a) => String -> Maybe a
@ -230,6 +243,7 @@ handleViolations violations = do
opts <- ask opts <- ask
let fname = filename $ NE.head violations let fname = filename $ NE.head violations
fname' = fname <> ".fix" fname' = fname <> ".fix"
todo = todoMarker opts
header' = "Processing " <> (show $ NE.length violations) <> " violation(s) in file " <> fname header' = "Processing " <> (show $ NE.length violations) <> " violation(s) in file " <> fname
header = unlines header = unlines
[ header' [ header'
@ -252,7 +266,7 @@ handleViolations violations = do
annotations <- (catMaybes . NE.toList) <$> mapM (handleViolation content) violations annotations <- (catMaybes . NE.toList) <$> mapM (handleViolation content) violations
let annotatedLines = sortBy (comparing fst) $ fmap annotationToLine annotations let annotatedLines = sortBy (comparing fst) $ fmap (annotationToLine todo) annotations
newContent = unlines . map snd $ mergeLines annotatedLines numberedContent newContent = unlines . map snd $ mergeLines annotatedLines numberedContent
liftIO $ writeFile fname' newContent liftIO $ writeFile fname' newContent
@ -261,10 +275,14 @@ handleViolations violations = do
else do else do
verbose Low $ "skipping non-existent file " <> fname verbose Low $ "skipping non-existent file " <> fname
where where
annotationToLine :: AnnotatedViolation -> (Int, String) annotationToLine :: String -> AnnotatedViolation -> (Int, String)
annotationToLine (AnnotatedViolation Violation{..} a indent) = annotationToLine todo (AnnotatedViolation Violation{..} a indent) =
(line, indent ++ showAnnotation a description) (line, indent ++ showAnnotation todo a description)
showAnnotation :: String -> Annotation -> String -> String
showAnnotation _ (Intentional (Rule rule) reason) _ = mconcat ["// ", "coverity[", rule, "_violation", "] ", reason]
showAnnotation _ (FalsePositive (Rule rule) reason) _ = mconcat ["// ", "coverity[", rule, "_violation", " : FALSE", "] ", reason]
showAnnotation todo (ToDo (Rule rule)) description = "// " <> todo <> " " <> rule <> ": " <> description
data UserChoice = Abort data UserChoice = Abort
| Annotate Annotation | Annotate Annotation

View file

@ -1,24 +1,23 @@
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
module Annotator.Annotation (Annotation(..), showAnnotation, fromFile) where module Annotator.Annotation (Annotation(..), fromFile) where
import Data.Char (isSpace)
import Annotator.Rule import Annotator.Rule
import Data.Monoid
data Annotation = Intentional !Rule !String data Annotation = Intentional !Rule !String
| FalsePositive !Rule !String | FalsePositive !Rule !String
| ToDo !Rule | ToDo !Rule
deriving (Show, Read, Eq) deriving (Show, Read, Eq)
showAnnotation :: Annotation -> String -> String
showAnnotation (Intentional (Rule rule) reason) _ = mconcat ["// ", "coverity[", rule, "_violation", "] ", reason]
showAnnotation (FalsePositive (Rule rule) reason) _ = mconcat ["// ", "coverity[", rule, "_violation", " : FALSE", "] ", reason]
showAnnotation (ToDo (Rule rule)) description = "// FIXME violation " <> rule <> ": " <> description
fromFile :: FilePath -> IO [Either String Annotation] fromFile :: FilePath -> IO [Either String Annotation]
fromFile fn = fmap safeRead . removeBoring . lines <$> readFile fn fromFile fn = fmap safeRead . removeBoring . lines <$> readFile fn
where where
removeBoring :: [String] -> [String] removeBoring :: [String] -> [String]
removeBoring = filter (const True) removeBoring = filter (not . isBoring)
isBoring x = isComment x || isEmpty x
isComment x = take 1 x == "#" || take 2 x == "--"
isEmpty = null . dropWhile isSpace
safeRead :: String -> Either String Annotation safeRead :: String -> Either String Annotation
safeRead s = case reads s of safeRead s = case reads s of

View file

@ -3,8 +3,4 @@
module Annotator.Rule where module Annotator.Rule where
newtype Rule = Rule String newtype Rule = Rule String
deriving (Eq, Read) deriving (Eq, Read, Show)
instance Show Rule where
show :: Rule -> String
show (Rule r) = r