diff --git a/addons/cert.py b/addons/cert.py index 18293c081..d2093f3df 100644 --- a/addons/cert.py +++ b/addons/cert.py @@ -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) diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 50c43bdf5..0c5c3acf9 100644 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -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 diff --git a/addons/findcasts.py b/addons/findcasts.py index f1d9a2a51..dc418771a 100644 --- a/addons/findcasts.py +++ b/addons/findcasts.py @@ -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) diff --git a/addons/naming.py b/addons/naming.py index f3188862f..bb650949d 100644 --- a/addons/naming.py +++ b/addons/naming.py @@ -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') diff --git a/addons/threadsafety.py b/addons/threadsafety.py index c192c6528..67637deb9 100644 --- a/addons/threadsafety.py +++ b/addons/threadsafety.py @@ -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) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 2f34a0c05..a4de5a298 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -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 << "" << std::endl; + fdump << "" << std::endl; + } + } + std::set checksums; unsigned int checkCount = 0; for (std::list::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 << "" << std::endl; fdump << "" << std::endl; _tokenizer.dump(fdump); fdump << "" << std::endl; @@ -265,6 +274,10 @@ unsigned int CppCheck::processFile(const std::string& filename, std::istream& fi } } + // dumped all configs, close root element now + if (_settings.dump && fdump.is_open()) + fdump << "" << std::endl; + } catch (const std::runtime_error &e) { internalError(filename, e.what()); } catch (const InternalError &e) {