diff -Nru cppcheck-1.85/addons/cert.py cppcheck-1.86/addons/cert.py --- cppcheck-1.85/addons/cert.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/addons/cert.py 2018-12-08 07:18:21.000000000 +0000 @@ -194,7 +194,7 @@ if token.valueType.sign == 'unsigned': found = False for value in token.astOperand1.values: - if value.intvalue < 0: + if value.intvalue and value.intvalue < 0: found = True reportError( token, @@ -215,7 +215,7 @@ minval = 0 maxval = ((1 << bits) - 1) for value in token.astOperand1.values: - if value.intvalue < minval or value.intvalue > maxval: + if value.intvalue and (value.intvalue < minval or value.intvalue > maxval): destType = '' if token.valueType.sign: destType = token.valueType.sign + ' ' + token.valueType.type diff -Nru cppcheck-1.85/addons/cppcheckdata.py cppcheck-1.86/addons/cppcheckdata.py --- cppcheck-1.85/addons/cppcheckdata.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/addons/cppcheckdata.py 2018-12-08 07:18:21.000000000 +0000 @@ -445,23 +445,45 @@ Value class Attributes: - intvalue integer value - tokvalue token value - condition condition where this Value comes from + intvalue integer value + tokvalue token value + floatvalue float value + containerSize container size + condition condition where this Value comes from + valueKind 'known' or 'possible' + inconclusive Is value inconclusive? """ intvalue = None tokvalue = None + floatvalue = None + containerSize = None condition = None + valueKind = None + inconclusive = False + + def isKnown(self): + return self.valueKind and self.valueKind == 'known' + + def isPossible(self): + return self.valueKind and self.valueKind == 'possible' def __init__(self, element): self.intvalue = element.get('intvalue') if self.intvalue: self.intvalue = int(self.intvalue) self.tokvalue = element.get('tokvalue') + self.floatvalue = element.get('floatvalue') + self.containerSize = element.get('container-size') self.condition = element.get('condition-line') if self.condition: self.condition = int(self.condition) + if element.get('known'): + valueKind = 'known' + elif element.get('possible'): + valueKind = 'possible' + if element.get('inconclusive'): + inconclusive = 'known' def __init__(self, element): self.Id = element.get('id') diff -Nru cppcheck-1.85/addons/misra.py cppcheck-1.86/addons/misra.py --- cppcheck-1.85/addons/misra.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/addons/misra.py 2018-12-08 07:18:21.000000000 +0000 @@ -521,6 +521,30 @@ sys.exit(1) +def remove_file_prefix(file_path, prefix): + """ + Remove a file path prefix from a give path. leftover + directory seperators at the beginning of a file + after the removal are also stripped. + + Example: + '/remove/this/path/file.c' + with a prefix of: + '/remove/this/path' + becomes: + file.c + """ + result = None + if file_path.startswith(prefix): + result = file_path[len(prefix):] + # Remove any leftover directory seperators at the + # beginning + result = result.lstrip('\\/') + else: + result = file_path + return result + + class MisraChecker: def __init__(self): @@ -543,7 +567,7 @@ # Major * 100 + minor. ie Rule 5.2 = (5*100) + 2 # Dict 2 is keyed by filename. An entry of None means suppress globaly. # Each file name entry contails a list of tuples of (lineNumber, symbolName) - # or None which indicates suppress rule for the entire file. + # or an item of None which indicates suppress rule for the entire file. # The line and symbol name tuple may have None as either of its elements but # should not be None for both. self.suppressedRules = dict() @@ -551,6 +575,8 @@ # List of suppression extracted from the dumpfile self.dumpfileSuppressions = None + # Prefix to ignore when matching suppression files. + self.filePrefix = None def misra_3_1(self, rawTokens): for token in rawTokens: @@ -618,10 +644,11 @@ def misra_5_2(self, data): scopeVars = {} for var in data.variables: - if var.nameToken.scope not in scopeVars: - scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = [] - scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = [] - scopeVars[var.nameToken.scope]["varlist"].append(var) + if var.nameToken is not None: + if var.nameToken.scope not in scopeVars: + scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = [] + scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = [] + scopeVars[var.nameToken.scope]["varlist"].append(var) for scope in data.scopes: if scope.nestedIn and scope.className: if scope.nestedIn not in scopeVars: @@ -664,9 +691,10 @@ enum = [] scopeVars = {} for var in data.variables: - if var.nameToken.scope not in scopeVars: - scopeVars[var.nameToken.scope] = [] - scopeVars[var.nameToken.scope].append(var) + if var.nameToken is not None: + if var.nameToken.scope not in scopeVars: + scopeVars[var.nameToken.scope] = [] + scopeVars[var.nameToken.scope].append(var) for innerScope in data.scopes: if innerScope.type == "Enum": enum_token = innerScope.bodyStart.next @@ -760,8 +788,9 @@ macroNames.append(res.group(1)) for var in data.variables: for macro in macroNames: - if var.nameToken.str[:31] == macro[:31]: - self.reportError(var.nameToken, 5, 5) + if var.nameToken is not None: + if var.nameToken.str[:31] == macro[:31]: + self.reportError(var.nameToken, 5, 5) for scope in data.scopes: for macro in macroNames: if scope.className and scope.className[:31] == macro[:31]: @@ -1675,13 +1704,17 @@ format. The value of that dictionary is a dictionary of filenames. If the value is None then the rule is assumed to be suppressed for all files. - If the filename exists then the value of that dictionary contains the - scope of the suppression. If the value is None then the rule is assumed - to be suppresed for the entire file. Otherwise the value of the dictionary - is a list of line number, symbol name tuples. + If the filename exists then the value of that dictionary contains a list + with the scope of the suppression. If the list contains an item of None + then the rule is assumed to be suppresed for the entire file. Otherwise + the list contains line number, symbol name tuples. For each tuple either line number or symbol name can can be none. """ + normalized_filename = None + + if fileName is not None: + normalized_filename = os.path.normpath(fileName) if lineNumber is not None or symbolName is not None: line_symbol = (lineNumber, symbolName) @@ -1694,7 +1727,7 @@ ruleItemList.append(line_symbol) fileDict = dict() - fileDict[fileName] = ruleItemList + fileDict[normalized_filename] = ruleItemList self.suppressedRules[ruleNum] = fileDict @@ -1708,22 +1741,21 @@ fileDict = self.suppressedRules[ruleNum] # If the filename is not in the dict already add it - if not fileName in fileDict: + if not normalized_filename in fileDict: ruleItemList = list() ruleItemList.append(line_symbol) - fileDict[fileName] = ruleItemList + fileDict[normalized_filename] = ruleItemList # Rule is added with a file scope. Done return - # Rule has a matching filename. Check for - # rule a rule item list. + # Rule has a matching filename. Get the rule item list. - # If it exists then check the lists of rule items - # to see if the lineNumber, symbonName combination - # exists - ruleItemList = fileDict[fileName] + # Check the lists of rule items + # to see if this (lineNumber, symbonName) combination + # or None already exists. + ruleItemList = fileDict[normalized_filename] if line_symbol is None: # is it already in the list? @@ -1754,14 +1786,21 @@ line number, symbol name tuples. If the list is None then the rule is suppressed for the entire file If the list of tuples exists then search the list looking for - maching line numbers. Symbol names are currently ignored + matching line numbers. Symbol names are currently ignored because they can include regular expressions. TODO: Support symbol names and expression matching. """ ruleIsSuppressed = False - filename = location.file - linenr = location.linenr + linenr = location.linenr + + # Remove any prefix listed in command arguments from the filename. + filename = None + if location.file is not None: + if self.filePrefix is not None: + filename = remove_file_prefix(location.file, self.filePrefix) + else: + filename = location.file if ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] @@ -1777,9 +1816,9 @@ # Get the list of ruleItems ruleItemList = fileDict[filename] - if ruleItemList is None: - # None for itemRuleList means the rule is suppressed - # for all lines in the filename + if None in ruleItemList: + # Entry of None in the ruleItemList means the rule is + # suppressed for all lines in the filename ruleIsSuppressed = True else: # Iterate though the the list of line numbers @@ -1815,6 +1854,39 @@ each.lineNumber, each.symbolName) + def showSuppressedRules(self): + """ + Print out rules in suppression list sorted by Rule Number + """ + print("Suppressed Rules List:") + outlist = list() + + for ruleNum in self.suppressedRules: + fileDict = self.suppressedRules[ruleNum] + + for fname in fileDict: + ruleItemList = fileDict[fname] + + for item in ruleItemList: + if item is None: + item_str = "None" + else: + item_str = str(item[0]) + + outlist.append("%s: %s: %s" % (float(ruleNum)/100,fname,item_str)) + + for line in sorted(outlist, reverse=True): + print(" %s" % line) + + + def setFilePrefix(self, prefix): + """ + Set the file prefix to ignnore from files when matching + supression files + """ + self.filePrefix = prefix + + def setSuppressionList(self, suppressionlist): num1 = 0 num2 = 0 @@ -2061,6 +2133,8 @@ parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck") +parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") +parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") args = parser.parse_args() checker = MisraChecker() @@ -2080,6 +2154,9 @@ if args.suppress_rules: checker.setSuppressionList(args.suppress_rules) + if args.file_prefix: + checker.setFilePrefix(args.file_prefix) + if args.quiet: QUIET = True if args.no_summary: @@ -2119,4 +2196,7 @@ if SHOW_SUMMARY: print("\nMISRA rule violations found: %d\n" % (number_of_violations)) + if args.show_suppressed_rules: + checker.showSuppressedRules() + sys.exit(exitCode) diff -Nru cppcheck-1.85/addons/naming.json cppcheck-1.86/addons/naming.json --- cppcheck-1.85/addons/naming.json 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/naming.json 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,10 @@ +{ + "RE_VARNAME": "[a-z]*[a-zA-Z0-9_]*\\Z", + "RE_PRIVATE_MEMBER_VARIABLE": null, + "RE_FUNCTIONNAME": "[a-z0-9A-Z]*\\Z", + "var_prefixes": {"uint32_t": "ui32", + "int*": "intp"}, + "function_prefixes": {"uint16_t": "ui16", + "uint32_t": "ui32"}, + "skip_one_char_variables": false +} diff -Nru cppcheck-1.85/addons/namingng.py cppcheck-1.86/addons/namingng.py --- cppcheck-1.85/addons/namingng.py 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/namingng.py 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# +# cppcheck addon for naming conventions +# An enhanced version. Configuration is taken from a json file +# It supports to check for type-based prefixes in function or variable names. +# +# Example usage (variable name must start with lowercase, function name must start with uppercase): +# $ cppcheck --dump path-to-src/ +# $ python naming.py namingng.py test.c.dump +# +# JSON format: +# +# { +# "RE_VARNAME": "[a-z]*[a-zA-Z0-9_]*\\Z", +# "RE_PRIVATE_MEMBER_VARIABLE": null, +# "RE_FUNCTIONNAME": "[a-z0-9A-Z]*\\Z", +# "var_prefixes": {"uint32_t": "ui32"}, +# "function_prefixes": {"uint16_t": "ui16", +# "uint32_t": "ui32"} +# } +# +# RE_VARNAME, RE_PRIVATE_MEMBER_VARIABE and RE_FUNCTIONNAME are regular expressions to cover hte basic names +# In var_prefixes and function_prefixes there are the variable-type/prefix pairs + +import cppcheckdata +import sys +import re +import argparse +import json + + +def reportError(filename, linenr, severity, msg): + message = "[{filename}:{linenr}] ( {severity} ) naming.py: {msg}\n".format( + filename=filename, + linenr=linenr, + severity=severity, + msg=msg + ) + sys.stderr.write(message) + return message + + +def loadConfig(configfile): + with open(configfile) as fh: + data = json.load(fh) + return data + + +def process(dumpfiles, configfile, debugprint=False): + + errors = [] + + conf = loadConfig(configfile) + + for afile in dumpfiles: + if not afile[-5:] == '.dump': + continue + print('Checking ' + afile + '...') + data = cppcheckdata.parsedump(afile) + for cfg in data.configurations: + if len(data.configurations) > 1: + print('Checking ' + afile + ', config "' + cfg.name + '"...') + if conf["RE_VARNAME"]: + for var in cfg.variables: + if var.nameToken: + prev = var.nameToken.previous + varType = prev.str + while "*" in varType and len(varType.replace("*", "")) == 0: + prev = prev.previous + varType = prev.str + varType + + if debugprint: + print("Variable Name: " + str(var.nameToken.str)) + print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) + print("Type Name: " + var.nameToken.valueType.type) + print("Sign: " + str(var.nameToken.valueType.sign)) + print("variable type: " + varType) + print("\n") + print("\t-- {} {}".format(varType, str(var.nameToken.str))) + + if conf["skip_one_char_variables"] and len(var.nameToken.str) == 1: + continue + if varType in conf["var_prefixes"]: + if not var.nameToken.str.startswith(conf["var_prefixes"][varType]): + errors.append(reportError( + var.typeStartToken.file, + var.typeStartToken.linenr, + 'style', + 'Variable ' + + var.nameToken.str + + ' violates naming convention')) + res = re.match(conf["RE_VARNAME"], var.nameToken.str) + if not res: + errors.append(reportError(var.typeStartToken.file, var.typeStartToken.linenr, 'style', 'Variable ' + + var.nameToken.str + ' violates naming convention')) + + if conf["RE_PRIVATE_MEMBER_VARIABLE"]: + # TODO: Not converted yet + for var in cfg.variables: + if (var.access is None) or var.access != 'Private': + continue + res = re.match(conf["RE_PRIVATE_MEMBER_VARIABLE"], var.nameToken.str) + if not res: + errors.append(reportError(var.typeStartToken.file, var.typeStartToken.linenr, 'style', 'Private member variable ' + + var.nameToken.str + ' violates naming convention')) + + if conf["RE_FUNCTIONNAME"]: + for token in cfg.tokenlist: + if token.function: + retval = token.previous.str + prev = token.previous + while "*" in retval and len(retval.replace("*", "")) == 0: + prev = prev.previous + retval = prev.str + retval + if debugprint: + print("\t:: {} {}".format(retval, token.function.name)) + + if retval and retval in conf["function_prefixes"]: + if not token.function.name.startswith(conf["function_prefixes"][retval]): + errors.append(reportError( + token.file, token.linenr, 'style', 'Function ' + token.function.name + ' violates naming convention')) + res = re.match(conf["RE_FUNCTIONNAME"], token.function.name) + if not res: + errors.append(reportError( + token.file, token.linenr, 'style', 'Function ' + token.function.name + ' violates naming convention')) + return errors + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Naming verification') + parser.add_argument('dumpfiles', type=str, nargs='+', + help='A set of dumpfiles to process') + parser.add_argument("--debugprint", action="store_true", default=False, + help="Add debug prints") + parser.add_argument("--configfile", type=str, default="naming.json", + help="Naming check config file") + parser.add_argument("--verify", action="store_true", default=False, + help="verify this script. Bust be executed in test folder !") + + args = parser.parse_args() + errors = process(args.dumpfiles, args.configfile, args.debugprint) + + if args.verify: + print(errors) + if len(errors) < 6: + print("Not enough errors found") + sys.exit(1) + target = [ + '[namingng_test.c:8] ( style ) naming.py: Variable badui32 violates naming convention\n', + '[namingng_test.c:11] ( style ) naming.py: Variable a violates naming convention\n', + '[namingng_test.c:29] ( style ) naming.py: Variable badui32 violates naming convention\n', + '[namingng_test.c:20] ( style ) naming.py: Function ui16bad_underscore violates naming convention\n', + '[namingng_test.c:25] ( style ) naming.py: Function u32Bad violates naming convention\n', + '[namingng_test.c:37] ( style ) naming.py: Function Badui16 violates naming convention\n'] + diff = set(errors) - set(target) + if len(diff): + print("Not the right errors found {}".format(str(diff))) + sys.exit(1) + print("Verification done\n") + sys.exit(0) + + if len(errors): + print('Found errors: {}'.format(len(errors))) + sys.exit(1) + sys.exit(0) diff -Nru cppcheck-1.85/addons/test/misra-suppressions1-test.c cppcheck-1.86/addons/test/misra-suppressions1-test.c --- cppcheck-1.85/addons/test/misra-suppressions1-test.c 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/misra-suppressions1-test.c 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,33 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +#include //21.6 + +extern int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +void misra_5_2_function_hides_var_31y(void) {}//5.2 +void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //14.4 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //14.4 + } + } +} diff -Nru cppcheck-1.85/addons/test/misra-suppressions2-test.c cppcheck-1.86/addons/test/misra-suppressions2-test.c --- cppcheck-1.85/addons/test/misra-suppressions2-test.c 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/misra-suppressions2-test.c 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,14 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 +const char *s41_2 = "\x41\x42"; diff -Nru cppcheck-1.85/addons/test/namingng_test.c cppcheck-1.86/addons/test/namingng_test.c --- cppcheck-1.85/addons/test/namingng_test.c 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/namingng_test.c 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,50 @@ +#include +#include + +uint32_t ui32Good (int a) +{ + uint32_t ui32good; + int32_t i32good; + uint32_t badui32; + int32_t badi32; + + uint32_t a; // Short + return 5; +} + +uint16_t ui16Good (int a) +{ + return 5; +} + +uint16_t ui16bad_underscore (int a) +{ + return 5; +} + +uint32_t u32Bad (int a) +{ + uint32_t ui32good; + int32_t i32good; + uint32_t badui32; + int32_t badi32; + int * intpointer=NULL; + int ** intppointer=NULL; + int *** intpppointer=NULL; + return 5; +} + +uint16_t Badui16 (int a) +{ + return 5; +} + +void * Pointer() +{ + return NULL; +} + +void ** PPointer() +{ + return NULL; +} diff -Nru cppcheck-1.85/addons/test/path1/misra-suppressions1-test.c cppcheck-1.86/addons/test/path1/misra-suppressions1-test.c --- cppcheck-1.85/addons/test/path1/misra-suppressions1-test.c 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/path1/misra-suppressions1-test.c 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,33 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +#include //21.6 + +extern int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +void misra_5_2_function_hides_var_31y(void) {}//5.2 +void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //14.4 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //14.4 + } + } +} diff -Nru cppcheck-1.85/addons/test/path1/misra-suppressions2-test.c cppcheck-1.86/addons/test/path1/misra-suppressions2-test.c --- cppcheck-1.85/addons/test/path1/misra-suppressions2-test.c 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/path1/misra-suppressions2-test.c 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,14 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 +const char *s41_2 = "\x41\x42"; diff -Nru cppcheck-1.85/addons/test/suppressions.txt cppcheck-1.86/addons/test/suppressions.txt --- cppcheck-1.85/addons/test/suppressions.txt 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/addons/test/suppressions.txt 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,7 @@ +misra_21.6:misra-suppressions1-test.c:7 +misra_14_4 +misra.5.2 +MISRA_16_4:misra-suppressions1-test.c +MISRA.16.6:misra-suppressions1-test.c +MISRA_4_1:misra-suppressions2-test.c +MISRA.19_2:misra-suppressions2-test.c diff -Nru cppcheck-1.85/.astylerc cppcheck-1.86/.astylerc --- cppcheck-1.85/.astylerc 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/.astylerc 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################## +# Documentation is available from # +# http://astyle.sourceforge.net/astyle.html # +############################################################## + +### Options ### +--pad-header +--unpad-paren +--suffix=none +--convert-tabs +--attach-inlines +--attach-classes +--attach-namespaces + +### Settings ### +--style=kr +--indent=spaces=4 +--indent-namespaces +--lineend=linux +--min-conditional-indent=0 diff -Nru cppcheck-1.85/AUTHORS cppcheck-1.86/AUTHORS --- cppcheck-1.85/AUTHORS 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/AUTHORS 2018-12-08 07:18:21.000000000 +0000 @@ -49,7 +49,9 @@ Cary R Changkyoon Kim Christian Ehrlicher +Christian Franke Chuck Larson +Colomban Wendling Daniel Marjamäki David Hallas David Korth @@ -95,6 +97,7 @@ Henrik Nilsson He Yuqi Hoang Tuan Su +Igor Zhukov Ivan Maidanski Iván Matellanes Ivan Ryabov @@ -229,6 +232,7 @@ Thomas Jarosch Thomas Otto Thomas Sondergaard +Thorsten Sick Tim Gerundt Tobias Weibel Tom Pollok @@ -244,6 +248,7 @@ x29a XhmikosR Xuecheng Zhang +Yurii Putin Zachary Blair Zhao Qifa Zhiyuan Zhang diff -Nru cppcheck-1.85/cfg/gnu.cfg cppcheck-1.86/cfg/gnu.cfg --- cppcheck-1.85/cfg/gnu.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/gnu.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -24,6 +24,8 @@ + + diff -Nru cppcheck-1.85/cfg/googletest.cfg cppcheck-1.86/cfg/googletest.cfg --- cppcheck-1.85/cfg/googletest.cfg 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/cfg/googletest.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru cppcheck-1.85/cfg/posix.cfg cppcheck-1.86/cfg/posix.cfg --- cppcheck-1.85/cfg/posix.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/posix.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -3943,6 +3943,26 @@ 1: + + + + + false + + + + + + + + + 0: + + + + posix_memalign + free + strdup strndup diff -Nru cppcheck-1.85/cfg/qt.cfg cppcheck-1.86/cfg/qt.cfg --- cppcheck-1.85/cfg/qt.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/qt.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -1267,6 +1267,7 @@ + diff -Nru cppcheck-1.85/cfg/std.cfg cppcheck-1.86/cfg/std.cfg --- cppcheck-1.85/cfg/std.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/std.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -1857,6 +1857,7 @@ + @@ -3177,7 +3178,7 @@ - + arg1>arg2?1:0 false @@ -3191,7 +3192,7 @@ - + arg1 >= arg2?1:0 false @@ -3254,7 +3255,7 @@ - + arg1<arg2?1:0 false @@ -3268,7 +3269,7 @@ - + arg1 <= arg2?1:0 false @@ -3282,7 +3283,8 @@ - + (arg1<arg2 || arg1>arg2)?1:0 + false @@ -3661,6 +3663,29 @@ 0: + + + + + false + + + + + + 0: + + + + + + + false + + + 0: + + @@ -4099,6 +4124,7 @@ + @@ -4234,6 +4260,22 @@ 0: + + + + false + + + + + + 0: + + + + 0: + + @@ -4505,6 +4547,7 @@ + 0:255 @@ -5726,6 +5769,7 @@ + @@ -5798,6 +5842,7 @@ + @@ -5878,6 +5923,7 @@ false + arg1<arg2?arg1:arg2 @@ -5890,6 +5936,7 @@ false + arg1>arg2?arg1:arg2 @@ -6571,6 +6618,29 @@ + + + + + + + + + + + false + + + + + + + + + + + + @@ -6628,6 +6698,8 @@ malloc calloc + aligned_alloc + valloc free diff -Nru cppcheck-1.85/cfg/windows.cfg cppcheck-1.86/cfg/windows.cfg --- cppcheck-1.85/cfg/windows.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/windows.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -3927,15 +3927,18 @@ + - + + false - + + false @@ -5434,6 +5437,30 @@ false + + + + + false + + + + + + + + + + + + false + + + + + + diff -Nru cppcheck-1.85/cfg/wxwidgets.cfg cppcheck-1.86/cfg/wxwidgets.cfg --- cppcheck-1.85/cfg/wxwidgets.cfg 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cfg/wxwidgets.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -937,6 +937,26 @@ + + + false + + + + + + + + + + + + + + + + + @@ -1569,6 +1589,61 @@ false + + + + + true + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + diff -Nru cppcheck-1.85/cfg/zlib.cfg cppcheck-1.86/cfg/zlib.cfg --- cppcheck-1.85/cfg/zlib.cfg 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/cfg/zlib.cfg 2018-12-08 07:18:21.000000000 +0000 @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + diff -Nru cppcheck-1.85/cli/filelister.cpp cppcheck-1.86/cli/filelister.cpp --- cppcheck-1.85/cli/filelister.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cli/filelister.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -186,13 +186,11 @@ if (stat(path.c_str(), &file_stat) != -1) { if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { DIR * dir = opendir(path.c_str()); - if (!dir) return; dirent entry; dirent * dir_result; - std::string new_path; new_path.reserve(path.length() + 100);// prealloc some memory to avoid constant new/deletes in loop @@ -210,6 +208,7 @@ } } else { if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) { + stat(new_path.c_str(), &file_stat); files[new_path] = file_stat.st_size; } } diff -Nru cppcheck-1.85/cli/main.cpp cppcheck-1.86/cli/main.cpp --- cppcheck-1.85/cli/main.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cli/main.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -20,7 +20,7 @@ /** * * @mainpage Cppcheck - * @version 1.85 + * @version 1.86 * * @section overview_sec Overview * Cppcheck is a simple tool for static analysis of C/C++ code. diff -Nru cppcheck-1.85/cmake/compileroptions.cmake cppcheck-1.86/cmake/compileroptions.cmake --- cppcheck-1.85/cmake/compileroptions.cmake 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cmake/compileroptions.cmake 2018-12-08 07:18:21.000000000 +0000 @@ -52,7 +52,7 @@ set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Woverloaded-virtual") # when a function declaration hides virtual functions from a base class set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wpacked") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wredundant-decls") # if anything is declared more than once in the same scope - set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wshadow") # whenever a local variable or type declaration shadows another one + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-shadow") # whenever a local variable or type declaration shadows another one set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wsign-promo") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-field-initializers") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-braces") @@ -73,6 +73,13 @@ MESSAGE( FATAL_ERROR "Clang++ not found. " ) endif() + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-four-char-constants") + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-braces") + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-field-initializers") + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-multichar") + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-sign-compare") + set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-unused-function") + if(ENABLE_COVERAGE OR ENABLE_COVERAGE_XML) MESSAGE(FATAL_ERROR "Not use clang for generate code coverage. Use gcc. ") endif() @@ -94,7 +101,7 @@ "${CMAKE_CXX_COMPILER_ID}" STREQUAL "c++-analyzer" ) if(WARNINGS_ANSI_ISO) - set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wextra -pedantic") + set(EXTRA_C_FLAGS "-Wextra -pedantic ${EXTRA_C_FLAGS}") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wlogical-op") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-long-long") # Don't warn about long long usage. endif() @@ -103,7 +110,7 @@ set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Werror") endif() - set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wall -std=c++0x") + set(EXTRA_C_FLAGS "-Wall -std=c++0x ${EXTRA_C_FLAGS}") set(EXTRA_C_FLAGS_DEBUG "${EXTRA_C_FLAGS_DEBUG} -O0") diff -Nru cppcheck-1.85/cmake/versions.cmake cppcheck-1.86/cmake/versions.cmake --- cppcheck-1.85/cmake/versions.cmake 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cmake/versions.cmake 2018-12-08 07:18:21.000000000 +0000 @@ -1,5 +1,5 @@ # Version for libraries CPP -SET(VERSION "1.85") +SET(VERSION "1.86") STRING(REGEX MATCHALL "[0-9]" VERSION_PARTS "${VERSION}") LIST(GET VERSION_PARTS 0 VERSION_MAJOR) LIST(GET VERSION_PARTS 1 VERSION_MINOR) diff -Nru cppcheck-1.85/.codacy.yml cppcheck-1.86/.codacy.yml --- cppcheck-1.85/.codacy.yml 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/.codacy.yml 2018-12-08 07:18:21.000000000 +0000 @@ -1,6 +1,5 @@ exclude_paths: - - addons/test/*.c - - addons/test/*.cpp + - addons/test/** - addons/y2038/test/*.c - htmlreport/example.cc - samples/**/bad.c diff -Nru cppcheck-1.85/cppcheck-errors.rng cppcheck-1.86/cppcheck-errors.rng --- cppcheck-1.85/cppcheck-errors.rng 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/cppcheck-errors.rng 2018-12-08 07:18:21.000000000 +0000 @@ -1,70 +1,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru cppcheck-1.85/createrelease cppcheck-1.86/createrelease --- cppcheck-1.85/createrelease 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/createrelease 2018-12-08 07:18:21.000000000 +0000 @@ -3,7 +3,7 @@ # A script for creating release packages. The release packages are create in the home directory. # # self check: -# ./cppcheck --library=cppcheck-lib --enable=style --inconclusive --suppress=bitwiseOnBoolean cli gui/*.cpp lib +# ./cppcheck --library=cppcheck-lib --enable=style --inconclusive --suppress=bitwiseOnBoolean --suppress=shadowFunction --suppress=useStlAlgorithm cli gui/*.cpp lib # # Update translations # lupdate gui.pro diff -Nru cppcheck-1.85/debian/changelog cppcheck-1.86/debian/changelog --- cppcheck-1.85/debian/changelog 2018-11-24 15:49:56.000000000 +0000 +++ cppcheck-1.86/debian/changelog 2018-12-29 17:29:50.000000000 +0000 @@ -1,14 +1,24 @@ +cppcheck (1.86-1) unstable; urgency=medium + + * New upstream release. + * Update Standards-Version to 4.3.0 (no changes needed). + * Change patch fix-python-default to set the default python path to + /usr/bin/python3; rename patch to fix-default-settings. + * Extend patch to set the default DATADIR to /usr/share/cppcheck/cfg + (Closes: #917630). + * Fix typo in changelog of 1.85-2. + + -- Joachim Reichel Sat, 29 Dec 2018 18:29:50 +0100 + cppcheck (1.85-2) unstable; urgency=medium - * Fix FTBFS due to char signedness: remove patch again, superseeded + * Fix FTBFS due to char signedness: remove patch again, superseded upstream (Closes: 914520). -- Joachim Reichel Sat, 24 Nov 2018 16:49:56 +0100 cppcheck (1.85-1) unstable; urgency=medium - * New upstream release. - * Update Standards-Version to 4.2.1 (no changes needed). * Fix lintian warnings insecure-copyright-format-uri and description-contains-invalid-control-statement diff -Nru cppcheck-1.85/debian/control cppcheck-1.86/debian/control --- cppcheck-1.85/debian/control 2018-11-23 16:41:34.000000000 +0000 +++ cppcheck-1.86/debian/control 2018-12-29 17:29:50.000000000 +0000 @@ -5,7 +5,7 @@ Build-Depends: debhelper (>= 11), dpkg-dev (>= 1.15.7~), xsltproc, docbook-xsl, docbook-xml, libpcre3-dev, libtinyxml2-dev (>= 6.2.0+dfsg), qt5-qmake, qtbase5-dev, qtbase5-dev-tools, gdb, dh-python, python3-all -Standards-Version: 4.2.1 +Standards-Version: 4.3.0 Homepage: http://sourceforge.net/p/cppcheck/wiki/Home/ X-Python3-Version: >= 3.5 diff -Nru cppcheck-1.85/debian/patches/fix-default-settings cppcheck-1.86/debian/patches/fix-default-settings --- cppcheck-1.85/debian/patches/fix-default-settings 1970-01-01 00:00:00.000000000 +0000 +++ cppcheck-1.86/debian/patches/fix-default-settings 2018-12-29 17:29:50.000000000 +0000 @@ -0,0 +1,32 @@ +Description: Set DATADIR default to CFGDIR +Author: Joachim Reichel + +Index: cppcheck-1.86/gui/main.cpp +=================================================================== +--- cppcheck-1.86.orig/gui/main.cpp ++++ cppcheck-1.86/gui/main.cpp +@@ -62,6 +62,12 @@ int main(int argc, char *argv[]) + + QSettings* settings = new QSettings("Cppcheck", "Cppcheck-GUI", &app); + ++ // Set some default settings ++ if( settings->value("DATADIR", QString()).toString().isEmpty()) ++ settings->setValue("DATADIR", CFGDIR); ++ if( settings->value(SETTINGS_PYTHON_PATH, QString()).toString().isEmpty()) ++ settings->setValue(SETTINGS_PYTHON_PATH, QString("/usr/bin/python3")); ++ + // Set data dir.. + foreach (const QString arg, app.arguments()) { + if (arg.startsWith("--data-dir=")) { +Index: cppcheck-1.86/gui/gui.pro +=================================================================== +--- cppcheck-1.86.orig/gui/gui.pro ++++ cppcheck-1.86/gui/gui.pro +@@ -9,6 +9,7 @@ INCLUDEPATH += . \ + ../lib + QT += widgets + QT += printsupport ++DEFINES += CFGDIR=\\\"/usr/share/cppcheck/cfg\\\" + + contains(LINKCORE, [yY][eE][sS]) { + LIBS += -l../bin/cppcheck-core diff -Nru cppcheck-1.85/debian/patches/fix-python-default cppcheck-1.86/debian/patches/fix-python-default --- cppcheck-1.85/debian/patches/fix-python-default 2018-04-12 19:45:43.000000000 +0000 +++ cppcheck-1.86/debian/patches/fix-python-default 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -Description: Fix default path of python3 binary -Author: Joachim Reichel - -Index: cppcheck-1.83/gui/settingsdialog.cpp -=================================================================== ---- cppcheck-1.83.orig/gui/settingsdialog.cpp -+++ cppcheck-1.83/gui/settingsdialog.cpp -@@ -53,7 +53,7 @@ SettingsDialog::SettingsDialog(Applicati - mUI.mEnableInconclusive->setCheckState(boolToCheckState(settings.value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool())); - mUI.mShowStatistics->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_STATISTICS, false).toBool())); - mUI.mShowErrorId->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, false).toBool())); -- mUI.mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString()).toString()); -+ mUI.mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString("/usr/bin/python3")).toString()); - mUI.mEditMisraFile->setText(settings.value(SETTINGS_MISRA_FILE, QString()).toString()); - - #ifdef Q_OS_WIN diff -Nru cppcheck-1.85/debian/patches/series cppcheck-1.86/debian/patches/series --- cppcheck-1.85/debian/patches/series 2018-11-24 15:49:44.000000000 +0000 +++ cppcheck-1.86/debian/patches/series 2018-12-29 17:29:50.000000000 +0000 @@ -1,3 +1,3 @@ use-system-tinyxml fix-cxxflags -fix-python-default +fix-default-settings diff -Nru cppcheck-1.85/externals/simplecpp/simplecpp.cpp cppcheck-1.86/externals/simplecpp/simplecpp.cpp --- cppcheck-1.85/externals/simplecpp/simplecpp.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/externals/simplecpp/simplecpp.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -439,7 +439,6 @@ if (oldLastToken != cback()) { oldLastToken = cback(); const std::string lastline(lastLine()); - if (lastline == "# file %str%") { loc.push(location); location.fileIndex = fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)); @@ -451,6 +450,10 @@ loc.push(location); location.fileIndex = fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)); location.line = std::atol(cback()->previous->str().c_str()); + } else if (lastline == "# %num% %str%") { + loc.push(location); + location.fileIndex = fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)); + location.line = std::atol(cback()->previous->str().c_str()); } // #endfile else if (lastline == "# endfile" && !loc.empty()) { diff -Nru cppcheck-1.85/gui/cppcheck_de.ts cppcheck-1.86/gui/cppcheck_de.ts --- cppcheck-1.85/gui/cppcheck_de.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_de.ts 2018-12-08 07:18:21.000000000 +0000 @@ -403,13 +403,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -953,23 +953,23 @@ %2 - + License Lizenz - + Authors Autoren - + Save the report file Speichert die Berichtdatei - - + + XML files (*.xml) XML-Dateien (*.xml) @@ -1030,17 +1030,17 @@ Unbekannter Fehler - + Error Fehler - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Laden von %1 fehlgeschlagen. Ihre Cppcheck-Installation ist defekt. Sie können --data-dir=<Verzeichnis> als Kommandozeilenparameter verwenden, um anzugeben, wo die Datei sich befindet. Bitte beachten Sie, dass --data-dir in Installationsroutinen genutzt werden soll, und die GUI bei dessen Nutzung nicht startet, sondern die Einstellungen konfiguriert. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1049,39 +1049,39 @@ Das Einlesen einer XML-Datei löscht die aktuellen Ergebnisse. Fortfahren? - + Open the report file Berichtdatei öffnen - + Text files (*.txt) Textdateien (*.txt) - + CSV files (*.csv) CSV-Dateien (*.csv) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) Projektdateien (*.cppcheck);;Alle Dateien(*.*) - + Select Project File Projektdatei auswählen - - + + Project: Projekt: @@ -1115,7 +1115,7 @@ Wollen sie fortfahren, ohne diese Projektdateien zu nutzen? - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? @@ -1124,37 +1124,37 @@ Wollen sie die Analyse abbrechen und Cppcheck beenden? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-Dateien (*.xml);;Textdateien (*.txt);;CSV-Dateien (*.csv) - + Build dir '%1' does not exist, create it? Erstellungsverzeichnis '%1' existiert nicht. Erstellen? - + Failed to import '%1', analysis is stopped Import von '%1' fehlgeschlagen; Analyse wurde abgebrochen. - + Project files (*.cppcheck) Projektdateien (*.cppcheck) - + Select Project Filename Projektnamen auswählen - + No project file loaded Keine Projektdatei geladen - + The project file %1 @@ -1171,7 +1171,7 @@ Möchten Sie die Datei von der Liste der zuletzt benutzten Projekte entfernen? - + Cppcheck GUI. Syntax: @@ -1202,7 +1202,7 @@ - + Cppcheck GUI - Command line parameters Cppcheck GUI - Kommandozeilenparameter @@ -1492,67 +1492,67 @@ ProjectFileDialog - + Project file: %1 Projektdatei: %1 - + Select Cppcheck build dir Wähle Cppcheck-Erstellungsverzeichnis - + Select include directory Wähle Include-Verzeichnisse - + Select a directory to check Wähle zu prüfendes Verzeichnis - + (no rule texts file) (keine Regeltexte) - + Clang-tidy (not found) Clang-tidy (nicht gefunden) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project Projekt importieren - + Select directory to ignore Wähle zu ignorierendes Verzeichnis - + Select MISRA rule texts file Wähle MISRA-Regeltext-Datei - + Misra rule texts file (%1) MISRA-Regeltext-Datei @@ -1603,7 +1603,7 @@ Zeile %1: Nicht behandeltes Element %2 - + (Not found) (nicht gefunden) @@ -1634,22 +1634,22 @@ ResultsTree - + File Datei - + Severity Schweregrad - + Line Zeile - + Summary Zusammenfassung @@ -1720,7 +1720,7 @@ - + Tag Tag @@ -1773,17 +1773,17 @@ Wähle Verzeichnis - + Id Id - + Inconclusive Unklar - + Since date Seit Datum @@ -1855,7 +1855,7 @@ - + Failed to read the report. Lesen des Berichts fehlgeschlagen. @@ -1865,27 +1865,27 @@ XML-Format-Version 1 wird nicht länger unterstützt. - + First included by Zuerst inkludiert von - + Id Id - + Clear Log Protokoll leeren - + Copy this Log entry Diesen Protokolleintrag kopieren - + Copy complete Log Gesamtes Protokoll kopieren diff -Nru cppcheck-1.85/gui/cppcheck_es.ts cppcheck-1.86/gui/cppcheck_es.ts --- cppcheck-1.85/gui/cppcheck_es.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_es.ts 2018-12-08 07:18:21.000000000 +0000 @@ -408,13 +408,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1027,13 +1027,13 @@ - - + + XML files (*.xml) Archivos XML (*.xml) - + Open the report file Abrir informe @@ -1046,12 +1046,12 @@ ¿Quieres parar la comprobación y salir del Cppcheck? - + License Licencia - + Authors Autores @@ -1060,7 +1060,7 @@ Archivos XML versión 2 (*.xml);;Archivos XML versión 1 (*.xml);;Archivos de texto (*.txt);;Archivos CSV (*.csv) - + Save the report file Guardar informe @@ -1118,17 +1118,17 @@ - + Error Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1145,34 +1145,34 @@ Archivos XML versión 2 (*.xml) - + Text files (*.txt) Ficheros de texto (*.txt) - + CSV files (*.csv) Ficheros CVS (*.cvs) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) Ficheros de proyecto (*.cppcheck;;Todos los ficheros (*.*) - + Select Project File Selecciona el archivo de proyecto - - + + Project: Proyecto: @@ -1224,44 +1224,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename Selecciona el nombre del proyecto - + No project file loaded No hay ningún proyecto cargado - + The project file %1 @@ -1278,7 +1278,7 @@ ¿Quiere eliminar el fichero de la lista de proyectos recientes? - + Cppcheck GUI. Syntax: @@ -1308,7 +1308,7 @@ - + Cppcheck GUI - Command line parameters @@ -1648,57 +1648,57 @@ ProjectFileDialog - + Project file: %1 Archivo de proyecto: %1 - + Select Cppcheck build dir - + Select include directory Selecciona una carpeta para incluir - + Select a directory to check Selecciona la carpeta a comprobar - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore Selecciona la carpeta a ignorar @@ -1707,12 +1707,12 @@ Añadir supresión - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1763,7 +1763,7 @@ - + (Not found) @@ -1794,22 +1794,22 @@ ResultsTree - + File Archivo - + Severity Severidad - + Line Línea - + Summary Resumen @@ -1910,7 +1910,7 @@ - + Tag @@ -1972,17 +1972,17 @@ Selecciona carpeta - + Id Id - + Inconclusive - + Since date @@ -2070,7 +2070,7 @@ - + Failed to read the report. Error al leer el informe. @@ -2088,27 +2088,27 @@ Mensaje - + First included by - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_fi.ts cppcheck-1.86/gui/cppcheck_fi.ts --- cppcheck-1.85/gui/cppcheck_fi.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_fi.ts 2018-12-08 07:18:21.000000000 +0000 @@ -391,13 +391,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -949,12 +949,12 @@ - + License Lisenssi - + Authors Tekijät @@ -964,13 +964,13 @@ XML-tiedostot (*.xml);;Tekstitiedostot (*.txt);;CSV-tiedostot (*.csv) - + Save the report file Tallenna raportti - - + + XML files (*.xml) XML-tiedostot (*xml) @@ -1022,56 +1022,56 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? - + Open the report file - + Text files (*.txt) Tekstitiedostot (*.txt) - + CSV files (*.csv) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) - + Select Project File - - + + Project: @@ -1123,44 +1123,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename - + No project file loaded - + The project file %1 @@ -1171,7 +1171,7 @@ - + Cppcheck GUI. Syntax: @@ -1201,7 +1201,7 @@ - + Cppcheck GUI - Command line parameters @@ -1500,67 +1500,67 @@ ProjectFileDialog - + Project file: %1 - + Select Cppcheck build dir - + Select include directory - + Select a directory to check - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1613,7 +1613,7 @@ - + (Not found) @@ -1644,22 +1644,22 @@ ResultsTree - + File Tiedosto - + Severity Tyyppi - + Line Rivi - + Summary @@ -1738,7 +1738,7 @@ - + Tag @@ -1788,17 +1788,17 @@ - + Id - + Inconclusive - + Since date @@ -1870,7 +1870,7 @@ - + Failed to read the report. @@ -1880,27 +1880,27 @@ - + First included by - + Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_it.ts cppcheck-1.86/gui/cppcheck_it.ts --- cppcheck-1.85/gui/cppcheck_it.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_it.ts 2018-12-08 07:18:21.000000000 +0000 @@ -420,13 +420,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1045,12 +1045,12 @@ - + License Licenza - + Authors Autori @@ -1059,13 +1059,13 @@ File XML Versione 2 (*.xml);;File XML Versione 1 (*.xml);;File di testo (*.txt);;File CSV (*.csv) - + Save the report file Salva il file di rapporto - - + + XML files (*.xml) File XML (*.xml) @@ -1114,17 +1114,17 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1133,7 +1133,7 @@ L'apertura di un nuovo file XML ripulirà i risultati correnti. Vuoi procedere? - + Open the report file Apri il file di rapporto @@ -1154,17 +1154,17 @@ Files XML versione 2 (*.xml) - + Text files (*.txt) File di testo (*.txt) - + CSV files (*.csv) Files CSV (*.csv) - + Cppcheck - %1 Cppcheck - %1 @@ -1181,19 +1181,19 @@ L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. - + Project files (*.cppcheck);;All files(*.*) Files di progetto (*.cppcheck);;Tutti i files(*.*) - + Select Project File Seleziona il file di progetto - - + + Project: Progetto: @@ -1245,44 +1245,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename Seleziona il nome del file di progetto - + No project file loaded Nessun file di progetto caricato - + The project file %1 @@ -1299,7 +1299,7 @@ Vuoi rimuovere il file dalla lista dei progetti recentemente usati? - + Cppcheck GUI. Syntax: @@ -1329,7 +1329,7 @@ - + Cppcheck GUI - Command line parameters @@ -1661,67 +1661,67 @@ ProjectFileDialog - + Project file: %1 File di progetto: %1 - + Select Cppcheck build dir - + Select include directory Seleziona la cartella da includere - + Select a directory to check Seleziona una cartella da scansionare - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore Seleziona la cartella da ignorare - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1772,7 +1772,7 @@ - + (Not found) @@ -1803,22 +1803,22 @@ ResultsTree - + File File - + Severity Severità - + Line Linea - + Summary Riassunto @@ -1909,7 +1909,7 @@ - + Tag @@ -1970,17 +1970,17 @@ Seleziona Cartella - + Id Id - + Inconclusive - + Since date @@ -2052,7 +2052,7 @@ - + Failed to read the report. Apertura del report fallito. @@ -2070,27 +2070,27 @@ Messaggio - + First included by - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_ja.ts cppcheck-1.86/gui/cppcheck_ja.ts --- cppcheck-1.85/gui/cppcheck_ja.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_ja.ts 2018-12-08 07:18:21.000000000 +0000 @@ -434,13 +434,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1127,7 +1127,7 @@ %2 - + Error エラー @@ -1136,12 +1136,12 @@ %1の読み込みに失敗しました。CppCheckのインストールに失敗しています。コマンドライン引数に --data-dir=<directory> を指定して、このファイルの場所を指定してください。 - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. %1のロードに失敗しました。あなたの Cppcheck は正しくインストールされていません。あなたは --data-dir=<directory> コマンドラインオプションでこのファイルの場所を指定してできます。ただし、この --data-dir はインストールスクリプトによってサポートされており、GUI版ではサポートされていません。全ての設定は調整済みでなければなりません。 - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1150,13 +1150,13 @@ 新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? - - + + XML files (*.xml) XML ファイル (*.xml) - + Open the report file レポートを開く @@ -1169,12 +1169,12 @@ 解析を停止してCppcheckを終了しますか?. - + License ライセンス - + Authors 作者 @@ -1184,7 +1184,7 @@ XML ファイル (*.xml);;テキストファイル (*.txt);;CSV形式ファイル (*.csv) - + Save the report file レポートを保存 @@ -1197,34 +1197,34 @@ XMLファイルのバージョン2 - + Text files (*.txt) テキストファイル (*.txt) - + CSV files (*.csv) CSV形式ファイル (*.csv) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) プロジェクトファイル (*.cppcheck);;すべてのファイル(*.*) - + Select Project File プロジェクトファイルを選択 - - + + Project: プロジェクト: @@ -1278,7 +1278,7 @@ みつかったプロジェクトファイルを使用せずにチェックしますか? - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? @@ -1287,37 +1287,37 @@ チェックを中断して、Cppcheckを終了しますか? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML ファイル (*.xml);;テキストファイル (*.txt);;CSVファイル (*.csv) - + Build dir '%1' does not exist, create it? ビルドディレクトリ'%1'がありません。作成しますか? - + Failed to import '%1', analysis is stopped '%1'のインポートに失敗しました。(チェック中断) - + Project files (*.cppcheck) プロジェクトファイル (*.cppcheck) - + Select Project Filename プロジェクトファイル名を選択 - + No project file loaded プロジェクトファイルが読み込まれていません - + The project file %1 @@ -1329,7 +1329,7 @@ 最近使用したプロジェクトのリストからこのファイルを取り除きますか? - + Cppcheck GUI. Syntax: @@ -1370,7 +1370,7 @@ --data-dir=<directory> GUI のデータファイル(翻訳やcfg)のあるディレクトリを指定する。このオプションを指定した場合、GUIで起動しません。 - + Cppcheck GUI - Command line parameters Cppcheck GUI - コマンドラインパラメータ @@ -1405,7 +1405,7 @@ Edit suppression - + 抑制の編集 @@ -1573,12 +1573,12 @@ Undefines: - + 定義取り消し(Undefines) Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 - + 定義の取り消しはセミコロンで区切ります。例: UNDEF1;UNDEF2;UNDEF3 @@ -1718,12 +1718,12 @@ ProjectFileDialog - + Project file: %1 プロジェクトファイル:%1 - + Select Cppcheck build dir Cppcheckビルドディレクトリ @@ -1732,47 +1732,47 @@ Visual Studio (*.sln *.vcxproj);;コンパイルデータベース (compile_commands.json) - + Select include directory includeディレクトリを選択 - + Select a directory to check チェックするディレクトリを選択してください - + (no rule texts file) (ルールテキストファイルがない) - + Clang-tidy (not found) Clang-tidy (みつかりません) - + Visual Studio Visual Studio - + Compile database コンパイルデータベース - + Borland C++ Builder 6 Borland C++ Builder 6 - + Import Project プロジェクトのインポート - + Select directory to ignore 除外するディレクトリを選択してください @@ -1785,12 +1785,12 @@ 抑制するエラーID(error id)を選択してください - + Select MISRA rule texts file MISRAルールテキストファイルを選択 - + Misra rule texts file (%1) Misraルールテキストファイル (%1) @@ -1841,7 +1841,7 @@ 行 %1: 扱われていない要素(Unhandled element) %2 - + (Not found) (みつかりません) @@ -1872,22 +1872,22 @@ ResultsTree - + File ファイル - + Severity 警告種別 - + Line - + Summary 内容 @@ -1978,7 +1978,7 @@ - + Tag タグ @@ -2040,17 +2040,17 @@ ディレクトリを選択 - + Id Id - + Inconclusive 結論のでない - + Since date 日付 @@ -2147,7 +2147,7 @@ - + Failed to read the report. レポートの読み込みに失敗. @@ -2165,27 +2165,27 @@ メッセージ - + First included by は次のものが最初にインクルードしました - + Id ID - + Clear Log ログの消去 - + Copy this Log entry このログ項目をコピー - + Copy complete Log ログ全体をコピー @@ -2485,7 +2485,7 @@ Undefines: - + Undefines: @@ -2636,7 +2636,7 @@ Undefines - + 定義取り消し(Undef) diff -Nru cppcheck-1.85/gui/cppcheck_nl.ts cppcheck-1.86/gui/cppcheck_nl.ts --- cppcheck-1.85/gui/cppcheck_nl.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_nl.ts 2018-12-08 07:18:21.000000000 +0000 @@ -421,13 +421,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1011,12 +1011,12 @@ - + License Licentie - + Authors Auteurs @@ -1026,13 +1026,13 @@ XML bestanden (*.xml);;Tekst bestanden (*.txt);;CSV bestanden (*.csv) - + Save the report file Rapport opslaan - - + + XML files (*.xml) XML bestanden (*.xml) @@ -1086,17 +1086,17 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1105,7 +1105,7 @@ Een nieuw XML-bestand openen zal de huidige resultaten wissen Wilt u verder gaan? - + Open the report file Open het rapport bestand @@ -1126,34 +1126,34 @@ XML files version 2 (*.xml) - + Text files (*.txt) Tekst bestanden (*.txt) - + CSV files (*.csv) CSV bestanden (*.csv) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) Project bestanden (*.cppcheck);;Alle bestanden(*.*) - + Select Project File Selecteer project bestand - - + + Project: Project: @@ -1205,44 +1205,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename Selecteer project bestandsnaam - + No project file loaded Geen project bestand geladen - + The project file %1 @@ -1281,7 +1281,7 @@ .....-v,.--versie Toon versie van programma - + Cppcheck GUI. Syntax: @@ -1311,7 +1311,7 @@ - + Cppcheck GUI - Command line parameters Cppcheck GUI - Command lijn parameters @@ -1643,67 +1643,67 @@ ProjectFileDialog - + Project file: %1 Project Bestand %1 - + Select Cppcheck build dir - + Select include directory Selecteer include map - + Select a directory to check Selecteer een map om te controleren - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore Selecteer een map om te negeren - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1756,7 +1756,7 @@ - + (Not found) @@ -1787,22 +1787,22 @@ ResultsTree - + File Bestand - + Severity Ernst - + Line Regel - + Summary Overzicht @@ -1893,7 +1893,7 @@ - + Tag @@ -1953,17 +1953,17 @@ Selecteer map - + Id Id - + Inconclusive - + Since date @@ -2035,7 +2035,7 @@ - + Failed to read the report. Kon rapport niet lezen. @@ -2053,27 +2053,27 @@ Bericht - + First included by - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_ru.ts cppcheck-1.86/gui/cppcheck_ru.ts --- cppcheck-1.85/gui/cppcheck_ru.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_ru.ts 2018-12-08 07:18:21.000000000 +0000 @@ -421,13 +421,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1071,12 +1071,12 @@ Не удалось загрузить %1. Установленный Cppcheck поврежден. Вы можете использовать ключ --data-dir=<directory> в командной строке, чтобы указать, где расположен этот файл. - + License Лицензия - + Authors Авторы @@ -1086,13 +1086,13 @@ XML файлы версии 2 (*.xml);;XML файлы версии 1 (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) - + Save the report file Сохранить файл с отчетом - - + + XML files (*.xml) XML-файлы (*.xml) @@ -1142,17 +1142,17 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1161,7 +1161,7 @@ Открытые нового XML файла приведет к очистке текущих результатов. Продолжить? - + Open the report file Открыть файл с отчетом @@ -1182,17 +1182,17 @@ XML файлы версии 2 (*.xml) - + Text files (*.txt) Текстовые файлы (*.txt) - + CSV files (*.csv) CSV файлы(*.csv) - + Cppcheck - %1 Cppcheck - %1 @@ -1209,19 +1209,19 @@ The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. - + Project files (*.cppcheck);;All files(*.*) Файлы проекта (*.cppcheck);;Все файлы(*.*) - + Select Project File Выберите файл проекта - - + + Project: Проект: @@ -1273,44 +1273,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename Выберите имя файла для проекта - + No project file loaded Файл с проектом не загружен - + The project file %1 @@ -1350,7 +1350,7 @@ -v, --version Показать версию программы - + Cppcheck GUI. Syntax: @@ -1380,7 +1380,7 @@ - + Cppcheck GUI - Command line parameters Cppcheck GUI - параметры Командной строки @@ -1716,67 +1716,67 @@ ProjectFileDialog - + Project file: %1 Файл проекта: %1 - + Select Cppcheck build dir - + Select include directory Выберите директорию для поиска заголовочных файлов - + Select a directory to check Выберите директорию для проверки - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore Выберите директорию, которую надо проигнорировать - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1829,7 +1829,7 @@ - + (Not found) @@ -1860,22 +1860,22 @@ ResultsTree - + File Файл - + Severity Важность - + Line Строка - + Summary Кратко @@ -1966,7 +1966,7 @@ - + Tag @@ -2025,17 +2025,17 @@ Выберите директорию - + Id Id - + Inconclusive Спорное - + Since date @@ -2107,7 +2107,7 @@ - + Failed to read the report. Не удалось прочитать отчет. @@ -2125,27 +2125,27 @@ Сообщение - + First included by - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_sr.ts cppcheck-1.86/gui/cppcheck_sr.ts --- cppcheck-1.85/gui/cppcheck_sr.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_sr.ts 2018-12-08 07:18:21.000000000 +0000 @@ -389,13 +389,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -975,12 +975,12 @@ - + License License - + Authors Authors @@ -989,13 +989,13 @@ XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Save the report file Save the report file - - + + XML files (*.xml) XML files (*.xml) @@ -1047,56 +1047,56 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? - + Open the report file - + Text files (*.txt) Text files (*.txt) - + CSV files (*.csv) - + Cppcheck - %1 Cppcheck - %1 - + Project files (*.cppcheck);;All files(*.*) - + Select Project File - - + + Project: @@ -1148,44 +1148,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename - + No project file loaded - + The project file %1 @@ -1196,7 +1196,7 @@ - + Cppcheck GUI. Syntax: @@ -1226,7 +1226,7 @@ - + Cppcheck GUI - Command line parameters @@ -1525,67 +1525,67 @@ ProjectFileDialog - + Project file: %1 - + Select Cppcheck build dir - + Select include directory - + Select a directory to check - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1636,7 +1636,7 @@ - + (Not found) @@ -1667,22 +1667,22 @@ ResultsTree - + File File - + Severity Severity - + Line Line - + Summary @@ -1761,7 +1761,7 @@ - + Tag @@ -1810,17 +1810,17 @@ - + Id - + Inconclusive - + Since date @@ -1892,7 +1892,7 @@ - + Failed to read the report. @@ -1902,27 +1902,27 @@ - + First included by - + Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_sv.ts cppcheck-1.86/gui/cppcheck_sv.ts --- cppcheck-1.85/gui/cppcheck_sv.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_sv.ts 2018-12-08 07:18:21.000000000 +0000 @@ -443,13 +443,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1099,12 +1099,12 @@ %2 - + License Licens - + Authors Utvecklare @@ -1114,13 +1114,13 @@ XML filer version 2 (*.xml);;XML filer version 1 (*.xml);;Text filer (*.txt);;CSV filer (*.csv) - + Save the report file Spara rapport - - + + XML files (*.xml) XML filer (*.xml) @@ -1170,17 +1170,17 @@ Något problem - + Error Fel - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Misslyckades att ladda %1. Din Cppcheck installation är ej komplett. Du kan använda --data-dir<directory> på kommandoraden för att specificera var denna fil finns. Det är meningen att --data-dir kommandot skall köras under installationen,så GUIt kommer ej visas när --data-dir används allt som händer är att en inställning görs. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1189,7 +1189,7 @@ När en ny XML fil öppnas så tas alla nuvarande resultat bort. Vill du fortsätta? - + Open the report file Öppna rapportfilen @@ -1218,17 +1218,17 @@ XML filer version 2 (*.xml) - + Text files (*.txt) Text filer (*.txt) - + CSV files (*.csv) CSV filer (*.csv) - + Cppcheck - %1 Cppcheck - %1 @@ -1245,19 +1245,19 @@ Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. - + Project files (*.cppcheck);;All files(*.*) Projektfiler (*.cppcheck);;Alla filer(*.*) - + Select Project File Välj projektfil - - + + Project: Projekt: @@ -1311,7 +1311,7 @@ Vill du fortsätta analysen utan att använda någon av dessa projekt filer? - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? @@ -1320,37 +1320,37 @@ Vill du stoppa analysen och avsluta Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML filer (*.xml);;Text filer (*.txt);;CSV filer (*.csv) - + Build dir '%1' does not exist, create it? Build dir '%1' existerar ej, skapa den? - + Failed to import '%1', analysis is stopped Misslyckades att importera '%1', analysen stoppas - + Project files (*.cppcheck) Projekt filer (*.cppcheck) - + Select Project Filename Välj Projektfil - + No project file loaded Inget projekt laddat - + The project file %1 @@ -1391,7 +1391,7 @@ -v, --version Show program version - + Cppcheck GUI. Syntax: @@ -1434,7 +1434,7 @@ is used. - + Cppcheck GUI - Command line parameters Cppcheck GUI - Command line parameters @@ -1801,22 +1801,22 @@ ProjectFileDialog - + Project file: %1 Projektfil: %1 - + (no rule texts file) - + Clang-tidy (not found) - + Select Cppcheck build dir Välj Cppcheck build dir @@ -1825,42 +1825,42 @@ Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) - + Select include directory Välj include sökväg - + Select MISRA rule texts file - + Misra rule texts file (%1) - + Select a directory to check Välj mapp att analysera - + Visual Studio Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project Importera Projekt @@ -1869,7 +1869,7 @@ Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json) - + Select directory to ignore Välj sökväg att ignorera @@ -1930,7 +1930,7 @@ - + (Not found) @@ -1961,22 +1961,22 @@ ResultsTree - + File Fil - + Severity Typ - + Line Rad - + Summary Sammanfattning @@ -2067,7 +2067,7 @@ - + Tag Tag @@ -2129,17 +2129,17 @@ Välj mapp - + Id Id - + Inconclusive Inconclusive - + Since date Sedan datum @@ -2211,7 +2211,7 @@ - + Failed to read the report. Misslyckades att läsa rapporten. @@ -2229,27 +2229,27 @@ Meddelande - + First included by Först inkluderad av - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/cppcheck_zh_CN.ts cppcheck-1.86/gui/cppcheck_zh_CN.ts --- cppcheck-1.85/gui/cppcheck_zh_CN.ts 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/cppcheck_zh_CN.ts 2018-12-08 07:18:21.000000000 +0000 @@ -418,13 +418,13 @@ - - - - - - - + + + + + + + Cppcheck Cppcheck @@ -1087,17 +1087,17 @@ - + Error - + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. - + Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? @@ -1106,13 +1106,13 @@ 打开一个新的 XML 文件将会清空当前结果。你要继续吗? - - + + XML files (*.xml) XML 文件(*.xml) - + Open the report file 打开报告文件 @@ -1125,12 +1125,12 @@ 你是否需要停止检查并退出 Cppcheck? - + License 许可证 - + Authors 作者 @@ -1140,7 +1140,7 @@ XML 文件版本 2 (*.xml);;XML 文件版本 1 (*.xml);; 文本文件(*.txt);; CSV 文件(*.csv) - + Save the report file 保存报告文件 @@ -1153,17 +1153,17 @@ XML 文件版本 2 (*.xml) - + Text files (*.txt) 文本文件(*.txt) - + CSV files (*.csv) CSV 文件(*.csv) - + Cppcheck - %1 Cppcheck - %1 @@ -1180,19 +1180,19 @@ 用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 - + Project files (*.cppcheck);;All files(*.*) 项目文件(*.cppcheck);;所有文件(*.*) - + Select Project File 选择项目文件 - - + + Project: 项目: @@ -1244,44 +1244,44 @@ - + Analyzer is running. Do you want to stop the analysis and exit Cppcheck? - + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) - + Build dir '%1' does not exist, create it? - + Failed to import '%1', analysis is stopped - + Project files (*.cppcheck) - + Select Project Filename 选择项目文件名 - + No project file loaded 项目文件未加载 - + The project file %1 @@ -1298,7 +1298,7 @@ 你要从最近使用的项目列表中删除此文件吗? - + Cppcheck GUI. Syntax: @@ -1328,7 +1328,7 @@ - + Cppcheck GUI - Command line parameters @@ -1660,67 +1660,67 @@ ProjectFileDialog - + Project file: %1 项目文件: %1 - + Select Cppcheck build dir - + Select include directory 选择 Include 目录 - + Select a directory to check 选择一个检查目录 - + (no rule texts file) - + Clang-tidy (not found) - + Visual Studio - + Compile database - + Borland C++ Builder 6 - + Import Project - + Select directory to ignore 选择忽略的目录 - + Select MISRA rule texts file - + Misra rule texts file (%1) @@ -1771,7 +1771,7 @@ - + (Not found) @@ -1802,22 +1802,22 @@ ResultsTree - + File 文件 - + Severity 严重性 - + Line - + Summary 概要 @@ -1908,7 +1908,7 @@ - + Tag @@ -1970,17 +1970,17 @@ 选择目录 - + Id Id - + Inconclusive - + Since date @@ -2078,7 +2078,7 @@ - + Failed to read the report. 读取报告失败。 @@ -2096,27 +2096,27 @@ 消息 - + First included by - + Id Id - + Clear Log - + Copy this Log entry - + Copy complete Log diff -Nru cppcheck-1.85/gui/erroritem.cpp cppcheck-1.86/gui/erroritem.cpp --- cppcheck-1.85/gui/erroritem.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/erroritem.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -79,3 +79,14 @@ } return str; } + +bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2) +{ + // TODO: Implement some better CID calculation + return errorItem1.errorId == errorItem2.errorId && + errorItem1.errorPath == errorItem2.errorPath && + errorItem1.file0 == errorItem2.file0 && + errorItem1.message == errorItem2.message && + errorItem1.inconclusive == errorItem2.inconclusive && + errorItem1.severity == errorItem2.severity; +} diff -Nru cppcheck-1.85/gui/erroritem.h cppcheck-1.86/gui/erroritem.h --- cppcheck-1.85/gui/erroritem.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/erroritem.h 2018-12-08 07:18:21.000000000 +0000 @@ -94,6 +94,11 @@ // Special GUI properties QString sinceDate; QString tags; + + /** + * Compare "CID" + */ + static bool sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2); }; Q_DECLARE_METATYPE(ErrorItem); diff -Nru cppcheck-1.85/gui/main.cpp cppcheck-1.86/gui/main.cpp --- cppcheck-1.85/gui/main.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/main.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -1,6 +1,6 @@ /* * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2017 Cppcheck team. + * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +44,7 @@ #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication app(argc, argv); diff -Nru cppcheck-1.85/gui/mainwindow.cpp cppcheck-1.86/gui/mainwindow.cpp --- cppcheck-1.85/gui/mainwindow.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/mainwindow.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -870,8 +870,7 @@ const QString platform = mProjectFile->getPlatform(); if (platform.endsWith(".xml")) { const QString applicationFilePath = QCoreApplication::applicationFilePath(); - const QString appPath = QFileInfo(applicationFilePath).canonicalPath(); - result.loadPlatformFile(appPath.toStdString().c_str(), platform.toStdString()); + result.loadPlatformFile(applicationFilePath.toStdString().c_str(), platform.toStdString()); } else { for (int i = cppcheck::Platform::Native; i <= cppcheck::Platform::Unix64; i++) { const cppcheck::Platform::PlatformType p = (cppcheck::Platform::PlatformType)i; diff -Nru cppcheck-1.85/gui/projectfiledialog.cpp cppcheck-1.86/gui/projectfiledialog.cpp --- cppcheck-1.85/gui/projectfiledialog.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/projectfiledialog.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -34,6 +34,18 @@ #include "library.h" #include "platforms.h" +/** Return paths from QListWidget */ +static QStringList getPaths(const QListWidget *list) +{ + const int count = list->count(); + QStringList paths; + for (int i = 0; i < count; i++) { + QListWidgetItem *item = list->item(i); + paths << QDir::fromNativeSeparators(item->text()); + } + return paths; +} + /** Platforms shown in the platform combobox */ static const cppcheck::Platform::PlatformType builtinPlatforms[] = { cppcheck::Platform::Native, @@ -107,9 +119,9 @@ } // Platforms.. - Platforms p; + Platforms platforms; for (int i = 0; i < numberOfBuiltinPlatforms; i++) - mUI.mComboBoxPlatform->addItem(p.get(builtinPlatforms[i]).mTitle); + mUI.mComboBoxPlatform->addItem(platforms.get(builtinPlatforms[i]).mTitle); QStringList platformFiles; foreach (QString sp, searchPaths) { if (sp.endsWith("/cfg")) @@ -121,8 +133,8 @@ foreach (QFileInfo item, dir.entryInfoList()) { const QString platformFile = item.fileName(); - cppcheck::Platform p; - if (!p.loadPlatformFile(applicationFilePath.toStdString().c_str(), platformFile.toStdString())) + cppcheck::Platform plat2; + if (!plat2.loadPlatformFile(applicationFilePath.toStdString().c_str(), platformFile.toStdString())) continue; if (platformFiles.indexOf(platformFile) == -1) @@ -253,14 +265,7 @@ mUI.mToolClangTidy->setText(tr("Clang-tidy (not found)")); mUI.mToolClangTidy->setEnabled(false); } - QString tags; - foreach (const QString tag, projectFile->getTags()) { - if (tags.isEmpty()) - tags = tag; - else - tags += ';' + tag; - } - mUI.mEditTags->setText(tags); + mUI.mEditTags->setText(projectFile->getTags().join(';')); updatePathsAndDefines(); } @@ -298,9 +303,7 @@ projectFile->setAddons(list); projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked()); projectFile->setClangTidy(mUI.mToolClangTidy->isChecked()); - QStringList tags(mUI.mEditTags->text().split(";")); - tags.removeAll(QString()); - projectFile->setTags(tags); + projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts)); } void ProjectFileDialog::ok() @@ -436,30 +439,14 @@ return mUI.mEditBuildDir->text(); } - QStringList ProjectFileDialog::getIncludePaths() const { - const int count = mUI.mListIncludeDirs->count(); - QStringList includePaths; - for (int i = 0; i < count; i++) { - QListWidgetItem *item = mUI.mListIncludeDirs->item(i); - includePaths << QDir::fromNativeSeparators(item->text()); - } - return includePaths; + return getPaths(mUI.mListIncludeDirs); } QStringList ProjectFileDialog::getDefines() const { - QString define = mUI.mEditDefines->text(); - QStringList defines; - if (!define.isEmpty()) { - define = define.trimmed(); - if (define.indexOf(';') != -1) - defines = define.split(";"); - else - defines.append(define); - } - return defines; + return mUI.mEditDefines->text().trimmed().split(QRegExp("\\s*;\\s*"), QString::SkipEmptyParts); } QStringList ProjectFileDialog::getUndefines() const @@ -472,24 +459,12 @@ QStringList ProjectFileDialog::getCheckPaths() const { - const int count = mUI.mListCheckPaths->count(); - QStringList paths; - for (int i = 0; i < count; i++) { - QListWidgetItem *item = mUI.mListCheckPaths->item(i); - paths << QDir::fromNativeSeparators(item->text()); - } - return paths; + return getPaths(mUI.mListCheckPaths); } QStringList ProjectFileDialog::getExcludedPaths() const { - const int count = mUI.mListExcludedPaths->count(); - QStringList paths; - for (int i = 0; i < count; i++) { - QListWidgetItem *item = mUI.mListExcludedPaths->item(i); - paths << QDir::fromNativeSeparators(item->text()); - } - return paths; + return getPaths(mUI.mListExcludedPaths); } QStringList ProjectFileDialog::getLibraries() const @@ -526,16 +501,7 @@ void ProjectFileDialog::setDefines(const QStringList &defines) { - QString definestr; - QString define; - foreach (define, defines) { - definestr += define; - definestr += ";"; - } - // Remove ; from the end of the string - if (definestr.endsWith(';')) - definestr = definestr.left(definestr.length() - 1); - mUI.mEditDefines->setText(definestr); + mUI.mEditDefines->setText(defines.join(";")); } void ProjectFileDialog::setUndefines(const QStringList &undefines) @@ -670,6 +636,9 @@ { const int row = mUI.mListSuppressions->currentRow(); QListWidgetItem *item = mUI.mListSuppressions->takeItem(row); + if (!item) + return; + int suppressionIndex = getSuppressionIndex(item->text()); if (suppressionIndex >= 0) mSuppressions.removeAt(suppressionIndex); diff -Nru cppcheck-1.85/gui/projectfiledialog.h cppcheck-1.86/gui/projectfiledialog.h --- cppcheck-1.85/gui/projectfiledialog.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/projectfiledialog.h 2018-12-08 07:18:21.000000000 +0000 @@ -132,9 +132,9 @@ /** * @brief Set undefine names to dialog control. - * @param defines List of undefine names to set to dialog control. + * @param undefines List of undefine names to set to dialog control. */ - void setUndefines(const QStringList &defines); + void setUndefines(const QStringList &undefines); /** * @brief Set check paths to dialog control. diff -Nru cppcheck-1.85/gui/resultstree.cpp cppcheck-1.86/gui/resultstree.cpp --- cppcheck-1.85/gui/resultstree.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/resultstree.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -1131,12 +1131,7 @@ static int indexOf(const QList &list, const ErrorItem &item) { for (int i = 0; i < list.size(); i++) { - if (list[i].errorId == item.errorId && - list[i].errorPath == item.errorPath && - list[i].file0 == item.file0 && - list[i].message == item.message && - list[i].inconclusive == item.inconclusive && - list[i].severity == item.severity) { + if (ErrorItem::sameCID(item, list[i])) { return i; } } diff -Nru cppcheck-1.85/gui/resultsview.cpp cppcheck-1.86/gui/resultsview.cpp --- cppcheck-1.85/gui/resultsview.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/gui/resultsview.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -331,20 +331,15 @@ return; } - XmlReport *report = new XmlReportV2(filename); - + XmlReportV2 report(filename); QList errors; - if (report) { - if (report->open()) - errors = report->read(); - else { - QMessageBox msgBox; - msgBox.setText(tr("Failed to read the report.")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.exec(); - } - delete report; - report = NULL; + if (report.open()) { + errors = report.read(); + } else { + QMessageBox msgBox; + msgBox.setText(tr("Failed to read the report.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); } ErrorItem item; diff -Nru cppcheck-1.85/htmlreport/cppcheck-htmlreport cppcheck-1.86/htmlreport/cppcheck-htmlreport --- cppcheck-1.85/htmlreport/cppcheck-htmlreport 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/htmlreport/cppcheck-htmlreport 2018-12-08 07:18:21.000000000 +0000 @@ -330,6 +330,10 @@ self.errors.append({ 'file': attributes.get('file', ''), 'line': int(attributes.get('line', 0)), + 'locations': [{ + 'file': attributes.get('file', ''), + 'line': int(attributes.get('line', 0)), + }], 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'] @@ -339,51 +343,36 @@ if name == 'cppcheck': self.versionCppcheck = attributes['version'] if name == 'error': - # is there a better solution than this? - if ('inconclusive' in attributes and 'cwe' in attributes): - self.errors.append({ - 'file': '', - 'line': 0, - 'id': attributes['id'], - 'severity': attributes['severity'], - 'msg': attributes['msg'], - 'verbose': attributes.get('verbose'), - 'inconclusive': attributes['inconclusive'], - 'cwe': attributes['cwe'] - }) - elif 'inconclusive' in attributes: - self.errors.append({ - 'file': '', - 'line': 0, - 'id': attributes['id'], - 'severity': attributes['severity'], - 'msg': attributes['msg'], - 'verbose': attributes.get('verbose'), - 'inconclusive': attributes['inconclusive'] - }) - elif 'cwe' in attributes: - self.errors.append({ - 'file': '', - 'line': 0, - 'id': attributes['id'], - 'severity': attributes['severity'], - 'msg': attributes['msg'], - 'verbose': attributes.get('verbose'), - 'cwe': attributes['cwe'] - }) - else: - self.errors.append({ - 'file': '', - 'line': 0, - 'id': attributes['id'], - 'severity': attributes['severity'], - 'msg': attributes['msg'], - 'verbose': attributes.get('verbose') - }) + error = { + 'locations': [], + 'file': '', + 'line': 0, + 'id': attributes['id'], + 'severity': attributes['severity'], + 'msg': attributes['msg'], + 'verbose': attributes.get('verbose') + } + + if 'inconclusive' in attributes: + error['inconclusive'] = attributes['inconclusive'] + if 'cwe' in attributes: + error['cwe'] = attributes['cwe'] + + self.errors.append(error) elif name == 'location': assert self.errors - self.errors[-1]['file'] = attributes['file'] - self.errors[-1]['line'] = int(attributes['line']) + error = self.errors[-1] + locations = error['locations'] + file = attributes['file'] + line = int(attributes['line']) + if not locations: + error['file'] = file + error['line'] = line + locations.append({ + 'file': file, + 'line': line, + 'info': attributes.get('info') + }) if __name__ == '__main__': # Configure all the options this little utility is using. @@ -464,7 +453,21 @@ decode_errors = [] for filename, data in sorted(files.items()): htmlfile = data['htmlfile'] - errors = data['errors'] + errors = [] + + for error in data['errors']: + for location in error['locations']: + if filename == location['file']: + newError = dict(error) + + del newError['locations'] + newError['line'] = location['line'] + if location.get('info'): + newError['msg'] = location['info'] + newError['severity'] = 'information' + del newError['verbose'] + + errors.append(newError) lines = [] for error in errors: diff -Nru cppcheck-1.85/htmlreport/test_htmlreport.py cppcheck-1.86/htmlreport/test_htmlreport.py --- cppcheck-1.85/htmlreport/test_htmlreport.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/htmlreport/test_htmlreport.py 2018-12-08 07:18:21.000000000 +0000 @@ -24,7 +24,7 @@ class TestHTMLReport(unittest.TestCase): def testReportError(self): - for xml_version in ['1', '2']: + for xml_version in ['2']: self.checkReportError(xml_version) def checkReportError(self, xml_version): @@ -47,7 +47,7 @@ self.assertIn('Memory leak:', detail_contents) def testReportNoError(self): - for xml_version in ['1', '2']: + for xml_version in ['2']: self.checkReportNoError(xml_version) def checkReportNoError(self, xml_version): diff -Nru cppcheck-1.85/lib/analyzerinfo.cpp cppcheck-1.86/lib/analyzerinfo.cpp --- cppcheck-1.85/lib/analyzerinfo.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/analyzerinfo.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -102,11 +102,11 @@ std::ifstream fin(files); if (fin.is_open()) { std::string line; - const std::string endsWith(':' + cfg + ':' + sourcefile); + const std::string end(':' + cfg + ':' + sourcefile); while (std::getline(fin,line)) { - if (line.size() <= endsWith.size() + 2U) + if (line.size() <= end.size() + 2U) continue; - if (line.compare(line.size()-endsWith.size(), endsWith.size(), endsWith) != 0) + if (!endsWith(line, end.c_str(), end.size())) continue; std::ostringstream ostr; ostr << buildDir << '/' << line.substr(0,line.find(':')); diff -Nru cppcheck-1.85/lib/astutils.cpp cppcheck-1.86/lib/astutils.cpp --- cppcheck-1.85/lib/astutils.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/astutils.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -28,7 +28,30 @@ #include "valueflow.h" #include -#include +#include + + +void visitAstNodes(const Token *ast, std::function visitor) +{ + std::stack tokens; + tokens.push(ast); + while (!tokens.empty()) { + const Token *tok = tokens.top(); + tokens.pop(); + if (!tok) + continue; + + ChildrenToVisit c = visitor(tok); + + if (c == ChildrenToVisit::done) + break; + if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2) + tokens.push(tok->astOperand1()); + if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2) + tokens.push(tok->astOperand2()); + } +} + static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign) { @@ -71,6 +94,21 @@ return tok && (tok->isBoolean() || (tok->valueType() && tok->valueType()->type == ValueType::Type::BOOL && !tok->valueType()->pointer)); } +bool astIsPointer(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->pointer; +} + +bool astIsIterator(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR; +} + +bool astIsContainer(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::CONTAINER; +} + std::string astCanonicalType(const Token *expr) { if (!expr) @@ -93,7 +131,7 @@ { if (tok->str() == rhs) return true; - if (tok->isName() && !tok->varId() && tok->values().size() == 1U && tok->values().front().isKnown() && MathLib::toString(tok->values().front().intvalue) == rhs) + if (tok->isName() && !tok->varId() && tok->hasKnownIntValue() && MathLib::toString(tok->values().front().intvalue) == rhs) return true; return false; } @@ -121,8 +159,11 @@ } else if (comp == "!=" && rhs == std::string("0")) { ret = tok; } else if (comp == "==" && rhs == std::string("0")) { - if (tok->str() == "!") + if (tok->str() == "!") { ret = tok->astOperand1(); + // handle (!(x!=0)) as (x==0) + astIsVariableComparison(ret, "!=", "0", &ret); + } } while (ret && ret->str() == ".") ret = ret->astOperand2(); @@ -133,6 +174,33 @@ return ret; } +static bool hasToken(const Token * startTok, const Token * stopTok, const Token * tok) +{ + for (const Token * tok2 = startTok; tok2 != stopTok; tok2 = tok2->next()) { + if (tok2 == tok) + return true; + } + return false; +} + +const Token * nextAfterAstRightmostLeaf(const Token * tok) +{ + const Token * rightmostLeaf = tok; + if (!rightmostLeaf || !rightmostLeaf->astOperand1()) + return nullptr; + do { + if (rightmostLeaf->astOperand2()) + rightmostLeaf = rightmostLeaf->astOperand2(); + else + rightmostLeaf = rightmostLeaf->astOperand1(); + } while (rightmostLeaf->astOperand1()); + while (Token::Match(rightmostLeaf->next(), "]|)") && !hasToken(rightmostLeaf->next()->link(), rightmostLeaf->next(), tok)) + rightmostLeaf = rightmostLeaf->next(); + if (rightmostLeaf->str() == "{" && rightmostLeaf->link()) + rightmostLeaf = rightmostLeaf->link(); + return rightmostLeaf->next(); +} + static const Token * getVariableInitExpression(const Variable * var) { if (!var || !var->declEndToken()) @@ -148,7 +216,7 @@ } /// If tok2 comes after tok1 -static bool precedes(const Token * tok1, const Token * tok2) +bool precedes(const Token * tok1, const Token * tok2) { if (!tok1) return false; @@ -173,9 +241,12 @@ // calling nonstatic method? if (Token::Match(expr->previous(), "!!:: %name% (") && expr->function() && expr->function()->nestedIn && expr->function()->nestedIn->isClassOrStruct()) { // is it a method of this? - const Scope *nestedIn = expr->scope(); - while (nestedIn && nestedIn != expr->function()->nestedIn) + const Scope *nestedIn = expr->scope()->functionOf; + if (nestedIn && nestedIn->function) + nestedIn = nestedIn->function->token->scope(); + while (nestedIn && nestedIn != expr->function()->nestedIn) { nestedIn = nestedIn->nestedIn; + } return nestedIn == expr->function()->nestedIn; } return exprDependsOnThis(expr->astOperand1()) || exprDependsOnThis(expr->astOperand2()); @@ -334,8 +405,6 @@ return false; if (pure && tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof") { if (!tok1->function()) { - if (!Token::Match(tok1->previous(), ".|::") && !library.isFunctionConst(tok1) && !tok1->isAttributeConst() && !tok1->isAttributePure()) - return false; if (Token::simpleMatch(tok1->previous(), ".")) { const Token *lhs = tok1->previous(); while (Token::Match(lhs, "(|.|[")) @@ -345,6 +414,12 @@ (Token::Match(lhs, "%var% . %name% (") && library.isFunctionConst(lhs->tokAt(2))); if (!lhsIsConst) return false; + } else { + const Token * ftok = tok1; + if (Token::simpleMatch(tok1->previous(), "::")) + ftok = tok1->previous(); + if (!library.isFunctionConst(ftok) && !ftok->isAttributeConst() && !ftok->isAttributePure()) + return false; } } else { if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure()) @@ -580,7 +655,7 @@ return false; } -bool isConstExpression(const Token *tok, const Library& library, bool pure) +bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp) { if (!tok) return true; @@ -592,10 +667,14 @@ } if (tok->tokType() == Token::eIncDecOp) return false; + if (tok->isAssignmentOp()) + return false; + if (isLikelyStreamRead(cpp, tok)) + return false; // bailout when we see ({..}) if (tok->str() == "{") return false; - return isConstExpression(tok->astOperand1(), library, pure) && isConstExpression(tok->astOperand2(), library, pure); + return isConstExpression(tok->astOperand1(), library, pure, cpp) && isConstExpression(tok->astOperand2(), library, pure, cpp); } bool isWithoutSideEffects(bool cpp, const Token* tok) @@ -775,10 +854,10 @@ const unsigned int argCount = numberOfArguments(tok); const Scope *typeScope = tok->variable()->typeScope(); if (typeScope) { - for (std::list::const_iterator it = typeScope->functionList.begin(); it != typeScope->functionList.end(); ++it) { - if (!it->isConstructor() || it->argCount() < argCount) + for (const Function &function : typeScope->functionList) { + if (!function.isConstructor() || function.argCount() < argCount) continue; - const Variable *arg = it->getArgumentVar(argnr); + const Variable *arg = function.getArgumentVar(argnr); if (arg && arg->isReference() && !arg->isConst()) return true; } @@ -871,6 +950,20 @@ return false; } +bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp) +{ + if (!var) + return false; + if (!var->scope()) + return false; + const Token * start = var->declEndToken(); + if (!start) + return false; + if (Token::Match(start, "; %varid% =", var->declarationId())) + start = start->tokAt(2); + return isVariableChanged(start->next(), var->scope()->bodyEnd, var->declarationId(), var->isGlobal(), settings, cpp); +} + int numberOfArguments(const Token *start) { int arguments=0; @@ -908,18 +1001,16 @@ { if (!first || first->str() != "[") return nullptr; - const Token* tok = first->link(); - if (Token::simpleMatch(tok, "] {")) - return tok->linkAt(1); - if (!Token::simpleMatch(tok, "] (")) + if (!Token::Match(first->link(), "] (|{")) + return nullptr; + if (first->astOperand1() != first->link()->next()) return nullptr; - tok = tok->linkAt(1)->next(); - if (tok && tok->str() == "constexpr") - tok = tok->next(); - if (tok && tok->str() == "mutable") - tok = tok->next(); - if (tok && tok->str() == "{") - return tok->link(); + const Token * tok = first; + + if (tok->astOperand1() && tok->astOperand1()->str() == "(") + tok = tok->astOperand1(); + if (tok->astOperand1() && tok->astOperand1()->str() == "{") + return tok->astOperand1()->link(); return nullptr; } @@ -946,3 +1037,144 @@ return (!parent->astOperand1()->valueType() || !parent->astOperand1()->valueType()->isIntegral()); } + +static bool nonLocal(const Variable* var) +{ + return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference(); +} + +static bool hasFunctionCall(const Token *tok) +{ + if (!tok) + return false; + if (Token::Match(tok, "%name% (")) + // todo, const/pure function? + return true; + return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); +} + +struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) +{ + // Parse the given tokens + for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { + if (Token::simpleMatch(tok, "try {")) { + // TODO: handle try + return Result(Result::Type::BAILOUT); + } + + if (tok->str() == "}" && (tok->scope()->type == Scope::eFor || tok->scope()->type == Scope::eWhile)) { + // TODO: handle loops better + return Result(Result::Type::BAILOUT); + } + + if (Token::simpleMatch(tok, "break ;")) { + return Result(Result::Type::BREAK, tok); + } + + if (Token::Match(tok, "continue|return|throw|goto")) { + // TODO: Handle these better + return Result(Result::Type::RETURN); + } + + if (Token::simpleMatch(tok, "else {")) + tok = tok->linkAt(1); + + if (Token::simpleMatch(tok, "asm (")) { + return Result(Result::Type::BAILOUT); + } + + if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT); + } + + if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { + const Token *parent = tok; + while (Token::Match(parent->astParent(), ".|::|[")) + parent = parent->astParent(); + if (Token::simpleMatch(parent->astParent(), "=") && parent == parent->astParent()->astOperand1()) { + if (!local && hasFunctionCall(parent->astParent()->astOperand2())) { + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT); + } + if (hasOperand(parent->astParent()->astOperand2(), expr)) { + if (mReassign) + return Result(Result::Type::READ); + continue; + } + const bool reassign = isSameExpression(mCpp, false, expr, parent, mLibrary, false, false, nullptr); + if (reassign) + return Result(Result::Type::WRITE, parent->astParent()); + return Result(Result::Type::READ); + } else { + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT); + } + } + + if (Token::Match(tok, ") {")) { + const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local); + if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) + return result1; + if (Token::simpleMatch(tok->linkAt(1), "} else {")) { + const Token *elseStart = tok->linkAt(1)->tokAt(2); + const Result &result2 = checkRecursive(expr, elseStart, elseStart->link(), exprVarIds, local); + if (result2.type == Result::Type::READ || result2.type == Result::Type::BAILOUT) + return result2; + if (result1.type == Result::Type::WRITE && result2.type == Result::Type::WRITE) + return result1; + tok = elseStart->link(); + } else { + tok = tok->linkAt(1); + } + } + } + + return Result(Result::Type::NONE); +} + +FwdAnalysis::Result FwdAnalysis::check(const Token *expr, const Token *startToken, const Token *endToken) +{ + // all variable ids in expr. + std::set exprVarIds; + bool local = true; + visitAstNodes(expr, + [&](const Token *tok) { + if (tok->varId() > 0) { + exprVarIds.insert(tok->varId()); + if (!Token::simpleMatch(tok->previous(), ".")) + local &= !nonLocal(tok->variable()); + } + return ChildrenToVisit::op1_and_op2; + }); + + Result result = checkRecursive(expr, startToken, endToken, exprVarIds, local); + + // Break => continue checking in outer scope + while (result.type == FwdAnalysis::Result::Type::BREAK) { + const Scope *s = result.token->scope(); + while (s->type == Scope::eIf) + s = s->nestedIn; + if (s->type != Scope::eSwitch) + break; + result = checkRecursive(expr, s->bodyEnd->next(), endToken, exprVarIds, local); + } + + return result; +} + +bool FwdAnalysis::hasOperand(const Token *tok, const Token *lhs) const +{ + if (!tok) + return false; + if (isSameExpression(mCpp, false, tok, lhs, mLibrary, false, false, nullptr)) + return true; + return hasOperand(tok->astOperand1(), lhs) || hasOperand(tok->astOperand2(), lhs); +} + +const Token *FwdAnalysis::reassign(const Token *expr, const Token *startToken, const Token *endToken) +{ + mReassign = true; + Result result = check(expr, startToken, endToken); + return result.type == FwdAnalysis::Result::Type::WRITE ? result.token : nullptr; +} diff -Nru cppcheck-1.85/lib/astutils.h cppcheck-1.86/lib/astutils.h --- cppcheck-1.85/lib/astutils.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/astutils.h 2018-12-08 07:18:21.000000000 +0000 @@ -22,6 +22,7 @@ #define astutilsH //--------------------------------------------------------------------------- +#include #include #include @@ -30,6 +31,20 @@ class Library; class Settings; class Token; +class Variable; + +enum class ChildrenToVisit { + none, + op1, + op2, + op1_and_op2, + done // found what we looked for, don't visit any more children +}; + +/** + * Visit AST nodes recursively. The order is not "well defined" + */ +void visitAstNodes(const Token *ast, std::function visitor); /** Is expression a 'signed char' if no promotion is used */ bool astIsSignedChar(const Token *tok); @@ -42,6 +57,12 @@ /** Is expression of boolean type? */ bool astIsBool(const Token *tok); +bool astIsPointer(const Token *tok); + +bool astIsIterator(const Token *tok); + +bool astIsContainer(const Token *tok); + /** * Get canonical type of expression. const/static/etc are not included and neither *&. * For example: @@ -56,6 +77,10 @@ /** Is given syntax tree a variable comparison against value */ const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); +const Token * nextAfterAstRightmostLeaf(const Token * tok); + +bool precedes(const Token * tok1, const Token * tok2); + bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isEqualKnownValue(const Token * const tok1, const Token * const tok2); @@ -75,7 +100,7 @@ bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); -bool isConstExpression(const Token *tok, const Library& library, bool pure); +bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp); bool isWithoutSideEffects(bool cpp, const Token* tok); @@ -108,6 +133,8 @@ /** Is variable changed in block of code? */ bool isVariableChanged(const Token *start, const Token *end, const unsigned int varid, bool globalvar, const Settings *settings, bool cpp); +bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp); + /** Determines the number of arguments - if token is a function call or macro * @param start token which is supposed to be the function/macro name. * \return Number of arguments @@ -121,7 +148,6 @@ /** * find lambda function end token - * \todo handle explicit return type * \param first The [ token * \return nullptr or the } */ @@ -134,4 +160,37 @@ */ bool isLikelyStreamRead(bool cpp, const Token *op); +class FwdAnalysis { +public: + FwdAnalysis(bool cpp, const Library &library) : mCpp(cpp), mLibrary(library), mReassign(false) {} + + bool hasOperand(const Token *tok, const Token *lhs) const; + + /** + * Check if "expr" is reassigned. The "expr" can be a tree (x.y[12]). + * @param expr Symbolic expression to perform forward analysis for + * @param startToken First token in forward analysis + * @param endToken Last token in forward analysis + * @return Token where expr is reassigned. If it's not reassigned then nullptr is returned. + */ + const Token *reassign(const Token *expr, const Token *startToken, const Token *endToken); + +private: + /** Result of forward analysis */ + struct Result { + enum class Type { NONE, READ, WRITE, BREAK, RETURN, BAILOUT } type; + explicit Result(Type type) : type(type), token(nullptr) {} + Result(Type type, const Token *token) : type(type), token(token) {} + const Token *token; + }; + + struct Result check(const Token *expr, const Token *startToken, const Token *endToken); + struct Result checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local); + + const bool mCpp; + const Library &mLibrary; + bool mReassign; + std::vector mReads; +}; + #endif // astutilsH diff -Nru cppcheck-1.85/lib/checkassert.cpp cppcheck-1.86/lib/checkassert.cpp --- cppcheck-1.85/lib/checkassert.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkassert.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -64,7 +64,7 @@ if (!scope) continue; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { - if (tok2->tokType() != Token::eAssignmentOp && tok2->tokType() != Token::eIncDecOp) + if (!tok2->isAssignmentOp() && tok2->tokType() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); diff -Nru cppcheck-1.85/lib/checkautovariables.cpp cppcheck-1.86/lib/checkautovariables.cpp --- cppcheck-1.85/lib/checkautovariables.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkautovariables.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -33,6 +33,7 @@ #include #include +#include //--------------------------------------------------------------------------- @@ -64,6 +65,12 @@ return (var && var->isArgument() && var->isArray()); } +static bool isArrayVar(const Token *tok) +{ + const Variable *var = tok->variable(); + return (var && var->isArray()); +} + static bool isRefPtrArg(const Token *tok) { const Variable *var = tok->variable(); @@ -160,6 +167,34 @@ return ((next->str() != "." || (!var->isPointer() && (!var->isClass() || var->type()))) && next->strAt(2) != "."); } +static bool isAddressOfLocalVariableRecursive(const Token *expr) +{ + if (!expr) + return false; + if (Token::Match(expr, "+|-")) + return isAddressOfLocalVariableRecursive(expr->astOperand1()) || isAddressOfLocalVariableRecursive(expr->astOperand2()); + if (expr->str() == "(" && !expr->astOperand2()) + return isAddressOfLocalVariableRecursive(expr->astOperand1()); + if (expr->str() == "&" && !expr->astOperand2()) { + const Token *op = expr->astOperand1(); + bool deref = false; + while (Token::Match(op, ".|[")) { + if (op->str() == "[") + deref = true; + op = op->astOperand1(); + } + return op && isAutoVar(op) && (!deref || !op->variable()->isPointer()); + } + return false; +} + +static bool isAddressOfLocalVariable(const Token *expr) +{ + if (!expr || !expr->valueType() || expr->valueType()->pointer == 0) + return false; + return isAddressOfLocalVariableRecursive(expr); +} + static bool variableIsUsedInScope(const Token* start, unsigned int varId, const Scope *scope) { if (!start) // Ticket #5024 @@ -223,13 +258,17 @@ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + // Skip lambda.. + if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { + tok = lambdaEndToken; + continue; + } // Critical assignment if (Token::Match(tok, "[;{}] %var% = & %var%") && isRefPtrArg(tok->next()) && isAutoVar(tok->tokAt(4))) { if (checkRvalueExpression(tok->tokAt(4))) errorAutoVariableAssignment(tok->next(), false); - } else if (Token::Match(tok, "[;{}] * %var% = & %var%") && isPtrArg(tok->tokAt(2)) && isAutoVar(tok->tokAt(5))) { - if (checkRvalueExpression(tok->tokAt(5))) - errorAutoVariableAssignment(tok->next(), false); + } else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAddressOfLocalVariable(tok->tokAt(3)->astOperand2())) { + errorAutoVariableAssignment(tok->next(), false); } else if (mSettings->isEnabled(Settings::WARNING) && Token::Match(tok, "[;{}] %var% = &| %var% ;") && isGlobalPtr(tok->next())) { const Token * const pointer = tok->next(); if (isAutoVarArray(tok->tokAt(3))) { @@ -269,48 +308,25 @@ if (checkRvalueExpression(varTok)) errorAutoVariableAssignment(tok->next(), false); } - // Critical return - else if (Token::Match(tok, "return %var% ;") && isAutoVar(tok->next())) { - const std::list &values = tok->next()->values(); - const ValueFlow::Value *value = nullptr; - for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { - if (!it->isTokValue()) - continue; - if (!mSettings->inconclusive && it->isInconclusive()) - continue; - if (!Token::Match(it->tokvalue->previous(), "= & %var%")) - continue; - if (!isAutoVar(it->tokvalue->next())) - continue; - if (!value || value->isInconclusive()) - value = &(*it); - } - - if (value) - errorReturnAddressToAutoVariable(tok, value); - } - - else if (Token::Match(tok, "return & %var% ;")) { - const Token* varTok = tok->tokAt(2); - if (isAutoVar(varTok)) - errorReturnAddressToAutoVariable(tok); - else if (varTok->varId()) { - const Variable * var1 = varTok->variable(); - if (var1 && var1->isArgument() && var1->typeEndToken()->str() != "&") - errorReturnAddressOfFunctionParameter(tok, varTok->str()); - } - } // Invalid pointer deallocation else if ((Token::Match(tok, "%name% ( %var% ) ;") && mSettings->library.dealloc(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); - if (isAutoVarArray(tok)) - errorInvalidDeallocation(tok); + if (isArrayVar(tok)) + errorInvalidDeallocation(tok, nullptr); + else if (tok && tok->variable() && tok->variable()->isPointer()) { + for (const ValueFlow::Value &v : tok->values()) { + if (v.isTokValue() && isArrayVar(v.tokvalue)) { + errorInvalidDeallocation(tok, &v); + break; + } + } + } } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.dealloc(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVar(tok)) - errorInvalidDeallocation(tok); + errorInvalidDeallocation(tok, nullptr); } } } @@ -318,28 +334,6 @@ //--------------------------------------------------------------------------- -void CheckAutoVariables::returnPointerToLocalArray() -{ - const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); - - for (const Scope * scope : symbolDatabase->functionScopes) { - if (!scope->function) - continue; - - const Token *tok = scope->function->tokenDef; - - // have we reached a function that returns a pointer - if (tok->previous() && tok->previous()->str() == "*") { - for (const Token *tok2 = scope->bodyStart->next(); tok2 && tok2 != scope->bodyEnd; tok2 = tok2->next()) { - // Return pointer to local array variable.. - if (tok2 ->str() == "return" && isAutoVarArray(tok2->astOperand1())) { - errorReturnPointerToLocalArray(tok2); - } - } - } - } -} - void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable returned.", CWE562, false); @@ -573,6 +567,183 @@ } } +static bool isInScope(const Token * tok, const Scope * scope) +{ + if (!tok) + return false; + if (!scope) + return false; + const Variable * var = tok->variable(); + if (var && (var->isGlobal() || var->isStatic() || var->isExtern())) + return false; + if (tok->scope() && tok->scope()->isNestedIn(scope)) + return true; + if (!var) + return false; + if (var->isArgument() && !var->isReference()) { + const Scope * tokScope = tok->scope(); + if (!tokScope) + return false; + for (const Scope * argScope:tokScope->nestedList) { + if (argScope && argScope->isNestedIn(scope)) + return true; + } + } + return false; +} + +static bool isDeadScope(const Token * tok, const Scope * scope) +{ + if (!tok) + return false; + if (!scope) + return false; + const Variable * var = tok->variable(); + if (var && (!var->isLocal() || var->isStatic() || var->isExtern())) + return false; + if (tok->scope() && tok->scope()->bodyEnd != scope->bodyEnd && precedes(tok->scope()->bodyEnd, scope->bodyEnd)) + return true; + return false; +} + +static int getPointerDepth(const Token *tok) +{ + if (!tok) + return 0; + return tok->valueType() ? tok->valueType()->pointer : 0; +} + +void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) +{ + if (!start) + return; + const Scope * scope = start->scope(); + if (!scope) + return; + // If the scope is not set correctly then skip checking it + if (scope->bodyStart != start) + return; + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + for (const ValueFlow::Value& val:tok->values()) { + if (!val.isLifetimeValue()) + continue; + // Skip temporaries for now + if (val.tokvalue == tok) + continue; + if (Token::Match(tok->astParent(), "return|throw")) { + if (getPointerDepth(tok) < getPointerDepth(val.tokvalue)) + continue; + if (tok->astParent()->str() == "return" && !astIsContainer(tok) && scope->function && + mSettings->library.detectContainer(scope->function->retDef)) + continue; + if (isInScope(val.tokvalue, scope)) { + errorReturnDanglingLifetime(tok, &val); + break; + } + } else if (isDeadScope(val.tokvalue, tok->scope())) { + errorInvalidLifetime(tok, &val); + break; + } else if (tok->variable() && tok->variable()->declarationId() == tok->varId() && + !tok->variable()->isLocal() && !tok->variable()->isArgument() && + isInScope(val.tokvalue, tok->scope())) { + errorDanglngLifetime(tok, &val); + break; + } + } + const Token *lambdaEndToken = findLambdaEndToken(tok); + if (lambdaEndToken) { + checkVarLifetimeScope(lambdaEndToken->link(), lambdaEndToken); + tok = lambdaEndToken; + } + } +} + +void CheckAutoVariables::checkVarLifetime() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + if (!scope->function) + continue; + checkVarLifetimeScope(scope->bodyStart, scope->bodyEnd); + } +} + +static std::string lifetimeType(const Token *tok, const ValueFlow::Value* val) +{ + std::string result; + if (!val) + return "object"; + switch (val->lifetimeKind) { + case ValueFlow::Value::Lambda: + result = "lambda"; + break; + case ValueFlow::Value::Iterator: + result = "iterator"; + break; + case ValueFlow::Value::Object: + if (astIsPointer(tok)) + result = "pointer"; + else + result = "object"; + break; + } + return result; +} + +static std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath) +{ + const Token *vartok = val ? val->tokvalue : nullptr; + std::string type = lifetimeType(tok, val); + std::string msg = type; + if (vartok) { + errorPath.emplace_back(vartok, "Variable created here."); + const Variable * var = vartok->variable(); + if (var) { + switch (val->lifetimeKind) { + case ValueFlow::Value::Object: + if (type == "pointer") + msg += " to local variable"; + else + msg += " that points to local variable"; + break; + case ValueFlow::Value::Lambda: + msg += " that captures local variable"; + break; + case ValueFlow::Value::Iterator: + msg += " to local container"; + break; + } + msg += " '" + var->name() + "'"; + } + } + return msg; +} + +void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) +{ + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string msg = "Returning " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, false); +} + +void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) +{ + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, false); +} + +void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) +{ + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string tokName = tok ? tok->str() : "x"; + std::string msg = "Non-local variable '" + tokName + "' will use " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, false); +} + void CheckAutoVariables::errorReturnReference(const Token *tok) { reportError(tok, Severity::error, "returnReference", "Reference to auto variable returned.", CWE562, false); @@ -583,12 +754,25 @@ reportError(tok, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, false); } -void CheckAutoVariables::errorInvalidDeallocation(const Token *tok) +void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) { - reportError(tok, + const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr); + + std::string type = "auto-variable"; + if (var) { + if (var->isGlobal()) + type = "global variable"; + else if (var->isStatic()) + type = "static variable"; + } + + if (val) + type += " (" + val->tokvalue->str() + ")"; + + reportError(getErrorPath(tok, val, "Deallocating memory that was not dynamically allocated"), Severity::error, "autovarInvalidDeallocation", - "Deallocation of an auto-variable results in undefined behaviour.\n" - "The deallocation of an auto-variable results in undefined behaviour. You should only free memory " + "Deallocation of an " + type + " results in undefined behaviour.\n" + "The deallocation of an " + type + " results in undefined behaviour. You should only free memory " "that has been allocated dynamically.", CWE590, false); } diff -Nru cppcheck-1.85/lib/checkautovariables.h cppcheck-1.86/lib/checkautovariables.h --- cppcheck-1.85/lib/checkautovariables.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkautovariables.h 2018-12-08 07:18:21.000000000 +0000 @@ -53,12 +53,12 @@ CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.assignFunctionArg(); checkAutoVariables.returnReference(); + checkAutoVariables.checkVarLifetime(); } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override { CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.autoVariables(); - checkAutoVariables.returnPointerToLocalArray(); } /** assign function argument */ @@ -67,12 +67,13 @@ /** Check auto variables */ void autoVariables(); - /** Returning pointer to local array */ - void returnPointerToLocalArray(); - /** Returning reference to local/temporary variable */ void returnReference(); + void checkVarLifetime(); + + void checkVarLifetimeScope(const Token * start, const Token * end); + private: /** * Returning a temporary object? @@ -87,9 +88,12 @@ void errorAssignAddressOfLocalVariableToGlobalPointer(const Token *pointer, const Token *variable); void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok, bool inconclusive); + void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); + void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); + void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); void errorReturnReference(const Token *tok); void errorReturnTempReference(const Token *tok); - void errorInvalidDeallocation(const Token *tok); + void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname); void errorUselessAssignmentArg(const Token *tok); void errorUselessAssignmentPtrArg(const Token *tok); @@ -102,10 +106,13 @@ c.errorReturnPointerToLocalArray(nullptr); c.errorReturnReference(nullptr); c.errorReturnTempReference(nullptr); - c.errorInvalidDeallocation(nullptr); + c.errorInvalidDeallocation(nullptr, nullptr); c.errorReturnAddressOfFunctionParameter(nullptr, "parameter"); c.errorUselessAssignmentArg(nullptr); c.errorUselessAssignmentPtrArg(nullptr); + c.errorReturnDanglingLifetime(nullptr, nullptr); + c.errorInvalidLifetime(nullptr, nullptr); + c.errorDanglngLifetime(nullptr, nullptr); } static std::string myName() { diff -Nru cppcheck-1.85/lib/checkbool.cpp cppcheck-1.86/lib/checkbool.cpp --- cppcheck-1.85/lib/checkbool.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkbool.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -27,6 +27,7 @@ #include "symboldatabase.h" #include "token.h" #include "tokenize.h" +#include "valueflow.h" #include #include @@ -188,7 +189,7 @@ for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { - if (tok->tokType() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") + if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; const Token *firstToken = tok->previous(); if (tok->strAt(-1) == ")") { @@ -250,7 +251,7 @@ for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { - if (tok->tokType() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") + if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; bool firstTokenBool = false; @@ -454,3 +455,31 @@ reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable.", CWE704, false); } + +void CheckBool::returnValueOfFunctionReturningBool(void) +{ + if (!mSettings->isEnabled(Settings::STYLE)) + return; + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + if (!(scope->function && Token::Match(scope->function->retDef, "bool|_Bool"))) + continue; + + for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { + // Skip lambdas + const Token* tok2 = findLambdaEndToken(tok); + if (tok2) + tok = tok2; + else if (Token::simpleMatch(tok, "return") && tok->astOperand1() && + (tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings))) + returnValueBoolError(tok); + } + } +} + +void CheckBool::returnValueBoolError(const Token *tok) +{ + reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); +} diff -Nru cppcheck-1.85/lib/checkbool.h cppcheck-1.86/lib/checkbool.h --- cppcheck-1.85/lib/checkbool.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkbool.h 2018-12-08 07:18:21.000000000 +0000 @@ -58,6 +58,7 @@ checkBool.checkComparisonOfBoolWithInt(); checkBool.checkAssignBoolToFloat(); checkBool.pointerArithBool(); + checkBool.returnValueOfFunctionReturningBool(); } /** @brief Run checks against the simplified token list */ @@ -100,6 +101,9 @@ void pointerArithBool(); void pointerArithBoolCond(const Token *tok); + /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ + void returnValueOfFunctionReturningBool(); + private: // Error messages.. void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); @@ -112,6 +116,7 @@ void bitwiseOnBooleanError(const Token *tok, const std::string &varname, const std::string &op); void comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1); void pointerArithBoolError(const Token *tok); + void returnValueBoolError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { CheckBool c(nullptr, settings, errorLogger); @@ -126,6 +131,7 @@ c.comparisonOfBoolExpressionWithIntError(nullptr, true); c.pointerArithBoolError(nullptr); c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); + c.returnValueBoolError(nullptr); } static std::string myName() { @@ -140,7 +146,8 @@ "- comparison of a boolean value with boolean value using relational operator\n" "- using bool in bitwise expression\n" "- pointer addition in condition (either dereference is forgot or pointer overflow is required to make the condition false)\n" - "- Assigning bool value to pointer or float\n"; + "- Assigning bool value to pointer or float\n" + "- Returning an integer other than 0 or 1 from a function with boolean return value\n"; } }; /// @} diff -Nru cppcheck-1.85/lib/checkbufferoverrun.cpp cppcheck-1.86/lib/checkbufferoverrun.cpp --- cppcheck-1.85/lib/checkbufferoverrun.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkbufferoverrun.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -1081,30 +1081,22 @@ // Negative size in array declarations //--------------------------------------------------------------------------- -static bool isVLAIndex(const Token *index) +static bool isVLAIndex(const Token *tok) { - std::stack tokens; - tokens.push(index); - while (!tokens.empty()) { - const Token *tok = tokens.top(); - tokens.pop(); - if (!tok) - continue; - if (tok->varId() != 0U) + if (!tok) + return false; + if (tok->varId() != 0U) + return true; + if (tok->str() == "?") { + // this is a VLA index if both expressions around the ":" is VLA index + if (tok->astOperand2() && + tok->astOperand2()->str() == ":" && + isVLAIndex(tok->astOperand2()->astOperand1()) && + isVLAIndex(tok->astOperand2()->astOperand2())) return true; - if (tok->str() == "?") { - // this is a VLA index if both expressions around the ":" is VLA index - if (tok->astOperand2() && - tok->astOperand2()->str() == ":" && - isVLAIndex(tok->astOperand2()->astOperand1()) && - isVLAIndex(tok->astOperand2()->astOperand2())) - return true; - continue; - } - tokens.push(tok->astOperand1()); - tokens.push(tok->astOperand2()); + return false; } - return false; + return isVLAIndex(tok->astOperand1()) || isVLAIndex(tok->astOperand2()); } void CheckBufferOverrun::negativeArraySize() @@ -1803,12 +1795,24 @@ const Token* tok = function->token; // Get the name of the argv variable - unsigned int varid = 0; - if (Token::Match(tok, "main ( int %var% , char * %var% [ ] ,|)")) { - varid = tok->tokAt(7)->varId(); + unsigned int argvVarid = 0; + if (Token::simpleMatch(tok, "main (")) + tok = tok->tokAt(2); + else + continue; - } else if (Token::Match(tok, "main ( int %var% , char * * %var% ,|)")) { - varid = tok->tokAt(8)->varId(); + if (Token::Match(tok, "const| int %var% ,")) + tok = tok->nextArgument(); + else + continue; + + if (Token::Match(tok, "char * %var% [ ] ,|)")) { + argvVarid = tok->tokAt(2)->varId(); + } else if (Token::Match(tok, "char * * %var% ,|)") || + Token::Match(tok, "const char * %var% [ ] ,|)")) { + argvVarid = tok->tokAt(3)->varId(); + } else if (Token::Match(tok, "const char * * %var% ,|)")) { + argvVarid = tok->tokAt(4)->varId(); } else continue; @@ -1818,9 +1822,13 @@ // Search within main() for possible buffer overruns involving argv for (const Token* end = tok->link(); tok != end; tok = tok->next()) { // If argv is modified or tested, its size may be being limited properly - if (tok->varId() == varid) + if (tok->varId() == argvVarid) break; + // Update varid in case the input is copied by strdup() + if (Token::Match(tok->tokAt(-2), "%var% = strdup ( %varid%", argvVarid)) + argvVarid = tok->tokAt(-2)->varId(); + // Match common patterns that can result in a buffer overrun // e.g. strcpy(buffer, argv[0]) if (Token::Match(tok, "strcpy|strcat (")) { @@ -1829,7 +1837,9 @@ tok = nextArgument; else continue; // Ticket #7964 - if (Token::Match(tok, "* %varid%", varid) || Token::Match(tok, "%varid% [", varid)) + if (Token::Match(tok, "* %varid%", argvVarid) || // e.g. strcpy(buf, * ptr) + Token::Match(tok, "%varid% [", argvVarid) || // e.g. strcpy(buf, argv[1]) + Token::Match(tok, "%varid%", argvVarid)) // e.g. strcpy(buf, pointer) cmdLineArgsError(tok); } } diff -Nru cppcheck-1.85/lib/checkclass.cpp cppcheck-1.86/lib/checkclass.cpp --- cppcheck-1.85/lib/checkclass.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkclass.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -764,7 +764,7 @@ else { assignAllVar(usage); } - } else if (Token::Match(ftok, "::| %name% (") && ftok->str() != "if") { + } else if (Token::Match(ftok, "::| %name% (") && !Token::Match(ftok, "if|while|for")) { if (ftok->str() == "::") ftok = ftok->next(); @@ -1345,7 +1345,7 @@ for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->hasBody()) { // make sure return signature is correct - if (Token::Match(func->retDef, "%type% &") && func->retDef->str() == scope->className) { + if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) == "&") { checkReturnPtrThis(scope, &(*func), func->functionScope->bodyStart, func->functionScope->bodyEnd); } } @@ -1547,26 +1547,23 @@ if (!Token::simpleMatch(tok, "if (")) continue; - std::stack tokens; - tokens.push(tok->next()->astOperand2()); - while (!tokens.empty()) { - const Token *tok2 = tokens.top(); - tokens.pop(); - if (!tok2) - continue; - tokens.push(tok2->astOperand1()); - tokens.push(tok2->astOperand2()); + bool ret = false; + visitAstNodes(tok->next()->astOperand2(), + [&](const Token *tok2) { if (!Token::Match(tok2, "==|!=")) - continue; + return ChildrenToVisit::op1_and_op2; if (Token::simpleMatch(tok2->astOperand1(), "this")) tok2 = tok2->astOperand2(); else if (Token::simpleMatch(tok2->astOperand2(), "this")) tok2 = tok2->astOperand1(); else - continue; + return ChildrenToVisit::op1_and_op2; if (tok2 && tok2->isUnaryOp("&") && tok2->astOperand1()->str() == rhs->str()) - return true; - } + ret = true; + return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (ret) + return ret; } return false; @@ -1988,7 +1985,7 @@ const Token* lhs = tok1->previous(); if (lhs->str() == "&") { lhs = lhs->previous(); - if (lhs->tokType() == Token::eAssignmentOp && lhs->previous()->variable()) { + if (lhs->isAssignmentOp() && lhs->previous()->variable()) { if (lhs->previous()->variable()->typeStartToken()->strAt(-1) != "const" && lhs->previous()->variable()->isPointer()) return false; } @@ -1997,7 +1994,7 @@ if (lhs->astParent()->strAt(1) != "const") return false; } else { - if (lhs->tokType() == Token::eAssignmentOp) { + if (lhs->isAssignmentOp()) { const Variable* lhsVar = lhs->previous()->variable(); if (lhsVar && !lhsVar->isConst() && lhsVar->isReference() && lhs == lhsVar->nameToken()->next()) return false; @@ -2039,7 +2036,7 @@ } // Assignment - else if (end->next()->tokType() == Token::eAssignmentOp) + else if (end->next()->isAssignmentOp()) return false; // Streaming @@ -2128,12 +2125,13 @@ else reportError(toks, Severity::performance, "functionStatic", "$symbol:" + classname + "::" + funcname +"\n" - "Technically the member function '$symbol' can be static.\n" + "Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\n" "The member function '$symbol' can be made a static " "function. Making a function static can bring a performance benefit since no 'this' instance is " "passed to the function. This change should not cause compiler errors but it does not " "necessarily make sense conceptually. Think about your design and the task of the function first - " - "is it a function that must not access members of class instances?", CWE398, true); + "is it a function that must not access members of class instances? And maybe it is more appropriate " + "to move this function to a unnamed namespace.", CWE398, true); } //--------------------------------------------------------------------------- diff -Nru cppcheck-1.85/lib/checkcondition.cpp cppcheck-1.86/lib/checkcondition.cpp --- cppcheck-1.85/lib/checkcondition.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkcondition.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -51,6 +51,18 @@ CheckCondition instance; } +bool CheckCondition::diag(const Token* tok, bool insert) +{ + if (!tok) + return false; + if (mCondDiags.find(tok) == mCondDiags.end()) { + if (insert) + mCondDiags.insert(tok); + return false; + } + return true; +} + bool CheckCondition::isAliased(const std::set &vars) const { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { @@ -273,8 +285,8 @@ (parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) || (parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok)); - const bool isTrue = (tok->astOperand1()->values().size() == 1 && tok->astOperand1()->values().front().intvalue != 0 && tok->astOperand1()->values().front().isKnown()) || - (tok->astOperand2()->values().size() == 1 && tok->astOperand2()->values().front().intvalue != 0 && tok->astOperand2()->values().front().isKnown()); + const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) || + (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0); if (isBoolean && isTrue) badBitmaskCheckError(tok); @@ -429,8 +441,8 @@ if (cond1 && tok2->astOperand2() && - !cond1->hasKnownValue() && - !tok2->astOperand2()->hasKnownValue() && + !cond1->hasKnownIntValue() && + !tok2->astOperand2()->hasKnownIntValue() && isOverlappingCond(cond1, tok2->astOperand2(), true)) multiConditionError(tok2, cond1->linenr()); } @@ -498,18 +510,12 @@ bool nonConstFunctionCall = false; bool nonlocal = false; // nonlocal variable used in condition std::set vars; // variables used in condition - std::stack tokens; - tokens.push(condTok); - while (!tokens.empty()) { - const Token *cond = tokens.top(); - tokens.pop(); - if (!cond) - continue; - + visitAstNodes(condTok, + [&](const Token *cond) { if (Token::Match(cond, "%name% (")) { nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library); if (nonConstFunctionCall) - break; + return ChildrenToVisit::done; } if (cond->varId()) { @@ -526,189 +532,217 @@ // varid is 0. this is possibly a nonlocal variable.. nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (mTokenizer->isCPP() && cond->str() == "this"); } else { - tokens.push(cond->astOperand1()); - tokens.push(cond->astOperand2()); + return ChildrenToVisit::op1_and_op2; } - } + return ChildrenToVisit::none; + }); if (nonConstFunctionCall) continue; // parse until second condition is reached.. - enum MULTICONDITIONTYPE { INNER, AFTER } type; + enum MULTICONDITIONTYPE { INNER, AFTER }; const Token *tok; - if (Token::Match(scope.bodyStart, "{ return|throw|continue|break")) { - tok = scope.bodyEnd->next(); - type = MULTICONDITIONTYPE::AFTER; - } else { - tok = scope.bodyStart; - type = MULTICONDITIONTYPE::INNER; - } - const Token * const endToken = tok->scope()->bodyEnd; - for (; tok && tok != endToken; tok = tok->next()) { - if (Token::simpleMatch(tok, "if (")) { - // Does condition modify tracked variables? - if (const Token *op = Token::findmatch(tok, "++|--", tok->linkAt(1))) { - bool bailout = false; - while (op) { - if (vars.find(op->astOperand1()->varId()) != vars.end()) { - bailout = true; - break; + // Parse inner condition first and then early return condition + std::vector types = {MULTICONDITIONTYPE::INNER}; + if (Token::Match(scope.bodyStart, "{ return|throw|continue|break")) + types.push_back(MULTICONDITIONTYPE::AFTER); + for (MULTICONDITIONTYPE type:types) { + if (type == MULTICONDITIONTYPE::AFTER) { + tok = scope.bodyEnd->next(); + } else { + tok = scope.bodyStart; + } + const Token * const endToken = tok->scope()->bodyEnd; + + for (; tok && tok != endToken; tok = tok->next()) { + if (Token::Match(tok, "if|return")) { + const Token * condStartToken = tok->str() == "if" ? tok->next() : tok; + const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";"); + // Does condition modify tracked variables? + if (const Token *op = Token::findmatch(tok, "++|--", condEndToken)) { + bool bailout = false; + while (op) { + if (vars.find(op->astOperand1()->varId()) != vars.end()) { + bailout = true; + break; + } + if (nonlocal && op->astOperand1()->varId() == 0) { + bailout = true; + break; + } + op = Token::findmatch(op->next(), "++|--", condEndToken); } - if (nonlocal && op->astOperand1()->varId() == 0) { - bailout = true; + if (bailout) break; - } - op = Token::findmatch(op->next(), "++|--", tok->linkAt(1)); } - if (bailout) - break; - } - // Condition.. - const Token *cond2 = tok->next()->astOperand2(); - - ErrorPath errorPath; - - if (type == MULTICONDITIONTYPE::INNER) { - std::stack tokens1; - tokens1.push(cond1); - while (!tokens1.empty()) { - const Token *firstCondition = tokens1.top(); - tokens1.pop(); - if (!firstCondition) - continue; - if (firstCondition->str() == "&&") { - tokens1.push(firstCondition->astOperand1()); - tokens1.push(firstCondition->astOperand2()); - } else if (!firstCondition->hasKnownValue()) { - if (isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) { - if (!isAliased(vars)) - oppositeInnerConditionError(firstCondition, cond2, errorPath); - } else if (isSameExpression(mTokenizer->isCPP(), true, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { - identicalInnerConditionError(firstCondition, cond2, errorPath); + // Condition.. + const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1(); + const bool isReturnVar = (tok->str() == "return" && !Token::Match(cond2, "%cop%")); + + ErrorPath errorPath; + + if (type == MULTICONDITIONTYPE::INNER) { + std::stack tokens1; + tokens1.push(cond1); + while (!tokens1.empty()) { + const Token *firstCondition = tokens1.top(); + tokens1.pop(); + if (!firstCondition) + continue; + if (firstCondition->str() == "&&") { + tokens1.push(firstCondition->astOperand1()); + tokens1.push(firstCondition->astOperand2()); + } else if (!firstCondition->hasKnownIntValue()) { + if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) { + if (!isAliased(vars)) + oppositeInnerConditionError(firstCondition, cond2, errorPath); + } else if (!isReturnVar && isSameExpression(mTokenizer->isCPP(), true, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { + identicalInnerConditionError(firstCondition, cond2, errorPath); + } } } - } - } else { - std::stack tokens2; - tokens2.push(cond2); - while (!tokens2.empty()) { - const Token *secondCondition = tokens2.top(); - tokens2.pop(); - if (!secondCondition) - continue; - if (secondCondition->str() == "||" || secondCondition->str() == "&&") { - tokens2.push(secondCondition->astOperand1()); - tokens2.push(secondCondition->astOperand2()); - } else if ((!cond1->hasKnownValue() || !secondCondition->hasKnownValue()) && - isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) { - if (!isAliased(vars)) - identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath); + } else { + std::stack tokens2; + tokens2.push(cond2); + while (!tokens2.empty()) { + const Token *secondCondition = tokens2.top(); + tokens2.pop(); + if (!secondCondition) + continue; + if (secondCondition->str() == "||" || secondCondition->str() == "&&") { + tokens2.push(secondCondition->astOperand1()); + tokens2.push(secondCondition->astOperand2()); + } else if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) && + isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) { + if (!isAliased(vars)) + identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath); + } } } } - } - if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables - break; - if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope()) - break; - if (Token::Match(tok, "[;{}] %name% :")) - break; - // bailout if loop is seen. - // TODO: handle loops better. - if (Token::Match(tok, "for|while|do")) { - const Token *tok1 = tok->next(); - const Token *tok2; - if (Token::simpleMatch(tok, "do {")) { - if (!Token::simpleMatch(tok->linkAt(1), "} while (")) + if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables + break; + if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope()) + break; + if (Token::Match(tok, "[;{}] %name% :")) + break; + // bailout if loop is seen. + // TODO: handle loops better. + if (Token::Match(tok, "for|while|do")) { + const Token *tok1 = tok->next(); + const Token *tok2; + if (Token::simpleMatch(tok, "do {")) { + if (!Token::simpleMatch(tok->linkAt(1), "} while (")) + break; + tok2 = tok->linkAt(1)->linkAt(2); + } else if (Token::Match(tok, "if|while (")) { + tok2 = tok->linkAt(1); + if (Token::simpleMatch(tok2, ") {")) + tok2 = tok2->linkAt(1); + if (!tok2) + break; + } else { + // Incomplete code break; - tok2 = tok->linkAt(1)->linkAt(2); - } else if (Token::Match(tok, "if|while (")) { - tok2 = tok->linkAt(1); - if (Token::simpleMatch(tok2, ") {")) - tok2 = tok2->linkAt(1); - if (!tok2) + } + bool changed = false; + for (unsigned int varid : vars) { + if (isVariableChanged(tok1, tok2, varid, nonlocal, mSettings, mTokenizer->isCPP())) { + changed = true; + break; + } + } + if (changed) break; - } else { - // Incomplete code - break; } - bool changed = false; - for (unsigned int varid : vars) { - if (isVariableChanged(tok1, tok2, varid, nonlocal, mSettings, mTokenizer->isCPP())) { - changed = true; + if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || + (!tok->varId() && nonlocal)) { + if (Token::Match(tok, "%name% %assign%|++|--")) break; + if (Token::Match(tok->astParent(), "*|.|[")) { + const Token *parent = tok; + while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*"))) + parent = parent->astParent(); + if (Token::Match(parent->astParent(), "%assign%|++|--")) + break; } - } - if (changed) - break; - } - if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || - (!tok->varId() && nonlocal)) { - if (Token::Match(tok, "%name% %assign%|++|--")) - break; - if (Token::Match(tok->astParent(), "*|.|[")) { - const Token *parent = tok; - while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*"))) - parent = parent->astParent(); - if (Token::Match(parent->astParent(), "%assign%|++|--")) + if (mTokenizer->isCPP() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral())) break; - } - if (mTokenizer->isCPP() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral())) - break; - if (isLikelyStreamRead(mTokenizer->isCPP(), tok->next()) || isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) - break; - if (Token::Match(tok, "%name% [")) { - const Token *tok2 = tok->linkAt(1); - while (Token::simpleMatch(tok2, "] [")) - tok2 = tok2->linkAt(1); - if (Token::Match(tok2, "] %assign%|++|--")) + if (isLikelyStreamRead(mTokenizer->isCPP(), tok->next()) || isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) break; - } - if (Token::Match(tok->previous(), "++|--|& %name%")) - break; - if (tok->variable() && - !tok->variable()->isConst() && - Token::Match(tok, "%name% . %name% (")) { - const Function* function = tok->tokAt(2)->function(); - if (!function || !function->isConst()) + if (Token::Match(tok, "%name% [")) { + const Token *tok2 = tok->linkAt(1); + while (Token::simpleMatch(tok2, "] [")) + tok2 = tok2->linkAt(1); + if (Token::Match(tok2, "] %assign%|++|--")) + break; + } + if (Token::Match(tok->previous(), "++|--|& %name%")) + break; + if (tok->variable() && + !tok->variable()->isConst() && + Token::Match(tok, "%name% . %name% (")) { + const Function* function = tok->tokAt(2)->function(); + if (!function || !function->isConst()) + break; + } + if (Token::Match(tok->previous(), "[(,] %name% [,)]") && isParameterChanged(tok)) break; } - if (Token::Match(tok->previous(), "[(,] %name% [,)]") && isParameterChanged(tok)) - break; } } } } +static std::string innerSmtString(const Token * tok) +{ + if (!tok) + return "if"; + if (!tok->astTop()) + return "if"; + const Token * top = tok->astTop(); + if (top->str() == "(" && top->astOperand1()) + return top->astOperand1()->str(); + return top->str(); +} + void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { + if (diag(tok1) & diag(tok2)) + return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "!x"); + const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "opposite inner condition: " + s2)); - const std::string msg("Opposite inner 'if' condition leads to a dead code block.\n" - "Opposite inner 'if' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); + const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n" + "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, false); } void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { + if (diag(tok1) & diag(tok2)) + return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "x"); + const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "identical inner condition: " + s2)); - const std::string msg("Identical inner 'if' condition is always true.\n" - "Identical inner 'if' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); + const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n" + "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, false); } void CheckCondition::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) { + if (diag(cond1) & diag(cond2)) + return; const std::string cond(cond1 ? cond1->expressionString() : "x"); errorPath.emplace_back(ErrorPathItem(cond1, "first condition")); errorPath.emplace_back(ErrorPathItem(cond2, "second condition")); @@ -1154,7 +1188,7 @@ for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); - else if (tok2->tokType() == Token::eComparisonOp) { + else if (tok2->isComparisonOp()) { // This might be a template if (!isC && tok2->link()) break; @@ -1204,6 +1238,25 @@ errmsg, CWE398, false); } +static bool isConstVarExpression(const Token * tok) +{ + if (!tok) + return false; + if (Token::Match(tok, "%cop%")) { + if (tok->astOperand1() && !isConstVarExpression(tok->astOperand1())) + return false; + if (tok->astOperand2() && !isConstVarExpression(tok->astOperand2())) + return false; + return true; + } + if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) + return true; + if (tok->isEnumerator()) + return true; + if (tok->variable()) + return tok->variable()->isConst(); + return false; +} void CheckCondition::alwaysTrueFalse() { @@ -1218,11 +1271,14 @@ continue; if (!tok->hasKnownIntValue()) continue; + // Skip already diagnosed values + if (diag(tok, false)) + continue; if (Token::Match(tok, "%num%|%bool%|%char%")) continue; if (Token::Match(tok, "! %num%|%bool%|%char%")) continue; - if (Token::Match(tok, "%oror%|&&")) + if (Token::Match(tok, "%oror%|&&|:")) continue; if (Token::Match(tok, "%comp%") && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true)) continue; @@ -1232,8 +1288,23 @@ (Token::Match(tok->astParent(), "%oror%|&&") || Token::Match(tok->astParent()->astOperand1(), "if|while")); const bool constValExpr = tok->isNumber() && Token::Match(tok->astParent(),"%oror%|&&|?"); // just one number in boolean expression const bool compExpr = Token::Match(tok, "%comp%|!"); // a compare expression + const bool returnStatement = Token::simpleMatch(tok->astTop(), "return") && + Token::Match(tok->astParent(), "%oror%|&&|return"); + + if (!(constIfWhileExpression || constValExpr || compExpr || returnStatement)) + continue; + + if (returnStatement && scope->function && !Token::simpleMatch(scope->function->retDef, "bool")) + continue; + + if (returnStatement && isConstVarExpression(tok)) + continue; - if (!(constIfWhileExpression || constValExpr || compExpr)) + if (returnStatement && Token::simpleMatch(tok->astParent(), "return") && tok->variable() && ( + !tok->variable()->isLocal() || + tok->variable()->isReference() || + tok->variable()->isConst() || + !isVariableChanged(tok->variable(), mSettings, mTokenizer->isCPP()))) continue; // Don't warn in assertions. Condition is often 'always true' by intention. diff -Nru cppcheck-1.85/lib/checkcondition.h cppcheck-1.86/lib/checkcondition.h --- cppcheck-1.85/lib/checkcondition.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkcondition.h 2018-12-08 07:18:21.000000000 +0000 @@ -118,6 +118,9 @@ void checkPointerAdditionResultNotNull(); private: + // The conditions that have been diagnosed + std::set mCondDiags; + bool diag(const Token* tok, bool insert=true); bool isAliased(const std::set &vars) const; bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); diff -Nru cppcheck-1.85/lib/checkfunctions.cpp cppcheck-1.86/lib/checkfunctions.cpp --- cppcheck-1.85/lib/checkfunctions.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkfunctions.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -125,6 +125,17 @@ else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); } + + if (mSettings->library.isargstrz(functionToken, argnr)) { + if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) { + const ValueType * valueType = argtok->next()->valueType(); + const Variable * variable = argtok->next()->variable(); + if (valueType->type == ValueType::Type::CHAR && !variable->isArray() && !variable->isGlobal() && + (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) { + invalidFunctionArgStrError(argtok, functionToken->str(), argnr); + } + } + } } } } @@ -167,6 +178,14 @@ reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false); } +void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, unsigned int argnr) +{ + std::ostringstream errmsg; + errmsg << "$symbol:" << functionName << '\n'; + errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required."; + reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, false); +} + //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- diff -Nru cppcheck-1.85/lib/checkfunctions.h cppcheck-1.86/lib/checkfunctions.h --- cppcheck-1.85/lib/checkfunctions.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkfunctions.h 2018-12-08 07:18:21.000000000 +0000 @@ -110,6 +110,7 @@ private: void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); + void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, unsigned int argnr); void ignoredReturnValueError(const Token* tok, const std::string& function); void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1); void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); @@ -126,6 +127,7 @@ c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); c.invalidFunctionArgBoolError(nullptr, "func_name", 1); + c.invalidFunctionArgStrError(nullptr, "func_name", 1); c.ignoredReturnValueError(nullptr, "malloc"); c.mathfunctionCallWarning(nullptr); c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); diff -Nru cppcheck-1.85/lib/checkleakautovar.cpp cppcheck-1.86/lib/checkleakautovar.cpp --- cppcheck-1.85/lib/checkleakautovar.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkleakautovar.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -668,6 +668,7 @@ { // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { + // TODO : Write a separate checker for this that uses valueFlowForward. const std::map::const_iterator var = varInfo->alloctype.find(tok->varId()); if (var != varInfo->alloctype.end()) { bool unknown = false; diff -Nru cppcheck-1.85/lib/checkmemoryleak.cpp cppcheck-1.86/lib/checkmemoryleak.cpp --- cppcheck-1.85/lib/checkmemoryleak.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkmemoryleak.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -353,6 +353,8 @@ // Get return pointer.. unsigned int varid = 0; for (const Token *tok2 = func->functionScope->bodyStart; tok2 != func->functionScope->bodyEnd; tok2 = tok2->next()) { + if (const Token *endOfLambda = findLambdaEndToken(tok2)) + tok2 = endOfLambda; if (tok2->str() == "return") { const AllocType allocType = getAllocationType(tok2->next(), 0, callstack); if (allocType != No) @@ -490,8 +492,7 @@ { if (!tok) return false; - if (tok->values().size() == 1U && - tok->values().front().isKnown() && + if (tok->hasKnownIntValue() && tok->values().front().intvalue != 0) return true; if (tok->str() == "||") @@ -2034,6 +2035,7 @@ tok2->deleteNext(); } if ((result = Token::findmatch(tok, "[;{}] dealloc ; use_ ;")) != nullptr) { + // TODO : Write a separate checker for this that uses valueFlowForward. deallocuseError(result->tokAt(3), varname); } diff -Nru cppcheck-1.85/lib/checkmemoryleak.h cppcheck-1.86/lib/checkmemoryleak.h --- cppcheck-1.85/lib/checkmemoryleak.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkmemoryleak.h 2018-12-08 07:18:21.000000000 +0000 @@ -187,7 +187,8 @@ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override { CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.checkReallocUsage(); - checkMemoryLeak.check(); + // Commented out so we can evaluate if this checking can be removed. + //checkMemoryLeak.check(); } /** @brief Unit testing : testing the white list */ diff -Nru cppcheck-1.85/lib/checkother.cpp cppcheck-1.86/lib/checkother.cpp --- cppcheck-1.85/lib/checkother.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkother.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -19,6 +19,7 @@ //--------------------------------------------------------------------------- #include "checkother.h" +#include "checkuninitvar.h" // CheckUninitVar::isVariableUsage #include "astutils.h" #include "errorlogger.h" @@ -36,7 +37,6 @@ #include #include #include -#include #include //--------------------------------------------------------------------------- @@ -412,310 +412,102 @@ // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- -static bool nonLocal(const Variable* var) -{ - return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference(); -} - -static bool nonLocalVolatile(const Variable* var) -{ - if (var && var->isVolatile()) - return false; - return nonLocal(var); -} - -static void eraseNotLocalArg(std::map& container, const SymbolDatabase* symbolDatabase) -{ - for (std::map::iterator i = container.begin(); i != container.end();) { - const Variable* var = symbolDatabase->getVariableFromVarId(i->first); - if (!var || nonLocal(var)) { - container.erase(i++); - } else - ++i; - } -} - -static void eraseMemberAssignments(const unsigned int varId, const std::map > &membervars, std::map &varAssignments) -{ - const std::map >::const_iterator it = membervars.find(varId); - if (it != membervars.end()) { - const std::set& vars = it->second; - for (unsigned int var : vars) { - varAssignments.erase(var); - if (var != varId) - eraseMemberAssignments(var, membervars, varAssignments); - } - } -} - -static bool checkExceptionHandling(const Token* tok) -{ - const Variable* var = tok->variable(); - const Scope* upperScope = tok->scope(); - if (var && upperScope == var->scope()) - return true; - while (upperScope && upperScope->type != Scope::eTry && upperScope->type != Scope::eLambda && (!var || upperScope->nestedIn != var->scope()) && upperScope->isExecutable()) { - upperScope = upperScope->nestedIn; - } - if (var && upperScope && upperScope->type == Scope::eTry) { - // Check all exception han - const Token* tok2 = upperScope->bodyEnd; - while (Token::simpleMatch(tok2, "} catch (")) { - tok2 = tok2->linkAt(2)->next(); - if (Token::findmatch(tok2, "%varid%", tok2->link(), var->declarationId())) - return false; - tok2 = tok2->link(); - } - } - return true; -} - void CheckOther::checkRedundantAssignment() { - const bool printPerformance = mSettings->isEnabled(Settings::PERFORMANCE); - const bool printStyle = mSettings->isEnabled(Settings::STYLE); - const bool printWarning = mSettings->isEnabled(Settings::WARNING); - if (!printWarning && !printPerformance && !printStyle) + if (!mSettings->isEnabled(Settings::STYLE)) return; - - const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); - - for (const Scope &scope : symbolDatabase->scopeList) { - if (!scope.isExecutable()) + for (const Scope *scope : symbolDatabase->functionScopes) { + if (!scope->bodyStart) continue; - - std::map> usedByLambda; // map key: lambda function varId. set of varIds used by lambda. - std::map varAssignments; - std::map memAssignments; - std::map > membervars; - std::set initialized; - const Token* writtenArgumentsEnd = nullptr; - - for (const Token* tok = scope.bodyStart->next(); tok && tok != scope.bodyEnd; tok = tok->next()) { - if (tok == writtenArgumentsEnd) - writtenArgumentsEnd = nullptr; - - if (tok->str() == "?" && tok->astOperand2()) { - tok = Token::findmatch(tok->astOperand2(), ";|}"); - if (!tok) - break; - varAssignments.clear(); - memAssignments.clear(); - } else if (tok->str() == "{" && tok->strAt(-1) != "{" && tok->strAt(-1) != "=" && tok->strAt(-4) != "case" && tok->strAt(-3) != "default") { // conditional or non-executable inner scope: Skip it and reset status - tok = tok->link(); - varAssignments.clear(); - memAssignments.clear(); - } else if (Token::Match(tok, "for|if|while (")) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "] (")) + // todo: handle lambdas + break; + if (Token::simpleMatch(tok, "try {")) + // todo: check try blocks tok = tok->linkAt(1); - } else if (Token::Match(tok, "break|return|continue|throw|goto|asm")) { - varAssignments.clear(); - memAssignments.clear(); - } else if (Token::Match(tok, "%var% = [ & ] (")) { - const unsigned int lambdaId = tok->varId(); - const Token *lambdaParams = tok->tokAt(5); - if (Token::simpleMatch(lambdaParams->link(), ") {")) { - const Token *lambdaBodyStart = lambdaParams->link()->next(); - const Token * const lambdaBodyEnd = lambdaBodyStart->link(); - for (const Token *tok2 = lambdaBodyStart; tok2 != lambdaBodyEnd; tok2 = tok2->next()) { - if (tok2->varId()) - usedByLambda[lambdaId].insert(tok2->varId()); - } - } - } else if (tok->tokType() == Token::eVariable && !Token::Match(tok, "%name% (")) { - const Token *eq = nullptr; - for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "[([]")) { - // bail out if there is a variable in rhs - we only track 1 variable - bool bailout = false; - for (const Token *tok3 = tok2->link(); tok3 != tok2; tok3 = tok3->previous()) { - if (tok3->varId()) { - const Variable *var = tok3->variable(); - if (!var || !var->isConst() || var->isReference() || var->isPointer()) { - bailout = true; - break; - } - } - } - if (bailout) - break; - tok2 = tok2->link(); - } else if (Token::Match(tok2, "[)];,]")) - break; - else if (tok2->str() == "=") { - eq = tok2; - break; - } + if ((tok->isAssignmentOp() || Token::Match(tok, "++|--")) && tok->astOperand1()) { + if (tok->astParent()) + continue; + + // Do not warn about redundant initialization when rhs is trivial + // TODO : do not simplify the variable declarations + if (Token::Match(tok->tokAt(-3), "%var% ; %var% =") && tok->previous()->variable() && tok->previous()->variable()->nameToken() == tok->tokAt(-3) && tok->tokAt(-3)->linenr() == tok->previous()->linenr()) { + bool trivial = true; + visitAstNodes(tok->astOperand2(), + [&](const Token *rhs) { + if (Token::Match(rhs, "%str%|%num%|%name%")) + return ChildrenToVisit::op1_and_op2; + if (rhs->str() == "(" && !rhs->previous()->isName()) + return ChildrenToVisit::op1_and_op2; + trivial = false; + return ChildrenToVisit::done; + }); + if (trivial) + continue; } - // Set initialization flag - if (!Token::Match(tok, "%var% [")) - initialized.insert(tok->varId()); - else { - const Token *tok2 = tok->next(); - while (tok2 && tok2->str() == "[") - tok2 = tok2->link()->next(); - if (tok2 && tok2->str() != ";") - initialized.insert(tok->varId()); - } - - const Token *startToken = tok; - while (Token::Match(startToken, "%name%|::|.")) { - startToken = startToken->previous(); - if (Token::Match(startToken, "%name% . %var%")) - membervars[startToken->varId()].insert(startToken->tokAt(2)->varId()); - } - - const std::map::iterator it = varAssignments.find(tok->varId()); - if (eq && Token::Match(startToken, "[;{}]")) { // Assignment - if (it != varAssignments.end()) { - const Token *oldeq = nullptr; - for (const Token *tok2 = it->second; tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "[([]")) - tok2 = tok2->link(); - else if (Token::Match(tok2, "[)];,]")) - break; - else if (Token::Match(tok2, "++|--|=")) { - oldeq = tok2; - break; - } - } - if (!oldeq) { - const Token *tok2 = it->second; - while (Token::Match(tok2, "%name%|.|[|*|(")) - tok2 = tok2->astParent(); - if (Token::Match(tok2, "++|--")) - oldeq = tok2; - } + // Do not warn about assignment with 0 / NULL + if (Token::Match(tok->astOperand2(), "0|NULL|nullptr")) + continue; - // Ensure that LHS in assignments are the same - bool error = oldeq && eq->astOperand1() && isSameExpression(mTokenizer->isCPP(), true, eq->astOperand1(), oldeq->astOperand1(), mSettings->library, true, false); + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) + // todo: check references + continue; - // Ensure that variable is not used on right side - std::stack tokens; - tokens.push(eq->astOperand2()); - while (!tokens.empty()) { - const Token *rhs = tokens.top(); - tokens.pop(); - if (!rhs) - continue; - tokens.push(rhs->astOperand1()); - tokens.push(rhs->astOperand2()); - if (rhs->varId() == tok->varId()) { - error = false; - break; - } - if (Token::Match(rhs->previous(), "%name% (") && nonLocalVolatile(tok->variable())) { // Called function might use the variable - const Function* const func = rhs->function(); - const Variable* const var = tok->variable(); - if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && rhs->strAt(-1) != ".")) {// Global variable, or member function - error = false; - break; - } - } - } + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) + // todo: check static variables + continue; - if (error) { - if (printWarning && scope.type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok)) - redundantAssignmentInSwitchError(it->second, tok, eq->astOperand1()->expressionString()); - else if (printStyle) { - // c++, unknown type => assignment might have additional side effects - const bool possibleSideEffects(mTokenizer->isCPP() && !tok->valueType()); - - // TODO nonlocal variables are not tracked entirely. - const bool nonlocal = it->second->variable() && nonLocalVolatile(it->second->variable()); - - // Warnings are inconclusive if there are possible side effects or if variable is not - // tracked perfectly. - const bool inconclusive = possibleSideEffects | nonlocal; - - if (printInconclusive || !inconclusive) - if (mTokenizer->isC() || checkExceptionHandling(tok)) // see #6555 to see how exception handling might have an impact - redundantAssignmentError(it->second, tok, eq->astOperand1()->expressionString(), inconclusive); - } - } - it->second = tok; - } - if (!Token::simpleMatch(tok->tokAt(2), "0 ;") || (tok->variable() && tok->variable()->nameToken() != tok->tokAt(-2))) - varAssignments[tok->varId()] = tok; - memAssignments.erase(tok->varId()); - eraseMemberAssignments(tok->varId(), membervars, varAssignments); - } else if ((tok->next() && tok->next()->tokType() == Token::eIncDecOp) || (tok->previous()->tokType() == Token::eIncDecOp && tok->strAt(1) == ";")) { // Variable incremented/decremented; Prefix-Increment is only suspicious, if its return value is unused - varAssignments[tok->varId()] = tok; - memAssignments.erase(tok->varId()); - eraseMemberAssignments(tok->varId(), membervars, varAssignments); - } else if (!Token::simpleMatch(tok->tokAt(-2), "sizeof (")) { // Other usage of variable - if (it != varAssignments.end()) - varAssignments.erase(it); - if (!writtenArgumentsEnd) // Indicates that we are in the first argument of strcpy/memcpy/... function - memAssignments.erase(tok->varId()); - } - } else if (Token::Match(tok, "%name% (") && !mSettings->library.isFunctionConst(tok->str(), true)) { // Function call. Global variables might be used. Reset their status - const bool memfunc = Token::Match(tok, "memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy|swprintf|wcscat|wcsncat"); - if (tok->varId()) { - // operator(), function pointer - varAssignments.erase(tok->varId()); - - // lambda.. - std::map>::const_iterator lambda = usedByLambda.find(tok->varId()); - if (lambda != usedByLambda.end()) { - for (unsigned int varId : lambda->second) { - varAssignments.erase(varId); + // If there is a custom assignment operator => this is inconclusive + bool inconclusive = false; + if (mTokenizer->isCPP() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->typeScope) { + const std::string op = "operator" + tok->str(); + for (const Function &f : tok->astOperand1()->valueType()->typeScope->functionList) { + if (f.name() == op) { + inconclusive = true; + break; } } } + if (inconclusive && !mSettings->inconclusive) + continue; - if (memfunc && tok->strAt(-1) != "(" && tok->strAt(-1) != "=") { - const Token* param1 = tok->tokAt(2); - writtenArgumentsEnd = param1->next(); - if (param1->varId() && param1->strAt(1) == "," && !Token::Match(tok, "strcat|strncat|wcscat|wcsncat") && param1->variable() && param1->variable()->isLocal() && param1->variable()->isArray()) { - if (tok->str() == "memset" && initialized.find(param1->varId()) == initialized.end()) - initialized.insert(param1->varId()); - else { - const std::map::const_iterator it = memAssignments.find(param1->varId()); - if (it == memAssignments.end()) - memAssignments[param1->varId()] = tok; - else { - bool read = false; - for (const Token *tok2 = tok->linkAt(1); tok2 != writtenArgumentsEnd; tok2 = tok2->previous()) { - if (tok2->varId() == param1->varId()) { - // TODO: is this a read? maybe it's a write - read = true; - break; - } - } - if (read) { - memAssignments[param1->varId()] = tok; - continue; - } + FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library); + if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) + continue; - if (printWarning && scope.type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok)) - redundantCopyInSwitchError(it->second, tok, param1->str()); - else if (printPerformance) - redundantCopyError(it->second, tok, param1->str()); - } - } - } - } else if (scope.type == Scope::eSwitch) { // Avoid false positives if noreturn function is called in switch - const Function* const func = tok->function(); - if (!func || !func->hasBody()) { - varAssignments.clear(); - memAssignments.clear(); - continue; - } - const Token* funcEnd = func->functionScope->bodyEnd; - bool noreturn; - if (!mTokenizer->IsScopeNoReturn(funcEnd, &noreturn) && !noreturn) { - eraseNotLocalArg(varAssignments, symbolDatabase); - eraseNotLocalArg(memAssignments, symbolDatabase); - } else { - varAssignments.clear(); - memAssignments.clear(); + // Is there a redundant assignment? + const Token *start; + if (tok->isAssignmentOp()) + start = tok->next(); + else + start = tok->findExpressionStartEndTokens().second->next(); + + // Get next assignment.. + const Token *nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); + + if (!nextAssign) + continue; + + if (nextAssign == tok) + continue; + + // there is redundant assignment. Is there a case between the assignments? + bool hasCase = false; + for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { + if (tok2->str() == "case") { + hasCase = true; + break; } - } else { // Noreturn functions outside switch don't cause problems - eraseNotLocalArg(varAssignments, symbolDatabase); - eraseNotLocalArg(memAssignments, symbolDatabase); } + + // warn + if (hasCase) + redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); + else + redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); } } } @@ -1015,6 +807,8 @@ else if (Token::Match(tok, "break|continue ;")) secondBreak = tok->tokAt(2); else if (Token::Match(tok, "[;{}:] return|throw")) { + if (Token::simpleMatch(tok->astParent(), "?")) + continue; tok = tok->next(); // tok should point to return or throw for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "{") @@ -1609,10 +1403,9 @@ tok = tok->link(); // C++11 struct/array/etc initialization in initializer list - else if (Token::Match(tok->previous(), "%name%|] {") && !Token::findsimplematch(tok,";",tok->link())) + else if (Token::Match(tok->previous(), "%var%|] {")) tok = tok->link(); - if (!Token::Match(tok, "[;{}] %str%|%num%")) continue; @@ -1716,12 +1509,15 @@ void CheckOther::checkNanInArithmeticExpression() { + if (!mSettings->isEnabled(Settings::STYLE)) + return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { - if (Token::Match(tok, "inf.0 +|-") || - Token::Match(tok, "+|- inf.0") || - Token::Match(tok, "+|- %num% / 0.0")) { + if (tok->str() != "/") + continue; + if (!Token::Match(tok->astParent(), "[+-]")) + continue; + if (Token::simpleMatch(tok->astOperand2(), "0.0")) nanInArithmeticExpressionError(tok); - } } } @@ -1769,6 +1565,19 @@ "Instance of '$symbol' object is destroyed immediately.", CWE563, false); } +static const Token * getSingleExpressionInBlock(const Token * tok) +{ + if (!tok) + return nullptr; + const Token * top = tok->astTop(); + if (!top) + return nullptr; + const Token * nextExpression = nextAfterAstRightmostLeaf(top); + if (!Token::simpleMatch(nextExpression, "; }")) + return nullptr; + return top; +} + //----------------------------------------------------------------------------- // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } @@ -1815,18 +1624,33 @@ // save else branch code const std::string branch2 = scope.bodyEnd->tokAt(3)->stringifyList(scope.bodyEnd->linkAt(2)); + ErrorPath errorPath; // check for duplicates - if (branch1 == branch2) - duplicateBranchError(scope.classDef, scope.bodyEnd->next()); + if (branch1 == branch2) { + duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); + continue; + } + + // check for duplicates using isSameExpression + const Token * branchTop1 = getSingleExpressionInBlock(scope.bodyStart->next()); + const Token * branchTop2 = getSingleExpressionInBlock(scope.bodyEnd->tokAt(3)); + if (!branchTop1 || !branchTop2) + continue; + if (branchTop1->str() != branchTop2->str()) + continue; + if (isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand1(), branchTop2->astOperand1(), mSettings->library, true, true, &errorPath) && + isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand2(), branchTop2->astOperand2(), mSettings->library, true, true, &errorPath)) + duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); } } } -void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) +void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) { - const std::list toks = { tok2, tok1 }; + errors.emplace_back(tok2, ""); + errors.emplace_back(tok1, ""); - reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" + reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" "Finding the same code in an 'if' and related 'else' branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct.", CWE398, true); @@ -1984,11 +1808,9 @@ Token::Match(tok->astOperand2()->previous(), "%name% (") ) && tok->next()->tokType() != Token::eType && - tok->next()->tokType() != Token::eName && isSameExpression(mTokenizer->isCPP(), true, tok->next(), nextAssign->next(), mSettings->library, true, false) && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), nextAssign->astOperand2(), mSettings->library, true, false) && - tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString() && - !isUniqueExpression(tok->astOperand2())) { + tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) { bool assigned = false; const Scope * varScope = var1->scope() ? var1->scope() : &scope; for (const Token *assignTok = Token::findsimplematch(var2, ";"); assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) { @@ -1999,8 +1821,10 @@ assigned = true; } } - if (!assigned) - duplicateAssignExpressionError(var1, var2); + if (!assigned && !isUniqueExpression(tok->astOperand2())) + duplicateAssignExpressionError(var1, var2, false); + else if (mSettings->inconclusive) + duplicateAssignExpressionError(var1, var2, true); } } } @@ -2032,19 +1856,16 @@ isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { oppositeExpressionError(tok, errorPath); } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative - if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true, false, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2())) + if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2())) duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath); - else if (tok->astOperand2()) { + else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { - if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, false, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1())) - // TODO: warn if variables are unchanged. See #5683 + if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath) && + isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()) && + isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2())) // Probably the message should be changed to 'duplicate expressions X in condition or something like that'. - ;//duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath); - else if (styleEnabled && isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand2(), tok->astOperand2(), mSettings->library, true, false, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2())) - duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok, errorPath); - if (!isConstExpression(ast1->astOperand2(), mSettings->library, true)) - break; + duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath); ast1 = ast1->astOperand1(); } } @@ -2098,15 +1919,18 @@ "determine if it is correct.", CWE398, false); } -void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2) +void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) { const std::list toks = { tok2, tok1 }; + const std::string& var1 = tok1 ? tok1->str() : "x"; + const std::string& var2 = tok2 ? tok2->str() : "x"; + reportError(toks, Severity::style, "duplicateAssignExpression", - "Same expression used in consecutive assignments of '" + tok1->str() + "' and '" + tok2->str() + "'.\n" - "Finding variables '" + tok1->str() + "' and '" + tok2->str() + "' that are assigned the same expression " + "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n" + "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression " "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " - "determine if it is correct.", CWE398, false); + "determine if it is correct.", CWE398, inconclusive); } void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) @@ -2701,23 +2525,16 @@ // Is expression used? bool foundError = false; - std::stack tokens; - tokens.push((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2()); - while (!tokens.empty() && !foundError) { - const Token * const tok3 = tokens.top(); - tokens.pop(); - if (!tok3) - continue; + visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), + [&](const Token *tok3) { if (tok3->str() == "&" && !tok3->astOperand2()) - continue; // don't handle address-of for now + return ChildrenToVisit::none; // don't handle address-of for now if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) - continue; // don't care about sizeof usage - tokens.push(tok3->astOperand1()); - tokens.push(tok3->astOperand2()); - if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok3, mSettings->library, true, false)) { + return ChildrenToVisit::none; // don't care about sizeof usage + if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok3, mSettings->library, true, false)) foundError = true; - } - } + return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); if (foundError) { unknownEvaluationOrder(parent); @@ -2738,6 +2555,7 @@ { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->isEnabled(Settings::WARNING)) return; + CheckUninitVar checkUninitVar(mTokenizer, mSettings, mErrorLogger); const bool reportInconclusive = mSettings->inconclusive; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -2763,7 +2581,7 @@ inconclusive = true; } else { const bool isVariableChanged = isVariableChangedByFunctionCall(tok, mSettings, &inconclusive); - accessOfMoved = !isVariableChanged; + accessOfMoved = !isVariableChanged && checkUninitVar.isVariableUsage(tok, false, CheckUninitVar::NO_ALLOC); if (inconclusive) { accessOfMoved = !isMovedParameterAllowedForInconclusiveFunction(tok); if (accessOfMoved) @@ -2937,3 +2755,49 @@ reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, false); } +static const Token *findShadowed(const Scope *scope, const std::string &varname, int linenr) +{ + if (!scope) + return nullptr; + for (const Variable &var : scope->varlist) { + if (scope->isExecutable() && var.nameToken()->linenr() > linenr) + continue; + if (var.name() == varname) + return var.nameToken(); + } + for (const Function &f : scope->functionList) { + if (f.name() == varname) + return f.tokenDef; + } + return findShadowed(scope->nestedIn, varname, linenr); +} + +void CheckOther::checkShadowVariables() +{ + if (!mSettings->isEnabled(Settings::STYLE)) + return; + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope & scope : symbolDatabase->scopeList) { + if (!scope.isExecutable()) + continue; + for (const Variable &var : scope.varlist) { + const Token *shadowed = findShadowed(scope.nestedIn, var.name(), var.nameToken()->linenr()); + if (!shadowed) + continue; + if (scope.type == Scope::eFunction && scope.className == var.name()) + continue; + shadowError(var.nameToken(), shadowed, shadowed->varId() != 0); + } + } +} + +void CheckOther::shadowError(const Token *var, const Token *shadowed, bool shadowVar) +{ + ErrorPath errorPath; + errorPath.push_back(ErrorPathItem(shadowed, "Shadowed declaration")); + errorPath.push_back(ErrorPathItem(var, "Shadow variable")); + const std::string &varname = var ? var->str() : (shadowVar ? "var" : "f"); + const char *id = shadowVar ? "shadowVar" : "shadowFunction"; + std::string message = "$symbol:" + varname + "\nLocal variable $symbol shadows outer " + (shadowVar ? "variable" : "function"); + reportError(errorPath, Severity::style, id, message, CWE398, false); +} diff -Nru cppcheck-1.85/lib/checkother.h cppcheck-1.86/lib/checkother.h --- cppcheck-1.85/lib/checkother.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkother.h 2018-12-08 07:18:21.000000000 +0000 @@ -81,6 +81,7 @@ checkOther.checkUnusedLabel(); checkOther.checkEvaluationOrder(); checkOther.checkFuncArgNamesDifferent(); + checkOther.checkShadowVariables(); } /** @brief Run checks against the simplified token list */ @@ -211,7 +212,11 @@ /** @brief %Check if function declaration and definition argument names different */ void checkFuncArgNamesDifferent(); + /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ + void checkShadowVariables(); + private: + // Error messages.. void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); @@ -237,8 +242,8 @@ void suspiciousEqualityComparisonError(const Token* tok); void selfAssignmentError(const Token *tok, const std::string &varname); void misusedScopeObjectError(const Token *tok, const std::string &varname); - void duplicateBranchError(const Token *tok1, const Token *tok2); - void duplicateAssignExpressionError(const Token *tok1, const Token *tok2); + void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); + void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); void oppositeExpressionError(const Token *opTok, ErrorPath errors); void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors); void duplicateValueTernaryError(const Token *tok); @@ -263,6 +268,7 @@ void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void funcArgNamesDifferent(const std::string & functionName, size_t index, const Token* declaration, const Token* definition); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); + void shadowError(const Token *var, const Token *shadowed, bool shadowVar); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { CheckOther c(nullptr, settings, errorLogger); @@ -301,7 +307,8 @@ c.selfAssignmentError(nullptr, "varname"); c.clarifyCalculationError(nullptr, "+"); c.clarifyStatementError(nullptr); - c.duplicateBranchError(nullptr, nullptr); + c.duplicateBranchError(nullptr, nullptr, errorPath); + c.duplicateAssignExpressionError(nullptr, nullptr, true); c.oppositeExpressionError(nullptr, errorPath); c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath); c.duplicateValueTernaryError(nullptr); @@ -324,6 +331,8 @@ c.accessMovedError(nullptr, "v", nullptr, false); c.funcArgNamesDifferent("function", 1, nullptr, nullptr); c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); + c.shadowError(nullptr, nullptr, false); + c.shadowError(nullptr, nullptr, true); const std::vector nullvec; c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); @@ -385,7 +394,8 @@ "- redundant pointer operation on pointer like &\\*some_ptr.\n" "- find unused 'goto' labels.\n" "- function declaration and definition argument names different.\n" - "- function declaration and definition argument order different.\n"; + "- function declaration and definition argument order different.\n" + "- shadow variable.\n"; } }; /// @} diff -Nru cppcheck-1.85/lib/checkstl.cpp cppcheck-1.86/lib/checkstl.cpp --- cppcheck-1.85/lib/checkstl.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkstl.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -110,18 +111,15 @@ errmsg = "Out of bounds access of item in container '$symbol'"; else if (containerSize->intvalue == 0) { if (containerSize->condition) - errmsg = "Accessing an item in container '$symbol'. " + ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or '$symbol' can be empty."; + errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or $symbol is accessed out of bounds when $symbol is empty."; else - errmsg = "Accessing an item in container '$symbol' that is empty."; + errmsg = "Out of bounds access in $symbol because $symbol is empty."; } else if (index) { - if (containerSize->condition || index->condition) - errmsg = "Possible access out of bounds"; - else - errmsg = "Access out of bounds"; - - errmsg += " of container '$symbol'; size=" + - MathLib::toString(containerSize->intvalue) + ", index=" + - MathLib::toString(index->intvalue); + errmsg = "Accessing $symbol[" + MathLib::toString(index->intvalue) + "] is out of bounds when $symbol size is " + MathLib::toString(containerSize->intvalue) + "."; + if (containerSize->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or $symbol size can be " + MathLib::toString(containerSize->intvalue) + ". " + errmsg; + else if (index->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(index->condition) + " or $symbol item " + MathLib::toString(index->intvalue) + " can be accessed. " + errmsg; } else { // should not happen return; @@ -151,6 +149,81 @@ (containerSize && containerSize->isInconclusive()) || (index && index->isInconclusive())); } +bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) const +{ + if (!Token::simpleMatch(expr, "( )")) + return false; + if (!Token::Match(expr->astOperand1(), ". %name% (")) + return false; + if (!isSameExpression(mTokenizer->isCPP(), false, containerToken, expr->astOperand1()->astOperand1(), mSettings->library, false, false)) + return false; + return containerToken->valueType()->container->getYield(expr->previous()->str()) == Library::Container::Yield::SIZE; +} + +bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr) const +{ + if (!expr) + return false; + if (isContainerSize(containerToken, expr)) + return true; + if (expr->str() == "*") { + const Token *mul; + if (isContainerSize(containerToken, expr->astOperand1())) + mul = expr->astOperand2(); + else if (isContainerSize(containerToken, expr->astOperand2())) + mul = expr->astOperand1(); + else + return false; + return mul && (!mul->hasKnownIntValue() || mul->values().front().intvalue != 0); + } + if (expr->str() == "+") { + const Token *op; + if (isContainerSize(containerToken, expr->astOperand1())) + op = expr->astOperand2(); + else if (isContainerSize(containerToken, expr->astOperand2())) + op = expr->astOperand1(); + else + return false; + return op && op->getValueGE(0, mSettings); + } + return false; +} + +void CheckStl::outOfBoundsIndexExpression() +{ + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + if (!tok->isName() || !tok->valueType()) + continue; + const Library::Container *container = tok->valueType()->container; + if (!container) + continue; + if (!container->arrayLike_indexOp && !container->stdStringLike) + continue; + if (!Token::Match(tok, "%name% [")) + continue; + if (isContainerSizeGE(tok, tok->next()->astOperand2())) + outOfBoundsIndexExpressionError(tok, tok->next()->astOperand2()); + } + } +} + +void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) +{ + const std::string varname = tok ? tok->str() : std::string("var"); + const std::string i = index ? index->expressionString() : std::string(varname + ".size()"); + + std::string errmsg = "Out of bounds access of $symbol, index '" + i + "' is out of bounds."; + + reportError(tok, + Severity::error, + "containerOutOfBoundsIndexExpression", + "$symbol:" + varname +"\n" + errmsg, + CWE398, + false); +} + + // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) @@ -158,12 +231,46 @@ reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, false); } -void CheckStl::iteratorsError(const Token *tok, const std::string &container1, const std::string &container2) +void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) +{ + reportError(tok, Severity::error, "iterators1", + "$symbol:" + containerName1 + "\n" + "$symbol:" + containerName2 + "\n" + "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); +} + +void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) +{ + std::list callstack = { tok, containerTok }; + reportError(callstack, Severity::error, "iterators2", + "$symbol:" + containerName1 + "\n" + "$symbol:" + containerName2 + "\n" + "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); +} + +void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) { - reportError(tok, Severity::error, "iterators", - "$symbol:" + container1 + "\n" - "$symbol:" + container2 + "\n" - "Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", CWE664, false); + std::list callstack = { tok, containerTok }; + reportError(callstack, Severity::error, "iterators3", + "$symbol:" + containerName + "\n" + "Same iterator is used with containers '" + containerName + "' that are defined in different scopes.", CWE664, false); +} + +void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2) +{ + std::list callstack = { cmpOperatorTok, containerTok1, containerTok2 }; + reportError(callstack, Severity::error, "iteratorsCmp1", + "$symbol:" + containerName1 + "\n" + "$symbol:" + containerName2 + "\n" + "Comparison of iterators from containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); +} + +void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName) +{ + std::list callstack = { cmpOperatorTok, containerTok1, containerTok2 }; + reportError(callstack, Severity::error, "iteratorsCmp2", + "$symbol:" + containerName + "\n" + "Comparison of iterators from containers '" + containerName + "' that are defined in different scopes.", CWE664, false); } // Error message used when dereferencing an iterator that has been erased.. @@ -229,10 +336,46 @@ return ret; } +enum OperandPosition { + Left, + Right +}; + +static const Token* findIteratorContainer(const Token* start, const Token* end, unsigned int id) +{ + const Token* containerToken = nullptr; + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (Token::Match(tok, "%varid% = %name% . %name% (", id)) { + // Iterator is assigned to value + if (tok->tokAt(5)->valueType() && tok->tokAt(5)->valueType()->type == ValueType::Type::ITERATOR) { + containerToken = tok->tokAt(2); + } + } else if (Token::Match(tok, "%varid% = %name% (", id)) { + // Prevent FP: iterator is assigned to something + // TODO: Fix it in future + containerToken = nullptr; + } + } + return containerToken; +} + void CheckStl::iterators() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + // Filling map of iterators id and their scope begin + std::map iteratorScopeBeginInfo; + for (const Variable* var : symbolDatabase->variableList()) { + bool inconclusiveType=false; + if (!isIterator(var, inconclusiveType)) + continue; + const unsigned int iteratorId = var->declarationId(); + if (iteratorId != 0) + iteratorScopeBeginInfo[iteratorId] = var->nameToken(); + } + // Storage to save found comparison problems to avoid duplicate error messages + std::set foundOperatorErrors; + for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) @@ -269,15 +412,12 @@ invalidationScope = nullptr; } - // Is iterator compared against different container? - if (tok2->isComparisonOp() && containerToken && tok2->astOperand1() && tok2->astOperand2()) { - const Token *other = nullptr; - if (tok2->astOperand1()->varId() == iteratorId) - other = tok2->astOperand2()->tokAt(-3); - else if (tok2->astOperand2()->varId() == iteratorId) - other = tok2->astOperand1()->tokAt(-3); - if (Token::Match(other, "%name% . end|rend|cend|crend ( )") && other->varId() != containerToken->varId()) - iteratorsError(tok2, getContainerName(containerToken), getContainerName(other)); + // Is comparison expression? + // Check whether iterator compared against different container or iterator of different container? + if (tok2->isComparisonOp() && tok2->astOperand1() && tok2->astOperand2() && + (foundOperatorErrors.find(tok2) == foundOperatorErrors.end()) && + compareIteratorAgainstDifferentContainer(tok2, containerToken, iteratorId, iteratorScopeBeginInfo)) { + foundOperatorErrors.insert(tok2); } // Is the iterator used in a insert/erase operation? @@ -397,6 +537,60 @@ } } +bool CheckStl::compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const unsigned int iteratorId, const std::map& iteratorScopeBeginInfo) +{ + if (!containerTok) + return false; + + const Token *otherOperand = nullptr; + OperandPosition operandPosition; + if (operatorTok->astOperand1()->varId() == iteratorId) { + otherOperand = operatorTok->astOperand2(); + operandPosition = OperandPosition::Right; + } else if (operatorTok->astOperand2()->varId() == iteratorId) { + otherOperand = operatorTok->astOperand1(); + operandPosition = OperandPosition::Left; + } + + if (!otherOperand) + return false; + + const Token * const otherExprPart = otherOperand->tokAt(-3); + if (Token::Match(otherExprPart, "%name% . end|rend|cend|crend ( )") && otherExprPart->varId() != containerTok->varId()) { + const std::string& firstContainerName = getContainerName(containerTok); + const std::string& secondContainerName = getContainerName(otherExprPart); + if (firstContainerName != secondContainerName) { + if (operandPosition == OperandPosition::Right) + iteratorsError(operatorTok, containerTok, firstContainerName, secondContainerName); + else + iteratorsError(operatorTok, containerTok, secondContainerName, firstContainerName); + } else { + iteratorsError(operatorTok, containerTok, firstContainerName); + } + return true; + } else { + const unsigned int otherId = otherOperand->varId(); + auto it = iteratorScopeBeginInfo.find(otherId); + if (it != iteratorScopeBeginInfo.end()) { + const Token* otherContainerToken = findIteratorContainer(it->second, operatorTok->astOperand1(), otherId); + if (otherContainerToken && otherContainerToken->varId() != containerTok->varId()) { + const std::string& firstContainerName = getContainerName(containerTok); + const std::string& secondContainerName = getContainerName(otherContainerToken); + if (firstContainerName != secondContainerName) { + if (operandPosition == OperandPosition::Right) + iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName, secondContainerName); + else + iteratorsCmpError(operatorTok, containerTok, otherContainerToken, secondContainerName, firstContainerName); + } else { + iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName); + } + return true; + } + } + } + + return false; +} // Error message for bad iterator usage.. void CheckStl::mismatchingContainersError(const Token *tok) @@ -572,50 +766,66 @@ if ((scope.type != Scope::eFor && scope.type != Scope::eWhile && scope.type != Scope::eIf && scope.type != Scope::eDo) || !tok) continue; - if (scope.type == Scope::eFor) - tok = Token::findsimplematch(tok->tokAt(2), ";"); - else if (scope.type == Scope::eDo) { - tok = tok->linkAt(1)->tokAt(2); - } else - tok = tok->next(); - - if (!tok) - continue; - tok = tok->next(); + const Token *condition = nullptr; + if (scope.type == Scope::eFor) { + if (Token::simpleMatch(tok->next()->astOperand2(), ";") && Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) + condition = tok->next()->astOperand2()->astOperand2()->astOperand1(); + } else if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok->linkAt(1), "} while (")) + condition = tok->linkAt(1)->tokAt(2)->astOperand2(); + else + condition = tok->next()->astOperand2(); - // check if the for loop condition is wrong - if (!Token::Match(tok, "%var% <= %var% . %name% ( ) ;|)|%oror%")) - continue; - // Is it a vector? - const Variable *var = tok->tokAt(2)->variable(); - if (!var) + if (!condition) continue; - const Library::Container* container = mSettings->library.detectContainer(var->typeStartToken()); - if (!container) - continue; + std::vector conds; - if (container->getYield(tok->strAt(4)) != Library::Container::SIZE) - continue; + visitAstNodes(condition, + [&](const Token *cond) { + if (Token::Match(cond, "%oror%|&&")) + return ChildrenToVisit::op1_and_op2; + if (cond->isComparisonOp()) + conds.emplace_back(cond); + return ChildrenToVisit::none; + }); + + for (const Token *cond : conds) { + const Token *vartok; + const Token *containerToken; + if (Token::Match(cond, "<= %var% . %name% ( )") && Token::Match(cond->astOperand1(), "%var%")) { + vartok = cond->astOperand1(); + containerToken = cond->next(); + } else { + continue; + } - // variable id for loop variable. - const unsigned int numId = tok->varId(); + // Is it a array like container? + const Library::Container* container = containerToken->valueType() ? containerToken->valueType()->container : nullptr; + if (!container) + continue; + if (container->getYield(containerToken->strAt(2)) != Library::Container::SIZE) + continue; - // variable id for the container variable - const unsigned int declarationId = var->declarationId(); + // variable id for loop variable. + const unsigned int numId = vartok->varId(); - for (const Token *tok3 = scope.bodyStart; tok3 && tok3 != scope.bodyEnd; tok3 = tok3->next()) { - if (tok3->varId() == declarationId) { - tok3 = tok3->next(); - if (Token::Match(tok3, ". %name% ( )")) { - if (container->getYield(tok3->strAt(1)) == Library::Container::SIZE) - break; - } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) - stlOutOfBoundsError(tok3, tok3->strAt(1), var->name(), false); - else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { - const Library::Container::Yield yield = container->getYield(tok3->strAt(1)); - if (yield == Library::Container::AT_INDEX) - stlOutOfBoundsError(tok3, tok3->strAt(3), var->name(), true); + // variable id for the container variable + const unsigned int declarationId = containerToken->varId(); + const std::string &containerName = containerToken->str(); + + for (const Token *tok3 = scope.bodyStart; tok3 && tok3 != scope.bodyEnd; tok3 = tok3->next()) { + if (tok3->varId() == declarationId) { + tok3 = tok3->next(); + if (Token::Match(tok3, ". %name% ( )")) { + if (container->getYield(tok3->strAt(1)) == Library::Container::SIZE) + break; + } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) + stlOutOfBoundsError(tok3, tok3->strAt(1), containerName, false); + else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { + const Library::Container::Yield yield = container->getYield(tok3->strAt(1)); + if (yield == Library::Container::AT_INDEX) + stlOutOfBoundsError(tok3, tok3->strAt(3), containerName, true); + } } } } diff -Nru cppcheck-1.85/lib/checkstl.h cppcheck-1.86/lib/checkstl.h --- cppcheck-1.85/lib/checkstl.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkstl.h 2018-12-08 07:18:21.000000000 +0000 @@ -61,6 +61,7 @@ CheckStl checkStl(tokenizer, settings, errorLogger); checkStl.outOfBounds(); + checkStl.outOfBoundsIndexExpression(); } /** Simplified checks. The token list is simplified. */ @@ -94,6 +95,9 @@ /** Accessing container out of bounds using ValueFlow */ void outOfBounds(); + /** Accessing container out of bounds, following index expression */ + void outOfBoundsIndexExpression(); + /** * Finds errors like this: * for (unsigned ii = 0; ii <= foo.size(); ++ii) @@ -185,6 +189,9 @@ void useStlAlgorithm(); private: + bool isContainerSize(const Token *container, const Token *expr) const; + bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; + void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); void string_c_strThrowError(const Token* tok); void string_c_strError(const Token* tok); @@ -192,10 +199,15 @@ void string_c_strParam(const Token* tok, unsigned int number); void outOfBoundsError(const Token *tok, const ValueFlow::Value *containerSize, const ValueFlow::Value *index); + void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); void negativeIndexError(const Token* tok, const ValueFlow::Value& index); void invalidIteratorError(const Token* tok, const std::string& iteratorName); - void iteratorsError(const Token* tok, const std::string& container1, const std::string& container2); + void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); + void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2); + void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName); void mismatchingContainersError(const Token* tok); void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); void sameIteratorExpressionError(const Token *tok); @@ -223,11 +235,17 @@ void useStlAlgorithmError(const Token *tok, const std::string &algoName); + bool compareIteratorAgainstDifferentContainer(const Token* tok, const Token* containerToken, const unsigned int iteratorId, const std::map& iteratorScopeBeginInfo); + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { CheckStl c(nullptr, settings, errorLogger); c.outOfBoundsError(nullptr, nullptr, nullptr); c.invalidIteratorError(nullptr, "iterator"); c.iteratorsError(nullptr, "container1", "container2"); + c.iteratorsError(nullptr, nullptr, "container1", "container2"); + c.iteratorsError(nullptr, nullptr, "container"); + c.iteratorsCmpError(nullptr, nullptr, nullptr, "container1", "container2"); + c.iteratorsCmpError(nullptr, nullptr, nullptr, "container"); c.mismatchingContainersError(nullptr); c.mismatchingContainerExpressionError(nullptr, nullptr); c.sameIteratorExpressionError(nullptr); diff -Nru cppcheck-1.85/lib/checkstring.cpp cppcheck-1.86/lib/checkstring.cpp --- cppcheck-1.85/lib/checkstring.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkstring.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -169,7 +169,7 @@ const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { - if (tok->tokType() != Token::eComparisonOp) + if (!tok->isComparisonOp()) continue; const Token* varTok = tok->astOperand1(); diff -Nru cppcheck-1.85/lib/checkuninitvar.cpp cppcheck-1.86/lib/checkuninitvar.cpp --- cppcheck-1.85/lib/checkuninitvar.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkuninitvar.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -65,39 +65,39 @@ } // check every executable scope - for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { - if (scope->isExecutable()) { - checkScope(&*scope, arrayTypeDefs); + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.isExecutable()) { + checkScope(&scope, arrayTypeDefs); } } } void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) { - for (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { - if ((mTokenizer->isCPP() && i->type() && !i->isPointer() && i->type()->needInitialization != Type::True) || - i->isStatic() || i->isExtern() || i->isReference()) + for (const Variable &var : scope->varlist) { + if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::True) || + var.isStatic() || var.isExtern() || var.isReference()) continue; // don't warn for try/catch exception variable - if (i->isThrow()) + if (var.isThrow()) continue; - if (Token::Match(i->nameToken()->next(), "[({:]")) + if (Token::Match(var.nameToken()->next(), "[({:]")) continue; - if (Token::Match(i->nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not - checkRhs(i->nameToken(), *i, NO_ALLOC, 0U, emptyString); + if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not + checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString); continue; } - if (Token::Match(i->nameToken(), "%name% ) (") && Token::simpleMatch(i->nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not - checkRhs(i->nameToken()->linkAt(2)->next(), *i, NO_ALLOC, 0U, emptyString); + if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not + checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString); continue; } - if (i->isArray() || i->isPointerToArray()) { - const Token *tok = i->nameToken()->next(); - if (i->isPointerToArray()) + if (var.isArray() || var.isPointerToArray()) { + const Token *tok = var.nameToken()->next(); + if (var.isPointerToArray()) tok = tok->next(); while (Token::simpleMatch(tok->link(), "] [")) tok = tok->link()->next(); @@ -105,13 +105,13 @@ continue; } - bool stdtype = mTokenizer->isC() && arrayTypeDefs.find(i->typeStartToken()->str()) == arrayTypeDefs.end(); - const Token* tok = i->typeStartToken(); - for (; tok != i->nameToken() && tok->str() != "<"; tok = tok->next()) { + bool stdtype = mTokenizer->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end(); + const Token* tok = var.typeStartToken(); + for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) { if (tok->isStandardType() || tok->isEnumType()) stdtype = true; } - if (i->isArray() && !stdtype) + if (var.isArray() && !stdtype) continue; while (tok && tok->str() != ";") @@ -120,38 +120,37 @@ continue; if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && - checkLoopBody(tok->astParent()->link()->next(), *i, i->isArray() ? ARRAY : NO_ALLOC, emptyString, true)) + checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true)) continue; - if (i->isArray()) { + if (var.isArray()) { Alloc alloc = ARRAY; const std::map variableValue; - checkScopeForVariable(tok, *i, nullptr, nullptr, &alloc, emptyString, variableValue); + checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); continue; } - if (stdtype || i->isPointer()) { + if (stdtype || var.isPointer()) { Alloc alloc = NO_ALLOC; const std::map variableValue; - checkScopeForVariable(tok, *i, nullptr, nullptr, &alloc, emptyString, variableValue); + checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); } - if (i->type()) - checkStruct(tok, *i); + if (var.type()) + checkStruct(tok, var); } if (scope->function) { - for (unsigned int i = 0; i < scope->function->argCount(); i++) { - const Variable *arg = scope->function->getArgumentVar(i); - if (arg && arg->declarationId() && Token::Match(arg->typeStartToken(), "%type% * %name% [,)]")) { + for (const Variable &arg : scope->function->argumentList) { + if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) { // Treat the pointer as initialized until it is assigned by malloc for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { - if (Token::Match(tok, "[;{}] %varid% = %name% (", arg->declarationId()) && + if (Token::Match(tok, "[;{}] %varid% = %name% (", arg.declarationId()) && mSettings->library.returnuninitdata.count(tok->strAt(3)) == 1U) { - if (arg->typeStartToken()->strAt(-1) == "struct" || (arg->type() && arg->type()->isStructType())) - checkStruct(tok, *arg); - else if (arg->typeStartToken()->isStandardType() || arg->typeStartToken()->isEnumType()) { + if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType())) + checkStruct(tok, arg); + else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) { Alloc alloc = NO_ALLOC; const std::map variableValue; - checkScopeForVariable(tok->next(), *arg, nullptr, nullptr, &alloc, emptyString, variableValue); + checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue); } } } @@ -164,23 +163,19 @@ { const Token *typeToken = structvar.typeStartToken(); const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase(); - for (std::size_t j = 0U; j < symbolDatabase->classAndStructScopes.size(); ++j) { - const Scope *scope2 = symbolDatabase->classAndStructScopes[j]; + for (const Scope *scope2 : symbolDatabase->classAndStructScopes) { if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) { - for (std::list::const_iterator it = scope2->varlist.begin(); it != scope2->varlist.end(); ++it) { - const Variable &var = *it; - + for (const Variable &var : scope2->varlist) { if (var.isStatic() || var.hasDefault() || var.isArray() || (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::True))) continue; // is the variable declared in a inner union? bool innerunion = false; - for (auto it2 = scope2->nestedList.cbegin(); it2 != scope2->nestedList.cend(); ++it2) { - const Scope &innerScope = **it2; - if (innerScope.type == Scope::eUnion) { - if (var.typeStartToken()->linenr() >= innerScope.bodyStart->linenr() && - var.typeStartToken()->linenr() <= innerScope.bodyEnd->linenr()) { + for (const Scope *innerScope : scope2->nestedList) { + if (innerScope->type == Scope::eUnion) { + if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() && + var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) { innerunion = true; break; } @@ -230,7 +225,7 @@ } else if (tok->isComparisonOp()) { - if (tok->values().size() == 1U && tok->values().front().isKnown()) { + if (tok->hasKnownIntValue()) { if (tok->values().front().intvalue) *alwaysTrue = true; else @@ -1231,10 +1226,10 @@ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every executable scope - for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { - if (!scope->isExecutable()) + for (const Scope &scope : symbolDatabase->scopeList) { + if (!scope.isExecutable()) continue; - for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + for (const Token* tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; @@ -1256,11 +1251,11 @@ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every executable scope - for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { - if (!scope->isExecutable()) + for (const Scope &scope : symbolDatabase->scopeList) { + if (!scope.isExecutable()) continue; // Dead pointers.. - for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + for (const Token* tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (tok->variable() && tok->variable()->isPointer() && isVariableUsage(tok, true, NO_ALLOC)) { diff -Nru cppcheck-1.85/lib/checkunusedvar.cpp cppcheck-1.86/lib/checkunusedvar.cpp --- cppcheck-1.85/lib/checkunusedvar.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/checkunusedvar.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -928,8 +928,11 @@ } const Variables::VariableUsage *const var = variables.find(varid); - if (var && !var->_allocateMemory) { - variables.readAll(varid, tok); + if (var) { + if (!var->_aliases.empty()) + variables.use(varid, tok); + else if (!var->_allocateMemory) + variables.readAll(varid, tok); } } @@ -1220,10 +1223,7 @@ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check functions - const std::size_t functions = symbolDatabase->functionScopes.size(); - for (std::size_t i = 0; i < functions; ++i) { - const Scope * scope = symbolDatabase->functionScopes[i]; - + for (const Scope * scope : symbolDatabase->functionScopes) { // Bailout when there are lambdas or inline functions // TODO: Handle lambdas and inline functions properly if (scope->hasInlineOrLambdaFunction()) @@ -1311,27 +1311,27 @@ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); - for (std::list::const_iterator scope = symbolDatabase->scopeList.cbegin(); scope != symbolDatabase->scopeList.cend(); ++scope) { - if (scope->type != Scope::eStruct && scope->type != Scope::eUnion) + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eStruct && scope.type != Scope::eUnion) continue; - if (scope->bodyStart->fileIndex() != 0 || scope->className.empty()) + if (scope.bodyStart->fileIndex() != 0 || scope.className.empty()) continue; // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware. - if (scope->bodyEnd->isAttributePacked()) + if (scope.bodyEnd->isAttributePacked()) continue; // Bail out if struct/union contains any functions - if (!scope->functionList.empty()) + if (!scope.functionList.empty()) continue; // bail out if struct is inherited bool bailout = false; - for (std::list::const_iterator i = symbolDatabase->scopeList.cbegin(); i != symbolDatabase->scopeList.cend(); ++i) { - if (i->definedType) { - for (size_t j = 0; j < i->definedType->derivedFrom.size(); j++) { - if (i->definedType->derivedFrom[j].type == scope->definedType) { + for (const Scope &derivedScope : symbolDatabase->scopeList) { + if (derivedScope.definedType) { + for (const Type::BaseInfo &derivedFrom : derivedScope.definedType->derivedFrom) { + if (derivedFrom.type == scope.definedType) { bailout = true; break; } @@ -1343,7 +1343,7 @@ // bail out for extern/global struct for (const Variable* var : symbolDatabase->variableList()) { - if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope->className) { + if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) { bailout = true; break; } @@ -1352,19 +1352,19 @@ continue; // Bail out if some data is casted to struct.. - const std::string castPattern("( struct| " + scope->className + " * ) & %name% ["); - if (Token::findmatch(scope->bodyEnd, castPattern.c_str())) + const std::string castPattern("( struct| " + scope.className + " * ) & %name% ["); + if (Token::findmatch(scope.bodyEnd, castPattern.c_str())) continue; // (struct S){..} - const std::string initPattern("( struct| " + scope->className + " ) {"); - if (Token::findmatch(scope->bodyEnd, initPattern.c_str())) + const std::string initPattern("( struct| " + scope.className + " ) {"); + if (Token::findmatch(scope.bodyEnd, initPattern.c_str())) continue; // Bail out if struct is used in sizeof.. - for (const Token *tok = scope->bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { + for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { tok = tok->tokAt(2); - if (Token::Match(tok, ("struct| " + scope->className).c_str())) { + if (Token::Match(tok, ("struct| " + scope.className).c_str())) { bailout = true; break; } @@ -1373,19 +1373,19 @@ continue; // Try to prevent false positives when struct members are not used directly. - if (Token::findmatch(scope->bodyEnd, (scope->className + " %type%| *").c_str())) + if (Token::findmatch(scope.bodyEnd, (scope.className + " %type%| *").c_str())) continue; - for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { + for (const Variable &var : scope.varlist) { // declaring a POD member variable? - if (!var->typeStartToken()->isStandardType() && !var->isPointer()) + if (!var.typeStartToken()->isStandardType() && !var.isPointer()) continue; // Check if the struct member variable is used anywhere in the file - if (Token::findsimplematch(mTokenizer->tokens(), (". " + var->name()).c_str())) + if (Token::findsimplematch(mTokenizer->tokens(), (". " + var.name()).c_str())) continue; - unusedStructMemberError(var->nameToken(), scope->className, var->name(), scope->type == Scope::eUnion); + unusedStructMemberError(var.nameToken(), scope.className, var.name(), scope.type == Scope::eUnion); } } } diff -Nru cppcheck-1.85/lib/cppcheck.cpp cppcheck-1.86/lib/cppcheck.cpp --- cppcheck-1.85/lib/cppcheck.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/cppcheck.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -156,9 +156,9 @@ simplecpp::TokenList tokens1(fileStream, files, filename, &outputList); // If there is a syntax error, report it and stop - for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { + for (const simplecpp::Output &output : outputList) { bool err; - switch (it->type) { + switch (output.type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: @@ -173,13 +173,13 @@ }; if (err) { - const ErrorLogger::ErrorMessage::FileLocation loc1(it->location.file(), it->location.line); + const ErrorLogger::ErrorMessage::FileLocation loc1(output.location.file(), output.location.line); std::list callstack(1, loc1); ErrorLogger::ErrorMessage errmsg(callstack, "", Severity::error, - it->msg, + output.msg, "syntaxError", false); reportErr(errmsg); @@ -314,7 +314,7 @@ unsigned int checkCount = 0; bool hasValidConfig = false; std::list configurationError; - for (std::set::const_iterator it = configurations.begin(); it != configurations.end(); ++it) { + for (const std::string &currCfg : configurations) { // bail out if terminated if (mSettings.terminated()) break; @@ -324,7 +324,7 @@ if (!mSettings.force && ++checkCount > mSettings.maxConfigs) break; - mCurrentConfig = *it; + mCurrentConfig = currCfg; if (!mSettings.userDefines.empty()) { if (!mCurrentConfig.empty()) @@ -367,7 +367,7 @@ hasValidConfig = true; // If only errors are printed, print filename after the check - if (!mSettings.quiet && (!mCurrentConfig.empty() || it != configurations.begin())) { + if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "..."); @@ -456,9 +456,11 @@ e.id, false); - reportErr(errmsg); - if (!mSuppressInternalErrorFound) - internalErrorFound = true; + if (errmsg._severity == Severity::error || mSettings.isEnabled(errmsg._severity)) { + reportErr(errmsg); + if (!mSuppressInternalErrorFound) + internalErrorFound = true; + } } } diff -Nru cppcheck-1.85/lib/errorlogger.cpp cppcheck-1.86/lib/errorlogger.cpp --- cppcheck-1.85/lib/errorlogger.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/errorlogger.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -34,7 +34,7 @@ #include InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) : - token(tok), errorMessage(errorMsg) + token(tok), errorMessage(errorMsg), type(type) { switch (type) { case AST: @@ -43,6 +43,9 @@ case SYNTAX: id = "syntaxError"; break; + case UNKNOWN_MACRO: + id = "unknownMacro"; + break; case INTERNAL: id = "cppcheckError"; break; @@ -503,7 +506,7 @@ endl = "\r\n"; else endl = "\r"; - findAndReplace(result, "{code}", readCode(_callStack.back().getfile(), _callStack.back().line, _callStack.back().col, endl)); + findAndReplace(result, "{code}", readCode(_callStack.back().getOrigFile(), _callStack.back().line, _callStack.back().col, endl)); } } else { findAndReplace(result, "{file}", "nofile"); @@ -534,7 +537,7 @@ endl = "\r\n"; else endl = "\r"; - findAndReplace(text, "{code}", readCode(fileLocation.getfile(), fileLocation.line, fileLocation.col, endl)); + findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.col, endl)); } result += '\n' + text; } @@ -584,12 +587,12 @@ ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList) - : fileIndex(tok->fileIndex()), line(tok->linenr()), col(tok->col()), mFileName(tokenList->file(tok)) + : fileIndex(tok->fileIndex()), line(tok->linenr()), col(tok->col()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)) { } ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList) - : fileIndex(tok->fileIndex()), line(tok->linenr()), col(tok->col()), mFileName(tokenList->file(tok)), mInfo(info) + : fileIndex(tok->fileIndex()), line(tok->linenr()), col(tok->col()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(info) { } @@ -600,6 +603,13 @@ return mFileName; } +std::string ErrorLogger::ErrorMessage::FileLocation::getOrigFile(bool convert) const +{ + if (convert) + return Path::toNativeSeparators(mOrigFileName); + return mOrigFileName; +} + void ErrorLogger::ErrorMessage::FileLocation::setfile(const std::string &file) { mFileName = file; diff -Nru cppcheck-1.85/lib/errorlogger.h cppcheck-1.86/lib/errorlogger.h --- cppcheck-1.85/lib/errorlogger.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/errorlogger.h 2018-12-08 07:18:21.000000000 +0000 @@ -54,10 +54,11 @@ /** @brief Simple container to be thrown when internal error is detected. */ struct InternalError { - enum Type {AST, SYNTAX, INTERNAL}; + enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL}; InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL); const Token *token; std::string errorMessage; + Type type; std::string id; }; @@ -193,11 +194,11 @@ } FileLocation(const std::string &file, unsigned int aline) - : fileIndex(0), line(aline), col(0), mFileName(file) { + : fileIndex(0), line(aline), col(0), mOrigFileName(file), mFileName(file) { } FileLocation(const std::string &file, const std::string &info, unsigned int aline) - : fileIndex(0), line(aline), col(0), mFileName(file), mInfo(info) { + : fileIndex(0), line(aline), col(0), mOrigFileName(file), mFileName(file), mInfo(info) { } FileLocation(const Token* tok, const TokenList* tokenList); @@ -210,6 +211,8 @@ */ std::string getfile(bool convert = true) const; + std::string getOrigFile(bool convert = true) const; + /** * Set the filename. * @param file Filename to set. @@ -233,6 +236,7 @@ } private: + std::string mOrigFileName; std::string mFileName; std::string mInfo; }; diff -Nru cppcheck-1.85/lib/importproject.cpp cppcheck-1.86/lib/importproject.cpp --- cppcheck-1.85/lib/importproject.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/importproject.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -248,20 +248,16 @@ } if (F=='D') { std::string defval; - bool escape = false; - while (pos < command.size() && command[pos] != ' ') { - if (command[pos] != '\\') { - defval += command[pos]; - escape = false; + bool str = false; + while (pos < command.size() && (str || command[pos] != ' ')) { + if (command.compare(pos, 4, "\\\\\\\"") == 0) { + defval += '\"'; + str = !str; + pos += 4; } else { - if (escape) { - defval += '\\'; - escape = false; - } else { - escape = true; - } + defval += command[pos]; + pos++; } - pos++; } fs.defines += fval; if (!defval.empty()) diff -Nru cppcheck-1.85/lib/mathlib.cpp cppcheck-1.86/lib/mathlib.cpp --- cppcheck-1.85/lib/mathlib.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/mathlib.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -548,6 +548,55 @@ return ret; } +// in-place conversion of (sub)string to double. Requires no heap. +static double myStod(const std::string& str, std::string::const_iterator from, std::string::const_iterator to, int base) +{ + double result = 0.; + bool positivesign = true; + std::string::const_iterator it; + if ('+' == *from) { + it = from + 1; + } else if ('-' == *from) { + it = from + 1; + positivesign = false; + } else + it = from; + const std::size_t decimalsep = str.find('.', it-str.begin()); + int distance; + if (std::string::npos == decimalsep) { + distance = to - it; + } else if (decimalsep > (to - str.begin())) + return 0.; // error handling?? + else + distance = int(decimalsep)-(from - str.begin()); + auto digitval = [&](char c) { + if ((10 < base) && (c > '9')) + return 10 + std::tolower(c) - 'a'; + else + return c - '0'; + }; + for (; it!=to; ++it) { + if ('.' == *it) + continue; + else + --distance; + result += digitval(*it)* std::pow(base, distance); + } + return (positivesign)?result:-result; +} + + +// Assuming a limited support of built-in hexadecimal floats (see C99, C++17) that is a fall-back implementation. +// Performance has been optimized WRT to heap activity, however the calculation part is not optimized. +static double FloatHexToDoubleNumber(const std::string& str) +{ + const std::size_t p = str.find_first_of("pP",3); + const double factor1 = myStod(str, str.begin() + 2, str.begin()+p, 16); + const bool suffix = (str.back() == 'f') || (str.back() == 'F') || (str.back() == 'l') || (str.back() == 'L'); + const double exponent = myStod(str, str.begin() + p + 1, (suffix)?str.end()-1:str.end(), 10); + const double factor2 = std::pow(2, exponent); + return factor1 * factor2; +} double MathLib::toDoubleNumber(const std::string &str) { @@ -563,6 +612,8 @@ // TODO : handle locale return std::strtod(str.c_str(), nullptr); #endif + if (isFloatHex(str)) + return FloatHexToDoubleNumber(str); // otherwise, convert to double std::istringstream istr(str); istr.imbue(std::locale::classic()); @@ -794,69 +845,76 @@ bool MathLib::isFloatHex(const std::string& str) { enum Status { - START, PLUSMINUS, HEX_PREFIX, WHOLE_NUMBER_DIGIT, WHOLE_NUMBER_DIGITS, FRACTION, EXPONENT_DIGIT, EXPONENT_DIGITS + START, HEX_0, HEX_X, WHOLE_NUMBER_DIGIT, POINT, FRACTION, EXPONENT_P, EXPONENT_SIGN, EXPONENT_DIGITS, EXPONENT_SUFFIX } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: - if (*it == '+' || *it == '-') - state = PLUSMINUS; - else if (*it == '0') - state = HEX_PREFIX; - else - return false; - break; - case PLUSMINUS: if (*it == '0') - state = HEX_PREFIX; + state = HEX_0; else return false; break; - case HEX_PREFIX: + case HEX_0: if (*it == 'x' || *it == 'X') - state = WHOLE_NUMBER_DIGIT; + state = HEX_X; else return false; break; - case WHOLE_NUMBER_DIGIT: + case HEX_X: if (isxdigit(static_cast(*it))) - state = WHOLE_NUMBER_DIGITS; + state = WHOLE_NUMBER_DIGIT; + else if (*it == '.') + state = POINT; else return false; break; - case WHOLE_NUMBER_DIGITS: + case WHOLE_NUMBER_DIGIT: if (isxdigit(static_cast(*it))) - state = WHOLE_NUMBER_DIGITS; + ; // state = WHOLE_NUMBER_DIGITS; else if (*it=='.') state = FRACTION; else if (*it=='p' || *it=='P') - state = EXPONENT_DIGIT; + state = EXPONENT_P; else return false; break; + case POINT: case FRACTION: if (isxdigit(static_cast(*it))) - state = FRACTION; - else if (*it=='p' || *it=='P') - state = EXPONENT_DIGIT; + state=FRACTION; + else if (*it == 'p' || *it == 'P') + state = EXPONENT_P; + else + return false; break; - case EXPONENT_DIGIT: - if (isxdigit(static_cast(*it))) + case EXPONENT_P: + if (isdigit(static_cast(*it))) state = EXPONENT_DIGITS; - else if (*it=='+' || *it=='-') + else if (*it == '+' || *it == '-') + state = EXPONENT_SIGN; + else + return false; + break; + case EXPONENT_SIGN: + if (isdigit(static_cast(*it))) state = EXPONENT_DIGITS; else return false; break; case EXPONENT_DIGITS: - if (isxdigit(static_cast(*it))) - state = EXPONENT_DIGITS; + if (isdigit(static_cast(*it))) + ; // state = EXPONENT_DIGITS; + else if (*it == 'f' || *it == 'F' || *it == 'l' || *it == 'L') + state = EXPONENT_SUFFIX; else - return *it=='f'||*it=='F'||*it=='l'||*it=='L'; + return false; break; + case EXPONENT_SUFFIX: + return false; } } - return state==EXPONENT_DIGITS; + return (EXPONENT_DIGITS==state) || (EXPONENT_SUFFIX == state); } bool MathLib::isValidIntegerSuffix(const std::string& str) diff -Nru cppcheck-1.85/lib/platform.h cppcheck-1.86/lib/platform.h --- cppcheck-1.85/lib/platform.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/platform.h 2018-12-08 07:18:21.000000000 +0000 @@ -23,6 +23,7 @@ #include "config.h" +#include #include /// @addtogroup Core @@ -41,7 +42,7 @@ private: static long long min_value(int bit) { if (bit >= 64) - return 1LL << 63; + return LLONG_MIN; return -(1LL << (bit-1)); } diff -Nru cppcheck-1.85/lib/symboldatabase.cpp cppcheck-1.86/lib/symboldatabase.cpp --- cppcheck-1.85/lib/symboldatabase.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/symboldatabase.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -345,7 +345,7 @@ // unnamed struct and union else if (Token::Match(tok, "struct|union {") && - Token::Match(tok->next()->link(), "} *|&| %name% ;|[")) { + Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); @@ -593,7 +593,7 @@ else if (declEnd && declEnd->str() == ";") { bool newFunc = true; // Is this function already in the database? for (std::multimap::const_iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { - if (Function::argsMatch(scope, i->second->argDef->next(), argStart->next(), emptyString, 0)) { + if (Function::argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) { newFunc = false; break; } @@ -1231,7 +1231,7 @@ ValueFlow::valueFlowConstantFoldAST(rhs, mSettings); // get constant folded value: - if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) { + if (rhs && rhs->hasKnownIntValue()) { enumerator.value = rhs->values().front().intvalue; enumerator.value_known = true; value = enumerator.value + 1; @@ -1334,7 +1334,7 @@ ValueFlow::valueFlowConstantFoldAST(rhs, mSettings); // get constant folded value: - if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) { + if (rhs && rhs->hasKnownIntValue()) { dimension.num = rhs->values().front().intvalue; dimension.known = true; } @@ -1856,24 +1856,26 @@ if (!isCPP) // C does not support overloads return true; - // skip "struct" - if (first->str() == "struct" || first->str() == "enum") - first = first->next(); - if (second->str() == "struct" || second->str() == "enum") - second = second->next(); - - // skip const on type passed by value - if (Token::Match(first, "const %type% %name%|,|)")) - first = first->next(); - if (Token::Match(second, "const %type% %name%|,|)")) - second = second->next(); - unsigned int arg_path_length = path_length; while (first->str() == second->str() && first->isLong() == second->isLong() && first->isUnsigned() == second->isUnsigned()) { + // skip optional type information + if (Token::Match(first->next(), "struct|enum|union|class")) + first = first->next(); + if (Token::Match(second->next(), "struct|enum|union|class")) + second = second->next(); + + // skip const on type passed by value + if (Token::Match(first->next(), "const %type% %name%|,|)") && + !Token::Match(first->next(), "const %type% %name%| [")) + first = first->next(); + if (Token::Match(second->next(), "const %type% %name%|,|)") && + !Token::Match(second->next(), "const %type% %name%| [")) + second = second->next(); + // at end of argument list if (first->str() == ")") { return true; @@ -1991,7 +1993,7 @@ } // remove class name - else if (arg_path_length > 2) { + else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) { std::string short_path = path; unsigned int short_path_length = arg_path_length; @@ -2028,20 +2030,6 @@ // reset path length if (first->str() == "," || second->str() == ",") arg_path_length = path_length; - - // skip "struct" - if (first->str() == "struct" || first->str() == "enum") - first = first->next(); - if (second->str() == "struct" || second->str() == "enum") - second = second->next(); - - // skip const on type passed by value - if (Token::Match(first, "const %type% %name%|,|)") && - !Token::Match(first, "const %type% %name%| [")) - first = first->next(); - if (Token::Match(second, "const %type% %name%|,|)") && - !Token::Match(second, "const %type% %name%| [")) - second = second->next(); } return false; @@ -2063,7 +2051,7 @@ const Function *f = i->second; if (f->hasBody()) continue; - if (Function::argsMatch(scope, f->argDef->next(), argStart->next(), emptyString, 0)) { + if (Function::argsMatch(scope, f->argDef, argStart, emptyString, 0)) { function = const_cast(i->second); break; } @@ -2628,16 +2616,7 @@ } } std::cout << indent << "mIndex: " << var->index() << std::endl; - std::cout << indent << "mAccess: " << - (var->isPublic() ? "Public" : - var->isProtected() ? "Protected" : - var->isPrivate() ? "Private" : - var->isGlobal() ? "Global" : - var->isNamespace() ? "Namespace" : - var->isArgument() ? "Argument" : - var->isLocal() ? "Local" : - var->isThrow() ? "Throw" : - "Unknown") << std::endl; + std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl; std::cout << indent << "mFlags: " << std::endl; std::cout << indent << " isMutable: " << var->isMutable() << std::endl; std::cout << indent << " isStatic: " << var->isStatic() << std::endl; @@ -2704,10 +2683,7 @@ func->type == Function::eDestructor ? "Destructor" : func->type == Function::eFunction ? "Function" : "Unknown") << std::endl; - std::cout << " access: " << (func->access == Public ? "Public" : - func->access == Protected ? "Protected" : - func->access == Private ? "Private" : - "Unknown") << std::endl; + std::cout << " access: " << accessControlToString(func->access) << std::endl; std::cout << " hasBody: " << func->hasBody() << std::endl; std::cout << " isInline: " << func->isInline() << std::endl; std::cout << " isConst: " << func->isConst() << std::endl; @@ -3309,7 +3285,6 @@ // skip over qualification if present nameTok = skipScopeIdentifiers(nameTok); if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name - className = nameTok->str(); } @@ -4747,7 +4722,7 @@ for (std::multimap::const_iterator it = ns->functionMap.find(func->str()); it != ns->functionMap.end() && it->first == func->str(); ++it) { - if (Function::argsMatch(ns, it->second->argDef->next(), func->tokAt(2), path, path_length) && + if (Function::argsMatch(ns, it->second->argDef, func->next(), path, path_length) && it->second->isDestructor() == destructor) { function = it->second; break; diff -Nru cppcheck-1.85/lib/symboldatabase.h cppcheck-1.86/lib/symboldatabase.h --- cppcheck-1.85/lib/symboldatabase.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/symboldatabase.h 2018-12-08 07:18:21.000000000 +0000 @@ -605,6 +605,10 @@ void setValueType(const ValueType &valueType); + AccessControl accessControl() const { + return mAccess; + } + private: // only symbol database can change the type friend class SymbolDatabase; @@ -955,6 +959,19 @@ return nullptr; } + bool isNestedIn(const Scope * outer) const { + if (!outer) + return false; + if (outer == this) + return true; + const Scope * parent = nestedIn; + while (outer != parent && parent) + parent = parent->nestedIn; + if (parent && parent == outer) + return true; + return false; + } + bool isClassOrStruct() const { return (type == eClass || type == eStruct); } diff -Nru cppcheck-1.85/lib/templatesimplifier.cpp cppcheck-1.86/lib/templatesimplifier.cpp --- cppcheck-1.85/lib/templatesimplifier.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/templatesimplifier.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -53,8 +53,8 @@ }; } -TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, const std::string &n) : - token(tok), scope(s), name(n) +TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, const std::string &n, const Token *nt) : + token(tok), scope(s), name(n), nameToken(nt) { token->hasTemplateSimplifierPointer(true); } @@ -169,7 +169,9 @@ // parse this statement and see if the '<' and '>' are matching unsigned int level = 0; - for (const Token *tok2 = tok; tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->next()) { + for (const Token *tok2 = tok; tok2 && !Token::simpleMatch(tok2, ";"); tok2 = tok2->next()) { + if (Token::simpleMatch(tok2, "{") && (!Token::Match(tok2->previous(), ">|%type%") || Token::simpleMatch(tok2->link(), "} ;"))) + break; if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == "<") { @@ -352,6 +354,20 @@ it->token->hasTemplateSimplifierPointer(false); it->token = nullptr; } + const std::list::iterator it2 = std::find_if(mTemplateDeclarations.begin(), + mTemplateDeclarations.end(), + FindToken(begin->next())); + if (it2 != mTemplateDeclarations.end()) { + // remove the pointer flag and prevent the deleted pointer from being used + it2->token->hasTemplateSimplifierPointer(false); + it2->token = nullptr; + } + for (size_t i = 0; i < mTypesUsedInTemplateInstantiation.size(); ++i) { + if (mTypesUsedInTemplateInstantiation[i] == begin->next()) { + mTypesUsedInTemplateInstantiation[i]->hasTemplateSimplifierPointer(false); + mTypesUsedInTemplateInstantiation[i] = nullptr; + } + } } begin->deleteNext(); } @@ -377,20 +393,20 @@ if (tok2->str() == "(") { tok2 = tok2->link(); } else if (tok2->str() == ")") { // garbage code! (#3504) - Token::eraseTokens(tok,tok2); + eraseTokens(tok,tok2); deleteToken(tok); return false; } else if (tok2->str() == "{") { tok2 = tok2->link()->next(); - Token::eraseTokens(tok, tok2); if (tok2 && tok2->str() == ";" && tok2->next()) - tok->deleteNext(); + tok2 = tok2->next(); + eraseTokens(tok, tok2); deleteToken(tok); return true; } else if (tok2->str() == "}") { // garbage code! (#3449) - Token::eraseTokens(tok,tok2); + eraseTokens(tok,tok2); deleteToken(tok); return false; } @@ -403,14 +419,14 @@ if (tok2->str() == "explicit" || (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) { - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } if (tok2->str() == ";") { tok2 = tok2->next(); - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } @@ -423,7 +439,7 @@ else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) { tok2 = tok2->next(); - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } @@ -432,64 +448,6 @@ return false; } -std::set TemplateSimplifier::expandSpecialized() -{ - std::set expandedtemplates; - - // Locate specialized templates.. - for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { - if (!Token::simpleMatch(tok, "template < >")) - continue; - - // what kind of template is this? - Token *tok2 = tok->tokAt(3); - while (tok2 && (tok2->isName() || tok2->str() == "*")) - tok2 = tok2->next(); - - if (!templateParameters(tok2)) - continue; - - // unknown template.. bail out - if (!tok2->previous()->isName()) - continue; - - tok2 = tok2->previous(); - std::string s; - { - std::ostringstream ostr; - const Token *tok3 = tok2; - for (; tok3 && tok3->str() != ">"; tok3 = tok3->next()) { - if (tok3 != tok2) - ostr << " "; - ostr << tok3->str(); - } - if (!Token::Match(tok3, "> (|{|:")) - continue; - s = ostr.str(); - } - - // remove spaces to create new name - const std::string name(s + " >"); - expandedtemplates.insert(name); - - // Rename template.. - Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); - tok2->str(name); - - // delete the "template < >" - tok->deleteNext(2); - tok->deleteThis(); - - // Use this special template in the code.. - while (nullptr != (tok2 = const_cast(Token::findsimplematch(tok2, name.c_str())))) { - Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); - tok2->str(name); - } - } - - return expandedtemplates; -} - /// TODO: This is copy pasted from Tokenizer. We should reuse this code. namespace { struct ScopeInfo2 { @@ -505,11 +463,6 @@ ret += (ret.empty() ? "" : " :: ") + i.name; return ret; } -static std::string getFullName(const std::list &scopeInfo, const std::string &name) -{ - const std::string &scopeName = getScopeName(scopeInfo); - return scopeName + (scopeName.empty() ? "" : " :: ") + name; -} static void setScopeInfo(const Token *tok, std::list *scopeInfo) { @@ -532,10 +485,10 @@ } } -std::list TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates) +bool TemplateSimplifier::getTemplateDeclarations() { + bool codeWithTemplates = false; std::list scopeInfo; - std::list declarations; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "}|namespace|class|struct|union")) { setScopeInfo(tok, &scopeInfo); @@ -556,19 +509,23 @@ tok2 = tok2->link(); else if (tok2->str() == ")") break; - // Just a declaration => ignore this - else if (tok2->str() == ";") + // Declaration => add to mTemplateForwardDeclarations + else if (tok2->str() == ";") { + const int namepos = getTemplateNamePosition(parmEnd, true); + if (namepos > 0) + mTemplateForwardDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos), parmEnd->tokAt(namepos)); break; - // Implementation => add to "templates" + } + // Implementation => add to mTemplateDeclarations else if (tok2->str() == "{") { - const int namepos = getTemplateNamePosition(parmEnd); + const int namepos = getTemplateNamePosition(parmEnd, false); if (namepos > 0) - declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos))); + mTemplateDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos), parmEnd->tokAt(namepos)); break; } } } - return declarations; + return codeWithTemplates; } @@ -592,7 +549,7 @@ const Token *tok2 = Token::findmatch(tok, "{|;"); if (tok2 && tok2->str() == "{") tok = tok2->link(); - } else if (Token::Match(tok->previous(), "[({};=] %name% ::|<") || + } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|. %name% ::|<") || Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { @@ -613,8 +570,10 @@ for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { if (Token::Match(tok2, ", %name% <") && templateParameters(tok2->tokAt(2))) { - mTemplateInstantiations.emplace_back(tok2->next(), getScopeName(scopeList), getFullName(scopeList, tok2->strAt(1))); - } + mTemplateInstantiations.emplace_back(tok2->next(), getScopeName(scopeList), tok2->strAt(1), tok2->tokAt(1)); + } else if (Token::Match(tok2->next(), "class|struct")) + const_cast(tok2)->deleteNext(); + } // Add outer template.. @@ -624,11 +583,11 @@ const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + tok->str(); const std::list::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindName(fullName)); if (it != mTemplateDeclarations.end()) { - mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), fullName); + mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), fullName, tok); break; } else { if (scopeName.empty()) { - mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), scopeName1 + (scopeName1.empty()?"":" :: ") + tok->str()); + mTemplateInstantiations.emplace_back(tok, getScopeName(scopeList), scopeName1 + (scopeName1.empty()?"":" :: ") + tok->str(), tok); break; } const std::string::size_type pos = scopeName.rfind(" :: "); @@ -777,7 +736,15 @@ if (!tok2) continue; - Token::eraseTokens(eqtok, tok2); + // don't strip args from uninstantiated templates + std::list::iterator ti2 = std::find_if(mTemplateInstantiations.begin(), + mTemplateInstantiations.end(), + FindName(template1.name)); + + if (ti2 == mTemplateInstantiations.end()) + continue; + + eraseTokens(eqtok, tok2); eqtok->deleteThis(); } } @@ -840,6 +807,11 @@ // Replace template alias code.. aliasUsage.name = templateAlias.name; if (aliasUsage.name.find(' ') == std::string::npos) { + const Token *temp = aliasToken1; + while (temp && temp != templateAlias.token) { + aliasUsage.token->insertToken(temp->str(), "", true); + temp = temp->next(); + } aliasUsage.token->str(templateAlias.token->str()); } else { tok2 = TokenList::copyTokens(aliasUsage.token, aliasToken1, templateAlias.token, true); @@ -859,7 +831,7 @@ mTemplateInstantiations.end(), FindToken(tok1)); if (it != mTemplateInstantiations.end()) - mTemplateInstantiations.emplace_back(tok2, it->scope, it->name); + mTemplateInstantiations.emplace_back(tok2, it->scope, it->name, it->nameToken); } continue; } @@ -951,14 +923,18 @@ return false; } -int TemplateSimplifier::getTemplateNamePosition(const Token *tok) +int TemplateSimplifier::getTemplateNamePosition(const Token *tok, bool forward) { // get the position of the template name int namepos = 0, starAmpPossiblePosition = 0; - if (Token::Match(tok, "> class|struct|union %type% {|:|<")) + if ((forward && Token::Match(tok, "> class|struct|union %type% :|<|;")) || + (!forward && Token::Match(tok, "> class|struct|union %type% {|:|<"))) namepos = 2; else if (Token::Match(tok, "> %type% *|&| %type% (")) namepos = 2; + else if (Token::Match(tok, "> %type% %type% <") && + Token::simpleMatch(tok->tokAt(3)->findClosingBracket(), "> (")) + namepos = 2; else if (Token::Match(tok, "> %type% %type% *|&| %type% (")) namepos = 3; else if (getTemplateNamePositionTemplateMember(tok, namepos)) @@ -979,20 +955,182 @@ return namepos; } +void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok) +{ + // find start of qualification + const Token * tokStart = tok; + int offset = 0; + while (Token::Match(tokStart->tokAt(-2), "%name% ::")) { + tokStart = tokStart->tokAt(-2); + offset -= 2; + } + // decide if namespace needs to be inserted in or appended to token list + const bool insert = tokStart != tok; + + std::string::size_type start = 0; + std::string::size_type end = 0; + while ((end = templateDeclaration.scope.find(" ", start)) != std::string::npos) { + std::string token = templateDeclaration.scope.substr(start, end - start); + if (insert) + mTokenList.back()->tokAt(offset)->insertToken(token, ""); + else + mTokenList.addtoken(token, tok->linenr(), tok->fileIndex()); + start = end + 1; + } + if (insert) { + mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope.substr(start), ""); + mTokenList.back()->tokAt(offset)->insertToken("::", ""); + } else { + mTokenList.addtoken(templateDeclaration.scope.substr(start), tok->linenr(), tok->fileIndex()); + mTokenList.addtoken("::", tok->linenr(), tok->fileIndex()); + } +} + +bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) const +{ + std::string scope = templateDeclaration.scope; + + // get the length in tokens of the namespace + std::string::size_type pos = 0; + int offset = -2; + + while ((pos = scope.find("::", pos)) != std::string::npos) { + offset -= 2; + pos += 2; + } + + return Token::simpleMatch(tok->tokAt(offset), scope.c_str()) ; +} void TemplateSimplifier::expandTemplate( + const TokenAndName &templateDeclaration, const Token *templateDeclarationToken, - const std::string &fullName, + const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation) + bool copy) { std::list scopeInfo; bool inTemplateDefinition = false; const Token *startOfTemplateDeclaration = nullptr; const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); - for (const Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { + const bool isClass = Token::Match(templateDeclarationToken->next(), "class|struct|union %name% <|{|:"); + const bool isFunction = templateDeclarationNameToken->strAt(1) == "("; + + // add forward declarations + if (copy && isClass) { + templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true); + templateDeclaration.token->insertToken(newName, "", true); + templateDeclaration.token->insertToken(";", "", true); + } else if (copy && isFunction) { + // check if this is an explicit instantiation + Token * temp = templateInstantiation.token; + while (temp && !Token::Match(temp->previous(), "}|;")) + temp = temp->previous(); + if (Token::Match(temp, "template !!<")) { + // just delete "template" + deleteToken(temp); + } else { + // add forward declaration + Token * dst = templateDeclaration.token; + Token * start; + Token * end; + auto it = mTemplateForwardDeclarationsMap.find(dst); + if (it != mTemplateForwardDeclarationsMap.end()) { + dst = it->second; + const Token * temp1 = dst->tokAt(1)->findClosingBracket(); + const Token * temp2 = temp1->tokAt(getTemplateNamePosition(temp1)); + start = temp1->next(); + end = temp2->linkAt(1)->next(); + } else { + start = templateDeclarationToken->next(); + end = templateDeclarationNameToken->linkAt(1)->next(); + } + unsigned int typeindentlevel = 0; + while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { + if (Token::Match(end, "<|(|{")) + ++typeindentlevel; + else if (Token::Match(end, ">|)|}")) + --typeindentlevel; + end = end->next(); + } + + std::map links; + while (start && start != end) { + unsigned int itype = 0; + while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != start->str()) + ++itype; + + if (itype < typeParametersInDeclaration.size()) { + typeindentlevel = 0; + for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype]; + typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>")); + typetok = typetok->next()) { + if (Token::simpleMatch(typetok, ". . .")) { + typetok = typetok->tokAt(2); + continue; + } + if (Token::Match(typetok, "%name% <") && templateParameters(typetok->next()) > 0) + ++typeindentlevel; + else if (typeindentlevel > 0 && typetok->str() == ">") + --typeindentlevel; + dst->insertToken(typetok->str(), typetok->originalName(), true); + dst->previous()->isTemplateArg(true); + dst->previous()->isSigned(typetok->isSigned()); + dst->previous()->isUnsigned(typetok->isUnsigned()); + dst->previous()->isLong(typetok->isLong()); + } + } else { + if (start->str() == templateDeclarationNameToken->str()) { + dst->insertToken(newName, "", true); + if (start->strAt(1) == "<") + start = start->next()->findClosingBracket(); + } else { + // check if type is a template + if (start->strAt(1) == "<") { + // get the instantiated name + Token * closing = start->next()->findClosingBracket(); + std::string name; + const Token * type = start; + while (type && type != closing->next()) { + if (!name.empty()) + name += " "; + name += type->str(); + type = type->next(); + } + // check if type is instantiated + for (const auto & inst : mTemplateInstantiations) { + if (Token::simpleMatch(inst.nameToken, name.c_str())) { + // use the instantiated name + dst->insertToken(name, "", true); + start = closing; + break; + } + } + // just copy the token if it wasn't instantiated + if (start != closing) + dst->insertToken(start->str(), "", true); + } else + dst->insertToken(start->str(), "", true); + } + if (start->link()) { + if (Token::Match(start, "[|{|(")) { + links[start->link()] = dst->previous(); + } else if (Token::Match(start, "]|}|)")) { + Token::createMutualLinks(links[start], dst->previous()); + links.erase(start); + } + } + } + + start = start->next(); + } + dst->insertToken(";", "", true); + } + } + + for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { if (Token::Match(tok3, "}|namespace|class|struct|union")) { setScopeInfo(tok3, &scopeInfo); continue; @@ -1030,12 +1168,12 @@ // member function implemented outside class definition else if (inTemplateDefinition && Token::Match(tok3, "%name% <") && - fullName == getFullName(scopeInfo, tok3->str()) && + templateInstantiation.name == tok3->str() && instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; - const Token * tok5 = nullptr; // start of function return type - for (const Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { + Token * tok5 = nullptr; // start of function return type + for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->str() == "template") { istemplate = true; tok5 = prev; @@ -1057,14 +1195,27 @@ // copy return type while (tok5 && tok5 != tok3) { // replace name if found - if (Token::Match(tok5, "%name% <") && tok5->str() == fullName) { - mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); - tok5 = tok5->next()->findClosingBracket(); - } else + if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name) { + if (copy) { + if (!templateDeclaration.scope.empty() && tok5->strAt(-1) != "::") + addNamespace(templateDeclaration, tok5); + mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); + tok5 = tok5->next()->findClosingBracket(); + } else { + tok5->str(newName); + eraseTokens(tok5, tok5->next()->findClosingBracket()->next()); + } + } else if (copy) mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex()); + tok5 = tok5->next(); } - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + if (copy) { + if (!templateDeclaration.scope.empty() && tok3->strAt(-1) != "::") + addNamespace(templateDeclaration, tok3); + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + } + while (tok3 && tok3->str() != "::") tok3 = tok3->next(); @@ -1082,7 +1233,8 @@ std::stack brackets; // holds "(", "[" and "{" tokens // FIXME use full name matching somehow - const std::string lastName = (fullName.find(' ') != std::string::npos) ? fullName.substr(fullName.rfind(' ')+1) : fullName; + const std::string lastName = (templateInstantiation.name.find(' ') != std::string::npos) ? templateInstantiation.name.substr(templateInstantiation.name.rfind(' ')+1) : templateInstantiation.name; + for (; tok3; tok3 = tok3->next()) { if (tok3->isName()) { @@ -1094,7 +1246,7 @@ // replace type with given type.. if (itype < typeParametersInDeclaration.size()) { unsigned int typeindentlevel = 0; - for (const Token *typetok = typesUsedInTemplateInstantiation[itype]; + for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype]; typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (Token::simpleMatch(typetok, ". . .")) { @@ -1105,8 +1257,10 @@ ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; - mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); - mTokenList.back()->isTemplateArg(true); + if (copy) { + mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); + mTokenList.back()->isTemplateArg(true); + } } continue; } @@ -1114,18 +1268,45 @@ // replace name.. if (tok3->str() == lastName) { - if (!Token::simpleMatch(tok3->next(), "<")) { - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); - continue; - } else if (tok3 == templateDeclarationNameToken) { - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); - tok3 = tok3->next()->findClosingBracket(); + if (Token::simpleMatch(tok3->next(), "<")) { + Token *closingBracket = tok3->next()->findClosingBracket(); + if (closingBracket) { + // replace multi token name with single token name + if (tok3 == templateDeclarationNameToken || + Token::Match(tok3, newName.c_str())) { + if (copy) { + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + tok3 = closingBracket; + } else { + tok3->str(newName); + eraseTokens(tok3, closingBracket->next()); + } + continue; + } else if (!templateDeclaration.scope.empty() && + !alreadyHasNamespace(templateDeclaration, tok3) && + closingBracket->strAt(1) != "(") { + if (copy) + addNamespace(templateDeclaration, tok3); + } + } + } else { + if (copy) { + // add namespace if necessary + if (!templateDeclaration.scope.empty() && + (isClass ? tok3->strAt(1) != "(" : true)) { + addNamespace(templateDeclaration, tok3); + } + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + } else if (!Token::Match(tok3->next(), ":|{")) + tok3->str(newName); continue; } } // copy - mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); + if (copy) + mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); + if (Token::Match(tok3, "%type% <") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { const Token *closingBracket = tok3->next()->findClosingBracket(); if (Token::simpleMatch(closingBracket->next(), "&")) { @@ -1145,36 +1326,38 @@ std::string name = tok3->str(); for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) name = prev->str() + " :: " + name; - mTemplateInstantiations.emplace_back(mTokenList.back(), getScopeName(scopeInfo), getFullName(scopeInfo, name)); + mTemplateInstantiations.emplace_back(mTokenList.back(), getScopeName(scopeInfo), name, tok3); } // link() newly tokens manually - else if (tok3->str() == "{") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "(") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "[") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "}") { - assert(brackets.empty() == false && brackets.top()->str() == "{"); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - if (tok3->strAt(1) == ";") { - const Token * tokSemicolon = tok3->next(); - mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); - } - brackets.pop(); - if (brackets.empty()) { - inTemplateDefinition = false; - break; + else if (copy) { + if (tok3->str() == "{") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "(") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "[") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "}") { + assert(brackets.empty() == false && brackets.top()->str() == "{"); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + if (tok3->strAt(1) == ";") { + const Token * tokSemicolon = tok3->next(); + mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); + } + brackets.pop(); + if (brackets.empty()) { + inTemplateDefinition = false; + break; + } + } else if (tok3->str() == ")") { + assert(brackets.empty() == false && brackets.top()->str() == "("); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); + } else if (tok3->str() == "]") { + assert(brackets.empty() == false && brackets.top()->str() == "["); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); } - } else if (tok3->str() == ")") { - assert(brackets.empty() == false && brackets.top()->str() == "("); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - brackets.pop(); - } else if (tok3->str() == "]") { - assert(brackets.empty() == false && brackets.top()->str() == "["); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - brackets.pop(); } } @@ -1492,7 +1675,7 @@ { typeParametersInDeclaration.clear(); for (; tok && tok->str() != ">"; tok = tok->next()) { - if (Token::Match(tok, "%name% ,|>")) + if (Token::Match(tok, "%name% ,|>|=")) typeParametersInDeclaration.push_back(tok); } return tok; @@ -1541,6 +1724,52 @@ return Token::Match(templateDeclarationNameToken, "%name% !!<"); } +std::string TemplateSimplifier::getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation) +{ + std::string typeForNewName; + unsigned int indentlevel = 0; + for (Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { + // #2648 - unhandled parentheses => bail out + // #2721 - unhandled [ => bail out + if (Token::Match(tok3, "(|[")) { + typeForNewName.clear(); + break; + } + if (!tok3->next()) { + typeForNewName.clear(); + break; + } + if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && templateParameters(tok3) > 0) + ++indentlevel; + else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) + --indentlevel; + if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { + tok3->hasTemplateSimplifierPointer(true); + mTypesUsedInTemplateInstantiation.push_back(tok3); + } + const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; + if (!constconst) { + typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); + } + // add additional type information + if (!constconst && !Token::Match(tok3, "class|struct|enum")) { + if (tok3->isUnsigned()) + typeForNewName += "unsigned"; + else if (tok3->isSigned()) + typeForNewName += "signed"; + if (tok3->isLong()) + typeForNewName += "long"; + if (!typeForNewName.empty()) + typeForNewName += ' '; + typeForNewName += tok3->str(); + } + } + + return typeForNewName; +} + bool TemplateSimplifier::simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, @@ -1572,7 +1801,16 @@ return false; } - const bool isfunc(tok->strAt(namepos + 1) == "("); + const bool specialized = Token::simpleMatch(templateDeclaration.token, "template < >"); + bool isfunc = false; + + if (tok->strAt(namepos + 1) == "(") + isfunc = true; + else if (tok->strAt(namepos + 1) == "<") { + const Token *tok1 = tok->tokAt(namepos + 1)->findClosingBracket(); + if (tok1 && tok1->strAt(1) == "(") + isfunc = true; + } // locate template usage.. std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); @@ -1613,54 +1851,28 @@ assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 const Token *startToken = tok2; - while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) - startToken = startToken->tokAt(-2); + while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || + Token::Match(startToken->tokAt(-2), "> :: %name%")) { + if (startToken->strAt(-2) == ">") { + const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } - if (Token::Match(startToken->previous(), "[;{}=]") && - !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")) + if (Token::Match(startToken->previous(), ";|{|}|=|const") && + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*|&| %name%"))) continue; // New type.. - std::vector typesUsedInTemplateInstantiation; - std::string typeForNewName; + mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; - unsigned int indentlevel = 0; - for (const Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { - // #2648 - unhandled parentheses => bail out - // #2721 - unhandled [ => bail out - if (Token::Match(tok3, "(|[")) { - typeForNewName.clear(); - break; - } - if (!tok3->next()) { - typeForNewName.clear(); - break; - } - if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && templateParameters(tok3) > 0) - ++indentlevel; - else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) - --indentlevel; - if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) - typesUsedInTemplateInstantiation.push_back(tok3); - const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; - if (!constconst) { - typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); - } - // add additional type information - if (!constconst && !Token::Match(tok3, "class|struct|union|enum")) { - if (tok3->isUnsigned()) - typeForNewName += "unsigned"; - else if (tok3->isSigned()) - typeForNewName += "signed"; - if (tok3->isLong()) - typeForNewName += "long"; - if (!typeForNewName.empty()) - typeForNewName += ' '; - typeForNewName += tok3->str(); - } - } + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); - if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { + if (typeForNewName.empty() || (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", @@ -1676,12 +1888,93 @@ if (expandedtemplates.find(newName) == expandedtemplates.end()) { expandedtemplates.insert(newName); - expandTemplate(tok, instantiation.name, typeParametersInDeclaration, newName, typesUsedInTemplateInstantiation); + expandTemplate(templateDeclaration, tok, instantiation, typeParametersInDeclaration, newName, !specialized); instantiated = true; } // Replace all these template usages.. - replaceTemplateUsage(tok2, instantiation.name, typeStringsUsedInTemplateInstantiation, newName, typesUsedInTemplateInstantiation); + replaceTemplateUsage(tok2, instantiation.name, typeStringsUsedInTemplateInstantiation, newName); + } + + // process uninstantiated templates + const std::list::iterator it = std::find_if(mTemplateInstantiations.begin(), + mTemplateInstantiations.end(), + FindName(templateDeclaration.name)); + + // TODO: remove the specialized check and handle all uninstantiated templates someday. + if (it == mTemplateInstantiations.end() && specialized) { + simplifyCalculations(); + + Token * tok2 = const_cast(tok->tokAt(namepos)); + if (mErrorLogger && !mTokenList.getFiles().empty()) + mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); +#ifdef MAXTIME + if (std::time(0) > maxtime) + return false; +#else + (void)maxtime; +#endif + assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 + + Token *startToken = tok2; + while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || + Token::Match(startToken->tokAt(-2), "> :: %name%")) { + if (startToken->strAt(-2) == ">") { + const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } + + if (Token::Match(startToken->previous(), ";|{|}|=|const") && + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*|&| %name%"))) + return false; + + // already simplified + if (!Token::Match(startToken, "%name% <")) + return false; + + if (templateDeclaration.scope.empty()) { + if (startToken->str() != templateDeclaration.name) + return false; + } else { + std::string name = templateDeclaration.scope + " :: " + startToken->str(); + if (name != templateDeclaration.name) + return false; + } + + if (!matchSpecialization(tok->tokAt(namepos), startToken, specializations)) + return false; + + tok2 = startToken; + + // New type.. + mTypesUsedInTemplateInstantiation.clear(); + std::list typeStringsUsedInTemplateInstantiation; + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); + + if (typeForNewName.empty()) { + if (printDebug && mErrorLogger) { + std::list callstack(1, tok2); + mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", + "Failed to instantiate template \"" + templateDeclaration.name + "\". The checking continues anyway.", false)); + } + return false; + } + + // New classname/funcname.. + const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); + if (expandedtemplates.find(newName) == expandedtemplates.end()) { + expandedtemplates.insert(newName); + expandTemplate(templateDeclaration, tok, templateDeclaration, typeParametersInDeclaration, newName, false); + instantiated = true; + } + + // Replace all these template usages.. + replaceTemplateUsage(startToken, templateDeclaration.name, typeStringsUsedInTemplateInstantiation, newName); } // Template has been instantiated .. then remove the template declaration @@ -1699,11 +1992,11 @@ return it == strings.end() && tok && tok->str() == ">"; } -void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, - const std::string &templateName, - const std::list &typeStringsUsedInTemplateInstantiation, - const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation) +void TemplateSimplifier::replaceTemplateUsage( + Token * const instantiationToken, + const std::string &templateName, + const std::list &typeStringsUsedInTemplateInstantiation, + const std::string &newName) { std::list scopeInfo; std::list< std::pair > removeTokens; @@ -1725,7 +2018,7 @@ // match parameters Token * tok2 = nameTok->tokAt(2); unsigned int typeCountInInstantiation = 1U; // There is always at least one type - const Token *typetok = (!typesUsedInTemplateInstantiation.empty()) ? typesUsedInTemplateInstantiation[0] : nullptr; + const Token *typetok = (!mTypesUsedInTemplateInstantiation.empty()) ? mTypesUsedInTemplateInstantiation[0] : nullptr; unsigned int indentlevel2 = 0; // indentlevel for tokgt while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) { if (tok2->str() == "<" && templateParameters(tok2) > 0) @@ -1743,8 +2036,8 @@ typetok = typetok->next(); } else { - if (typeCountInInstantiation < typesUsedInTemplateInstantiation.size()) - typetok = typesUsedInTemplateInstantiation[typeCountInInstantiation++]; + if (typeCountInInstantiation < mTypesUsedInTemplateInstantiation.size()) + typetok = mTypesUsedInTemplateInstantiation[typeCountInInstantiation++]; else typetok = nullptr; } @@ -1757,10 +2050,8 @@ // matching template usage => replace tokens.. // Foo < int > => Foo - if (tok2->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) { + if (tok2->str() == ">" && typeCountInInstantiation == mTypesUsedInTemplateInstantiation.size()) { const Token * const nameTok1 = nameTok; - while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%")) - nameTok = nameTok->tokAt(-2); nameTok->str(newName); for (std::list::iterator it = mTemplateInstantiations.begin(); it != mTemplateInstantiations.end(); ++it) { if (it->token == nameTok1) @@ -1788,50 +2079,121 @@ } } - -void TemplateSimplifier::simplifyTemplates( - const std::time_t maxtime, - bool &codeWithTemplates -) +void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() { - std::set expandedtemplates(expandSpecialized()); + // try to locate a matching declaration for each forward declaration + for (const auto & forwardDecl : mTemplateForwardDeclarations) { + std::vector params1; - if (getTemplateDeclarations(codeWithTemplates).empty()) - return; + getTemplateParametersInDeclaration(forwardDecl.token, params1); - // There are templates.. - // Remove "typename" unless used in template arguments.. - for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { - if (tok->str() == "typename") - tok->deleteThis(); + for (auto & decl : mTemplateDeclarations) { + std::vector params2; - if (Token::simpleMatch(tok, "template <")) { - while (tok && tok->str() != ">") - tok = tok->next(); - if (!tok) - break; + getTemplateParametersInDeclaration(decl.token, params2); + + // make sure the number of arguments match + if (params1.size() == params2.size()) { + std::string declName = decl.scope; + if (!declName.empty()) + declName += " :: "; + if (decl.nameToken->strAt(-1) == "::") { + const Token * start = decl.nameToken; + + while (Token::Match(start->tokAt(-2), "%name% ::")) + start = start->tokAt(-2); + + while (start != decl.nameToken) { + declName += (start->str() + " "); + start = start->next(); + } + } + declName += decl.name; + + std::string forwardDeclName = forwardDecl.scope; + if (!forwardDeclName.empty()) + forwardDeclName += " :: "; + forwardDeclName += forwardDecl.name; + + // make sure the scopes and names match + if (forwardDeclName == declName) { + // save forward declaration for lookup later + if ((decl.nameToken->strAt(1) == "(" && forwardDecl.nameToken->strAt(1) == "(") || + (decl.nameToken->strAt(1) == "{" && forwardDecl.nameToken->strAt(1) == ";")) { + mTemplateForwardDeclarationsMap[decl.token] = forwardDecl.token; + } + + for (size_t k = 0; k < params1.size(); k++) { + // copy default value to declaration if not present + if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { + const_cast(params2[k])->insertToken(params1[k]->strAt(2)); + const_cast(params2[k])->insertToken(params1[k]->strAt(1)); + } + } + } + } } } +} - mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates); +void TemplateSimplifier::simplifyTemplates( + const std::time_t maxtime, + bool &codeWithTemplates) +{ + // TODO: 2 is not the ideal number of loops. + // We should loop until the number of declarations is 0 but we can't + // do that until we instantiate unintstantiated templates with their symbolic types. + // That will allow the uninstantiated template code to be removed from the symbol database. + // Unfortunately the template simplifier doesn't handle namespaces properly so + // the uninstantiated template code in the symbol database can't be removed until #8768 + // is fixed. + for (int i = 0; i < 2; ++i) { + if (i) { + mTemplateDeclarations.clear(); + mTemplateForwardDeclarations.clear(); + mTemplateForwardDeclarationsMap.clear(); + mTemplateInstantiations.clear(); + mInstantiatedTemplates.clear(); + } + + bool hasTemplates = getTemplateDeclarations(); + + if (i == 0) { + codeWithTemplates = hasTemplates; + if (hasTemplates) { + // There are templates.. + // Remove "typename" unless used in template arguments.. + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (tok->str() == "typename") + tok->deleteThis(); + + if (Token::simpleMatch(tok, "template <")) { + while (tok && tok->str() != ">") + tok = tok->next(); + if (!tok) + break; + } + } + } + } - // Locate possible instantiations of templates.. - getTemplateInstantiations(); + // Make sure there is something to simplify. + if (mTemplateDeclarations.empty()) + return; - // No template instantiations? Then return. - if (mTemplateInstantiations.empty()) - return; + // Copy default argument values from forward declaration to declaration + fixForwardDeclaredDefaultArgumentValues(); - // Template arguments with default values - useDefaultArgumentValues(); + // Locate possible instantiations of templates.. + getTemplateInstantiations(); - simplifyTemplateAliases(); + // Template arguments with default values + useDefaultArgumentValues(); + + simplifyTemplateAliases(); + + std::set expandedtemplates; - // expand templates - //bool done = false; - //while (!done) - { - //done = true; for (std::list::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { // get specializations.. std::list specializations; @@ -1860,15 +2222,27 @@ break; } if (decl != mTemplateDeclarations.end()) { + if (Token::simpleMatch(it->token, "template < >")) { + // delete the "template < >" + Token * tok = it->token; + tok->deleteNext(2); + tok->deleteThis(); + } else + removeTemplate(it->token); mTemplateDeclarations.erase(decl); - removeTemplate(it->token); } } // remove out of line member functions while (!mMemberFunctionsToDelete.empty()) { - removeTemplate(mMemberFunctionsToDelete.begin()->token); - mTemplateDeclarations.remove(mMemberFunctionsToDelete.front()); + const std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), + mTemplateDeclarations.end(), + FindToken(mMemberFunctionsToDelete.begin()->token)); + // multiple functions can share the same declaration so make sure it hasn't already been deleted + if (it != mTemplateDeclarations.end()) { + removeTemplate(it->token); + mTemplateDeclarations.erase(it); + } mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin()); } } diff -Nru cppcheck-1.85/lib/templatesimplifier.h cppcheck-1.86/lib/templatesimplifier.h --- cppcheck-1.85/lib/templatesimplifier.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/templatesimplifier.h 2018-12-08 07:18:21.000000000 +0000 @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -68,13 +69,14 @@ * Token and its full scopename */ struct TokenAndName { - TokenAndName(Token *tok, const std::string &s, const std::string &n); + TokenAndName(Token *tok, const std::string &s, const std::string &n, const Token *nt); bool operator == (const TokenAndName & rhs) const { - return token == rhs.token && scope == rhs.scope && name == rhs.name; + return token == rhs.token && scope == rhs.scope && name == rhs.name && nameToken == rhs.nameToken; } Token *token; std::string scope; std::string name; + const Token *nameToken; }; /** @@ -89,10 +91,11 @@ /** * Match template declaration/instantiation * @param tok The ">" token e.g. before "class" + * @param forward declaration or forward declaration * @return -1 to bail out or positive integer to identity the position * of the template name. */ - static int getTemplateNamePosition(const Token *tok); + static int getTemplateNamePosition(const Token *tok, bool forward = false); /** * Simplify templates @@ -109,7 +112,7 @@ * @return true if modifications to token-list are done. * false if no modifications are done. */ - bool simplifyNumericCalculations(Token *tok); + static bool simplifyNumericCalculations(Token *tok); /** * Simplify constant calculations such as "1+2" => "3". @@ -122,9 +125,9 @@ private: /** * Get template declarations - * @return list of template declarations + * @return true if code has templates. */ - std::list getTemplateDeclarations(bool &codeWithTemplates); + bool getTemplateDeclarations(); /** * Get template instantiations @@ -132,6 +135,12 @@ void getTemplateInstantiations(); /** + * Fix forward declared default argument values by copying them + * when they are not present in the declaration. + */ + void fixForwardDeclaredDefaultArgumentValues(); + + /** * simplify template instantiations (use default argument values) */ void useDefaultArgumentValues(); @@ -157,18 +166,36 @@ std::set &expandedtemplates); /** + * Simplify templates : add namespace to template name + * @param templateDeclaration template declaration + * @param tok place to insert namespace + */ + void addNamespace(const TokenAndName &templateDeclaration, const Token *tok); + + /** + * Simplify templates : check if namespace already present + * @param templateDeclaration template declaration + * @param tok place to start looking for namespace + * @return true if namespace already present + */ + bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) const; + + /** * Expand a template. Create "expanded" class/function at end of tokenlist. - * @param fullName Full name of template + * @param templateDeclaration Template declaration information + * @param templateDeclarationToken Template declaration token + * @param templateInstantiation Full name of template * @param typeParametersInDeclaration The type parameters of the template * @param newName New name of class/function. - * @param typesUsedInTemplateInstantiation Type parameters in instantiation + * @param copy copy or expand in place */ void expandTemplate( + const TokenAndName &templateDeclaration, const Token *templateDeclarationToken, - const std::string &fullName, + const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation); + bool copy); /** * Replace all matching template usages 'Foo < int >' => 'Foo' @@ -176,19 +203,11 @@ * @param templateName full template name with scope info * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param newName The new type name - * @param typesUsedInTemplateInstantiation template instantiation parameters */ void replaceTemplateUsage(Token *const instantiationToken, const std::string &templateName, const std::list &typeStringsUsedInTemplateInstantiation, - const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation); - - /** - * Expand specialized templates : "template<>.." - * @return names of expanded templates - */ - std::set expandSpecialized(); + const std::string &newName); /** * @brief TemplateParametersInDeclaration @@ -199,7 +218,7 @@ * @return template < typename T, typename S > * ^ return */ - const Token * getTemplateParametersInDeclaration( + static const Token * getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration); @@ -211,7 +230,7 @@ /** Syntax error */ static void syntaxError(const Token *tok); - bool matchSpecialization( + static bool matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations); @@ -228,16 +247,29 @@ * tok will be invalidated. * @param tok token to delete */ - void deleteToken(Token *tok); + static void deleteToken(Token *tok); + + /** + * Get the new token name. + * @param tok name token + * @param &typeStringsUsedInTemplateInstantiation type strings use in template instantiation + * @return new token name + */ + std::string getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation); TokenList &mTokenList; const Settings *mSettings; ErrorLogger *mErrorLogger; std::list mTemplateDeclarations; + std::list mTemplateForwardDeclarations; + std::map mTemplateForwardDeclarationsMap; std::list mTemplateInstantiations; std::list mInstantiatedTemplates; std::list mMemberFunctionsToDelete; + std::vector mTypesUsedInTemplateInstantiation; }; /// @} diff -Nru cppcheck-1.85/lib/token.cpp cppcheck-1.86/lib/token.cpp --- cppcheck-1.85/lib/token.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/token.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -878,6 +878,38 @@ return const_cast(const_cast(this)->findClosingBracket()); } +const Token * Token::findOpeningBracket() const +{ + if (mStr != ">") + return nullptr; + + const Token *opening = nullptr; + + unsigned int depth = 0; + for (opening = this; opening != nullptr; opening = opening->previous()) { + if (Token::Match(opening, "}|]|)")) { + opening = opening->link(); + if (!opening) + return nullptr; + } else if (Token::Match(opening, "{|{|(|;")) + return nullptr; + else if (opening->str() == ">") + ++depth; + else if (opening->str() == "<") { + if (--depth == 0) + return opening; + } + } + + return opening; +} + +Token * Token::findOpeningBracket() +{ + // return value of const function + return const_cast(const_cast(this)->findOpeningBracket()); +} + //--------------------------------------------------------------------------- const Token *Token::findsimplematch(const Token * const startTok, const char pattern[]) @@ -918,10 +950,6 @@ void Token::insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend) { - //TODO: Find a solution for the first token on the list - if (prepend && !this->previous()) - return; - Token *newToken; if (mStr.empty()) newToken = this; @@ -937,12 +965,12 @@ newToken->mProgressValue = mProgressValue; if (prepend) { - /*if (this->previous())*/ { + if (this->previous()) { newToken->previous(this->previous()); newToken->previous()->next(newToken); - } /*else if (tokensFront?) { - *tokensFront? = newToken; - }*/ + } else if (mTokensFrontBack) { + mTokensFrontBack->front = newToken; + } this->previous(newToken); newToken->next(this); } else { @@ -1134,6 +1162,62 @@ mAstOperand2 = tok; } +static const Token* goToLeftParenthesis(const Token* start, const Token* end) +{ + // move start to lpar in such expression: '(*it).x' + int par = 0; + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + if (tok->str() == "(") + ++par; + else if (tok->str() == ")") { + if (par == 0) + start = tok->link(); + else + --par; + } + } + return start; +} + +static const Token* goToRightParenthesis(const Token* start, const Token* end) +{ + // move end to rpar in such expression: '2>(x+1)' + int par = 0; + for (const Token *tok = end; tok && tok != start; tok = tok->previous()) { + if (tok->str() == ")") + ++par; + else if (tok->str() == "(") { + if (par == 0) + end = tok->link(); + else + --par; + } + } + return end; +} + +std::pair Token::findExpressionStartEndTokens() const +{ + const Token * const top = this; + const Token *start = top; + while (start->astOperand1() && + (start->astOperand2() || !start->isUnaryPreOp() || Token::simpleMatch(start, "( )") || start->str() == "{")) + start = start->astOperand1(); + const Token *end = top; + while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { + if (Token::Match(end,"(|[") && + !(Token::Match(end, "( %type%") && !end->astOperand2())) { + end = end->link(); + break; + } + end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); + } + + start = goToLeftParenthesis(start, end); + end = goToRightParenthesis(start, end); + return std::pair(start,end); +} + bool Token::isCalculation() const { if (!Token::Match(this, "%cop%|++|--")) @@ -1189,40 +1273,6 @@ return false; // <- guess } -static const Token* goToLeftParenthesis(const Token* start, const Token* end) -{ - // move start to lpar in such expression: '(*it).x' - int par = 0; - for (const Token *tok = start; tok && tok != end; tok = tok->next()) { - if (tok->str() == "(") - ++par; - else if (tok->str() == ")") { - if (par == 0) - start = tok->link(); - else - --par; - } - } - return start; -} - -static const Token* goToRightParenthesis(const Token* start, const Token* end) -{ - // move end to rpar in such expression: '2>(x+1)' - int par = 0; - for (const Token *tok = end; tok && tok != start; tok = tok->previous()) { - if (tok->str() == ")") - ++par; - else if (tok->str() == "(") { - if (par == 0) - end = tok->link(); - else - --par; - } - } - return end; -} - static std::string stringFromTokenRange(const Token* start, const Token* end) { std::ostringstream ret; @@ -1245,25 +1295,8 @@ std::string Token::expressionString() const { - const Token * const top = this; - const Token *start = top; - while (start->astOperand1() && - (start->astOperand2() || !start->isUnaryPreOp() || Token::simpleMatch(start, "( )") || start->str() == "{")) - start = start->astOperand1(); - const Token *end = top; - while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { - if (Token::Match(end,"(|[") && - !(Token::Match(end, "( %type%") && !end->astOperand2())) { - end = end->link(); - break; - } - end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); - } - - start = goToLeftParenthesis(start, end); - end = goToRightParenthesis(start, end); - - return stringFromTokenRange(start, end); + const auto tokens = findExpressionStartEndTokens(); + return stringFromTokenRange(tokens.first, tokens.second); } static void astStringXml(const Token *tok, std::size_t indent, std::ostream &out) @@ -1399,6 +1432,9 @@ case ValueFlow::Value::CONTAINER_SIZE: out << "container-size=\"" << value.intvalue << '\"'; break; + case ValueFlow::Value::LIFETIME: + out << "lifetime=\"" << value.tokvalue << '\"'; + break; } if (value.condition) out << " condition-line=\"" << value.condition->linenr() << '\"'; @@ -1436,6 +1472,9 @@ case ValueFlow::Value::CONTAINER_SIZE: out << "size=" << value.intvalue; break; + case ValueFlow::Value::LIFETIME: + out << "lifetime=" << value.tokvalue->str(); + break; } } } @@ -1601,8 +1640,10 @@ bool Token::addValue(const ValueFlow::Value &value) { if (value.isKnown() && mValues) { - // Clear all other values since value is known - mValues->clear(); + // Clear all other values of the same type since value is known + mValues->remove_if([&](const ValueFlow::Value & x) { + return x.valueType == value.valueType; + }); } if (mValues) { @@ -1621,7 +1662,7 @@ // different types => continue if (it->valueType != value.valueType) continue; - if (value.isTokValue() && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str())) + if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str())) continue; // same value, but old value is inconclusive so replace it @@ -1641,7 +1682,10 @@ ValueFlow::Value v(value); if (v.varId == 0) v.varId = mVarId; - mValues->push_back(v); + if (v.isKnown() && v.isIntValue()) + mValues->push_front(v); + else + mValues->push_back(v); } } else { ValueFlow::Value v(value); diff -Nru cppcheck-1.85/lib/token.h cppcheck-1.86/lib/token.h --- cppcheck-1.85/lib/token.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/token.h 2018-12-08 07:18:21.000000000 +0000 @@ -25,7 +25,9 @@ #include "mathlib.h" #include "valueflow.h" +#include #include +#include #include #include #include @@ -446,6 +448,12 @@ void isEnumType(const bool value) { setFlag(fIsEnumType, value); } + bool isAtAddress() const { + return getFlag(fAtAddress); + } + void isAtAddress(bool b) { + setFlag(fAtAddress, b); + } bool isBitfield() const { return mBits > 0; @@ -812,6 +820,9 @@ const Token* findClosingBracket() const; Token* findClosingBracket(); + const Token* findOpeningBracket() const; + Token* findOpeningBracket(); + /** * @return the original name. */ @@ -835,11 +846,11 @@ } bool hasKnownIntValue() const { - return mValues && mValues->size() == 1U && mValues->front().isKnown() && mValues->front().isIntValue(); + return hasKnownValue() && std::any_of(mValues->begin(), mValues->end(), std::mem_fn(&ValueFlow::Value::isIntValue)); } bool hasKnownValue() const { - return mValues && mValues->size() == 1U && mValues->front().isKnown(); + return mValues && std::any_of(mValues->begin(), mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown)); } const ValueFlow::Value * getValue(const MathLib::bigint val) const { @@ -979,6 +990,7 @@ fIsTemplateArg = (1 << 22), fIsAttributeNodiscard = (1 << 23), // __attribute__ ((warn_unused_result)), [[nodiscard]] fHasTemplateSimplifierPointer = (1 << 24), // used by template simplifier to indicate it has a pointer to this token + fAtAddress = (1 << 25), // @ 0x4000 }; unsigned int mFlags; @@ -1046,6 +1058,8 @@ return ret; } + std::pair findExpressionStartEndTokens() const; + /** * Is current token a calculation? Only true for operands. * For '*' and '&' tokens it is looked up if this is a diff -Nru cppcheck-1.85/lib/tokenize.cpp cppcheck-1.86/lib/tokenize.cpp --- cppcheck-1.85/lib/tokenize.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/tokenize.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -1501,13 +1501,11 @@ } } else if (typeOf) { tok2 = TokenList::copyTokens(tok2, argStart, argEnd); - } else if (tok2->strAt(2) == "[") { - do { - if (!tok2->linkAt(2)) - syntaxError(tok2); - - tok2 = tok2->linkAt(2)->previous(); - } while (tok2->strAt(2) == "["); + } else if (Token::Match(tok2, "%name% [")) { + while (Token::Match(tok2, "%name%|] [")) { + tok2 = tok2->linkAt(1); + } + tok2 = tok2->previous(); } if (arrayStart && arrayEnd) { @@ -2345,6 +2343,9 @@ } } else if (!c && ((TemplateSimplifier::templateParameters(tok2) > 0) || Token::simpleMatch(tok2, "< >") /* Ticket #4764 */)) { + const Token *start = *tok; + if (Token::Match(start->previous(), "%or%|%oror%|&&|&|^|+|-|*|/")) + return false; const Token * tok3 = tok2->findClosingBracket(); if (tok3 == nullptr) { /* Ticket #8151 */ throw tok2; @@ -2353,6 +2354,14 @@ if (tok2->str() != ">") break; singleNameCount = 1; + if (Token::Match(tok2, "> %name% %or%|%oror%|&&|&|^|+|-|*|/")) + return false; + if (Token::Match(tok2, "> %name% )")) { + if (Token::Match(tok2->linkAt(2)->previous(), "if|for|while (")) + return false; + if (!Token::Match(tok2->linkAt(2)->previous(), "%name% (")) + return false; + } } else if (Token::Match(tok2, "&|&&")) { ref = !bracket; } else if (singleNameCount == 1 && Token::Match(tok2, "( [*&]") && Token::Match(tok2->link()->next(), "(|[")) { @@ -2440,12 +2449,15 @@ return; } - while (Token::Match(tok->next(), ". %name% !!(")) { + while (Token::Match(tok->next(), ")| . %name% !!(")) { const unsigned int struct_varid = tok->varId(); tok = tok->tokAt(2); if (struct_varid == 0) continue; + if (tok->str() == ".") + tok = tok->next(); + // Don't set varid for template function if (TemplateSimplifier::templateParameters(tok->next()) > 0) break; @@ -3081,8 +3093,13 @@ if (tok2->strAt(-1) == ")" || tok2->strAt(-2) == ")") setVarIdClassFunction(scopeName2 + classname, tok2, tok2->link(), thisClassVars, structMembers, &mVarId); tok2 = tok2->link(); - } else if (tok2->str() == "(" && tok2->link()->strAt(1) != "(") + } else if (tok2->str() == "(" && tok2->link()->strAt(1) != "(") { tok2 = tok2->link(); + + // Skip initialization list + while (Token::Match(tok2, ") [:,] %name% (")) + tok2 = tok2->linkAt(3); + } } // Found a member variable.. @@ -3232,10 +3249,12 @@ if (Token::Match(token, "{|[|(")) type.push(token); else if (!type.empty() && Token::Match(token, "}|]|)")) { - while (type.top()->str() == "<") + while (type.top()->str() == "<") { + if (templateToken && templateToken->next() == type.top()) + templateToken = nullptr; type.pop(); + } type.pop(); - templateToken = nullptr; } else token->link(nullptr); } else if (!templateToken && !isStruct && Token::Match(token, "%oror%|&&|;")) { @@ -3326,7 +3345,7 @@ for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "sizeof !!(")) continue; - if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!")) { + if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!|&")) { Token *endToken = tok->next(); while (Token::simpleMatch(endToken, "* *")) endToken = endToken->next(); @@ -3788,6 +3807,20 @@ // Put ^{} statements in asm() simplifyAsm2(); + // @.. + simplifyAt(); + + // When the assembly code has been cleaned up, no @ is allowed + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "(") { + tok = tok->link(); + if (!tok) + syntaxError(nullptr); + } else if (tok->str() == "@") { + syntaxError(nullptr); + } + } + // Order keywords "static" and "const" simplifyStaticConst(); @@ -3831,11 +3864,6 @@ simplifyRedundantParentheses(); if (!isC()) { - // TODO: Only simplify template parameters - for (Token *tok = list.front(); tok; tok = tok->next()) - while (mTemplateSimplifier->simplifyNumericCalculations(tok)) - ; - // Handle templates.. simplifyTemplates(); @@ -4194,12 +4222,15 @@ tok->deleteNext(); } - if ((!tok->previous() || Token::Match(tok->previous(), "[;{}]")) && - Token::Match(tok, "%type%") && tok->isUpperCaseName()) { + if (Token::Match(tok, "%type%") && tok->isUpperCaseName() && + (!tok->previous() || Token::Match(tok->previous(), "[;{}]") || (tok->previous()->isName() && endsWith(tok->previous()->str(), ':')))) { const Token *tok2 = tok->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); + if (Token::Match(tok, "%type% (") && Token::Match(tok2, "%type% (") && !Token::Match(tok2, "noexcept|throw") && isFunctionHead(tok2->next(), ":;{")) + unknownMacroError(tok); + // remove unknown macros before namespace|class|struct|union if (Token::Match(tok2, "namespace|class|struct|union")) { // is there a "{" for? @@ -4214,14 +4245,15 @@ } // replace unknown macros before foo( - if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { - std::string typeName; - for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) - typeName += tok3->str(); - Token::eraseTokens(tok, tok2); - tok->str(typeName); - } - + /* + if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { + std::string typeName; + for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) + typeName += tok3->str(); + Token::eraseTokens(tok, tok2); + tok->str(typeName); + } + */ // remove unknown macros before foo::foo( if (Token::Match(tok2, "%type% :: %type%")) { const Token *tok3 = tok2; @@ -4813,6 +4845,8 @@ for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "[;{}] (| *| (| %name%")) continue; + if (tok->next()->str() == "return") + continue; // backup current token.. Token * const tok1 = tok; @@ -7910,6 +7944,12 @@ throw InternalError(tok, "Code '"+what+"' is invalid C code. Use --std or --language to configure the language.", InternalError::SYNTAX); } +void Tokenizer::unknownMacroError(const Token *tok1) const +{ + printDebugOutput(0); + throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO); +} + void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const { reportError(tok, @@ -8406,6 +8446,18 @@ void Tokenizer::findGarbageCode() const { + // Inside [] there can't be ; or various keywords + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (tok->str() != "[") + continue; + for (const Token *inner = tok->next(); inner != tok->link(); inner = inner->next()) { + if (Token::Match(inner, "(|[")) + inner = inner->link(); + else if (Token::Match(inner, ";|goto|return|typedef")) + syntaxError(inner); + } + } + for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "if|while|for|switch")) { // if|while|for|switch (EXPR) { ... } if (tok->previous() && !Token::Match(tok->previous(), "%name%|:|;|{|}|(|)|,")) @@ -8486,6 +8538,12 @@ if (Token::Match(tok, "> %cop%")) continue; } + if (Token::Match(tok, "%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|++|--|::|sizeof|throw|decltype|typeof {|if|else|try|catch|while|do|for|return|switch|break|namespace")) + syntaxError(tok); + if (Token::Match(tok, "( %any% )") && tok->next()->isKeyword() && !Token::simpleMatch(tok->next(), "void")) + syntaxError(tok); + if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%")) + syntaxError(tok); if (Token::Match(tok, "%assign% typename|class %assign%")) syntaxError(tok); if (Token::Match(tok, "%cop%|=|,|[ %or%|%oror%|/|%")) @@ -8494,6 +8552,8 @@ syntaxError(tok); if (Token::Match(tok, "%cop%|= ]") && !(isCPP() && Token::Match(tok->previous(), "[|, &|= ]"))) syntaxError(tok); + if (Token::Match(tok, "[+-] [;,)]}]")) + syntaxError(tok); } // ternary operator without : @@ -8728,7 +8788,7 @@ continue; // check for anonymous struct/union if (Token::Match(tok, "struct|union {")) { - if (Token::Match(tok->next()->link(), "} *|&| %type% ,|;|[|(|{")) { + if (Token::Match(tok->next()->link(), "} *|&| %type% ,|;|[|(|{|=")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } @@ -9047,7 +9107,7 @@ , "__restrict__" , "__thread" }; -// Remove "inline", "register", "restrict", "override", "final", "static" and "constexpr" +// Remove "inline", "register", "restrict", "override", "static" and "constexpr" // "restrict" keyword // - New to 1999 ANSI/ISO C standard // - Not in C++ standard yet @@ -9059,6 +9119,9 @@ // linux kernel code at least uses "_inline" as struct member name at some // places. + const bool c99 = isC() && mSettings->standards.c >= Standards::C99; + const bool cpp11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; + for (Token *tok = list.front(); tok; tok = tok->next()) { if (keywords.find(tok->str()) != keywords.end()) { // Don't remove struct members @@ -9071,33 +9134,30 @@ tok->deleteThis(); } + // simplify static keyword: + // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); + if (Token::Match(tok, "[ static %num%")) + tok->deleteNext(); - if (mSettings->standards.c >= Standards::C99) { - while (tok->str() == "restrict") { + if (c99) { + while (tok->str() == "restrict") tok->deleteThis(); - } - // simplify static keyword: - // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); - if (Token::Match(tok, "[ static %num%")) { - tok->deleteNext(); - } - } - - if (mSettings->standards.c >= Standards::C11) { - while (tok->str() == "_Atomic") { - tok->deleteThis(); + if (mSettings->standards.c >= Standards::C11) { + while (tok->str() == "_Atomic") + tok->deleteThis(); } } - if (isCPP() && mSettings->standards.cpp >= Standards::CPP11) { - while (tok->str() == "constexpr") { - tok->deleteThis(); + else if (cpp11) { + if (tok->str() == "constexpr") { + tok->originalName(tok->str()); + tok->str("const"); } // final: // 1) struct name final { }; <- struct is final - if (Token::Match(tok, "%type% final [:{]")) { + if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) { tok->deleteNext(); } @@ -9321,15 +9381,34 @@ } } } +} - // When the assembly code has been cleaned up, no @ is allowed - for (const Token *tok = list.front(); tok; tok = tok->next()) { - if (tok->str() == "(") { - tok = tok->link(); - if (!tok) - syntaxError(nullptr); - } else if (tok->str()[0] == '@') { - syntaxError(nullptr); +void Tokenizer::simplifyAt() +{ + std::set var; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% @ %num% ;")) { + var.insert(tok->str()); + tok->isAtAddress(true); + Token::eraseTokens(tok,tok->tokAt(3)); + } + if (Token::Match(tok, "%name% @ %num% : %num% ;")) { + var.insert(tok->str()); + tok->isAtAddress(true); + Token::eraseTokens(tok,tok->tokAt(5)); + } + if (Token::Match(tok, "%name% @ %name% : %num% ;") && var.find(tok->strAt(2)) != var.end()) { + var.insert(tok->str()); + tok->isAtAddress(true); + Token::eraseTokens(tok,tok->tokAt(5)); + } + + // keywords in compiler from cosmic software for STM8 + // TODO: Should use platform configuration. + if (Token::Match(tok, "@ builtin|eeprom|far|inline|interrupt|near|noprd|nostack|nosvf|packed|stack|svlreg|tiny|vector")) { + tok->str(tok->next()->str() + "@"); + tok->deleteNext(); } } } diff -Nru cppcheck-1.85/lib/tokenize.h cppcheck-1.86/lib/tokenize.h --- cppcheck-1.85/lib/tokenize.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/tokenize.h 2018-12-08 07:18:21.000000000 +0000 @@ -598,6 +598,9 @@ /** Syntax error. C++ code in C file. */ void syntaxErrorC(const Token *tok, const std::string &what) const; + /** Warn about unknown macro(s), configuration is recommended */ + void unknownMacroError(const Token *tok1) const; + private: /** Report that there is an unhandled "class x y {" code */ @@ -655,6 +658,11 @@ void simplifyAsm2(); /** + * Simplify @.. + */ + void simplifyAt(); + + /** * Simplify bitfields - the field width is removed as we don't use it. */ void simplifyBitfields(); diff -Nru cppcheck-1.85/lib/tokenlist.cpp cppcheck-1.86/lib/tokenlist.cpp --- cppcheck-1.85/lib/tokenlist.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/tokenlist.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -266,7 +266,7 @@ void TokenList::createTokens(const simplecpp::TokenList *tokenList) { if (tokenList->cfront()) - mFiles = tokenList->cfront()->location.files; + mOrigFiles = mFiles = tokenList->cfront()->location.files; else mFiles.clear(); @@ -362,7 +362,7 @@ unsigned int inArrayAssignment; bool cpp; unsigned int assign; - bool inCase; + bool inCase; // true from case to : explicit AST_state(bool cpp_) : depth(0), inArrayAssignment(0), cpp(cpp_), assign(0U), inCase(false) {} }; @@ -414,10 +414,15 @@ bool type = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->varId() != 0) + return false; + while (tok2->link() && Token::Match(tok2, "(|[|<")) tok2 = tok2->link()->next(); if (tok2->str() == ")") { + if (Token::simpleMatch(tok2, ") (") && Token::simpleMatch(tok2->linkAt(1), ") .")) + return true; return type || tok2->strAt(-1) == "*" || Token::simpleMatch(tok2, ") ~") || (Token::Match(tok2, ") %any%") && !tok2->next()->isOp() && @@ -563,8 +568,10 @@ state.inCase = true; compileUnaryOp(tok, state, compileExpression); state.op.pop(); - if (state.inCase && Token::simpleMatch(tok, ": ;")) + if (state.inCase && Token::simpleMatch(tok, ": ;")) { + state.inCase = false; tok = tok->next(); + } } else if (Token::Match(tok, "sizeof !!(")) { compileUnaryOp(tok, state, compileExpression); state.op.pop(); @@ -680,8 +687,12 @@ if (Token::simpleMatch(squareBracket->link(), "] (")) { Token* const roundBracket = squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); - while (Token::Match(curlyBracket, "%name%|.|::|&")) + if (Token::Match(curlyBracket, "mutable|const")) curlyBracket = curlyBracket->next(); + if (curlyBracket && curlyBracket->originalName() == "->") { + while (Token::Match(curlyBracket, "%name%|.|::|&|*")) + curlyBracket = curlyBracket->next(); + } if (curlyBracket && curlyBracket->str() == "{") { squareBracket->astOperand1(roundBracket); roundBracket->astOperand1(curlyBracket); @@ -751,10 +762,10 @@ } compileUnaryOp(tok, state, compilePrecedence3); } else if (tok->str() == "(" && iscast(tok)) { - Token* tok2 = tok; + Token* castTok = tok; tok = tok->link()->next(); compilePrecedence3(tok, state); - compileUnaryOp(tok2, state, nullptr); + compileUnaryOp(castTok, state, nullptr); } else if (state.cpp && Token::Match(tok, "new %name%|::|(")) { Token* newtok = tok; tok = tok->next(); @@ -956,8 +967,11 @@ compileBinOp(tok, state, compileAssignTernary); state.assign = assign; } else if (tok->str() == ":") { - if (state.depth == 1U && state.inCase) + if (state.depth == 1U && state.inCase) { + state.inCase = false; + tok = tok->next(); break; + } if (state.assign > 0U) break; compileBinOp(tok, state, compileAssignTernary); @@ -1211,7 +1225,7 @@ // Skip lambda captures if (Token::Match(tok, "= ,|]")) continue; - // Dont check templates + // Don't check templates if (tok->link()) continue; // Skip pure virtual functions @@ -1229,18 +1243,17 @@ // FIXME: Workaround broken AST assignment in type aliases if (Token::Match(tok->previous(), "%name% = %name%")) continue; - // FIXME: Workaround when assigning from a new expression: #8749 - if (Token::simpleMatch(tok, "= new")) - continue; - // FIXME: Workaround assigning when using c style cast: #8786 - if (Token::Match(tok, "= ( %name%")) - continue; if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, binary operator '" + tok->str() + "' doesn't have two operands.", InternalError::AST); } } } +std::string TokenList::getOrigFile(const Token *tok) const +{ + return mOrigFiles.at(tok->fileIndex()); +} + const std::string& TokenList::file(const Token *tok) const { return mFiles.at(tok->fileIndex()); diff -Nru cppcheck-1.85/lib/tokenlist.h cppcheck-1.86/lib/tokenlist.h --- cppcheck-1.85/lib/tokenlist.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/tokenlist.h 2018-12-08 07:18:21.000000000 +0000 @@ -128,6 +128,8 @@ return mFiles; } + std::string getOrigFile(const Token *tok) const; + /** * get filename for given token * @param tok The given token @@ -187,6 +189,9 @@ /** filenames for the tokenized source code (source + included) */ std::vector mFiles; + /** Original filenames for the tokenized source code (source + included) */ + std::vector mOrigFiles; + /** settings */ const Settings* mSettings; diff -Nru cppcheck-1.85/lib/valueflow.cpp cppcheck-1.86/lib/valueflow.cpp --- cppcheck-1.85/lib/valueflow.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/valueflow.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -100,6 +100,8 @@ #include #include +static const int TIMEOUT = 10; // Do not repeat ValueFlow analysis more than 10 seconds + namespace { struct ProgramMemory { std::map values; @@ -282,26 +284,37 @@ continue; // Is variable protected in LHS.. - std::stack tokens; - tokens.push(tok->astOperand1()); - while (!tokens.empty()) { - const Token * const tok2 = tokens.top(); - tokens.pop(); - if (!tok2 || tok2->str() == ".") - continue; + bool bailout = false; + visitAstNodes(tok->astOperand1(), [&](const Token *tok2) { + if (tok2->str() == ".") + return ChildrenToVisit::none; // A variable is seen.. if (tok2 != valuetok && tok2->variable() && (tok2->varId() == valuetok->varId() || !tok2->variable()->isArgument())) { // TODO: limit this bailout - return tok; + bailout = true; + return ChildrenToVisit::done; } - tokens.push(tok2->astOperand2()); - tokens.push(tok2->astOperand1()); - } - + return ChildrenToVisit::op1_and_op2; + }); + if (bailout) + return tok; } return nullptr; } +static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false) +{ + if (!Token::simpleMatch(tok, "{")) + return false; + const Token * termTok = Token::findmatch(tok, "return|continue|break|throw|goto", tok->link()); + if (termTok && termTok->scope() == tok->scope()) + return true; + std::string unknownFunction; + if (tokenlist && tokenlist->getSettings()->library.isScopeNoReturn(tok->link(), &unknownFunction)) + return unknownFunction.empty() || unknown; + return false; +} + static bool bailoutSelfAssignment(const Token * const tok) { const Token *parent = tok; @@ -345,6 +358,20 @@ return value; } +static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result) +{ + if (value1.isKnown() && value2.isKnown()) + result->setKnown(); + else if (value1.isInconclusive() || value2.isInconclusive()) + result->setInconclusive(); + else + result->setPossible(); + result->condition = value1.condition ? value1.condition : value2.condition; + result->varId = (value1.varId != 0U) ? value1.varId : value2.varId; + result->varvalue = (result->varId == value1.varId) ? value1.varvalue : value2.varvalue; + result->errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; +} + /** set ValueFlow value and perform calculations if possible */ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Settings *settings) { @@ -359,6 +386,58 @@ if (!parent) return; + if (value.isContainerSizeValue()) { + // .empty, .size, +"abc", +'a' + if (parent->str() == "+") { + for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { + for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { + ValueFlow::Value result; + result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + if (value1.isContainerSizeValue() && value2.isContainerSizeValue()) + result.intvalue = value1.intvalue + value2.intvalue; + else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString) + result.intvalue = value1.intvalue + Token::getStrLength(value2.tokvalue); + else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString) + result.intvalue = Token::getStrLength(value1.tokvalue) + value2.intvalue; + else + continue; + + combineValueProperties(value1, value2, &result); + + setTokenValue(parent, result, settings); + } + } + } + + + else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) && parent->astOperand1() && parent->astOperand1()->valueType()) { + const Library::Container *c = parent->astOperand1()->valueType()->container; + const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD; + if (yields == Library::Container::Yield::SIZE) { + ValueFlow::Value v(value); + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(const_cast(parent->astParent()), v, settings); + } else if (yields == Library::Container::Yield::EMPTY) { + ValueFlow::Value v(value); + v.intvalue = !v.intvalue; + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(const_cast(parent->astParent()), v, settings); + } + } + + return; + } + + if (value.isLifetimeValue()) { + if (value.lifetimeKind == ValueFlow::Value::Iterator && astIsIterator(parent)) { + setTokenValue(parent,value,settings); + } else if (astIsPointer(tok) && astIsPointer(parent) && + (parent->isArithmeticalOp() || Token::Match(parent, "( %type%"))) { + setTokenValue(parent,value,settings); + } + return; + } + if (parent->str() == "(" && !parent->astOperand2() && Token::Match(parent,"( %name%")) { const ValueType &valueType = ValueType::parseDecl(parent->next(), settings); if (valueType.pointer) @@ -404,23 +483,20 @@ } } else { // is condition only depending on 1 variable? - std::stack tokens; - tokens.push(parent->astOperand1()); unsigned int varId = 0; - while (!tokens.empty()) { - const Token *t = tokens.top(); - tokens.pop(); - if (!t) - continue; - tokens.push(t->astOperand1()); - tokens.push(t->astOperand2()); + bool ret = false; + visitAstNodes(parent->astOperand1(), + [&](const Token *t) { if (t->varId()) { if (varId > 0 || value.varId != 0U) - return; + ret = true; varId = t->varId(); } else if (t->str() == "(" && Token::Match(t->previous(), "%name%")) - return; // function call - } + ret = true; // function call + return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (ret) + return; ValueFlow::Value v(value); v.conditional = true; @@ -471,13 +547,7 @@ if (known || value1.varId == 0U || value2.varId == 0U || (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())) { ValueFlow::Value result(0); - result.condition = value1.condition ? value1.condition : value2.condition; - result.setInconclusive(value1.isInconclusive() | value2.isInconclusive()); - result.varId = (value1.varId != 0U) ? value1.varId : value2.varId; - result.varvalue = (result.varId == value1.varId) ? value1.varvalue : value2.varvalue; - result.errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; - if (value1.valueKind == value2.valueKind) - result.valueKind = value1.valueKind; + combineValueProperties(value1, value2, &result); const float floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue; const float floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue; switch (parent->str()[0]) { @@ -1037,6 +1107,197 @@ } } +static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, const Settings *settings) +{ + const bool cpp = symboldatabase->isCPP(); + typedef std::pair Condition; + for (const Scope * scope : symboldatabase->functionScopes) { + std::vector conds; + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "if (")) + continue; + // Skip known values + if (tok->next()->hasKnownValue()) + continue; + const Token * condTok = tok->next(); + if (!Token::simpleMatch(condTok->link(), ") {")) + continue; + const Token * blockTok = condTok->link()->tokAt(1); + // Check if the block terminates early + if (!isEscapeScope(blockTok, tokenlist)) + continue; + // Check if any variables are modified in scope + bool bail = false; + for (const Token * tok2=condTok->next(); tok2 != condTok->link(); tok2 = tok2->next()) { + const Variable * var = tok2->variable(); + if (!var) + continue; + if (!var->scope()) + continue; + const Token * endToken = var->scope()->bodyEnd; + if (!var->isLocal() && !var->isConst() && !var->isArgument()) { + bail = true; + break; + } + if (var->isStatic() && !var->isConst()) { + bail = true; + break; + } + if (!var->isConst() && var->declEndToken() && isVariableChanged(var->declEndToken()->next(), endToken, tok2->varId(), false, settings, cpp)) { + bail = true; + break; + } + } + if (bail) + continue; + // TODO: Handle multiple conditions + if (Token::Match(condTok->astOperand2(), "%oror%|%or%|&|&&")) + continue; + const Scope * condScope = nullptr; + for (const Scope * parent = condTok->scope(); parent; parent = parent->nestedIn) { + if (parent->type == Scope::eIf || + parent->type == Scope::eWhile || + parent->type == Scope::eSwitch) { + condScope = parent; + break; + } + } + conds.emplace_back(condTok->astOperand2(), condScope); + + } + for (Condition cond:conds) { + if (!cond.first) + continue; + Token *const startToken = cond.first->findExpressionStartEndTokens().second->next(); + for (Token* tok = startToken; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%comp%")) + continue; + // Skip known values + if (tok->hasKnownValue()) + continue; + if (cond.second) { + bool bail = true; + for (const Scope * parent = tok->scope()->nestedIn; parent; parent = parent->nestedIn) { + if (parent == cond.second) { + bail = false; + break; + } + } + if (bail) + continue; + } + ErrorPath errorPath; + if (isOppositeCond(true, cpp, tok, cond.first, settings->library, true, true, &errorPath)) { + ValueFlow::Value val(1); + val.setKnown(); + val.condition = cond.first; + val.errorPath = errorPath; + val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false"); + setTokenValue(tok, val, tokenlist->getSettings()); + } else if (isSameExpression(cpp, true, tok, cond.first, settings->library, true, true, &errorPath)) { + ValueFlow::Value val(0); + val.setKnown(); + val.condition = cond.first; + val.errorPath = errorPath; + val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false"); + setTokenValue(tok, val, tokenlist->getSettings()); + } + } + } + } +} + +static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) +{ + if (expr->hasKnownIntValue()) { + if (minvalue) + *minvalue = expr->values().front().intvalue; + if (maxvalue) + *maxvalue = expr->values().front().intvalue; + return true; + } + + if (expr->str() == "&" && expr->astOperand1() && expr->astOperand2()) { + MathLib::bigint vals[4]; + bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); + bool rhsHasKnownRange = getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]); + if (!lhsHasKnownRange && !rhsHasKnownRange) + return false; + if (!lhsHasKnownRange || !rhsHasKnownRange) { + if (minvalue) + *minvalue = lhsHasKnownRange ? vals[0] : vals[2]; + if (maxvalue) + *maxvalue = lhsHasKnownRange ? vals[1] : vals[3]; + } else { + if (minvalue) + *minvalue = vals[0] & vals[2]; + if (maxvalue) + *maxvalue = vals[1] & vals[3]; + } + return true; + } + + if (expr->str() == "%" && expr->astOperand1() && expr->astOperand2()) { + MathLib::bigint vals[4]; + if (!getExpressionRange(expr->astOperand2(), &vals[2], &vals[3])) + return false; + if (vals[2] <= 0) + return false; + bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); + if (lhsHasKnownRange && vals[0] < 0) + return false; + // If lhs has unknown value, it must be unsigned + if (!lhsHasKnownRange && (!expr->astOperand1()->valueType() || expr->astOperand1()->valueType()->sign != ValueType::Sign::UNSIGNED)) + return false; + if (minvalue) + *minvalue = 0; + if (maxvalue) + *maxvalue = vals[3] - 1; + return true; + } + + return false; +} + +static void valueFlowRightShift(TokenList *tokenList) +{ + for (Token *tok = tokenList->front(); tok; tok = tok->next()) { + if (tok->str() != ">>") + continue; + + if (tok->hasKnownValue()) + continue; + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + if (!tok->astOperand2()->hasKnownValue()) + continue; + + const MathLib::bigint rhsvalue = tok->astOperand2()->values().front().intvalue; + if (rhsvalue < 0) + continue; + + if (!tok->astOperand1()->valueType() || !tok->astOperand1()->valueType()->isIntegral()) + continue; + + if (!tok->astOperand2()->valueType() || !tok->astOperand2()->valueType()->isIntegral()) + continue; + + MathLib::bigint lhsmax=0; + if (!getExpressionRange(tok->astOperand1(), nullptr, &lhsmax)) + continue; + if (lhsmax < 0) + continue; + if ((1 << rhsvalue) <= lhsmax) + continue; + + ValueFlow::Value val(0); + val.setKnown(); + setTokenValue(tok, val, tokenList->getSettings()); + } +} + static void valueFlowOppositeCondition(SymbolDatabase *symboldatabase, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { @@ -1545,14 +1806,6 @@ return true; } -static bool isEscapeScope(const Token* tok, TokenList * tokenlist) -{ - if (!Token::simpleMatch(tok, "{")) - return false; - return Token::findmatch(tok, "return|continue|break|throw|goto", tok->link()) || - (tokenlist && tokenlist->getSettings()->library.isScopeNoReturn(tok->link(), nullptr)); -} - static bool valueFlowForward(Token * const startToken, const Token * const endToken, const Variable * const var, @@ -1627,9 +1880,12 @@ // skip lambda functions // TODO: handle lambda functions - if (Token::simpleMatch(tok2, "= [")) { - Token *lambdaEndToken = const_cast(findLambdaEndToken(tok2->next())); - if (lambdaEndToken) { + if (tok2->str() == "[" && findLambdaEndToken(tok2)) { + Token *lambdaEndToken = const_cast(findLambdaEndToken(tok2)); + if (isVariableChanged(lambdaEndToken->link(), lambdaEndToken, varid, var->isGlobal(), settings, tokenlist->isCPP())) + return false; + // Dont skip lambdas for lifetime values + if (!std::all_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { tok2 = lambdaEndToken; continue; } @@ -2108,6 +2364,14 @@ const Token *astTop = parent->astTop(); if (Token::simpleMatch(astTop->astOperand1(), "for (")) tok2 = const_cast(astTop->link()); + + // bailout if address of var is taken.. + if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok2, "Taking address of " + tok2->str()); + return false; + } + continue; } @@ -2209,6 +2473,395 @@ return true; } +static const Variable *getLifetimeVariable(const Token *tok, ErrorPath &errorPath) +{ + const Variable *var = tok->variable(); + if (!var) + return nullptr; + if (var->isReference() || var->isRValueReference()) { + for (const ValueFlow::Value &v : tok->values()) { + if (!v.isLifetimeValue()) + continue; + if (v.tokvalue == tok) + continue; + errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end()); + const Variable *var2 = getLifetimeVariable(v.tokvalue, errorPath); + if (var2) + return var2; + } + return nullptr; + } + return var; +} + +static bool isNotLifetimeValue(const ValueFlow::Value& val) +{ + return !val.isLifetimeValue(); +} + +static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); + +static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) +{ + const Token *parent = tok->astParent(); + while (parent && (parent->isArithmeticalOp() || parent->str() == ",")) + parent = parent->astParent(); + if (!parent) + return; + // Assignment + if (parent->str() == "=" && !parent->astParent()) { + // Lhs should be a variable + if (!parent->astOperand1() || !parent->astOperand1()->varId()) + return; + const Variable *var = parent->astOperand1()->variable(); + if (!var || (!var->isLocal() && !var->isGlobal() && !var->isArgument())) + return; + + const Token *const endOfVarScope = var->typeStartToken()->scope()->bodyEnd; + + // Rhs values.. + if (!parent->astOperand2() || parent->astOperand2()->values().empty()) + return; + + if (astIsPointer(parent->astOperand2()) && !var->isPointer() && + !(var->valueType() && var->valueType()->isIntegral())) + return; + + std::list values = parent->astOperand2()->values(); + + // Static variable initialisation? + if (var->isStatic() && var->nameToken() == parent->astOperand1()) + changeKnownToPossible(values); + + // Skip RHS + const Token *nextExpression = nextAfterAstRightmostLeaf(parent); + + // Only forward lifetime values + values.remove_if(&isNotLifetimeValue); + valueFlowForward(const_cast(nextExpression), + endOfVarScope, + var, + var->declarationId(), + values, + false, + false, + tokenlist, + errorLogger, + settings); + // Function call + } else if (Token::Match(parent->previous(), "%name% (")) { + valueFlowLifetimeFunction(const_cast(parent->previous()), tokenlist, errorLogger, settings); + // Variable + } else if (tok->variable()) { + const Variable *var = tok->variable(); + if (!var->typeStartToken() && !var->typeStartToken()->scope()) + return; + const Token *endOfVarScope = var->typeStartToken()->scope()->bodyEnd; + + std::list values = tok->values(); + const Token *nextExpression = nextAfterAstRightmostLeaf(parent); + // Only forward lifetime values + values.remove_if(&isNotLifetimeValue); + valueFlowForward(const_cast(nextExpression), + endOfVarScope, + var, + var->declarationId(), + values, + false, + false, + tokenlist, + errorLogger, + settings); + } +} + +struct LifetimeStore { + const Token *argtok; + std::string message; + ValueFlow::Value::LifetimeKind type; + + template + void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { + ErrorPath errorPath; + const Variable *var = getLifetimeVariable(argtok, errorPath); + if (!var) + return; + if (!pred(var)) + return; + errorPath.emplace_back(argtok, message); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::LIFETIME; + value.tokvalue = var->nameToken(); + value.errorPath = errorPath; + value.lifetimeKind = type; + // Dont add the value a second time + if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) + return; + setTokenValue(tok, value, tokenlist->getSettings()); + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + + void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { + byRef(tok, tokenlist, errorLogger, settings, [](const Variable *) { + return true; + }); + } + + template + void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { + for (const ValueFlow::Value &v : argtok->values()) { + if (!v.isLifetimeValue()) + continue; + const Token *tok3 = v.tokvalue; + ErrorPath errorPath = v.errorPath; + const Variable *var = getLifetimeVariable(tok3, errorPath); + if (!var) + continue; + if (!pred(var)) + return; + errorPath.emplace_back(argtok, message); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::LIFETIME; + value.tokvalue = var->nameToken(); + value.errorPath = errorPath; + value.lifetimeKind = type; + // Dont add the value a second time + if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) + continue; + setTokenValue(tok, value, tokenlist->getSettings()); + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } + + void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { + byVal(tok, tokenlist, errorLogger, settings, [](const Variable *) { + return true; + }); + } + + template + void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { + for (const ValueFlow::Value &v : argtok->values()) { + if (!v.isLifetimeValue()) + continue; + const Token *tok2 = v.tokvalue; + ErrorPath errorPath = v.errorPath; + const Variable *var = getLifetimeVariable(tok2, errorPath); + if (!var) + continue; + for (const Token *tok3 = tok; tok3 && tok3 != var->declEndToken(); tok3 = tok3->previous()) { + if (tok3->varId() == var->declarationId()) { + LifetimeStore{tok3, message, type} .byVal(tok, tokenlist, errorLogger, settings, pred); + break; + } + } + } + } + + void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { + byDerefCopy(tok, tokenlist, errorLogger, settings, [](const Variable *) { + return true; + }); + } +}; + +static const Token *endTemplateArgument(const Token *tok) +{ + for (; tok; tok = tok->next()) { + if (Token::Match(tok, ">|,")) + return tok; + else if (tok->link() && Token::Match(tok, "(|{|[|<")) + tok = tok->link(); + else if (Token::simpleMatch(tok, ";")) + return nullptr; + } + return nullptr; +} + +static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) +{ + if (!Token::Match(tok, "%name% (")) + return; + if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) { + for (const Token *argtok : getArguments(tok)) { + LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::Object} .byRef( + tok->next(), tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok->tokAt(-2), "std :: make_tuple|tuple_cat|make_pair|make_reverse_iterator|next|prev|move")) { + for (const Token *argtok : getArguments(tok)) { + LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::Object} .byVal( + tok->next(), tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok->tokAt(-2), "%var% . push_back|push_front|insert|push|assign") && + astIsContainer(tok->tokAt(-2))) { + const Token *containerTypeTok = tok->tokAt(-2)->valueType()->containerTypeToken; + const Token *endTypeTok = endTemplateArgument(containerTypeTok); + const bool isPointer = endTypeTok && Token::simpleMatch(endTypeTok->previous(), "*"); + Token *vartok = tok->tokAt(-2); + std::vector args = getArguments(tok); + std::size_t n = args.size(); + if (n > 1 && astCanonicalType(args[n - 2]) == astCanonicalType(args[n - 1]) && + (((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) || + (astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) { + LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::Object} .byDerefCopy( + vartok, tokenlist, errorLogger, settings); + } else if (!args.empty() && astIsPointer(args.back()) == isPointer) { + LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::Object} .byVal( + vartok, tokenlist, errorLogger, settings); + } + } +} + +struct Lambda { + explicit Lambda(const Token * tok) + : capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr) { + if (!Token::simpleMatch(tok, "[") || !tok->link()) + return; + capture = tok; + + if (Token::simpleMatch(capture->link(), "] (")) { + arguments = capture->link()->next(); + } + const Token * afterArguments = arguments ? arguments->link()->next() : capture->link()->next(); + if (afterArguments && afterArguments->originalName() == "->") { + returnTok = afterArguments->next(); + bodyTok = Token::findsimplematch(returnTok, "{"); + } else if (Token::simpleMatch(afterArguments, "{")) { + bodyTok = afterArguments; + } + } + + const Token * capture; + const Token * arguments; + const Token * returnTok; + const Token * bodyTok; + + bool isLambda() const { + return capture && bodyTok; + } +}; + +static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings) +{ + for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { + Lambda lam(tok); + // Lamdas + if (lam.isLambda()) { + const Scope * bodyScope = lam.bodyTok->scope(); + + std::set scopes; + + auto isCapturingVariable = [&](const Variable *var) { + const Scope *scope = var->scope(); + if (!scope) + return false; + if (scopes.count(scope) > 0) + return false; + if (scope->isNestedIn(bodyScope)) + return false; + scopes.insert(scope); + return true; + }; + + // TODO: Handle explicit capture + bool captureByRef = Token::Match(lam.capture, "[ & ]"); + bool captureByValue = Token::Match(lam.capture, "[ = ]"); + + for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) { + ErrorPath errorPath; + if (captureByRef) { + LifetimeStore{tok2, "Lambda captures variable by reference here.", ValueFlow::Value::Lambda} .byRef( + tok, tokenlist, errorLogger, settings, isCapturingVariable); + } else if (captureByValue) { + LifetimeStore{tok2, "Lambda captures variable by value here.", ValueFlow::Value::Lambda} .byVal( + tok, tokenlist, errorLogger, settings, isCapturingVariable); + } + } + } + // address of + else if (tok->isUnaryOp("&")) { + ErrorPath errorPath; + // child should be some buffer or variable + const Token *vartok = tok->astOperand1(); + while (vartok) { + if (vartok->str() == "[") + vartok = vartok->astOperand1(); + else if (vartok->str() == "." || vartok->str() == "::") + vartok = vartok->astOperand2(); + else + break; + } + + if (!vartok) + continue; + const Variable * var = getLifetimeVariable(vartok, errorPath); + if (!var) + continue; + if (var->isPointer() && Token::Match(vartok->astParent(), "[|*")) + continue; + + errorPath.emplace_back(tok, "Address of variable taken here."); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::LIFETIME; + value.tokvalue = var->nameToken(); + value.errorPath = errorPath; + setTokenValue(tok, value, tokenlist->getSettings()); + + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + // container lifetimes + else if (tok->variable() && Token::Match(tok, "%var% . begin|cbegin|rbegin|crbegin|end|cend|rend|crend|data|c_str (")) { + ErrorPath errorPath; + const Library::Container * container = settings->library.detectContainer(tok->variable()->typeStartToken()); + if (!container) + continue; + const Variable * var = tok->variable(); + + bool isIterator = !Token::Match(tok->tokAt(2), "data|c_str"); + if (isIterator) + errorPath.emplace_back(tok, "Iterator to container is created here."); + else + errorPath.emplace_back(tok, "Pointer to container is created here."); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::LIFETIME; + value.tokvalue = var->nameToken(); + value.errorPath = errorPath; + value.lifetimeKind = isIterator ? ValueFlow::Value::Iterator : ValueFlow::Value::Object; + setTokenValue(tok->tokAt(3), value, tokenlist->getSettings()); + + valueFlowForwardLifetime(tok->tokAt(3), tokenlist, errorLogger, settings); + + } + // Check function calls + else if (Token::Match(tok, "%name% (")) { + valueFlowLifetimeFunction(tok, tokenlist, errorLogger, settings); + } + // Check variables + else if (tok->variable()) { + ErrorPath errorPath; + const Variable * var = getLifetimeVariable(tok, errorPath); + if (!var) + continue; + if (var->isArray() && !var->isStlType() && !var->isArgument() && tok->astParent() && + (astIsPointer(tok->astParent()) || Token::Match(tok->astParent(), "%assign%|return"))) { + errorPath.emplace_back(tok, "Array decayed to pointer here."); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::LIFETIME; + value.tokvalue = var->nameToken(); + value.errorPath = errorPath; + setTokenValue(tok, value, tokenlist->getSettings()); + + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } + } +} + static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr) { if (tok->str() != "std") @@ -2245,20 +2898,6 @@ varTok->next()->originalName() == emptyString; } -static const Token * nextAfterAstRightmostLeaf(Token const * tok) -{ - const Token * rightmostLeaf = tok; - if (!rightmostLeaf || !rightmostLeaf->astOperand1()) - return nullptr; - do { - if (rightmostLeaf->astOperand2()) - rightmostLeaf = rightmostLeaf->astOperand2(); - else - rightmostLeaf = rightmostLeaf->astOperand1(); - } while (rightmostLeaf->astOperand1()); - return rightmostLeaf->next(); -} - static const Token * findOpenParentesisOfMove(const Token * moveVarTok) { const Token * tok = moveVarTok; @@ -2327,7 +2966,7 @@ (parent->str() == "return" || // MOVED in return statement parent->str() == "(")) // MOVED in self assignment, isOpenParenthesisMemberFunctionCallOfVarId == true continue; - if (parent && parent->astOperand1()->varId() == varId) + if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId) continue; const Variable *var = varTok->variable(); if (!var) @@ -2368,7 +3007,7 @@ continue; // Lhs should be a variable - if (!tok->astOperand1() || !tok->astOperand1()->varId()) + if (!tok->astOperand1() || !tok->astOperand1()->varId() || tok->astOperand1()->hasKnownValue()) continue; const unsigned int varid = tok->astOperand1()->varId(); if (aliased.find(varid) != aliased.end()) @@ -2384,6 +3023,12 @@ continue; std::list values = tok->astOperand2()->values(); + if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); + } + if (!var->isPointer()) + values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue)); for (std::list::iterator it = values.begin(); it != values.end(); ++it) { const std::string info = "Assignment '" + tok->expressionString() + "', assigned value is " + it->infoString(); it->errorPath.emplace_back(tok->astOperand2(), info); @@ -2412,260 +3057,283 @@ } } -static void valueFlowAfterCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) -{ - for (const Scope * scope : symboldatabase->functionScopes) { - std::set aliased; - for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { - const Token * vartok = nullptr; - const Token * numtok = nullptr; - const Token * lowertok = nullptr; - const Token * uppertok = nullptr; - - if (Token::Match(tok, "= & %var% ;")) - aliased.insert(tok->tokAt(2)->varId()); - - // Comparison - if (Token::Match(tok, "==|!=|>=|<=")) { - if (!tok->astOperand1() || !tok->astOperand2()) +struct ValueFlowConditionHandler { + struct Condition { + const Token *vartok; + std::list true_values; + std::list false_values; + + Condition() : vartok(nullptr), true_values(), false_values() {} + }; + std::function &values, bool constValue)> + forward; + std::function parse; + + void afterCondition(TokenList *tokenlist, + SymbolDatabase *symboldatabase, + ErrorLogger *errorLogger, + const Settings *settings) const { + for (const Scope *scope : symboldatabase->functionScopes) { + std::set aliased; + for (Token *tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "= & %var% ;")) + aliased.insert(tok->tokAt(2)->varId()); + + Condition cond = parse(tok); + if (!cond.vartok) continue; - if (tok->astOperand1()->hasKnownIntValue()) { - numtok = tok->astOperand1(); - vartok = tok->astOperand2(); - } else { - numtok = tok->astOperand2(); - vartok = tok->astOperand1(); - } - if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) - vartok = vartok->astOperand1(); - if (!vartok->isName()) + if (cond.true_values.empty() || cond.false_values.empty()) continue; - } else if (Token::simpleMatch(tok, ">")) { - if (!tok->astOperand1() || !tok->astOperand2()) + const unsigned int varid = cond.vartok->varId(); + if (varid == 0U) continue; - if (tok->astOperand1()->hasKnownIntValue()) { - uppertok = tok->astOperand1(); - vartok = tok->astOperand2(); - } else { - lowertok = tok->astOperand2(); - vartok = tok->astOperand1(); - } - if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) - vartok = vartok->astOperand1(); - if (!vartok->isName()) + const Variable *var = cond.vartok->variable(); + if (!var || !(var->isLocal() || var->isGlobal() || var->isArgument())) continue; - } else if (Token::simpleMatch(tok, "<")) { - if (!tok->astOperand1() || !tok->astOperand2()) + if (aliased.find(varid) != aliased.end()) { + if (settings->debugwarnings) + bailout(tokenlist, + errorLogger, + cond.vartok, + "variable is aliased so we just skip all valueflow after condition"); continue; - if (tok->astOperand1()->hasKnownIntValue()) { - lowertok = tok->astOperand1(); - vartok = tok->astOperand2(); - } else { - uppertok = tok->astOperand2(); - vartok = tok->astOperand1(); } - if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) - vartok = vartok->astOperand1(); - if (!vartok->isName()) - continue; - } else if (tok->str() == "!") { - vartok = tok->astOperand1(); - numtok = nullptr; - if (!vartok || !vartok->isName()) - continue; - - } else if (tok->isName() && - (Token::Match(tok->astParent(), "%oror%|&&") || - Token::Match(tok->tokAt(-2), "if|while ( %var% [)=]"))) { - vartok = tok; - numtok = nullptr; - - } else { - continue; - } - - if (numtok && !numtok->hasKnownIntValue()) - continue; - if (lowertok && !lowertok->hasKnownIntValue()) - continue; - if (uppertok && !uppertok->hasKnownIntValue()) - continue; - - const unsigned int varid = vartok->varId(); - if (varid == 0U) - continue; - const Variable *var = vartok->variable(); - if (!var || !(var->isLocal() || var->isGlobal() || var->isArgument())) - continue; - if (aliased.find(varid) != aliased.end()) { - if (settings->debugwarnings) - bailout(tokenlist, errorLogger, vartok, "variable is aliased so we just skip all valueflow after condition"); - continue; - } - std::list true_values; - std::list false_values; - // TODO: We should add all known values - if (numtok) { - false_values.emplace_back(tok, numtok->values().front().intvalue); - true_values.emplace_back(tok, numtok->values().front().intvalue); - } else if (lowertok) { - long long v = lowertok->values().front().intvalue; - true_values.emplace_back(tok, v+1); - false_values.emplace_back(tok, v); - - } else if (uppertok) { - long long v = uppertok->values().front().intvalue; - true_values.emplace_back(tok, v-1); - false_values.emplace_back(tok, v); - - } else { - true_values.emplace_back(tok, 0LL); - false_values.emplace_back(tok, 0LL); - - } - if (Token::Match(tok->astParent(), "%oror%|&&")) { - Token *parent = const_cast(tok->astParent()); - const std::string &op(parent->str()); - - if (parent->astOperand1() == tok && - ((op == "&&" && Token::Match(tok, "==|>=|<=|!")) || - (op == "||" && Token::Match(tok, "%name%|!=")))) { - for (; parent && parent->str() == op; parent = const_cast(parent->astParent())) { - std::stack tokens; - tokens.push(const_cast(parent->astOperand2())); - bool assign = false; - while (!tokens.empty()) { - Token *rhstok = tokens.top(); - tokens.pop(); - if (!rhstok) - continue; - tokens.push(const_cast(rhstok->astOperand1())); - tokens.push(const_cast(rhstok->astOperand2())); - if (rhstok->varId() == varid) - setTokenValue(rhstok, true_values.front(), settings); - else if (Token::Match(rhstok, "++|--|=") && Token::Match(rhstok->astOperand1(), "%varid%", varid)) { - assign = true; - break; + if (Token::Match(tok->astParent(), "%oror%|&&")) { + Token *parent = const_cast(tok->astParent()); + const std::string &op(parent->str()); + + if (parent->astOperand1() == tok && ((op == "&&" && Token::Match(tok, "==|>=|<=|!")) || + (op == "||" && Token::Match(tok, "%name%|!=")))) { + for (; parent && parent->str() == op; parent = const_cast(parent->astParent())) { + std::stack tokens; + tokens.push(const_cast(parent->astOperand2())); + bool assign = false; + while (!tokens.empty()) { + Token *rhstok = tokens.top(); + tokens.pop(); + if (!rhstok) + continue; + tokens.push(const_cast(rhstok->astOperand1())); + tokens.push(const_cast(rhstok->astOperand2())); + if (rhstok->varId() == varid) + setTokenValue(rhstok, cond.true_values.front(), settings); + else if (Token::Match(rhstok, "++|--|=") && + Token::Match(rhstok->astOperand1(), "%varid%", varid)) { + assign = true; + break; + } } + if (assign) + break; + while (parent->astParent() && parent == parent->astParent()->astOperand2()) + parent = const_cast(parent->astParent()); } - if (assign) - break; - while (parent->astParent() && parent == parent->astParent()->astOperand2()) - parent = const_cast(parent->astParent()); } } - } - - const Token *top = tok->astTop(); - if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) { - // does condition reassign variable? - if (tok != top->astOperand2() && - Token::Match(top->astOperand2(), "%oror%|&&") && - isVariableChanged(top, top->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { - if (settings->debugwarnings) - bailout(tokenlist, errorLogger, tok, "assignment in condition"); - continue; - } - // start token of conditional code - Token *startTokens[] = { nullptr, nullptr }; - - // based on the comparison, should we check the if or while? - bool check_if = false; - bool check_else = false; - if (Token::Match(tok, "==|>=|<=|!|>|<")) - check_if = true; - if (Token::Match(tok, "%name%|!=|>|<")) - check_else = true; - - if (!check_if && !check_else) - continue; - - // if astParent is "!" we need to invert codeblock - { - const Token *parent = tok->astParent(); - while (parent && parent->str() == "&&") - parent = parent->astParent(); - if (parent && parent->str() == "!") { - check_if = !check_if; - check_else = !check_else; + const Token *top = tok->astTop(); + if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) { + // does condition reassign variable? + if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") && + isVariableChanged(top, top->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok, "assignment in condition"); + continue; } - } - // determine startToken(s) - if (check_if && Token::simpleMatch(top->link(), ") {")) - startTokens[0] = top->link()->next(); - if (check_else && Token::simpleMatch(top->link()->linkAt(1), "} else {")) - startTokens[1] = top->link()->linkAt(1)->tokAt(2); + // start token of conditional code + Token *startTokens[] = {nullptr, nullptr}; - bool bail = false; + // based on the comparison, should we check the if or while? + bool check_if = false; + bool check_else = false; + if (Token::Match(tok, "==|>=|<=|!|>|<|(")) + check_if = true; + if (Token::Match(tok, "%name%|!=|>|<")) + check_else = true; - for (int i=0; i<2; i++) { - const Token * const startToken = startTokens[i]; - if (!startToken) + if (!check_if && !check_else) continue; - std::list & values = (i==0 ? true_values : false_values); - if (values.size() == 1U && Token::Match(tok, "==|!")) { + + // if astParent is "!" we need to invert codeblock + { const Token *parent = tok->astParent(); while (parent && parent->str() == "&&") parent = parent->astParent(); - if (parent && parent->str() == "(") - values.front().setKnown(); + if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) { + check_if = !check_if; + check_else = !check_else; + } } - valueFlowForward(startTokens[i]->next(), startTokens[i]->link(), var, varid, values, true, false, tokenlist, errorLogger, settings); - values.front().setPossible(); - if (isVariableChanged(startTokens[i], startTokens[i]->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { - // TODO: The endToken should not be startTokens[i]->link() in the valueFlowForward call - if (settings->debugwarnings) - bailout(tokenlist, errorLogger, startTokens[i]->link(), "valueFlowAfterCondition: " + var->name() + " is changed in conditional block"); - bail = true; - break; - } - } - if (bail) - continue; + // determine startToken(s) + if (check_if && Token::simpleMatch(top->link(), ") {")) + startTokens[0] = top->link()->next(); + if (check_else && Token::simpleMatch(top->link()->linkAt(1), "} else {")) + startTokens[1] = top->link()->linkAt(1)->tokAt(2); + + bool bail = false; + + for (int i = 0; i < 2; i++) { + const Token *const startToken = startTokens[i]; + if (!startToken) + continue; + std::list &values = (i == 0 ? cond.true_values : cond.false_values); + if (values.size() == 1U && Token::Match(tok, "==|!|(")) { + const Token *parent = tok->astParent(); + while (parent && parent->str() == "&&") + parent = parent->astParent(); + if (parent && parent->str() == "(") + values.front().setKnown(); + } - // After conditional code.. - if (Token::simpleMatch(top->link(), ") {")) { - Token *after = top->link()->linkAt(1); - std::string unknownFunction; - if (settings->library.isScopeNoReturn(after, &unknownFunction)) { - if (settings->debugwarnings && !unknownFunction.empty()) - bailout(tokenlist, errorLogger, after, "possible noreturn scope"); - continue; + bool changed = forward(startTokens[i], startTokens[i]->link(), var, values, true); + values.front().setPossible(); + if (changed) { + // TODO: The endToken should not be startTokens[i]->link() in the valueFlowForward call + if (settings->debugwarnings) + bailout(tokenlist, + errorLogger, + startTokens[i]->link(), + "valueFlowAfterCondition: " + var->name() + " is changed in conditional block"); + bail = true; + break; + } } + if (bail) + continue; - const bool dead_if = isReturnScope(after); - bool dead_else = false; - - if (Token::simpleMatch(after, "} else {")) { - after = after->linkAt(2); - if (Token::simpleMatch(after->tokAt(-2), ") ; }")) { - if (settings->debugwarnings) + // After conditional code.. + if (Token::simpleMatch(top->link(), ") {")) { + Token *after = top->link()->linkAt(1); + std::string unknownFunction; + if (settings->library.isScopeNoReturn(after, &unknownFunction)) { + if (settings->debugwarnings && !unknownFunction.empty()) bailout(tokenlist, errorLogger, after, "possible noreturn scope"); continue; } - dead_else = isReturnScope(after); - } - std::list * values = nullptr; - if (!dead_if && check_if) - values = &true_values; - else if (!dead_else && check_else) - values = &false_values; - - if (values) { - // TODO: constValue could be true if there are no assignments in the conditional blocks and - // perhaps if there are no && and no || in the condition - bool constValue = false; - valueFlowForward(after->next(), top->scope()->bodyEnd, var, varid, *values, constValue, false, tokenlist, errorLogger, settings); + const bool dead_if = isReturnScope(after); + bool dead_else = false; + + if (Token::simpleMatch(after, "} else {")) { + after = after->linkAt(2); + if (Token::simpleMatch(after->tokAt(-2), ") ; }")) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, after, "possible noreturn scope"); + continue; + } + dead_else = isReturnScope(after); + } + + std::list *values = nullptr; + if (!dead_if && check_if) + values = &cond.true_values; + else if (!dead_else && check_else) + values = &cond.false_values; + + if (values) { + // TODO: constValue could be true if there are no assignments in the conditional blocks and + // perhaps if there are no && and no || in the condition + bool constValue = false; + forward(after, top->scope()->bodyEnd, var, *values, constValue); + } } } } } } +}; + +static void setConditionalValues(const Token *tok, + bool invert, + MathLib::bigint value, + ValueFlow::Value &true_value, + ValueFlow::Value &false_value) +{ + if (Token::Match(tok, "==|!=|>=|<=")) { + true_value = ValueFlow::Value{tok, value}; + false_value = ValueFlow::Value{tok, value}; + return; + } + const char *greaterThan = ">"; + const char *lessThan = "<"; + if (invert) + std::swap(greaterThan, lessThan); + if (Token::simpleMatch(tok, greaterThan)) { + true_value = ValueFlow::Value{tok, value + 1}; + false_value = ValueFlow::Value{tok, value}; + } else if (Token::simpleMatch(tok, lessThan)) { + true_value = ValueFlow::Value{tok, value - 1}; + false_value = ValueFlow::Value{tok, value}; + } +} + +static const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) +{ + if (!tok->astOperand1() || !tok->astOperand2()) + return nullptr; + if (Token::Match(tok, "%comp%")) { + if (tok->astOperand1()->hasKnownIntValue()) { + setConditionalValues(tok, true, tok->astOperand1()->values().front().intvalue, true_value, false_value); + return tok->astOperand2(); + } else if (tok->astOperand2()->hasKnownIntValue()) { + setConditionalValues(tok, false, tok->astOperand2()->values().front().intvalue, true_value, false_value); + return tok->astOperand1(); + } + } + return nullptr; +} + +static void valueFlowAfterCondition(TokenList *tokenlist, + SymbolDatabase *symboldatabase, + ErrorLogger *errorLogger, + const Settings *settings) +{ + ValueFlowConditionHandler handler; + handler.forward = [&](Token *start, + const Token *stop, + const Variable *var, + const std::list &values, + bool constValue) { + valueFlowForward( + start->next(), stop, var, var->declarationId(), values, constValue, false, tokenlist, errorLogger, settings); + return isVariableChanged(start, stop, var->declarationId(), var->isGlobal(), settings, tokenlist->isCPP()); + }; + handler.parse = [&](const Token *tok) { + ValueFlowConditionHandler::Condition cond; + ValueFlow::Value true_value; + ValueFlow::Value false_value; + const Token *vartok = parseCompareInt(tok, true_value, false_value); + if (vartok) { + if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) + vartok = vartok->astOperand1(); + if (!vartok->isName()) + return cond; + cond.true_values.push_back(true_value); + cond.false_values.push_back(false_value); + cond.vartok = vartok; + return cond; + } + + if (tok->str() == "!") { + vartok = tok->astOperand1(); + + } else if (tok->isName() && (Token::Match(tok->astParent(), "%oror%|&&") || + Token::Match(tok->tokAt(-2), "if|while ( %var% [)=]"))) { + vartok = tok; + } + + if (!vartok || !vartok->isName()) + return cond; + cond.true_values.emplace_back(tok, 0LL); + cond.false_values.emplace_back(tok, 0LL); + cond.vartok = vartok; + + return cond; + }; + handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } static void execute(const Token *expr, @@ -2806,11 +3474,14 @@ else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { const Token *tokvalue = nullptr; if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) { - if (expr->astOperand1()->values().size() != 1U || !expr->astOperand1()->values().front().isTokValue()) { + auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(), + expr->astOperand1()->values().end(), + std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tokvalue_it == expr->astOperand1()->values().end()) { *error = true; return; } - tokvalue = expr->astOperand1()->values().front().tokvalue; + tokvalue = tokvalue_it->tokvalue; } if (!tokvalue || !tokvalue->isLiteral()) { *error = true; @@ -2890,19 +3561,16 @@ return false; if (error) { // If a variable is reassigned in second expression, return false - std::stack tokens; - tokens.push(secondExpression); - while (!tokens.empty()) { - const Token *t = tokens.top(); - tokens.pop(); - if (!t) - continue; + bool reassign = false; + visitAstNodes(secondExpression, + [&](const Token *t) { if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId())) // TODO: investigate what variable is assigned. - return false; - tokens.push(t->astOperand1()); - tokens.push(t->astOperand2()); - } + reassign = true; + return reassign ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (reassign) + return false; } ProgramMemory startMemory(programMemory); @@ -3170,64 +3838,198 @@ static void setTokenValues(Token *tok, const std::list &values, const Settings *settings) { - for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { - const ValueFlow::Value &value = *it; + for (const ValueFlow::Value &value : values) { if (value.isIntValue()) setTokenValue(tok, value, settings); } } -static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings) +static bool evaluate(const Token *expr, const std::vector> &values, std::list *result) { - std::istringstream istr(returnValue); - TokenList tokenList(settings); - if (!tokenList.createTokens(istr)) - return; - - const Token *arg1 = tok->astOperand2(); - while (arg1 && arg1->str() == ",") - arg1 = arg1->astOperand1(); - if (Token::findsimplematch(tokenList.front(), "arg1") && !arg1) - return; + if (!expr) + return false; - if (Token::simpleMatch(tokenList.front(), "strlen ( arg1 )") && arg1) { - for (const ValueFlow::Value &value : arg1->values()) { - if (value.isTokValue() && value.tokvalue->tokType() == Token::eString) { - ValueFlow::Value retval(value); // copy all "inconclusive", "condition", etc attributes + // strlen(arg).. + if (expr->str() == "(" && Token::Match(expr->previous(), "strlen ( %name% )")) { + const Token *arg = expr->next(); + if (arg->str().compare(0,3,"arg") != 0 || arg->str().size() != 4) + return false; + const char n = arg->str()[3]; + if (n < '1' || n - '1' >= values.size()) + return false; + for (const ValueFlow::Value &argvalue : values[n - '1']) { + if (argvalue.isTokValue() && argvalue.tokvalue->tokType() == Token::eString) { + ValueFlow::Value res(argvalue); // copy all "inconclusive", "condition", etc attributes // set return value.. - retval.valueType = ValueFlow::Value::INT; - retval.tokvalue = nullptr; - retval.intvalue = Token::getStrLength(value.tokvalue); - setTokenValue(tok, retval, settings); + res.valueType = ValueFlow::Value::INT; + res.tokvalue = nullptr; + res.intvalue = Token::getStrLength(argvalue.tokvalue); + result->emplace_back(res); + } + } + return !result->empty(); + } + + // unary operands + if (expr->astOperand1() && !expr->astOperand2()) { + std::list opvalues; + if (!evaluate(expr->astOperand1(), values, &opvalues)) + return false; + if (expr->str() == "+") { + result->swap(opvalues); + return true; + } + if (expr->str() == "-") { + for (ValueFlow::Value v: opvalues) { + if (v.isIntValue()) { + v.intvalue = -v.intvalue; + result->emplace_back(v); + } + } + return true; + } + return false; + } + // binary/ternary operands + if (expr->astOperand1() && expr->astOperand2()) { + std::list lhsValues, rhsValues; + if (!evaluate(expr->astOperand1(), values, &lhsValues)) + return false; + if (expr->str() != "?" && !evaluate(expr->astOperand2(), values, &rhsValues)) + return false; + + for (const ValueFlow::Value &val1 : lhsValues) { + if (!val1.isIntValue()) + continue; + if (expr->str() == "?") { + rhsValues.clear(); + const Token *expr2 = val1.intvalue ? expr->astOperand2()->astOperand1() : expr->astOperand2()->astOperand2(); + if (!evaluate(expr2, values, &rhsValues)) + continue; + result->insert(result->end(), rhsValues.begin(), rhsValues.end()); + continue; + } + + for (const ValueFlow::Value &val2 : rhsValues) { + if (!val2.isIntValue()) + continue; + + if (val1.varId != 0 && val2.varId != 0) { + if (val1.varId != val2.varId || val1.varvalue != val2.varvalue) + continue; + } + + if (expr->str() == "+") + result->emplace_back(ValueFlow::Value(val1.intvalue + val2.intvalue)); + else if (expr->str() == "-") + result->emplace_back(ValueFlow::Value(val1.intvalue - val2.intvalue)); + else if (expr->str() == "*") + result->emplace_back(ValueFlow::Value(val1.intvalue * val2.intvalue)); + else if (expr->str() == "/" && val2.intvalue != 0) + result->emplace_back(ValueFlow::Value(val1.intvalue / val2.intvalue)); + else if (expr->str() == "%" && val2.intvalue != 0) + result->emplace_back(ValueFlow::Value(val1.intvalue % val2.intvalue)); + else if (expr->str() == "&") + result->emplace_back(ValueFlow::Value(val1.intvalue & val2.intvalue)); + else if (expr->str() == "|") + result->emplace_back(ValueFlow::Value(val1.intvalue | val2.intvalue)); + else if (expr->str() == "^") + result->emplace_back(ValueFlow::Value(val1.intvalue ^ val2.intvalue)); + else if (expr->str() == "==") + result->emplace_back(ValueFlow::Value(val1.intvalue == val2.intvalue)); + else if (expr->str() == "!=") + result->emplace_back(ValueFlow::Value(val1.intvalue != val2.intvalue)); + else if (expr->str() == "<") + result->emplace_back(ValueFlow::Value(val1.intvalue < val2.intvalue)); + else if (expr->str() == ">") + result->emplace_back(ValueFlow::Value(val1.intvalue > val2.intvalue)); + else if (expr->str() == ">=") + result->emplace_back(ValueFlow::Value(val1.intvalue >= val2.intvalue)); + else if (expr->str() == "<=") + result->emplace_back(ValueFlow::Value(val1.intvalue <= val2.intvalue)); + else if (expr->str() == "&&") + result->emplace_back(ValueFlow::Value(val1.intvalue && val2.intvalue)); + else if (expr->str() == "||") + result->emplace_back(ValueFlow::Value(val1.intvalue || val2.intvalue)); + else if (expr->str() == "<<") + result->emplace_back(ValueFlow::Value(val1.intvalue << val2.intvalue)); + else if (expr->str() == ">>") + result->emplace_back(ValueFlow::Value(val1.intvalue >> val2.intvalue)); + else + return false; + combineValueProperties(val1, val2, &result->back()); } } + return !result->empty(); + } + if (expr->str().compare(0,3,"arg")==0) { + *result = values[expr->str()[3] - '1']; + return true; + } + if (expr->isNumber()) { + result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str()))); + result->back().setKnown(); + return true; + } else if (expr->tokType() == Token::eChar) { + result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str()))); + result->back().setKnown(); + return true; + } + return false; +} + +static std::list getFunctionArgumentValues(const Token *argtok) +{ + std::list argvalues(argtok->values()); + if (argvalues.empty() && Token::Match(argtok, "%comp%|%oror%|&&|!")) { + argvalues.emplace_back(0); + argvalues.emplace_back(1); + } + return argvalues; +} + +static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings) +{ + std::vector> argValues; + for (const Token *argtok : getArguments(tok->previous())) { + argValues.emplace_back(getFunctionArgumentValues(argtok)); + if (argValues.back().empty()) + return; + } + if (returnValue.find("arg") != std::string::npos && argValues.empty()) return; + + TokenList tokenList(settings); + { + const std::string code = "return " + returnValue + ";"; + std::istringstream istr(code); + if (!tokenList.createTokens(istr)) + return; } // combine operators, set links, etc.. + std::stack lpar; for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[!<>=] =")) { tok2->str(tok2->str() + "="); tok2->deleteNext(); + } else if (tok2->str() == "(") + lpar.push(tok2); + else if (tok2->str() == ")") { + if (lpar.empty()) + return; + Token::createMutualLinks(lpar.top(), tok2); + lpar.pop(); } } + if (!lpar.empty()) + return; // Evaluate expression tokenList.createAst(); - valueFlowNumber(&tokenList); - for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { - if (tok2->str() == "arg1" && arg1) { - setTokenValues(tok2, arg1->values(), settings); - } - } - - // Find result.. - for (const Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { - if (!tok2->astParent() && !tok2->values().empty()) { - setTokenValues(tok, tok2->values(), settings); - return; - } - } + std::list results; + if (evaluate(tokenList.front()->astOperand1(), argValues, &results)) + setTokenValues(tok, results, settings); } static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) @@ -3249,6 +4051,7 @@ if (!calledFunctionScope) continue; + // TODO: Rewrite this. It does not work well to inject 1 argument at a time. const std::vector &callArguments = getArguments(tok); for (unsigned int argnr = 0U; argnr < callArguments.size(); ++argnr) { const Token *argtok = callArguments[argnr]; @@ -3258,30 +4061,27 @@ break; // passing value(s) to function - std::list argvalues; - if (Token::Match(argtok, "%comp%|%oror%|&&|!") && !argtok->hasKnownIntValue()) { - argvalues.emplace_back(0); - argvalues.emplace_back(1); - } else { - argvalues = argtok->values(); - } + std::list argvalues(getFunctionArgumentValues(argtok)); + + // Dont forward lifetime values + argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); if (argvalues.empty()) continue; // Error path.. - for (std::list::iterator it = argvalues.begin(); it != argvalues.end(); ++it) { + for (ValueFlow::Value &v : argvalues) { const std::string nr = MathLib::toString(argnr + 1) + getOrdinalText(argnr + 1); - it->errorPath.emplace_back(argtok, - "Calling function '" + - calledFunction->name() + - "', " + - nr + - " argument '" + - argvar->name() + - "' value is " + - it->infoString()); + v.errorPath.emplace_back(argtok, + "Calling function '" + + calledFunction->name() + + "', " + + nr + + " argument '" + + argtok->expressionString() + + "' value is " + + v.infoString()); } // passed values are not "known".. @@ -3331,6 +4131,9 @@ if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand1()->function()) continue; + if (tok->hasKnownValue()) + continue; + // Arguments.. std::vector parvalues; if (tok->astOperand2()) { @@ -3440,22 +4243,47 @@ if (!Token::Match(parent, "%oror%|&&|?")) continue; // is container found in lhs? - std::stack tokens; - tokens.push(parent->astOperand1()); - while (!tokens.empty()) { - const Token *t = tokens.top(); - tokens.pop(); - if (!t) - continue; + bool found = false; + visitAstNodes(parent->astOperand1(), + [&](const Token *t) { if (t->varId() == containerId) - return true; - tokens.push(t->astOperand1()); - tokens.push(t->astOperand2()); - } + found = true; + return found ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (found) + return true; } return false; } +static bool isContainerSize(const Token* tok) +{ + if (!Token::Match(tok, "%var% . %name% (")) + return false; + if (!astIsContainer(tok)) + return false; + if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) + return true; + if (Token::Match(tok->tokAt(2), "size|length ( )")) + return true; + return false; +} + +static bool isContainerEmpty(const Token* tok) +{ + if (!Token::Match(tok, "%var% . %name% (")) + return false; + if (!astIsContainer(tok)) + return false; + if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) + return true; + if (Token::simpleMatch(tok->tokAt(2), "empty ( )")) + return true; + return false; +} + +static bool isContainerSizeChanged(unsigned int varId, const Token *start, const Token *end); + static bool isContainerSizeChangedByFunction(const Token *tok) { const Token *parent = tok->astParent(); @@ -3495,15 +4323,41 @@ } } -static void valueFlowContainerForward(const Token *tok, unsigned int containerId, const ValueFlow::Value &value, const Settings *settings, bool cpp) +static void valueFlowContainerForward(const Token *tok, unsigned int containerId, ValueFlow::Value value, const Settings *settings, bool cpp) { while (nullptr != (tok = tok->next())) { if (Token::Match(tok, "[{}]")) break; + if (Token::Match(tok, "while|for (")) { + const Token *start = tok->linkAt(1)->next(); + if (!Token::simpleMatch(start->link(), "{")) + break; + if (isContainerSizeChanged(containerId, start, start->link())) + break; + } if (tok->varId() != containerId) continue; if (Token::Match(tok, "%name% =")) break; + if (Token::Match(tok, "%name% +=")) { + if (!tok->valueType() || !tok->valueType()->container || !tok->valueType()->container->stdStringLike) + break; + const Token *rhs = tok->next()->astOperand2(); + if (rhs->tokType() == Token::eString) + value.intvalue += Token::getStrLength(rhs); + else if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) { + bool found = false; + for (const ValueFlow::Value &rhsval : rhs->values()) { + if (rhsval.isKnown() && rhsval.isContainerSizeValue()) { + value.intvalue += rhsval.intvalue; + found = true; + } + } + if (!found) + break; + } else + break; + } if (isLikelyStreamRead(cpp, tok->astParent())) break; if (isContainerSizeChangedByFunction(tok)) @@ -3559,6 +4413,8 @@ continue; if (!Token::Match(var->nameToken(), "%name% ;")) continue; + if (var->nameToken()->hasKnownValue()) + continue; ValueFlow::Value value(0); if (var->valueType()->container->size_templateArgNo >= 0) { if (var->dimensions().size() == 1 && var->dimensions().front().known) @@ -3593,26 +4449,31 @@ for (const Token *tok = scope.classDef; tok && tok->str() != "{"; tok = tok->next()) { if (!tok->isName() || !tok->valueType() || tok->valueType()->type != ValueType::CONTAINER || !tok->valueType()->container) continue; - if (!Token::Match(tok, "%name% . %name% (")) - continue; const Token *conditionToken; MathLib::bigint intval; - if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) { - const Token *parent = tok->tokAt(3)->astParent(); - if (!parent || !parent->isComparisonOp() || !parent->astOperand2()) - continue; - if (parent->astOperand1()->hasKnownIntValue()) - intval = parent->astOperand1()->values().front().intvalue; - else if (parent->astOperand2()->hasKnownIntValue()) - intval = parent->astOperand2()->values().front().intvalue; - else + if (Token::Match(tok, "%name% . %name% (")) { + if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) { + const Token *parent = tok->tokAt(3)->astParent(); + if (!parent || !parent->isComparisonOp() || !parent->astOperand2()) + continue; + if (parent->astOperand1()->hasKnownIntValue()) + intval = parent->astOperand1()->values().front().intvalue; + else if (parent->astOperand2()->hasKnownIntValue()) + intval = parent->astOperand2()->values().front().intvalue; + else + continue; + conditionToken = parent; + } else if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) { + conditionToken = tok->tokAt(3); + intval = 0; + } else { continue; - conditionToken = parent; - } else if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) { - conditionToken = tok->tokAt(3); - intval = 0; + } + } else if (tok->valueType()->container->stdStringLike && Token::Match(tok, "%name% ==|!= %str%") && tok->next()->astOperand2() == tok->tokAt(2)) { + intval = Token::getStrLength(tok->tokAt(2)); + conditionToken = tok->next(); } else { continue; } @@ -3622,28 +4483,78 @@ // possible value before condition valueFlowContainerReverse(scope.classDef, tok->varId(), value, settings); + } + } +} - // possible value after condition - if (!isEscapeScope(scope.bodyStart, tokenlist)) { - const Token *after = scope.bodyEnd; - if (Token::simpleMatch(after, "} else {")) - after = isEscapeScope(after->tokAt(2), tokenlist) ? nullptr : after->linkAt(2); - if (after && !isContainerSizeChanged(tok->varId(), scope.bodyStart, after)) - valueFlowContainerForward(after, tok->varId(), value, settings, tokenlist->isCPP()); - } - - // known value in conditional code - if (conditionToken->str() == "==" || conditionToken->str() == "(") { - const Token *parent = conditionToken->astParent(); - while (parent && !Token::Match(parent, "!|==|!=")) - parent = parent->astParent(); - if (!parent) { - value.setKnown(); - valueFlowContainerForward(scope.bodyStart, tok->varId(), value, settings, tokenlist->isCPP()); - } +static void valueFlowContainerAfterCondition(TokenList *tokenlist, + SymbolDatabase *symboldatabase, + ErrorLogger *errorLogger, + const Settings *settings) +{ + ValueFlowConditionHandler handler; + handler.forward = + [&](Token *start, const Token *stop, const Variable *var, const std::list &values, bool) { + // TODO: Forward multiple values + if (values.empty()) + return false; + valueFlowContainerForward(start, var->declarationId(), values.front(), settings, tokenlist->isCPP()); + return isContainerSizeChanged(var->declarationId(), start, stop); + }; + handler.parse = [&](const Token *tok) { + ValueFlowConditionHandler::Condition cond; + ValueFlow::Value true_value; + ValueFlow::Value false_value; + const Token *vartok = parseCompareInt(tok, true_value, false_value); + if (vartok) { + vartok = vartok->tokAt(-3); + if (!isContainerSize(vartok)) + return cond; + true_value.valueType = ValueFlow::Value::CONTAINER_SIZE; + false_value.valueType = ValueFlow::Value::CONTAINER_SIZE; + cond.true_values.push_back(true_value); + cond.false_values.push_back(false_value); + cond.vartok = vartok; + return cond; + } + + // Empty check + if (tok->str() == "(") { + vartok = tok->tokAt(-3); + // TODO: Handle .size() + if (!isContainerEmpty(vartok)) + return cond; + ValueFlow::Value value(tok, 0LL); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + cond.true_values.emplace_back(value); + cond.false_values.emplace_back(value); + cond.vartok = vartok; + return cond; + } + // String compare + if (Token::Match(tok, "==|!=")) { + const Token *strtok = nullptr; + if (Token::Match(tok->astOperand1(), "%str%")) { + strtok = tok->astOperand1(); + vartok = tok->astOperand2(); + } else if (Token::Match(tok->astOperand2(), "%str%")) { + strtok = tok->astOperand2(); + vartok = tok->astOperand1(); } + if (!strtok) + return cond; + if (!astIsContainer(vartok)) + return cond; + ValueFlow::Value value(tok, Token::getStrLength(strtok)); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + cond.false_values.emplace_back(value); + cond.true_values.emplace_back(value); + cond.vartok = vartok; + return cond; } - } + return cond; + }; + handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } ValueFlow::Value::Value(const Token *c, long long val) @@ -3657,6 +4568,7 @@ varId(0U), conditional(false), defaultArg(false), + lifetimeKind(Object), valueKind(ValueKind::Possible) { errorPath.emplace_back(c, "Assuming that condition '" + c->expressionString() + "' is not redundant"); @@ -3677,6 +4589,8 @@ return ""; case CONTAINER_SIZE: return "size=" + MathLib::toString(intvalue); + case LIFETIME: + return "lifetime=" + tokvalue->str(); }; throw InternalError(nullptr, "Invalid ValueFlow Value type"); } @@ -3691,6 +4605,13 @@ return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr; } +static std::size_t getTotalValues(TokenList *tokenlist) +{ + std::size_t n = 1; + for (Token *tok = tokenlist->front(); tok; tok = tok->next()) + n += tok->values().size(); + return n; +} void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { @@ -3702,20 +4623,32 @@ valueFlowArray(tokenlist); valueFlowGlobalStaticVar(tokenlist, settings); valueFlowPointerAlias(tokenlist); + valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowBitAnd(tokenlist); - valueFlowOppositeCondition(symboldatabase, settings); - valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings); - valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings); - valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); - valueFlowAfterCondition(tokenlist, symboldatabase, errorLogger, settings); - valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); - valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); - valueFlowSubFunction(tokenlist, errorLogger, settings); - valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); - valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); - if (tokenlist->isCPP()) - valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings); + + // Temporary hack.. run valueflow until there is nothing to update or timeout expires + const std::time_t timeout = std::time(0) + TIMEOUT; + std::size_t values = 0; + while (std::time(0) < timeout && values < getTotalValues(tokenlist)) { + values = getTotalValues(tokenlist); + valueFlowRightShift(tokenlist); + valueFlowOppositeCondition(symboldatabase, settings); + valueFlowTerminatingCondition(tokenlist, symboldatabase, settings); + valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings); + valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings); + valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); + valueFlowAfterCondition(tokenlist, symboldatabase, errorLogger, settings); + valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); + valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); + valueFlowSubFunction(tokenlist, errorLogger, settings); + valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); + valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); + if (tokenlist->isCPP()) { + valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings); + valueFlowContainerAfterCondition(tokenlist, symboldatabase, errorLogger, settings); + } + } } diff -Nru cppcheck-1.85/lib/valueflow.h cppcheck-1.86/lib/valueflow.h --- cppcheck-1.85/lib/valueflow.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/valueflow.h 2018-12-08 07:18:21.000000000 +0000 @@ -39,7 +39,7 @@ typedef std::pair ErrorPathItem; typedef std::list ErrorPath; - explicit Value(long long val = 0) : valueType(INT), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(NonMovedVariable), varvalue(val), condition(nullptr), varId(0U), conditional(false), defaultArg(false), valueKind(ValueKind::Possible) {} + explicit Value(long long val = 0) : valueType(INT), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(NonMovedVariable), varvalue(val), condition(nullptr), varId(0U), conditional(false), defaultArg(false), lifetimeKind(Object), valueKind(ValueKind::Possible) {} Value(const Token *c, long long val); bool operator==(const Value &rhs) const { @@ -68,6 +68,10 @@ case CONTAINER_SIZE: if (intvalue != rhs.intvalue) return false; + break; + case LIFETIME: + if (tokvalue != rhs.tokvalue) + return false; }; return varvalue == rhs.varvalue && @@ -80,7 +84,7 @@ std::string infoString() const; - enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE } valueType; + enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE, LIFETIME } valueType; bool isIntValue() const { return valueType == INT; } @@ -99,6 +103,9 @@ bool isContainerSizeValue() const { return valueType == CONTAINER_SIZE; } + bool isLifetimeValue() const { + return valueType == LIFETIME; + } /** int value */ long long intvalue; @@ -129,6 +136,8 @@ /** Is this value passed as default parameter to the function? */ bool defaultArg; + enum LifetimeKind {Object, Lambda, Iterator} lifetimeKind; + static const char * toString(MoveKind moveKind) { switch (moveKind) { case NonMovedVariable: diff -Nru cppcheck-1.85/lib/version.h cppcheck-1.86/lib/version.h --- cppcheck-1.85/lib/version.h 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/lib/version.h 2018-12-08 07:18:21.000000000 +0000 @@ -1,6 +1,6 @@ #define CPPCHECK_MAJOR 1 -#define CPPCHECK_MINOR 85 -#define CPPCHECK_DEVMINOR 85 +#define CPPCHECK_MINOR 86 +#define CPPCHECK_DEVMINOR 86 #define STRINGIFY(x) STRING(x) #define STRING(VER) #VER diff -Nru cppcheck-1.85/Makefile cppcheck-1.86/Makefile --- cppcheck-1.85/Makefile 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/Makefile 2018-12-08 07:18:21.000000000 +0000 @@ -300,6 +300,23 @@ install -m 644 cfg/* ${DESTDIR}${CFGDIR} endif +uninstall: + @if test -d ${BIN}; then \ + files="cppcheck cppcheck-htmlreport \ + `ls -d addons/*.py addons/*/*.py 2>/dev/null | sed 's,^.*/,,'`"; \ + echo '(' cd ${BIN} '&&' rm -f $$files ')'; \ + ( cd ${BIN} && rm -f $$files ); \ + fi +ifdef CFGDIR + @if test -d ${DESTDIR}${CFGDIR}; then \ + files="`cd cfg 2>/dev/null && ls`"; \ + if test -n "$$files"; then \ + echo '(' cd ${DESTDIR}${CFGDIR} '&&' rm -f $$files ')'; \ + ( cd ${DESTDIR}${CFGDIR} && rm -f $$files ); \ + fi; \ + fi +endif + # Validation of library files: ConfigFiles := $(wildcard cfg/*.cfg) ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles)) @@ -327,6 +344,8 @@ xmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml xmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml +checkCWEEntries: /tmp/errorlist.xml + ./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml ###### Build $(SRCDIR)/analyzerinfo.o: lib/analyzerinfo.cpp lib/analyzerinfo.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/path.h @@ -383,7 +402,7 @@ $(SRCDIR)/checknullpointer.o: lib/checknullpointer.cpp lib/checknullpointer.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h lib/astutils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checknullpointer.o $(SRCDIR)/checknullpointer.cpp -$(SRCDIR)/checkother.o: lib/checkother.cpp lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h +$(SRCDIR)/checkother.o: lib/checkother.cpp lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/checkuninitvar.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkother.o $(SRCDIR)/checkother.cpp $(SRCDIR)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/checkpostfixoperator.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h diff -Nru cppcheck-1.85/man/manual.docbook cppcheck-1.86/man/manual.docbook --- cppcheck-1.85/man/manual.docbook 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/man/manual.docbook 2018-12-08 07:18:21.000000000 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> - Cppcheck 1.85 + Cppcheck 1.86 2018-04-23 @@ -853,7 +853,7 @@ - \t + \n Newline @@ -1586,8 +1586,7 @@
strz - This setting is not used by Cppcheck currently. But with this - you can say that an argument must be a zero-terminated + With this you can say that an argument must be a zero-terminated string. <?xml version="1.0"?> diff -Nru cppcheck-1.85/man/manual-ja.docbook cppcheck-1.86/man/manual-ja.docbook --- cppcheck-1.85/man/manual-ja.docbook 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/man/manual-ja.docbook 2018-12-08 07:18:21.000000000 +0000 @@ -2,9 +2,9 @@ - Cppcheck 1.82 + Cppcheck 1.85 - 2018-01-21 + 2018-04-23 @@ -478,7 +478,7 @@ cwe - メッセージのCWE ID。こ属性は、メッセージのCWE IDが判明している場合のみ使用されます。 + メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。 @@ -517,10 +517,10 @@ - msg + info - この属性は使用しません。ただし将来のいつか、それぞれの位置に短いメッセージを追加するようになるかもしれません。 + オプションの、それぞれの位置につiての短い情報 @@ -532,89 +532,271 @@ もし、テンプレートを使用して、出力の形式を変更することができます。 - Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 +
+ 事前定義した出力フォーマット - cppcheck --template=vs gui/test.cpp + Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 - このオプションは出力形式を次のように変更します。: + cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c - Checking gui/test.cpp... -gui/test.cpp(31): error: Memory leak: b -gui/test.cpp(16): error: Mismatching allocation and deallocation: k + このオプションは出力形式を次のように変更します。: - gcc に互換性のある形式が必要な場合には、--template=gccを使用します。: + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. - cppcheck --template=gcc gui/test.cpp + gccに互換性のある出力が必要な場合には、--template=gccを使用します。: - このオプションは出力形式を次のように変更します。: + cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c - Checking gui/test.cpp... -gui/test.cpp:31: error: Memory leak: b -gui/test.cpp:16: error: Mismatching allocation and deallocation: k + このオプションは出力形式を次のように変更します。: - それ以外に、自分自身の作成したパターンで指定することもできます。例としてコンマ区切りで出力してみましょう。: + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[2] = 0; + ^ +
- cppcheck --template="{file},{line},{severity},{id},{message}" gui/test.cpp +
+ ユーザー定義出力形式(1行) - このオプションは出力形式を次のように変更します。: + 自分で自身でパターンを作成できます。例えば古いgcc のよuな出力フォーマットで警告メッセージを出力してほしい場合次のように指定します。: - Checking gui/test.cpp... -gui/test.cpp,31,error,memleak,Memory leak: b -gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocation: k + cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c - 以下のようなフォーマット指定項目がサポートされています。 + このオプションは出力形式を次のように変更します。: - - - callstack + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. - - callstack ただし可能な場合に限る。 - - + コンマ区切りフォーマット: - - file + cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c - - ファイル名 - - + このオプションは出力形式を次のように変更します。: - - id + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. +
- - メッセージid - - +
+ ユーザー定義出力形式(複数行) - - line + 多くの警告は、複数の位置を指定します。サンプルコード: - - 行数 - - + void f(int *p) +{ + *p = 3; // line 3 +} - - message +int main() +{ + int *p = 0; // line 8 + f(p); // line 9 + return 0; +} - - 長い形式のメッセージ - - + 3行目でヌルポインタのデリファレンスの可能性があります。Cppcheckは追加の位置情報を表示してその結論がどこから発生したかを示すことができます。そのためには、コマンドラインで--template--template-locationの両方を使用する必要があります。 - - severity + サンプルコマンド: - - メッセージの種類、レベル - - - + cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c + + cppcheck は次のように出力します。 + + Checking multiline.c ... +multiline.c:3: warning: Possible null pointer dereference: p + *p = 3; + ^ +multiline.c:8: note: Assignment 'p=0', assigned value is 0 + int *p = 0; + ^ +multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 + f(p); + ^ +multiline.c:3: note: Null pointer dereference + *p = 3; + ^ - その他エスケープシーケンス \b (バックスペース), \n (改行), \r (改ページ) , \t (タブ) がサポートされています。 + この警告の最初の行は--template で指定したフォーマットです。 + + この警告の残りの行は--template-locationで指定したフォーマットです。 +
+ +
+ --templateで指定するフォーマット + + --template では以下の要素が利用できます。: + + + + {file} + + + ファイル名 + + + + + {line} + + + 行数 + + + + + {column} + + + カラム番号 + + + + + {callstack} + + + 全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] + + + + + {inconclusive:text} + + + 警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,} + + + + + {severity} + + + エラー/警告/スタイル/性能/移植性/情報 + + + + + {message} + + + 警告メッセージ + + + + + {id} + + + 警告id + + + + + {code} + + + 実際のコード + + + + + \t + + + タブ + + + + + \n + + + 改行 + + + + + \r + + + キャリッジリターン + + + +
+ +
+ --template-location で指定するフォーマット + + --template-locationでは以下の要素が利用できます。: + + + + {file} + + + ファイル名 + + + + + {line} + + + 行数 + + + + + {column} + + + カラム番号 + + + + + {info} + + + 現在位置についての情報メッセージ + + + + + {code} + + + 実際のコード + + + + + \t + + + タブ + + + + + \t + + + 改行 + + + + + \r + + + キャリッジリターン + + + +
@@ -622,12 +804,10 @@ CppcheckはMISRA C 2012 向けのチェッカのアドオンを持っています。 - MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。その代わりにこのアドオンはMisra PDF ファイルまたはテキストファイルのどちらかからルールテキストを読み込めます。 -
要求事項 - Cppcheck MISRA アドオンには次のものが必要です。: + 必要なもの: @@ -635,26 +815,22 @@ - MISRA PDF/Text ファイル(ルールテキスト) + MISRA C 2012の PDFこのPDFはhttp://www.misra.org.ukで購入できます (15-20 ポンド)
- MISRA PDF - - もしあなたのコードMISRA規格に合致していることを望んでいるなら、あなたはMISRA PDFを持っているでしょう。 - - しかしながら、MISRA PDF が直接このアドオンから使用するには、xpdfパッケージを持っている場合に限ります。これはクロスプラットフォームのオープンソースパッケージです。 -
- -
MISRA テキストファイル + MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。代わりにこのアドオンはテキストファイルからルールのテキストを読み込みます。MISRA PDFの ”Appendix A Summary of guidelines"のテキストをコピーペーストした場合、それがルールのテキストになります。 + もしあなたがxpdfを持っているなら、テキストファイルはコマンドラインから簡単に生成できます。 (pdftotextxpdfに含まれています。): pdftotext misra-c-2012.pdf output.txt + この出力は100%完璧であるとは限りません。少し手直しする必要があることもあります。 + その他のpdfからtextに変換するソフトでもうまくいくでしょう。 テキストファイルをマニュアルで作成してするには、MISRA PDFの Appendix A "Summary of guidelines" をコピーペーストします。フォーマット: @@ -665,6 +841,8 @@ Rule 1.2 Rule text ... + + あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。
@@ -674,7 +852,7 @@ ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
- エラー種による出力の抑制 + プレーンテキスト抑制 エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。: @@ -715,6 +893,24 @@
+ XML 抑制 + + XMLファイルで抑制を指定できます。サンプルファイル: + + <?xml version="1.0"?> +<suppressions> + <suppression> + <id>uninitvar</id> + <fileName>src/file1.c</fileName> + <lineNumber>10</lineNumber> + <symbolName>var</symbolName> + </suppression> +</suppressions> + + このXMLフォーマットは拡張可能であり、将来さらなる属性を加えるかもしれません。 +
+ +
インライン出力抑制 エラー出力の抑制をソースコード中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。 @@ -744,6 +940,15 @@ これで、--inline-suppr オプションの準備ができました。次のようにcppcheckを起動するとエラーが抑制されます。: cppcheck --inline-suppr test.c + + 特定のシンボルにのみ適用するインライン抑制を指定できます。: + + // cppcheck-suppress arrayIndexOutOfBounds symbolName=arr + + 抑制のためにコメントを書きます。; や // を使って開始点を指定できます。 + + // cppcheck-suppress arrayIndexOutOfBounds ; some comment +// cppcheck-suppress arrayIndexOutOfBounds // some comment
@@ -1045,7 +1250,8 @@ -10:20 => -10 から 20 までの値(両端含む)が有効な値です。 :0 => 0または0未満の値が有効な値です。 0: => 0または0以上の値が有効な値です。 -0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。
+0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。 +-1.5:5.6 => -1.5 から 5.6 までの値(両端含む)が有効な値です。
@@ -1121,7 +1327,7 @@
strz - この設定は、Cppcheckで現在使用できません。しかし、これであなたは引数が0終端文字列でなければならないということができます。 + これを指定すると、数が0終端文字列でなければならないということができます。 <?xml version="1.0"?> <def> @@ -1686,4 +1892,4 @@ このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。
- \ No newline at end of file + diff -Nru cppcheck-1.85/readme.md cppcheck-1.86/readme.md --- cppcheck-1.85/readme.md 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/readme.md 2018-12-08 07:18:21.000000000 +0000 @@ -31,7 +31,7 @@ There are multiple compilation choices: * qmake - cross platform build tool * cmake - cross platform build tool -* Windows: Visual Studio (VS 2010 and above) +* Windows: Visual Studio (VS 2013 and above) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) diff -Nru cppcheck-1.85/runastyle cppcheck-1.86/runastyle --- cppcheck-1.85/runastyle 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/runastyle 2018-12-08 07:18:21.000000000 +0000 @@ -1,4 +1,3 @@ - #!/bin/bash # The version check in this script is used to avoid commit battles # between different developers that use different astyle versions as @@ -7,38 +6,37 @@ # If project management wishes to take a newer astyle version into use # just change this string to match the start of astyle version string. -ASTYLE_VERSION="Artistic Style Version 3.0.1" -ASTYLE="astyle" +ASTYLE_VERSION="3.0.1" +ASTYLE="${ASTYLE-astyle}" -DETECTED_VERSION=`$ASTYLE --version 2>&1` -if [[ "$DETECTED_VERSION" != ${ASTYLE_VERSION}* ]]; then - echo "You should use: ${ASTYLE_VERSION}"; - echo "Detected: ${DETECTED_VERSION}" - exit 1; +DETECTED_VERSION=$("$ASTYLE" --version 2>&1 | awk '{ print $NF; }') +if [ "$DETECTED_VERSION" != "${ASTYLE_VERSION}" ]; then + echo "You should use version: ${ASTYLE_VERSION}" + echo "Detected version: ${DETECTED_VERSION}" + exit 1 fi -style="--style=kr --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0" -options="--options=none --pad-header --unpad-paren --suffix=none --convert-tabs --attach-inlines --attach-classes --attach-namespaces" +RCFILE=.astylerc -$ASTYLE $style $options cli/*.cpp -$ASTYLE $style $options cli/*.h -$ASTYLE $style $options democlient/*.cpp -$ASTYLE $style $options gui/*.cpp -$ASTYLE $style $options gui/*.h -$ASTYLE $style $options -r gui/test/*.cpp -$ASTYLE $style $options -r gui/test/*.h -$ASTYLE $style $options lib/*.cpp -$ASTYLE $style $options lib/*.h -$ASTYLE $style $options test/*.cpp -$ASTYLE $style $options test/cfg/*.c -$ASTYLE $style $options test/cfg/*.cpp -$ASTYLE $style $options test/*.h +"$ASTYLE" --options=$RCFILE cli/*.cpp +"$ASTYLE" --options=$RCFILE cli/*.h +"$ASTYLE" --options=$RCFILE democlient/*.cpp +"$ASTYLE" --options=$RCFILE gui/*.cpp +"$ASTYLE" --options=$RCFILE gui/*.h +"$ASTYLE" --options=$RCFILE --recursive "gui/test/*.cpp" +"$ASTYLE" --options=$RCFILE --recursive "gui/test/*.h" +"$ASTYLE" --options=$RCFILE lib/*.cpp +"$ASTYLE" --options=$RCFILE lib/*.h +"$ASTYLE" --options=$RCFILE test/*.cpp +"$ASTYLE" --options=$RCFILE test/cfg/*.c +"$ASTYLE" --options=$RCFILE test/cfg/*.cpp +"$ASTYLE" --options=$RCFILE test/*.h -$ASTYLE $style $options --recursive "tools/*.cpp" -$ASTYLE $style $options --recursive "tools/*.h" +"$ASTYLE" --options=$RCFILE --recursive "tools/*.cpp" +"$ASTYLE" --options=$RCFILE --recursive "tools/*.h" -$ASTYLE $style $options --recursive "samples/*.c" -$ASTYLE $style $options --recursive "samples/*.cpp" +"$ASTYLE" --options=$RCFILE --recursive "samples/*.c" +"$ASTYLE" --options=$RCFILE --recursive "samples/*.cpp" # Convert tabs to spaces.. even in strings # sed -i "s/\t/ /g" test/test*.cpp @@ -47,14 +45,17 @@ # TODO: use other tool than xmllint? use tabs instead of spaces? for CFGFILE in cfg/*.cfg do - xmllint --format -o ${CFGFILE}_ ${CFGFILE} - mv -f ${CFGFILE}_ ${CFGFILE} + xmllint --format -o "${CFGFILE}_" "$CFGFILE" + mv -f "${CFGFILE}_" "$CFGFILE" done for PLATFORMFILE in platforms/*.xml do - xmllint --format -o ${PLATFORMFILE}_ ${PLATFORMFILE} - mv -f ${PLATFORMFILE}_ ${PLATFORMFILE} + xmllint --format -o "${PLATFORMFILE}_" "$PLATFORMFILE" + mv -f "${PLATFORMFILE}_" "$PLATFORMFILE" done xmllint --format -o man/cppcheck.1.xml_ man/cppcheck.1.xml mv -f man/cppcheck.1.xml_ man/cppcheck.1.xml + +xmllint --format -o cppcheck-errors_.rng cppcheck-errors.rng +mv cppcheck-errors_.rng cppcheck-errors.rng diff -Nru cppcheck-1.85/runastyle.bat cppcheck-1.86/runastyle.bat --- cppcheck-1.85/runastyle.bat 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/runastyle.bat 2018-12-08 07:18:21.000000000 +0000 @@ -19,28 +19,27 @@ GOTO EXIT_ERROR ) -@SET STYLE=--style=kr --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0 -@SET OPTIONS=--pad-header --unpad-paren --suffix=none --convert-tabs --attach-inlines --attach-classes --attach-namespaces +@SET RCFILE=.astylerc -%ASTYLE% %STYLE% %OPTIONS% cli/*.cpp -%ASTYLE% %STYLE% %OPTIONS% cli/*.h -%ASTYLE% %STYLE% %OPTIONS% democlient/*.cpp -%ASTYLE% %STYLE% %OPTIONS% gui/*.cpp -%ASTYLE% %STYLE% %OPTIONS% gui/*.h -%ASTYLE% %STYLE% %OPTIONS% -r gui/test/*.cpp -%ASTYLE% %STYLE% %OPTIONS% -r gui/test/*.h -%ASTYLE% %STYLE% %OPTIONS% lib/*.cpp -%ASTYLE% %STYLE% %OPTIONS% lib/*.h -%ASTYLE% %STYLE% %OPTIONS% test/*.cpp -%ASTYLE% %STYLE% %OPTIONS% test/cfg/*.c -%ASTYLE% %STYLE% %OPTIONS% test/cfg/*.cpp -%ASTYLE% %STYLE% %OPTIONS% test/*.h +%ASTYLE% --options=%RCFILE% cli/*.cpp +%ASTYLE% --options=%RCFILE% cli/*.h +%ASTYLE% --options=%RCFILE% democlient/*.cpp +%ASTYLE% --options=%RCFILE% gui/*.cpp +%ASTYLE% --options=%RCFILE% gui/*.h +%ASTYLE% --options=%RCFILE% -r gui/test/*.cpp +%ASTYLE% --options=%RCFILE% -r gui/test/*.h +%ASTYLE% --options=%RCFILE% lib/*.cpp +%ASTYLE% --options=%RCFILE% lib/*.h +%ASTYLE% --options=%RCFILE% test/*.cpp +%ASTYLE% --options=%RCFILE% test/cfg/*.c +%ASTYLE% --options=%RCFILE% test/cfg/*.cpp +%ASTYLE% --options=%RCFILE% test/*.h -%ASTYLE% %STYLE% %OPTIONS% -r tools/*.cpp -%ASTYLE% %STYLE% %OPTIONS% -r tools/*.h +%ASTYLE% --options=%RCFILE% -r tools/*.cpp +%ASTYLE% --options=%RCFILE% -r tools/*.h -%ASTYLE% %STYLE% %OPTIONS% -r samples/*.c -%ASTYLE% %STYLE% %OPTIONS% -r samples/*.cpp +%ASTYLE% --options=%RCFILE% -r samples/*.c +%ASTYLE% --options=%RCFILE% -r samples/*.cpp @REM Format configuration files @SET XMLLINT=xmllint diff -Nru cppcheck-1.85/test/cfg/gnu.c cppcheck-1.86/test/cfg/gnu.c --- cppcheck-1.85/test/cfg/gnu.c 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/cfg/gnu.c 2018-12-08 07:18:21.000000000 +0000 @@ -8,10 +8,35 @@ // #include +#include +#include #ifndef __CYGWIN__ #include #endif +void ignoreleak(void) +{ + char *p = (char *)malloc(10); + __builtin_memset(&(p[0]), 0, 10); + // cppcheck-suppress memleak +} + +void uninitvar__builtin_memset(void) +{ + void *s; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)__builtin_memset(s,c,n); +} + +void bufferAccessOutOfBounds__builtin_memset(void) +{ + uint8_t buf[42]; + // cppcheck-suppress bufferAccessOutOfBounds + (void)__builtin_memset(buf,0,1000); +} + void bufferAccessOutOfBounds() { char buf[2]; diff -Nru cppcheck-1.85/test/cfg/std.c cppcheck-1.86/test/cfg/std.c --- cppcheck-1.85/test/cfg/std.c 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/cfg/std.c 2018-12-08 07:18:21.000000000 +0000 @@ -32,27 +32,27 @@ fgets(a,6,stdin); sprintf(a, "ab%s", "cd"); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy sprintf(a, "ab%s", "cde"); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy snprintf(a, 5, "abcde%i", 1); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds snprintf(a, 6, "abcde%i", 1); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strcpy(a, "abcde"); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strncpy(a,"abcde",6); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strncpy(a,"a",6); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy strncpy(a,"abcdefgh",4); // valid call strncpy_s(a,5,"abcd",5); @@ -2901,6 +2901,19 @@ (void)strchr(cs,c); } +void invalidFunctionArg_strchr(char *cs, int c) +{ + // cppcheck-suppress invalidFunctionArg + (void)strchr(cs,-1); + + // No warning shall be issued for + (void)strchr(cs, 0); + (void)strchr(cs, 255); + + // cppcheck-suppress invalidFunctionArg + (void)strchr(cs, 256); +} + void uninitvar_wcschr(void) { wchar_t *cs; @@ -3797,6 +3810,24 @@ (void)toupper(255); } +void invalidFunctionArgString(char c) +{ + /* cppcheck-suppress invalidFunctionArgStr */ + (void)atoi(&c); + char x = 'x'; + /* cppcheck-suppress invalidFunctionArgStr */ + (void)strlen(&x); + + char y = '\0'; + (void)strlen(&y); + + // #5225 + char str[80] = "hello worl"; + char d='d'; + /* cppcheck-suppress invalidFunctionArgStr */ + (void)strcat(str,&d); +} + void ignoredReturnValue_abs(int i) { // cppcheck-suppress ignoredReturnValue diff -Nru cppcheck-1.85/test/cfg/std.cpp cppcheck-1.86/test/cfg/std.cpp --- cppcheck-1.85/test/cfg/std.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/cfg/std.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -27,17 +27,60 @@ #include #include +void returnValue_std_isgreater(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isgreater(4,2) == 0) {} + // @todo support floats + if (std::isgreater(4.0f,2.0f) == 0) {} +} + +void returnValue_std_isgreaterequal(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isgreaterequal(4,2) == 0) {} + // @todo support floats + if (std::isgreaterequal(4.0f,2.0f) == 0) {} +} + +void returnValue_std_isless(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isless(4,2) == 0) {} + // @todo support floats + if (std::isless(4.0f,2.0f) == 0) {} +} + +void returnValue_std_islessequal(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::islessequal(4,2) == 0) {} + // @todo support floats + if (std::islessequal(4.0f,2.0f) == 0) {} +} + +void returnValue_std_islessgreater(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::islessgreater(4,2) == 0) {} + // cppcheck-suppress knownConditionTrueFalse + if (std::islessgreater(2,4) == 0) {} + + if (std::islessgreater(4.0f,2.0f) == 0) {} // @todo support floats + if (std::islessgreater(2.0f,4.0f) == 0) {} // @todo support floats +} + void bufferAccessOutOfBounds(void) { char a[5]; std::strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy std::strcpy(a, "abcde"); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy std::strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy std::strncpy(a,"abcde",6); } diff -Nru cppcheck-1.85/test/cfg/windows.cpp cppcheck-1.86/test/cfg/windows.cpp --- cppcheck-1.85/test/cfg/windows.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/cfg/windows.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -135,6 +135,7 @@ WSACleanup(); wordInit = MAKEWORD(1, 2); + // cppcheck-suppress redundantAssignment dwordInit = MAKELONG(1, 2); // cppcheck-suppress redundantAssignment wordInit = LOWORD(dwordInit); @@ -210,13 +211,13 @@ RtlCompareMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds RtlMoveMemory(byteBuf, byteBuf2, 20); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds MoveMemory(byteBuf, byteBuf2, 20); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds RtlCopyMemory(byteBuf, byteBuf2, 20); - // cppcheck-suppress redundantCopy + // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds CopyMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds diff -Nru cppcheck-1.85/test/cfg/wxwidgets.cpp cppcheck-1.86/test/cfg/wxwidgets.cpp --- cppcheck-1.85/test/cfg/wxwidgets.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/cfg/wxwidgets.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -20,6 +20,8 @@ #include #include #include +#include +#include void validCode() { @@ -76,7 +78,26 @@ unsigned long long * ulongLongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToULongLong(ulongLongPtr); +} +void nullPointer_wxSizer_Add(wxSizer &sizer, wxWindow *w) +{ + wxWindow * const ptr = 0; + // @todo cppcheck-suppress nullPointer + sizer.Add(ptr); + // No warning shall be issued for + sizer.Add(w); +} + +void uninitvar_wxSizer_Add(wxSizer &sizer, wxWindow *w,wxObject* userData) +{ + int uninit; + // cppcheck-suppress uninitvar + sizer.Add(w,uninit); + // cppcheck-suppress uninitvar + sizer.Add(w,4,uninit); + // cppcheck-suppress uninitvar + sizer.Add(w,4,2,uninit,userData); } void ignoredReturnValue(const wxString &s) @@ -110,17 +131,29 @@ (void)str.ToLong(&l, 37); } -void uninitvar(void) +void uninitvar(wxWindow &w) { wxLogLevel logLevelUninit; char cBufUninit[10]; char *pcUninit; + bool uninitBool; // cppcheck-suppress uninitvar wxLogGeneric(logLevelUninit, "test"); // cppcheck-suppress uninitvar wxLogMessage(cBufUninit); // cppcheck-suppress uninitvar wxLogMessage(pcUninit); + // cppcheck-suppress uninitvar + w.Close(uninitBool); +} + +void uninitvar_wxStaticText(wxStaticText &s) +{ + // no warning + s.Wrap(-1); + bool uninitBool; + // cppcheck-suppress uninitvar + s.Wrap(uninitBool); } void uninitvar_wxString_NumberConversion(const wxString &str, const int numberBase) diff -Nru cppcheck-1.85/test/CMakeLists.txt cppcheck-1.86/test/CMakeLists.txt --- cppcheck-1.85/test/CMakeLists.txt 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/CMakeLists.txt 2018-12-08 07:18:21.000000000 +0000 @@ -23,4 +23,7 @@ add_test(NAME testrunner COMMAND testrunner WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + add_custom_target(check COMMAND $ -q WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + add_dependencies(check testrunner) + endif() diff -Nru cppcheck-1.85/test/testastutils.cpp cppcheck-1.86/test/testastutils.cpp --- cppcheck-1.85/test/testastutils.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testastutils.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -33,9 +33,43 @@ private: void run() override { + TEST_CASE(findLambdaEndToken); TEST_CASE(isReturnScope); TEST_CASE(isVariableChanged); TEST_CASE(isVariableChangedByFunctionCall); + TEST_CASE(nextAfterAstRightmostLeaf); + } + + bool findLambdaEndToken(const char code[]) { + Settings settings; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + const Token * const tokEnd = ::findLambdaEndToken(tokenizer.tokens()); + return tokEnd && tokEnd->next() == nullptr; + } + + void findLambdaEndToken() { + ASSERT(nullptr == ::findLambdaEndToken(nullptr)); + ASSERT_EQUALS(false, findLambdaEndToken("void f() { }")); + ASSERT_EQUALS(true, findLambdaEndToken("[]{ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[]{ return 0 }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[&](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[&, i](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) { return -1 }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) { return a + b }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) mutable { return a + b }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) constexpr { return a + b }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) -> int { return -1 }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> int { return -1 }")); + ASSERT_EQUALS(false, findLambdaEndToken("[](void) foo -> int { return -1 }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int { return -1 }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int* { return x }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * int { return x }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> const * int { return x }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const ** int { return x }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * const* int { return x }")); } bool isReturnScope(const char code[], int offset) { @@ -96,6 +130,29 @@ ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); ASSERT_EQUALS(true, inconclusive); } + + bool nextAfterAstRightmostLeaf(const char code[], const char parentPattern[], const char rightPattern[]) { + Settings settings; + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + const Token * tok = Token::findsimplematch(tokenizer.tokens(), parentPattern); + return Token::simpleMatch(::nextAfterAstRightmostLeaf(tok), rightPattern); + } + + void nextAfterAstRightmostLeaf() { + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f(int a, int b) { int x = a + b; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b]; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b]); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b] + a); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f() { int a; int b; int x = [](int a){}; }", "=", "; }")); + + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = a + b; }", "+", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "+", "] ; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a + 1)[b]; }", "+", ") [")); + } }; REGISTER_TEST(TestAstUtils) diff -Nru cppcheck-1.85/test/testautovariables.cpp cppcheck-1.86/test/testautovariables.cpp --- cppcheck-1.85/test/testautovariables.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testautovariables.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -45,13 +45,13 @@ CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this); checkAutoVariables.returnReference(); checkAutoVariables.assignFunctionArg(); + checkAutoVariables.checkVarLifetime(); if (runSimpleChecks) { tokenizer.simplifyTokenList2(); // Check auto variables checkAutoVariables.autoVariables(); - checkAutoVariables.returnPointerToLocalArray(); } } @@ -75,12 +75,14 @@ TEST_CASE(testautovar13); // ticket #5537 - crash TEST_CASE(testautovar14); // ticket #4776 - assignment of function parameter, goto TEST_CASE(testautovar15); // ticket #6538 + TEST_CASE(testautovar16); // ticket #8114 TEST_CASE(testautovar_array1); TEST_CASE(testautovar_array2); TEST_CASE(testautovar_ptrptr); // ticket #6956 TEST_CASE(testautovar_return1); TEST_CASE(testautovar_return2); TEST_CASE(testautovar_return3); + TEST_CASE(testautovar_return4); TEST_CASE(testautovar_extern); TEST_CASE(testinvaliddealloc); TEST_CASE(testinvaliddealloc_C); @@ -118,6 +120,12 @@ TEST_CASE(testconstructor); // ticket #5478 - crash TEST_CASE(variableIsUsedInScope); // ticket #5599 crash in variableIsUsedInScope() + + TEST_CASE(danglingLifetimeLambda); + TEST_CASE(danglingLifetimeContainer); + TEST_CASE(danglingLifetime); + TEST_CASE(danglingLifetimeFunction); + TEST_CASE(invalidLifetime); } @@ -432,6 +440,14 @@ ASSERT_EQUALS("", errout.str()); } + void testautovar16() { // Ticket #8114 + check("void f(const void* ptr, bool* result) {\n" + " int dummy;\n" + " *result = (&dummy < ptr);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void testautovar_array1() { check("void func1(int* arr[2])\n" "{\n" @@ -467,7 +483,7 @@ " int num=2;" " return #" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Address of an auto-variable returned.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:3]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return2() { @@ -479,7 +495,7 @@ " int num=2;" " return #" "}"); - ASSERT_EQUALS("[test.cpp:6]: (error) Address of an auto-variable returned.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return3() { @@ -492,6 +508,15 @@ ASSERT_EQUALS("", errout.str()); } + void testautovar_return4() { + // #8058 - FP ignore return in lambda + check("void foo() {\n" + " int cond2;\n" + " dostuff([&cond2]() { return &cond2; });\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void testautovar_extern() { check("struct foo *f()\n" "{\n" @@ -527,6 +552,20 @@ "}"); ASSERT_EQUALS("", errout.str()); + check("void func1() {\n" + " static char tmp1[256];\n" + " char *p = tmp1;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of an static variable (tmp1) results in undefined behaviour.\n", errout.str()); + + check("char tmp1[256];\n" + "void func1() {\n" + " char *p; if (x) p = tmp1;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of an global variable (tmp1) results in undefined behaviour.\n", errout.str()); + check("void f()\n" "{\n" " char psz_title[10];\n" @@ -677,7 +716,9 @@ " char str[100] = {0};\n" " return str;\n" "}"); - ASSERT_EQUALS("[test.cpp:4]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout.str()); check("char *foo()\n" // use ValueFlow "{\n" @@ -685,7 +726,9 @@ " char *p = str;\n" " return p;\n" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout.str()); check("class Fred {\n" " char *foo();\n" @@ -695,7 +738,9 @@ " char str[100] = {0};\n" " return str;\n" "}"); - ASSERT_EQUALS("[test.cpp:7]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout.str()); check("char * format_reg(char *outbuffer_start) {\n" " return outbuffer_start;\n" @@ -734,7 +779,7 @@ " char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout.str()); check("char *foo()\n" "{\n" @@ -749,13 +794,17 @@ " char x[10] = {0};\n" " return x+5;\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout.str()); check("char *foo(int y) {\n" " char x[10] = {0};\n" " return (x+8)-y;\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout.str()); } void returnLocalVariable5() { // cast @@ -763,7 +812,9 @@ " int x[10] = {0};\n" " return (char *)x;\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout.str()); } void returnLocalVariable6() { // valueflow @@ -772,7 +823,7 @@ " int p = &x;\n" " return p;\n" "}"); - ASSERT_EQUALS("[test.cpp:4]: (error) Address of auto-variable 'x' returned\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnReference1() { @@ -1147,14 +1198,14 @@ " return &y;\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Address of function parameter 'y' returned.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("int ** foo(int * y)\n" "{\n" " return &y;\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Address of function parameter 'y' returned.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("const int * foo(const int & y)\n" "{\n" @@ -1162,6 +1213,13 @@ "}"); ASSERT_EQUALS("", errout.str()); + + check("int * foo(int * y)\n" + "{\n" + " return y;\n" + "}"); + + ASSERT_EQUALS("", errout.str()); } void testconstructor() { // Ticket #5478 - crash while checking a constructor @@ -1183,6 +1241,462 @@ "}"); } + void danglingLifetimeLambda() { + check("auto f() {\n" + " int a = 1;\n" + " auto l = [&](){ return a; };\n" + " return l;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto f() {\n" + " int a = 1;\n" + " return [&](){ return a; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto f(int a) {\n" + " return [&](){ return a; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto f(int a) {\n" + " auto p = &a;\n" + " return [=](){ return p; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto g(int& a) {\n" + " int p = a;\n" + " return [&](){ return p; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'p' that will be invalid when returning.\n", errout.str()); + + check("auto f() {\n" + " return [=](){\n" + " int a = 1;\n" + " return [&](){ return a; };\n" + " };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + // TODO: Variable is not set correctly for this case + check("auto f(int b) {\n" + " return [=](int a){\n" + " a += b;\n" + " return [&](){ return a; };\n" + " };\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", "", errout.str()); + + check("auto g(int& a) {\n" + " return [&](){ return a; };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto g(int a) {\n" + " auto p = a;\n" + " return [=](){ return p; };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto g(int& a) {\n" + " auto p = a;\n" + " return [=](){ return p; };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto g(int& a) {\n" + " int& p = a;\n" + " return [&](){ return p; };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("template\n" + "void g(F);\n" + "auto f() {\n" + " int x;\n" + " return g([&]() { return x; });\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + void danglingLifetimeContainer() { + check("auto f(const std::vector& x) {\n" + " auto it = x.begin();\n" + " return it;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return it;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto p = x.data();\n" + " return p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); + + check("auto f(std::vector x) {\n" + " auto it = x.begin();\n" + " return it;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return std::next(it);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", + errout.str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return it + 1;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", + errout.str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return std::next(it + 1);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", + errout.str()); + + check("std::vector f() {\n" + " int i = 0;\n" + " std::vector v;\n" + " v.push_back(&i);\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:5]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout.str()); + + check("std::vector f() {\n" + " std::vector r;\n" + " int i = 0;\n" + " std::vector v;\n" + " v.push_back(&i);\n" + " r.assign(v.begin(), v.end());\n" + " return r;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout.str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " int i;\n" + " v.push_back(&i);\n" + " }\n" + "};\n"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", + errout.str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " int i;\n" + " int * p = &i;\n" + " v.push_back(p);\n" + " }\n" + "};\n"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:6]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", + errout.str()); + + check("struct A {\n" + " std::vector m;\n" + " void f() {\n" + " int x;\n" + " std::vector v;\n" + " v.push_back(&x);\n" + " m.insert(m.end(), v.begin(), v.end());\n" + " }\n" + "};\n"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:7]: (error) Non-local variable 'm' will use object that points to local variable 'x'.\n", + errout.str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " char s[3];\n" + " v.push_back(s);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + check("std::vector f() {\n" + " const char * s = \"hello\";\n" + " std::vector v;\n" + " v.push_back(s);\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto f() {\n" + " static std::vector x;\n" + " return x.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("std::string g() {\n" + " std::vector v;\n" + " return v.data();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("std::vector::iterator f(std::vector* v) {\n" + " return v->begin();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("std::vector::iterator f(std::vector* v) {\n" + " std::vector* v = new std::vector();\n" + " return v->begin();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int f(std::vector v) {\n" + " return *v.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int f(std::vector v) {\n" + " return v.end() - v.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("auto g() {\n" + " std::vector v;\n" + " return {v, [v]() { return v.data(); }};\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("template\n" + "void g(F);\n" + "auto f() {\n" + " std::vector v;\n" + " return g([&]() { return v.data(); });\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("std::vector g();\n" + "struct A {\n" + " std::vector m;\n" + " void f() {\n" + " std::vector v = g();\n" + " m.insert(m.end(), v.begin(), v.end());\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + check("class A {\n" + " int f( P p ) {\n" + " std::vector< S > maps;\n" + " m2.insert( m1.begin(), m1.end() );\n" + " }\n" + " struct B {};\n" + " std::map< S, B > m1;\n" + " std::map< S, B > m2;\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + } + + void danglingLifetime() { + check("auto f() {\n" + " std::vector a;\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto f(std::vector a) {\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); + + check("auto f(std::vector& a) {\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int * f(int a[]) {\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " struct b {\n" + " uint32_t f[6];\n" + " } d;\n" + " uint32_t *a = d.f;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Dont decay std::array + check("std::array f() {\n" + " std::array x;\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Make sure we dont hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Make sure we dont hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1]();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Make sure we dont hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Make sure we dont hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1]();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // Crash #8872 + check("struct a {\n" + " void operator()(b c) override {\n" + " d(c, [&] { c->e });\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct a {\n" + " void operator()(b c) override {\n" + " d(c, [=] { c->e });\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + } + + void danglingLifetimeFunction() { + check("auto f() {\n" + " int a;\n" + " return std::ref(a);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", + errout.str()); + + check("auto f() {\n" + " int a;\n" + " return std::make_tuple(std::ref(a));\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", + errout.str()); + + check("auto f(int x) {\n" + " int a;\n" + " std::tie(a) = x;\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + void invalidLifetime() { + check("void foo(int a) {\n" + " std::function f;\n" + " if (a > 0) {\n" + " int b = a + 1;\n" + " f = [&]{ return b; };\n" + " }\n" + " f();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using lambda that captures local variable 'b' that is out of scope.\n", errout.str()); + + check("void f(bool b) {\n" + " int* x;\n" + " if(b) {\n" + " int y[6] = {0,1,2,3,4,5};\n" + " x = y;\n" + " }\n" + " x[3];\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n", + errout.str()); + + check("void foo(int a) {\n" + " std::function f;\n" + " if (a > 0) {\n" + " int b = a + 1;\n" + " f = [&]{ return b; };\n" + " f();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct a {\n" + " b();\n" + " std::list c;\n" + "};\n" + "void a::b() {\n" + " c.end()\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void b(char f[], char c[]) {\n" + " std::string d(c); {\n" + " std::string e;\n" + " b(f, e.c_str())\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void f(bool b) {\n" + " std::string s;\n" + " if(b) {\n" + " char buf[3];\n" + " s = buf;\n" + " }\n" + " std::cout << s;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int &a[];\n" + "void b(){int *c = a};\n"); + ASSERT_EQUALS("", errout.str()); + } + }; REGISTER_TEST(TestAutoVariables) diff -Nru cppcheck-1.85/test/testbool.cpp cppcheck-1.86/test/testbool.cpp --- cppcheck-1.85/test/testbool.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testbool.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -64,6 +64,8 @@ // Converting pointer addition result to bool TEST_CASE(pointerArithBool1); + + TEST_CASE(returnNonBool); } void check(const char code[], bool experimental = false, const char filename[] = "test.cpp") { @@ -1008,6 +1010,103 @@ "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); } + + void returnNonBool() { + check("bool f(void) {\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + " return 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " return 1 + 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " int x = 0;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + " int x = 10;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " return 2 < 1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + " int ret = 0;\n" + " if (a)\n" + " ret = 1;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + " int ret = 0;\n" + " if (a)\n" + " ret = 3;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " if (a)\n" + " return 3;\n" + " return 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n" + "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + "auto x = [](void) { return -1; };\n" + "return false;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + "auto x = [](void) { return -1; };\n" + "return 2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + + check("bool f(void) {\n" + "auto x = [](void) -> int { return -1; };\n" + "return false;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(void) {\n" + "auto x = [](void) -> int { return -1; };\n" + "return 2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); + } }; REGISTER_TEST(TestBool) diff -Nru cppcheck-1.85/test/testbufferoverrun.cpp cppcheck-1.86/test/testbufferoverrun.cpp --- cppcheck-1.85/test/testbufferoverrun.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testbufferoverrun.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -235,7 +235,7 @@ TEST_CASE(executionPaths5); // Ticket #2920 - False positive when size is unknown TEST_CASE(executionPaths6); // unknown types - TEST_CASE(cmdLineArgs1); + TEST_CASE(insecureCmdLineArgs); TEST_CASE(checkBufferAllocatedWithStrlen); TEST_CASE(scope); // handling different scopes @@ -3750,8 +3750,48 @@ ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 1000, which is out of bounds.\n", errout.str()); } - void cmdLineArgs1() { - check("int main(int argc, char* argv[])\n" + void insecureCmdLineArgs() { + check("int main(int argc, char *argv[])\n" + "{\n" + " if(argc>1)\n" + " {\n" + " char buf[2];\n" + " char *p = strdup(argv[1]);\n" + " strcpy(buf,p);\n" + " free(p);\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(int argc, char *argv[])\n" + "{\n" + " if(argc>1)\n" + " {\n" + " char buf[2] = {'\\0','\\0'};\n" + " char *p = strdup(argv[1]);\n" + " strcat(buf,p);\n" + " free(p);\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(const int argc, char* argv[])\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(int argc, const char* argv[])\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(const int argc, const char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" @@ -3776,6 +3816,27 @@ "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(const int argc, const char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(int argc, const char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); + + check("int main(const int argc, char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); diff -Nru cppcheck-1.85/test/testclass.cpp cppcheck-1.86/test/testclass.cpp --- cppcheck-1.85/test/testclass.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testclass.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -171,6 +171,7 @@ TEST_CASE(const62); // ticket #5701 TEST_CASE(const63); // ticket #5983 TEST_CASE(const64); // ticket #6268 + TEST_CASE(const65); // ticket #8693 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); @@ -1236,6 +1237,90 @@ "};\n" "A::B & A::B::operator=(const A::B &b) { return b; }"); ASSERT_EQUALS("[test.cpp:10]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " B & operator=(const B & b) { return b; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " B & operator=(const B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }\n"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B & b) { return b; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }\n"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " B & operator=(const B & b) { return b; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " B & operator=(const B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }\n"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B & b) { return b; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }\n"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); } void operatorEqRetRefThis2() { @@ -1243,7 +1328,7 @@ checkOpertorEqRetRefThis( "class szp\n" "{\n" - " szp &operator =(int *other) {};\n" + " szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); @@ -1254,6 +1339,90 @@ "};\n" "szp &szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " szp &operator =(int *other) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " szp &operator =(int *other);\n" + "};\n" + "NS::szp &NS::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " NS::szp &operator =(int *other) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " NS::szp &operator =(int *other);\n" + "};\n" + "NS::szp &NS::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " szp &operator =(int *other) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " szp &operator =(int *other);\n" + "};\n" + "A::szp &A::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " A::szp &operator =(int *other) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " A::szp &operator =(int *other);\n" + "};\n" + "A::szp &A::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); } void operatorEqRetRefThis3() { @@ -3161,13 +3330,13 @@ checkConst("class Fred {\n" " const std::string foo() { return \"\"; }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred {\n" " std::string s;\n" " const std::string & foo() { return \"\"; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // constructors can't be const.. checkConst("class Fred {\n" @@ -3206,7 +3375,7 @@ " int x;\n" " void b() { a(); }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::b' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::b' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // static functions can't be const.. checkConst("class foo\n" @@ -3220,7 +3389,7 @@ checkConst("class Fred {\n" " const std::string foo() const throw() { return \"\"; }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const2() { @@ -3332,7 +3501,7 @@ " const std::string & foo();\n" "};\n" "const std::string & Fred::foo() { return \"\"; }"); - ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // functions with a function call to a non-const member can't be const.. (#1305) checkConst("class Fred\n" @@ -3468,7 +3637,7 @@ "void Fred::foo() { }" "void Fred::foo(std::string & a) { a = s; }" "void Fred::foo(const std::string & a) { s = a; }"); - ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n" + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // check functions with different or missing parameter names @@ -3485,11 +3654,11 @@ "void Fred::foo3(int a, int b) { }\n" "void Fred::foo4(int a, int b) { }\n" "void Fred::foo5(int, int) { }"); - ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo1' can be static.\n" - "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static.\n" - "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static.\n" - "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static.\n" - "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo1' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // check nested classes checkConst("class Fred {\n" @@ -3720,7 +3889,7 @@ "public:\n" " void foo() { }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct fast_string\n" "{\n" @@ -4157,7 +4326,7 @@ "public:\n" " void set(int i) { x = i; }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::set' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::set' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const19() { @@ -4404,7 +4573,7 @@ " UnknownScope::x = x_;\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'AA::vSetXPos' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'AA::vSetXPos' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } @@ -4832,7 +5001,7 @@ "{\n" "}"); - ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred\n" "{\n" @@ -4846,7 +5015,7 @@ "{\n" "}"); - ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" @@ -4862,7 +5031,7 @@ " }\n" "}"); - ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" @@ -4878,7 +5047,7 @@ "{\n" "}"); - ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Foo {\n" " class Fred\n" @@ -4894,7 +5063,7 @@ "{\n" "}"); - ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'Foo::Fred::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'Foo::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const43() { // ticket 2377 @@ -4983,7 +5152,7 @@ " };\n" "}"); - ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Technically the member function 'tools::WorkspaceControl::toGrid' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Technically the member function 'tools::WorkspaceControl::toGrid' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const46() { // ticket 2663 @@ -4998,8 +5167,8 @@ " }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Altren::fun1' can be static.\n" - "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Altren::fun1' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const47() { // ticket 2670 @@ -5010,7 +5179,7 @@ " void bar() { foo(); }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Altren {\n" "public:\n" @@ -5019,7 +5188,7 @@ " void bar() { foo(1); }\n" "};"); - ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static.\n" + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'Altren::bar' can be const.\n", errout.str()); } @@ -5110,7 +5279,7 @@ "private:\n" " int bar;\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'foo::DoSomething' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'foo::DoSomething' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const53() { // ticket 3049 @@ -5154,7 +5323,7 @@ " switch (x) { }\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class A\n" "{\n" @@ -5194,7 +5363,7 @@ "\n" " return RET_NOK;\n" "}"); - ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'A::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'A::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class MyObject {\n" "public:\n" @@ -5202,7 +5371,7 @@ " for (int i = 0; i < 5; i++) { }\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const57() { // tickets #2669 and #2477 @@ -5227,9 +5396,9 @@ "private:\n" " MyGUI::IntCoord mCoordValue;\n" "};"); - TODO_ASSERT_EQUALS("[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static.\n" + TODO_ASSERT_EQUALS("[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:15]: (style, inconclusive) Technically the member function 'SelectorControl::getSize' can be const.\n", - "[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static.\n", errout.str()); + "[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct Foo {\n" " Bar b;\n" @@ -5260,7 +5429,7 @@ " b.run();\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Bar::run' can be static.\n" + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Bar::run' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:6]: (style, inconclusive) Technically the member function 'Foo::foo' can be const.\n", errout.str()); } @@ -5270,14 +5439,14 @@ " f.clear();\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " int foo(Foo f) {\n" " return f.length();\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " Foo f;\n" @@ -5444,6 +5613,25 @@ ASSERT_EQUALS("", errout.str()); } + void const65() { + checkConst("template \n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "template <>\n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "int main() {\n" + " TemplateClass a;\n" + " TemplateClass b;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n" @@ -5474,8 +5662,8 @@ " return foo3();\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:11]: (performance, inconclusive) Technically the member function 'Foo::bar3' can be static.\n" - "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:11]: (performance, inconclusive) Technically the member function 'Foo::bar3' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const_passThisToMemberOfOtherClass() { @@ -5493,7 +5681,7 @@ " f.foo();\n" " }\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Foo::foo' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Foo::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A;\n" // #5839 - operator() "struct B {\n" @@ -5563,25 +5751,25 @@ "class Fred {\n" " void nextA() { return ++a; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return --a; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a++; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a--; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign1() { @@ -5619,31 +5807,31 @@ "class Fred {\n" " void nextA() { return a=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a-=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a+=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a*=-1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a/=-2; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign2() { @@ -5675,31 +5863,31 @@ "class Fred {\n" " void nextA() { return s.a=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a-=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a+=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a*=-1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a/=-2; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" @@ -5767,25 +5955,25 @@ "class Fred {\n" " void nextA() { return ++a[0]; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return --a[0]; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]++; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]--; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassignarray() { @@ -5823,31 +6011,31 @@ "class Fred {\n" " void nextA() { return a[0]=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]-=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]+=1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]*=-1; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]/=-2; }\n" "};"); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } // return pointer/reference => not const @@ -5890,7 +6078,7 @@ " void f() const { };\n" " void a() { f(); };\n" "};"); - ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::f' can be static.\n" + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::a' can be const.\n", errout.str()); // ticket #1593 @@ -6151,7 +6339,7 @@ "};"; checkConst(code, &settings0, true); - ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst(code, &settings0, false); // TODO: Set inconclusive to true (preprocess it) ASSERT_EQUALS("", errout.str()); diff -Nru cppcheck-1.85/test/testcondition.cpp cppcheck-1.86/test/testcondition.cpp --- cppcheck-1.85/test/testcondition.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testcondition.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -38,6 +38,7 @@ void run() override { LOAD_LIB_2(settings0.library, "qt.cfg"); + LOAD_LIB_2(settings0.library, "std.cfg"); settings0.addEnabled("style"); settings0.addEnabled("warning"); @@ -1193,8 +1194,7 @@ " else\n" " return;\n" "}"); - TODO_ASSERT_EQUALS("", "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: neg < -1.0 && neg > -1.0.\n", errout.str()); - + ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator8() { // opposite expressions @@ -1441,6 +1441,13 @@ "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); + check("bool foo(int a, int b) {\n" + " if(a==b)\n" + " return a!=b;\n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'return' condition leads to a dead code block.\n", errout.str()); + check("void foo(int a, int b) {\n" " if(a==b)\n" " if(b!=a)\n" @@ -1650,6 +1657,12 @@ " }\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("int * f(int * x, int * y) {\n" + " if(!x) return x;\n" + " return y;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionClass() { @@ -1895,8 +1908,7 @@ void oppositeInnerCondition3() { check("void f3(char c) { if(c=='x') if(c=='y') {}} "); - ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" - "[test.cpp:1] -> [test.cpp:1]: (style) Condition 'c=='y'' is always false\n", errout.str()); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f4(char *p) { if(*p=='x') if(*p=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); @@ -1914,8 +1926,7 @@ ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f8(int i) { if(i==4) if(i==2) {}} "); - ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" - "[test.cpp:1] -> [test.cpp:1]: (style) Condition 'i==2' is always false\n", errout.str()); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f9(int *p) { if (*p==4) if(*p==2) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); @@ -1947,8 +1958,7 @@ ASSERT_EQUALS("", errout.str()); check("void f(int x) { if (x == 1) if (x != 1) {} }"); - ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" - "[test.cpp:1] -> [test.cpp:1]: (style) Condition 'x!=1' is always false\n", errout.str()); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionAnd() { @@ -1982,9 +1992,6 @@ check("void f1(QString s) { if(s.isEmpty()) if(s.length() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); - check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}} "); - ASSERT_EQUALS("", errout.str()); - check("void f1(const std::string &s, bool b) { if(s.empty() || ((s.size() == 1) && b)) {}} "); ASSERT_EQUALS("", errout.str()); @@ -2031,6 +2038,20 @@ " int get() const;\n" "};"); ASSERT_EQUALS("", errout.str()); + + check("class C {\n" + "public:\n" + " bool f() const { return x > 0; }\n" + " void g();\n" + " int x = 0;\n" + "};\n" + "\n" + "void C::g() {\n" + " bool b = f();\n" + " x += 1;\n" + " if (!b && f()) {}\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void identicalInnerCondition() { @@ -2049,6 +2070,37 @@ "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); + check("bool f(int a, int b) {\n" + " if(a == b) { return a == b; }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Identical inner 'return' condition is always true.\n", errout.str()); + + check("bool f(bool a) {\n" + " if(a) { return a; }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int* f(int* a, int * b) {\n" + " if(a) { return a; }\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int* f(std::shared_ptr a, std::shared_ptr b) {\n" + " if(a.get()) { return a.get(); }\n" + " return b.get();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct A { int * x; };\n" + "int* f(A a, int * b) {\n" + " if(a.x) { return a.x; }\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + check("void f() {\n" " uint32_t value;\n" " get_value(&value);\n" @@ -2069,6 +2121,12 @@ "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); + check("bool f(int x) {\n" + " if (x > 100) { return false; }\n" + " return x > 100;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); + check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 || y > 100) {}\n" @@ -2100,7 +2158,7 @@ " X(do);\n" " if (x > 100) {}\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", errout.str()); check("void f(const int *i) {\n" " if (!i) return;\n" @@ -2444,6 +2502,12 @@ "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!x' is always true\n", errout.str()); + check("bool f(int x) {\n" + " if(x == 0) { x++; return x == 0; } \n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0' is always false\n", errout.str()); + check("void f() {\n" // #6898 (Token::expressionString) " int x = 0;\n" " A(x++ == 1);\n" @@ -2453,6 +2517,9 @@ "[test.cpp:4]: (style) Condition 'x++==2' is always false\n", errout.str()); + check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}} "); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.size()==0' is always true\n", errout.str()); + // Avoid FP when condition comes from macro check("#define NOT !\n" "void f() {\n" @@ -2546,6 +2613,102 @@ " {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always true\n", errout.str()); + + check("bool f() { return nullptr; }"); + ASSERT_EQUALS("", errout.str()); + + check("enum E { A };\n" + "bool f() { return A; }\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool f() { \n" + " const int x = 0;\n" + " return x; \n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int f(void){return 1/abs(10);}"); + ASSERT_EQUALS("", errout.str()); + + check("bool f() { \n" + " int x = 0;\n" + " return x; \n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool f() {\n" + " const int a = 50;\n" + " const int b = 52;\n" + " return a+b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int f() {\n" + " int a = 50;\n" + " int b = 52;\n" + " a++;\n" + " b++;\n" + " return a+b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool& g();\n" + "bool f() {\n" + " bool & b = g();\n" + " b = false;\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct A {\n" + " bool b;\n" + " bool f() {\n" + " b = false;\n" + " return b;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + check("bool f(long maxtime) {\n" + " if (std::time(0) > maxtime)\n" + " return std::time(0) > maxtime;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void foo(double param) {\n" + " while(bar()) {\n" + " if (param<0.)\n" + " return;\n" + " }\n" + " if (param<0.)\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void foo(int i) {\n" + " if (i==42)\n" + " {\n" + " bar();\n" + " }\n" + " if (cond && (42==i))\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // 8842 crash + check("class a {\n" + " int b;\n" + " c(b);\n" + " void f() {\n" + " if (b) return;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + check("void f(const char* x, const char* t) {\n" + " if (!(strcmp(x, y) == 0)) { return; }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void multiConditionAlwaysTrue() { diff -Nru cppcheck-1.85/test/testconstructors.cpp cppcheck-1.86/test/testconstructors.cpp --- cppcheck-1.85/test/testconstructors.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testconstructors.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -145,6 +145,7 @@ TEST_CASE(uninitVar29); TEST_CASE(uninitVar30); // ticket #6417 TEST_CASE(uninitVar31); // ticket #8271 + TEST_CASE(uninitVar32); // ticket #8835 TEST_CASE(uninitVarEnum1); TEST_CASE(uninitVarEnum2); // ticket #8146 TEST_CASE(uninitVarStream); @@ -171,9 +172,6 @@ TEST_CASE(privateCtor1); // If constructor is private.. TEST_CASE(privateCtor2); // If constructor is private.. TEST_CASE(function); // Function is not variable - TEST_CASE(uninitVarHeader1); // Class is defined in header - TEST_CASE(uninitVarHeader2); // Class is defined in header - TEST_CASE(uninitVarHeader3); // Class is defined in header TEST_CASE(uninitVarPublished); // Borland C++: Variables in the published section are auto-initialized TEST_CASE(uninitOperator); // No FP about uninitialized 'operator[]' TEST_CASE(uninitFunction1); // No FP when initialized in function @@ -2429,6 +2427,39 @@ ASSERT_EQUALS("", errout.str()); } + void uninitVar32() { // ticket #8835 + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " if (1) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " while (1) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " for (;;) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); + } + void uninitVarArray1() { check("class John\n" "{\n" @@ -3043,42 +3074,6 @@ } - void uninitVarHeader1() { - check("#line 1 \"fred.h\"\n" - "class Fred\n" - "{\n" - "private:\n" - " unsigned int i;\n" - "public:\n" - " Fred();\n" - "};\n"); - ASSERT_EQUALS("", errout.str()); - } - - void uninitVarHeader2() { - check("#line 1 \"fred.h\"\n" - "class Fred\n" - "{\n" - "private:\n" - " unsigned int i;\n" - "public:\n" - " Fred() { }\n" - "};\n"); - ASSERT_EQUALS("[fred.h:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); - } - - void uninitVarHeader3() { - check("#line 1 \"fred.h\"\n" - "class Fred\n" - "{\n" - "private:\n" - " mutable int i;\n" - "public:\n" - " Fred() { }\n" - "};\n"); - ASSERT_EQUALS("[fred.h:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); - } - // Borland C++: No FP for published pointers - they are automatically initialized void uninitVarPublished() { check("class Fred\n" diff -Nru cppcheck-1.85/test/testfunctions.cpp cppcheck-1.86/test/testfunctions.cpp --- cppcheck-1.85/test/testfunctions.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testfunctions.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -62,6 +62,7 @@ // Invalid function usage TEST_CASE(invalidFunctionUsage1); + TEST_CASE(invalidFunctionUsageStrings); // Math function usage TEST_CASE(mathfunctionCall_fmod); @@ -447,6 +448,113 @@ ASSERT_EQUALS("", errout.str()); } + void invalidFunctionUsageStrings() { + check("size_t f() { char x = 'x'; return strlen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("size_t f() { return strlen(&x); }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f(char x) { return strlen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("size_t f() { char x = '\\0'; return strlen(&x); }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f() {\n" + " char x;\n" + " if (y)\n" + " x = '\\0';\n" + " else\n" + " x = 'a';\n" + " return strlen(&x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("int f() { char x = '\\0'; return strcmp(\"Hello world\", &x); }"); + ASSERT_EQUALS("", errout.str()); + + check("int f() { char x = 'x'; return strcmp(\"Hello world\", &x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); + + check("size_t f(char x) { char * y = &x; return strlen(y) }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("size_t f(char x) { char * y = &x; char *z = y; return strlen(z) }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("size_t f() { char x = 'x'; char * y = &x; char *z = y; return strlen(z) }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("size_t f() { char x = '\\0'; char * y = &x; char *z = y; return strlen(z) }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f() { char x[] = \"Hello world\"; return strlen(x) }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f(char x[]) { return strlen(x) }"); + ASSERT_EQUALS("", errout.str()); + + check("int f(char x, char y) { return strcmp(&x, &y); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 1. A nul-terminated string is required.\n" + "[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); + + check("size_t f() { char x[] = \"Hello world\"; return strlen(&x[0]) }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f() { char* x = \"Hello world\"; return strlen(&x[0]) }"); + ASSERT_EQUALS("", errout.str()); + + check("struct S {\n" + " char x;\n" + "};\n" + "size_t f() {\n" + " S s1 = {0};\n" + " S s2;\n;" + " s2.x = 'x';\n" + " size_t l1 = strlen(&s1.x);\n" + " size_t l2 = strlen(&s2.x);\n" + " return l1 + l2;\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); + + check("const char x = 'x'; size_t f() { return strlen(&x); }"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); + + check("const char x = 'x'; size_t f() { char y = x; return strlen(&y); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); + + check("const char x = '\\0'; size_t f() { return strlen(&x); }"); + ASSERT_EQUALS("", errout.str()); + + check("const char x = '\\0'; size_t f() { char y = x; return strlen(&y); }"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f() {\n" + " char * a = \"Hello world\";\n" + " char ** b = &a;\n" + " return strlen(&b[0][0]);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("size_t f() {\n" + " char ca[] = \"asdf\";\n" + " return strlen((char*) &ca);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // #5225 + check("int main(void)\n" + "{\n" + " char str[80] = \"hello worl\";\n" + " char d = 'd';\n" + " strcat(str, &d);\n" + " puts(str);\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Invalid strcat() argument nr 2. A nul-terminated string is required.\n", errout.str()); + } + void mathfunctionCall_sqrt() { // sqrt, sqrtf, sqrtl check("void foo()\n" diff -Nru cppcheck-1.85/test/testgarbage.cpp cppcheck-1.86/test/testgarbage.cpp --- cppcheck-1.85/test/testgarbage.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testgarbage.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -230,6 +230,8 @@ TEST_CASE(garbageCode197); // #8385 TEST_CASE(garbageCode198); // #8383 TEST_CASE(garbageCode199); // #8752 + TEST_CASE(garbageCode200); // #8757 + TEST_CASE(garbageCode201); // #8873 TEST_CASE(garbageCodeFuzzerClientMode1); // test cases created with the fuzzer client, mode 1 @@ -782,7 +784,7 @@ } void garbageCode91() { // #6791 - checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"); // do not crash + ASSERT_THROW(checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"), InternalError); // throw syntax error } void garbageCode92() { // #6792 @@ -799,7 +801,7 @@ } void garbageCode96() { // #6807 - ASSERT_THROW(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), InternalError); + ASSERT_THROW(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), InternalError); // throw syntax error } void garbageCode97() { // #6808 @@ -975,7 +977,7 @@ } void garbageCode131() { - checkCode("( void ) { ( ) } ( ) / { ( ) }"); + ASSERT_THROW(checkCode("( void ) { ( ) } ( ) / { ( ) }"), InternalError); // actually the invalid code should trigger an syntax error... } @@ -1425,7 +1427,7 @@ } void garbageCode181() { - checkCode("int test() { int +; }"); + ASSERT_THROW(checkCode("int test() { int +; }"), InternalError); } // #4195 - segfault for "enum { int f ( ) { return = } r = f ( ) ; }" @@ -1545,6 +1547,16 @@ checkCode("d f(){e n00e0[]n00e0&""0+f=0}"); } + // #8757 + void garbageCode200() { + ASSERT_THROW(checkCode("(){e break,{(case)!{e:[]}}}"), InternalError); + } + + // #8873 + void garbageCode201() { + ASSERT_THROW(checkCode("void f() { std::string s=\"abc\"; return s + }"), InternalError); + } + void syntaxErrorFirstToken() { ASSERT_THROW(checkCode("&operator(){[]};"), InternalError); // #7818 ASSERT_THROW(checkCode("*(*const<> (size_t); foo) { } *(*const (size_t)() ; foo) { }"), InternalError); // #6858 @@ -1599,6 +1611,10 @@ // case must be inside switch block ASSERT_THROW(checkCode("void f() { switch (a) {}; case 1: }"), InternalError); // #8184 ASSERT_THROW(checkCode("struct V : { public case {} ; struct U : U void { V *f (int x) (x) } }"), InternalError); // #5120 + ASSERT_THROW(checkCode("void f() { 0 0; }"), InternalError); + ASSERT_THROW(checkCode("void f() { true 0; }"), InternalError); + ASSERT_THROW(checkCode("void f() { 'a' 0; }"), InternalError); + ASSERT_THROW(checkCode("void f() { 1 \"\"; }"), InternalError); } void enumTrailingComma() { @@ -1619,19 +1635,6 @@ // #8749 checkCode( - "typedef char A[1];\n" - "void f(void) {\n" - " char (*p)[1] = new A[1];\n" - "}\n"); - - // #8786 - checkCode( - "void f() {\n" - " char * pBuf = (char*)(new int[32]);\n" - "}\n"); - - // #8749 - checkCode( "struct A {\n" " void operator+=(A&) && = delete;\n" "};\n"); diff -Nru cppcheck-1.85/test/testimportproject.cpp cppcheck-1.86/test/testimportproject.cpp --- cppcheck-1.85/test/testimportproject.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testimportproject.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -94,13 +94,13 @@ void importCompileCommands() const { const char json[] = "[ { \"directory\": \"/tmp\"," - "\"command\": \"gcc -I/tmp -DTEST1 -DTEST2=2 -DTEST3=\\\"\\\\\\\"3\\\\\\\"\\\" -o /tmp/src.o -c /tmp/src.c\"," + "\"command\": \"gcc -I/tmp -DCFGDIR=\\\\\\\"/usr/local/share/Cppcheck\\\\\\\" -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c\"," "\"file\": \"/tmp/src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); - ASSERT_EQUALS("TEST1=1;TEST2=2;TEST3=\"\\\"3\\\"\"", importer.fileSettings.begin()->defines); + ASSERT_EQUALS("CFGDIR=\"/usr/local/share/Cppcheck\";TEST1=1;TEST2=2", importer.fileSettings.begin()->defines); } }; diff -Nru cppcheck-1.85/test/testincompletestatement.cpp cppcheck-1.86/test/testincompletestatement.cpp --- cppcheck-1.85/test/testincompletestatement.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testincompletestatement.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -79,6 +79,7 @@ TEST_CASE(cast); // #3009 : (struct Foo *)123.a = 1; TEST_CASE(increment); // #3251 : FP for increment TEST_CASE(cpp11init); // #5493 : int i{1}; + TEST_CASE(cpp11init2); // #8449 TEST_CASE(block); // ({ do_something(); 0; }) TEST_CASE(mapindex); } @@ -272,6 +273,13 @@ ASSERT_EQUALS("", errout.str()); } + void cpp11init2() { + check("x handlers{\n" + " { \"mode2\", []() { return 2; } },\n" + "};"); + ASSERT_EQUALS("", errout.str()); + } + void block() { check("void f() {\n" " ({ do_something(); 0; });\n" diff -Nru cppcheck-1.85/test/testleakautovar.cpp cppcheck-1.86/test/testleakautovar.cpp --- cppcheck-1.85/test/testleakautovar.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testleakautovar.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -98,6 +98,7 @@ TEST_CASE(ifelse7); // #5576 - if (fd < 0) TEST_CASE(ifelse8); // #5747 - if (fd == -1) TEST_CASE(ifelse9); // #5273 - if (X(p==NULL, 0)) + TEST_CASE(ifelse10); // #8794 - if (!(x!=NULL)) // switch TEST_CASE(switch1); @@ -1149,6 +1150,16 @@ "}"); ASSERT_EQUALS("", errout.str()); } + + void ifelse10() { // #8794 + check("void f() {\n" + " void *x = malloc(1U);\n" + " if (!(x != NULL))\n" + " return;\n" + " free(x);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } void switch1() { check("void f() {\n" diff -Nru cppcheck-1.85/test/testmathlib.cpp cppcheck-1.86/test/testmathlib.cpp --- cppcheck-1.85/test/testmathlib.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testmathlib.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -371,6 +371,15 @@ ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0.0"), 0.000001); ASSERT_EQUALS_DOUBLE('0', MathLib::toDoubleNumber("'0'"), 0.000001); + ASSERT_EQUALS_DOUBLE(192, MathLib::toDoubleNumber("0x0.3p10"), 0.000001); + ASSERT_EQUALS_DOUBLE(5.42101e-20, MathLib::toDoubleNumber("0x1p-64"), 1e-20); + ASSERT_EQUALS_DOUBLE(3.14159, MathLib::toDoubleNumber("0x1.921fb5p+1"), 0.000001); + ASSERT_EQUALS_DOUBLE(2006, MathLib::toDoubleNumber("0x1.f58000p+10"), 0.000001); + ASSERT_EQUALS_DOUBLE(1e-010, MathLib::toDoubleNumber("0x1.b7cdfep-34"), 0.000001); + ASSERT_EQUALS_DOUBLE(.484375, MathLib::toDoubleNumber("0x1.fp-2"), 0.000001); + ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001); + // verify: string --> double --> string conversion ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0f"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0"))); @@ -598,7 +607,6 @@ void isFloatHex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] - ASSERT_EQUALS(false, MathLib::isFloatHex("")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.999999999999ap-4")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x0.3p10")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.fp3")); @@ -606,7 +614,13 @@ ASSERT_EQUALS(true, MathLib::isFloatHex("0xcc.ccccccccccdp-11")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x3.243F6A88p+03")); ASSERT_EQUALS(true, MathLib::isFloatHex("0xA.Fp-10")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p-10f")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10F")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10l")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10L")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0X.2p-0")); + ASSERT_EQUALS(false, MathLib::isFloatHex("")); ASSERT_EQUALS(false, MathLib::isFloatHex("0")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xa")); @@ -616,6 +630,10 @@ ASSERT_EQUALS(false, MathLib::isFloatHex("0x.")); ASSERT_EQUALS(false, MathLib::isFloatHex("0XP")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xx")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1P+-1")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10e")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+1af")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10LL")); } void isIntHex() const { diff -Nru cppcheck-1.85/test/testmemleak.cpp cppcheck-1.86/test/testmemleak.cpp --- cppcheck-1.85/test/testmemleak.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testmemleak.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -5869,6 +5869,16 @@ "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function 'f' is not stored.\n", errout.str()); + check("void f()\n" // #8100 + "{\n" + " auto lambda = [](){return malloc(10);};\n" + "}\n" + "void x()\n" + "{\n" + " f();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + check("void x()\n" "{\n" " if(!malloc(5)) fail();\n" diff -Nru cppcheck-1.85/test/testnullpointer.cpp cppcheck-1.86/test/testnullpointer.cpp --- cppcheck-1.85/test/testnullpointer.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testnullpointer.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -82,6 +82,7 @@ TEST_CASE(nullpointer28); // #6491 TEST_CASE(nullpointer30); // #6392 TEST_CASE(nullpointer31); // #8482 + TEST_CASE(nullpointer32); // #8460 TEST_CASE(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 @@ -1376,6 +1377,18 @@ ASSERT_EQUALS("", errout.str()); } + void nullpointer32() { // #8460 + check("int f(int * ptr) {\n" + " if(ptr)\n" + " { return 0;}\n" + " else{\n" + " int *p1 = ptr;\n" + " return *p1;\n" + " }\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'ptr' is redundant or there is possible null pointer dereference: p1.\n", errout.str()); + } + void nullpointer_addressOf() { // address of check("void f() {\n" " struct X *x = 0;\n" diff -Nru cppcheck-1.85/test/testother.cpp cppcheck-1.86/test/testother.cpp --- cppcheck-1.85/test/testother.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testother.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -124,6 +124,7 @@ TEST_CASE(duplicateBranch); TEST_CASE(duplicateBranch1); // tests extracted by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ): Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 TEST_CASE(duplicateBranch2); // empty macro + TEST_CASE(duplicateBranch3); TEST_CASE(duplicateExpression1); TEST_CASE(duplicateExpression2); // ticket #2730 TEST_CASE(duplicateExpression3); // ticket #3317 @@ -158,9 +159,12 @@ TEST_CASE(incompleteArrayFill); TEST_CASE(redundantVarAssignment); + TEST_CASE(redundantVarAssignment_struct); TEST_CASE(redundantVarAssignment_7133); TEST_CASE(redundantVarAssignment_stackoverflow); TEST_CASE(redundantVarAssignment_lambda); + TEST_CASE(redundantVarAssignment_for); + TEST_CASE(redundantVarAssignment_after_switch); TEST_CASE(redundantMemWrite); TEST_CASE(varFuncNullUB); @@ -207,6 +211,7 @@ TEST_CASE(moveAndReturn); TEST_CASE(moveAndClear); TEST_CASE(movedPointer); + TEST_CASE(moveAndAddressOf); TEST_CASE(partiallyMoved); TEST_CASE(moveAndLambda); TEST_CASE(forwardAndUsed); @@ -214,6 +219,8 @@ TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgOrderDifferent); TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" + + TEST_CASE(shadowVariables); } void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, Settings* settings = 0) { @@ -638,7 +645,7 @@ void nanInArithmeticExpression() { check("void f()\n" "{\n" - " double x = 3.0 / 0.0 + 1.0\n" + " double x = 3.0 / 0.0 + 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS( @@ -646,7 +653,7 @@ check("void f()\n" "{\n" - " double x = 3.0 / 0.0 - 1.0\n" + " double x = 3.0 / 0.0 - 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS( @@ -654,7 +661,7 @@ check("void f()\n" "{\n" - " double x = 1.0 + 3.0 / 0.0\n" + " double x = 1.0 + 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS( @@ -662,7 +669,7 @@ check("void f()\n" "{\n" - " double x = 1.0 - 3.0 / 0.0\n" + " double x = 1.0 - 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS( @@ -670,7 +677,7 @@ check("void f()\n" "{\n" - " double x = 3.0 / 0.0\n" + " double x = 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("", errout.str()); @@ -1820,7 +1827,7 @@ " }\n" " bar(y);\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void bar() {}\n" // bar isn't noreturn "void foo()\n" @@ -1848,7 +1855,7 @@ " strcpy(str, \"b'\");\n" " }\n" "}", 0, false, false, false); - ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); + // TODO ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" @@ -1860,7 +1867,7 @@ " strncpy(str, \"b'\");\n" " }\n" "}"); - ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); + // TODO ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" @@ -1875,7 +1882,7 @@ " z++;\n" " }\n" "}", nullptr, false, false, false); - ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); + // TODO ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" @@ -1928,6 +1935,14 @@ "}\n", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); + check("void f() {\n" + " int x;\n" + " switch (state) {\n" + " case 1: x = 3; goto a;\n" + " case 1: x = 6; goto a;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void switchRedundantOperationTest() { @@ -2267,7 +2282,7 @@ " }\n" " bar(y);\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); } void switchRedundantBitwiseOperationTest() { @@ -2357,7 +2372,7 @@ " break;\n" " }\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'y' is reassigned a value before the old one has been used.\n", errout.str()); check("void foo(int a)\n" "{\n" @@ -2750,6 +2765,13 @@ " bar();\n" "}", nullptr, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); + + // #8261 + check("void foo() {\n" + " (beat < 100) ? (void)0 : throw(0);\n" + " bar();\n" + "}", nullptr, false, false, false, &settings); + ASSERT_EQUALS("", errout.str()); } @@ -2990,11 +3012,11 @@ // #6406 - designated initializer doing bogus self assignment check("struct callbacks {\n" - " void (*something)(void);\n" + " void (*s)(void);\n" "};\n" "void something(void) {}\n" "void f() {\n" - " struct callbacks ops = { .something = ops.something };\n" + " struct callbacks ops = { .s = ops.s };\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'something' to itself.\n", "", errout.str()); @@ -3489,6 +3511,29 @@ ASSERT_EQUALS("", errout.str()); } + void duplicateBranch3() { + check("void f(bool b, int i) {\n" + " int j = i;\n" + " if (b) {\n" + " x = i;\n" + " } else {\n" + " x = j;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); + + check("void f(bool b, int i) {\n" + " int j = i;\n" + " i++;\n" + " if (b) {\n" + " x = i;\n" + " } else {\n" + " x = j;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void duplicateExpression1() { check("void foo(int a) {\n" " if (a == a) { }\n" @@ -3545,7 +3590,7 @@ check("void foo() {\n" " if (x!=2 || y!=3 || x!=2) {}\n" "}"); - TODO_ASSERT_EQUALS("error", "", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (x!=2 && (x=y) && x!=2) {}\n" @@ -4015,6 +4060,12 @@ " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("bool f(bool a, bool b) {\n" + " const bool c = a;\n" + " return a && b && c;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because 'a' and 'c' represent the same value.\n", errout.str()); } void duplicateExpression8() { @@ -4455,7 +4506,7 @@ " int i = f.f();\n" " int j = f.f();\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f(); int g(); };\n" "void test() {\n" @@ -4508,26 +4559,26 @@ " int start = x->first;\n" " int end = x->first;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct SW { int first; };\n" "void foo(SW* x, int i, int j) {\n" " int start = x->first;\n" " int end = x->first;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("void test(int * p) {\n" " int i = *p;\n" " int j = *p;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g(int) const; };\n" "void test() {\n" @@ -4535,7 +4586,7 @@ " int i = f.f();\n" " int j = f.f();\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" @@ -4543,7 +4594,7 @@ " int i = f.f();\n" " int j = f.f();\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); } void duplicateVarExpressionAssign() { @@ -4555,7 +4606,7 @@ " use(i);\n" " i = j;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" @@ -4565,7 +4616,7 @@ " use(j);\n" " j = i;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); // Issue #8612 check("struct P\n" @@ -4594,7 +4645,7 @@ " previous = current;\n" " }\n" "}\n"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:16] -> [test.cpp:15]: (style, inconclusive) Same expression used in consecutive assignments of 'current' and 'previous'.\n", errout.str()); } void duplicateVarExpressionCrash() { @@ -4610,7 +4661,7 @@ " (void)a;\n" " (void)b;\n" "}\n"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (style, inconclusive) Same expression used in consecutive assignments of 'a' and 'b'.\n", errout.str()); // Issue #8712 check("void f() {\n" @@ -4621,7 +4672,7 @@ check("template \n" "T f() {\n" - " T a = T();\n" + " T x = T();\n" "}\n" "int &a = f();\n"); ASSERT_EQUALS("", errout.str()); @@ -5340,7 +5391,7 @@ " const int a = getA + 3;\n" " return 0;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Local variable getA shadows outer function\n", errout.str()); check("class A{public:A(){}};\n" "const A& getA(){static A a;return a;}\n" @@ -5491,10 +5542,10 @@ " memcpy(a, b, 5);\n" " memmove(a, b, 5);\n" "}"); - ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n" - "[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n" - "[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memcpy()' with 'sizeof(*a)'?\n" - "[test.cpp:5]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memmove()' with 'sizeof(*a)'?\n", errout.str()); + ASSERT_EQUALS(// TODO "[test.cpp:4] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n" + "[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n" + "[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memcpy()' with 'sizeof(*a)'?\n" + "[test.cpp:5]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memmove()' with 'sizeof(*a)'?\n", errout.str()); check("void f() {\n" " Foo* a[5];\n" @@ -5542,24 +5593,20 @@ "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); - { - // non-local variable => only show warning when inconclusive is used - const char code[] = "int i;\n" - "void f() {\n" - " i = 1;\n" - " i = 1;\n" - "}"; - check(code, "test.cpp", false, false); // inconclusive = false - ASSERT_EQUALS("", errout.str()); - check(code, "test.cpp", false, true); // inconclusive = true - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); - } + + // non-local variable => only show warning when inconclusive is used + check("int i;\n" + "void f() {\n" + " i = 1;\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " int i;\n" " i = 1;\n" " i = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" @@ -5567,13 +5614,13 @@ " i = 1;\n" " i = 1;\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); + TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " int i[10];\n" " i[2] = 1;\n" " i[2] = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i[2]' is reassigned a value before the old one has been used.\n", errout.str()); check("void f(int x) {\n" @@ -5581,14 +5628,14 @@ " i[x] = 1;\n" " x=1;\n" " i[x] = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int x) {\n" " int i[10];\n" " i[x] = 1;\n" " i[x] = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i[x]' is reassigned a value before the old one has been used.\n", errout.str()); // Testing different types @@ -5603,7 +5650,7 @@ " bar = x;\n" " bar = y;\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'bar' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); + TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" // #4425. bar might refer to something global, etc. @@ -5642,7 +5689,7 @@ " i = 1;\n" " bar();\n" " i = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); check("void bar(int i) {}\n" @@ -5650,7 +5697,7 @@ " i = 1;\n" " bar(i);\n" // Passed as argument " i = 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" @@ -5705,7 +5752,7 @@ check("class C {\n" " int x;\n" - " void g() { return x*x; }\n" + " void g() { return x * x; }\n" " void f();\n" "};\n" "\n" @@ -5733,7 +5780,7 @@ " x = 1;\n" " x = 1;\n" " return x + 1;\n" - "}", nullptr, false, false, false); + "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'x' is reassigned a value before the old one has been used.\n", errout.str()); // from #3103 (avoid a false positive) @@ -5746,23 +5793,25 @@ "}"); ASSERT_EQUALS("", errout.str()); + // initialization, assignment with 0 check("void f() {\n" // Ticket #4356 - " int x = 0;\n" // <- ignore assignment with 0 + " int x = 0;\n" // <- ignore initialization with 0 " x = 3;\n" - "}", 0, false, false, false); + "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" - " int i = 54;\n" - " i = 0;\n" - "}", 0, false, false, false); - ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); + " state_t *x = NULL;\n" + " x = dostuff();\n" + "}"); + ASSERT_EQUALS("", errout.str()); check("void f() {\n" - " int i = 54;\n" - " i = 1;\n" - "}", 0, false, false, false); - ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); + " state_t *x;\n" + " x = NULL;\n" + " x = dostuff();\n" + "}"); + ASSERT_EQUALS("", errout.str()); check("int foo() {\n" // #4420 " int x;\n" @@ -5893,8 +5942,8 @@ " barney(x);\n" " }\n" "}"); - ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style) Variable 'p' is reassigned a value before the old one has been used.\n" - "[test.cpp:2]: (style) The scope of the variable 'p' can be reduced.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'p' can be reduced.\n", + errout.str()); check("void foo() {\n" " char *p = 0;\n" @@ -5918,7 +5967,28 @@ " if (memptr)\n" " memptr = 0;\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'memptr' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'memptr' is reassigned a value before the old one has been used.\n", errout.str()); + + // issue #8884 + check("template \n" + "auto foo(F f, Ts... xs) {\n" + " auto x = f(xs...);\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + void redundantVarAssignment_struct() { + check("struct foo {\n" + " int a,b;\n" + "};\n" + "\n" + "int main() {\n" + " struct foo x;\n" + " x.a = _mm_set1_ps(1.0);\n" + " x.a = _mm_set1_ps(2.0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'x.a' is reassigned a value before the old one has been used.\n", errout.str()); } void redundantVarAssignment_7133() { @@ -5958,9 +6028,16 @@ " aSrcBuf.mnBitCount = nDestBits;\n" " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style, inconclusive) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str()); + check("class C { void operator=(int x); };\n" // #8368 - assignment operator might have side effects => inconclusive + "void f() {\n" + " C c;\n" + " c = x;\n" + " c = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Variable 'c' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); } void redundantVarAssignment_stackoverflow() { @@ -5991,7 +6068,33 @@ ASSERT_EQUALS("", errout.str()); } + void redundantVarAssignment_for() { + check("void f() {\n" + " char buf[10];\n" + " int i;\n" + " for (i = 0; i < 4; i++)\n" + " buf[i] = 131;\n" + " buf[i] = 0;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + + void redundantVarAssignment_after_switch() { + check("void f(int x) {\n" // #7907 + " int ret;\n" + " switch (x) {\n" + " case 123:\n" + " ret = 1;\n" // redundant assignment + " break;\n" + " }\n" + " ret = 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:8]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); + } + void redundantMemWrite() { + return; // FIXME: temporary hack + // Simple tests check("void f() {\n" " char a[10];\n" @@ -6285,7 +6388,7 @@ // check getc - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" @@ -6293,7 +6396,7 @@ "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" @@ -6301,7 +6404,7 @@ "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" @@ -6309,7 +6412,7 @@ "}"); ASSERT_EQUALS("", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" @@ -6319,7 +6422,7 @@ // check fgetc - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = fgetc (pFile);\n" @@ -6327,7 +6430,7 @@ "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "char c;\n" "do {\n" " c = fgetc (pFile);\n" @@ -6335,7 +6438,7 @@ "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "signed char c;\n" "do {\n" " c = fgetc (pFile);\n" @@ -6343,7 +6446,7 @@ "}"); ASSERT_EQUALS("", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" @@ -6351,7 +6454,7 @@ "}"); ASSERT_EQUALS("", errout.str()); - check("voif f (FILE * pFile){\n" + check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" @@ -7295,6 +7398,15 @@ "[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout.str()); } + void moveAndAddressOf() { + check("void f() {\n" + " std::string s1 = x;\n" + " std::string s2 = std::move(s1);\n" + " p = &s1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void partiallyMoved() { check("void f() {\n" " A a;\n" @@ -7384,6 +7496,33 @@ )); ASSERT_EQUALS("", errout.str()); } + + void shadowVariables() { + check("int x;\n" + "void f() { int x; }\n"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable x shadows outer variable\n", errout.str()); + + check("int x();\n" + "void f() { int x; }\n"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable x shadows outer function\n", errout.str()); + + check("struct C {\n" + " C(int x) : x(x) {}\n" // <- we do not want a FP here + " int x;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " if (cond) {int x;}\n" // <- not a shadow variable + " int x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("int size() {\n" + " int size;\n" // <- not a shadow variable + "}\n"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestOther) diff -Nru cppcheck-1.85/test/testsamples.cpp cppcheck-1.86/test/testsamples.cpp --- cppcheck-1.85/test/testsamples.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsamples.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -61,6 +61,8 @@ FileLister::recursiveAddFiles(files, "samples", matcher); #endif for (std::map::const_iterator i = files.begin(); i != files.end(); ++i) { + if (i->first.find("memleak") != std::string::npos) + continue; CLEAR_REDIRECT_ERROUT; char* path = new char[i->first.size() + 1]; strcpy(path, i->first.c_str()); diff -Nru cppcheck-1.85/test/testsimplifytemplate.cpp cppcheck-1.86/test/testsimplifytemplate.cpp --- cppcheck-1.85/test/testsimplifytemplate.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsimplifytemplate.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -106,11 +106,28 @@ TEST_CASE(template66); // #8725 TEST_CASE(template67); // #8122 TEST_CASE(template68); // union + TEST_CASE(template69); // #8791 + TEST_CASE(template70); // #5289 + TEST_CASE(template71); // #8821 + TEST_CASE(template72); + TEST_CASE(template73); + TEST_CASE(template74); + TEST_CASE(template75); + TEST_CASE(template76); + TEST_CASE(template77); + TEST_CASE(template78); + TEST_CASE(template79); // #5133 + TEST_CASE(template80); + TEST_CASE(template81); + TEST_CASE(template82); // 8603 + TEST_CASE(template83); + TEST_CASE(template84); // #8880 TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); + TEST_CASE(template_forward_declared_default_parameter); TEST_CASE(template_default_type); TEST_CASE(template_typename); TEST_CASE(template_constructor); // #3152 - template constructor is removed @@ -121,13 +138,22 @@ TEST_CASE(template_namespace_3); TEST_CASE(template_namespace_4); TEST_CASE(template_namespace_5); + TEST_CASE(template_namespace_6); + TEST_CASE(template_namespace_7); // #8768 + TEST_CASE(template_namespace_8); + TEST_CASE(template_namespace_9); + TEST_CASE(template_namespace_10); + TEST_CASE(template_namespace_11); // #7145 // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); - TEST_CASE(expandSpecialized); + TEST_CASE(expandSpecialized1); + TEST_CASE(expandSpecialized2); + TEST_CASE(expandSpecialized3); // #8671 + TEST_CASE(expandSpecialized4); TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); @@ -171,7 +197,8 @@ const char code[] = "template void f(T val) { T a; }\n" "f(10);"; - const char expected[] = "f ( 10 ) ; " + const char expected[] = "void f ( int val ) ; " + "f ( 10 ) ; " "void f ( int val ) { }"; ASSERT_EQUALS(expected, tok(code)); @@ -181,7 +208,8 @@ const char code[] = "template class Fred { T a; };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "class Fred ; " + "Fred fred ; " "class Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -191,7 +219,8 @@ const char code[] = "template class Fred { T data[sz]; };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "class Fred ; " + "Fred fred ; " "class Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -201,7 +230,8 @@ const char code[] = "template class Fred { Fred(); };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "class Fred ; " + "Fred fred ; " "class Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -212,7 +242,8 @@ "template Fred::Fred() { }\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "class Fred ; " + "Fred fred ; " "class Fred { } ; " "Fred :: Fred ( ) { }"; @@ -224,7 +255,8 @@ "Fred fred1;\n" "Fred fred2;"; - const char expected[] = "Fred fred1 ; " + const char expected[] = "class Fred ; " + "Fred fred1 ; " "Fred fred2 ; " "class Fred { } ;"; @@ -338,7 +370,8 @@ "} ;\n"; // The expected result.. - const char expected[] = "void f ( ) { A a ; } " + const char expected[] = "class A ; " + "void f ( ) { A a ; } " "template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; " "class A { } ;"; @@ -355,7 +388,8 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "int * foo<3,int> ( ) ; " + "void f ( ) " "{" " foo<3,int> ( ) ; " "} " @@ -373,7 +407,8 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "char * foo<3,char> ( ) ; " + "void f ( ) " "{" " char * p ; p = foo<3,char> ( ) ; " "} " @@ -392,7 +427,8 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "class A<12,12,11> ; " + "void f ( ) " "{" " A<12,12,11> a ; " "} " @@ -464,7 +500,9 @@ "}\n"; // The expected result.. - const char expected[] = "void a<0> ( ) { } " + const char expected[] = "void a<2> ( ) ; " + "void a<1> ( ) ; " + "void a<0> ( ) { } " "int main ( ) " "{ a<2> ( ) ; return 0 ; } " "void a<2> ( ) { a<1> ( ) ; } " @@ -479,7 +517,8 @@ "};\n" "\n" "vec<4> v;"; - const char expected2[] = "vec<4> v ; " + const char expected2[] = "struct vec<4> ; " + "vec<4> v ; " "struct vec<4> { " "vec<4> ( ) { } " "vec<4> ( const vec < 4 - 1 > & v ) { } " @@ -501,7 +540,9 @@ " return 0;\n" "}\n"; - const char expected[] = "int main ( ) { b<2> ( ) ; return 0 ; } " + const char expected[] = "void a<2> ( ) ; " + "void b<2> ( ) ; " + "int main ( ) { b<2> ( ) ; return 0 ; } " "void b<2> ( ) { a<2> ( ) ; } " "void a<2> ( ) { }"; @@ -528,7 +569,8 @@ const char code[] = "template class foo { T a; };\n" "foo *f;"; - const char expected[] = "foo * f ; " + const char expected[] = "class foo ; " + "foo * f ; " "class foo { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -544,7 +586,8 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "char & foo ( ) ; " + "void f ( ) " "{" " char p ; p = foo ( ) ; " "} " @@ -559,7 +602,8 @@ "A a;\n"; // The expected result.. - const char expected[] = "A a ; " + const char expected[] = "class A ; " + "A a ; " "class A { public: ~ A ( ) ; } ; " "A :: ~ A ( ) { }"; ASSERT_EQUALS(expected, tok(code)); @@ -570,7 +614,8 @@ const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "struct Fred ; " + "Fred fred ; " "struct Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -580,7 +625,8 @@ const char code[] = "template struct Fred { T data[sz]; };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "struct Fred ; " + "Fred fred ; " "struct Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -590,7 +636,8 @@ const char code[] = "template struct Fred { Fred(); };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "struct Fred ; " + "Fred fred ; " "struct Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -601,7 +648,8 @@ "Fred fred1;\n" "Fred fred2;"; - const char expected[] = "Fred fred1 ; " + const char expected[] = "struct Fred ; " + "Fred fred1 ; " "Fred fred2 ; " "struct Fred { } ;"; @@ -613,7 +661,8 @@ const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; - const char expected[] = "Fred fred ; " + const char expected[] = "struct Fred ; " + "Fred fred ; " "struct Fred { std :: string a ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -625,7 +674,8 @@ " std::cout << (foo());\n" "}"; - const char expected[] = "void bar ( ) {" + const char expected[] = "void foo ( ) ; " + "void bar ( ) {" " std :: cout << ( foo ( ) ) ; " "} " "void foo ( ) { }"; @@ -644,7 +694,9 @@ "{};\n" "\n" "bitset<1> z;"; - const char expected[] = "bitset<1> z ; " + const char expected[] = "struct B<4> ; " + "class bitset<1> ; " + "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); @@ -662,15 +714,16 @@ "bitset<1> z;"; const char actual[] = "template < int n > struct B { int a [ n ] ; } ; " + "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B < 4 > { } ;"; - const char expected[] = "bitset<1> z ; " + const char expected[] = "class bitset<1> ; " + "bitset<1> z ; " "class bitset<1> : B < 4 > { } ; " "struct B < 4 > { int a [ 4 ] ; } ;"; TODO_ASSERT_EQUALS(expected, actual, tok(code)); - } void template26() { @@ -683,7 +736,7 @@ "\n" "C<2> a;\n"; // TODO: expand A also - ASSERT_EQUALS("template < class T > class A { public: T x ; } ; C<2> a ; class C<2> : public A < char [ 2 ] > { } ;", tok(code)); + ASSERT_EQUALS("template < class T > class A { public: T x ; } ; class C<2> ; C<2> a ; class C<2> : public A < char [ 2 ] > { } ;", tok(code)); } void template27() { @@ -696,7 +749,11 @@ // #3226 - inner template const char code[] = "template class Fred {};\n" "Fred > x;\n"; - ASSERT_EQUALS("Fred> x ; class Fred { } ; class Fred> { } ;", tok(code)); + ASSERT_EQUALS("class Fred ; " + "class Fred> ; " + "Fred> x ; " + "class Fred { } ; " + "class Fred> { } ;", tok(code)); } void template30() { @@ -708,11 +765,15 @@ void template31() { // #4010 - template reference type const char code[] = "template struct A{}; A a;"; - ASSERT_EQUALS("A a ; struct A { } ;", tok(code)); + ASSERT_EQUALS("struct A ; " + "A a ; " + "struct A { } ;", tok(code)); // #7409 - rvalue const char code2[] = "template struct A{}; A a;"; - ASSERT_EQUALS("A a ; struct A { } ;", tok(code2)); + ASSERT_EQUALS("struct A ; " + "A a ; " + "struct A { } ;", tok(code2)); } void template32() { @@ -728,6 +789,7 @@ "\n" "B b;\n"; ASSERT_EQUALS("template < class T1 , class T2 , class T3 , class T4 > struct A { } ; " + "struct B ; " "B b ; " "struct B { public: A < int , Pair < int , int > , int > a ; } ;", tok(code)); } @@ -739,7 +801,10 @@ "template struct B { };\n" "template struct C { A > > ab; };\n" "C c;"; - ASSERT_EQUALS("C c ; " + ASSERT_EQUALS("struct A>> ; " + "struct B> ; " + "struct C ; " + "C c ; " "struct C { A>> ab ; } ; " "struct B> { } ; " // <- redundant.. but nevermind "struct A>> { } ;", tok(code)); @@ -753,7 +818,9 @@ "C< B > c;"; ASSERT_EQUALS("struct A { } ; " "template < class T > struct B { } ; " // <- redundant.. but nevermind - "C> c ; struct C> { } ;", + "struct C> ; " + "C> c ; " + "struct C> { } ;", tok(code)); } } @@ -764,23 +831,24 @@ "template struct X { void f(X &x) {} };\n" "}\n" "template <> int X::Y(0);"; - ASSERT_EQUALS("namespace abc { " - "template < typename T > struct X { void f ( X < T > & x ) { } } ; " - "} " - "template < > int X < int > :: Y ( 0 ) ;", tok(code)); + tok(code); } void template35() { // #4074 - "A<'x'> a;" is not recognized as template instantiation const char code[] = "template class A {};\n" "A <'x'> a;"; - ASSERT_EQUALS("A<'x'> a ; class A<'x'> { } ;", tok(code)); + ASSERT_EQUALS("class A<'x'> ; " + "A<'x'> a ; " + "class A<'x'> { } ;", tok(code)); } void template36() { // #4310 - Passing unknown template instantiation as template argument const char code[] = "template struct X { T t; };\n" "template struct Y { Foo < X< Bar > > _foo; };\n" // <- Bar is unknown "Y bar;"; - ASSERT_EQUALS("Y bar ; " + ASSERT_EQUALS("struct X> ; " + "struct Y ; " + "Y bar ; " "struct Y { Foo < X> > _foo ; } ; " "struct X> { Bar < int > t ; } ;", tok(code)); @@ -792,7 +860,7 @@ "template class B {};\n" "B b1;\n" "B b2;"; - ASSERT_EQUALS("class A { } ; B b1 ; B b2 ; class B { } ;", + ASSERT_EQUALS("class A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { @@ -800,7 +868,7 @@ "template class B {};\n" "B b1;\n" "B b2;"; - ASSERT_EQUALS("struct A { } ; B b1 ; B b2 ; class B { } ;", + ASSERT_EQUALS("struct A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { @@ -808,7 +876,7 @@ "template class B {};\n" "B b1;\n" "B b2;"; - ASSERT_EQUALS("enum A { } ; B b1 ; B b2 ; class B { } ;", + ASSERT_EQUALS("enum A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } } @@ -854,11 +922,15 @@ void template41() { // #4710 - const in template instantiation not handled perfectly const char code1[] = "template struct X { };\n" "void f(const X x) { }"; - ASSERT_EQUALS("void f ( const X x ) { } struct X { } ;", tok(code1)); + ASSERT_EQUALS("struct X ; " + "void f ( const X x ) { } " + "struct X { } ;", tok(code1)); const char code2[] = "template T f(T t) { return t; }\n" "int x() { return f(123); }"; - ASSERT_EQUALS("int x ( ) { return f ( 123 ) ; } int f ( int t ) { return t ; }", tok(code2)); + ASSERT_EQUALS("int f ( int t ) ; " + "int x ( ) { return f ( 123 ) ; } " + "int f ( int t ) { return t ; }", tok(code2)); } void template42() { // #4878 cpcheck aborts in ext-blocks.cpp (clang testcode) @@ -945,7 +1017,9 @@ "template void Fred::f();\n" "template void Fred::g();\n"; - const char expected[] = "template void Fred :: f ( ) ; " + const char expected[] = "class Fred ; " + "class Fred ; " + "template void Fred :: f ( ) ; " "template void Fred :: g ( ) ; " "class Fred { void f ( ) ; void g ( ) ; } ; " "void Fred :: f ( ) { } " @@ -1027,7 +1101,10 @@ // Similar problem can also happen with ... ASSERT_EQUALS( - "A a ( 0 ) ; struct A { " + "struct A ; " + "struct A ; " + "A a ( 0 ) ; " + "struct A { " "A ( int * p ) { p ; } " "} ; " "struct A { " @@ -1056,7 +1133,8 @@ void template57() { // #7891 const char code[] = "template struct Test { Test(T); };\n" "Test test( 0 );"; - const char exp [] = "Test test ( 0 ) ; " + const char exp [] = "struct Test ; " + "Test test ( 0 ) ; " "struct Test { Test ( long ) ; } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1069,7 +1147,8 @@ "void foo() {\n" " TestArithmetic();\n" "}"; - const char exp[] = "void foo ( ) {" + const char exp[] = "void TestArithmetic ( ) ; " + "void foo ( ) {" " TestArithmetic ( ) ; " "} " "void TestArithmetic ( ) {" @@ -1094,13 +1173,18 @@ "int main () {\n" " return diagonalGroupTest<4>();\n" "}"; - const char exp[] = "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " + const char exp[] = "struct Factorial<4> ; " + "struct Factorial<3> ; " + "struct Factorial<2> ; " + "struct Factorial<1> ; " + "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " + "int diagonalGroupTest<4> ( ) ; " "int main ( ) { return diagonalGroupTest<4> ( ) ; } " "int diagonalGroupTest<4> ( ) { return Factorial<4> :: value ; } " "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " - "struct Factorial<1> { enum FacHelper { value = Factorial < 0 > :: value } ; } ;"; + "struct Factorial<1> { enum FacHelper { value = Factorial<0> :: value } ; } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1110,7 +1194,9 @@ "template void h() { f::type(0)>(); }\n" "\n" "void j() { h(); }"; - const char exp[] = "template < typename T > void f ( ) { } " // <- TODO: This template is not expanded + const char exp[] = "struct S ; " + "template < typename T > void f ( ) { } " // <- TODO: This template is not expanded + "void h ( ) ; " "void j ( ) { h ( ) ; } " "void h ( ) { f < S :: type ( 0 ) > ( ) ; } " "struct S { } ;"; @@ -1124,7 +1210,9 @@ " Foo> f2() { }\n" "};\n" "Bar c;"; - const char exp[] = "Bar c ; " + const char exp[] = "struct Foo> ; " + "struct Bar ; " + "Bar c ; " "struct Bar {" " void f1 ( Bar x ) { }" " Foo> f2 ( ) { } " @@ -1139,7 +1227,9 @@ "template class C3 {};\n" "template C3::C3(const C3 &v) { C1 c1; }\n" "C3 c3;"; - const char exp[] = "template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } " + const char exp[] = "struct C1 ; " + "template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } " + "class C3 ; " "C3 c3 ; " "class C3 { } ; " "C3 :: C3 ( const C3 & v ) { C1 c1 ; } " @@ -1148,8 +1238,11 @@ } void template63() { // #8576 - const char code[] = "template struct TestClass { T m_hi; }; TestClass> objTest3;"; - const char exp[] = "TestClass> objTest3 ; struct TestClass> { std :: auto_ptr < v > m_hi ; } ;"; + const char code[] = "template struct TestClass { T m_hi; };" + "TestClass> objTest3;"; + const char exp[] = "struct TestClass> ; " + "TestClass> objTest3 ; " + "struct TestClass> { std :: auto_ptr < v > m_hi ; } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1196,7 +1289,9 @@ "};\n" "template const int ** Fred::foo() { return nullptr; }\n" "Fred fred;"; - const char exp [] = "Fred fred ; struct Fred { " + const char exp [] = "struct Fred ; " + "Fred fred ; " + "struct Fred { " "const int * * foo ( ) ; " "} ; " "const int * * Fred :: foo ( ) { return nullptr ; }"; @@ -1218,7 +1313,8 @@ "template Containter::~Containter() {}\n" "Containter intContainer;"; - const char expected[] = "Containter intContainer ; " + const char expected[] = "struct Containter ; " + "Containter intContainer ; " "struct Containter { " "Containter ( ) ; " "Containter ( const Containter & ) ; " @@ -1241,19 +1337,296 @@ " T value;\n" "};\n" "Fred fred;"; - const char exp [] = "Fred fred ; union Fred { " + const char exp [] = "union Fred ; " + "Fred fred ; " + "union Fred { " "char dummy [ 4 ] ; " "int value ; " "} ;"; ASSERT_EQUALS(exp, tok(code)); } + void template69() { // #8791 + const char code[] = "class Test {\n" + " int test;\n" + " template T lookup() { return test; }\n" + " int Fun() { return lookup(); }\n" + "};"; + const char exp [] = "class Test { " + "int test ; " + "int lookup ( ) ; " + "int Fun ( ) { return lookup ( ) ; } " + "} ; " + "int Test :: lookup ( ) { return test ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template70() { // #5289 + const char code[] = "template class Bar;\n" + "template<>\n" + "class Bar {\n" + "};\n" + "template\n" + "class Bar : private Bar {\n" + " void foo() { }\n" + "};"; + const char exp [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " + "class Bar { " + "} ; " + "template < typename K , typename V , int KeySize = 0 > " + "class Bar : private Bar { " + "void foo ( ) { } " + "} ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template71() { // #8821 + const char code[] = "int f1(int * pInterface, int x) { return 0; }\n" + "\n" + "template< class interface_type > class Reference {\n" + " template< class interface_type > int i();\n" + " int *pInterface;\n" + "};\n" + "\n" + "template< class interface_type > int Reference< interface_type >::i() {\n" + " return f1(pInterface, interface_type::static_type());\n" + "}\n" + "\n" + "Reference< class XPropertyList > dostuff();"; + const char exp [] = "int f1 ( int * pInterface , int x ) { return 0 ; } " + "class Reference ; " + "Reference dostuff ( ) ; " + "class Reference { template < class XPropertyList > int i ( ) ; int * pInterface ; } ; " + "int Reference :: i ( ) { return f1 ( pInterface , XPropertyList :: static_type ( ) ) ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template72() { + const char code[] = "template class Tokenizer;\n" + "const Tokenizer *tokenizer() const;\n" + "template \n" + "Tokenizer::Tokenizer() { }"; + const char exp [] = "template < typename N , typename P > class Tokenizer ; " + "const Tokenizer < Node , Path > * tokenizer ( ) const ; " + "template < typename N , typename P > " + "Tokenizer < N , P > :: Tokenizer ( ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template73() { + const char code[] = "template\n" + "void keep_range(T& value, const T mini, const T maxi){}\n" + "template void keep_range(float& v, const float l, const float u);\n" + "template void keep_range(int& v, const int l, const int u);"; + const char exp[] = "void keep_range ( float & v , const float l , const float u ) ; " + "void keep_range ( int & v , const int l , const int u ) ; " + "void keep_range ( float & value , const float mini , const float maxi ) { } " + "void keep_range ( int & value , const int mini , const int maxi ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template74() { + const char code[] = "template class BTlist { };\n" + "class PushBackStreamBuf {\n" + "public:\n" + " void pushBack(const BTlist &vec);\n" + "};"; + const char exp[] = "class BTlist ; " + "class PushBackStreamBuf { " + "public: " + "void pushBack ( const BTlist & vec ) ; " + "} ; " + "class BTlist { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template75() { + const char code[] = "template\n" + "T foo(T& value){ return value; }\n" + "template std::vector> foo>>(std::vector>& v);"; + const char exp[] = "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & v ) ; " + "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template76() { + const char code[] = "namespace NS {\n" + " template T foo(T& value) { return value; }\n" + " template std::vector> foo>>(std::vector>& v);\n" + "}\n" + "std::vector> v;\n" + "v = foo>>(v);\n"; + const char exp[] = "namespace NS { " + "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & v ) ; " + "} " + "std :: vector < std :: vector < int > > v ; " + "v = foo>> ( v ) ; " + "std :: vector < std :: vector < int > > NS :: foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template77() { + const char code[] = "template\n" + "struct is_void : std::false_type { };\n" + "template<>\n" + "struct is_void : std::true_type { };\n" + "int main() {\n" + " std::cout << is_void::value << std::endl;\n" + " std::cout << is_void::value << std::endl;\n" + "}"; + const char exp[] = "struct is_void ; " + "struct is_void : std :: true_type { } ; " + "int main ( ) { " + "std :: cout << is_void :: value << std :: endl ; " + "std :: cout << is_void :: value << std :: endl ; " + "} " + "struct is_void : std :: false_type { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template78() { + const char code[] = "template \n" + "struct Base { };\n" + "struct S : Base ::Type { };"; + const char exp[] = "struct Base ; " + "struct S : Base :: Type { } ; " + "struct Base { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template79() { // #5133 + const char code[] = "class Foo {\n" + "public:\n" + " template void foo() { bar(); }\n" + "private:\n" + " template void bar() { bazz(); }\n" + " void bazz() { }\n" + "};\n" + "void some_func() {\n" + " Foo x;\n" + " x.foo();\n" + "}"; + const char exp[] = "class Foo { " + "public: " + "void foo ( ) ; " + "private: " + "void bar ( ) ; " + "void bazz ( ) { } " + "} ; " + "void some_func ( ) { " + "Foo x ; " + "x . foo ( ) ; " + "} " + "void Foo :: foo ( ) { bar ( ) ; } " + "void Foo :: bar ( ) { bazz ( ) ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template80() { + const char code[] = "class Fred {\n" + " template T foo(T t) const { return t; }\n" + "};\n" + "const void * p = Fred::foo(nullptr);"; + const char exp[] = "class Fred { " + "const void * foo ( const void * t ) const ; " + "} ; " + "const void * p ; p = Fred :: foo ( nullptr ) ; " + "const void * Fred :: foo ( const void * t ) const { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template81() { + const char code[] = "template \n" + "struct SortWith {\n" + " SortWith(Type);\n" + "};\n" + "template \n" + "SortWith::SortWith(Type) {}\n" + "int main() {\n" + " SortWith(0);\n" + "}"; + const char exp[] = "template < typename Type > " + "struct SortWith { " + "SortWith ( Type ) ; " + "} ; " + "SortWith :: SortWith ( int ) ; " + "int main ( ) { " + "SortWith ( 0 ) ; " + "} " + "SortWith :: SortWith ( int ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template82() { // 8603 + const char code[] = "typedef int comp;\n" + "const int f16=16;\n" + "template\n" + "class tvec2 {};\n" + "template\n" + "class tvec3 {};\n" + "namespace swizzle {\n" + "template void swizzle(tvec2 v) { }\n" + "template void swizzle(tvec3 v) { }\n" + "}\n" + "void foo() {\n" + " using namespace swizzle;\n" + " tvec2 tt2;\n" + " swizzle<1>(tt2);\n" + " tvec3 tt3;\n" + " swizzle<2,3>(tt3);\n" + "}"; + const char exp[] = "class tvec2 ; " + "class tvec3 ; " + "namespace swizzle { " + "void swizzle<1> ( tvec2 v ) ; " + "void swizzle<2,3> ( tvec3 v ) ; " + "} " + "void foo ( ) { " + "using namespace swizzle ; " + "tvec2 tt2 ; " + "swizzle<1> ( tt2 ) ; " + "tvec3 tt3 ; " + "swizzle<2,3> ( tt3 ) ; " + "} " + "void swizzle :: swizzle<2,3> ( tvec3 v ) { } " + "void swizzle :: swizzle<1> ( tvec2 v ) { } " + "class tvec3 { } ; " + "class tvec2 { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template83() { + const char code[] = "template\n" + "MultiConsumer::MultiConsumer() : sizeBuffer(0) {}\n" + "MultiReads::MultiReads() {\n" + " mc = new MultiConsumer();\n" + "}"; + const char exp[] = "MultiConsumer :: MultiConsumer ( ) ; " + "MultiReads :: MultiReads ( ) { " + "mc = new MultiConsumer ( ) ; " + "} " + "MultiConsumer :: MultiConsumer ( ) : sizeBuffer ( 0 ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template84() { // #8880 + const char code[] = "template \n" + "auto d() -> typename a::e {\n" + " d();\n" + "}"; + const char exp[] = "auto d ( ) . a < decltype ( int { } ) > :: e ; " + "auto d ( ) . a < decltype ( int { } ) > :: e { " + "d ( ) ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" "template struct S> {b};\n" "S s;"; - const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S < C < T > > { b } ; S s ; struct S { a } ;"; + const char exp[] = "template < typename T > struct C { } ; struct S ; template < typename T > struct S < C < T > > { b } ; S s ; struct S { a } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1262,7 +1635,7 @@ "template struct S {a};\n" "template struct S> {b};\n" "S> s;"; - const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S { a } ; S> s ; struct S> { b } ;"; + const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S { a } ; struct S> ; S> s ; struct S> { b } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1319,7 +1692,9 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "class A ; " + "class A ; " + "void f ( ) " "{" " A a1 ;" " A a2 ; " @@ -1342,7 +1717,8 @@ "}\n"; // The expected result.. - const char expected[] = "void f ( ) " + const char expected[] = "class A ; " + "void f ( ) " "{" " A a1 ;" " A a2 ; " @@ -1375,7 +1751,8 @@ " class A" " { int ar [ 3 ] ; }"; - const char current[] = "void f ( ) " + const char current[] = "class A ; " + "void f ( ) " "{ " "A < int , ( int ) 2 > a1 ; " "A a2 ; " @@ -1403,12 +1780,90 @@ "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; const char curr[] = "template < class T , class U > class DefaultMemory { } ; " + "class thv_table_c> ; " "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; TODO_ASSERT_EQUALS(exp, curr, tok(code)); } } + void template_forward_declared_default_parameter() { + { + const char code[] = "template class A;\n" + "template \n" + "class A\n" + "{ T ar[n]; };\n" + "\n" + "void f()\n" + "{\n" + " A a1;\n" + " A a2;\n" + "}\n"; + + const char wanted[] = "class A ; " + "class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + const char current[] = "template < class T , int n = 3 > class A ; " + "class A ; " + "class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + TODO_ASSERT_EQUALS(wanted, current, tok(code)); + } + { + const char code[] = "template class A;\n" + "template \n" + "class A\n" + "{ T ar[n]; };\n" + "\n" + "void f()\n" + "{\n" + " A a1;\n" + " A a2;\n" + "}\n"; + + const char wanted[] = "class A ; " + "class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + const char current[] = "template < class , int = 3 > class A ; " + "class A ; " + "class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + TODO_ASSERT_EQUALS(wanted, current, tok(code)); + } + } + void template_default_type() { const char code[] = "template \n" "class A\n" @@ -1554,7 +2009,9 @@ " template void Fred(T value) { }\n" "}\n" "Fred(123);"; - ASSERT_EQUALS("namespace { } " + ASSERT_EQUALS("namespace { " + "void Fred ( int value ) ; " + "} " "Fred ( 123 ) ; " "void Fred ( int value ) { }", tok(code)); } @@ -1565,8 +2022,11 @@ " template struct S { };\n" "}\n" "X::S s;"; - ASSERT_EQUALS("X::S s ; " - "struct X::S { } ;", tok(code)); + ASSERT_EQUALS("namespace X { " + "struct S ; " + "} " + "X :: S s ; " + "struct X :: S { } ;", tok(code)); } void template_namespace_3() { @@ -1577,11 +2037,12 @@ " void *test() { return foo::bar(); }\n" "}"; ASSERT_EQUALS("namespace test16 {" + " struct foo ;" " void * test ( ) {" - " return test16::foo :: bar ( ) ;" + " return foo :: bar ( ) ;" " } " "} " - "struct test16::foo {" + "struct test16 :: foo {" " static void * bar ( ) ; " "} ;", tok(code)); } @@ -1596,19 +2057,201 @@ " };\n" "}"; ASSERT_EQUALS("namespace foo {" - " struct S : public foo::A {" + " class A ;" + " struct S : public A {" " void f ( ) {" - " foo::A :: dostuff ( ) ;" + " A :: dostuff ( ) ;" " }" " } ; " "} " - "class foo::A { void dostuff ( ) { } } ;", tok(code)); + "class foo :: A { void dostuff ( ) { } } ;", tok(code)); } void template_namespace_5() { const char code[] = "template struct S {};\n" "namespace X { S s; }"; - ASSERT_EQUALS("namespace X { S s ; } struct S { } ;", tok(code)); + ASSERT_EQUALS("struct S ; " + "namespace X { S s ; } " + "struct S { } ;", tok(code)); + } + + void template_namespace_6() { + const char code[] = "namespace NS {\n" + "template union C {\n" + " char dummy[sizeof(T)];\n" + " T value;\n" + " C();\n" + " ~C();\n" + " C(const C &);\n" + " C & operator = (const C &);\n" + "};\n" + "}\n" + "NS::C intC;\n" + "template NS::C::C() {}\n" + "template NS::C::~C() {}\n" + "template NS::C::C(const NS::C &) {}\n" + "template NS::C & NS::C::operator=(const NS::C &) {}"; + ASSERT_EQUALS("namespace NS { " + "union C ; " + "} " + "NS :: C intC ; union NS :: C { " + "char dummy [ 4 ] ; " + "int value ; " + "C ( ) ; " + "~ C ( ) ; " + "C ( const NS :: C & ) ; " + "NS :: C & operator= ( const NS :: C & ) ; " + "} ; " + "NS :: C :: C ( ) { } " + "NS :: C :: ~ C ( ) { } " + "NS :: C :: C ( const NS :: C & ) { } " + "NS :: C & NS :: C :: operator= ( const NS :: C & ) { }", tok(code)); + } + + void template_namespace_7() { // #8768 + const char code[] = "namespace N1 {\n" + "namespace N2 {\n" + " struct C { };\n" + " template struct CT { };\n" + " C c1;\n" + " CT ct1;\n" + "}\n" + "N2::C c2;\n" + "N2::CT ct2;\n" + "}\n" + "N1::N2::C c3;\n" + "N1::N2::CT ct3;"; + ASSERT_EQUALS("namespace N1 { " + "namespace N2 { " + "struct C { } ; " + "struct CT ; " + "C c1 ; " + "CT ct1 ; " + "} " + "N2 :: C c2 ; " + "N2 :: CT ct2 ; " + "} " + "N1 :: N2 :: C c3 ; " + "N1 :: N2 :: CT ct3 ; struct N1 :: N2 :: CT { } ;", tok(code)); + } + + void template_namespace_8() { // #8768 + const char code[] = "namespace NS1 {\n" + "namespace NS2 {\n" + " template \n" + " struct Fred {\n" + " Fred();\n" + " Fred(const Fred &);\n" + " Fred & operator = (const Fred &);\n" + " ~Fred();\n" + " };\n" + " template \n" + " Fred::Fred() { }\n" + " template \n" + " Fred::Fred(const Fred & f) { }\n" + " template \n" + " Fred & Fred::operator = (const Fred & f) { }\n" + " template \n" + " Fred::~Fred() { }\n" + "}\n" + "}\n" + "NS1::NS2::Fred fred;"; + ASSERT_EQUALS("namespace NS1 { " + "namespace NS2 { " + "struct Fred ; " + "} " + "} " + "NS1 :: NS2 :: Fred fred ; struct NS1 :: NS2 :: Fred { " + "Fred ( ) ; " + "Fred ( const NS1 :: NS2 :: Fred & ) ; " + "NS1 :: NS2 :: Fred & operator= ( const NS1 :: NS2 :: Fred & ) ; " + "~ Fred ( ) ; " + "} ; " + "NS1 :: NS2 :: Fred :: Fred ( ) { } " + "NS1 :: NS2 :: Fred :: Fred ( const NS1 :: NS2 :: Fred & f ) { } " + "NS1 :: NS2 :: Fred & NS1 :: NS2 :: Fred :: operator= ( const NS1 :: NS2 :: Fred & f ) { } " + "NS1 :: NS2 :: Fred :: ~ Fred ( ) { }", tok(code)); + } + + void template_namespace_9() { + const char code[] = "namespace NS {\n" + "template struct Barney;\n" + "template<> struct Barney<1> { };\n" + "template\n" + "class Fred {\n" + "public:\n" + " Fred();\n" + "private:\n" + " Barney m_data;\n" + "};\n" + "template class Fred<1>;\n" + "}\n"; + ASSERT_EQUALS("namespace NS { " + "template < int type > struct Barney ; " + "struct Barney<1> { } ; " + "class Fred<1> ; " + "template class Fred<1> ; " + "} " + "class NS :: Fred<1> { " + "public: " + "Fred<1> ( ) ; " + "private: " + "Barney<1> m_data ; " + "} ;", tok(code)); + } + + void template_namespace_10() { + const char code[] = "namespace NS1 {\n" + "namespace NS2 {\n" + "template\n" + "class Fred {\n" + " T * t;\n" + "public:\n" + " Fred() : t(nullptr) {}\n" + "};\n" + "}\n" + "}\n" + "NS1::NS2::Fred fred;"; + ASSERT_EQUALS("namespace NS1 { " + "namespace NS2 { " + "class Fred ; " + "} " + "} " + "NS1 :: NS2 :: Fred fred ; class NS1 :: NS2 :: Fred " + "{ " + "int * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ;", tok(code)); + } + + void template_namespace_11() {// #7145 + const char code[] = "namespace MyNamespace {\n" + "class TestClass {\n" + "public:\n" + " TestClass() {\n" + " SomeFunction();\n" + " TemplatedMethod< int >();\n" + " }\n" + " void SomeFunction() { }\n" + "private:\n" + " template< typename T > T TemplatedMethod(T);\n" + "};\n" + "template< typename T > T TestClass::TemplatedMethod(T t) { return t; }\n" + "}"; + ASSERT_EQUALS("namespace MyNamespace { " + "class TestClass { " + "public: " + "TestClass ( ) { " + "SomeFunction ( ) ; " + "TemplatedMethod ( ) ; " + "} " + "void SomeFunction ( ) { } " + "private: " + "int TemplatedMethod ( int ) ; " + "template < typename T > T TemplatedMethod ( T ) ; " // this should be removed + "} ; " + "} int MyNamespace :: TestClass :: TemplatedMethod ( int t ) { return t ; }", tok(code)); } unsigned int templateParameters(const char code[]) { @@ -1694,9 +2337,180 @@ "template<> unsigned A >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); } - void expandSpecialized() { + void expandSpecialized1() { ASSERT_EQUALS("class A { } ;", tok("template<> class A {};")); ASSERT_EQUALS("class A : public B { } ;", tok("template<> class A : public B {};")); + ASSERT_EQUALS("class A { A ( ) ; ~ A ( ) ; } ;", tok("template<> class A { A(); ~A(); };")); + ASSERT_EQUALS("class A { A ( ) { } ~ A ( ) { } } ;", tok("template<> class A { A() {} ~A() {} };")); + ASSERT_EQUALS("class A { A ( ) ; ~ A ( ) ; } ; A :: A ( ) { } ~ A :: A ( ) { }", + tok("template<> class A { A(); ~A(); }; A::A() { } ~A::A() {}")); + ASSERT_EQUALS("class A { A ( ) ; A ( const A & ) ; A foo ( ) ; } ; A :: A ( ) { } A :: A ( const A & ) { } A A :: foo ( ) { A a ; return a ; }", + tok("template<> class A { A(); A(const A &) ; A foo(); }; A::A() { } A::A(const A &) { } A A::foo() { A a; return a; }")); + } + + void expandSpecialized2() { + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C() { }\n" + " C(const C &) { }\n" + " ~C() { }\n" + " C & operator=(const C &) { return *this; }\n" + "};\n" + "C b;\n"; + const char expected[] = "class C { " + "public: " + "C ( ) { } " + "C ( const C & ) { } " + "~ C ( ) { } " + "C & operator= ( const C & ) { return * this ; } " + "} ; " + "C b ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C() { }\n" + " C(const C &) { }\n" + " ~C() { }\n" + " C & operator=(const C &) { return *this; }\n" + "};"; + const char expected[] = "class C { " + "public: " + "C ( ) { } " + "C ( const C & ) { } " + "~ C ( ) { } " + "C & operator= ( const C & ) { return * this ; } " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C();\n" + " C(const C &);\n" + " ~C();\n" + " C & operator=(const C &);\n" + "};\n" + "C::C() { }\n" + "C::C(const C &) { }\n" + "C::~C() { }\n" + "C & C::operator=(const C &) { return *this; }\n" + "C b;\n"; + const char expected[] = "class C { " + "public: " + "C ( ) ; " + "C ( const C & ) ; " + "~ C ( ) ; " + "C & operator= ( const C & ) ; " + "} ; " + "C :: C ( ) { } " + "C :: C ( const C & ) { } " + "C :: ~ C ( ) { } " + "C & C :: operator= ( const C & ) { return * this ; } " + "C b ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C();\n" + " C(const C &);\n" + " ~C();\n" + " C & operator=(const C &);\n" + "};\n" + "C::C() { }\n" + "C::C(const C &) { }\n" + "C::~C() { }\n" + "C & C::operator=(const C &) { return *this; }"; + const char expected[] = "class C { " + "public: " + "C ( ) ; " + "C ( const C & ) ; " + "~ C ( ) ; " + "C & operator= ( const C & ) ; " + "} ; " + "C :: C ( ) { } " + "C :: C ( const C & ) { } " + "C :: ~ C ( ) { } " + "C & C :: operator= ( const C & ) { return * this ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + } + + void expandSpecialized3() { // #8671 + const char code[] = "template <> struct OutputU16 final {\n" + " explicit OutputU16(std::basic_ostream &t) : outputStream_(t) {}\n" + " void operator()(unsigned short) const;\n" + "private:\n" + " std::basic_ostream &outputStream_;\n" + "};"; + const char expected[] = "struct OutputU16 final { " + "explicit OutputU16 ( std :: basic_ostream < char > & t ) : outputStream_ ( t ) { } " + "void operator() ( short ) const ; " + "private: " + "std :: basic_ostream < char > & outputStream_ ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void expandSpecialized4() { + { + const char code[] = "template<> class C { };\n" + "map m;"; + const char expected[] = "class C { } ; " + "map < int > m ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template<> class C { };\n" + "map m;\n" + "C c;"; + const char expected[] = "class C { } ; " + "map < int > m ; " + "C c ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template class C { };\n" + "template<> class C { };\n" + "map m;\n"; + const char expected[] = "template < typename T > class C { } ; " + "class C { } ; " + "map < int > m ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template class C { };\n" + "template<> class C { };\n" + "map m;\n" + "C i;"; + const char expected[] = "class C ; " + "class C { } ; " + "map < int > m ; " + "C i ; " + "class C { } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template class C { };\n" + "template<> class C { };\n" + "map m;\n" + "C i;\n" + "C c;"; + const char expected[] = "class C ; " + "class C { } ; " + "map < int > m ; " + "C i ; " + "C c ; " + "class C { } ;"; + ASSERT_EQUALS(expected, tok(code)); + } } void templateAlias1() { @@ -1704,7 +2518,9 @@ "template using Bar = Foo;\n" "Bar b;\n"; - const char expected[] = "; Foo b ; struct Foo { } ;"; + const char expected[] = "struct Foo ; " + "Foo b ; " + "struct Foo { } ;"; ASSERT_EQUALS(expected, tok(code)); } @@ -1714,7 +2530,10 @@ "template using Bar = A::Foo;\n" "Bar b;\n"; - const char expected[] = "; A::Foo b ; struct A::Foo { } ;"; + const char expected[] = "namespace A { struct Foo ; } " + "; " + "A :: Foo b ; " + "struct A :: Foo { } ;"; ASSERT_EQUALS(expected, tok(code)); } @@ -1723,7 +2542,9 @@ const char code[] = "template struct Tag {};\n" "template using SPtr = std::shared_ptr)>;\n" "SPtr<0> s;"; - const char expected[] = "; std :: shared_ptr < void ( Tag<0> ) > s ; struct Tag<0> { } ;"; + const char expected[] = "struct Tag<0> ; " + "std :: shared_ptr < void ( Tag<0> ) > s ; " + "struct Tag<0> { } ;"; ASSERT_EQUALS(expected, tok(code)); } diff -Nru cppcheck-1.85/test/testsimplifytokens.cpp cppcheck-1.86/test/testsimplifytokens.cpp --- cppcheck-1.85/test/testsimplifytokens.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsimplifytokens.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -373,6 +373,9 @@ // #7571 ASSERT_EQUALS("; foo = foo + [ & ] ( ) { } ;", tok("; foo += [&]() {int i;};")); + + // #8796 + ASSERT_EQUALS("{ return ( a = b ) += c ; }", tok("{ return (a = b) += c; }")); } @@ -3257,6 +3260,7 @@ ASSERT_EQUALS("enum Anonymous0 { x , y , z } ; enum Anonymous0 d ; d = x ;", tok("enum { x, y, z } d(x);", false)); ASSERT_EQUALS("enum Anonymous0 { x , y , z } ; enum Anonymous0 e ; e = x ;", tok("enum { x, y, z } e{x};", false)); ASSERT_EQUALS("struct Anonymous0 { int i ; } ; struct Anonymous0 f ; f = { 0 } ;", tok("struct { int i; } f{0};", false)); + ASSERT_EQUALS("struct Anonymous0 { } ; struct Anonymous0 x ; x = { 0 } ;", tok("struct {} x = {0};", false)); ASSERT_EQUALS("enum G : short { x , y , z } ; enum G g ; g = x ;", tok("enum G : short { x, y, z } g(x);", false)); ASSERT_EQUALS("enum H : short { x , y , z } ; enum H h ; h = x ;", tok("enum H : short { x, y, z } h{x};", false)); ASSERT_EQUALS("enum class I : short { x , y , z } ; enum I i ; i = x ;", tok("enum class I : short { x, y, z } i(x);", false)); @@ -3269,7 +3273,7 @@ ASSERT_EQUALS("int foo ( ) { }", tok("inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__forceinline int foo ( ) { }", true)); - ASSERT_EQUALS("int foo ( ) { }", tok("constexpr int foo() { }", true)); + ASSERT_EQUALS("const int foo ( ) { }", tok("constexpr int foo() { }", true)); ASSERT_EQUALS("void f ( ) { int final [ 10 ] ; }", tok("void f() { int final[10]; }", true)); ASSERT_EQUALS("int * p ;", tok("int * __restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * __restrict__ * p;", "test.c")); @@ -3277,6 +3281,7 @@ ASSERT_EQUALS("int * p ;", tok("int * restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * restrict * p;", "test.c")); ASSERT_EQUALS("void foo ( float * a , float * b ) ;", tok("void foo(float * restrict a, float * restrict b);", "test.c")); + ASSERT_EQUALS("void foo ( int restrict ) ;", tok("void foo(int restrict);")); ASSERT_EQUALS("int * p ;", tok("typedef int * __restrict__ rint; rint p;", "test.c")); // don't remove struct members: diff -Nru cppcheck-1.85/test/testsimplifytypedef.cpp cppcheck-1.86/test/testsimplifytypedef.cpp --- cppcheck-1.85/test/testsimplifytypedef.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsimplifytypedef.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -162,6 +162,7 @@ TEST_CASE(simplifyTypedef122); // segmentation fault TEST_CASE(simplifyTypedef123); // ticket #7406 TEST_CASE(simplifyTypedef124); // ticket #7792 + TEST_CASE(simplifyTypedef125); // #8749 - typedef char A[10]; p = new A[1]; TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction2); // ticket #1685 @@ -1447,9 +1448,8 @@ const char code[] = "typedef char (* type1)[10];\n" "LOCAL(type1) foo() { }"; - // this is invalid C so just make sure it doesn't generate an internal error - checkSimplifyTypedef(code); - ASSERT_EQUALS("", errout.str()); + // this is invalid C, assert that an "unknown macro" warning is written + ASSERT_THROW(checkSimplifyTypedef(code), InternalError); } } @@ -1604,7 +1604,7 @@ // The expected tokens.. const char expected2[] = "void f ( ) { char a [ 256 ] ; a = { 0 } ; char b [ 256 ] ; b = { 0 } ; }"; - ASSERT_EQUALS(expected2, tok(code2, false)); + ASSERT_EQUALS(expected2, tok(code2, false, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); const char code3[] = "typedef char TString[256];\n" @@ -1615,7 +1615,7 @@ // The expected tokens.. const char expected3[] = "void f ( ) { char a [ 256 ] ; a = \"\" ; char b [ 256 ] ; b = \"\" ; }"; - ASSERT_EQUALS(expected3, tok(code3, false)); + ASSERT_EQUALS(expected3, tok(code3, false, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); const char code4[] = "typedef char TString[256];\n" @@ -1626,7 +1626,7 @@ // The expected tokens.. const char expected4[] = "void f ( ) { char a [ 256 ] ; a = \"1234\" ; char b [ 256 ] ; b = \"5678\" ; }"; - ASSERT_EQUALS(expected4, tok(code4, false)); + ASSERT_EQUALS(expected4, tok(code4, false, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } @@ -1863,8 +1863,7 @@ void simplifyTypedef81() { // ticket #2603 segmentation fault ASSERT_THROW(checkSimplifyTypedef("typedef\n"), InternalError); - checkSimplifyTypedef("typedef constexpr\n"); - ASSERT_EQUALS("", errout.str()); + ASSERT_THROW(checkSimplifyTypedef("typedef constexpr\n"), InternalError); } void simplifyTypedef82() { // ticket #2403 @@ -2526,6 +2525,13 @@ } + void simplifyTypedef125() { // #8749 + const char code[] = "typedef char A[3];\n" + "char (*p)[3] = new A[4];"; + const char exp [] = "char ( * p ) [ 3 ] = new char [ 4 ] [ 3 ] ;"; + ASSERT_EQUALS(exp, tok(code, false)); + } + void simplifyTypedefFunction1() { { const char code[] = "typedef void (*my_func)();\n" diff -Nru cppcheck-1.85/test/teststl.cpp cppcheck-1.86/test/teststl.cpp --- cppcheck-1.85/test/teststl.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/teststl.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -41,6 +41,7 @@ LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(outOfBounds); + TEST_CASE(outOfBoundsIndexExpression); TEST_CASE(iterator1); TEST_CASE(iterator2); @@ -57,6 +58,12 @@ TEST_CASE(iterator13); TEST_CASE(iterator14); // #8191 TEST_CASE(iterator15); // #8341 + TEST_CASE(iterator16); + TEST_CASE(iterator17); + TEST_CASE(iterator18); + TEST_CASE(iterator19); + TEST_CASE(iterator20); + TEST_CASE(iterator21); TEST_CASE(iteratorExpression); TEST_CASE(iteratorSameExpression); @@ -191,11 +198,23 @@ void outOfBounds() { setMultiline(); + checkNormal("void f() {\n" + " std::string s;\n" + " s[10] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:3:error:Out of bounds access in s because s is empty.\n", errout.str()); + + checkNormal("void f() {\n" + " std::string s = \"abcd\";\n" + " s[10] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:3:error:Accessing s[10] is out of bounds when s size is 4.\n", errout.str()); + checkNormal("void f(std::vector v) {\n" " v.front();\n" " if (v.empty()) {}\n" "}\n"); - ASSERT_EQUALS("test.cpp:2:warning:Accessing an item in container 'v'. Either the condition 'v.empty()' is redundant or 'v' can be empty.\n" + ASSERT_EQUALS("test.cpp:2:warning:Either the condition 'v.empty()' is redundant or v is accessed out of bounds when v is empty.\n" "test.cpp:3:note:condition 'v.empty()'\n" "test.cpp:2:note:Access out of bounds\n", errout.str()); @@ -203,7 +222,7 @@ " if (v.size() == 3) {}\n" " v[16] = 0;\n" "}\n"); - ASSERT_EQUALS("test.cpp:3:warning:Possible access out of bounds of container 'v'; size=3, index=16\n" + ASSERT_EQUALS("test.cpp:3:warning:Either the condition 'v.size()==3' is redundant or v size can be 3. Accessing v[16] is out of bounds when v size is 3.\n" "test.cpp:2:note:condition 'v.size()==3'\n" "test.cpp:3:note:Access out of bounds\n", errout.str()); @@ -213,7 +232,7 @@ " v[i] = 0;\n" " }\n" "}\n"); - ASSERT_EQUALS("test.cpp:4:warning:Possible access out of bounds of container 'v'; size=3, index=16\n" + ASSERT_EQUALS("test.cpp:4:warning:Either the condition 'v.size()==3' is redundant or v size can be 3. Accessing v[16] is out of bounds when v size is 3.\n" "test.cpp:3:note:condition 'v.size()==3'\n" "test.cpp:4:note:Access out of bounds\n", errout.str()); @@ -233,9 +252,41 @@ " s[2] = 0;\n" " }\n" "}\n"); - ASSERT_EQUALS("test.cpp:3:warning:Possible access out of bounds of container 's'; size=1, index=2\n" + ASSERT_EQUALS("test.cpp:3:warning:Either the condition 's.size()==1' is redundant or s size can be 1. Accessing s[2] is out of bounds when s size is 1.\n" "test.cpp:2:note:condition 's.size()==1'\n" "test.cpp:3:note:Access out of bounds\n", errout.str()); + + // Do not crash + checkNormal("void a() {\n" + " std::string b[];\n" + " for (auto c : b)\n" + " c.data();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + void outOfBoundsIndexExpression() { + setMultiline(); + + checkNormal("void f(std::string s) {\n" + " s[s.size()] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:error:Out of bounds access of s, index 's.size()' is out of bounds.\n", errout.str()); + + checkNormal("void f(std::string s) {\n" + " s[s.size()+1] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:error:Out of bounds access of s, index 's.size()+1' is out of bounds.\n", errout.str()); + + checkNormal("void f(std::string s) {\n" + " s[1+s.size()] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:error:Out of bounds access of s, index '1+s.size()' is out of bounds.\n", errout.str()); + + checkNormal("void f(std::string s) {\n" + " s[x*s.size()] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:error:Out of bounds access of s, index 'x*s.size()' is out of bounds.\n", errout.str()); } void iterator1() { @@ -246,7 +297,7 @@ " for (list::iterator it = l1.begin(); it != l2.end(); ++it)\n" " { }\n" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); check("void f()\n" "{\n" @@ -255,7 +306,7 @@ " for (list::iterator it = l1.begin(); l2.end() != it; ++it)\n" " { }\n" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str()); check("struct C { std::list l1; void func(); };\n" "void C::func() {\n" @@ -274,7 +325,7 @@ " for (list::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n" " { }\n" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); } void iterator2() { @@ -288,7 +339,19 @@ " ++it;\n" " }\n" "}"); - ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " list l1;\n" + " list l2;\n" + " list::iterator it = l1.begin();\n" + " while (l2.end() != it)\n" + " {\n" + " ++it;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str()); } void iterator3() { @@ -512,7 +575,7 @@ " if (it != s2.end()) continue;\n" " }\n" "}"); - ASSERT_EQUALS("[test.cpp:8]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str()); } void iterator11() { @@ -534,7 +597,7 @@ " std::map::const_iterator it = map1.find(123);\n" " if (it == map2.end()) { }" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); check("void f() {\n" " std::map map1;\n" @@ -542,7 +605,7 @@ " std::map::const_iterator it = map1.find(123);\n" " if (map2.end() == it) { }" "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map2' and 'map1'.\n", errout.str()); check("void f(std::string &s) {\n" " int pos = s.find(x);\n" @@ -564,7 +627,7 @@ " while (it!=a.end())\n" " ++it;\n" "}"); - ASSERT_EQUALS("[test.cpp:9]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:8]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str()); // #4062 check("void f() {\n" @@ -613,6 +676,286 @@ ASSERT_EQUALS("", errout.str()); } + void iterator16() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l2.end();\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::iterator it2 = l2.begin();\n" + " while (it2 != it1)\n" + " {\n" + " ++it2;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it2 = l2.end();\n" + " std::list::iterator it1 = l1.begin();\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2(10, 4);\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l2.find(4);\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + + } + + void iterator17() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " { it2 = l2.end(); }\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + " it2 = l2.end();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " it1 = l2.end();\n" + " it1 = l1.end();\n" + " if (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " { it2 = l2.end(); }\n" + " it2 = l1.end();\n" + " { it2 = l2.end(); }\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:9]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + } + + void iterator18() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " while (++it1 != --it2)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " while (it1++ != --it2)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l1.end();\n" + " if (--it2 > it1++)\n" + " {\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("", "[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n", errout.str()); + } + + void iterator19() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " if (it1 != l1.end())\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " if (l1.end() > it1)\n" + " {\n" + " }\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n", + "[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n", + errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " std::list::iterator it2 = l1.begin();\n" + " if (it1 != it2)\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list::iterator it1 = l1.begin();\n" + " {\n" + " std::list l1;\n" + " std::list::iterator it2 = l1.begin();\n" + " if (it2 != it1)\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str()); + + } + + void iterator20() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l2.begin();\n" + " it1 = it2;\n" + " while (it1 != l1.end())\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", "", errout.str()); + + check("std::list l3;\n" + "std::list::iterator bar()\n" + "{\n" + " return l3.end();\n" + "}\n" + "void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.begin();\n" + " std::list::iterator it2 = l2.begin();\n" + " it1 = bar();\n" + " while (it1 != it2)\n" + " {\n" + " ++it1;\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:13] -> [test.cpp:10] -> [test.cpp:11]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", "", errout.str()); + + } + + void iterator21() { + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::iterator it2 = l2.begin();\n" + " if (it1 != it2)\n" + " {\n" + " }\n" + " if (it2 != it1)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n" + "[test.cpp:10] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str()); + + check("void foo()\n" + "{\n" + " std::list l1;\n" + " std::list l2;\n" + " std::list::iterator it1 = l1.end();\n" + " std::list::iterator it2 = l2.begin();\n" + " if (it1 != it2 && it1 != it2)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str()); + + } + void iteratorExpression() { check("std::vector& f();\n" "std::vector& g();\n" @@ -649,7 +992,7 @@ " std::vector& g();\n" "};\n" "void foo() {\n" - " (void)std::find(A{}.f().begin(), A{}.g().end(), 0);\n" + " (void)std::find(A{} .f().begin(), A{} .g().end(), 0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (warning) Iterators to containers from different expressions 'A{}.f()' and 'A{}.g()' are used together.\n", errout.str()); diff -Nru cppcheck-1.85/test/testsuite.cpp cppcheck-1.86/test/testsuite.cpp --- cppcheck-1.85/test/testsuite.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsuite.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -33,7 +33,7 @@ **/ struct CompareFixtures { - bool operator()(const TestFixture* lhs, const TestFixture* rhs) { + bool operator()(const TestFixture* lhs, const TestFixture* rhs) const { return lhs->classname < rhs->classname; } }; diff -Nru cppcheck-1.85/test/testsymboldatabase.cpp cppcheck-1.86/test/testsymboldatabase.cpp --- cppcheck-1.85/test/testsymboldatabase.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testsymboldatabase.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -294,6 +294,9 @@ TEST_CASE(symboldatabase71); TEST_CASE(symboldatabase72); // #8600 TEST_CASE(symboldatabase73); // #8603 + TEST_CASE(symboldatabase74); // #8838 - final + + TEST_CASE(createSymbolDatabaseFindAllScopes1); TEST_CASE(enum1); TEST_CASE(enum2); @@ -3089,18 +3092,106 @@ } void symboldatabase62() { - GET_SYMBOL_DB("struct A {\n" - "public:\n" - " struct X { int a; };\n" - " void Foo(const std::vector &includes);\n" - "};\n" - "void A::Foo(const std::vector &includes) {\n" - " for (std::vector::const_iterator it = includes.begin(); it != includes.end(); ++it) {\n" - " const struct A::X currentIncList = *it;\n" - " }\n" - "}"); - ASSERT(db != nullptr); - ASSERT(db && db->scopeList.size() == 5); + { + GET_SYMBOL_DB("struct A {\n" + "public:\n" + " struct X { int a; };\n" + " void Foo(const std::vector &includes);\n" + "};\n" + "void A::Foo(const std::vector &includes) {\n" + " for (std::vector::const_iterator it = includes.begin(); it != includes.end(); ++it) {\n" + " const struct A::X currentIncList = *it;\n" + " }\n" + "}"); + ASSERT(db != nullptr); + ASSERT(db && db->scopeList.size() == 5); + if (db && db->scopeList.size() == 5) { + const Scope *scope = db->findScopeByName("A"); + ASSERT(scope != nullptr); + if (scope) { + const Function *function = findFunctionByName("Foo", scope); + ASSERT(function != nullptr); + if (function) { + ASSERT(function->hasBody()); + } + } + } + } + { + GET_SYMBOL_DB("class A {\n" + "public:\n" + " class X { public: int a; };\n" + " void Foo(const std::vector &includes);\n" + "};\n" + "void A::Foo(const std::vector &includes) {\n" + " for (std::vector::const_iterator it = includes.begin(); it != includes.end(); ++it) {\n" + " const class A::X currentIncList = *it;\n" + " }\n" + "}"); + ASSERT(db != nullptr); + ASSERT(db && db->scopeList.size() == 5); + if (db && db->scopeList.size() == 5) { + const Scope *scope = db->findScopeByName("A"); + ASSERT(scope != nullptr); + if (scope) { + const Function *function = findFunctionByName("Foo", scope); + ASSERT(function != nullptr); + if (function) { + ASSERT(function->hasBody()); + } + } + } + } + { + GET_SYMBOL_DB("struct A {\n" + "public:\n" + " union X { int a; float b; };\n" + " void Foo(const std::vector &includes);\n" + "};\n" + "void A::Foo(const std::vector &includes) {\n" + " for (std::vector::const_iterator it = includes.begin(); it != includes.end(); ++it) {\n" + " const union A::X currentIncList = *it;\n" + " }\n" + "}"); + ASSERT(db != nullptr); + ASSERT(db && db->scopeList.size() == 5); + if (db && db->scopeList.size() == 5) { + const Scope *scope = db->findScopeByName("A"); + ASSERT(scope != nullptr); + if (scope) { + const Function *function = findFunctionByName("Foo", scope); + ASSERT(function != nullptr); + if (function) { + ASSERT(function->hasBody()); + } + } + } + } + { + GET_SYMBOL_DB("struct A {\n" + "public:\n" + " enum X { a, b };\n" + " void Foo(const std::vector &includes);\n" + "};\n" + "void A::Foo(const std::vector &includes) {\n" + " for (std::vector::const_iterator it = includes.begin(); it != includes.end(); ++it) {\n" + " const enum A::X currentIncList = *it;\n" + " }\n" + "}"); + ASSERT(db != nullptr); + ASSERT(db && db->scopeList.size() == 5); + if (db && db->scopeList.size() == 5) { + const Scope *scope = db->findScopeByName("A"); + ASSERT(scope != nullptr); + if (scope) { + const Function *function = findFunctionByName("Foo", scope); + ASSERT(function != nullptr); + if (function) { + ASSERT(function->hasBody()); + } + } + } + } } void symboldatabase63() { @@ -4045,6 +4136,23 @@ ASSERT_EQUALS(3, f2->function->token->linenr()); } + void symboldatabase74() { // #8838 - final + GET_SYMBOL_DB("class Base { virtual int f() const = 0; };\n" + "class Derived : Base { virtual int f() const final { return 6; } };"); + + ASSERT_EQUALS(4, db->scopeList.size()); + ASSERT_EQUALS(1, db->functionScopes.size()); + + const Scope *f1 = db->functionScopes[0]; + ASSERT(f1->function->hasFinalSpecifier()); + } + + void createSymbolDatabaseFindAllScopes1() { + GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }"); + ASSERT(db->scopeList.size() == 3); + ASSERT_EQUALS(Scope::eUnion, db->scopeList.back().type); + } + void enum1() { GET_SYMBOL_DB("enum BOOL { FALSE, TRUE }; enum BOOL b;"); @@ -4400,8 +4508,8 @@ " void foo() {\n" " }\n" "};"); - ASSERT(db && db->findScopeByName("Bar") && !db->findScopeByName("Bar")->functionList.front().isImplicitlyVirtual(false)); - if (db) + ASSERT(db && db->findScopeByName("Bar") && !db->findScopeByName("Bar")->functionList.empty() && !db->findScopeByName("Bar")->functionList.front().isImplicitlyVirtual(false)); + if (db && db->findScopeByName("Bar")) ASSERT_EQUALS(1, db->findScopeByName("Bar")->functionList.size()); } diff -Nru cppcheck-1.85/test/testtokenize.cpp cppcheck-1.86/test/testtokenize.cpp --- cppcheck-1.85/test/testtokenize.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testtokenize.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -109,6 +109,8 @@ TEST_CASE(simplifyCasts16); // #6278 TEST_CASE(simplifyCasts17); // #6110 - don't remove any parentheses in 'a(b)(c)' + TEST_CASE(simplifyAt); + TEST_CASE(inlineasm); TEST_CASE(simplifyAsm2); // #4725 (writing asm() around "^{}") @@ -476,6 +478,7 @@ // Make sure the Tokenizer::findGarbageCode() does not have false positives // The TestGarbage ensures that there are true positives TEST_CASE(findGarbageCode); + TEST_CASE(checkEnableIf); // --check-config TEST_CASE(checkConfiguration); @@ -839,7 +842,11 @@ "};\n" "template Containter::Containter() : mElements(nullptr) {}\n" "Containter intContainer;"; - const char exp [] = "6: Containter intContainer@1 ; struct Containter {\n" + const char exp [] = "1: struct Containter ;\n" + "2:\n" + "|\n" + "5:\n" + "6: Containter intContainer@1 ; struct Containter {\n" "2: Containter ( ) ;\n" "3: int * mElements@2 ;\n" "4: } ;\n" @@ -1060,6 +1067,14 @@ tokenizeAndStringify("if (a(b)(c) >= 3)", true)); } + void simplifyAt() { + ASSERT_EQUALS("int x ;", tokenizeAndStringify("int x@123;")); + ASSERT_EQUALS("bool x ;", tokenizeAndStringify("bool x@123:1;")); + ASSERT_EQUALS("char PORTB ; bool PB3 ;", tokenizeAndStringify("char PORTB @ 0x10; bool PB3 @ PORTB:3;\n")); + + ASSERT_EQUALS("interrupt@ f ( ) { }", tokenizeAndStringify("@interrupt f() {}")); + } + void inlineasm() { ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("asm { mov ax,bx };")); ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("_asm { mov ax,bx };")); @@ -3305,7 +3320,7 @@ void removeParentheses15() { ASSERT_EQUALS("a = b ? c : 123 ;", tokenizeAndStringify("a = b ? c : (123);", false)); - ASSERT_EQUALS("a = b ? c : ( 579 ) ;", tokenizeAndStringify("a = b ? c : ((123)+(456));", false)); + ASSERT_EQUALS("a = b ? c : ( 123 + 456 ) ;", tokenizeAndStringify("a = b ? c : ((123)+(456));", false)); ASSERT_EQUALS("a = b ? 123 : c ;", tokenizeAndStringify("a = b ? (123) : c;", false)); // #4316 @@ -3402,7 +3417,7 @@ "float b ; b = 4.2f ;\n" "double c ; c = 4.2e+10 ;\n" "double d ; d = 4.2e-10 ;\n" - "int e ; e = 6 ;\n" + "int e ; e = 4 + 2 ;\n" "}", tokenizeAndStringify(code)); } @@ -3722,7 +3737,7 @@ // Ticket #4450 const char code[] = "static int large_eeprom_type = (13 | (5)), " "default_flash_type = 42;"; - ASSERT_EQUALS("static int large_eeprom_type = 13 ; static int default_flash_type = 42 ;", + ASSERT_EQUALS("static int large_eeprom_type = 13 | 5 ; static int default_flash_type = 42 ;", tokenizeAndStringify(code)); } @@ -4711,6 +4726,16 @@ const Token *A = Token::findsimplematch(tokenizer.tokens(), "A <"); ASSERT_EQUALS(true, A->next()->link() == A->tokAt(3)); } + { + // #8851 + const char code[] = "template::type>" + "void basic_json() {}"; + errout.str(""); + Tokenizer tokenizer(&settings0, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + ASSERT_EQUALS(true, Token::simpleMatch(tokenizer.tokens()->next()->link(), "> void")); + } } void simplifyString() { @@ -5026,7 +5051,14 @@ "{\n" " fn2();\n" "}\n"; - ASSERT_EQUALS("int main ( )\n{\nfn2 ( ) ;\n} void fn2 ( int t = [ ] { return 1 ; } ( ) )\n{ }", tokenizeAndStringify(code)); + ASSERT_EQUALS("void fn2 ( int t = [ ] { return 1 ; } ( ) ) ;\n" + "\n" + "\n" + "int main ( )\n" + "{\n" + "fn2 ( ) ;\n" + "} void fn2 ( int t = [ ] { return 1 ; } ( ) )\n" + "{ }", tokenizeAndStringify(code)); } void cpp0xtemplate2() { @@ -6012,9 +6044,6 @@ ASSERT_EQUALS("( 0 ) ;", tokenizeAndStringify("( 0 && a[123] );", true)); - // ticket #3964 - simplify numeric calculations in tokenization - ASSERT_EQUALS("char a [ 10 ] ;", tokenizeAndStringify("char a[9+1];")); - // ticket #4931 ASSERT_EQUALS("dostuff ( 1 ) ;", tokenizeAndStringify("dostuff(9&&8);", true)); } @@ -6153,8 +6182,8 @@ ASSERT_EQUALS("; foo :: foo ( ) { }", tokenizeAndStringify("; AB(foo*) foo::foo() { }")); - // #4834 - ASSERT_EQUALS("A(B) foo ( ) { }", tokenizeAndStringify("A(B) foo() {}")); + // #4834 - syntax error + ASSERT_THROW(tokenizeAndStringify("A(B) foo() {}"), InternalError); // #3855 ASSERT_EQUALS("; class foo { }", @@ -8262,6 +8291,9 @@ ASSERT_EQUALS("DerivedDerived::(", testAst("Derived::~Derived() {}")); ASSERT_EQUALS("ifCA_FarReadfilenew(,sizeofobjtype(,(!(", testAst("if (!CA_FarRead(file, (void far *)new, sizeof(objtype)))")); // #5910 - don't hang if C code is parsed as C++ + + // Variable declaration + ASSERT_EQUALS("charp*(3[char5[3[new=", testAst("char (*p)[3] = new char[5][3];")); } void astexpr2() { // limit for large expressions @@ -8403,6 +8435,8 @@ ASSERT_EQUALS("a1(2+=",testAst("a=(t&)1+2;")); ASSERT_EQUALS("ab::r&c(=", testAst("a::b& r = (a::b&)c;")); // #5261 ASSERT_EQUALS("ab10:?=", testAst("a=(b)?1:0;")); + ASSERT_EQUALS("ac5[new(=", testAst("a = (b*)(new c[5]);")); // #8786 + ASSERT_EQUALS("a(4+", testAst("(int)(a) + 4;")); // TODO: This AST is incomplete however it's very weird syntax (taken from clang test suite) ASSERT_EQUALS("a&(", testAst("(int (**)[i]){&a}[0][1][5] = 0;")); @@ -8529,6 +8563,8 @@ ASSERT_EQUALS("xatoistr({(=", testAst("x = (struct X){atoi(str)};")); ASSERT_EQUALS("xa.0=b.0=,c.0=,{(=", testAst("x = (struct abc) { .a=0, .b=0, .c=0 };")); + ASSERT_EQUALS("yz.(return", testAst("return (x)(y).z;")); + // not cast ASSERT_EQUALS("AB||", testAst("(A)||(B)")); ASSERT_EQUALS("abc[1&=", testAst("a = (b[c]) & 1;")); @@ -8546,11 +8582,17 @@ ASSERT_EQUALS("{([(return 0return", testAst("return []() -> int { return 0; }();")); ASSERT_EQUALS("{([(return 0return", testAst("return [something]() -> int { return 0; }();")); ASSERT_EQUALS("{([cd,(return 0return", testAst("return [](int a, int b) -> int { return 0; }(c, d);")); - TODO_ASSERT_EQUALS("x{([=", "stdconst::x{([=&", testAst("x = [&]()->std::string const & { 1; }")); + ASSERT_EQUALS("x{([=", testAst("x = [&]()->std::string const & {};")); + ASSERT_EQUALS("f{([=", testAst("f = []() -> foo* {};")); + ASSERT_EQUALS("f{([=", testAst("f = [](void) mutable -> foo* {};")); + ASSERT_EQUALS("f{([=", testAst("f = []() mutable {};")); ASSERT_EQUALS("x{([= 0return", testAst("x = [](){return 0; };")); ASSERT_EQUALS("ab{[(= cd=", testAst("a = b([&]{c=d;});")); + + // 8628 + ASSERT_EQUALS("f{([( switchx( 1case y++", testAst("f([](){switch(x){case 1:{++y;}}});")); } void astcase() { @@ -8630,6 +8672,7 @@ ASSERT_EQUALS("sizeof ( a . b ) + 3 ;", tokenizeAndStringify("sizeof a.b+3;")); ASSERT_EQUALS("sizeof ( a [ 2 ] . b ) + 3 ;", tokenizeAndStringify("sizeof a[2].b+3;")); ASSERT_EQUALS("f ( 0 , sizeof ( ptr . bar ) ) ;", tokenizeAndStringify("f(0, sizeof ptr->bar );")); + ASSERT_EQUALS("sizeof ( a ) > sizeof ( & main ) ;", tokenizeAndStringify("sizeof a > sizeof &main;")); } void findGarbageCode() { // Make sure the Tokenizer::findGarbageCode() does not have FPs @@ -8637,6 +8680,8 @@ ASSERT_NO_THROW(tokenizeAndStringify("void f() { do switch (a) {} while (1); }")) ASSERT_NO_THROW(tokenizeAndStringify("void f() { label: switch (a) {} }")); ASSERT_NO_THROW(tokenizeAndStringify("void f() { UNKNOWN_MACRO if (a) {} }")) + ASSERT_NO_THROW(tokenizeAndStringify("void f() { []() -> int * {}; }")); + ASSERT_NO_THROW(tokenizeAndStringify("void f() { const char* var = \"1\" \"2\"; }")); // TODO ASSERT_NO_THROW(tokenizeAndStringify("void f() { MACRO(switch); }")); // TODO ASSERT_NO_THROW(tokenizeAndStringify("void f() { MACRO(x,switch); }")); @@ -8645,6 +8690,38 @@ } + void checkEnableIf() { + ASSERT_NO_THROW(tokenizeAndStringify( + "template<\n" + " typename U,\n" + " typename std::enable_if<\n" + " std::is_convertible{}>::type* = nullptr>\n" + "void foo(U x);\n")) + + ASSERT_NO_THROW(tokenizeAndStringify( + "template\n" + "T f(const T a, const T b) {\n" + " return a < b ? b : a;\n" + "}\n")) + + ASSERT_NO_THROW(tokenizeAndStringify( + "template\n" + "struct A {\n" + " T f(const T a, const T b) {\n" + " return a < b ? b : a;\n" + " }\n" + "};\n")) + + ASSERT_NO_THROW(tokenizeAndStringify( + "const int a = 1;\n" + "const int b = 2;\n" + "template\n" + "struct A {\n" + " int x = a < b ? b : a;" + "};\n")) + + } + void checkConfig(const char code[]) { errout.str(""); diff -Nru cppcheck-1.85/test/testunusedvar.cpp cppcheck-1.86/test/testunusedvar.cpp --- cppcheck-1.85/test/testunusedvar.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testunusedvar.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -171,6 +171,7 @@ TEST_CASE(localvarTemplate); // #4955 - variable is used as template parameter TEST_CASE(localvarFuncPtr); // #7194 TEST_CASE(localvarAddr); // #7477 + TEST_CASE(localvarDelete); TEST_CASE(localvarCppInitialization); TEST_CASE(localvarCpp11Initialization); @@ -4103,6 +4104,20 @@ "}"); ASSERT_EQUALS("", errout.str()); } + + void localvarDelete() { // #8339 + functionVariableUsage("void reassign(char*& data, int size)" + "{" + " char* buf = new char[size];" + "" + " char* tmp = data;" + " data = buf;" + " buf = tmp;" + "" + " delete [] buf;" + "}"); + ASSERT_EQUALS("", errout.str()); + } void chainedAssignment() { // #5466 diff -Nru cppcheck-1.85/test/testvalueflow.cpp cppcheck-1.86/test/testvalueflow.cpp --- cppcheck-1.85/test/testvalueflow.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testvalueflow.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -53,10 +53,12 @@ TEST_CASE(valueFlowNumber); TEST_CASE(valueFlowString); TEST_CASE(valueFlowPointerAlias); + TEST_CASE(valueFlowLifetime); TEST_CASE(valueFlowArrayElement); TEST_CASE(valueFlowMove); TEST_CASE(valueFlowBitAnd); + TEST_CASE(valueFlowRightShift); TEST_CASE(valueFlowCalculations); TEST_CASE(valueFlowSizeof); @@ -89,6 +91,7 @@ TEST_CASE(valueFlowForLoop); TEST_CASE(valueFlowSubFunction); + TEST_CASE(valueFlowSubFunctionLibrary); TEST_CASE(valueFlowFunctionReturn); TEST_CASE(valueFlowFunctionDefaultParameter); @@ -105,6 +108,8 @@ TEST_CASE(valueFlowUninit); + TEST_CASE(valueFlowTerminatingCond); + TEST_CASE(valueFlowContainerSize); } @@ -189,7 +194,7 @@ return ""; } - bool testValueOfX(const char code[], unsigned int linenr, const char value[]) { + bool testValueOfX(const char code[], unsigned int linenr, const char value[], ValueFlow::Value::ValueType type) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); @@ -199,7 +204,7 @@ if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { - if (it->isTokValue() && Token::simpleMatch(it->tokvalue, value)) + if (it->valueType == type && Token::simpleMatch(it->tokvalue, value)) return true; } } @@ -301,7 +306,7 @@ " if (a) x = \"123\";\n" " return x;\n" "}"; - ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\"")); + ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\"", ValueFlow::Value::TOK)); // valueFlowSubFunction code = "void dostuff(const char *x) {\n" @@ -309,7 +314,7 @@ "}\n" "\n" "void test() { dostuff(\"abc\"); }"; - ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"")); + ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"", ValueFlow::Value::TOK)); } void valueFlowPointerAlias() { @@ -321,7 +326,7 @@ " if (a) x = &ret[0];\n" " return x;\n" "}"; - ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]")); + ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]", ValueFlow::Value::TOK)); // dead pointer code = "void f() {\n" @@ -329,7 +334,7 @@ " if (cond) { int i; x = &i; }\n" " *x = 0;\n" // <- x can point at i "}"; - ASSERT_EQUALS(true, testValueOfX(code, 4, "& i")); + ASSERT_EQUALS(true, testValueOfX(code, 4, "& i", ValueFlow::Value::TOK)); code = "void f() {\n" " struct X *x;\n" @@ -339,6 +344,41 @@ ASSERT_EQUALS(true, tokenValues(code, "x [").empty()); } + void valueFlowLifetime() { + const char *code; + + LOAD_LIB_2(settings.library, "std.cfg"); + + code = "void f() {\n" + " int a = 1;\n" + " auto x = [&]() { return a + 1; };\n" + " auto b = x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 4, "a ;", ValueFlow::Value::LIFETIME)); + + code = "void f() {\n" + " int a = 1;\n" + " auto x = [=]() { return a + 1; };\n" + " auto b = x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfX(code, 4, "a ;", ValueFlow::Value::LIFETIME)); + + code = "void f(int v) {\n" + " int a = v;\n" + " int * p = &a;\n" + " auto x = [=]() { return p + 1; };\n" + " auto b = x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 5, "a ;", ValueFlow::Value::LIFETIME)); + + code = "void f() {\n" + " std::vector v;\n" + " auto x = v.begin();\n" + " auto it = x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 4, "v ;", ValueFlow::Value::LIFETIME)); + } + void valueFlowArrayElement() { const char *code; @@ -346,26 +386,26 @@ " const int x[] = {43,23,12};\n" " return x;\n" "}"; - ASSERT_EQUALS(true, testValueOfX(code, 3U, "{ 43 , 23 , 12 }")); + ASSERT_EQUALS(true, testValueOfX(code, 3U, "{ 43 , 23 , 12 }", ValueFlow::Value::TOK)); code = "void f() {\n" " const char x[] = \"abcd\";\n" " return x;\n" "}"; - ASSERT_EQUALS(true, testValueOfX(code, 3U, "\"abcd\"")); + ASSERT_EQUALS(true, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::TOK)); code = "void f() {\n" " char x[32] = \"abcd\";\n" " return x;\n" "}"; - TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, "\"abcd\"")); + TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::TOK)); code = "void f() {\n" " int a[10];\n" " int *x = a;\n" // <- a value is a " *x = 0;\n" // .. => x value is a "}"; - ASSERT_EQUALS(true, testValueOfX(code, 4, "a")); + ASSERT_EQUALS(true, testValueOfX(code, 4, "a", ValueFlow::Value::TOK)); code = "char f() {\n" " const char *x = \"abcd\";\n" @@ -455,6 +495,20 @@ " return h(std::move(x));\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MovedVariable)); + + code = "struct X {\n" + "};\n" + "struct Data {\n" + " template\n" + " void foo(Fun f) {}\n" + "};\n" + "Data g(X value) { return Data(); }\n" + "void f() {\n" + " X x;\n" + " g(std::move(x)).foo([=](int value) mutable {;});\n" + " X y=x;\n" + "}"; + TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 11U, ValueFlow::Value::MovedVariable)); } void valueFlowCalculations() { @@ -874,7 +928,7 @@ " f1(x+1);\n" "}\n"; ASSERT_EQUALS("5,Assignment 'x=3', assigned value is 3\n" - "6,Calling function 'f1', 1st argument 'x' value is 4\n", + "6,Calling function 'f1', 1st argument 'x+1' value is 4\n", getErrorPathForX(code, 2U)); code = "void f(int a) {\n" @@ -1377,6 +1431,25 @@ "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); + code = "void f() {\n" + " int x = 0;\n" + " dostuff([&]() {\n" + " if (x > 0) {}\n" + " x++;\n" + " });\n" + " dosomething(q);\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); + + code = "int f() {\n" + " int x = 1;\n" + " dostuff([&]() {\n" + " x = y;\n" + " });\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfX(code, 6U, 1)); + // ?: code = "void f() {\n" " int x = 8;\n" @@ -1390,7 +1463,7 @@ " a = ((x[0] == 'U') ?\n" " x[1] : 0);\n" // <- x is not "" "}"; - ASSERT_EQUALS(false, testValueOfX(code, 4U, "\"\"")); + ASSERT_EQUALS(false, testValueOfX(code, 4U, "\"\"", ValueFlow::Value::TOK)); code = "void f() {\n" // #6973 " char *x = getenv (\"LC_ALL\");\n" @@ -1403,10 +1476,10 @@ " x[2] ))\n" // x can't be "" " {}\n" "}\n"; - ASSERT_EQUALS(true, testValueOfX(code, 6U, "\"\"")); - ASSERT_EQUALS(false, testValueOfX(code, 7U, "\"\"")); - ASSERT_EQUALS(false, testValueOfX(code, 8U, "\"\"")); - ASSERT_EQUALS(false, testValueOfX(code, 9U, "\"\"")); + ASSERT_EQUALS(true, testValueOfX(code, 6U, "\"\"", ValueFlow::Value::TOK)); + ASSERT_EQUALS(false, testValueOfX(code, 7U, "\"\"", ValueFlow::Value::TOK)); + ASSERT_EQUALS(false, testValueOfX(code, 8U, "\"\"", ValueFlow::Value::TOK)); + ASSERT_EQUALS(false, testValueOfX(code, 9U, "\"\"", ValueFlow::Value::TOK)); code = "void f() {\n" // #7599 " t *x = 0;\n" @@ -2199,6 +2272,15 @@ " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 8U, 1)); + + code = "int f(int *);\n" + "int g() {\n" + " const int a = 1;\n" + " int x = 11;\n" + " c = (a && f(&x));\n" + " if (x == 42) {}\n" + "}"; + ASSERT_EQUALS(false, testValueOfX(code, 6U, 11)); } void valueFlowForwardTernary() { @@ -2258,6 +2340,22 @@ ASSERT_EQUALS(false, testValueOfX(code,3U,16)); } + void valueFlowRightShift() { + const char *code; + + code = "int f(int a) {\n" + " int x = (a & 0xff) >> 16;\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code,3U,0)); + + code = "int f(unsigned int a) {\n" + " int x = (a % 123) >> 16;\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code,3U,0)); + } + void valueFlowSwitchVariable() { const char *code; code = "void f(int x) {\n" @@ -2664,6 +2762,40 @@ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); } + void valueFlowSubFunctionLibrary() { + const char *code; + + const char xmldata[] = "\n" + "\n" + " \n" + " arg1+arg2\n" + " \n" + " \n" + " \n" + ""; + + settings.library.loadxmldata(xmldata, sizeof(xmldata)); + + code = "void f() {\n" + " int x = add(100, 23);\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); + + code = "void f() {\n" + " int a;\n" + " if (cond)\n" + " a = 1;\n" + " else\n" + " a = 2;\n" + " add(a, a);\n" + "}"; + std::list values = tokenValues(code, "( a , a )"); + ASSERT_EQUALS(2, values.size()); + ASSERT_EQUALS(2, values.front().intvalue); + ASSERT_EQUALS(4, values.back().intvalue); + } + void valueFlowFunctionReturn() { const char *code; @@ -3252,6 +3384,76 @@ ASSERT_EQUALS(true, values.front().intvalue == 0 || values.back().intvalue == 0); } + void valueFlowTerminatingCond() { + const char* code; + + // opposite condition + code = "void f(int i, int j) {\n" + " if (i == j) return;\n" + " if(i != j) {}\n" + "}\n"; + ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); + + code = "void f(int i, int j) {\n" + " if (i == j) return;\n" + " i++;\n" + " if (i != j) {}\n" + "}\n"; + ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); + + code = "void f(int i, int j, bool a) {\n" + " if (a) {\n" + " if (i == j) return;\n" + " }\n" + " if (i != j) {}\n" + "}\n"; + ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); + + code = "void f(int i, int j, bool a) {\n" + " if (i != j) {}\n" + " if (i == j) return; \n" + "}\n"; + ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); + + // same expression + code = "void f(int i, int j) {\n" + " if (i != j) return;\n" + " bool x = (i != j);\n" + " bool b = x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); + + code = "void f(int i, int j) {\n" + " if (i != j) return;\n" + " i++;\n" + " bool x = (i != j);\n" + " bool b = x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 0)); + + code = "void f(int i, int j, bool a) {\n" + " if (a) {\n" + " if (i != j) return;\n" + " }\n" + " bool x = (i != j);\n" + " bool b = x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 0)); + + code = "void f(int i, int j, bool a) {\n" + " bool x = (i != j);\n" + " bool b = x;\n" + " if (i != j) return; \n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 0)); + + code = "void f(int i, int j, bool b) {\n" + " if (i == j) { if(b) return; }\n" + " if(i != j) {}\n" + "}\n"; + ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); + } + static std::string isPossibleContainerSizeValue(const std::list &values, MathLib::bigint i) { if (values.size() != 1) return "values.size():" + std::to_string(values.size()); @@ -3347,6 +3549,41 @@ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(const std::list &ints) {\n" + " if (ints.size() == 3) {\n" + " ints.front();\n" // <- container size is 3 + " }\n" + "}"; + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 3)); + + code = "void f(const std::list &ints) {\n" + " if (ints.size() <= 3) {\n" + " ints.front();\n" // <- container size is 3 + " }\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3)); + + code = "void f(const std::list &ints) {\n" + " if (ints.size() >= 3) {\n" + " ints.front();\n" // <- container size is 3 + " }\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3)); + + code = "void f(const std::list &ints) {\n" + " if (ints.size() < 3) {\n" + " ints.front();\n" // <- container size is 2 + " }\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 2)); + + code = "void f(const std::list &ints) {\n" + " if (ints.size() > 3) {\n" + " ints.front();\n" // <- container size is 4 + " }\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 4)); + + code = "void f(const std::list &ints) {\n" " if (ints.empty() == false) {\n" " ints.front();\n" // <- container is not empty " }\n" @@ -3379,11 +3616,57 @@ ASSERT(tokenValues(code, "s [").empty()); code = "void f() {\n" - " std::string s=\"abc\";\n" // size of s is 3 + " std::string s = \"abc\";\n" // size of s is 3 " s.size();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3)); + code = "void f() {\n" + " std::string s=\"abc\";\n" // size of s is 3 + " s += unknown;\n" + " s.size();\n" + "}"; + ASSERT(tokenValues(code, "s . size").empty()); + + code = "void f() {\n" + " std::string s=\"abc\";\n" // size of s is 3 + " s += \"def\";\n" // size of s => 6 + " s.size();\n" + "}"; + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 6)); + + code = "void f(std::string s) {\n" + " if (s == \"hello\")\n" + " s[40] = c;\n" + "}"; + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s ["), 5)); + + code = "void f(std::string s) {\n" + " s[40] = c;\n" + " if (s == \"hello\") {}\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5)); + + code = "void f(std::string s) {\n" + " if (s != \"hello\") {}\n" + " s[40] = c;\n" + "}"; + ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5)); + + code = "void f(std::string s) {\n" + " if (s != \"hello\")\n" + " s[40] = c;\n" + "}"; + ASSERT(tokenValues(code, "s [").empty()); + + // valueFlowContainerForward, loop + code = "void f() {\n" + " std::stack links;\n" + " while (!links.empty() || indentlevel)\n" + " links.push(tok);\n" + "}"; + ASSERT(tokenValues(code, "links . empty").empty()); + // valueFlowContainerForward, function call code = "void f() {\n" " std::list x;\n" @@ -3406,6 +3689,25 @@ "}"; ASSERT(tokenValues(code, "ints [").empty()); + // container size => yields + code = "void f() {\n" + " std::string s = \"abcd\";\n" + " s.size();\n" + "}"; + ASSERT_EQUALS(4, tokenValues(code, "( ) ;").front().intvalue); + + code = "void f() {\n" + " std::string s;\n" + " s.empty();\n" + "}"; + ASSERT_EQUALS(1, tokenValues(code, "( ) ;").front().intvalue); + + // Calculations + code = "void f() {\n" + " std::string s = \"abcd\";\n" + " x = s + s;\n" + "}"; + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8)); } }; diff -Nru cppcheck-1.85/test/testvarid.cpp cppcheck-1.86/test/testvarid.cpp --- cppcheck-1.85/test/testvarid.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/test/testvarid.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -129,6 +129,7 @@ TEST_CASE(varid_in_class21); // #7788 TEST_CASE(varid_namespace_1); // #7272 TEST_CASE(varid_namespace_2); // #7000 + TEST_CASE(varid_namespace_3); // #8627 TEST_CASE(varid_initList); TEST_CASE(varid_initListWithBaseTemplate); TEST_CASE(varid_initListWithScope); @@ -143,6 +144,7 @@ TEST_CASE(varid_templateArray); TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array Y; TEST_CASE(varid_templateUsing); // #5781 #7273 + TEST_CASE(varid_not_template_in_condition); // #7988 TEST_CASE(varid_cppcast); // #6190 TEST_CASE(varid_variadicFunc); TEST_CASE(varid_typename); // #4644 @@ -185,6 +187,8 @@ TEST_CASE(usingNamespace1); TEST_CASE(usingNamespace2); TEST_CASE(usingNamespace3); + + TEST_CASE(setVarIdStructMembers1); } std::string tokenize(const char code[], bool simplify = false, const char filename[] = "test.cpp") { @@ -1514,21 +1518,14 @@ "void Foo::Clone(FooBase& g) {\n" " ((FooBase)g)->m_bar = m_bar;\n" "}"; - TODO_ASSERT_EQUALS("1: class Foo : public FooBase {\n" - "2: void Clone ( FooBase & g@1 ) ;\n" - "3: short m_bar@2 ;\n" - "4: } ;\n" - "5: void Foo :: Clone ( FooBase & g@3 ) {\n" - "6: ( ( FooBase ) g@3 ) . m_bar@4 = m_bar@2 ;\n" - "7: }\n", - "1: class Foo : public FooBase {\n" - "2: void Clone ( FooBase & g@1 ) ;\n" - "3: short m_bar@2 ;\n" - "4: } ;\n" - "5: void Foo :: Clone ( FooBase & g@3 ) {\n" - "6: ( ( FooBase ) g@3 ) . m_bar = m_bar@2 ;\n" - "7: }\n", - tokenize(code)); + ASSERT_EQUALS("1: class Foo : public FooBase {\n" + "2: void Clone ( FooBase & g@1 ) ;\n" + "3: short m_bar@2 ;\n" + "4: } ;\n" + "5: void Foo :: Clone ( FooBase & g@3 ) {\n" + "6: ( ( FooBase ) g@3 ) . m_bar@4 = m_bar@2 ;\n" + "7: }\n", + tokenize(code)); } void varid_in_class11() { // #4277 - anonymous union @@ -1829,6 +1826,41 @@ ASSERT(actual.find("X@2 = 0") != std::string::npos); } + std::string getLine(const std::string &code, int lineNumber) { + std::string nr = MathLib::toString(lineNumber); + const std::string::size_type pos1 = code.find('\n' + nr + ": "); + if (pos1 == std::string::npos) + return ""; + const std::string::size_type pos2 = code.find('\n', pos1+1); + if (pos2 == std::string::npos) + return ""; + return code.substr(pos1+1, pos2-pos1-1); + } + + void varid_namespace_3() { // #8627 + const char code[] = "namespace foo {\n" + "struct Bar {\n" + " explicit Bar(int type);\n" + " void f();\n" + " int type;\n" // <- Same varid here ... + "};\n" + "\n" + "Bar::Bar(int type) : type(type) {}\n" + "\n" + "void Bar::f() {\n" + " type = 0;\n" // <- ... and here + "}\n" + "}"; + + const std::string actual = tokenize(code, false, "test.cpp"); + + const std::string line5 = getLine(actual, 5); + const std::string line11 = getLine(actual, 11); + + ASSERT_EQUALS("5: int type@2 ;", getLine(actual,5)); + ASSERT_EQUALS("11: type@2 = 0 ;", getLine(actual,11)); + } + void varid_initList() { const char code1[] = "class A {\n" " A() : x(0) {}\n" @@ -2121,6 +2153,20 @@ tokenize(code)); } + void varid_not_template_in_condition() { + const char code1[] = "void f() { if (xb); }"; + ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b ) { ; } }\n", tokenize(code1)); + + const char code2[] = "void f() { if (1+xb); }"; + ASSERT_EQUALS("1: void f ( ) { if ( 1 + x < a || x > b ) { ; } }\n", tokenize(code2)); + + const char code3[] = "void f() { if (xb+1); }"; + ASSERT_EQUALS("1: void f ( ) { if ( x < a || x > b + 1 ) { ; } }\n", tokenize(code3)); + + const char code4[] = "void f() { if ((x==13) && (xb)); }"; + ASSERT_EQUALS("1: void f ( ) { if ( ( x == 13 ) && ( x < a || x > b ) ) { ; } }\n", tokenize(code4)); + } + void varid_cppcast() { ASSERT_EQUALS("1: const_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("const_cast(code)[0] = 0;")); @@ -2945,6 +2991,20 @@ ASSERT_EQUALS(expected, tokenize(code)); } + + void setVarIdStructMembers1() { + const char code[] = "void f(Foo foo)\n" + "{\n" + " foo.size = 0;\n" + " return ((uint8_t)(foo).size);\n" + "}"; + const char expected[] = "1: void f ( Foo foo@1 )\n" + "2: {\n" + "3: foo@1 . size@2 = 0 ;\n" + "4: return ( ( uint8_t ) ( foo@1 ) . size@2 ) ;\n" + "5: }\n"; + ASSERT_EQUALS(expected, tokenize(code)); + } }; REGISTER_TEST(TestVarID) diff -Nru cppcheck-1.85/tools/daca2-addons.py cppcheck-1.86/tools/daca2-addons.py --- cppcheck-1.85/tools/daca2-addons.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/daca2-addons.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,249 +0,0 @@ -#!/usr/bin/env python -# -# 1. Create the folder ~/daca2-addons -# 2. Put cppcheck-O2 in ~/daca2-addons. It should be built with all optimisations. -# 3. Optional: tweak FTPSERVER and FTPPATH in this script below. -# 4. Run the daca2-addons script: python daca2-addons.py FOLDER - -import subprocess -import sys -import shutil -import glob -import os -import datetime -import time - -DEBIAN = ('ftp://ftp.se.debian.org/debian/', - 'ftp://ftp.debian.org/debian/') - - -def wget(filepath): - filename = filepath - if '/' in filepath: - filename = filename[filename.rfind('/') + 1:] - for d in DEBIAN: - subprocess.call( - ['nice', 'wget', '--tries=10', '--timeout=300', '-O', filename, d + filepath]) - if os.path.isfile(filename): - return True - print('Sleep for 10 seconds..') - time.sleep(10) - return False - - -def getpackages(folder): - if not wget('ls-lR.gz'): - return [] - subprocess.call(['nice', 'gunzip', 'ls-lR.gz']) - f = open('ls-lR', 'rt') - lines = f.readlines() - f.close() - subprocess.call(['rm', 'ls-lR']) - - path = None - archives = [] - filename = None - for line in lines: - line = line.strip() - if len(line) < 4: - if filename: - archives.append(path + '/' + filename) - path = None - filename = None - elif line[:13 + len(folder)] == './pool/main/' + folder + '/': - path = line[2:-1] - elif path and '.orig.tar.' in line: - filename = line[1 + line.rfind(' '):] - - for a in archives: - print(a) - - return archives - - -def handleRemoveReadonly(func, path, exc): - import stat - if not os.access(path, os.W_OK): - # Is the error an access error ? - os.chmod(path, stat.S_IWUSR) - func(path) - - -def removeAllExceptResults(): - count = 5 - while count > 0: - count -= 1 - - filenames = [] - filenames.extend(glob.glob('[A-Za-z0-9]*')) - filenames.extend(glob.glob('.[a-z]*')) - - try: - for filename in filenames: - if os.path.isdir(filename): - shutil.rmtree(filename, onerror=handleRemoveReadonly) - elif filename != 'results.txt': - os.remove(filename) - except WindowsError as err: - time.sleep(30) - if count == 0: - print('Failed to cleanup files/folders') - print(err) - sys.exit(1) - continue - except OSError as err: - time.sleep(30) - if count == 0: - print('Failed to cleanup files/folders') - print(err) - sys.exit(1) - continue - count = 0 - - -def removeLargeFiles(path): - for g in glob.glob(path + '*'): - if g == '.' or g == '..': - continue - if os.path.islink(g): - continue - if os.path.isdir(g): - removeLargeFiles(g + '/') - elif os.path.isfile(g) and g[-4:] != '.txt': - statinfo = os.stat(g) - if '/clang/INPUTS/' in path or statinfo.st_size > 100000: - os.remove(g) - - -def dumpfiles(path): - ret = [] - for g in glob.glob(path + '*'): - if os.path.islink(g): - continue - if os.path.isdir(g): - ret.extend(dumpfiles(path + g + '/')) - elif os.path.isfile(g) and g[-5:] == '.dump': - ret.append(g) - return ret - - -def scanarchive(filepath, jobs): - # remove all files/folders except results.txt - removeAllExceptResults() - - results = open('results.txt', 'at') - results.write(DEBIAN[0] + filepath + '\n') - results.close() - - if not wget(filepath): - if not wget(filepath): - results = open('results.txt', 'at') - results.write('wget failed\n') - results.close() - return - - filename = filepath[filepath.rfind('/') + 1:] - if filename[-3:] == '.gz': - subprocess.call(['tar', 'xzvf', filename]) - elif filename[-3:] == '.xz': - subprocess.call(['tar', 'xJvf', filename]) - elif filename[-4:] == '.bz2': - subprocess.call(['tar', 'xjvf', filename]) - -# -# List of skipped packages - which trigger known yet unresolved problems with cppcheck. -# The issues on trac (http://trac.cppcheck.net) are given for reference -# boost #3654 (?) -# flite #5975 -# insight#5184 -# valgrind #6151 -# gcc-arm - no ticket. Reproducible timeout in daca2 though as of 1.73/early 2016. -# - - if filename[:5] == 'flite' or filename[:5] == 'boost' or filename[:7] == 'insight' or\ - filename[:8] == 'valgrind' or filename[:7] == 'gcc-arm': - results = open('results.txt', 'at') - results.write('fixme: skipped package to avoid hang\n') - results.close() - return - - removeLargeFiles('') - - print('cppcheck ' + filename) - - p = subprocess.Popen( - ['nice', - '../cppcheck-O2', - '--dump', - '-D__GCC__', - '--enable=style', - '--error-exitcode=0', - jobs, - '.'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p.communicate() - - results = open('results.txt', 'at') - - addons = sorted(glob.glob(os.path.expanduser('~/cppcheck/addons/*.py'))) - for dumpfile in sorted(dumpfiles('')): - for addon in addons: - if 'cppcheckdata.py' in addon: - continue - - p2 = subprocess.Popen(['nice', - 'python', - addon, - dumpfile], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - comm = p2.communicate() - results.write(comm[1]) - results.close() - -FOLDER = None -JOBS = '-j1' -REV = None -for arg in sys.argv[1:]: - if arg[:6] == '--rev=': - REV = arg[6:] - elif arg[:2] == '-j': - JOBS = arg - else: - FOLDER = arg - -if not FOLDER: - print('no folder given') - sys.exit(1) - -workdir = os.path.expanduser('~/daca2-addons/') -if not os.path.isdir(workdir + FOLDER): - os.makedirs(workdir + FOLDER) -os.chdir(workdir + FOLDER) - -archives = getpackages(FOLDER) -if len(archives) == 0: - print('failed to load packages') - sys.exit(1) - -try: - results = open('results.txt', 'wt') - results.write('STARTDATE ' + str(datetime.date.today()) + '\n') - if REV: - results.write('GIT-REVISION ' + REV + '\n') - results.write('\n') - results.close() - - for archive in archives: - scanarchive(archive, JOBS) - - results = open('results.txt', 'at') - results.write('DATE ' + str(datetime.date.today()) + '\n') - results.close() - -except EOFError: - pass - -# remove all files/folders except results.txt -removeAllExceptResults() diff -Nru cppcheck-1.85/tools/daca2.py cppcheck-1.86/tools/daca2.py --- cppcheck-1.85/tools/daca2.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/daca2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,239 +0,0 @@ -#!/usr/bin/env python -# -# 1. Create a folder daca2 in your HOME folder -# 2. Put cppcheck-head in daca2. It should be built with all optimisations. -# 3. Optional: Put a file called "suppressions.txt" in the daca2 folder. -# 4. Optional: tweak FTPSERVER and FTPPATH in this script below. -# 5. Run the daca2 script: python daca2.py FOLDER - -import argparse -import logging -import subprocess -import sys -import shutil -import glob -import os -import datetime -import time - -DEBIAN = ('ftp://ftp.se.debian.org/debian/', - 'ftp://ftp.debian.org/debian/') - -RESULTS_FILES = ['results.txt'] - -def wget(filepath): - filename = filepath - if '/' in filepath: - filename = filename[filename.rfind('/') + 1:] - for d in DEBIAN: - subprocess.call( - ['nice', 'wget', '--tries=10', '--timeout=300', '-O', filename, d + filepath]) - if os.path.isfile(filename): - return True - print('Sleep for 10 seconds..') - time.sleep(10) - return False - - -def getpackages(folder): - if not wget('ls-lR.gz'): - return [] - subprocess.call(['nice', 'gunzip', 'ls-lR.gz']) - f = open('ls-lR', 'rt') - lines = f.readlines() - f.close() - subprocess.call(['rm', 'ls-lR']) - - path = None - archives = [] - filename = None - for line in lines: - line = line.strip() - if len(line) < 4: - if filename: - archives.append(path + '/' + filename) - path = None - filename = None - elif line[:13 + len(folder)] == './pool/main/' + folder + '/': - path = line[2:-1] - elif path and '.orig.tar.' in line: - filename = line[1 + line.rfind(' '):] - - for a in archives: - print(a) - - return archives - - -def handleRemoveReadonly(func, path, exc): - import stat - if not os.access(path, os.W_OK): - # Is the error an access error ? - os.chmod(path, stat.S_IWUSR) - func(path) - - -def removeAllExceptResults(): - filenames = [] - filenames.extend(glob.glob('[A-Za-z0-9]*')) - filenames.extend(glob.glob('.[a-z]*')) - - for filename in filenames: - count = 5 - while count > 0: - count -= 1 - - try: - if os.path.isdir(filename): - shutil.rmtree(filename, onerror=handleRemoveReadonly) - elif filename not in RESULTS_FILES: - os.remove(filename) - break - except WindowsError as err: - time.sleep(30) - if count == 0: - logging.error('Failed to cleanup {}: {}'.format(filename, err)) - except OSError as err: - time.sleep(30) - if count == 0: - logging.error('Failed to cleanup {}: {}'.format(filename, err)) - - -def removeLargeFiles(path): - for g in glob.glob(path + '*'): - if g in {'.', '..'}: - continue - if os.path.islink(g): - continue - if os.path.isdir(g): - # Remove test code - if g.endswith('/testsuite') or g.endswith('/clang/INPUTS'): - shutil.rmtree(g, onerror=handleRemoveReadonly) - # Remove docs and examples ... that might be garbage - elif g.endswith('/doc') or g.endswith('/examples'): - shutil.rmtree(g, onerror=handleRemoveReadonly) - else: - removeLargeFiles(g + '/') - elif os.path.isfile(g) and not g.endswith('.txt'): - statinfo = os.stat(g) - if statinfo.st_size > 1000000: - try: - os.remove(g) - except OSError as err: - logging.error('Failed to remove {}: {}'.format(g, err)) - - -def strfCurrTime(fmt): - return datetime.time.strftime(datetime.datetime.now().time(), fmt) - - -def scanarchive(filepath, args, run, resultsFile): - # remove all files/folders except RESULTS_FILENAME - removeAllExceptResults() - - resultsFile.write(DEBIAN[0] + filepath + '\n') - - if not wget(filepath): - if not wget(filepath): - resultsFile.write('wget failed at {}'.format(filepath)) - return - - filename = filepath[filepath.rfind('/') + 1:] - if filename[-3:] == '.gz': - subprocess.call(['tar', 'xzvf', filename]) - elif filename[-3:] == '.xz': - subprocess.call(['tar', 'xJvf', filename]) - elif filename[-4:] == '.bz2': - subprocess.call(['tar', 'xjvf', filename]) - - removeLargeFiles('') - - print(strfCurrTime('[%H:%M] cppcheck ') + filename) - - if args.cpulimit: - cmd = 'cpulimit --limit=' + args.cpulimit - else: - cmd = 'nice --adjustment=1000' - # TODO: The --exception-handling=stderr is skipped right now because it hangs (#8589) - cppcheck = '../cppcheck-' + run - cmd = cmd + ' ' + cppcheck + ' -D__GCC__ --enable=style --inconclusive --error-exitcode=0 ' +\ - args.jobs + ' --template=daca2 .' - cmds = cmd.split() - - p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - comm = p.communicate() - - if p.returncode == 0: - resultsFile.write(comm[1] + strfCurrTime('[%H:%M]') + '\n') - elif 'cppcheck: error: could not find or open any of the paths given.' not in comm[0]: - stdout = comm[0] - pos1 = stdout.rfind('Checking ') - if pos1 > 0: - resultsFile.write(stdout[pos1:]+'\n') - resultsFile.write(comm[1] + strfCurrTime('[%H:%M]')+'\n') - resultsFile.write('Exit code is not zero! Crash?\n') - resultsFile.write('\n') - - -parser = argparse.ArgumentParser(description='Checks debian source code') -parser.add_argument('folder', metavar='FOLDER') -parser.add_argument('--rev') -parser.add_argument('--workdir', default='~/daca2') -parser.add_argument('-j', '--jobs', default='-j1') -parser.add_argument('--skip', default=[], action='append') -parser.add_argument('--cpulimit') -parser.add_argument('--baseversion') - -args = parser.parse_args() - -workdir = os.path.expanduser(args.workdir) -if not os.path.isdir(workdir): - print('workdir \'' + workdir + '\' is not a folder') - sys.exit(1) -os.chdir(workdir) - -archives = getpackages(args.folder) -if len(archives) == 0: - print('failed to load packages') - sys.exit(1) - -workdir = os.path.join(workdir, args.folder) -if not os.path.isdir(workdir): - os.makedirs(workdir) -os.chdir(workdir) - -versions = ['head'] -if args.baseversion: - versions.append(args.baseversion) - RESULTS_FILES = ['results-head.txt', 'results-' + args.baseversion + '.txt'] - -for run in versions: - try: - f = None - if args.baseversion: - f = open('results-' + run + '.txt', 'wt') - else: - f = open('results.txt', 'wt') - f.write('STARTDATE ' + str(datetime.date.today()) + '\n') - f.write('STARTTIME ' + strfCurrTime('%H:%M:%S') + '\n') - if args.rev: - f.write('GIT-REVISION ' + args.rev + '\n') - f.write('\n') - - for archive in archives: - if len(args.skip) > 0: - a = archive[:archive.rfind('/')] - a = a[a.rfind('/') + 1:] - if a in args.skip: - continue - scanarchive(archive, args, run, f) - - f.write('DATE {}'.format(datetime.date.today()) + '\n') - f.write('TIME {}'.format(strfCurrTime('%H:%M:%S')) + '\n') - f.close() - - except EOFError: - pass - -# remove all files/folders except RESULTS_FILENAME -removeAllExceptResults() diff -Nru cppcheck-1.85/tools/daca2-search.cgi cppcheck-1.86/tools/daca2-search.cgi --- cppcheck-1.85/tools/daca2-search.cgi 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/daca2-search.cgi 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -#!/usr/bin/python - -# cgi-script for searching the results - -import sys -import glob -import os -import cgi -import cgitb -import re - -def getfiles(path, arguments): - files = [] - if 'folder' in arguments: - files.append(path + '/daca2-' + arguments['folder'].value + '.html') - else: - files.extend(sorted(glob.glob(path+'/daca2-?.html'))) - files.extend(sorted(glob.glob(path+'/daca2-lib?.html'))) - return files - -def readlines(filename): - if not os.path.isfile(filename): - return [] - f = open(filename, 'rt') - lines = f.readlines() - f.close() - return lines - -def trimline(line): - while len(line)>1 and (line[-1]=='\r' or line[-1]=='\n'): - line = line[:-1] - return line - -def matchline(line, id): - return line.endswith('[' + id + ']') - -def doSearch(path,arguments): - id = arguments['id'].value - for g in getfiles(path, arguments): - ftp = '' - found = False - for line in readlines(g): - line = trimline(line) - if line.startswith('ftp://'): - ftp = line - if matchline(line, id): - found = True - sys.stdout.write(ftp + '\n') - elif line.find(': note:') < 0: - found = False - if found: - sys.stdout.write(line + '\n') - -def summary(path, arguments): - count = {} - pattern = re.compile(r'.*: (error|warning|style|performance|portability):.*\[([a-zA-Z0-9]+)\]$') - for g in getfiles(path, arguments): - for line in readlines(g): - res = pattern.match(trimline(line)) - if res is None: - continue - id = res.group(2) - if id in count: - count[id] += 1 - else: - count[id] = 1 - print('') - for id in sorted(count.keys()): - print('') - print('
' + id +''+str(count[id])+'
') - -sys.stdout.write('Content-type: text/html\r\n\r\n' - '\n') - -cgitb.enable() -arguments = cgi.FieldStorage() -if 'id' in arguments: - id = arguments['id'].value - #id = 'oppositeInnerCondition' - print(id) - sys.stdout.write('
\n')
-  doSearch('../htdocs/devinfo/daca2-report', arguments)
-  #doSearch(os.path.expanduser('~/temp'), id)
-  sys.stdout.write('
\n') -else: - summary('../htdocs/devinfo/daca2-report', arguments) - #summary(os.path.expanduser('~/temp'), arguments) -sys.stdout.write('\n') diff -Nru cppcheck-1.85/tools/dmake.cpp cppcheck-1.86/tools/dmake.cpp --- cppcheck-1.85/tools/dmake.cpp 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/dmake.cpp 2018-12-08 07:18:21.000000000 +0000 @@ -301,13 +301,13 @@ // "-Woverloaded-virtual " // danmar: we get fp when overloading analyseWholeProgram() "-Wpacked " "-Wredundant-decls " - "-Wshadow " + "-Wno-shadow " // "-Wsign-conversion " // "-Wsign-promo " "-Wno-missing-field-initializers " "-Wno-missing-braces " // "-Wunreachable-code " - "-Wno-sign-compare " // danmar: I don't like this warning, it's very rarelly a bug + "-Wno-sign-compare " // danmar: I don't like this warning, it's very rarely a bug "-Wno-multichar " "$(CPPCHK_GLIBCXX_DEBUG) " "-g"); @@ -400,6 +400,22 @@ fout << "\tinstall -d ${DESTDIR}${CFGDIR}\n"; fout << "\tinstall -m 644 cfg/* ${DESTDIR}${CFGDIR}\n"; fout << "endif\n\n"; + fout << "uninstall:\n"; + fout << "\t@if test -d ${BIN}; then \\\n"; + fout << "\t files=\"cppcheck cppcheck-htmlreport \\\n"; + fout << "\t `ls -d addons/*.py addons/*/*.py 2>/dev/null | sed 's,^.*/,,'`\"; \\\n"; + fout << "\t echo '(' cd ${BIN} '&&' rm -f $$files ')'; \\\n"; + fout << "\t ( cd ${BIN} && rm -f $$files ); \\\n"; + fout << "\tfi\n"; + fout << "ifdef CFGDIR \n"; + fout << "\t@if test -d ${DESTDIR}${CFGDIR}; then \\\n"; + fout << "\t files=\"`cd cfg 2>/dev/null && ls`\"; \\\n"; + fout << "\t if test -n \"$$files\"; then \\\n"; + fout << "\t echo '(' cd ${DESTDIR}${CFGDIR} '&&' rm -f $$files ')'; \\\n"; + fout << "\t ( cd ${DESTDIR}${CFGDIR} && rm -f $$files ); \\\n"; + fout << "\t fi; \\\n"; + fout << "\tfi\n"; + fout << "endif\n\n"; fout << "# Validation of library files:\n"; fout << "ConfigFiles := $(wildcard cfg/*.cfg)\n"; fout << "ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles))\n"; @@ -424,6 +440,8 @@ fout << "validateXML: createXMLExamples\n"; fout << "\txmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml\n"; fout << "\txmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml\n"; + fout << "\ncheckCWEEntries: /tmp/errorlist.xml\n"; + fout << "\t./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml"; fout << "\n###### Build\n\n"; diff -Nru cppcheck-1.85/tools/donate-cpu.py cppcheck-1.86/tools/donate-cpu.py --- cppcheck-1.85/tools/donate-cpu.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/donate-cpu.py 2018-12-08 07:18:21.000000000 +0000 @@ -2,8 +2,9 @@ # # A script a user can run to donate CPU to cppcheck project # -# Syntax: donate-cpu.py [-jN] [--stop-time=HH:MM] [--work-path=path] +# Syntax: donate-cpu.py [-jN] [--package=url] [--stop-time=HH:MM] [--work-path=path] # -jN Use N threads in compilation/analysis. Default is 1. +# --package=url Check a specific package and then stop. Can be useful if you want to reproduce some warning/crash/exception/etc.. # --stop-time=HH:MM Stop analysis when time has passed. Default is that you must terminate the script. # --work-path=path Work folder path. Default path is cppcheck-donate-cpu-workfolder in your home folder. # @@ -19,7 +20,6 @@ # Quick start: just run this script without any arguments import shutil -import glob import os import subprocess import sys @@ -46,7 +46,7 @@ subprocess.call(['git', 'checkout', '-f']) subprocess.call(['git', 'pull']) else: - subprocess.call(['git', 'clone', 'http://github.com/danmar/cppcheck.git', cppcheckPath]) + subprocess.call(['git', 'clone', 'https://github.com/danmar/cppcheck.git', cppcheckPath]) if not os.path.exists(cppcheckPath): print('Failed to clone, will try again in 10 minutes..') time.sleep(600) @@ -87,17 +87,33 @@ return True +def getCppcheckVersions(): + print('Connecting to server to get Cppcheck versions..') + package = None + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = ('cppcheck.osuosl.org', 8000) + try: + sock.connect(server_address) + sock.send(b'GetCppcheckVersions\n') + versions = sock.recv(256) + except socket.error: + return ['head', '1.85'] + sock.close() + return versions.decode('utf-8').split() + + def getPackage(): print('Connecting to server to get assigned work..') package = None sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = ('cppcheck.osuosl.org', 8000) - sock.connect(server_address) try: + sock.connect(server_address) sock.send(b'get\n') package = sock.recv(256) - finally: - sock.close() + except socket.error: + package = '' + sock.close() return package.decode('utf-8') @@ -164,16 +180,46 @@ # Skip dangerous file names continue elif member.name.lower().endswith(('.c', '.cl', '.cpp', '.cxx', '.cc', '.c++', '.h', '.hpp', '.hxx', '.hh', '.tpp', '.txx')): - tf.extract(member.name) - print(member.name) + try: + tf.extract(member.name) + except OSError: + pass + except AttributeError: + pass tf.close() os.chdir(workPath) +def hasInclude(path, inc): + for root, _, files in os.walk(path): + for name in files: + filename = os.path.join(root, name) + try: + f = open(filename, 'rt') + filedata = f.read() + try: + # Python2 needs to decode the data first + filedata = filedata.decode(encoding='utf-8', errors='ignore') + except AttributeError: + # Python3 directly reads the data into a string object that has no decode() + pass + f.close() + if filedata.find('\n#include ' + inc) >= 0: + return True + except IOError: + pass + return False + + def scanPackage(workPath, cppcheck, jobs): print('Analyze..') os.chdir(workPath) - cmd = 'nice ' + cppcheck + ' ' + jobs + ' -D__GCC__ --inconclusive --enable=style --library=posix --platform=unix64 --template={file}:{line}:{inconclusive:inconclusive:}{message}[{id}] -rp=temp temp' + libraries = ' --library=posix' + if hasInclude('temp', ''): + libraries += ' --library=wxwidgets' + if hasInclude('temp', ''): + libraries += ' --library=qt' + cmd = 'nice ' + cppcheck + ' ' + jobs + libraries + ' -D__GCC__ --inconclusive --enable=style --platform=unix64 --template=daca2 -rp=temp temp' print(cmd) startTime = time.time() p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -185,6 +231,10 @@ # Crash! print('Crash!') return -1, '', -1 + if stderr.find('Internal error: Child process crashed with signal 11 [cppcheckError]') > 0: + # Crash! + print('Crash!') + return -1, '', -1 elapsedTime = stopTime - startTime count = 0 for line in stderr.split('\n'): @@ -194,11 +244,25 @@ return count, stderr, elapsedTime +def splitResults(results): + ret = [] + w = None + for line in results.split('\n'): + if line.endswith(']') and re.search(r': (error|warning|style|performance|portability|information|debug):', line): + if w is not None: + ret.append(w.strip()) + w = '' + if w is not None: + w += ' ' * 5 + line + '\n' + if w is not None: + ret.append(w.strip()) + return ret + def diffResults(workPath, ver1, results1, ver2, results2): print('Diff results..') ret = '' - r1 = sorted(results1.split('\n')) - r2 = sorted(results2.split('\n')) + r1 = sorted(splitResults(results1)) + r2 = sorted(splitResults(results2)) i1 = 0 i2 = 0 while i1 < len(r1) and i2 < len(r2): @@ -217,11 +281,12 @@ while i2 < len(r2): ret += ver2 + ' ' + r2[i2] + '\n' i2 += 1 + return ret def sendAll(connection, data): - bytes = data.encode() + bytes = data.encode('ascii', 'ignore') while bytes: num = connection.send(bytes) if num < len(bytes): @@ -232,19 +297,24 @@ def uploadResults(package, results): print('Uploading results..') - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_address = ('cppcheck.osuosl.org', 8000) - sock.connect(server_address) - try: - sendAll(sock, 'write\n' + package + '\n' + results + '\nDONE') - sock.close() - except socket.error: - pass - return package + for retry in range(4): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = ('cppcheck.osuosl.org', 8000) + sock.connect(server_address) + sendAll(sock, 'write\n' + package + '\n' + results + '\nDONE') + sock.close() + return True + except socket.error: + print('Upload failed, retry in 60 seconds') + time.sleep(30) + pass + return False jobs = '-j1' stopTime = None workpath = os.path.expanduser('~/cppcheck-donate-cpu-workfolder') +packageUrl = None for arg in sys.argv[1:]: # --stop-time=12:00 => run until ~12:00 and then stop if arg.startswith('--stop-time='): @@ -253,6 +323,9 @@ elif arg.startswith('-j'): jobs = arg print('Jobs:' + jobs[2:]) + elif arg.startswith('--package='): + packageUrl = arg[arg.find('=')+1:] + print('Package:' + packageUrl) elif arg.startswith('--work-path='): workpath = arg[arg.find('=')+1:] print('workpath:' + workpath) @@ -288,20 +361,34 @@ if not getCppcheck(cppcheckPath): print('Failed to clone Cppcheck, retry later') sys.exit(1) - if compile_version(workpath, jobs, '1.84') == False: - print('Failed to compile Cppcheck-1.84, retry later') - sys.exit(1) - if compile(cppcheckPath, jobs) == False: - print('Failed to compile Cppcheck, retry later') - sys.exit(1) - package = getPackage() + cppcheckVersions = getCppcheckVersions() + for ver in cppcheckVersions: + if ver == 'head': + if compile(cppcheckPath, jobs) == False: + print('Failed to compile Cppcheck, retry later') + sys.exit(1) + elif compile_version(workpath, jobs, ver) == False: + print('Failed to compile Cppcheck-{}, retry later'.format(ver)) + sys.exit(1) + if packageUrl: + package = packageUrl + else: + package = getPackage() + while len(package) == 0: + print("network or server might be temporarily down.. will try again in 30 seconds..") + time.sleep(30) + package = getPackage() tgz = downloadPackage(workpath, package) unpackPackage(workpath, tgz) crash = False count = '' elapsedTime = '' resultsToDiff = [] - for cppcheck in ['cppcheck/cppcheck', '1.84/cppcheck']: + for ver in cppcheckVersions: + if ver == 'head': + cppcheck = 'cppcheck/cppcheck' + else: + cppcheck = ver + '/cppcheck' c,errout,t = scanPackage(workpath, cppcheck, jobs) if c < 0: crash = True @@ -313,11 +400,18 @@ if not crash and len(resultsToDiff[0]) + len(resultsToDiff[1]) == 0: print('No results') continue - output = 'cppcheck: head 1.84\n' + output = 'cppcheck: ' + ' '.join(cppcheckVersions) + '\n' output += 'count:' + count + '\n' output += 'elapsed-time:' + elapsedTime + '\n' + if 'head' in cppcheckVersions: + output += 'head results:\n' + resultsToDiff[cppcheckVersions.index('head')] if not crash: - output += 'diff:\n' + diffResults(workpath, 'head', resultsToDiff[0], '1.84', resultsToDiff[1]) + '\n' + output += 'diff:\n' + diffResults(workpath, cppcheckVersions[0], resultsToDiff[0], cppcheckVersions[1], resultsToDiff[1]) + '\n' + if packageUrl: + print('=========================================================') + print(output) + print('=========================================================') + break uploadResults(package, output) print('Results have been uploaded') print('Sleep 5 seconds..') diff -Nru cppcheck-1.85/tools/donate-cpu-server.py cppcheck-1.86/tools/donate-cpu-server.py --- cppcheck-1.85/tools/donate-cpu-server.py 2018-10-14 12:56:51.000000000 +0000 +++ cppcheck-1.86/tools/donate-cpu-server.py 2018-12-08 07:18:21.000000000 +0000 @@ -51,10 +51,12 @@ def latestReport(latestResults): html = 'Latest daca@home results\n' html += '

Latest daca@home results

' - html += '
\n' + fmt('Package','Date       Time ','1.84','Head','Diff') + '\n'
+    html += '
\n' + fmt('Package','Date       Time ','1.85','Head','Diff') + '\n'
 
     # Write report for latest results
     for filename in latestResults:
+        if not os.path.isfile(filename):
+            continue
         package = filename[filename.rfind('/')+1:]
 
         datestr = ''
@@ -71,7 +73,7 @@
                 count = line.split(' ')[1:]
             elif line.startswith('head '):
                 added += 1
-            elif line.startswith('1.84 '):
+            elif line.startswith('1.85 '):
                 lost += 1
         diff = ''
         if lost > 0:
@@ -88,7 +90,7 @@
     html = 'Crash report\n'
     html += '

Crash report

\n' html += '
\n'
-    html += 'Package                                 1.84  Head\n'
+    html += 'Package                                 1.85  Head\n'
     for filename in sorted(glob.glob(os.path.expanduser('~/daca@home/donated-results/*'))):
         if not os.path.isfile(filename):
             continue
@@ -112,13 +114,14 @@
             html += out + '\n'
             break
     html += '
\n' + html += '\n' return html def diffReportFromDict(out, today): html = '
\n'
-    html += 'MessageID                           1.84    Head\n'
+    html += 'MessageID                           1.85    Head\n'
     sum0 = 0
     sum1 = 0
     for messageId in sorted(out.keys()):
@@ -174,7 +177,7 @@
             if not line.endswith(']'):
                 continue
             index = None
-            if line.startswith('1.84 '):
+            if line.startswith('1.85 '):
                 index = 0
             elif line.startswith('head '):
                 index = 1
@@ -250,7 +253,7 @@
 
 def timeReport(resultPath):
     text = 'Time report\n\n'
-    text += 'Package 1.84 Head\n'
+    text += 'Package 1.85 Head\n'
 
     totalTime184 = 0.0
     totalTimeHead = 0.0
@@ -302,7 +305,7 @@
         try:
             cmd = self.cmd
             print('[' + strDateTime() + '] ' + cmd)
-            res = re.match(r'GET /([a-zA-Z0-9_\-\.]*) HTTP', cmd)
+            res = re.match(r'GET /([a-zA-Z0-9_\-\.\+]*) HTTP', cmd)
             if res is None:
                 self.connection.close()
                 return
@@ -344,6 +347,7 @@
             time.sleep(1)
             self.connection.close()
 
+
 def server(server_address_port, packages, packageIndex, resultPath):
     socket.setdefaulttimeout(30)
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -354,8 +358,9 @@
     sock.listen(1)
 
     latestResults = []
-    with open('latest.txt', 'rt') as f:
-        latestResults = f.read().strip().split(' ')
+    if os.path.isfile('latest.txt'):
+        with open('latest.txt', 'rt') as f:
+            latestResults = f.read().strip().split(' ')
 
     while True:
         # wait for a connection
@@ -375,16 +380,23 @@
         if cmd.startswith('GET /'):
             newThread = HttpClientThread(connection, cmd, resultPath, latestResults)
             newThread.start()
+        elif cmd=='GetCppcheckVersions\n':
+            print('[' + strDateTime() + '] GetCppcheckVersions: head 1.85')
+            connection.send('head 1.85')
+            connection.close()
         elif cmd=='get\n':
-            packages[packageIndex] = packages[packageIndex].strip()
-            print('[' + strDateTime() + '] get:' + packages[packageIndex])
-            connection.send(packages[packageIndex])
+            pkg = packages[packageIndex].strip()
+            packages[packageIndex] = pkg
             packageIndex += 1
             if packageIndex >= len(packages):
                 packageIndex = 0
+
             f = open('package-index.txt', 'wt')
             f.write(str(packageIndex) + '\n')
             f.close()
+
+            print('[' + strDateTime() + '] get:' + pkg)
+            connection.send(pkg)
             connection.close()
         elif cmd.startswith('write\nftp://'):
             # read data
@@ -411,17 +423,24 @@
 
             # save data
             res = re.match(r'ftp://.*pool/main/[^/]+/([^/]+)/[^/]*tar.gz',url)
-            if res and url in packages:
-                print('results added for package ' + res.group(1))
-                filename = resultPath + '/' + res.group(1)
-                with open(filename, 'wt') as f:
-                    f.write(strDateTime() + '\n' + data)
-                # track latest added results..
-                if len(latestResults) >= 20:
-                    latestResults = latestResults[1:]
-                latestResults.append(filename)
-                with open('latest.txt', 'wt') as f:
-                    f.write(' '.join(latestResults))
+            if res is None:
+                print('results not written. res is None.')
+                continue
+            if url not in packages:
+                url2 = url + '\n'
+                if url2 not in packages:
+                    print('results not written. url is not in packages.')
+                    continue
+            print('results added for package ' + res.group(1))
+            filename = resultPath + '/' + res.group(1)
+            with open(filename, 'wt') as f:
+                f.write(strDateTime() + '\n' + data)
+            # track latest added results..
+            if len(latestResults) >= 20:
+                latestResults = latestResults[1:]
+            latestResults.append(filename)
+            with open('latest.txt', 'wt') as f:
+                f.write(' '.join(latestResults))
         else:
             print('[' + strDateTime() + '] invalid command: ' + firstLine)
             connection.close()
diff -Nru cppcheck-1.85/tools/pr.py cppcheck-1.86/tools/pr.py
--- cppcheck-1.85/tools/pr.py	1970-01-01 00:00:00.000000000 +0000
+++ cppcheck-1.86/tools/pr.py	2018-12-08 07:18:21.000000000 +0000
@@ -0,0 +1,40 @@
+
+import requests
+import subprocess
+import sys
+
+response = requests.get('https://api.github.com/repos/danmar/cppcheck/pulls/' + sys.argv[1])
+if response.status_code == 200:
+    j = response.json()
+    login = j['user']['login']
+    title = j['title']
+    body  = j['body']
+    branch = j['head']['ref']
+    sha = j['head']['sha']
+
+    subprocess.call('git checkout -b {}-{} master'.format(login, branch).split())
+    subprocess.call('git pull https://github.com/{}/cppcheck.git {}'.format(login, branch).split())
+
+    p = subprocess.Popen(['git', 'show', '--format=%an <%ae>', sha], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    comm = p.communicate()
+    author = comm[0].decode(encoding='utf-8', errors='ignore').split('\n')[0]
+    if login == 'pfultz2':
+        author = 'Paul Fultz II ' + author[author.find('<'):]
+
+    subprocess.call(['./runastyle'])
+    subprocess.call('git commit -a -m astyle'.split())
+
+    subprocess.call('git checkout master'.split())
+    subprocess.call('git merge --squash {}-{}'.format(login, branch).split())
+    subprocess.call(['git', 'commit', '-a', '--author='+author, '-m', title + '\n\n' + body])
+    subprocess.call('git branch -D {}-{}'.format(login, branch).split())
+
+    p = subprocess.Popen('git show --format=format:%h'.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    comm = p.communicate()
+    stdout = comm[0]
+    if stdout.find('\n') > 0:
+        stdout = stdout[:stdout.find('\n')]
+    print('\nMessage: I merged this with ' + stdout)
+
+
+
diff -Nru cppcheck-1.85/tools/rundaca2.py cppcheck-1.86/tools/rundaca2.py
--- cppcheck-1.85/tools/rundaca2.py	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/tools/rundaca2.py	1970-01-01 00:00:00.000000000 +0000
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-
-import subprocess
-import pexpect
-import os
-import sys
-import time
-
-BASE = '1.84'
-
-def compilecppcheck(CPPFLAGS):
-    subprocess.call(['nice', 'make', 'clean'])
-    subprocess.call(['nice', 'make', 'SRCDIR=build', 'CFGDIR=' +
-                    os.path.expanduser('~/cppcheck/cfg'), 'CXXFLAGS=-g -O2', 'CPPFLAGS=' + CPPFLAGS])
-    subprocess.call(['cp', 'cppcheck', os.path.expanduser('~/daca2/cppcheck-head')])
-
-
-def runcppcheck(rev, folder):
-    subprocess.call(['rm', '-rf', os.path.expanduser('~/daca2/' + folder)])
-    subprocess.call(['nice', '--adjustment=19', 'python',
-                    os.path.expanduser('~/cppcheck/tools/daca2.py'), folder, '--rev=' + rev,
-                    '--baseversion='+BASE, '--skip=hashdeep', '--skip=lice'])
-
-
-def daca2report(reportfolder):
-    subprocess.call(['rm', '-rf', reportfolder])
-    subprocess.call(['mkdir', reportfolder])
-    subprocess.call(['python', os.path.expanduser('~/cppcheck/tools/daca2-report.py'), reportfolder])
-
-
-# Upload file to sourceforge server using scp
-def upload(localfolder, webfolder, password):
-    if len(password) < 3:
-        return
-    tries = 1
-    while tries <= 5:
-        try:
-            child = pexpect.spawn(
-                'scp -r ' + localfolder + ' danielmarjamaki,cppcheck@web.sf.net:htdocs/' + webfolder)
-            # child.expect('upload@trac.cppcheck.net\'s password:')
-            child.expect('Password:')
-            child.sendline(password)
-            child.interact()
-            return
-        except (IOError, OSError, pexpect.TIMEOUT, pexpect.EOF):
-            print('rundaca2.py: Upload failed. Sleep for 10 seconds..')
-            time.sleep(10)
-            tries = tries + 1
-
-def getDate(filename):
-    for line in open(filename):
-        if line.startswith('DATE '):
-            return line[5:]
-    return None
-
-def getFolderNum():
-    folders = '0123456789abcdefghijklmnopqrstuvwxyz'
-    oldestDate = None
-    oldestFolderNum = 0
-    for folderNum in range(len(folders)):
-        folder = folders[folderNum]
-        path = os.path.expanduser('~/daca2/' + folder)
-        if not os.path.isdir(path):
-            if folder == '0' or folder >= 'a':
-                return folderNum
-            continue
-        if not os.path.isfile(path + '/results-head.txt'):
-            return folderNum
-        if not os.path.isfile(path + '/results-'+BASE+'.txt'):
-            return folderNum
-        d1 = getDate(path + '/results-head.txt')
-        if d1 is None: # results are unfinished so they need to be recreated
-            return folderNum
-        d2 = getDate(path + '/results-'+BASE+'.txt')
-        if d2 is None: # results are unfinished so they need to be recreated
-            return folderNum
-        if oldestDate is None or d1 < oldestDate:
-            oldestDate = d1
-            oldestFolderNum = folderNum
-        if d2 < oldestDate:
-            oldestDate = d2
-            oldestFolderNum = folderNum
-
-    return oldestFolderNum
-
-
-def daca2(folderNum, password):
-    folders = '0123456789abcdefghijklmnopqrstuvwxyz'
-    folder = folders[folderNum % len(folders)]
-
-    print('Daca2 folder=' + folder)
-
-    os.chdir(os.path.expanduser('~/cppcheck'))
-    subprocess.call(['git', 'pull'])
-    p = subprocess.Popen(['git', 'show', '--format=%h'],
-                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    comm = p.communicate()
-    rev = comm[0]
-    rev = rev[:rev.find('\n')]
-
-    print('rundaca2.py: compile cppcheck')
-    compilecppcheck('-DMAXTIME=600 -DDACA2')
-
-    print('rundaca2.py: runcppcheck')
-    runcppcheck(rev, folder)
-    runcppcheck(rev, 'lib' + folder)
-
-    print('rundaca2.py: daca2 report')
-    daca2report(os.path.expanduser('~/daca2-report'))
-
-    print('rundaca2.py: upload')
-    upload(os.path.expanduser('~/daca2-report'), 'devinfo/', password)
-
-
-print('enter password:')
-password = sys.stdin.readline().rstrip()
-folderNum = getFolderNum()
-while True:
-    daca2(folderNum, password)
-    folderNum = folderNum + 1
-
diff -Nru cppcheck-1.85/tools/triage/mainwindow.cpp cppcheck-1.86/tools/triage/mainwindow.cpp
--- cppcheck-1.85/tools/triage/mainwindow.cpp	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/tools/triage/mainwindow.cpp	2018-12-08 07:18:21.000000000 +0000
@@ -47,15 +47,20 @@
             url = line;
             errorMessage.clear();
         } else if (!url.isEmpty() && QRegExp(".*: (error|warning|style|note):.*").exactMatch(line)) {
-            if (!errorMessage.isEmpty())
-                errorMessage += '\n';
-            errorMessage += line;
-        } else if (!url.isEmpty() && QRegExp("(head|1.8.) .*:[0-9]+:.*\\]").exactMatch(line)) {
+            if (line.indexOf(": note:") > 0)
+                errorMessage += '\n' + line;
+            else if (errorMessage.isEmpty()) {
+                errorMessage = url + '\n' + line;
+            } else {
+                allErrors << errorMessage;
+                errorMessage = url + '\n' + line;
+            }
+        } else if (!url.isEmpty() && QRegExp("^(head|1.[0-9][0-9]) .*:[0-9]+:.*\\]").exactMatch(line)) {
             allErrors << (url + '\n' + line);
         }
     }
-    if (!url.isEmpty() && !errorMessage.isEmpty())
-        allErrors << (url + "\n" + errorMessage);
+    if (!errorMessage.isEmpty())
+        allErrors << errorMessage;
     if (allErrors.size() > 100) {
         // remove items in /test/
         for (int i = allErrors.size()-1; i >= 0 && allErrors.size() > 100; --i) {
@@ -105,7 +110,7 @@
         return;
     const QString url = lines[0];
     QString msg = lines[1];
-    if (msg.startsWith("head ") || msg.startsWith("1.84 "))
+    if (QRegExp("^(head|1.[0-9][0-9]) .*").exactMatch(msg))
         msg = msg.mid(5);
     const QString archiveName = url.mid(url.lastIndexOf("/") + 1);
     const int pos1 = msg.indexOf(":");
diff -Nru cppcheck-1.85/.travis_llvmcheck_suppressions cppcheck-1.86/.travis_llvmcheck_suppressions
--- cppcheck-1.85/.travis_llvmcheck_suppressions	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/.travis_llvmcheck_suppressions	2018-12-08 07:18:21.000000000 +0000
@@ -1 +1,8 @@
 unreadVariable
+shadowVar
+shadowFunction
+unusedStructMember
+nullPointer
+uninitvar
+noExplicitConstructor
+
diff -Nru cppcheck-1.85/.travis_suppressions cppcheck-1.86/.travis_suppressions
--- cppcheck-1.85/.travis_suppressions	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/.travis_suppressions	2018-12-08 07:18:21.000000000 +0000
@@ -6,6 +6,10 @@
 missingOverride
 noValidConfiguration
 useStlAlgorithm
+shadowFunction
+# false positive
+knownConditionTrueFalse:lib/checkcondition.cpp
+knownConditionTrueFalse:build/checkcondition.cpp
 
 *:gui/test/*
 *:test/test.cxx
diff -Nru cppcheck-1.85/.travis.yml cppcheck-1.86/.travis.yml
--- cppcheck-1.85/.travis.yml	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/.travis.yml	2018-12-08 07:18:21.000000000 +0000
@@ -1,6 +1,5 @@
 language: cpp
 dist: trusty
-sudo: required
 
 compiler:
   - gcc
@@ -10,7 +9,7 @@
   global:
 #   unfortunately we need this to stay within 50min timelimit given by travis.
 #   this also turns off the debug/warning cxxflags
-    - ORIGINAL_CXXFLAGS="-pedantic -Wall -Wextra -Wabi -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wshadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g "
+    - ORIGINAL_CXXFLAGS="-pedantic -Wall -Wextra -Wabi -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g "
     - CXXFLAGS="${ORIGINAL_CXXFLAGS} -O2 -march=native -Wstrict-aliasing=2 -Werror=strict-aliasing"
     - CPPCHECK=${TRAVIS_BUILD_DIR}/cppcheck
   matrix:
@@ -82,6 +81,8 @@
 #  - make validateXML doesn't work in this context unfortunately
   - ${CPPCHECK} --errorlist >/tmp/errorlist.xml
   - xmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml
+# check for missing CWE entries
+  - make checkCWEEntries
 # check test/cfg
   - make checkcfg
   - make validateXML
@@ -138,6 +139,11 @@
   - ${CPPCHECK} --dump misra-test.cpp
   - python3 ../misra.py -verify misra-test.cpp.dump
   - cd ../../
+# check addons/namingng.py
+  - cd addons/test
+  - ${CPPCHECK} --dump namingng_test.c
+  - python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump
+  - cd ../..
 
 
 notifications:
diff -Nru cppcheck-1.85/win_installer/productInfo.wxi cppcheck-1.86/win_installer/productInfo.wxi
--- cppcheck-1.85/win_installer/productInfo.wxi	2018-10-14 12:56:51.000000000 +0000
+++ cppcheck-1.86/win_installer/productInfo.wxi	2018-12-08 07:18:21.000000000 +0000
@@ -1,8 +1,8 @@
 
 
-  
+  
   
-  
+