diff --git a/addons/misra.py b/addons/misra.py index 19f5abab8..c61f561c9 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -521,6 +521,30 @@ def generateTable(): sys.exit(1) +def remove_file_prefix(file_path, prefix): + """ + Remove a file path prefix from a give path. leftover + directory seperators at the beginning of a file + after the removal are also stripped. + + Example: + '/remove/this/path/file.c' + with a prefix of: + '/remove/this/path' + becomes: + file.c + """ + result = None + if file_path.startswith(prefix): + result = file_path[len(prefix):] + # Remove any leftover directory seperators at the + # beginning + result = result.lstrip('\\/') + else: + result = file_path + return result + + class MisraChecker: def __init__(self): @@ -543,7 +567,7 @@ class MisraChecker: # Major * 100 + minor. ie Rule 5.2 = (5*100) + 2 # Dict 2 is keyed by filename. An entry of None means suppress globaly. # Each file name entry contails a list of tuples of (lineNumber, symbolName) - # or None which indicates suppress rule for the entire file. + # or an item of None which indicates suppress rule for the entire file. # The line and symbol name tuple may have None as either of its elements but # should not be None for both. self.suppressedRules = dict() @@ -551,6 +575,8 @@ class MisraChecker: # List of suppression extracted from the dumpfile self.dumpfileSuppressions = None + # Prefix to ignore when matching suppression files. + self.filePrefix = None def misra_3_1(self, rawTokens): for token in rawTokens: @@ -1678,13 +1704,17 @@ class MisraChecker: format. The value of that dictionary is a dictionary of filenames. If the value is None then the rule is assumed to be suppressed for all files. - If the filename exists then the value of that dictionary contains the - scope of the suppression. If the value is None then the rule is assumed - to be suppresed for the entire file. Otherwise the value of the dictionary - is a list of line number, symbol name tuples. + If the filename exists then the value of that dictionary contains a list + with the scope of the suppression. If the list contains an item of None + then the rule is assumed to be suppresed for the entire file. Otherwise + the list contains line number, symbol name tuples. For each tuple either line number or symbol name can can be none. """ + normalized_filename = None + + if fileName is not None: + normalized_filename = os.path.normpath(fileName) if lineNumber is not None or symbolName is not None: line_symbol = (lineNumber, symbolName) @@ -1697,7 +1727,7 @@ class MisraChecker: ruleItemList.append(line_symbol) fileDict = dict() - fileDict[fileName] = ruleItemList + fileDict[normalized_filename] = ruleItemList self.suppressedRules[ruleNum] = fileDict @@ -1711,22 +1741,21 @@ class MisraChecker: fileDict = self.suppressedRules[ruleNum] # If the filename is not in the dict already add it - if not fileName in fileDict: + if not normalized_filename in fileDict: ruleItemList = list() ruleItemList.append(line_symbol) - fileDict[fileName] = ruleItemList + fileDict[normalized_filename] = ruleItemList # Rule is added with a file scope. Done return - # Rule has a matching filename. Check for - # rule a rule item list. + # Rule has a matching filename. Get the rule item list. - # If it exists then check the lists of rule items - # to see if the lineNumber, symbonName combination - # exists - ruleItemList = fileDict[fileName] + # Check the lists of rule items + # to see if this (lineNumber, symbonName) combination + # or None already exists. + ruleItemList = fileDict[normalized_filename] if line_symbol is None: # is it already in the list? @@ -1763,8 +1792,15 @@ class MisraChecker: """ ruleIsSuppressed = False - filename = location.file - linenr = location.linenr + linenr = location.linenr + + # Remove any prefix listed in command arguments from the filename. + filename = None + if location.file is not None: + if self.filePrefix is not None: + filename = remove_file_prefix(location.file, self.filePrefix) + else: + filename = location.file if ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] @@ -1780,9 +1816,9 @@ class MisraChecker: # Get the list of ruleItems ruleItemList = fileDict[filename] - if ruleItemList is None: - # None for itemRuleList means the rule is suppressed - # for all lines in the filename + if None in ruleItemList: + # Entry of None in the ruleItemList means the rule is + # suppressed for all lines in the filename ruleIsSuppressed = True else: # Iterate though the the list of line numbers @@ -1818,6 +1854,39 @@ class MisraChecker: each.lineNumber, each.symbolName) + def showSuppressedRules(self): + """ + Print out rules in suppression list sorted by Rule Number + """ + print("Suppressed Rules List:") + outlist = list() + + for ruleNum in self.suppressedRules: + fileDict = self.suppressedRules[ruleNum] + + for fname in fileDict: + ruleItemList = fileDict[fname] + + for item in ruleItemList: + if item is None: + item_str = "None" + else: + item_str = str(item[0]) + + outlist.append("%s: %s: %s" % (float(ruleNum)/100,fname,item_str)) + + for line in sorted(outlist, reverse=True): + print(" %s" % line) + + + def setFilePrefix(self, prefix): + """ + Set the file prefix to ignnore from files when matching + supression files + """ + self.filePrefix = prefix + + def setSuppressionList(self, suppressionlist): num1 = 0 num2 = 0 @@ -2064,6 +2133,8 @@ parser.add_argument("--no-summary", help="Hide summary of violations", action="s parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck") +parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") +parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") args = parser.parse_args() checker = MisraChecker() @@ -2083,6 +2154,9 @@ else: if args.suppress_rules: checker.setSuppressionList(args.suppress_rules) + if args.file_prefix: + checker.setFilePrefix(args.file_prefix) + if args.quiet: QUIET = True if args.no_summary: @@ -2122,4 +2196,7 @@ else: if SHOW_SUMMARY: print("\nMISRA rule violations found: %d\n" % (number_of_violations)) + if args.show_suppressed_rules: + checker.showSuppressedRules() + sys.exit(exitCode) diff --git a/addons/test/misra-suppressions1-test.c b/addons/test/misra-suppressions1-test.c new file mode 100644 index 000000000..59c77598e --- /dev/null +++ b/addons/test/misra-suppressions1-test.c @@ -0,0 +1,33 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +#include //21.6 + +extern int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +void misra_5_2_function_hides_var_31y(void) {}//5.2 +void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //14.4 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //14.4 + } + } +} diff --git a/addons/test/misra-suppressions2-test.c b/addons/test/misra-suppressions2-test.c new file mode 100644 index 000000000..7fadf1869 --- /dev/null +++ b/addons/test/misra-suppressions2-test.c @@ -0,0 +1,14 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 +const char *s41_2 = "\x41\x42"; diff --git a/addons/test/path1/misra-suppressions1-test.c b/addons/test/path1/misra-suppressions1-test.c new file mode 100644 index 000000000..59c77598e --- /dev/null +++ b/addons/test/path1/misra-suppressions1-test.c @@ -0,0 +1,33 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +#include //21.6 + +extern int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +void misra_5_2_function_hides_var_31y(void) {}//5.2 +void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //14.4 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //14.4 + } + } +} diff --git a/addons/test/path1/misra-suppressions2-test.c b/addons/test/path1/misra-suppressions2-test.c new file mode 100644 index 000000000..7fadf1869 --- /dev/null +++ b/addons/test/path1/misra-suppressions2-test.c @@ -0,0 +1,14 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 +const char *s41_2 = "\x41\x42"; diff --git a/addons/test/suppressions.txt b/addons/test/suppressions.txt new file mode 100644 index 000000000..acac96847 --- /dev/null +++ b/addons/test/suppressions.txt @@ -0,0 +1,7 @@ +misra_21.6:misra-suppressions1-test.c:7 +misra_14_4 +misra.5.2 +MISRA_16_4:misra-suppressions1-test.c +MISRA.16.6:misra-suppressions1-test.c +MISRA_4_1:misra-suppressions2-test.c +MISRA.19_2:misra-suppressions2-test.c