From 157d3be27bdaf96b22dadea52e5cbe92937eb02d Mon Sep 17 00:00:00 2001
From: Grigoris Pavlakis <grigpavl@ece.auth.gr>
Date: Sat, 23 Mar 2019 01:01:03 +0200
Subject: [PATCH] Clean up MISRA scripts according to review comments

Change variable and function names to be more pythonic

Replace the ugly ANSI escapes with more readable, reusable strings

Pad the resulting strings for readability

Close the file after reading its lines

Delete a redundant loop in the parser code, credits to @dimst23

Fix comments to foolow Python conventions

Comment fixing vol. 2

Encapsulate the 2 functions into a class

Fix various blunders with the class encapsulation
---
 ci/cppcheck-misra.sh |  12 ++---
 ci/summarizer.py     | 110 ++++++++++++++++++++++++-------------------
 2 files changed, 64 insertions(+), 58 deletions(-)

diff --git a/ci/cppcheck-misra.sh b/ci/cppcheck-misra.sh
index 96e0c9cd..529f96ae 100755
--- a/ci/cppcheck-misra.sh
+++ b/ci/cppcheck-misra.sh
@@ -14,17 +14,11 @@ curl https://raw.githubusercontent.com/danmar/cppcheck/f4b5b156d720c712f6ce99f6e
 
 # generate dump files (XML representations of AST etc.) for all headers, source files etc.
 echo -e "\u001b[34;1mGenerating dump files...\u001b[0m"
-for file in $(find inc/ src/ -type f \( -iname "*.cpp" -or -iname "*.hpp" \))
-do 
-    cppcheck --dump $file
-done
+find inc/ src/ -type f \( -iname "*.cpp" -or -iname "*.hpp" \) | xargs cppcheck --dump
 
 # run the MISRA checks against the dumps and send the results to a file
 echo -e "\u001b[34;1mRunning MISRA C(2012) rule compliance tests...\u001b[0m"
-for file in $(find inc/ src/ -type f -name "*.dump")
-do 
-    python ci/misra.py $file >> ci/report.msr 2>&1
-done
+find inc/ src/ -type f -name "*.dump" | xargs python3 ci/misra.py >> ci/report.msr 2>&1
 
 # pre-process the generated report to remove all useless strings
 echo -e "\u001b[34;1mPre-processing report...\u001b[0m"
@@ -32,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"
-python ci/summarizer.py ci/report.msr
+python3 ci/summarizer.py ci/report.msr
diff --git a/ci/summarizer.py b/ci/summarizer.py
index 60090ef3..b7a3aa17 100755
--- a/ci/summarizer.py
+++ b/ci/summarizer.py
@@ -1,68 +1,80 @@
-#!/bin/env python
+#!/bin/env python3
 
 from sys import argv
 from sys import exit
 from collections import Counter
 
-'''
+"""
 Naive parser and pretty printer for the MISRA reports by cppcheck
-'''
+"""
 
-script, reportfile = argv
-def analyze():
-    '''
-    A really dumb parser for the pre-processed report generated by cppcheck
-    '''
-    errorsMap = {} # keys: filenames, values: list of tuples containing rule violated and line
-    file = open(reportfile, 'r')
-    fileLines = file.readlines()
-    cppcheckNumOfErrors = len(fileLines) # number of errors in the original report (most of these are duplicates)
-    linesSeen = set() # contains the unique lines from the file
+red = "\033[91m"
+yellow = "\033[93m"
+green = "\033[92m"
+bold = "\033[1m"
+end = "\033[0m"
+
+class Summarizer(object):
+    def __init__(self, report_name):
+        with open(report_name, 'r') as f:
+            self.file_lines = f.readlines()
+        f.close()
+
+    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
     
-    if len(fileLines) == 0:
-        print("\033[1m\033[92mStatic analysis for MISRA compliance complete. No errors found.")
-        return 0
-    else:
-        for line in fileLines:  # remove duplicate lines
-            if line not in linesSeen:
-                linesSeen.add(line)
+        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
+        else:
+            for line in self.file_lines:  # remove duplicate lines
+                if line not in lines_seen:
+                    lines_seen.add(line)
             
-        for line in linesSeen: # after cleaning up the duplicates, split the line into parts
-            lineContents = line.split(':')
-            fileName = lineContents[0] # first part is the filename (index 0)
-            error = (lineContents[1], lineContents[2].strip('\n')) # index 1: line number, index 2: number of violated rule
+                    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
 
-            if fileName not in errorsMap.keys():
-                errorsMap[fileName] = list()  # create a new list for the new filename and append the tuple in it
-                errorsMap[fileName].append(error)
-            else:
-                errorsMap[fileName].append(error) # do not append if it already exists
+                    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)
+                    else:
+                        errors_map[file_name].append(error) # do not append if it already exists
             
-        return errorsMap # return the completed error dictionary
+            return errors_map # return the completed error dictionary
+
+
+    def pretty_print(self, errors):
+        """
+        Pretty-prints the contents of the error dictionary with colors and stuff
+        """
 
+        print(bold + red + "=================================================\n" + end)
+        print(bold + red + "       Static analysis results: Error Summary        \n" + end)
+        for key in errors:
+            for error in errors[key]:
+                name_string = f"{bold}{red}File {yellow}{key}{red}"
+                rule_violated_string = f"violates rule {yellow}#{error[1]}{red} of the MISRA C 2012 standard"
+                line_number_string = f"at line {yellow}{error[0]}{end}"
 
-def prettyprint(errors):
-    '''
-    Prettyprints the contents of the error dictionary with colors and stuff
-    '''
-    print("\033[1m\033[91m=================================================\n")
-    print("\033[1m       Static analysis results: Error Summary        \n")
-    for key in errors:
-        for error in errors[key]:
-            print("\033[1mFile \033[93m{0}\033[91m violates rule \033[93m#{1}\033[91m of the MISRA C 2012 standard at line \033[93m{2}\033[91m".format(key, error[1], error[0]))
-    print ""
-    print ""
-    print("\033[1m=================================================\033[0m\n")
+                print(f"{name_string.ljust(75)} {rule_violated_string} {line_number_string}")
 
+        print("")
+        print("")
+        print(bold + red +"=================================================" + end)
 
-def run():
-    errors = analyze()
+
+if __name__ == "__main__":
+    s = Summarizer(argv[1])
+    errors = s.analyze()
     if isinstance(errors, dict):
-        prettyprint(errors)
+        s.pretty_print(errors)
         exit(127)
     elif isinstance(errors, int) and errors == 0:
         exit(0)
-
-
-if __name__ == "__main__":
-    run()
-- 
GitLab