Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 96 additions & 19 deletions addons/misra.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -543,14 +567,16 @@ def __init__(self):
# 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()

# 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:
Expand Down Expand Up @@ -1675,13 +1701,17 @@ def addSuppressedRule(self, ruleNum,
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)
Expand All @@ -1694,7 +1724,7 @@ def addSuppressedRule(self, ruleNum,
ruleItemList.append(line_symbol)

fileDict = dict()
fileDict[fileName] = ruleItemList
fileDict[normalized_filename] = ruleItemList

self.suppressedRules[ruleNum] = fileDict

Expand All @@ -1708,22 +1738,21 @@ def addSuppressedRule(self, ruleNum,
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?
Expand Down Expand Up @@ -1760,8 +1789,15 @@ def isRuleSuppressed(self, location, ruleNum):

"""
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]
Expand All @@ -1777,9 +1813,9 @@ def isRuleSuppressed(self, location, ruleNum):
# 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
Expand Down Expand Up @@ -1815,6 +1851,39 @@ def parseSuppressions(self):
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
Expand Down Expand Up @@ -2061,6 +2130,8 @@ def parseDump(self, dumpfile):
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()
Expand All @@ -2080,6 +2151,9 @@ def parseDump(self, dumpfile):
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:
Expand Down Expand Up @@ -2119,4 +2193,7 @@ def parseDump(self, dumpfile):
if SHOW_SUMMARY:
print("\nMISRA rule violations found: %d\n" % (number_of_violations))

if args.show_suppressed_rules:
checker.showSuppressedRules()

sys.exit(exitCode)
33 changes: 33 additions & 0 deletions addons/test/misra-suppressions1-test.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h> //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
}
}
}
14 changes: 14 additions & 0 deletions addons/test/misra-suppressions2-test.c
Original file line number Diff line number Diff line change
@@ -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";
33 changes: 33 additions & 0 deletions addons/test/path1/misra-suppressions1-test.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h> //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
}
}
}
14 changes: 14 additions & 0 deletions addons/test/path1/misra-suppressions2-test.c
Original file line number Diff line number Diff line change
@@ -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";
7 changes: 7 additions & 0 deletions addons/test/suppressions.txt
Original file line number Diff line number Diff line change
@@ -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