From 94a3ef0bdbc63b6179c84043524e64d8ba23e816 Mon Sep 17 00:00:00 2001
From: Grigoris Pavlakis <grigpavl@ece.auth.gr>
Date: Sat, 30 Mar 2019 01:45:04 +0200
Subject: [PATCH] Add rule suppression functionality; just pass , do a general
 refactor to comply with PEP8 and get rid of most of the ugly code

---
 ci/cppcheck-misra.sh |   2 +-
 ci/summarizer.py     | 105 +++++++++++++++++++++++++++----------------
 2 files changed, 67 insertions(+), 40 deletions(-)

diff --git a/ci/cppcheck-misra.sh b/ci/cppcheck-misra.sh
index 16cc6fca..6f9f2197 100755
--- a/ci/cppcheck-misra.sh
+++ b/ci/cppcheck-misra.sh
@@ -26,4 +26,4 @@ sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)//gm; /(^$)/d; s/(\s\(.*\)\s
 
 # run the summarizer for a nice, clean summary of errors
 echo -e "\u001b[34;1mSummarizing results...\u001b[0m"
-python3.6 ci/summarizer.py ci/report.msr
+ci/summarizer.py --report ci/report.msr --suppress 5.2 5.3 12.1 15.5 # these are random, will be replaced
diff --git a/ci/summarizer.py b/ci/summarizer.py
index c39c2a11..af31c29e 100755
--- a/ci/summarizer.py
+++ b/ci/summarizer.py
@@ -1,80 +1,107 @@
 #!/bin/env python3.6
 
-from sys import argv
-from collections import Counter
+from argparse import ArgumentParser
 
 """
 Naive parser and pretty printer for the MISRA reports by cppcheck
 """
 
+
 class Summarizer(object):
-    def __init__(self, report_name):
+    def __init__(self, report_name, suppression_list):
         with open(report_name, 'r') as f:
-            self.file_lines = f.readlines()
+            self.file_lines = f.readlines()  # read the report file
         f.close()
-        
-        self.red = "\033[91m"
+
+        self.red = "\033[91m"  # terminal colors
         self.yellow = "\033[93m"
         self.green = "\033[92m"
         self.bold = "\033[1m"
         self.end = "\033[0m"
 
+        self.violations_map = {}  # dictionary containing filenames, rule violations and line where the violation
+        # occurred
+        self.suppression_list = suppression_list  # list of rule numbers to be suppressed
 
     def analyze(self):
         """
         A really dumb parser for the pre-processed report generated by cppcheck
         """
-    
-        errors_map = {} # dictionary containing filenames, rule violations and line where the violation occurred
-    
-        lines_seen = set() # contains the unique lines from the file
-    
-        if len(self.file_lines) == 0:
-            print(bold + green + "Static analysis for MISRA compliance complete. No errors found." + end)
-            return 0
+
+        lines_seen = set()  # contains the unique lines from the file
+
         for line in self.file_lines:  # remove duplicate lines
             if line not in lines_seen:
                 lines_seen.add(line)
-            
+
                 line_contents = line.split(':')
-                file_name = line_contents[0] # first part is the filename (index 0)
-                error = (line_contents[1], line_contents[2].strip('\n')) # index 1 is the line number, index 2 is the number of violated rule
+                file_name = line_contents[0]  # first part is the filename (index 0)
+                violation = (line_contents[1], line_contents[2].strip(
+                    '\n'))  # index 1 is the line number, index 2 is the number of violated rule (both are strings)
 
-                if file_name not in errors_map.keys():
-                    errors_map[file_name] = list()  # create a new list for the new filename and append the tuple in it
-                    errors_map[file_name].append(error)
+                if file_name not in self.violations_map.keys():
+                    self.violations_map[
+                        file_name] = list()  # create a new list for the new filename and append the tuple w/ line &
+                    # rule no.
+                    self.violations_map[file_name].append(violation)
                 else:
-                    errors_map[file_name].append(error) # do not append if it already exists
-            
-        return errors_map # return the completed error dictionary
+                    self.violations_map[file_name].append(violation)  # do not append if it already exists
+
+        for e in self.suppression_list:
+            for file_name in self.violations_map.keys():
+                self.violations_map[file_name] = [x for x in self.violations_map[file_name] if x[1] != str(e)]
+                # replace the list of infractions with a new one, containing everything except the suppressed rules
 
+        self.violations_map = {k: v for (k, v) in self.violations_map.items() if len(v) != 0}
+        # "delete" all keys whose lists are empty
 
-    def pretty_print(self, errors):
+    def pretty_print_violations(self):
         """
-        Pretty-prints the contents of the error dictionary with colors and stuff
+        Just a pretty printing function, no fancy logic here.
         """
-
-        print(self.bold + self.red + "=================================================\n" + self.end)
-        print(self.bold + self.red + "       Static analysis results: Error Summary        \n" + self.end)
-        for file_name in errors:
+        print(self.bold + self.red + "=========================================================\n" + self.end)
+        print(self.bold + self.red + "       Static analysis results: Infraction summary        \n" + self.end)
+        for file_name in self.violations_map:
             print("")
-            for error in sorted(errors[file_name], key=lambda x: int(x[0])):
+            for violation in sorted(self.violations_map[file_name], key=lambda x: int(x[0])):
                 name_string = f"{self.bold}{self.red}File {self.yellow}{file_name}{self.red}"
-                rule_violated_string = f"violates rule {self.yellow}#{error[1]}{self.red} of the MISRA C 2012 standard"
-                line_number_string = f"at line {self.yellow}{error[0]}{self.end}"
-                
+                rule_violated_string = f"violates rule {self.yellow}#{violation[1]}{self.red} " \
+                    f"of the MISRA C 2012 standard"
+                line_number_string = f"at line {self.yellow}{violation[0]}{self.end}"
+
                 print(f"{name_string.ljust(75)} {rule_violated_string} {line_number_string}")
 
         print("")
         print("")
-        print(self.bold + self.red +"=================================================" + self.end)
+        print(self.bold + self.red + "=================================================" + self.end)
+
+    def suppression_info(self):
+        """
+        Pretty-prints the suppressed rule numbers.
+        :return:
+        """
+        if (len(self.suppression_list) != 0):
+            print(self.bold + self.yellow + "WARNING: Suppressed infractions of rules: ", end="")
+            print(f", ".join(self.suppression_list), end=".")
+            print("")
+            print("")
+        else:
+            print(self.bold + self.green + "All available rules enforced - no suppressions")
 
 
 if __name__ == "__main__":
-    s = Summarizer(argv[1])
-    errors = s.analyze()
-    if isinstance(errors, dict):
-        s.pretty_print(errors)
+    cli = ArgumentParser()
+    cli.add_argument("--report", nargs=1, default="./report.msr")
+    cli.add_argument("--suppress", nargs="*", type=str, default="")
+
+    args = cli.parse_args()
+    s = Summarizer(str(args.report[0]), args.suppress)
+    s.analyze()
+    s.suppression_info()
+
+    if len(s.violations_map) != 0:
+        s.pretty_print_violations()
         exit(127)
-    elif isinstance(errors, int) and errors == 0:
+    elif len(s.violations_map) == 0:
+        print(s.bold + s.green + "Static analysis for MISRA compliance complete. No infractions found." + s.end)
         exit(0)
-- 
GitLab