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 = []
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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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