Compare commits
10 commits
d74b7397c6
...
5a55e96615
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a55e96615 | ||
|
|
687b0ef632 | ||
|
|
3019f4ddc5 | ||
|
|
c70ce7c1d8 | ||
|
|
c9b6ad7427 | ||
|
|
de1be0aba3 | ||
|
|
737ff6afe3 | ||
|
|
f55a6c71b6 | ||
|
|
8b4754e9ad | ||
|
|
59eb3b502e |
9 changed files with 131 additions and 156 deletions
|
|
@ -29,6 +29,7 @@ build-job: # This job runs in the build stage, which runs first.
|
||||||
script:
|
script:
|
||||||
- make release
|
- make release
|
||||||
artifacts:
|
artifacts:
|
||||||
|
paths:
|
||||||
- build/dist/*.tar.gz
|
- build/dist/*.tar.gz
|
||||||
|
|
||||||
unit-test-job: # This job runs in the test stage.
|
unit-test-job: # This job runs in the test stage.
|
||||||
|
|
@ -38,7 +39,7 @@ unit-test-job: # This job runs in the test stage.
|
||||||
- linux
|
- linux
|
||||||
script:
|
script:
|
||||||
- echo "Running unit tests... This will take about 60 seconds."
|
- echo "Running unit tests... This will take about 60 seconds."
|
||||||
- sleep 60
|
- sleep 1
|
||||||
- echo "Code coverage is 90%"
|
- echo "Code coverage is 90%"
|
||||||
|
|
||||||
lint-test-job: # This job also runs in the test stage.
|
lint-test-job: # This job also runs in the test stage.
|
||||||
|
|
|
||||||
24
Makefile
24
Makefile
|
|
@ -1,27 +1,32 @@
|
||||||
.PHONY: default all install watch clean build release distclean lint
|
.PHONY: default all install watch clean build release distclean lint doc
|
||||||
default: all
|
default: all
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build: builddir build/bin/annotator
|
|
||||||
|
|
||||||
GHCFLAGS = -iapp -isrc -Wall -O2 -outputdir build/.obj
|
GHCFLAGS = -iapp -isrc -Wall -O2 -outputdir build/.obj
|
||||||
BUILDDIR = build
|
BUILDDIR = build
|
||||||
DESTDIR = ~/.local
|
DESTDIR = ~/.local
|
||||||
BINDIR = ${DESTDIR}/bin
|
BINDIR = ${DESTDIR}/bin
|
||||||
DOCDIR = ${DESTDIR}/share/doc/annotator
|
DOCDIR = ${DESTDIR}/share/doc/annotator
|
||||||
|
|
||||||
|
build: builddir | $(BUILDDIR)/bin/annotator doc
|
||||||
|
|
||||||
builddir:
|
builddir:
|
||||||
mkdir -p $(BUILDDIR)/bin $(BUILDDIR)/.obj $(BUILDDIR)/ci $(BUILDDIR)/dist
|
mkdir -p $(BUILDDIR)/{bin,.obj,ci,dist,doc}
|
||||||
|
|
||||||
|
doc: builddir | $(BUILDDIR)/doc/README.md
|
||||||
|
|
||||||
$(BUILDDIR)/bin/annotator: app/Main.hs src/*.hs
|
$(BUILDDIR)/bin/annotator: app/Main.hs src/*.hs
|
||||||
ghc --make $(GHCFLAGS) $< -o $@
|
ghc --make $(GHCFLAGS) $< -o $@
|
||||||
|
|
||||||
|
$(BUILDDIR)/doc/README.md: README.org
|
||||||
|
pandoc -t gfm $< -o $@
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
@git ls-files src app Makefile | entr make -s build install
|
@git ls-files src app Makefile | entr make -s build install
|
||||||
|
|
||||||
lint:
|
lint: builddir
|
||||||
@-hlint -g --cc > $(BUILDDIR)/ci/hlint.report.json
|
hlint lint --git --cc --no-exit-code | tr -d '\0' | jq -s . > $(BUILDDIR)/ci/hlint.report.json
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -rf $(BUILDDIR)
|
@rm -rf $(BUILDDIR)
|
||||||
|
|
@ -31,9 +36,10 @@ clean:
|
||||||
distclean: clean
|
distclean: clean
|
||||||
@rm -rf dist
|
@rm -rf dist
|
||||||
|
|
||||||
install: $(BUILDDIR)/bin/annotator
|
install: build
|
||||||
@install -m 0755 -D -t ${BINDIR} $<
|
@install -m 0755 -D -t ${BINDIR} $(BUILDDIR)/bin/annotator
|
||||||
@install -m 0644 -D -t ${DOCDIR} README.md
|
@install -m 0644 -D -t ${DOCDIR} README.org
|
||||||
|
@install -m 0644 -D -t ${DOCDIR} $(BUILDDIR)/doc/README.md
|
||||||
|
|
||||||
release: script/make-release
|
release: script/make-release
|
||||||
@$<
|
@$<
|
||||||
|
|
|
||||||
84
README.md
84
README.md
|
|
@ -1,84 +0,0 @@
|
||||||
# Annotator - an interactive TM2/TM3 annotation tool
|
|
||||||
|
|
||||||
This tool lets you interactively annotate your code given a =defects.err= file.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Get the most recent release, unpack it and add the binary to your PATH.
|
|
||||||
|
|
||||||
## How to use it
|
|
||||||
|
|
||||||
After a /Coverity™/ run, you end up with a =defects.err= and would like to
|
|
||||||
annotate your code accordingly. In order to do so, just execute the =annotator=:
|
|
||||||
|
|
||||||
```
|
|
||||||
annotator
|
|
||||||
```
|
|
||||||
|
|
||||||
This will by default use the =defects.err= file in the current directory and
|
|
||||||
scan it for violations. It will then ask you what to do with each violation - by
|
|
||||||
default only /Newest/ violations will be handled, but this can be overridden
|
|
||||||
with a command-line switch.
|
|
||||||
|
|
||||||
After all violations have been treated, you'll end up with a bunch of =*.fix=
|
|
||||||
files next to each source file - those are the annotated source files - you may
|
|
||||||
run diff on them to check if they look fine or just move them over your original
|
|
||||||
source file.
|
|
||||||
|
|
||||||
### Possible annotations
|
|
||||||
|
|
||||||
The annotator is able to generate three kinds of annotations:
|
|
||||||
|
|
||||||
- *Intentional* - by pressing `i`, this will annotate with `coverity[rule] <reason>`
|
|
||||||
- *False-Positive* - by pressing `f`, this will annotate with `coverity[rule : FALSE] <reason>`
|
|
||||||
- *Todo* - by pressing `t`, this will annotate with a FIXME marker
|
|
||||||
|
|
||||||
## Advanced usage scenarios
|
|
||||||
|
|
||||||
The current annotator supports the following command-line arguments:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ annotator --help
|
|
||||||
Usage: annotator [OPTIONS] files...
|
|
||||||
|
|
||||||
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
|
|
||||||
-i --inplace replace source-file after inserting annotations
|
|
||||||
-V --version show full version information
|
|
||||||
--short-version show just the version number
|
|
||||||
-h --help show usage information
|
|
||||||
-a --all handle all defects not just Newest
|
|
||||||
-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
|
|
||||||
|
|
||||||
The `annotator` allows to annotate in-place, i.e. it will automatically rename
|
|
||||||
the `.fix` file after you are done with all violations within that file. This
|
|
||||||
can be achieved by passing `-i` or `--inplace`.
|
|
||||||
|
|
||||||
### Process all violations
|
|
||||||
|
|
||||||
By default the annotator will only handle *Newest* violations and not those that
|
|
||||||
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`
|
|
||||||
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 `--`.
|
|
||||||
68
README.org
68
README.org
|
|
@ -1,14 +1,54 @@
|
||||||
#+title: Readme
|
#+title: Annotator - an interactive Coverity annotation tool
|
||||||
|
|
||||||
|
* Abstract
|
||||||
|
|
||||||
|
This tool lets you interactively annotate your code given a =defects.err= file. The file should contain file and line information along with the Coverity rule that was violated. An example may look like this:
|
||||||
|
|
||||||
|
#+begin_example csv
|
||||||
|
main.cpp:162:INFO: Newest, autosar_cpp14_a20_8_6_violation: Object "std::unique_ptr<Configuration const, std::default_delete<Configuration const> >(read_configuration(uri))" is not constructed using "std::make_shared".
|
||||||
|
#+end_example
|
||||||
|
|
||||||
* Installation
|
* Installation
|
||||||
|
|
||||||
|
Get the most recent release, unpack it and add the binary to your PATH.
|
||||||
|
|
||||||
* Usage
|
* Usage
|
||||||
|
|
||||||
#+begin_src sh :results output
|
After a /Coverity™/ run, you end up with a =defects.err= and would like to
|
||||||
|
annotate your code accordingly. In order to do so, just execute the =annotator=:
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
annotator
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
This will by default use the =defects.err= file in the current directory and
|
||||||
|
scan it for violations. It will then ask you what to do with each violation - by
|
||||||
|
default only /Newest/ violations will be handled, but this can be overridden
|
||||||
|
with a command-line switch.
|
||||||
|
|
||||||
|
After all violations have been treated, you'll end up with a bunch of =*.fix=
|
||||||
|
files next to each source file - those are the annotated source files - you may
|
||||||
|
run diff on them to check if they look fine or just move them over your original
|
||||||
|
source file.
|
||||||
|
|
||||||
|
** Possible annotations
|
||||||
|
|
||||||
|
The annotator is able to generate three kinds of annotations:
|
||||||
|
|
||||||
|
- Intentional :: by pressing =i=, this will annotate with =coverity[rule] <reason>=
|
||||||
|
- FalsePositive :: by pressing =f=, this will annotate with =coverity[rule : FALSE] <reason>=
|
||||||
|
- Todo :: by pressing =t=, this will annotate with a TODO marker
|
||||||
|
|
||||||
|
* Advanced usage scenarios
|
||||||
|
|
||||||
|
The current annotator supports the following command-line arguments:
|
||||||
|
|
||||||
|
#+name: annotator-help
|
||||||
|
#+begin_src sh :results output :exports both
|
||||||
annotator --help
|
annotator --help
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
#+RESULTS:
|
#+RESULTS: annotator-help
|
||||||
#+begin_example
|
#+begin_example
|
||||||
Usage: annotator [OPTIONS] files...
|
Usage: annotator [OPTIONS] files...
|
||||||
|
|
||||||
|
|
@ -32,3 +72,25 @@ A tool to semi-automatically add Coverity source-code annotations based on found
|
||||||
# a comment
|
# a comment
|
||||||
-- another comment
|
-- another comment
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
|
** In-place annotations
|
||||||
|
|
||||||
|
The =annotator= allows to annotate in-place, i.e. it will automatically rename the =.fix= file after you are done with all violations within that file. This can be achieved by passing =-i= or =--inplace=.
|
||||||
|
|
||||||
|
** Process all violations
|
||||||
|
|
||||||
|
By default the annotator will only handle *Newest* violations and not those that 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= 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:
|
||||||
|
|
||||||
|
#+begin_example haskell
|
||||||
|
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")
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Lines may be disabled by prefixing them with =#= or =--=.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <asap/asap.hpp>
|
#include <asap/asap.hpp>
|
||||||
|
|
||||||
#include <process/process_collection.hpp>
|
#include <process/process_collection.hpp>
|
||||||
#include "process_definition.hpp" // TODO generate to <process/{idl/}process_definition.hpp>
|
#include "process_definition.hpp"
|
||||||
|
|
||||||
namespace process::controller {
|
namespace process::controller {
|
||||||
class DefinitionListener;
|
class DefinitionListener;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef PROCESS_CONTROLLER_PREDICATE_HPP
|
#ifndef PROCESS_CONTROLLER_PREDICATE_HPP
|
||||||
#define PROCESS_CONTROLLER_PREDICATE_HPP
|
#define PROCESS_CONTROLLER_PREDICATE_HPP
|
||||||
|
|
||||||
#include "process_definition.hpp" // TODO: generate to <build>/idl/process/process_definition.hpp
|
#include "process_definition.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
@ -62,8 +62,6 @@ namespace predicate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a function that checks whether the node of a ProcessDefinition matches a given pattern
|
* returns a function that checks whether the node of a ProcessDefinition matches a given pattern
|
||||||
*
|
|
||||||
* TODO implement more sophisticated glob matching, we currently support '*' and exact match
|
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] inline std::function<bool(const ProcessDefinition&)> node_matches(const std::string& pattern) noexcept
|
[[nodiscard]] inline std::function<bool(const ProcessDefinition&)> node_matches(const std::string& pattern) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE InstanceSigs #-}
|
{-# LANGUAGE InstanceSigs #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE BangPatterns #-}
|
{-# LANGUAGE StrictData #-}
|
||||||
|
|
||||||
module Annotator (defaultMain) where
|
module Annotator (defaultMain) where
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ import Control.Monad.IO.Class
|
||||||
import Control.Monad.Trans.Reader
|
import Control.Monad.Trans.Reader
|
||||||
import Control.Exception (evaluate)
|
import Control.Exception (evaluate)
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
import Data.List (delete, intercalate, foldl', sortBy, nub)
|
import Data.List (delete, foldl', sortOn, nub)
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
import Data.Either (rights)
|
import Data.Either (rights)
|
||||||
import Data.Ord
|
import Data.Ord
|
||||||
|
|
@ -31,7 +30,7 @@ import Annotator.Rule
|
||||||
import Annotator.Annotation
|
import Annotator.Annotation
|
||||||
|
|
||||||
version :: Vsn.Version
|
version :: Vsn.Version
|
||||||
version = Vsn.makeVersion [0,0,5,1]
|
version = Vsn.makeVersion [0,0,5,2]
|
||||||
|
|
||||||
type App a = ReaderT Options IO a
|
type App a = ReaderT Options IO a
|
||||||
|
|
||||||
|
|
@ -104,7 +103,7 @@ options =
|
||||||
(ReqArg (\f opts -> opts { todoMarker = f }) "STRING")
|
(ReqArg (\f opts -> opts { todoMarker = f }) "STRING")
|
||||||
"override the default TODO marker with a custom 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 = f : annotationFiles opts }) "FILE")
|
||||||
(unlines [ "load automatic annotation rules"
|
(unlines [ "load automatic annotation rules"
|
||||||
, " some examples:"
|
, " some examples:"
|
||||||
, " " <> show (Intentional (Rule "rule_1") "some reason")
|
, " " <> show (Intentional (Rule "rule_1") "some reason")
|
||||||
|
|
@ -124,7 +123,9 @@ readMaybe s = case reads s of
|
||||||
parseOptions :: [String] -> IO (Options, [String])
|
parseOptions :: [String] -> IO (Options, [String])
|
||||||
parseOptions argv =
|
parseOptions argv =
|
||||||
case getOpt Permute options argv of
|
case getOpt Permute options argv of
|
||||||
(o, n, []) -> pure (foldl' (flip id) defaultOptions o, n)
|
(o, n, []) -> do
|
||||||
|
let o' = foldl' (flip id) defaultOptions o
|
||||||
|
pure (o'{annotationFiles = reverse $ nub $ annotationFiles o'}, n)
|
||||||
(_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))
|
(_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))
|
||||||
where
|
where
|
||||||
header = "Usage: annotator [OPTIONS] files..."
|
header = "Usage: annotator [OPTIONS] files..."
|
||||||
|
|
@ -145,18 +146,18 @@ defaultMain = do
|
||||||
Just FullVersion -> do
|
Just FullVersion -> do
|
||||||
putStr $ unlines
|
putStr $ unlines
|
||||||
[
|
[
|
||||||
"Annotator v" <> (Vsn.showVersion version)
|
"Annotator v" <> Vsn.showVersion version
|
||||||
, "Copyright (c) 2022 Alexander Kobjolke <alexander.kobjolke@atlas-elektronik.com>"
|
, "Copyright (c) 2022 Alexander Kobjolke <alexander.kobjolke@atlas-elektronik.com>"
|
||||||
]
|
]
|
||||||
exitWith ExitSuccess
|
exitSuccess
|
||||||
Just ShortVersion -> do
|
Just ShortVersion -> do
|
||||||
putStrLn $ Vsn.showVersion version
|
putStrLn $ Vsn.showVersion version
|
||||||
exitWith ExitSuccess
|
exitSuccess
|
||||||
Nothing -> pure ()
|
Nothing -> pure ()
|
||||||
|
|
||||||
when (showHelp opts) $ do
|
when (showHelp opts) $ do
|
||||||
putStr $ usageInfo header options
|
putStr $ usageInfo header options
|
||||||
exitWith ExitSuccess
|
exitSuccess
|
||||||
|
|
||||||
automaticAnnotations <- rights . concat <$> (filterM fileExist (annotationFiles opts) >>= mapM fromFile)
|
automaticAnnotations <- rights . concat <$> (filterM fileExist (annotationFiles opts) >>= mapM fromFile)
|
||||||
|
|
||||||
|
|
@ -165,9 +166,8 @@ defaultMain = do
|
||||||
runReaderT (genericMain fn) opts'
|
runReaderT (genericMain fn) opts'
|
||||||
|
|
||||||
where
|
where
|
||||||
header = unlines $
|
header = unlines
|
||||||
[
|
[ "Usage: annotator [OPTIONS] files..."
|
||||||
"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."
|
||||||
, ""
|
, ""
|
||||||
|
|
@ -177,11 +177,11 @@ data Age = Old | New | Newest
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Violation = Violation {
|
data Violation = Violation {
|
||||||
filename :: !FilePath,
|
filename :: FilePath,
|
||||||
line :: !Int,
|
line :: Int,
|
||||||
age :: !Age,
|
age :: Age,
|
||||||
rule :: !Rule,
|
rule :: Rule,
|
||||||
description :: !String
|
description :: String
|
||||||
}
|
}
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@ split _ _ [] = []
|
||||||
split n p xs
|
split n p xs
|
||||||
| n > 0 = case break p xs of
|
| n > 0 = case break p xs of
|
||||||
(match, []) -> [match]
|
(match, []) -> [match]
|
||||||
(match, (_ : rest)) -> match : split (n-1) p rest
|
(match, _ : rest) -> match : split (n-1) p rest
|
||||||
| otherwise = [xs]
|
| otherwise = [xs]
|
||||||
|
|
||||||
parseViolations :: String -> [Violation]
|
parseViolations :: String -> [Violation]
|
||||||
|
|
@ -208,7 +208,7 @@ parseViolation s = case split 4 (== ':') s of
|
||||||
where
|
where
|
||||||
violation = Violation file (read line) a (Rule (removeSuffix "_violation" r)) (dropWhile isSpace $ concat desc)
|
violation = Violation file (read line) a (Rule (removeSuffix "_violation" r)) (dropWhile isSpace $ concat desc)
|
||||||
(_:age':r:_) = split 2 (== ' ') rule'
|
(_:age':r:_) = split 2 (== ' ') rule'
|
||||||
a = case (delete ',' age') of
|
a = case delete ',' age' of
|
||||||
"Newest" -> Newest
|
"Newest" -> Newest
|
||||||
"New" -> New
|
"New" -> New
|
||||||
_ -> Old
|
_ -> Old
|
||||||
|
|
@ -230,7 +230,7 @@ genericMain file = do
|
||||||
verbose Chatty $ "all violations: " <> show sortedViolations
|
verbose Chatty $ "all violations: " <> show sortedViolations
|
||||||
forM_ groupedViolations handleViolations
|
forM_ groupedViolations handleViolations
|
||||||
|
|
||||||
Nothing -> do
|
Nothing ->
|
||||||
liftIO $ hPutStrLn stderr "Defects file is empty"
|
liftIO $ hPutStrLn stderr "Defects file is empty"
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
@ -249,10 +249,10 @@ handleViolations violations = do
|
||||||
let fname = filename $ NE.head violations
|
let fname = filename $ NE.head violations
|
||||||
fname' = fname <> ".fix"
|
fname' = fname <> ".fix"
|
||||||
todo = todoMarker opts
|
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'
|
||||||
, take (length header') $ repeat '='
|
, replicate (length header') '='
|
||||||
, ""
|
, ""
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -268,15 +268,15 @@ handleViolations violations = do
|
||||||
|
|
||||||
let numberedContent = zip [1..] . lines $ content
|
let numberedContent = zip [1..] . lines $ content
|
||||||
|
|
||||||
annotations <- (catMaybes . NE.toList) <$> mapM (handleViolation content) violations
|
annotations <- catMaybes . NE.toList <$> mapM (handleViolation content) violations
|
||||||
|
|
||||||
let annotatedLines = sortBy (comparing fst) $ fmap (annotationToLine todo) annotations
|
let annotatedLines = sortOn 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
|
||||||
|
|
||||||
liftIO $ when (inplace opts) $ rename fname' fname
|
liftIO $ when (inplace opts) $ rename fname' fname
|
||||||
else do
|
else
|
||||||
verbose Low $ "skipping non-existent file " <> fname
|
verbose Low $ "skipping non-existent file " <> fname
|
||||||
where
|
where
|
||||||
annotationToLine :: String -> AnnotatedViolation -> (Int, String)
|
annotationToLine :: String -> AnnotatedViolation -> (Int, String)
|
||||||
|
|
@ -296,24 +296,17 @@ data UserChoice = Abort
|
||||||
|
|
||||||
-- | let the user decide what to do with a violation
|
-- | let the user decide what to do with a violation
|
||||||
getUserChoice :: Violation -> App UserChoice
|
getUserChoice :: Violation -> App UserChoice
|
||||||
getUserChoice Violation{..} = do
|
getUserChoice Violation{..} = liftIO queryUser
|
||||||
liftIO $ queryUser
|
|
||||||
where queryUser = do
|
where queryUser = do
|
||||||
putStr $ "> What shall we do [s/t/i/f/q/?]: "
|
putStr "> What shall we do [s/t/i/f/q/?]: "
|
||||||
hFlush stdout
|
hFlush stdout
|
||||||
c <- getChar
|
c <- getChar
|
||||||
putStrLn ""
|
putStrLn ""
|
||||||
case c of
|
case c of
|
||||||
's' -> do
|
's' -> pure Skip
|
||||||
pure Skip
|
't' -> pure $ Annotate (ToDo rule)
|
||||||
't' -> do
|
'i' -> Annotate . Intentional rule <$> getExcuse
|
||||||
pure $ Annotate (ToDo rule)
|
'f' -> Annotate . FalsePositive rule <$> getExcuse
|
||||||
'i' -> do
|
|
||||||
excuse <- getExcuse
|
|
||||||
pure $ Annotate (Intentional rule excuse)
|
|
||||||
'f' -> do
|
|
||||||
excuse <- getExcuse
|
|
||||||
pure $ Annotate (FalsePositive rule excuse)
|
|
||||||
'q' -> pure Abort
|
'q' -> pure Abort
|
||||||
'?' -> do
|
'?' -> do
|
||||||
putStrLn $ unlines [ "t - add TODO marker to fix this issue"
|
putStrLn $ unlines [ "t - add TODO marker to fix this issue"
|
||||||
|
|
@ -328,7 +321,7 @@ getUserChoice Violation{..} = do
|
||||||
_ -> queryUser
|
_ -> queryUser
|
||||||
|
|
||||||
getExcuse = do
|
getExcuse = do
|
||||||
putStr $ "What's your excuse? "
|
putStr "What's your excuse? "
|
||||||
hFlush stdout
|
hFlush stdout
|
||||||
mode <- hGetBuffering stdin
|
mode <- hGetBuffering stdin
|
||||||
hSetBuffering stdin LineBuffering
|
hSetBuffering stdin LineBuffering
|
||||||
|
|
@ -349,7 +342,7 @@ type NumberedLine = (Int, String)
|
||||||
mergeLines :: [NumberedLine] -> [NumberedLine] -> [NumberedLine]
|
mergeLines :: [NumberedLine] -> [NumberedLine] -> [NumberedLine]
|
||||||
mergeLines [] r = r
|
mergeLines [] r = r
|
||||||
mergeLines l [] = l
|
mergeLines l [] = l
|
||||||
mergeLines !lhs@(left@(nl,_):ls) !rhs@(right@(nr,_):rs)
|
mergeLines lhs@(left@(nl,_):ls) rhs@(right@(nr,_):rs)
|
||||||
| nl <= nr = left : mergeLines ls rhs
|
| nl <= nr = left : mergeLines ls rhs
|
||||||
| otherwise = right : mergeLines lhs rs
|
| otherwise = right : mergeLines lhs rs
|
||||||
|
|
||||||
|
|
@ -379,7 +372,7 @@ handleViolation content v@Violation{..} = do
|
||||||
-- print some context
|
-- print some context
|
||||||
liftIO $ forM_ context (\(n, code) -> do
|
liftIO $ forM_ context (\(n, code) -> do
|
||||||
let marker = ">>>>"
|
let marker = ">>>>"
|
||||||
when (n == line) $ putStrLn (intercalate " " [ marker
|
when (n == line) $ putStrLn (unwords [ marker
|
||||||
, show age
|
, show age
|
||||||
, "violation of rule"
|
, "violation of rule"
|
||||||
, show rule
|
, show rule
|
||||||
|
|
@ -387,13 +380,13 @@ handleViolation content v@Violation{..} = do
|
||||||
, show line <> ":"
|
, show line <> ":"
|
||||||
, description
|
, description
|
||||||
])
|
])
|
||||||
putStrLn (code))
|
putStrLn code)
|
||||||
if batchMode opts
|
if batchMode opts
|
||||||
then pure $ Nothing
|
then pure Nothing
|
||||||
else do
|
else do
|
||||||
choice <- getUserChoice v
|
choice <- getUserChoice v
|
||||||
case choice of
|
case choice of
|
||||||
Abort -> liftIO $ exitSuccess
|
Abort -> liftIO exitSuccess
|
||||||
Annotate annotation -> pure $ Just (AnnotatedViolation v annotation indent)
|
Annotate annotation -> pure $ Just (AnnotatedViolation v annotation indent)
|
||||||
Skip -> pure Nothing
|
Skip -> pure Nothing
|
||||||
Help -> handleViolation content v
|
Help -> handleViolation content v
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
module Annotator.Annotation (Annotation(..), fromFile) where
|
module Annotator.Annotation (Annotation(..), fromFile) where
|
||||||
|
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
module Annotator.Util where
|
module Annotator.Util where
|
||||||
|
|
||||||
anyp :: [a -> Bool] -> a -> Bool
|
anyp :: [a -> Bool] -> a -> Bool
|
||||||
anyp preds x = or (map ($ x) preds)
|
anyp preds x = any ($ x) preds
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue