misra: implement rule 2.5

This commit is contained in:
Daniel Marjamäki 2021-07-08 22:03:27 +02:00
parent 8dc1fa7a59
commit e05a9d7e65
5 changed files with 109 additions and 22 deletions

View File

@ -17,6 +17,27 @@ EXIT_CODE = 0
current_dumpfile_suppressions = [] current_dumpfile_suppressions = []
def _load_location(location, element):
"""Load location from element/dict"""
location.file = element.get('file')
line = element.get('line')
if line is None:
line = element.get('linenr')
if line is None:
line = '0'
location.linenr = int(line)
location.column = int(element.get('column', '0'))
class Location:
"""Utility location class"""
file = None
linenr = None
column = None
def __init__(self, element):
_load_location(self, element)
class Directive: class Directive:
""" """
Directive class. Contains information about each preprocessor directive in the source code. Directive class. Contains information about each preprocessor directive in the source code.
@ -38,12 +59,11 @@ class Directive:
str = None str = None
file = None file = None
linenr = None linenr = None
column = 0 column = None
def __init__(self, element): def __init__(self, element):
self.str = element.get('str') self.str = element.get('str')
self.file = element.get('file') _load_location(self, element)
self.linenr = int(element.get('linenr'))
def __repr__(self): def __repr__(self):
attrs = ["str", "file", "linenr"] attrs = ["str", "file", "linenr"]
@ -52,6 +72,28 @@ class Directive:
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
) )
class MacroUsage:
"""
Tracks preprocessor macro usage
"""
name = None # Macro name
file = None
linenr = None
column = None
def __init__(self, element):
self.name = element.get('name')
_load_location(self, element)
def __repr__(self):
attrs = ["name", "file", "linenr", "column"]
return "{}({})".format(
"Directive",
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
)
class ValueType: class ValueType:
""" """
@ -283,9 +325,7 @@ class Token:
self.astOperand1 = None self.astOperand1 = None
self.astOperand2Id = element.get('astOperand2') self.astOperand2Id = element.get('astOperand2')
self.astOperand2 = None self.astOperand2 = None
self.file = element.get('file') _load_location(self, element)
self.linenr = int(element.get('linenr'))
self.column = int(element.get('column'))
def __repr__(self): def __repr__(self):
attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned", attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned",
@ -574,16 +614,14 @@ class TypedefInfo:
TypedefInfo class -- information about typedefs TypedefInfo class -- information about typedefs
""" """
name = None name = None
filename = None
lineNumber = None
column = None
used = None used = None
file = None
linenr = None
column = None
def __init__(self, element): def __init__(self, element):
self.name = element.get('name') self.name = element.get('name')
self.filename = element.get('file') _load_location(self, element)
self.lineNumber = int(element.get('line'))
self.column = int(element.get('column'))
self.used = (element.get('used') == '1') self.used = (element.get('used') == '1')
class Value: class Value:
@ -720,6 +758,7 @@ class Configuration:
Attributes: Attributes:
name Name of the configuration, "" for default name Name of the configuration, "" for default
directives List of Directive items directives List of Directive items
macro_usage List of used macros
tokenlist List of Token items tokenlist List of Token items
scopes List of Scope items scopes List of Scope items
functions List of Function items functions List of Function items
@ -730,6 +769,7 @@ class Configuration:
name = '' name = ''
directives = [] directives = []
macro_usage = []
tokenlist = [] tokenlist = []
scopes = [] scopes = []
functions = [] functions = []
@ -741,6 +781,7 @@ class Configuration:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.directives = [] self.directives = []
self.macro_usage = []
self.tokenlist = [] self.tokenlist = []
self.scopes = [] self.scopes = []
self.functions = [] self.functions = []
@ -1000,6 +1041,10 @@ class CppcheckData:
elif node.tag == 'directive' and event == 'start': elif node.tag == 'directive' and event == 'start':
cfg.directives.append(Directive(node)) cfg.directives.append(Directive(node))
# Parse macro usage
elif node.tag == 'macro' and event == 'start':
cfg.macro_usage.append(MacroUsage(node))
# Parse tokens # Parse tokens
elif node.tag == 'tokenlist' and event == 'start': elif node.tag == 'tokenlist' and event == 'start':
continue continue

View File

@ -25,8 +25,6 @@ import argparse
import codecs import codecs
import string import string
from collections import namedtuple
try: try:
from itertools import izip as zip from itertools import izip as zip
except ImportError: except ImportError:
@ -1129,7 +1127,7 @@ class MisraChecker:
summary = [] summary = []
for ti in typedef_info: for ti in typedef_info:
summary.append({ 'name': ti.name, 'file': ti.filename, 'line': ti.lineNumber, 'column': ti.column, 'used': ti.used }) summary.append({ 'name': ti.name, 'file': ti.file, 'line': ti.linenr, 'column': ti.column, 'used': ti.used })
if len(summary) > 0: if len(summary) > 0:
cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary) cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary)
@ -1167,6 +1165,21 @@ class MisraChecker:
cfg = data[1] cfg = data[1]
self._save_ctu_summary_tagnames(dumpfile, cfg) self._save_ctu_summary_tagnames(dumpfile, cfg)
def misra_2_5(self, data):
dumpfile = data[0]
cfg = data[1]
used_macros = list()
for m in cfg.macro_usage:
used_macros.append(m.name)
summary = []
for directive in cfg.directives:
res = re.match(r'#define[ \t]+([a-zA-Z_][a-zA-Z_0-9]*).*', directive.str)
if res:
macro_name = res.group(1)
summary.append({'name': macro_name, 'used': (macro_name in used_macros), 'file': directive.file, 'line': directive.linenr, 'column': directive.column})
if len(summary) > 0:
cppcheckdata.reportSummary(dumpfile, 'MisraMacro', summary)
def misra_2_7(self, data): def misra_2_7(self, data):
for func in data.functions: for func in data.functions:
# Skip function with no parameter # Skip function with no parameter
@ -3316,6 +3329,7 @@ class MisraChecker:
self.executeCheck(203, self.misra_2_3, (dumpfile, cfg.typedefInfo)) self.executeCheck(203, self.misra_2_3, (dumpfile, cfg.typedefInfo))
self.executeCheck(204, self.misra_2_4, (dumpfile, cfg)) self.executeCheck(204, self.misra_2_4, (dumpfile, cfg))
self.executeCheck(205, self.misra_2_5, (dumpfile, cfg))
self.executeCheck(207, self.misra_2_7, cfg) self.executeCheck(207, self.misra_2_7, cfg)
# data.rawTokens is same for all configurations # data.rawTokens is same for all configurations
if cfgNumber == 0: if cfgNumber == 0:
@ -3426,8 +3440,9 @@ class MisraChecker:
def analyse_ctu_info(self, files): def analyse_ctu_info(self, files):
all_typedef_info = [] all_typedef_info = []
all_tagname_info = [] all_tagname_info = []
all_macro_info = []
Location = namedtuple('Location', 'file linenr column') from cppcheckdata import Location
for filename in files: for filename in files:
if not filename.endswith('.ctu-info'): if not filename.endswith('.ctu-info'):
@ -3447,11 +3462,12 @@ class MisraChecker:
if old_typedef_info['name'] == new_typedef_info['name']: if old_typedef_info['name'] == new_typedef_info['name']:
found = True found = True
if old_typedef_info['file'] != new_typedef_info['file'] or old_typedef_info['line'] != new_typedef_info['line']: if old_typedef_info['file'] != new_typedef_info['file'] or old_typedef_info['line'] != new_typedef_info['line']:
self.reportError(Location(old_typedef_info['file'], old_typedef_info['line'], old_typedef_info['column']), 5, 6) self.reportError(Location(old_typedef_info), 5, 6)
self.reportError(Location(new_typedef_info['file'], new_typedef_info['line'], new_typedef_info['column']), 5, 6) self.reportError(Location(new_typedef_info), 5, 6)
else: else:
if new_typedef_info['used']: if new_typedef_info['used']:
old_typedef_info['used'] = True old_typedef_info['used'] = True
break
if not found: if not found:
all_typedef_info.append(new_typedef_info) all_typedef_info.append(new_typedef_info)
@ -3462,22 +3478,38 @@ class MisraChecker:
if old_tagname_info['name'] == new_tagname_info['name']: if old_tagname_info['name'] == new_tagname_info['name']:
found = True found = True
if old_tagname_info['file'] != new_tagname_info['file'] or old_tagname_info['line'] != new_tagname_info['line']: if old_tagname_info['file'] != new_tagname_info['file'] or old_tagname_info['line'] != new_tagname_info['line']:
self.reportError(Location(old_tagname_info['file'], old_tagname_info['line'], old_tagname_info['column']), 5, 7) self.reportError(Location(old_tagname_info), 5, 7)
self.reportError(Location(new_tagname_info['file'], new_tagname_info['line'], new_tagname_info['column']), 5, 7) self.reportError(Location(new_tagname_info), 5, 7)
else: else:
if new_tagname_info['used']: if new_tagname_info['used']:
old_tagname_info['used'] = True old_tagname_info['used'] = True
break
if not found: if not found:
all_tagname_info.append(new_tagname_info) all_tagname_info.append(new_tagname_info)
if summary_type == 'MisraMacro':
for new_macro in summary_data:
found = False
for old_macro in all_macro_info:
if old_macro['name'] == new_macro['name']:
found = True
if new_macro['used']:
old_macro['used'] = True
break
if not found:
all_macro_info.append(new_macro)
for ti in all_typedef_info: for ti in all_typedef_info:
if not ti['used']: if not ti['used']:
self.reportError(Location(ti['file'], ti['line'], ti['column']), 2, 3) self.reportError(Location(ti), 2, 3)
for ti in all_tagname_info: for ti in all_tagname_info:
if not ti['used']: if not ti['used']:
self.reportError(Location(ti['file'], ti['line'], ti['column']), 2, 4) self.reportError(Location(ti), 2, 4)
for m in all_macro_info:
if not m['used']:
self.reportError(Location(m), 2, 5)
RULE_TEXTS_HELP = '''Path to text file of MISRA rules RULE_TEXTS_HELP = '''Path to text file of MISRA rules

View File

@ -14,3 +14,5 @@ struct misra_5_7_violation_t {
int x; int x;
}; };
static misra_5_7_violation_t misra_5_7_var; static misra_5_7_violation_t misra_5_7_var;
x = MISRA_2_5_OK_1;

View File

@ -14,3 +14,5 @@ struct misra_5_7_violation_t {
int x; int x;
}; };
static misra_5_7_violation_t misra_5_7_var; static misra_5_7_violation_t misra_5_7_var;
x = MISRA_2_5_OK_2;

View File

@ -9,3 +9,9 @@ struct misra_2_4_violation_t {
int x; int x;
}; };
#define MISRA_2_5_OK_1 1
#define MISRA_2_5_OK_2 2
// cppcheck-suppress misra-c2012-2.5
#define MISRA_2_5_VIOLATION 0