diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 3c507c98d..8fdeffaea 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -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 diff --git a/addons/misra.py b/addons/misra.py index 6f56f3b58..d4064abeb 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -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 diff --git a/addons/test/misra/misra-ctu-1-test.c b/addons/test/misra/misra-ctu-1-test.c index 48a54eaba..8c5882935 100644 --- a/addons/test/misra/misra-ctu-1-test.c +++ b/addons/test/misra/misra-ctu-1-test.c @@ -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; diff --git a/addons/test/misra/misra-ctu-2-test.c b/addons/test/misra/misra-ctu-2-test.c index 80d94c17c..ca528612f 100644 --- a/addons/test/misra/misra-ctu-2-test.c +++ b/addons/test/misra/misra-ctu-2-test.c @@ -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; diff --git a/addons/test/misra/misra-ctu-test.h b/addons/test/misra/misra-ctu-test.h index ab3b29197..b971cd8a9 100644 --- a/addons/test/misra/misra-ctu-test.h +++ b/addons/test/misra/misra-ctu-test.h @@ -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 + +