misra: implement rule 2.5
This commit is contained in:
parent
8dc1fa7a59
commit
e05a9d7e65
|
@ -17,6 +17,27 @@ EXIT_CODE = 0
|
|||
|
||||
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:
|
||||
"""
|
||||
Directive class. Contains information about each preprocessor directive in the source code.
|
||||
|
@ -38,12 +59,11 @@ class Directive:
|
|||
str = None
|
||||
file = None
|
||||
linenr = None
|
||||
column = 0
|
||||
column = None
|
||||
|
||||
def __init__(self, element):
|
||||
self.str = element.get('str')
|
||||
self.file = element.get('file')
|
||||
self.linenr = int(element.get('linenr'))
|
||||
_load_location(self, element)
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ["str", "file", "linenr"]
|
||||
|
@ -52,6 +72,28 @@ class Directive:
|
|||
", ".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:
|
||||
"""
|
||||
|
@ -283,9 +325,7 @@ class Token:
|
|||
self.astOperand1 = None
|
||||
self.astOperand2Id = element.get('astOperand2')
|
||||
self.astOperand2 = None
|
||||
self.file = element.get('file')
|
||||
self.linenr = int(element.get('linenr'))
|
||||
self.column = int(element.get('column'))
|
||||
_load_location(self, element)
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned",
|
||||
|
@ -574,16 +614,14 @@ class TypedefInfo:
|
|||
TypedefInfo class -- information about typedefs
|
||||
"""
|
||||
name = None
|
||||
filename = None
|
||||
lineNumber = None
|
||||
column = None
|
||||
used = None
|
||||
file = None
|
||||
linenr = None
|
||||
column = None
|
||||
|
||||
def __init__(self, element):
|
||||
self.name = element.get('name')
|
||||
self.filename = element.get('file')
|
||||
self.lineNumber = int(element.get('line'))
|
||||
self.column = int(element.get('column'))
|
||||
_load_location(self, element)
|
||||
self.used = (element.get('used') == '1')
|
||||
|
||||
class Value:
|
||||
|
@ -720,6 +758,7 @@ class Configuration:
|
|||
Attributes:
|
||||
name Name of the configuration, "" for default
|
||||
directives List of Directive items
|
||||
macro_usage List of used macros
|
||||
tokenlist List of Token items
|
||||
scopes List of Scope items
|
||||
functions List of Function items
|
||||
|
@ -730,6 +769,7 @@ class Configuration:
|
|||
|
||||
name = ''
|
||||
directives = []
|
||||
macro_usage = []
|
||||
tokenlist = []
|
||||
scopes = []
|
||||
functions = []
|
||||
|
@ -741,6 +781,7 @@ class Configuration:
|
|||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.directives = []
|
||||
self.macro_usage = []
|
||||
self.tokenlist = []
|
||||
self.scopes = []
|
||||
self.functions = []
|
||||
|
@ -1000,6 +1041,10 @@ class CppcheckData:
|
|||
elif node.tag == 'directive' and event == 'start':
|
||||
cfg.directives.append(Directive(node))
|
||||
|
||||
# Parse macro usage
|
||||
elif node.tag == 'macro' and event == 'start':
|
||||
cfg.macro_usage.append(MacroUsage(node))
|
||||
|
||||
# Parse tokens
|
||||
elif node.tag == 'tokenlist' and event == 'start':
|
||||
continue
|
||||
|
|
|
@ -25,8 +25,6 @@ import argparse
|
|||
import codecs
|
||||
import string
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
try:
|
||||
from itertools import izip as zip
|
||||
except ImportError:
|
||||
|
@ -1129,7 +1127,7 @@ class MisraChecker:
|
|||
|
||||
summary = []
|
||||
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:
|
||||
cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary)
|
||||
|
||||
|
@ -1167,6 +1165,21 @@ class MisraChecker:
|
|||
cfg = data[1]
|
||||
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):
|
||||
for func in data.functions:
|
||||
# Skip function with no parameter
|
||||
|
@ -3316,6 +3329,7 @@ class MisraChecker:
|
|||
|
||||
self.executeCheck(203, self.misra_2_3, (dumpfile, cfg.typedefInfo))
|
||||
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)
|
||||
# data.rawTokens is same for all configurations
|
||||
if cfgNumber == 0:
|
||||
|
@ -3426,8 +3440,9 @@ class MisraChecker:
|
|||
def analyse_ctu_info(self, files):
|
||||
all_typedef_info = []
|
||||
all_tagname_info = []
|
||||
all_macro_info = []
|
||||
|
||||
Location = namedtuple('Location', 'file linenr column')
|
||||
from cppcheckdata import Location
|
||||
|
||||
for filename in files:
|
||||
if not filename.endswith('.ctu-info'):
|
||||
|
@ -3447,11 +3462,12 @@ class MisraChecker:
|
|||
if old_typedef_info['name'] == new_typedef_info['name']:
|
||||
found = True
|
||||
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(new_typedef_info['file'], new_typedef_info['line'], new_typedef_info['column']), 5, 6)
|
||||
self.reportError(Location(old_typedef_info), 5, 6)
|
||||
self.reportError(Location(new_typedef_info), 5, 6)
|
||||
else:
|
||||
if new_typedef_info['used']:
|
||||
old_typedef_info['used'] = True
|
||||
break
|
||||
if not found:
|
||||
all_typedef_info.append(new_typedef_info)
|
||||
|
||||
|
@ -3462,22 +3478,38 @@ class MisraChecker:
|
|||
if old_tagname_info['name'] == new_tagname_info['name']:
|
||||
found = True
|
||||
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(new_tagname_info['file'], new_tagname_info['line'], new_tagname_info['column']), 5, 7)
|
||||
self.reportError(Location(old_tagname_info), 5, 7)
|
||||
self.reportError(Location(new_tagname_info), 5, 7)
|
||||
else:
|
||||
if new_tagname_info['used']:
|
||||
old_tagname_info['used'] = True
|
||||
break
|
||||
if not found:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
|
||||
|
|
|
@ -14,3 +14,5 @@ struct misra_5_7_violation_t {
|
|||
int x;
|
||||
};
|
||||
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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
#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