Fix multiple config dumps

With multiple configurations, option --dump only dumps
the last configuration.

Fix it to dump every configuration.

Also update all Python addons so that they can handle
multiple-configuration dumps.

Additionally run autopep8 on addons/*.py.

The results of 'make test' before and after applying
this commit are identical.
This commit is contained in:
Albert ARIBAUD (3ADEV) 2015-12-14 09:37:26 +01:00
parent dbc7e2f338
commit e7fdb1c825
6 changed files with 169 additions and 78 deletions

View File

@ -12,8 +12,11 @@ import cppcheckdata
import sys
import re
def reportError(token, severity, msg):
sys.stderr.write('[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') cert.py: ' + msg + '\n')
sys.stderr.write(
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') cert.py: ' + msg + '\n')
def isUnpackedStruct(var):
decl = var.typeStartToken
@ -26,17 +29,19 @@ def isUnpackedStruct(var):
linenr = linenr - 1
if linenr == 0:
return True
if re.match(r'#pragma\s+pack\s*\(',line):
if re.match(r'#pragma\s+pack\s*\(', line):
return False
break
decl = decl.next
return False
def isLocalUnpackedStruct(arg):
if arg and arg.str == '&' and not arg.astOperand2:
arg = arg.astOperand1
return arg and arg.variable and (arg.variable.isLocal or arg.variable.isArgument) and isUnpackedStruct(arg.variable)
def isBitwiseOp(token):
return token and (token.str in ['&', '|', '^'])
@ -60,7 +65,8 @@ def exp42(data):
arg2 = token.astOperand2.astOperand1.astOperand2
if token.astOperand1.str == 'memcmp' and (isLocalUnpackedStruct(arg1) or isLocalUnpackedStruct(arg2)):
reportError(token, 'style', 'EXP42-C Comparison of struct padding data (fix either by packing the struct using \'#pragma pack\' or by rewriting the comparison)')
reportError(
token, 'style', 'EXP42-C Comparison of struct padding data (fix either by packing the struct using \'#pragma pack\' or by rewriting the comparison)')
# EXP46-C
# Do not use a bitwise operator with a Boolean-like operand
@ -70,10 +76,14 @@ def exp42(data):
def exp46(data):
for token in data.tokenlist:
if isBitwiseOp(token) and (isComparisonOp(token.astOperand1) or isComparisonOp(token.astOperand2)):
reportError(token, 'style', 'EXP46-C Bitwise operator is used with a Boolean-like operand')
reportError(
token, 'style', 'EXP46-C Bitwise operator is used with a Boolean-like operand')
for arg in sys.argv[1:]:
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)
exp42(data)
exp46(data)
for cfg in data.configurations:
if len(data.configurations) > 1:
print('Checking ' + arg + ', config "' + cfg.name + '"...')
exp42(cfg)
exp46(cfg)

View File

@ -15,13 +15,15 @@ import xml.etree.ElementTree as ET
#
# To iterate through all tokens use such code:
# @code
# data = CppcheckData.parsedump(...)
# data = cppcheckdata.parsedump(...)
# code = ''
# for token in data.tokenlist:
# code = code + token.str + ' '
# print(code)
# @endcode
#
class Token:
Id = None
## Token string
@ -69,7 +71,7 @@ class Token:
#
# Example code:
# @code
# data = CppcheckData.parsedump(...)
# data = cppcheckdata.parsedump(...)
# code = ''
# for token in data.tokenlist:
# code = code + token.str
@ -91,7 +93,7 @@ class Token:
#
# Example code:
# @code
# data = CppcheckData.parsedump(...)
# data = cppcheckdata.parsedump(...)
# code = ''
# for token in data.tokenlist:
# code = code + token.str
@ -119,7 +121,7 @@ class Token:
#
# Example code:
# @code
# data = CppcheckData.parsedump(...)
# data = cppcheckdata.parsedump(...)
# for token in data.tokenlist:
#
# # is this a addition?
@ -127,7 +129,7 @@ class Token:
#
# # print LHS operand
# print(token.astOperand1.str)
#
#
# @endcode
astOperand1 = None
astOperand2Id = None
@ -135,7 +137,7 @@ class Token:
#
# Example code:
# @code
# data = CppcheckData.parsedump(...)
# data = cppcheckdata.parsedump(...)
# for token in data.tokenlist:
#
# # is this a division?
@ -143,7 +145,7 @@ class Token:
#
# # print RHS operand
# print(token.astOperand2.str)
#
#
# @endcode
astOperand2 = None
@ -226,16 +228,20 @@ class Token:
## Scope. Information about global scope, function scopes, class scopes, inner scopes, etc.
# C++ class: http://cppcheck.sourceforge.net/devinfo/doxyoutput/classScope.html
class Scope:
Id = None
classStartId = None
## The { Token for this scope
classStart = None
classEndId = None
## The } Token for this scope
classEnd = None
## Name of this scope. For a function scope, this is the function name, For a class scope, this is the class name.
## Name of this scope.
# For a function scope, this is the function name;
# for a class scope, this is the class name.
className = None
## Type of scope: Global, Function, Class, If, While
type = None
@ -257,7 +263,10 @@ class Scope:
self.nestedIn = IdMap[self.nestedInId]
## Information about a function
# C++ class: http://cppcheck.sourceforge.net/devinfo/doxyoutput/classFunction.html
# C++ class:
# http://cppcheck.sourceforge.net/devinfo/doxyoutput/classFunction.html
class Function:
Id = None
argument = None
@ -281,11 +290,14 @@ class Function:
self.tokenDef = IdMap[self.tokenDefId]
## Information about a variable
# C++ class: http://cppcheck.sourceforge.net/devinfo/doxyoutput/classVariable.html
# C++ class:
# http://cppcheck.sourceforge.net/devinfo/doxyoutput/classVariable.html
class Variable:
Id = None
nameTokenId = None
# name token in variable declaration
## name token in variable declaration
nameToken = None
typeStartTokenId = None
## start token of variable declaration
@ -330,17 +342,21 @@ class Variable:
self.typeEndToken = IdMap[self.typeEndTokenId]
## ValueFlow class
class ValueFlow:
## ValueFlow::Value class
# Each possible value has a ValueFlow::Value item.
# Each ValueFlow::Value either has a intvalue or tokvalue
# C++ class: http://cppcheck.sourceforge.net/devinfo/doxyoutput/classValueFlow_1_1Value.html
# C++ class:
# http://cppcheck.sourceforge.net/devinfo/doxyoutput/classValueFlow_1_1Value.html
class Value:
# integer value
# #integer value
intvalue = None
# token value
## token value
tokvalue = None
# condition where this Value comes from
## condition where this Value comes from
condition = None
def __init__(self, element):
@ -363,25 +379,14 @@ class ValueFlow:
for value in element:
self.values.append(ValueFlow.Value(value))
## Class that makes cppcheck dump data available
#
# To iterate through all tokens use such code:
# @code
# data = CppcheckData.parsedump(...)
# code = ''
# for token in data.tokenlist:
# code = code + token.str + ' '
# print(code)
# @endcode
#
# To iterate through all scopes (functions, types, etc) use such code:
# @code
# data = CppcheckData.parsedump(...)
# for scope in data.scopes:
# print('type:' + scope.type + ' name:' + scope.className)
# @endcode
#
class CppcheckData:
## Configuration class
# This class contains the tokens, scopes, functions, variables and
# value flows for one configuration.
class Configuration:
## Name of the configuration, "" for default
name = ''
## List of Token items
tokenlist = []
## List of Scope items
@ -393,15 +398,15 @@ class CppcheckData:
## List of ValueFlow values
valueflow = []
def __init__(self, filename):
def __init__(self, confignode):
self.name = confignode.get('cfg')
self.tokenlist = []
self.scopes = []
self.functions = []
self.variables = []
self.valueflow = []
data = ET.parse(filename)
for element in data.getroot():
for element in confignode:
if element.tag == 'tokenlist':
for token in element:
self.tokenlist.append(Token(token))
@ -450,11 +455,60 @@ class CppcheckData:
for variable in self.variables:
variable.setId(IdMap)
## Class that makes cppcheck dump data available
# Contains a list of Configuration instances
#
# To iterate through all configurations use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# @endcode
#
# To iterate through all tokens in each configuration use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# code = ''
# for token in cfg.tokenlist:
# code = code + token.str + ' '
# print(' ' + code)
# @endcode
#
# To iterate through all scopes (functions, types, etc) use such code:
# @code
# data = cppcheckdata.parsedump(...)
# for cfg in data.configurations:
# print('cfg: ' + cfg.name)
# for scope in cfg.scopes:
# print(' type:' + scope.type + ' name:' + scope.className)
# @endcode
#
#
class CppcheckData:
## List of Configurations
configurations = []
def __init__(self, filename):
self.configurations = []
data = ET.parse(filename)
# root is 'dumps' node, each config has its own 'dump' subnode.
for cfgnode in data.getroot():
self.configurations.append(Configuration(cfgnode))
## parse a cppcheck dump file
def parsedump(filename):
return CppcheckData(filename)
## Check if type of ast node is float/double
def astIsFloat(token):
if not token:
return False

View File

@ -12,25 +12,29 @@ for arg in sys.argv[1:]:
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)
for token in data.tokenlist:
if token.str != '(' or not token.astOperand1 or token.astOperand2:
continue
for cfg in data.configurations:
if len(data.configurations) > 1:
print('Checking ' + arg + ', config "' + cfg.name + '"...')
for token in cfg.tokenlist:
if token.str != '(' or not token.astOperand1 or token.astOperand2:
continue
# we probably have a cast.. if there is something inside the parentheses
# there is a cast. Otherwise this is a function call.
typetok = token.next
if not typetok.isName:
continue
# we probably have a cast.. if there is something inside the parentheses
# there is a cast. Otherwise this is a function call.
typetok = token.next
if not typetok.isName:
continue
# cast number => skip output
if token.astOperand1.isNumber:
continue
# cast number => skip output
if token.astOperand1.isNumber:
continue
# void cast => often used to suppress compiler warnings
if typetok.str == 'void':
continue
# void cast => often used to suppress compiler warnings
if typetok.str == 'void':
continue
msg = '[' + token.file + ':' + str(token.linenr) + '] (information) findcasts.py: found a cast\n'
if not msg in messages:
messages.append(msg)
sys.stderr.write(msg)
msg = '[' + token.file + ':' + str(
token.linenr) + '] (information) findcasts.py: found a cast\n'
if not msg in messages:
messages.append(msg)
sys.stderr.write(msg)

View File

@ -21,21 +21,27 @@ for arg in sys.argv[1:]:
def reportError(token, severity, msg):
sys.stderr.write('[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') naming.py: ' + msg + '\n')
sys.stderr.write(
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') naming.py: ' + msg + '\n')
for arg in sys.argv[1:]:
if not arg[-5:] == '.dump':
continue
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)
if RE_VARNAME:
for var in data.variables:
res = re.match(RE_VARNAME, var.nameToken.str)
if not res:
reportError(var.typeStartToken, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention')
if RE_FUNCTIONNAME:
for scope in data.scopes:
if scope.type == 'Function':
res = re.match(RE_FUNCTIONNAME, scope.className)
for cfg in data.configurations:
if len(data.configurations) > 1:
print('Checking ' + arg + ', config "' + cfg.name + '"...')
if RE_VARNAME:
for var in cfg.variables:
res = re.match(RE_VARNAME, var.nameToken.str)
if not res:
reportError(scope.classStart, 'style', 'Function ' + scope.className + ' violates naming convention')
reportError(var.typeStartToken, 'style', 'Variable ' +
var.nameToken.str + ' violates naming convention')
if RE_FUNCTIONNAME:
for scope in cfg.scopes:
if scope.type == 'Function':
res = re.match(RE_FUNCTIONNAME, scope.className)
if not res:
reportError(
scope.classStart, 'style', 'Function ' + scope.className + ' violates naming convention')

View File

@ -9,7 +9,8 @@ import sys
def reportError(token, severity, msg):
sys.stderr.write('[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') threadsafety.py: ' + msg + '\n')
sys.stderr.write(
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') threadsafety.py: ' + msg + '\n')
def checkstatic(data):
@ -20,4 +21,7 @@ def checkstatic(data):
for arg in sys.argv[1:]:
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)
checkstatic(data)
for cfg in data.configurations:
if len(data.configurations) > 1:
print('Checking ' + arg + ', config "' + cfg.name + '"...')
checkstatic(cfg)

View File

@ -143,6 +143,17 @@ unsigned int CppCheck::processFile(const std::string& filename, std::istream& fi
}
}
// write dump file xml prolog
std::ofstream fdump;
if (_settings.dump) {
const std::string dumpfile(filename + ".dump");
fdump.open(dumpfile.c_str());
if (fdump.is_open()) {
fdump << "<?xml version=\"1.0\"?>" << std::endl;
fdump << "<dumps>" << std::endl;
}
}
std::set<unsigned long long> checksums;
unsigned int checkCount = 0;
for (std::list<std::string>::const_iterator it = configurations.begin(); it != configurations.end(); ++it) {
@ -203,11 +214,9 @@ unsigned int CppCheck::processFile(const std::string& filename, std::istream& fi
if (!result)
continue;
// dump xml
// dump xml if --dump
if (_settings.dump) {
std::ofstream fdump((filename + ".dump").c_str());
if (fdump.is_open()) {
fdump << "<?xml version=\"1.0\"?>" << std::endl;
fdump << "<dump cfg=\"" << cfg << "\">" << std::endl;
_tokenizer.dump(fdump);
fdump << "</dump>" << std::endl;
@ -265,6 +274,10 @@ unsigned int CppCheck::processFile(const std::string& filename, std::istream& fi
}
}
// dumped all configs, close root </dumps> element now
if (_settings.dump && fdump.is_open())
fdump << "</dumps>" << std::endl;
} catch (const std::runtime_error &e) {
internalError(filename, e.what());
} catch (const InternalError &e) {