diff --git a/Makefile b/Makefile index 161c1461c..ca92f981f 100644 --- a/Makefile +++ b/Makefile @@ -362,7 +362,7 @@ $(SRCDIR)/tokenize.o: lib/tokenize.cpp lib/tokenize.h lib/errorlogger.h lib/conf $(SRCDIR)/tokenlist.o: lib/tokenlist.cpp lib/tokenlist.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/path.h lib/preprocessor.h lib/settings.h lib/library.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/tokenlist.o $(SRCDIR)/tokenlist.cpp -$(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/valueflow.h lib/token.h lib/config.h lib/mathlib.h +$(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/valueflow.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/token.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/valueflow.o $(SRCDIR)/valueflow.cpp cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h lib/checkunusedfunctions.h lib/check.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/filelister.h diff --git a/htdocs/devinfo/index.php b/htdocs/devinfo/index.php index c9000fae5..04754b447 100644 --- a/htdocs/devinfo/index.php +++ b/htdocs/devinfo/index.php @@ -112,7 +112,7 @@ the latest sources in a zip or tgz archive from the github website.

Other

diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 8abfa8bb0..0283f81b2 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -468,7 +468,7 @@ void CheckClass::initializeVarList(const Function &func, std::liststr() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) { + } else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), "%type%|,|{")) { initVar(ftok->str(), scope, usage); ftok = ftok->linkAt(1); } else if (level != 0 && Token::Match(ftok, "%var% =")) // assignment in the initializer: var(value = x) diff --git a/lib/checkinternal.cpp b/lib/checkinternal.cpp index ca780cd07..b4e13eb40 100644 --- a/lib/checkinternal.cpp +++ b/lib/checkinternal.cpp @@ -22,6 +22,8 @@ #include "symboldatabase.h" #include #include +#include +#include // Register this check class (by creating a static instance of it). // Disabled in release builds @@ -48,6 +50,34 @@ void CheckInternal::checkTokenMatchPatterns() continue; } + const char *p = pattern.c_str(); + while (*p) { + while (*p && std::isspace(*p)) + p++; + const char *start = p; + while (*p && !std::isspace(*p)) + p++; + const char *end = p - 1; + if (start < end && !(*start == '[' && *end == ']')) { + bool cmd = (*start=='%' && std::isalpha(*(start+1))); + // check multicompare pattern.. + for (const char *s = start; s != end; s++) { + if (*s == '|') { + if (!(*(s+1) == '%' && std::isalpha(*(s+2)))) { + cmd = false; + } else if (!cmd && + std::strncmp(s+1,"%op%",4)!=0 && + std::strncmp(s+1,"%or%",4)!=0 && + std::strncmp(s+1,"%cop%",5)!=0 && + std::strncmp(s+1,"%var%",5)!=0 && + std::strncmp(s+1,"%oror%",6)!=0) { + multiComparePatternError(tok, pattern, funcname); + } + } + } + } + } + // Check for signs of complex patterns if (pattern.find_first_of("[|%") != std::string::npos) continue; @@ -245,6 +275,13 @@ void CheckInternal::checkRedundantNextPrevious() } } +void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::error, "multiComparePatternError", + "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\"" + ); +} + void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "simplePatternError", diff --git a/lib/checkinternal.h b/lib/checkinternal.h index a49336c1a..805b64c60 100644 --- a/lib/checkinternal.h +++ b/lib/checkinternal.h @@ -73,6 +73,7 @@ public: void checkRedundantNextPrevious(); private: + void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); @@ -81,6 +82,7 @@ private: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckInternal c(0, settings, errorLogger); + c.multiComparePatternError(0, ";|%type%", "Match"); c.simplePatternError(0, "class {", "Match"); c.complexPatternError(0, "%type% ( )", "Match"); c.missingPercentCharacterError(0, "%num", "Match"); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index be5ef24c0..7dd067319 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2158,8 +2158,10 @@ void CheckOther::checkZeroDivision() std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if (it->intvalue == 0) { - if (!it->link || _settings->isEnabled("warning")) + if (it->condition == NULL) zerodivError(tok); + else if (_settings->isEnabled("warning")) + zerodivcondError(it->condition,tok); } } } @@ -2176,6 +2178,11 @@ void CheckOther::checkZeroDivisionOrUselessCondition() { if (!_settings->isEnabled("warning")) return; + + // Use experimental checking instead based on value flow analysis + if (_settings->valueFlow) + return; + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t numberOfFunctions = symbolDatabase->functionScopes.size(); for (std::size_t functionIndex = 0; functionIndex < numberOfFunctions; ++functionIndex) { @@ -2297,6 +2304,24 @@ void CheckOther::checkZeroDivisionOrUselessCondition() } } +// TODO: this utility function should probably be moved to some common file +static std::string astStringify(const Token *top) +{ + const Token *start = top; + while (start->astOperand1() && start->astOperand2()) + start = start->astOperand1(); + const Token *end = top; + while (end->astOperand1() && end->astOperand2()) + end = end->astOperand2(); + std::string str; + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + str += tok->str(); + if (Token::Match(tok, "%var%|%num% %var%|%num%")) + str += " "; + } + return str + end->str(); +} + void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv) { std::list callstack; @@ -2307,9 +2332,13 @@ void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv) callstack.push_back(tokdiv); } std::string condition; - if (Token::Match(tokcond, "%num% <|<=")) { + if (!tokcond) { + // getErrorMessages + } else if (Token::Match(tokcond, "%num% <|<=")) { condition = tokcond->strAt(2) + ((tokcond->strAt(1) == "<") ? ">" : ">=") + tokcond->str(); - } else if (tokcond) { + } else if (tokcond->isComparisonOp()) { + condition = astStringify(tokcond); + } else { if (tokcond->str() == "!") condition = tokcond->next()->str() + "==0"; else diff --git a/lib/library.h b/lib/library.h index ea146bf80..b2429480b 100644 --- a/lib/library.h +++ b/lib/library.h @@ -98,8 +98,12 @@ public: class ArgumentChecks { public: - ArgumentChecks() { - notbool = notnull = notuninit = formatstr = strz = false; + ArgumentChecks() : + notbool(false), + notnull(false), + notuninit(false), + formatstr(false), + strz(false) { } bool notbool; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index cb0fa41ca..b459ea849 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2023,7 +2023,7 @@ bool Function::isImplicitlyVirtual(bool defaultVal) const bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const { // check each base class - for (unsigned int i = 0; i < baseType->derivedFrom.size(); ++i) { + for (std::size_t i = 0; i < baseType->derivedFrom.size(); ++i) { // check if base class exists in database if (baseType->derivedFrom[i].type && baseType->derivedFrom[i].type->classScope) { const Scope *parent = baseType->derivedFrom[i].type->classScope; @@ -2055,9 +2055,11 @@ bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const } } - if (!baseType->derivedFrom[i].type->derivedFrom.empty()) - if (isImplicitlyVirtual_rec(baseType->derivedFrom[i].type, safe)) + if (!baseType->derivedFrom[i].type->derivedFrom.empty()) { + // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN + if ((baseType != baseType->derivedFrom[i].type) && isImplicitlyVirtual_rec(baseType->derivedFrom[i].type, safe)) return true; + } } else { // unable to find base class so assume it has no virtual function safe = false; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 953d4a4bb..2a2cfd7ff 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -65,6 +65,10 @@ public: } needInitialization; struct BaseInfo { + BaseInfo() : + type(NULL), nameTok(NULL), access(Public), isVirtual(false) { + } + std::string name; const Type* type; const Token* nameTok; @@ -73,6 +77,10 @@ public: }; struct FriendInfo { + FriendInfo() : + nameStart(NULL), nameEnd(NULL), type(NULL) { + } + const Token* nameStart; const Token* nameEnd; std::string name; diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 54462f93c..c0fdcaf9b 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -620,7 +620,7 @@ bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::stri if (patternAfter) { const Token *tok = instance; unsigned int indentlevel = 0; - for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0); tok = tok->next()) { + for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0) && (tok->str() != ">>" || indentlevel > 1); tok = tok->next()) { if (Token::Match(tok, "[<,] %var% <") && templateParameters(tok->tokAt(2)) > 0) ++indentlevel; if (indentlevel > 0 && tok->str() == ">") diff --git a/lib/token.cpp b/lib/token.cpp index a069f73dd..77774cc5d 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -206,6 +206,7 @@ void Token::deleteThis() _function = _next->_function; _variable = _next->_variable; _originalName = _next->_originalName; + values = _next->values; if (_link) _link->link(this); @@ -229,6 +230,7 @@ void Token::deleteThis() _function = _previous->_function; _variable = _previous->_variable; _originalName = _previous->_originalName; + values = _previous->values; if (_link) _link->link(this); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 54a7a7e53..bdb7500a0 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -370,6 +370,7 @@ Token * Tokenizer::deleteInvalidTypedef(Token *typeDef) } struct Space { + Space() : classEnd(NULL), isNamespace(false) { } std::string className; const Token * classEnd; bool isNamespace; @@ -1605,7 +1606,7 @@ bool Tokenizer::tokenize(std::istream &code, list.createAst(); if (_settings->valueFlow) - ValueFlow::setValues(list.front()); + ValueFlow::setValues(&list, _errorLogger, _settings); return true; } @@ -7531,23 +7532,23 @@ bool Tokenizer::duplicateDefinition(Token ** tokPtr, const Token * name) const class EnumValue { public: - EnumValue() { - name = 0; - value = 0; - start = 0; - end = 0; + EnumValue() : + name(NULL), + value(NULL), + start(NULL), + end(NULL) { } - EnumValue(const EnumValue &ev) { - name = ev.name; - value = ev.value; - start = ev.start; - end = ev.end; + EnumValue(const EnumValue &ev) : + name(ev.name), + value(ev.value), + start(ev.start), + end(ev.end) { } - EnumValue(Token *name_, Token *value_, Token *start_, Token *end_) { - name = name_; - value = value_; - start = start_; - end = end_; + EnumValue(Token *name_, Token *value_, Token *start_, Token *end_) : + name(name_), + value(value_), + start(start_), + end(end_) { } void simplify(const std::map &enumValues) { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index a6fe9d104..3f33825cf 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -17,20 +17,47 @@ */ #include "valueflow.h" -#include "token.h" +#include "errorlogger.h" #include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenlist.h" -static void valueFlowBeforeCondition(Token *tokens) +#include + + +static void printvalues(const Token *tok) { - for (Token *tok = tokens; tok; tok = tok->next()) { + if (tok->values.empty()) + std::cout << "empty"; + for (std::list::const_iterator it = tok->values.begin(); it != tok->values.end(); ++it) + std::cout << " " << (it->intvalue); + std::cout << std::endl; +} + +static void bailout(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what) +{ + std::list callstack; + callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist)); + ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "ValueFlow bailout: " + what, "valueFlowBailout", false); + errorLogger->reportErr(errmsg); +} + +static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) +{ + for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { unsigned int varid; MathLib::bigint num; + const Variable *var; if (Token::Match(tok, "==|!=|>=|<=") && tok->astOperand1() && tok->astOperand2()) { if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) { varid = tok->astOperand1()->varId(); + var = tok->astOperand1()->variable(); num = MathLib::toLongNumber(tok->astOperand2()->str()); } else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) { varid = tok->astOperand2()->varId(); + var = tok->astOperand2()->variable(); num = MathLib::toLongNumber(tok->astOperand1()->str()); } else { continue; @@ -38,9 +65,11 @@ static void valueFlowBeforeCondition(Token *tokens) } else if (Token::Match(tok->previous(), "if|while ( %var% %oror%|&&|)") || Token::Match(tok, "%oror%|&& %var% %oror%|&&|)")) { varid = tok->next()->varId(); + var = tok->next()->variable(); num = 0; } else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) { varid = tok->astOperand1()->varId(); + var = tok->astOperand1()->variable(); num = 0; } else { continue; @@ -49,27 +78,112 @@ static void valueFlowBeforeCondition(Token *tokens) if (varid == 0U) continue; + // bailout: global variables + if (var && var->isGlobal()) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok, "global variable " + var->nameToken()->str()); + continue; + } + struct ValueFlow::Value val; - val.link = tok; + val.condition = tok; val.intvalue = num; - for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { - if (tok2->varId() == varid) + for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) { + if (!tok2) { + if (settings->debugwarnings) { + std::list callstack; + callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist)); + ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "iterated too far", "debugValueFlowBeforeCondition", false); + errorLogger->reportErr(errmsg); + } + break; + } + + if (tok2->varId() == varid) { + // bailout: assignment + if (Token::Match(tok2, "%var% =")) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); + break; + } + tok2->values.push_back(val); - if (tok2->str() == "{") { - if (!Token::simpleMatch(tok2->previous(), ") {")) - break; - if (!Token::simpleMatch(tok2->previous()->link()->previous(), "if (")) + if (var && tok2 == var->nameToken()) break; } + + if (tok2->str() == "}") { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " stopping on }"); + break; + } } } } -void ValueFlow::setValues(Token *tokens) +static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { - for (Token *tok = tokens; tok; tok = tok->next()) + std::list argvalues; + for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { + if (Token::Match(tok, "[(,] %var% [,)]") && !tok->next()->values.empty()) + argvalues = tok->next()->values; + else if (Token::Match(tok, "[(,] %num% [,)]")) { + ValueFlow::Value val; + val.condition = 0; + val.intvalue = MathLib::toLongNumber(tok->next()->str()); + argvalues.clear(); + argvalues.push_back(val); + } else { + continue; + } + + const Token * const argumentToken = tok->next(); + + // is this a function call? + const Token *ftok = tok; + while (ftok && ftok->str() != "(") + ftok = ftok->astParent(); + if (!ftok || !ftok->astOperand1() || !ftok->astOperand2() || !ftok->astOperand1()->function()) + continue; + + // Get argument nr + unsigned int argnr = 0; + for (const Token *argtok = ftok->next(); argtok && argtok != argumentToken; argtok = argtok->nextArgument()) + ++ argnr; + + // Get function argument, and check if parameter is passed by value + const Function * const function = ftok->astOperand1()->function(); + const Variable * const arg = function ? function->getArgumentVar(argnr) : NULL; + if (!Token::Match(arg ? arg->typeStartToken() : NULL, "const| %type% %var% ,|)")) + continue; + + // Function scope.. + const Scope * const functionScope = function ? function->functionScope : NULL; + if (!functionScope) + continue; + + // Set value in function scope.. + const unsigned int varid2 = arg->nameToken()->varId(); + for (const Token *tok2 = functionScope->classStart->next(); tok2 != functionScope->classEnd; tok2 = tok2->next()) { + if (Token::Match(tok2, "%cop%|return %varid%", varid2)) { + tok2 = tok2->next(); + std::list &values = const_cast(tok2)->values; + values.insert(values.begin(), argvalues.begin(), argvalues.end()); + } else if (tok2->varId() == varid2 || tok2->str() == "{") { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok2, "parameter " + arg->nameToken()->str()); + continue; + } + } + } +} + +void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) +{ + for (Token *tok = tokenlist->front(); tok; tok = tok->next()) tok->values.clear(); - valueFlowBeforeCondition(tokens); + valueFlowBeforeCondition(tokenlist, errorLogger, settings); + valueFlowSubFunction(tokenlist, errorLogger, settings); } diff --git a/lib/valueflow.h b/lib/valueflow.h index e707270ea..7e5474b66 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -22,15 +22,17 @@ //--------------------------------------------------------------------------- class Token; +class TokenList; +class ErrorLogger; +class Settings; namespace ValueFlow { - struct Value { - const Token *link; + const Token *condition; long long intvalue; }; - void setValues(Token *tokens); + void setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); } #endif // valueflowH diff --git a/test/testinternal.cpp b/test/testinternal.cpp index dcaafced4..cc8604df9 100644 --- a/test/testinternal.cpp +++ b/test/testinternal.cpp @@ -40,6 +40,7 @@ private: TEST_CASE(unknownPattern) TEST_CASE(redundantNextPrevious) TEST_CASE(internalError) + TEST_CASE(invalidMultiCompare); } void check(const char code[]) { @@ -222,7 +223,9 @@ private: " const Token *tok;\n" " Token::Match(tok, \"foo|%type|bar\");\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (error) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \"foo|%type|bar\"\n" + "[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n" + , errout.str()); // Make sure we don't take %or% for a broken %oror% check("void f() {\n" @@ -312,6 +315,27 @@ private: "};"); ASSERT_EQUALS("", errout.str()); } + + void invalidMultiCompare() { + // #5310 + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \";|%type%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \";|%type%\"\n", errout.str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \";|%oror%\");\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" // The %var%|%num% works.. + " const Token *tok;\n" + " Token::Match(tok, \"%var%|%num%\");\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestInternal) diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index d076cd44a..ea773fa3f 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -135,6 +135,7 @@ private: TEST_CASE(template40); // #5055 - template specialization outside struct TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly TEST_CASE(template42); // #4878 - variadic templates + TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); TEST_CASE(template_default_type); @@ -2367,6 +2368,19 @@ private: tok(code); } + void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantation + const char code[] = "template struct C { };" + "template struct D { static int f() { return C::f(); } };" + "template inline int f2() { return D::f(); }" + "template int f1(int x, T *) { int id = f2(); return id; }" + "template <> struct C < B < A >> {" + " static int f() {" + " return f1 < B < A >> (0, reinterpret_cast< B *>(E::Int(-1)));" + " }" + "};"; + tok(code); // Don't assert + } + void template_default_parameter() { { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 3cd5cbe43..ba10d8b2e 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -1760,6 +1760,19 @@ private: "};"); ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(false)); // Default false, but we saw "virtual" -> true } + // #5289 + { + GET_SYMBOL_DB("template<>\n" + "class Bar {\n" + "};\n" + "template\n" + "class Bar : private Bar {\n" + " void foo() {\n" + " }\n" + "};"); + ASSERT(db && db->findScopeByName("Bar") && !db->findScopeByName("Bar")->functionList.front().isImplicitlyVirtual(false)); + } + } void garbage() { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index b12e4eefd..d33a0cc98 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -34,7 +34,8 @@ public: private: void run() { - valueFlowBeforeCondition(); + TEST_CASE(valueFlowBeforeCondition); + TEST_CASE(valueFlowSubFunction); } bool testValueOfX(const char code[], unsigned int linenr, int value) { @@ -53,19 +54,71 @@ private: if (it->intvalue == value) return true; } - return false; } } return false; } + + void bailout(const char code[]) { + Settings settings; + settings.valueFlow = true; // temporary flag + settings.debugwarnings = true; + + // Tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + errout.str(""); + tokenizer.tokenize(istr, "test.cpp"); + } + + void valueFlowBeforeCondition() { const char code[] = "void f(int x) {\n" " int a = x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 123)); + + // bailout: if/else/etc + bailout("void f(int x) {\n" + " if (x != 123) { b = x; }\n" + " if (x == 123) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: variable x stopping on }\n", errout.str()); + + // bailout: assignment + bailout("void f(int x) {\n" + " x = y;\n" + " if (x == 123) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: assignment of x\n", errout.str()); + + // bailout: global variables + bailout("int x;\n" + "void f() {\n" + " int a = x;\n" + " if (x == 123) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (debug) ValueFlow bailout: global variable x\n", errout.str()); + } + + void valueFlowSubFunction() { + const char *code; + + code = "void f1(int x) { return x; }\n" + "void f2(int x) {\n" + " f1(123);\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 1U, 123)); + + code = "void f1(int x) { return x; }\n" + "void f2(int x) {\n" + " f1(x);\n" + " if (x==0){}\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 1U, 0)); } }; diff --git a/tools/daca2-report.py b/tools/daca2-report.py index a2c4efa8d..783edbe3d 100644 --- a/tools/daca2-report.py +++ b/tools/daca2-report.py @@ -31,13 +31,6 @@ def readdate(data): return None datepos = datepos + 1 -if os.path.isfile(os.path.expanduser('~/aws-debian.pem')): - subprocess.call(['scp', - '-i', - os.path.expanduser('~/aws-debian.pem'), - 'admin@ec2-54-201-59-232.us-west-2.compute.amazonaws.com:daca2/results-*.txt', - os.path.expanduser('~/daca2/')]) - path = '.' if len(sys.argv) == 2: path = sys.argv[1] diff --git a/tools/times-tags.sh b/tools/times-tags.sh old mode 100644 new mode 100755 diff --git a/tools/times.sh b/tools/times.sh old mode 100644 new mode 100755