misra: implement rule 2.5
This commit is contained in:
parent
8dc1fa7a59
commit
e05a9d7e65
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue