Skip to content

Commit f775f03

Browse files
committed
Preserve C declaration metadata for MISRA addon checks
1 parent 765b016 commit f775f03

10 files changed

Lines changed: 414 additions & 105 deletions

‎addons/cppcheckdata.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,26 @@ def setId(self, IdMap):
702702
#symboldatabase.cpp/SymbolDatabase::printXml
703703

704704

705+
class VariableDeclaration:
706+
"""One source declaration of a variable with shared object identity."""
707+
708+
def __init__(self, element):
709+
self.nameTokenId = element.get('nameToken')
710+
self.nameToken = None
711+
self.typeStartTokenId = element.get('typeStartToken')
712+
self.typeStartToken = None
713+
self.typeEndTokenId = element.get('typeEndToken')
714+
self.typeEndToken = None
715+
self.isExtern = element.get('isExtern') == 'true'
716+
self.isStatic = element.get('isStatic') == 'true'
717+
self.isInit = element.get('isInit') == 'true'
718+
719+
def setId(self, IdMap):
720+
self.nameToken = IdMap[self.nameTokenId]
721+
self.typeStartToken = IdMap[self.typeStartTokenId]
722+
self.typeEndToken = IdMap[self.typeEndTokenId]
723+
724+
705725
class Variable:
706726
"""
707727
Information about a variable
@@ -726,6 +746,8 @@ class Variable:
726746
isReference Is this variable a reference
727747
isStatic Is this variable static?
728748
isVolatile Is this variable volatile?
749+
declarations Original declarations when several declarations name this object.
750+
Empty for older dumps and variables without merged declarations.
729751
"""
730752
#symboldatabase.cpp/SymbolDatabase::printXml
731753

@@ -753,6 +775,7 @@ class Variable:
753775
constness = 0
754776

755777
def __init__(self, element):
778+
self.declarations = []
756779
self.Id = element.get('id')
757780
self.nameTokenId = element.get('nameToken')
758781
self.nameToken = None
@@ -791,6 +814,8 @@ def setId(self, IdMap):
791814
self.typeStartToken = IdMap[self.typeStartTokenId]
792815
self.typeEndToken = IdMap[self.typeEndTokenId]
793816
self.scope = IdMap[self.scopeId]
817+
for declaration in self.declarations:
818+
declaration.setId(IdMap)
794819

795820
class Container:
796821
"""
@@ -1282,6 +1307,7 @@ def iterconfigurations(self):
12821307
cfg = None
12831308
cfg_arguments = [] # function arguments for Configuration node initialization
12841309
cfg_function = None
1310+
cfg_variable = None
12851311
cfg_valueflow = None
12861312

12871313
# Iterating <varlist> in a <scope>.
@@ -1378,10 +1404,15 @@ def iterconfigurations(self):
13781404
cfg.scopes[-1].varlistId.append(node.get('id'))
13791405
else:
13801406
var = Variable(node)
1407+
cfg_variable = var
13811408
if var.nameTokenId:
13821409
cfg.variables.append(var)
13831410
else:
13841411
cfg_arguments.append(var)
1412+
elif node.tag == 'var' and event == 'end':
1413+
cfg_variable = None
1414+
elif node.tag == 'declaration' and event == 'start' and cfg_variable is not None:
1415+
cfg_variable.declarations.append(VariableDeclaration(node))
13851416

13861417
# Parse containers
13871418
elif node.tag == 'containers' and event == 'start':

‎addons/misra.py‎

Lines changed: 120 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,9 +1662,18 @@ def identifier(nameToken):
16621662
internal_identifiers.append(i)
16631663
else:
16641664
names.append(var.nameToken.str)
1665-
i = identifier(var.nameToken)
1666-
i['decl'] = var.isExtern
1667-
external_identifiers.append(i)
1665+
has_definition = False
1666+
for declaration in var.declarations or [var]:
1667+
if declaration.isExtern and not getattr(declaration, 'isInit', False):
1668+
i = identifier(declaration.nameToken)
1669+
i['decl'] = True
1670+
external_identifiers.append(i)
1671+
else:
1672+
has_definition = True
1673+
if has_definition:
1674+
i = identifier(var.nameToken)
1675+
i['decl'] = False
1676+
external_identifiers.append(i)
16681677

16691678
for func in cfg.functions:
16701679
if func.tokenDef is None:
@@ -1690,21 +1699,31 @@ def _save_ctu_summary_usage(self, dumpfile, cfg):
16901699
return
16911700
self._ctu_summary_usage = True
16921701

1693-
names = []
1702+
declaration_tokens = {declaration.nameToken
1703+
for var in cfg.variables
1704+
for declaration in (var.declarations or [var])}
1705+
names = set()
1706+
usage = []
16941707
for token in cfg.tokenlist:
16951708
if not token.isName:
16961709
continue
16971710
if token.function and token != token.function.tokenDef:
1698-
if (not token.function.isStatic) and (token.str not in names):
1699-
names.append({'name': token.str, 'file': token.file})
1711+
if token.function.isStatic:
1712+
continue
17001713
elif token.variable:
1701-
if token == token.variable.nameToken:
1714+
if token in declaration_tokens:
1715+
continue
1716+
if token.variable.access != 'Global' or token.variable.isStatic:
17021717
continue
1703-
if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names):
1704-
names.append({'name': token.str, 'file': token.file})
1718+
else:
1719+
continue
1720+
key = (token.str, token.file)
1721+
if key not in names:
1722+
names.add(key)
1723+
usage.append({'name': token.str, 'file': token.file})
17051724

1706-
if len(names) > 0:
1707-
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names)
1725+
if len(usage) > 0:
1726+
cppcheckdata.reportSummary(dumpfile, 'MisraUsage', usage)
17081727

17091728

17101729
def misra_1_2(self, cfg):
@@ -2277,30 +2296,28 @@ def misra_8_4(self, cfg):
22772296
continue
22782297
if var.nameToken is None:
22792298
continue
2280-
tok = var.nameToken
2281-
if tok.next.str == ";":
2282-
if tok.next.isSplittedVarDeclEq:
2283-
self.insert_in_dict(extern_var_with_def, tok.str, tok)
2299+
for declaration in var.declarations or [var]:
2300+
tok = declaration.nameToken
2301+
if tok.next.str == ";" and tok.next.isSplittedVarDeclEq:
2302+
self.insert_in_dict(extern_var_with_def, tok.str, declaration)
22842303
else:
2285-
self.insert_in_dict(extern_var_without_def, tok.str, tok)
2286-
else:
2287-
self.insert_in_dict(extern_var_without_def, var.nameToken.str, var.nameToken)
2304+
self.insert_in_dict(extern_var_without_def, tok.str, declaration)
22882305

22892306
for var in extern_var_with_def:
22902307
if var not in extern_var_without_def:
22912308
for t in extern_var_with_def[var]:
2292-
self.reportError(t, 8, 4)
2309+
self.reportError(t.nameToken, 8, 4)
22932310

22942311
for var_str, var_tok in extern_var_without_def.items():
22952312
warn = True
22962313
if var_str not in extern_var_with_def:
22972314
for t in var_tok:
2298-
if t.variable.isExtern:
2315+
if t.isExtern:
22992316
warn = False
23002317
break
23012318
if warn:
23022319
for t in var_tok:
2303-
self.reportError(t, 8, 4)
2320+
self.reportError(t.nameToken, 8, 4)
23042321

23052322
def misra_8_5(self, dumpfile, cfg):
23062323
self._save_ctu_summary_identifiers(dumpfile, cfg)
@@ -2320,9 +2337,9 @@ def misra_8_8(self, cfg):
23202337
continue
23212338
varname = var.nameToken.str
23222339
if varname in vars:
2323-
vars[varname].append(var)
2340+
vars[varname].extend(var.declarations or [var])
23242341
else:
2325-
vars[varname] = [var]
2342+
vars[varname] = list(var.declarations or [var])
23262343
for varname, varlist in vars.items():
23272344
static_var = None
23282345
extern_var = None
@@ -2363,8 +2380,9 @@ def misra_8_10(self, cfg):
23632380

23642381
def misra_8_11(self, data):
23652382
for var in data.variables:
2366-
if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global':
2367-
self.reportError(var.nameToken, 8, 11)
2383+
for declaration in var.declarations or [var]:
2384+
if declaration.isExtern and simpleMatch(declaration.nameToken.next, '[ ]') and declaration.nameToken.scope.type == 'Global':
2385+
self.reportError(declaration.nameToken, 8, 11)
23682386

23692387
def misra_8_12(self, data):
23702388
for scope in data.scopes:
@@ -4908,84 +4926,85 @@ def is_different_file(loc1, loc2):
49084926

49094927
try:
49104928
for filename in ctu_info_files:
4911-
for line in open(filename, 'rt'):
4912-
s = self.read_ctu_info_line(line)
4913-
if s is None:
4914-
continue
4915-
summary_type = s.get('summary', '')
4916-
summary_data = s.get('data', None)
4917-
4918-
if summary_type == 'MisraTypedefInfo':
4919-
for new_typedef_info in summary_data:
4920-
key = new_typedef_info['name']
4921-
existing_typedef_info = all_typedef_info.get(key, None)
4922-
if existing_typedef_info:
4923-
if is_different_location(existing_typedef_info, new_typedef_info):
4924-
self.reportError(Location(existing_typedef_info), 5, 6)
4925-
self.reportError(Location(new_typedef_info), 5, 6)
4929+
with open(filename, 'rt') as ctu_file:
4930+
for line in ctu_file:
4931+
s = self.read_ctu_info_line(line)
4932+
if s is None:
4933+
continue
4934+
summary_type = s.get('summary', '')
4935+
summary_data = s.get('data', None)
4936+
4937+
if summary_type == 'MisraTypedefInfo':
4938+
for new_typedef_info in summary_data:
4939+
key = new_typedef_info['name']
4940+
existing_typedef_info = all_typedef_info.get(key, None)
4941+
if existing_typedef_info:
4942+
if is_different_location(existing_typedef_info, new_typedef_info):
4943+
self.reportError(Location(existing_typedef_info), 5, 6)
4944+
self.reportError(Location(new_typedef_info), 5, 6)
4945+
else:
4946+
existing_typedef_info['used'] = existing_typedef_info['used'] or new_typedef_info['used']
4947+
else:
4948+
all_typedef_info[key] = new_typedef_info
4949+
4950+
if summary_type == 'MisraTagName':
4951+
for new_tagname_info in summary_data:
4952+
key = new_tagname_info['name']
4953+
existing_tagname_info = all_tagname_info.get(key, None)
4954+
if existing_tagname_info:
4955+
if is_different_location(existing_tagname_info, new_tagname_info):
4956+
self.reportError(Location(existing_tagname_info), 5, 7)
4957+
self.reportError(Location(new_tagname_info), 5, 7)
4958+
else:
4959+
existing_tagname_info['used'] = existing_tagname_info['used'] or new_tagname_info['used']
4960+
else:
4961+
all_tagname_info[key] = new_tagname_info
4962+
4963+
if summary_type == 'MisraMacro':
4964+
for new_macro in summary_data:
4965+
key = new_macro['name']
4966+
existing_macro = all_macro_info.get(key, None)
4967+
if existing_macro:
4968+
existing_macro['used'] = existing_macro['used'] or new_macro['used']
4969+
else:
4970+
all_macro_info[key] = new_macro
4971+
4972+
if summary_type == 'MisraExternalIdentifiers':
4973+
for s in sorted(summary_data, key=lambda d: "%s %s %s" %(d['file'],d['line'], d['column'] )):
4974+
is_declaration = s['decl']
4975+
if is_declaration:
4976+
all_external_identifiers = all_external_identifiers_decl
49264977
else:
4927-
existing_typedef_info['used'] = existing_typedef_info['used'] or new_typedef_info['used']
4928-
else:
4929-
all_typedef_info[key] = new_typedef_info
4930-
4931-
if summary_type == 'MisraTagName':
4932-
for new_tagname_info in summary_data:
4933-
key = new_tagname_info['name']
4934-
existing_tagname_info = all_tagname_info.get(key, None)
4935-
if existing_tagname_info:
4936-
if is_different_location(existing_tagname_info, new_tagname_info):
4937-
self.reportError(Location(existing_tagname_info), 5, 7)
4938-
self.reportError(Location(new_tagname_info), 5, 7)
4978+
all_external_identifiers = all_external_identifiers_def
4979+
4980+
name = s['name']
4981+
if name in all_external_identifiers:
4982+
if is_declaration and is_different_location(s, all_external_identifiers[name]):
4983+
self.reportError(Location(s), 8, 5)
4984+
self.reportError(Location(all_external_identifiers[name]), 8, 5)
4985+
elif is_different_file(s, all_external_identifiers[name]):
4986+
self.reportError(Location(s), 8, 6)
4987+
self.reportError(Location(all_external_identifiers[name]), 8, 6)
4988+
all_external_identifiers[name] = s
4989+
4990+
if summary_type == 'MisraInternalIdentifiers':
4991+
for s in summary_data:
4992+
if s['name'] in all_internal_identifiers:
4993+
if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']:
4994+
self.reportError(Location(s), 5, 9)
4995+
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
4996+
all_internal_identifiers[s['name']] = s
4997+
4998+
if summary_type == 'MisraLocalIdentifiers':
4999+
for s in summary_data:
5000+
all_local_identifiers[s['name']] = s
5001+
5002+
if summary_type == 'MisraUsage':
5003+
for s in summary_data:
5004+
if s['name'] in all_usage_files:
5005+
all_usage_files[s['name']].append(s['file'])
49395006
else:
4940-
existing_tagname_info['used'] = existing_tagname_info['used'] or new_tagname_info['used']
4941-
else:
4942-
all_tagname_info[key] = new_tagname_info
4943-
4944-
if summary_type == 'MisraMacro':
4945-
for new_macro in summary_data:
4946-
key = new_macro['name']
4947-
existing_macro = all_macro_info.get(key, None)
4948-
if existing_macro:
4949-
existing_macro['used'] = existing_macro['used'] or new_macro['used']
4950-
else:
4951-
all_macro_info[key] = new_macro
4952-
4953-
if summary_type == 'MisraExternalIdentifiers':
4954-
for s in sorted(summary_data, key=lambda d: "%s %s %s" %(d['file'],d['line'], d['column'] )):
4955-
is_declaration = s['decl']
4956-
if is_declaration:
4957-
all_external_identifiers = all_external_identifiers_decl
4958-
else:
4959-
all_external_identifiers = all_external_identifiers_def
4960-
4961-
name = s['name']
4962-
if name in all_external_identifiers:
4963-
if is_declaration and is_different_location(s, all_external_identifiers[name]):
4964-
self.reportError(Location(s), 8, 5)
4965-
self.reportError(Location(all_external_identifiers[name]), 8, 5)
4966-
elif is_different_file(s, all_external_identifiers[name]):
4967-
self.reportError(Location(s), 8, 6)
4968-
self.reportError(Location(all_external_identifiers[name]), 8, 6)
4969-
all_external_identifiers[name] = s
4970-
4971-
if summary_type == 'MisraInternalIdentifiers':
4972-
for s in summary_data:
4973-
if s['name'] in all_internal_identifiers:
4974-
if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']:
4975-
self.reportError(Location(s), 5, 9)
4976-
self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9)
4977-
all_internal_identifiers[s['name']] = s
4978-
4979-
if summary_type == 'MisraLocalIdentifiers':
4980-
for s in summary_data:
4981-
all_local_identifiers[s['name']] = s
4982-
4983-
if summary_type == 'MisraUsage':
4984-
for s in summary_data:
4985-
if s['name'] in all_usage_files:
4986-
all_usage_files[s['name']].append(s['file'])
4987-
else:
4988-
all_usage_files[s['name']] = [s['file']]
5007+
all_usage_files[s['name']] = [s['file']]
49895008

49905009
except FileNotFoundError:
49915010
return
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "misra-regression-redeclarations.h"
2+
3+
int two_definitions = 10;
4+
5+
static int read_other(void)
6+
{
7+
return elsewhere;
8+
}

0 commit comments

Comments
 (0)