From f1212e66f7955cd278cffc5f54ddcaf586b1712d Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 22 Jun 2022 21:22:04 +0200 Subject: [PATCH 01/36] Fix FP constVariable with 2D array. (#4228) Test case #8717 was actually a FP as well. --- lib/checkother.cpp | 7 +++++-- test/testother.cpp | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index b31f2cdf3..fc3cc333b 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1565,11 +1565,14 @@ void CheckOther::checkConstPointer() if (Token::simpleMatch(gparent, "return")) continue; else if (Token::Match(gparent, "%assign%") && parent == gparent->astOperand2()) { - bool takingRef = false; + bool takingRef = false, nonConstPtrAssignment = false; const Token *lhs = gparent->astOperand1(); if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs) takingRef = true; - if (!takingRef) + if (lhs && lhs->valueType() && lhs->valueType()->pointer && (lhs->valueType()->constness & 1) == 0 && + parent->valueType() && parent->valueType()->pointer) + nonConstPtrAssignment = true; + if (!takingRef && !nonConstPtrAssignment) continue; } else if (Token::simpleMatch(gparent, "[") && gparent->astOperand2() == parent) continue; diff --git a/test/testother.cpp b/test/testother.cpp index 4af565858..6446bdf56 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -3230,6 +3230,14 @@ private: ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n" "[test.cpp:5]: (style) Variable 'p' can be declared as pointer to const\n", errout.str()); + + check("void f() {\n" + " char a[1][1];\n" + " char* b[1];\n" + " b[0] = a[0];\n" + " **b = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void switchRedundantAssignmentTest() { @@ -9383,7 +9391,7 @@ private: " int local_argc = 0;\n" " local_argv[local_argc++] = argv[0];\n" "}\n", "test.c"); - ASSERT_EQUALS("[test.c:1]: (style) Parameter 'argv' can be declared as const array\n", errout.str()); + ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 0;\n" From 60c1eef6592f7ed49ccc2412923cc7ea9f04c67d Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 23 Jun 2022 20:26:47 +0200 Subject: [PATCH 02/36] Fix FP containerOutOfBounds with std::map (#4230) --- lib/checkstl.cpp | 2 +- test/teststl.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 9b3a5effe..fdf0bf5f6 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -128,7 +128,7 @@ void CheckStl::outOfBounds() for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { const Library::Container *container = getLibraryContainer(tok); - if (!container) + if (!container || container->stdAssociativeLike) continue; const Token * parent = astParentSkipParens(tok); const Token* accessTok = parent; diff --git a/test/teststl.cpp b/test/teststl.cpp index b4d686229..0caa695a0 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -854,6 +854,12 @@ private: " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + checkNormal("std::string f() {\n" + " std::map m = { { 1, \"1\" } };\n" + " return m.at(1);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void outOfBoundsSymbolic() From 11860cce4bd0ac3002c753f6188f1a5cd9fe99cb Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 24 Jun 2022 13:55:04 +0200 Subject: [PATCH 03/36] Add test for #10463 (#4231) * Add test for #10643 * Comment * Fox comment --- test/testuninitvar.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 426ad475e..88eb40372 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -6189,6 +6189,15 @@ private: "[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->c\n", "[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->b\n", errout.str()); + + valueFlowUninit("struct S { int* p; };\n" // #10463 + "void f(S* in) {\n" + " S* s;\n" + " memcpy(in, s, sizeof(S));\n" + " s->p = NULL;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", + errout.str()); } void uninitvar_memberfunction() { From 63e567eb508ff4e59170b844de94b5893b121c0c Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 25 Jun 2022 08:38:17 +0200 Subject: [PATCH 04/36] Remove simplifyKnownVariables() (#4232) * Remove simplifyKnownVariables() * Add some test cases back * Remove unused functions * Format --- lib/mathlib.cpp | 17 - lib/mathlib.h | 1 - lib/tokenize.cpp | 828 ------------------------------------ lib/tokenize.h | 21 - test/testbufferoverrun.cpp | 20 + test/testleakautovar.cpp | 18 + test/testmathlib.cpp | 22 - test/testother.cpp | 12 + test/testsimplifytokens.cpp | 575 ------------------------- 9 files changed, 50 insertions(+), 1464 deletions(-) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index 9784e03fa..bf6c5fb83 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -1037,23 +1037,6 @@ std::string MathLib::subtract(const std::string &first, const std::string &secon #endif } -std::string MathLib::incdec(const std::string & var, const std::string & op) -{ -#ifdef TEST_MATHLIB_VALUE - if (op == "++") - return value(var).add(1).str(); - else if (op == "--") - return value(var).add(-1).str(); -#else - if (op == "++") - return MathLib::add(var, "1"); - else if (op == "--") - return MathLib::subtract(var, "1"); -#endif - - throw InternalError(nullptr, std::string("Unexpected operation '") + op + "' in MathLib::incdec(). Please report this to Cppcheck developers."); -} - std::string MathLib::divide(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE diff --git a/lib/mathlib.h b/lib/mathlib.h index 394c2ebb2..74c45c5a3 100644 --- a/lib/mathlib.h +++ b/lib/mathlib.h @@ -106,7 +106,6 @@ public: static std::string multiply(const std::string & first, const std::string & second); static std::string divide(const std::string & first, const std::string & second); static std::string mod(const std::string & first, const std::string & second); - static std::string incdec(const std::string & var, const std::string & op); static std::string calculate(const std::string & first, const std::string & second, char action); static std::string sin(const std::string & tok); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index da6acbc47..fcfdd740e 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5178,8 +5178,6 @@ bool Tokenizer::simplifyTokenList2() return false; modified = false; - modified |= simplifyKnownVariables(); - modified |= simplifyConstTernaryOp(); modified |= simplifyCalculations(); validate(); @@ -7038,767 +7036,6 @@ Token * Tokenizer::initVar(Token * tok) return tok; } - -bool Tokenizer::simplifyKnownVariables() -{ - // return value for function. Set to true if any simplifications are made - bool ret = false; - - // constants.. - { - std::unordered_map constantValues; - std::map constantVars; - std::unordered_map> constantValueUsages; - for (Token *tok = list.front(); tok; tok = tok->next()) { - // Reference to variable - if (Token::Match(tok, "%type%|* & %name% = %name% ;")) { - Token *start = tok->previous(); - while (Token::Match(start,"%type%|*|&")) - start = start->previous(); - if (!Token::Match(start,"[;{}]")) - continue; - const Token *reftok = tok->tokAt(2); - const Token *vartok = reftok->tokAt(2); - int level = 0; - for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) { - if (tok2->str() == "{") { - ++level; - } else if (tok2->str() == "}") { - if (level <= 0) - break; - --level; - } else if (tok2->varId() == reftok->varId()) { - tok2->str(vartok->str()); - tok2->varId(vartok->varId()); - } - } - Token::eraseTokens(start, tok->tokAt(6)); - tok = start; - } - - if (tok->isName() && (Token::Match(tok, "static| const| static| %type% const| %name% = %any% ;") || - Token::Match(tok, "static| const| static| %type% const| %name% ( %any% ) ;"))) { - bool isconst = false; - for (const Token *tok2 = tok; (tok2->str() != "=") && (tok2->str() != "("); tok2 = tok2->next()) { - if (tok2->str() == "const") { - isconst = true; - break; - } - } - if (!isconst) - continue; - - Token *tok1 = tok; - - // start of statement - if (tok != list.front() && !Token::Match(tok->previous(),";|{|}|private:|protected:|public:")) - continue; - // skip "const" and "static" - while (Token::Match(tok, "const|static")) - tok = tok->next(); - // pod type - if (!tok->isStandardType()) - continue; - - Token * const vartok = (tok->next() && tok->next()->str() == "const") ? tok->tokAt(2) : tok->next(); - const Token * const valuetok = vartok->tokAt(2); - if (Token::Match(valuetok, "%bool%|%char%|%num%|%str% )| ;")) { - // record a constant value for this variable - constantValues[vartok->varId()] = valuetok->str(); - constantVars[vartok->varId()] = tok1; - } - } else if (tok->varId()) { - // find the entry for the known variable, if any. Exclude the location where the variable is assigned with next == "=" - if (constantValues.find(tok->varId()) != constantValues.end() && tok->next()->str() != "=") { - constantValueUsages[tok->varId()].push_back(tok); - } - } - } - - for (auto constantVar = constantVars.rbegin(); constantVar != constantVars.rend(); constantVar++) { - bool referenceFound = false; - std::list usageList = constantValueUsages[constantVar->first]; - for (Token* usage : usageList) { - // check if any usages of each known variable are a reference - if (Token::Match(usage->tokAt(-2), "(|[|,|{|return|%op% & %varid%", constantVar->first)) { - referenceFound = true; - break; - } - } - - if (!referenceFound) { - // replace all usages of non-referenced known variables with their value - for (Token* usage : usageList) { - usage->str(constantValues[constantVar->first]); - } - - Token* startTok = constantVar->second; - // remove variable assignment statement - while (startTok->next()->str() != ";") - startTok->deleteNext(); - startTok->deleteNext(); - - // #8579 if we can we want another token to delete startTok. if we can't it doesn't matter - if (startTok->previous()) { - startTok->previous()->deleteNext(); - } else if (startTok->next()) { - startTok->next()->deletePrevious(); - } else { - startTok->deleteThis(); - } - startTok = nullptr; - - constantVar->second = nullptr; - ret = true; - } - } - } - - // variable id for local, float/double, array variables - std::set localvars; - std::set floatvars; - std::set arrays; - - // auto variables.. - for (Token *tok = list.front(); tok; tok = tok->next()) { - // Search for a block of code - Token * const start = const_cast(startOfExecutableScope(tok)); - if (!start) - continue; - - for (const Token *tok2 = start->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) { - if (tok2->varId() != 0) - localvars.insert(tok2->varId()); - } - - tok = start; - // parse the block of code.. - int indentlevel = 0; - Token *tok2 = tok; - for (; tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "[;{}] %type% %name%|*")) { - bool isfloat = false; - bool ispointer = false; - const Token *vartok = tok2->next(); - while (Token::Match(vartok, "%name%|* %name%|*")) { - if (Token::Match(vartok, "float|double")) - isfloat = true; - if (vartok->str() == "*") - ispointer = true; - vartok = vartok->next(); - } - if (Token::Match(vartok, "%var% ;|[")) - localvars.insert(vartok->varId()); - if (isfloat && !ispointer && Token::Match(vartok, "%var% ;")) - floatvars.insert(vartok->varId()); - if (Token::Match(vartok, "%var% [")) - arrays.insert(vartok->varId()); - } - - if (tok2->str() == "{") - ++indentlevel; - - else if (tok2->str() == "}") { - --indentlevel; - if (indentlevel <= 0) - break; - } - - else if (Token::simpleMatch(tok2, "for (")) - tok2 = tok2->next()->link(); - - else if (tok2->previous()->str() != "*" && !Token::Match(tok2->tokAt(-2), "* --|++") && - (Token::Match(tok2, "%name% = %bool%|%char%|%num%|%str%|%name% ;") || - Token::Match(tok2, "%name% [ %num%| ] = %str% ;") || - Token::Match(tok2, "%name% = & %name% ;") || - (Token::Match(tok2, "%name% = & %name% [ 0 ] ;") && arrays.find(tok2->tokAt(3)->varId()) != arrays.end()))) { - const int varid = tok2->varId(); - if (varid == 0) - continue; - - if (Token::Match(tok2->previous(), "[;{}]") && localvars.find(varid) == localvars.end()) - continue; - - // initialization of static variable => the value is not *known* - { - bool isstatic = false; - const Token *decl = tok2->previous(); - while (decl && (decl->isName() || decl->str() == "*")) { - if (decl->str() == "static") { - isstatic = true; - break; - } - decl = decl->previous(); - } - if (isstatic) - continue; - } - - // skip loop variable - if (Token::Match(tok2->tokAt(-2), "(|:: %type%")) { - const Token *tok3 = tok2->previous(); - do { - tok3 = tok3->tokAt(-2); - } while (Token::Match(tok3->previous(), ":: %type%")); - if (Token::Match(tok3->tokAt(-2), "for ( %type%")) - continue; - } - - // struct name.. - if (Token::Match(tok2, "%varid% = &| %varid%", tok2->varId())) - continue; - - const std::string structname = Token::Match(tok2->tokAt(-3), "[;{}] %name% .") ? - std::string(tok2->strAt(-2) + " .") : - std::string(); - - const Token * const valueToken = tok2->tokAt(2); - - std::string value; - nonneg int valueVarId = 0; - - Token *tok3 = nullptr; - bool valueIsPointer = false; - - // there could be a hang here if tok2 is moved back by the function calls below for some reason - if (Settings::terminated()) - return false; - - if (!simplifyKnownVariablesGetData(varid, &tok2, &tok3, value, valueVarId, valueIsPointer, floatvars.find(tok2->varId()) != floatvars.end())) - continue; - - if (valueVarId > 0 && arrays.find(valueVarId) != arrays.end()) - continue; - - ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, valueToken, indentlevel); - } - - else if (Token::Match(tok2, "strcpy|sprintf ( %name% , %str% ) ;")) { - const int varid(tok2->tokAt(2)->varId()); - if (varid == 0) - continue; - - const Token * const valueToken = tok2->tokAt(4); - std::string value(valueToken->str()); - if (tok2->str() == "sprintf") { - std::string::size_type n = 0; - while ((n = value.find("%%", n)) != std::string::npos) { - // Replace "%%" with "%" - erase the first '%' and continue past the second '%' - value.erase(n, 1); - ++n; - } - } - const int valueVarId(0); - const bool valueIsPointer(false); - Token *tok3 = tok2->tokAt(6); - ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, emptyString, value, valueVarId, valueIsPointer, valueToken, indentlevel); - - // there could be a hang here if tok2 was moved back by the function call above for some reason - if (Settings::terminated()) - return false; - } - } - - if (tok2) - tok = tok2->previous(); - } - - return ret; -} - -bool Tokenizer::simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar) -{ - Token *tok2 = *_tok2; - Token *tok3 = nullptr; - - if (Token::simpleMatch(tok2->tokAt(-2), "for (")) { - // only specific for loops is handled - if (!Token::Match(tok2, "%varid% = %num% ; %varid% <|<= %num% ; ++| %varid% ++| ) {", varid)) - return false; - - // is there a "break" in the for loop? - bool hasbreak = false; - const Token* end4 = tok2->linkAt(-1)->linkAt(1); - for (const Token *tok4 = tok2->previous()->link(); tok4 != end4; tok4 = tok4->next()) { - if (tok4->str() == "break") { - hasbreak = true; - break; - } - } - if (hasbreak) - return false; - - // no break => the value of the counter value is known after the for loop.. - const Token* compareTok = tok2->tokAt(5); - if (compareTok->str() == "<") { - value = compareTok->next()->str(); - valueVarId = compareTok->next()->varId(); - } else - value = MathLib::toString(MathLib::toLongNumber(compareTok->next()->str()) + 1); - - // Skip for-body.. - tok3 = tok2->previous()->link()->next()->link()->next(); - } else { - value = tok2->strAt(2); - valueVarId = tok2->tokAt(2)->varId(); - if (tok2->strAt(1) == "[") { - value = tok2->next()->link()->strAt(2); - valueVarId = 0; - } else if (value == "&") { - value = tok2->strAt(3); - valueVarId = tok2->tokAt(3)->varId(); - - // *ptr = &var; *ptr = 5; - // equals - // var = 5; not *var = 5; - if (tok2->strAt(4) == ";") - valueIsPointer = true; - } - - // Add a '.0' to a decimal value and therefore convert it to an floating point number. - else if (MathLib::isDec(tok2->strAt(2)) && floatvar) { - value += ".0"; - } - - // float variable: convert true/false to 1.0 / 0.0 - else if (tok2->tokAt(2)->isBoolean() && floatvar) { - value = (value == "true") ? "1.0" : "0.0"; - } - - if (Token::simpleMatch(tok2->next(), "= &")) - tok2 = tok2->tokAt(3); - - tok3 = tok2->next(); - } - *_tok2 = tok2; - *_tok3 = tok3; - return true; -} - -bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const -{ - const bool pointeralias(valueToken->isName() || Token::Match(valueToken, "& %name% [")); - const bool varIsGlobal = (indentlevel == 0); - const bool printDebug = mSettings->debugwarnings; - - if (mErrorLogger && !list.getFiles().empty()) - mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (simplifyKnownVariables)", tok3->progressValue()); - - if (isMaxTime()) - return false; - - bool ret = false; - - Token* bailOutFromLoop = nullptr; - int indentlevel3 = indentlevel; - bool ret3 = false; - for (; tok3; tok3 = tok3->next()) { - if (tok3->str() == "{") { - ++indentlevel3; - } else if (tok3->str() == "}") { - --indentlevel3; - if (indentlevel3 < indentlevel) { - if (Token::Match((*tok2)->tokAt(-7), "%type% * %name% ; %name% = & %name% ;") && - (*tok2)->strAt(-5) == (*tok2)->strAt(-3)) { - (*tok2) = (*tok2)->tokAt(-4); - Token::eraseTokens((*tok2), (*tok2)->tokAt(6)); - } - break; - } - } - - // Stop if there is a pointer alias and a shadow variable is - // declared in an inner scope (#3058) - if (valueIsPointer && tok3->varId() > 0 && - tok3->previous() && (tok3->previous()->isName() || tok3->previous()->str() == "*") && - valueToken->str() == "&" && - valueToken->next() && - valueToken->next()->isName() && - tok3->str() == valueToken->next()->str() && - tok3->varId() > valueToken->next()->varId()) { - // more checking if this is a variable declaration - bool decl = true; - for (const Token *tok4 = tok3->previous(); tok4; tok4 = tok4->previous()) { - if (Token::Match(tok4, "[;{}]")) - break; - - else if (tok4->isName()) { - if (tok4->varId() > 0) { - decl = false; - break; - } - } - - else if (!Token::Match(tok4, "[&*]")) { - decl = false; - break; - } - } - if (decl) - break; - } - - // Stop if label is found - if (Token::Match(tok3, "; %type% : ;")) - break; - - // Stop if break/continue is found .. - if (Token::Match(tok3, "break|continue")) - break; - if ((indentlevel3 > 1 || !Token::simpleMatch(Token::findsimplematch(tok3,";"), "; }")) && tok3->str() == "return") - ret3 = true; - if (ret3 && tok3->str() == ";") - break; - - if (pointeralias && Token::Match(tok3, ("!!= " + value).c_str())) - break; - - // Stop if a loop is found - if (pointeralias && Token::Match(tok3, "do|for|while")) - break; - - // Stop if unknown function call is seen and the variable is global: it might be - // changed by the function call - if (varIsGlobal && tok3->str() == ")" && tok3->link() && - Token::Match(tok3->link()->tokAt(-2), "[;{}] %name% (") && - !Token::Match(tok3->link()->previous(), "if|for|while|switch|BOOST_FOREACH")) - break; - - // Stop if something like 'while (--var)' is found - if (Token::Match(tok3, "for|while|do")) { - const Token *endpar = tok3->next()->link(); - if (Token::simpleMatch(endpar, ") {")) - endpar = endpar->next()->link(); - bool bailout = false; - for (const Token *tok4 = tok3; tok4 && tok4 != endpar; tok4 = tok4->next()) { - if (Token::Match(tok4, "++|-- %varid%", varid) || - Token::Match(tok4, "%varid% ++|--|=", varid)) { - bailout = true; - break; - } - } - if (bailout) - break; - } - - if (bailOutFromLoop) { - // This could be a loop, skip it, but only if it doesn't contain - // the variable we are checking for. If it contains the variable - // we will bail out. - if (tok3->varId() == varid) { - // Continue - //tok2 = bailOutFromLoop; - break; - } else if (tok3 == bailOutFromLoop) { - // We have skipped the loop - bailOutFromLoop = nullptr; - continue; - } - - continue; - } else if (tok3->str() == "{" && tok3->previous()->str() == ")") { - // There is a possible loop after the assignment. Try to skip it. - if (tok3->previous()->link() && - tok3->previous()->link()->strAt(-1) != "if") - bailOutFromLoop = tok3->link(); - continue; - } - - // Variable used in realloc (see Ticket #1649) - if (Token::Match(tok3, "%name% = realloc ( %name% ,") && - tok3->varId() == varid && - tok3->tokAt(4)->varId() == varid) { - tok3->tokAt(4)->str(value); - ret = true; - } - - // condition "(|&&|%OROR% %varid% )|&&|%OROR%|; - if (!Token::Match(tok3->previous(), "( %name% )") && - Token::Match(tok3->previous(), "&&|(|%oror% %varid% &&|%oror%|)|;", varid)) { - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - - // parameter in function call.. - if (tok3->varId() == varid && Token::Match(tok3->previous(), "[(,] %name% [,)]")) { - // If the parameter is passed by value then simplify it - if (isFunctionParameterPassedByValue(tok3)) { - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - } - - // Variable is used somehow in a non-defined pattern => bail out - if (tok3->varId() == varid) { - // This is a really generic bailout so let's try to avoid this. - // There might be lots of false negatives. - if (printDebug) { - // FIXME: Fix all the debug warnings for values and then - // remove this bailout - if (pointeralias) - break; - - // suppress debug-warning when calling member function - if (Token::Match(tok3->next(), ". %name% (")) - break; - - // suppress debug-warning when assignment - if (tok3->strAt(1) == "=") - break; - - // taking address of variable.. - if (Token::Match(tok3->tokAt(-2), "return|= & %name% ;")) - break; - - // parameter in function call.. - if (Token::Match(tok3->tokAt(-2), "%name% ( %name% ,|)") || - Token::Match(tok3->previous(), ", %name% ,|)")) - break; - - // conditional increment - if (Token::Match(tok3->tokAt(-3), ") { ++|--") || - Token::Match(tok3->tokAt(-2), ") { %name% ++|--")) - break; - - reportError(tok3, Severity::debug, "debug", - "simplifyKnownVariables: bailing out (variable="+tok3->str()+", value="+value+")"); - } - - break; - } - - // Using the variable in condition.. - if (Token::Match(tok3->previous(), ("if ( " + structname + " %varid% %cop%|)").c_str(), varid) || - Token::Match(tok3, ("( " + structname + " %varid% %comp%").c_str(), varid) || - Token::Match(tok3, ("%comp%|!|= " + structname + " %varid% %cop%|)|;").c_str(), varid) || - Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid)) { - if (value[0] == '\"' && tok3->previous()->str() != "strlen") { - // bail out if value is a string unless if it's just given - // as parameter to strlen - break; - } - if (!structname.empty()) { - tok3->deleteNext(2); - } - if (Token::Match(valueToken, "& %name% ;")) { - tok3->insertToken("&"); - tok3 = tok3->next(); - } - tok3 = tok3->next(); - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - - // pointer alias used in condition.. - if (Token::Match(valueToken,"& %name% ;") && Token::Match(tok3, ("( * " + structname + " %varid% %cop%").c_str(), varid)) { - tok3->deleteNext(); - if (!structname.empty()) - tok3->deleteNext(2); - tok3 = tok3->next(); - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - - // Delete pointer alias - if (isCPP() && pointeralias && (tok3->str() == "delete") && tok3->next() && - (Token::Match(tok3->next(), "%varid% ;", varid) || - Token::Match(tok3->next(), "[ ] %varid%", varid))) { - tok3 = (tok3->next()->str() == "[") ? tok3->tokAt(3) : tok3->next(); - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - - // Variable is used in function call.. - if (Token::Match(tok3, ("%name% ( " + structname + " %varid% ,").c_str(), varid)) { - static const char * const functionName[] = { - // always simplify - "strcmp", "strdup", - // don't simplify buffer value - "memcmp","memcpy","memmove","memset","strcpy","strncmp","strncpy" - }; - for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { - if (valueVarId == 0U && i >= 2) - break; - if (tok3->str() == functionName[i]) { - Token *par1 = tok3->tokAt(2); - if (!structname.empty()) { - par1->deleteNext(); - par1->deleteThis(); - } - par1->str(value); - par1->varId(valueVarId); - break; - } - } - } - - // Variable is used as 2nd parameter in function call.. - if (Token::Match(tok3, ("%name% ( %any% , " + structname + " %varid% ,|)").c_str(), varid)) { - static const char * const functionName[] = { - // always simplify - "strcmp","strcpy","strncmp","strncpy", - // don't simplify buffer value - "memcmp","memcpy","memmove" - }; - for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { - if (valueVarId == 0U && i >= 4) - break; - if (tok3->str() == functionName[i]) { - Token *par = tok3->tokAt(4); - if (!structname.empty()) { - par->deleteNext(); - par->deleteThis(); - } - par->str(value); - par->varId(valueVarId); - break; - } - } - } - - // array usage - if (value[0] != '\"' && Token::Match(tok3, ("[(,] " + structname + " %varid% [|%cop%").c_str(), varid)) { - if (!structname.empty()) { - tok3->deleteNext(2); - } - tok3 = tok3->next(); - tok3->str(value); - tok3->varId(valueVarId); - ret = true; - } - - // The >> operator is sometimes used to assign a variable in C++ - if (isCPP() && Token::Match(tok3, (">> " + structname + " %varid%").c_str(), varid)) { - // bailout for such code: ; std :: cin >> i ; - const Token *prev = tok3->previous(); - while (prev && prev->str() != "return" && Token::Match(prev, "%name%|::|*")) - prev = prev->previous(); - if (Token::Match(prev, ";|{|}|>>")) - break; - } - - // Variable is used in calculation.. - if (((tok3->previous()->varId() > 0) && Token::Match(tok3, ("& " + structname + " %varid%").c_str(), varid)) || - (Token::Match(tok3, ("[=+-*/%^|[] " + structname + " %varid% [=?+-*/%^|;])]").c_str(), varid) && !Token::Match(tok3, ("= " + structname + " %name% =").c_str())) || - Token::Match(tok3, ("[(=+-*/%^|[] " + structname + " %varid% <<|>>").c_str(), varid) || - Token::Match(tok3, ("<<|>> " + structname + " %varid% %cop%|;|]|)").c_str(), varid) || - Token::Match(tok3->previous(), ("[=+-*/%^|[] ( " + structname + " %varid% !!=").c_str(), varid)) { - if (value[0] == '\"') - break; - if (!structname.empty()) { - tok3->deleteNext(2); - ret = true; - } - tok3 = tok3->next(); - if (tok3->str() != value) - ret = true; - tok3->str(value); - tok3->varId(valueVarId); - if (tok3->previous()->str() == "*" && (valueIsPointer || Token::Match(valueToken, "& %name% ;"))) { - tok3 = tok3->previous(); - tok3->deleteThis(); - ret = true; - } else if (Token::Match(valueToken, "& %name% ;")) - tok3->insertToken("&", emptyString, true); - } - - if (Token::simpleMatch(tok3, "= {")) { - const Token* const end4 = tok3->linkAt(1); - for (const Token *tok4 = tok3; tok4 != end4; tok4 = tok4->next()) { - if (Token::Match(tok4, "{|, %varid% ,|}", varid)) { - tok4->next()->str(value); - tok4->next()->varId(valueVarId); - ret = true; - } - } - } - - // Using the variable in for-condition.. - if (Token::simpleMatch(tok3, "for (")) { - for (Token *tok4 = tok3->tokAt(2); tok4; tok4 = tok4->next()) { - if (Token::Match(tok4, "(|)")) - break; - - // Replace variable used in condition.. - if (Token::Match(tok4, "; %name% <|<=|!= %name% ; ++| %name% ++| )")) { - const Token *inctok = tok4->tokAt(5); - if (inctok->str() == "++") - inctok = inctok->next(); - if (inctok->varId() == varid) - break; - - if (tok4->next()->varId() == varid) { - tok4->next()->str(value); - tok4->next()->varId(valueVarId); - ret = true; - } - if (tok4->tokAt(3)->varId() == varid) { - tok4->tokAt(3)->str(value); - tok4->tokAt(3)->varId(valueVarId); - ret = true; - } - } - } - } - - if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "%varid% ++|--", varid) && MathLib::isInt(value)) { - const std::string op(tok3->strAt(2)); - if (Token::Match(tok3, "[{};] %any% %any% ;")) { - tok3->deleteNext(3); - } else { - tok3 = tok3->next(); - tok3->str(value); - tok3->varId(valueVarId); - tok3->deleteNext(); - } - value = MathLib::incdec(value, op); - if (!Token::simpleMatch((*tok2)->tokAt(-2), "for (")) { - (*tok2)->tokAt(2)->str(value); - (*tok2)->tokAt(2)->varId(valueVarId); - } - ret = true; - } - - if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "++|-- %varid%", varid) && MathLib::isInt(value) && - !Token::Match(tok3->tokAt(3), "[.[]")) { - value = MathLib::incdec(value, tok3->next()->str()); - (*tok2)->tokAt(2)->str(value); - (*tok2)->tokAt(2)->varId(valueVarId); - if (Token::Match(tok3, "[;{}] %any% %any% ;")) { - tok3->deleteNext(3); - } else { - tok3->deleteNext(); - tok3->next()->str(value); - tok3->next()->varId(valueVarId); - } - tok3 = tok3->next(); - ret = true; - } - - // return variable.. - if (Token::Match(tok3, "return %varid% %any%", varid) && - valueToken->str() != "&" && - (tok3->tokAt(2)->isExtendedOp() || tok3->strAt(2) == ";") && - value[0] != '\"') { - tok3->next()->str(value); - tok3->next()->varId(valueVarId); - } - - else if (pointeralias && Token::Match(tok3, "return * %varid% ;", varid) && value[0] != '\"') { - tok3->deleteNext(); - tok3->next()->str(value); - tok3->next()->varId(valueVarId); - } - } - return ret; -} - - void Tokenizer::elseif() { for (Token *tok = list.front(); tok; tok = tok->next()) { @@ -8162,71 +7399,6 @@ bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const //--------------------------------------------------------------------------- -bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const -{ - // TODO: If symbol database is available, use it. - const Token *ftok; - - // Look at function call, what parameter number is it? - int parameter = 1; - for (ftok = fpar->previous(); ftok; ftok = ftok->previous()) { - if (ftok->str() == "(") - break; - else if (ftok->str() == ")") - ftok = ftok->link(); - else if (ftok->str() == ",") - ++parameter; - else if (Token::Match(ftok, "[;{}]")) - break; - } - - // Is this a function call? - if (ftok && Token::Match(ftok->tokAt(-2), "[;{}=] %name% (")) { - const std::string& functionName(ftok->previous()->str()); - - if (functionName == "return") - return true; - - // Locate function declaration.. - for (const Token *tok = tokens(); tok; tok = tok->next()) { - if (tok->str() == "{") - tok = tok->link(); - else if (Token::Match(tok, "%type% (") && tok->str() == functionName) { - // Goto parameter - tok = tok->tokAt(2); - int par = 1; - while (tok && par < parameter) { - if (tok->str() == ")") - break; - if (tok->str() == ",") - ++par; - tok = tok->next(); - } - if (!tok) - return false; - - // If parameter was found, determine if it's passed by value - if (par == parameter) { - bool knowntype = false; - while (tok && tok->isName()) { - knowntype |= tok->isStandardType(); - knowntype |= (tok->str() == "struct"); - tok = tok->next(); - } - if (!tok || !knowntype) - return false; - if (tok->str() != "," && tok->str() != ")") - return false; - return true; - } - } - } - } - return false; -} - -//--------------------------------------------------------------------------- - void Tokenizer::eraseDeadCode(Token *begin, const Token *end) { if (!begin) diff --git a/lib/tokenize.h b/lib/tokenize.h index 8bcc07b30..c5c1c3ce6 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -329,27 +329,6 @@ public: */ bool simplifyUsing(); - /** - * A simplify function that replaces a variable with its value in cases - * when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)" - * - * @return true if modifications to token-list are done. - * false if no modifications are done. - */ - bool simplifyKnownVariables(); - - /** - * Utility function for simplifyKnownVariables. Get data about an - * assigned variable. - */ - static bool simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar); - - /** - * utility function for simplifyKnownVariables. Perform simplification - * of a given variable - */ - bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const; - /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ void simplifyEmptyNamespaces(); diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 109944fe3..6d34d4a12 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -191,6 +191,7 @@ private: TEST_CASE(array_index_64); // #10878 TEST_CASE(array_index_65); // #11066 TEST_CASE(array_index_66); // #10740 + TEST_CASE(array_index_67); // #1596 TEST_CASE(array_index_multidim); TEST_CASE(array_index_switch_in_for); TEST_CASE(array_index_for_in_for); // FP: #2634 @@ -1852,6 +1853,25 @@ private: errout.str()); } + void array_index_67() { + check("void func(int i) {\n" // #1596 + " int types[3];\n" + " int type_cnt = 0;\n" + " if (i == 0) {\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " } else {\n" + " types[type_cnt] = 1;\n" + " type_cnt++;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void array_index_multidim() { check("void f()\n" "{\n" diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 4262b59d3..cd46b070b 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -93,6 +93,7 @@ private: TEST_CASE(assign21); // #10186 TEST_CASE(assign22); // #9139 TEST_CASE(assign23); + TEST_CASE(assign24); // #7440 TEST_CASE(isAutoDealloc); @@ -504,6 +505,23 @@ private: settings = s; } + void assign24() { // #7440 + check("void f() {\n" + " char* data = new char[100];\n" + " char** dataPtr = &data;\n" + " delete[] *dataPtr;\n" + "}\n", true); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " char* data = new char[100];\n" + " char** dataPtr = &data;\n" + " printf(\"test\");\n" + " delete[] *dataPtr;\n" + "}\n", true); + ASSERT_EQUALS("", errout.str()); + } + void isAutoDealloc() { check("void f() {\n" " char *p = new char[100];" diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 3167e4a5f..07d0fd622 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -57,7 +57,6 @@ private: TEST_CASE(toDoubleNumber); TEST_CASE(naninf); TEST_CASE(isNullValue); - TEST_CASE(incdec); TEST_CASE(sin); TEST_CASE(cos); TEST_CASE(tan); @@ -1192,27 +1191,6 @@ private: ASSERT_EQUALS(false, MathLib::isNullValue("-ENOMEM")); } - void incdec() const { - // increment - { - const MathLib::biguint num = ~10U; - const std::string op = "++"; - const std::string strNum = MathLib::incdec(MathLib::toString(num), op); - const MathLib::biguint incrementedNum = MathLib::toULongNumber(strNum); - ASSERT_EQUALS(num + 1U, incrementedNum); - } - // decrement - { - const MathLib::biguint num = ~10U; - const std::string op = "--"; - const std::string strNum = MathLib::incdec(MathLib::toString(num), op); - const MathLib::biguint decrementedNum = MathLib::toULongNumber(strNum); - ASSERT_EQUALS(num - 1U, decrementedNum); - } - // invalid operation - ASSERT_THROW(MathLib::incdec("1", "x"), InternalError); // throw - } - void sin() const { ASSERT_EQUALS("0.0", MathLib::sin("0")); } diff --git a/test/testother.cpp b/test/testother.cpp index 6446bdf56..406371886 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -64,6 +64,7 @@ private: TEST_CASE(zeroDiv11); TEST_CASE(zeroDiv12); TEST_CASE(zeroDiv13); + TEST_CASE(zeroDiv14); // #1169 TEST_CASE(zeroDivCond); // division by zero / useless condition @@ -587,6 +588,17 @@ private: ASSERT_EQUALS("[test.cpp:4]: (error) Division by zero.\n", errout.str()); } + void zeroDiv14() { + check("void f() {\n" // #1169 + " double dx = 1.;\n" + " int ix = 1;\n" + " int i = 1;\n" + " std::cout << ix / (i >> 1) << std::endl;\n" + " std::cout << dx / (i >> 1) << std::endl;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Division by zero.\n", errout.str()); + } + void zeroDivCond() { check("void f(unsigned int x) {\n" " int y = 17 / x;\n" diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 6a73b8bab..38f53c57c 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -101,9 +101,7 @@ private: TEST_CASE(simplifyArrayAccessSyntax); TEST_CASE(pointeralias1); - TEST_CASE(pointeralias2); TEST_CASE(pointeralias3); - TEST_CASE(pointeralias4); // struct ABC { } abc; => struct ABC { }; ABC abc; TEST_CASE(simplifyStructDecl1); @@ -137,58 +135,36 @@ private: TEST_CASE(simplifyNamespaceAliases1); TEST_CASE(simplifyNamespaceAliases2); // ticket #10281 - TEST_CASE(simplifyKnownVariables1); TEST_CASE(simplifyKnownVariables2); TEST_CASE(simplifyKnownVariables3); TEST_CASE(simplifyKnownVariables4); TEST_CASE(simplifyKnownVariables5); - TEST_CASE(simplifyKnownVariables6); - TEST_CASE(simplifyKnownVariables7); - TEST_CASE(simplifyKnownVariables8); - TEST_CASE(simplifyKnownVariables9); - TEST_CASE(simplifyKnownVariables10); - TEST_CASE(simplifyKnownVariables11); TEST_CASE(simplifyKnownVariables13); TEST_CASE(simplifyKnownVariables14); - TEST_CASE(simplifyKnownVariables15); TEST_CASE(simplifyKnownVariables16); TEST_CASE(simplifyKnownVariables17); TEST_CASE(simplifyKnownVariables18); TEST_CASE(simplifyKnownVariables19); - TEST_CASE(simplifyKnownVariables20); TEST_CASE(simplifyKnownVariables21); - TEST_CASE(simplifyKnownVariables22); - TEST_CASE(simplifyKnownVariables23); TEST_CASE(simplifyKnownVariables25); - TEST_CASE(simplifyKnownVariables27); - TEST_CASE(simplifyKnownVariables28); // FIXME Does expression id handle these? TEST_CASE(simplifyKnownVariables29); // ticket #1811 TEST_CASE(simplifyKnownVariables30); - TEST_CASE(simplifyKnownVariables31); - TEST_CASE(simplifyKnownVariables32); // const - TEST_CASE(simplifyKnownVariables33); // struct variable TEST_CASE(simplifyKnownVariables34); TEST_CASE(simplifyKnownVariables36); // ticket #2304 - known value for strcpy parameter - TEST_CASE(simplifyKnownVariables39); - TEST_CASE(simplifyKnownVariables41); // p=&x; if (p) .. TEST_CASE(simplifyKnownVariables42); // ticket #2031 - known string value after strcpy TEST_CASE(simplifyKnownVariables43); TEST_CASE(simplifyKnownVariables44); // ticket #3117 - don't simplify static variables - TEST_CASE(simplifyKnownVariables45); // ticket #3281 - static constant variable not simplified TEST_CASE(simplifyKnownVariables46); // ticket #3587 - >> TEST_CASE(simplifyKnownVariables47); // ticket #3627 - >> TEST_CASE(simplifyKnownVariables48); // ticket #3754 - wrong simplification in for loop header TEST_CASE(simplifyKnownVariables49); // #3691 - continue in switch TEST_CASE(simplifyKnownVariables50); // #4066 sprintf changes TEST_CASE(simplifyKnownVariables51); // #4409 hang - TEST_CASE(simplifyKnownVariables53); // references TEST_CASE(simplifyKnownVariables54); // #4913 'x' is not 0 after *--x=0; - TEST_CASE(simplifyKnownVariables55); // pointer alias TEST_CASE(simplifyKnownVariables56); // ticket #5301 - >> TEST_CASE(simplifyKnownVariables57); // ticket #4724 TEST_CASE(simplifyKnownVariables58); // ticket #5268 TEST_CASE(simplifyKnownVariables59); // skip for header - TEST_CASE(simplifyKnownVariables60); // #6829 TEST_CASE(simplifyKnownVariables61); // #7805 TEST_CASE(simplifyKnownVariables62); // #5666 - p=&str[0] TEST_CASE(simplifyKnownVariables63); // #10798 @@ -203,11 +179,8 @@ private: TEST_CASE(simplifyKnownVariablesClassMember); // #2815 - value of class member may be changed by function call TEST_CASE(simplifyKnownVariablesFunctionCalls); // Function calls (don't assume pass by reference) TEST_CASE(simplifyKnownVariablesGlobalVars); - TEST_CASE(simplifyKnownVariablesPointerAliasFunctionCall); // #7440 TEST_CASE(simplifyKnownVariablesNamespace); // #10059 - TEST_CASE(simplify_constants2); - TEST_CASE(simplify_constants4); TEST_CASE(simplify_constants6); // Ticket #5625: Ternary operator as template parameter TEST_CASE(simplifyVarDeclInitLists); } @@ -2607,16 +2580,6 @@ private: } } - void pointeralias2() { - const char code[] = "void f()\n" - "{\n" - " int i;\n" - " int *p = &i;\n" - " return *p;\n" - "}\n"; - ASSERT_EQUALS("void f ( ) { int i ; int * p ; return i ; }", tok(code)); - } - void pointeralias3() { const char code[] = "void f()\n" "{\n" @@ -2635,18 +2598,6 @@ private: ASSERT_EQUALS(expected, tok(code)); } - void pointeralias4() { - const char code[] = "int f()\n" - "{\n" - " int i;\n" - " int *p = &i;\n" - " *p = 5;\n" - " return i;\n" - "}\n"; - const char expected[] = "int f ( ) { int i ; int * p ; i = 5 ; return 5 ; }"; - ASSERT_EQUALS(expected, tok(code)); - } - void simplifyStructDecl1() { { const char code[] = "struct ABC { } abc;"; @@ -3228,37 +3179,9 @@ private: std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - (tokenizer.simplifyKnownVariables)(); - return tokenizer.tokens()->stringifyList(nullptr, false); } - void simplifyKnownVariables1() { - { - const char code[] = "void f()\n" - "{\n" - " int a = 10;\n" - " if (a);\n" - "}\n"; - - ASSERT_EQUALS( - "void f ( ) { int a ; a = 10 ; if ( 10 ) { ; } }", - simplifyKnownVariables(code)); - } - - { - const char code[] = "void f()\n" - "{\n" - " int a = 10;\n" - " if (!a);\n" - "}\n"; - - ASSERT_EQUALS( - "void f ( ) { int a ; a = 10 ; if ( ! 10 ) { ; } }", - simplifyKnownVariables(code)); - } - } - void simplifyKnownVariables2() { const char code[] = "void f()\n" "{\n" @@ -3313,136 +3236,6 @@ private: simplifyKnownVariables(code)); } - void simplifyKnownVariables6() { - const char code[] = "void f()\n" - "{\n" - " char str[2];" - " int a = 4;\n" - " str[a] = 0;\n" - "}\n"; - - ASSERT_EQUALS( - "void f ( ) { char str [ 2 ] ; int a ; a = 4 ; str [ 4 ] = 0 ; }", - simplifyKnownVariables(code)); - } - - void simplifyKnownVariables7() { - const char code[] = "void foo()\n" - "{\n" - " int i = 22;\n" - " abc[i++] = 1;\n" - " abc[++i] = 2;\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int i ; i = 24 ; abc [ 22 ] = 1 ; abc [ 24 ] = 2 ; }", - simplifyKnownVariables(code)); - } - - void simplifyKnownVariables8() { - const char code[] = "void foo()\n" - "{\n" - " int i = 22;\n" - " i++;\n" - " abc[i] = 0;\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int i ; i = 23 ; abc [ 23 ] = 0 ; }", - simplifyKnownVariables(code)); - } - - void simplifyKnownVariables9() { - const char code[] = "void foo()\n" - "{\n" - " int a = 1, b = 2;\n" - " if (a < b)\n" - " ;\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int a ; a = 1 ; int b ; b = 2 ; if ( 1 < 2 ) { ; } }", - simplifyKnownVariables(code)); - } - - void simplifyKnownVariables10() { - { - const char code[] = "void f()\n" - "{\n" - " bool b=false;\n" - "\n" - " {\n" - " b = true;\n" - " }\n" - "\n" - " if( b )\n" - " {\n" - " a();\n" - " }\n" - "}\n"; - - const std::string expected1("void f ( ) {" - " bool b ; b = false ;" - " { b = true ; }"); - - TODO_ASSERT_EQUALS( - expected1 + " if ( true ) { a ( ) ; } }", - expected1 + " if ( b ) { a ( ) ; } }", - simplifyKnownVariables(code)); - - } - - { - const char code[] = "void f()\n" - "{\n" - " bool b=false;\n" - " { b = false; }\n" - " {\n" - " b = true;\n" - " }\n" - "\n" - " if( b )\n" - " {\n" - " a();\n" - " }\n" - "}\n"; - - TODO_ASSERT_EQUALS( - "void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( true ) { a ( ) ; } }", - "void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( b ) { a ( ) ; } }", - simplifyKnownVariables(code)); - } - - { - const char code[] = "void f()\n" - "{\n" - " int b=0;\n" - " b = 1;\n" - " for( int i = 0; i < 10; i++ )" - " {\n" - " }\n" - "\n" - " return b;\n" - "}\n"; - - ASSERT_EQUALS( - "void f ( ) { int b ; b = 0 ; b = 1 ; for ( int i = 0 ; i < 10 ; i ++ ) { } return 1 ; }", - simplifyKnownVariables(code)); - } - } - - void simplifyKnownVariables11() { - const char code[] = "const int foo = 0;\n" - "int main()\n" - "{\n" - " int foo=0;\n" - "}\n"; - - ASSERT_EQUALS( - "int main ( ) { int foo ; foo = 0 ; }", - simplifyKnownVariables(code)); - } - void simplifyKnownVariables13() { const char code[] = "void f()\n" "{\n" @@ -3461,32 +3254,6 @@ private: ASSERT_EQUALS(code, simplifyKnownVariables(code)); } - void simplifyKnownVariables15() { - { - const char code[] = "int main()\n" - "{\n" - " int x=5;\n" - " std::cout << 10 / x << std::endl;\n" - "}\n"; - - ASSERT_EQUALS( - "int main ( ) { int x ; x = 5 ; std :: cout << 10 / 5 << std :: endl ; }", - simplifyKnownVariables(code)); - } - - { - const char code[] = "int main()\n" - "{\n" - " int x=5;\n" - " std::cout << x / ( x == 1 ) << std::endl;\n" - "}\n"; - - ASSERT_EQUALS( - "int main ( ) { int x ; x = 5 ; std :: cout << 5 / ( 5 == 1 ) << std :: endl ; }", - simplifyKnownVariables(code)); - } - } - void simplifyKnownVariables16() { // ticket #807 - segmentation fault when macro isn't found const char code[] = "void f ( ) { int n = 1; DISPATCH(while); }"; @@ -3515,111 +3282,12 @@ private: simplifyKnownVariables(code)); } - void simplifyKnownVariables20() { - const char code[] = "void f()\n" - "{\n" - " int i = 0;\n" - " if (x) {\n" - " if (i) i=0;\n" - " }\n" - "}\n"; - - ASSERT_EQUALS( - "void f ( ) { int i ; i = 0 ; if ( x ) { if ( 0 ) { i = 0 ; } } }", - simplifyKnownVariables(code)); - } - void simplifyKnownVariables21() { - const char code[] = "void foo() { int n = 10; for (int i = 0; i < n; ++i) { } }"; - - ASSERT_EQUALS( - "void foo ( ) { int n ; n = 10 ; for ( int i = 0 ; i < 10 ; ++ i ) { } }", - simplifyKnownVariables(code)); - ASSERT_EQUALS( "void foo ( int i ) { int n ; n = i ; for ( i = 0 ; i < n ; ++ i ) { } }", simplifyKnownVariables("void foo(int i) { int n = i; for (i = 0; i < n; ++i) { } }")); } - void simplifyKnownVariables22() { - // This testcase is related to ticket #1169 - { - const char code[] = "void foo()\n" - "{\n" - " int n = 10;\n" - " i = (n >> 1);\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int n ; n = 10 ; i = 10 >> 1 ; }", - simplifyKnownVariables(code)); - } - { - const char code[] = "void foo()\n" - "{\n" - " int n = 10;\n" - " i = (n << 1);\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int n ; n = 10 ; i = 10 << 1 ; }", - simplifyKnownVariables(code)); - } - { - const char code[] = "void foo()\n" - "{\n" - " int n = 10;\n" - " i = (1 << n);\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int n ; n = 10 ; i = 1 << 10 ; }", - simplifyKnownVariables(code)); - } - { - const char code[] = "void foo()\n" - "{\n" - " int n = 10;\n" - " i = (1 >> n);\n" - "}\n"; - - ASSERT_EQUALS( - "void foo ( ) { int n ; n = 10 ; i = 1 >> 10 ; }", - simplifyKnownVariables(code)); - } - } - - void simplifyKnownVariables23() { - // This testcase is related to ticket #1596 - const char code[] = "void foo(int x)\n" - "{\n" - " int a[10], c = 0;\n" - " if (x) {\n" - " a[c] = 0;\n" - " c++;\n" - " } else {\n" - " a[c] = 0;\n" - " }\n" - "}\n"; - - TODO_ASSERT_EQUALS( - "void foo ( int x ) " - "{" - " int a [ 10 ] ; int c ; c = 0 ;" - " if ( x ) { a [ 0 ] = 0 ; c = 1 ; }" - " else { a [ 0 ] = 0 ; } " - "}", - - "void foo ( int x ) " - "{" - " int a [ 10 ] ; int c ; c = 0 ;" - " if ( x ) { a [ 0 ] = 0 ; c ++ ; }" - " else { a [ c ] = 0 ; } " - "}", - - simplifyKnownVariables(code)); - } - void simplifyKnownVariables25() { { // This testcase is related to ticket #1646 @@ -3672,43 +3340,6 @@ private: } } - void simplifyKnownVariables27() { - // This testcase is related to ticket #1633 - const char code[] = "void foo()\n" - "{\n" - " int i1 = 1;\n" - " int i2 = 2;\n" - " int i3 = (i1 + i2) * 3;\n" - "}\n"; - ASSERT_EQUALS( - "void foo ( ) " - "{" - " int i1 ; i1 = 1 ;" - " int i2 ; i2 = 2 ;" - " int i3 ; i3 = ( 1 + 2 ) * 3 ; " - "}", - simplifyKnownVariables(code)); - } - - void simplifyKnownVariables28() { - const char code[] = "void foo(int g)\n" - "{\n" - " int i = 2;\n" - " if (g) {\n" - " }\n" - " if (i > 0) {\n" - " }\n" - "}\n"; - ASSERT_EQUALS( - "void foo ( int g ) " - "{" - " int i ; i = 2 ;" - " if ( g ) { }" - " if ( 2 > 0 ) { } " - "}", - simplifyKnownVariables(code)); - } - void simplifyKnownVariables29() { // ticket #1811 { const char code[] = "int foo(int u, int v)\n" @@ -4049,48 +3680,6 @@ private: ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } - void simplifyKnownVariables31() { - const char code[] = "void foo(const char str[]) {\n" - " const char *p = str;\n" - " if (p[0] == 0) {\n" - " }\n" - "}\n"; - const char expected[] = "void foo ( const char str [ ] ) {\n" - "const char * p ; p = str ;\n" - "if ( str [ 0 ] == 0 ) {\n" - "}\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - - void simplifyKnownVariables32() { - { - const char code[] = "void foo() {\n" - " const int x = 0;\n" - " bar(0,x);\n" - "}\n"; - const char expected[] = "void foo ( ) {\n\nbar ( 0 , 0 ) ;\n}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - - { - const char code[] = "static int const SZ = 22; char str[SZ];\n"; - ASSERT_EQUALS("char str [ 22 ] ;", tokenizeAndStringify(code,true)); - } - } - - void simplifyKnownVariables33() { - const char code[] = "static void foo(struct Foo *foo) {\n" - " foo->a = 23;\n" - " x[foo->a] = 0;\n" - "}\n"; - const char expected[] = "static void foo ( struct Foo * foo ) {\n" - "foo . a = 23 ;\n" - "x [ 23 ] = 0 ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - void simplifyKnownVariables34() { const char code[] = "void f() {\n" " int x = 10;\n" @@ -4106,13 +3695,6 @@ private: } void simplifyKnownVariables36() { - // Ticket #2304 - const char code[] = "void f() {" - " const char *q = \"hello\";" - " strcpy(p, q);" - "}"; - const char expected[] = "void f ( ) { const char * q ; q = \"hello\" ; strcpy ( p , \"hello\" ) ; }"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); // Ticket #5972 const char code2[] = "void f() {" @@ -4123,50 +3705,7 @@ private: ASSERT_EQUALS(expected2, tokenizeAndStringify(code2, true)); } - void simplifyKnownVariables39() { - // Ticket #2296 - simplify pointer alias 'delete p;' - { - const char code[] = "void f() {\n" - " int *x;\n" - " int *y = x;\n" - " delete y;\n" - "}"; - ASSERT_EQUALS("void f ( ) {\nint * x ;\nint * y ; y = x ;\ndelete x ;\n}", tokenizeAndStringify(code, true)); - } - { - const char code[] = "void f() {\n" - " int *x;\n" - " int *y = x;\n" - " delete [] y;\n" - "}"; - ASSERT_EQUALS("void f ( ) {\nint * x ;\nint * y ; y = x ;\ndelete [ ] x ;\n}", tokenizeAndStringify(code, true)); - } - } - - void simplifyKnownVariables41() { - const char code[] = "void f() {\n" - " int x = 0;\n" - " const int *p; p = &x;\n" - " if (p) { return 0; }\n" - "}"; - ASSERT_EQUALS("void f ( ) {\nint x ; x = 0 ;\nconst int * p ; p = & x ;\nif ( & x ) { return 0 ; }\n}", tokenizeAndStringify(code, true)); - } - void simplifyKnownVariables42() { - { - const char code[] = "void f() {\n" - " char str1[10], str2[10];\n" - " strcpy(str1, \"abc\");\n" - " strcpy(str2, str1);\n" - "}"; - const char expected[] = "void f ( ) {\n" - "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" - "strcpy ( str1 , \"abc\" ) ;\n" - "strcpy ( str2 , \"abc\" ) ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - { const char code[] = "void f() {\n" " char a[10];\n" @@ -4261,20 +3800,6 @@ private: ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } - void simplifyKnownVariables45() { - const char code[] = "class Fred {\n" - "private:\n" - " const static int NUM = 2;\n" - " int array[NUM];\n" - "}"; - const char expected[] = "class Fred {\n" - "private:\n" - "\n" - "int array [ 2 ] ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - void simplifyKnownVariables46() { const char code[] = "void f() {\n" " int x = 0;\n" @@ -4335,45 +3860,6 @@ private: } void simplifyKnownVariables50() { // #4066 - { - const char code[] = "void f() {\n" - " char str1[10], str2[10];\n" - " sprintf(str1, \"%%\");\n" - " strcpy(str2, str1);\n" - "}"; - const char expected[] = "void f ( ) {\n" - "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" - "sprintf ( str1 , \"%%\" ) ;\n" - "strcpy ( str2 , \"%\" ) ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - { - const char code[] = "void f() {\n" - " char str1[25], str2[25];\n" - " sprintf(str1, \"abcdef%%%% and %% and %\");\n" - " strcpy(str2, str1);\n" - "}"; - const char expected[] = "void f ( ) {\n" - "char str1 [ 25 ] ; char str2 [ 25 ] ;\n" - "sprintf ( str1 , \"abcdef%%%% and %% and %\" ) ;\n" - "strcpy ( str2 , \"abcdef%% and % and %\" ) ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } - { - const char code[] = "void f() {\n" - " char str1[10], str2[10];\n" - " sprintf(str1, \"abc\");\n" - " strcpy(str2, str1);\n" - "}"; - const char expected[] = "void f ( ) {\n" - "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" - "sprintf ( str1 , \"abc\" ) ;\n" - "strcpy ( str2 , \"abc\" ) ;\n" - "}"; - ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); - } { //don't simplify '&x'! const char code[] = "const char * foo ( ) {\n" @@ -4425,21 +3911,10 @@ private: ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); } - void simplifyKnownVariables53() { // references - ASSERT_EQUALS("void f ( ) { int x ; x = abc ( ) ; }", tokenizeAndStringify("void f() { int x; int &ref=x; ref=abc(); }", true)); - ASSERT_EQUALS("void f ( ) { int * p ; p = abc ( ) ; }", tokenizeAndStringify("void f() { int *p; int *&ref=p; ref=abc(); }", true)); - } - void simplifyKnownVariables54() { // #4913 ASSERT_EQUALS("void f ( int * p ) { * -- p = 0 ; * p = 0 ; }", tokenizeAndStringify("void f(int*p) { *--p=0; *p=0; }", true)); } - void simplifyKnownVariables55() { // pointer alias - ASSERT_EQUALS("void f ( ) { int a ; int * p ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (*p>0) {} }", true)); - ASSERT_EQUALS("void f ( ) { int a ; struct AB ab ; ab . a = & a ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; struct AB ab; ab.a = &a; if (*ab.a>0) {} }", true)); - ASSERT_EQUALS("void f ( ) { int a ; int * p ; if ( x > a ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (x>*p) {} }", true)); - } - void simplifyKnownVariables56() { // ticket #5301 - >> ASSERT_EQUALS("void f ( ) { int a ; a = 0 ; int b ; b = 0 ; * p >> a >> b ; return a / b ; }", tokenizeAndStringify("void f() { int a=0,b=0; *p>>a>>b; return a/b; }", true)); @@ -4485,21 +3960,6 @@ private: "}", tokenizeAndStringify(code, true)); } - void simplifyKnownVariables60() { // #6829 - const char code[] = "void f() {\n" - " int i = 1;\n" - " const int * const constPtrToConst = &i;\n" - " std::cout << *constPtrToConst << std::endl;\n" - " std::cout << constPtrToConst << std::endl;\n" - "}"; - ASSERT_EQUALS("void f ( ) {\n" - "int i ; i = 1 ;\n" - "const int * const constPtrToConst ; constPtrToConst = & i ;\n" - "std :: cout << i << std :: endl ;\n" - "std :: cout << & i << std :: endl ;\n" - "}", tokenizeAndStringify(code, true)); - } - void simplifyKnownVariables61() { // #7805 tokenizeAndStringify("static const int XX = 0;\n" "enum E { XX };\n" @@ -4679,22 +4139,6 @@ private: ASSERT_EQUALS("static int x ; void f ( ) { x = 123 ; while ( ! x ) { dostuff ( ) ; } }", tokenizeAndStringify(code,true)); } - void simplifyKnownVariablesPointerAliasFunctionCall() { // #7440 - const char code[] = "int main() {\n" - " char* data = new char[100];\n" - " char** dataPtr = &data;\n" - " printf(\"test\");\n" - " delete [] *dataPtr;\n" - "}"; - const char exp[] = "int main ( ) {\n" - "char * data ; data = new char [ 100 ] ;\n" - "char * * dataPtr ; dataPtr = & data ;\n" - "printf ( \"test\" ) ;\n" - "delete [ ] data ;\n" - "}"; - ASSERT_EQUALS(exp, tokenizeAndStringify(code, /*simplify=*/ true)); - } - void simplifyKnownVariablesNamespace() { { // #10059 const char code[] = "namespace N {\n" @@ -4785,25 +4229,6 @@ private: } } - - void simplify_constants2() { - const char code[] = - "void f( Foo &foo, Foo *foo2 ) {\n" - "const int a = 45;\n" - "foo.a=a+a;\n" - "foo2->a=a;\n" - "}"; - ASSERT_EQUALS("void f ( Foo & foo , Foo * foo2 ) { foo . a = 90 ; foo2 . a = 45 ; }", tok(code)); - } - - void simplify_constants4() { - const char code[] = "static const int bSize = 4;\n" - "static const int aSize = 50;\n" - "x = bSize;\n" - "y = aSize;\n"; - ASSERT_EQUALS("x = 4 ; y = 50 ;", tok(code)); - } - void simplify_constants6() { // Ticket #5625 { const char code[] = "template < class T > struct foo ;\n" From d204c5f89477a9a89ee75475cb90cd2650150052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 25 Jun 2022 17:09:51 +0200 Subject: [PATCH 05/36] Tokenizer: Cleanup stuff in simplifyTokenList2 --- lib/tokenize.cpp | 293 ----------------------------------------------- lib/tokenize.h | 22 ---- 2 files changed, 315 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index fcfdd740e..3cdb03998 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5187,33 +5187,6 @@ bool Tokenizer::simplifyTokenList2() removeRedundantSemicolons(); - simplifyFlowControl(); - - simplifyRedundantConsecutiveBraces(); - - simplifyEmptyNamespaces(); - - validate(); - - Token::assignProgressValues(list.front()); - - list.front()->assignIndexes(); - - list.createAst(); - // needed for #7208 (garbage code) and #7724 (ast max depth limit) - list.validateAst(); - - // Create symbol database and then remove const keywords - createSymbolDatabase(); - mSymbolDatabase->setValueTypeInTokenList(true); - - ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); - - if (Settings::terminated()) - return false; - - printDebugOutput(2); - return true; } //--------------------------------------------------------------------------- @@ -5766,82 +5739,6 @@ void Tokenizer::simplifyEmptyNamespaces() } } -void Tokenizer::simplifyFlowControl() -{ - for (Token *begin = list.front(); begin; begin = begin->next()) { - - if (Token::Match(begin, "(|[") || - (begin->str() == "{" && begin->previous() && begin->strAt(-1) == "=")) - begin = begin->link(); - - //function scope - if (!Token::simpleMatch(begin, ") {") && !Token::Match(begin, ") %name% {")) - continue; - - Token* end = begin->linkAt(1+(begin->next()->str() == "{" ? 0 : 1)); - int indentLevel = 0; - bool stilldead = false; - - for (Token *tok = begin; tok && tok != end; tok = tok->next()) { - if (Token::Match(tok, "(|[")) { - tok = tok->link(); - continue; - } - - if (tok->str() == "{") { - if (tok->previous() && tok->previous()->str() == "=") { - tok = tok->link(); - continue; - } - ++indentLevel; - } else if (tok->str() == "}") { - if (indentLevel == 0) - break; - --indentLevel; - if (stilldead) { - eraseDeadCode(tok, nullptr); - if (indentLevel == 1 || tok->next()->str() != "}" || !Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) - stilldead = false; - continue; - } - } - - if (indentLevel == 0) - continue; - - if (Token::Match(tok,"continue|break ;")) { - tok = tok->next(); - eraseDeadCode(tok, nullptr); - - } else if (Token::Match(tok,"return|goto") || - (Token::Match(tok->previous(), "[;{}] %name% (") && - mSettings->library.isnoreturn(tok)) || - (isCPP() && tok->str() == "throw")) { - if (tok->next()->str() == "}") - syntaxError(tok->next()); // invalid code like in #6731 - //TODO: ensure that we exclude user-defined 'exit|abort|throw', except for 'noreturn' - //catch the first ';' - for (Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "(|[")) { - tok2 = tok2->link(); - } else if (tok2->str() == ";") { - tok = tok2; - eraseDeadCode(tok, nullptr); - break; - } else if (Token::Match(tok2, "[{}]")) - break; - } - //if everything is removed, then remove also the code after an inferior scope - //only if the actual scope is not special - if (indentLevel > 1 && tok->next()->str() == "}" && Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) - stilldead = true; - } - } - begin = end; - } -} - - void Tokenizer::removeRedundantSemicolons() { for (Token *tok = list.front(); tok; tok = tok->next()) { @@ -7399,196 +7296,6 @@ bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const //--------------------------------------------------------------------------- -void Tokenizer::eraseDeadCode(Token *begin, const Token *end) -{ - if (!begin) - return; - const bool isgoto = Token::Match(begin->tokAt(-2), "goto %name% ;"); - int indentlevel = 1; - int indentcase = 0; - int indentswitch = 0; - int indentlabel = 0; - int roundbraces = 0; - int indentcheck = 0; - std::vector switchindents; - bool checklabel = false; - Token *tok = begin; - Token *tokcheck = nullptr; - while (tok->next() && tok->next() != end) { - if (tok->next()->str() == "(") { - ++roundbraces; - tok->deleteNext(); - continue; - } else if (tok->next()->str() == ")") { - if (!roundbraces) - break; //too many ending round parentheses - --roundbraces; - tok->deleteNext(); - continue; - } - - if (roundbraces) { - tok->deleteNext(); - continue; - } - - if (Token::Match(tok, "[{};] switch (")) { - if (!checklabel) { - if (!indentlabel) { - //remove 'switch ( ... )' - Token::eraseTokens(tok, tok->linkAt(2)->next()); - } else { - tok = tok->linkAt(2); - } - if (tok->next()->str() == "{") { - ++indentswitch; - indentcase = indentlevel + 1; - switchindents.push_back(indentcase); - } - } else { - tok = tok->linkAt(2); - if (Token::simpleMatch(tok, ") {")) { - ++indentswitch; - indentcase = indentlevel + 1; - switchindents.push_back(indentcase); - } - } - } else if (tok->next()->str() == "{") { - ++indentlevel; - if (!checklabel) { - checklabel = true; - tokcheck = tok; - indentcheck = indentlevel; - indentlabel = 0; - } - tok = tok->next(); - } else if (tok->next()->str() == "}") { - --indentlevel; - if (!indentlevel) - break; - - if (!checklabel) { - tok->deleteNext(); - } else { - if (indentswitch && indentlevel == indentcase) - --indentlevel; - if (indentlevel < indentcheck) { - const Token *end2 = tok->next(); - tok = end2->link()->previous(); //return to initial '{' - if (indentswitch && Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->tokAt(-2), "[{};] switch (")) - tok = tok->link()->tokAt(-2); //remove also 'switch ( ... )' - Token::eraseTokens(tok, end2->next()); - checklabel = false; - tokcheck = nullptr; - indentcheck = 0; - } else { - tok = tok->next(); - } - } - if (indentswitch && indentlevel <= indentcase) { - --indentswitch; - switchindents.pop_back(); - if (!indentswitch) - indentcase = 0; - else - indentcase = switchindents[indentswitch-1]; - } - } else if (Token::Match(tok, "[{};:] case")) { - const Token *tok2 = Token::findsimplematch(tok->next(), ": ;", end); - if (!tok2) { - tok->deleteNext(); - continue; - } - if (indentlevel == 1) - break; //it seems like the function was called inside a case-default block. - if (indentlevel == indentcase) - ++indentlevel; - tok2 = tok2->next(); - if (!checklabel || !indentswitch) { - Token::eraseTokens(tok, tok2->next()); - } else { - tok = const_cast(tok2); - } - } else if (Token::Match(tok, "[{};] default : ;")) { - if (indentlevel == 1) - break; //it seems like the function was called inside a case-default block. - if (indentlevel == indentcase) - ++indentlevel; - if (!checklabel || !indentswitch) { - tok->deleteNext(3); - } else { - tok = tok->tokAt(3); - } - } else if (Token::Match(tok, "[{};] %name% : ;") && tok->next()->str() != "default") { - if (checklabel) { - indentlabel = indentlevel; - tok = tokcheck->next(); - checklabel = false; - indentlevel = indentcheck; - } else { - if (indentswitch) { - //Before stopping the function, since the 'switch()' - //instruction is removed, there's no sense to keep the - //case instructions. Remove them, if there are any. - Token *tok2 = tok->tokAt(3); - int indentlevel2 = indentlevel; - while (tok2->next() && tok2->next() != end) { - if (Token::Match(tok2->next(), "{|[|(")) { - tok2 = tok2->next()->link(); - } else if (Token::Match(tok2, "[{};:] case")) { - const Token *tok3 = Token::findsimplematch(tok2->next(), ": ;", end); - if (!tok3) { - tok2 = tok2->next(); - continue; - } - Token::eraseTokens(tok2, tok3->next()); - } else if (Token::Match(tok2, "[{};] default : ;")) { - tok2->deleteNext(3); - } else if (tok2->next()->str() == "}") { - --indentlevel2; - if (indentlevel2 <= indentcase) - break; - tok2 = tok2->next(); - } else { - tok2 = tok2->next(); - } - } - } - break; //stop removing tokens, we arrived to the label. - } - } else if (isgoto && Token::Match(tok, "[{};] do|while|for|BOOST_FOREACH")) { - //it's possible that code inside loop is not dead, - //because of the possible presence of the label pointed by 'goto' - const Token *start = tok->tokAt(2); - if (start && start->str() == "(") - start = start->link()->next(); - if (start && start->str() == "{") { - std::string labelpattern = "[{};] " + begin->previous()->str() + " : ;"; - bool simplify = true; - for (Token *tok2 = start->next(); tok2 != start->link(); tok2 = tok2->next()) { - if (Token::Match(tok2, labelpattern.c_str())) { - simplify = false; - break; - } - } - //bailout for now - if (!simplify) - break; - } - tok->deleteNext(); - } else { - // no need to keep the other strings, remove them. - if (tok->strAt(1) == "while") { - if (tok->str() == "}" && tok->link()->strAt(-1) == "do") - tok->link()->previous()->deleteThis(); - } - tok->deleteNext(); - } - } -} - -//--------------------------------------------------------------------------- - void Tokenizer::syntaxError(const Token *tok, const std::string &code) const { printDebugOutput(0); diff --git a/lib/tokenize.h b/lib/tokenize.h index c5c1c3ce6..a919170b2 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -159,23 +159,6 @@ public: void splitTemplateRightAngleBrackets(bool check); - /** - * Deletes dead code between 'begin' and 'end'. - * In general not everything can be erased, such as: - * - code after labels; - * - code outside the scope where the function is called; - * - code after a change of scope caused by 'switch(...);' - * instructions, like 'case %any%;' or 'default;' - * Also, if the dead code contains a 'switch' block - * and inside it there's a label, the function removes all - * the 'switch(..)' tokens and every occurrence of 'case %any%; | default;' - * expression, such as the 'switch' block is reduced to a simple block. - * - * @param begin Tokens after this have a possibility to be erased. - * @param end Tokens before this have a possibility to be erased. - */ - static void eraseDeadCode(Token *begin, const Token *end); - /** * Calculates sizeof value for given type. * @param type Token which will contain e.g. "int", "*", or string. @@ -332,11 +315,6 @@ public: /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ void simplifyEmptyNamespaces(); - /** Simplify redundant code placed after control flow statements : - * 'return', 'throw', 'goto', 'break' and 'continue' - */ - void simplifyFlowControl(); - /** Simplify "if else" */ void elseif(); From 242afc389d2e4dbbfb9f3f2974a84df92eb31ca2 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 25 Jun 2022 21:42:55 +0200 Subject: [PATCH 06/36] Fix #11148 Regression: Parameter \'ptr\' can be declared as pointer to const (#4233) --- lib/checkother.cpp | 4 +++- test/testother.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index fc3cc333b..b63e5820f 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1556,6 +1556,8 @@ void CheckOther::checkConstPointer() deref = true; else if (Token::simpleMatch(parent, "[") && parent->astOperand1() == tok && tok != nameTok) deref = true; + else if (Token::Match(parent, "%op%") && Token::simpleMatch(parent->astParent(), ".")) + deref = true; else if (astIsRangeBasedForDecl(tok)) continue; if (deref) { @@ -1577,7 +1579,7 @@ void CheckOther::checkConstPointer() } else if (Token::simpleMatch(gparent, "[") && gparent->astOperand2() == parent) continue; } else { - if (Token::Match(parent, "%oror%|%comp%|&&|?|!")) + if (Token::Match(parent, "%oror%|%comp%|&&|?|!|-")) continue; else if (Token::simpleMatch(parent, "(") && Token::Match(parent->astOperand1(), "if|while")) continue; diff --git a/test/testother.cpp b/test/testother.cpp index 406371886..5f81695cd 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -3250,6 +3250,13 @@ private: " **b = 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("ptrdiff_t f(int *p0, int *p1) {\n" // #11148 + " return p0 - p1;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p0' can be declared as pointer to const\n" + "[test.cpp:1]: (style) Parameter 'p1' can be declared as pointer to const\n", + errout.str()); } void switchRedundantAssignmentTest() { From 88bf11abba7054bf09e17b80a4f7c359974bc96f Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 27 Jun 2022 14:19:19 +0200 Subject: [PATCH 07/36] Fix #10857 FN: leakNoVarFunctionCall (#4236) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO --- lib/checkmemoryleak.cpp | 11 +++++++---- test/testmemleak.cpp | 8 +++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 9169b09e2..8d02e87e1 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1016,8 +1016,10 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) for (const Token* arg : args) { if (arg->isOp()) continue; - while (arg->astOperand1()) - arg = arg->astOperand1(); + if (!(mTokenizer->isCPP() && Token::simpleMatch(arg, "new"))) { + while (arg->astOperand1()) + arg = arg->astOperand1(); + } if (getAllocationType(arg, 0) == No) continue; if (isReopenStandardStream(arg)) @@ -1068,8 +1070,9 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link()))) continue; returnValueNotUsedError(tok, tok->str()); - } else if (Token::Match(parent, "%comp%|!")) { - returnValueNotUsedError(tok, tok->str()); + } else if (Token::Match(parent, "%comp%|!|,")) { + if (!(parent->astParent() && parent->str() == ",")) + returnValueNotUsedError(tok, tok->str()); } } } diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index a2a9064cd..6d845c4e9 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2401,6 +2401,12 @@ private: " return static_cast(malloc(size));\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("void f() { if (new int[42]) {} }\n" // #10857 + "void g() { if (malloc(42)) {} }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Allocation with new, if doesn't release it.\n" + "[test.cpp:2]: (error) Allocation with malloc, if doesn't release it.\n", + errout.str()); } void missingAssignment() { @@ -2452,7 +2458,7 @@ private: "{\n" " 42,malloc(42);\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void *f()\n" "{\n" From e7e23e87c2b4ee1855a643787e219fcb9532bdb0 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 27 Jun 2022 19:35:22 +0200 Subject: [PATCH 08/36] Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) (#4237) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) --- lib/checkmemoryleak.cpp | 2 +- test/testmemleak.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 8d02e87e1..b9eb70d62 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1070,7 +1070,7 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link()))) continue; returnValueNotUsedError(tok, tok->str()); - } else if (Token::Match(parent, "%comp%|!|,")) { + } else if (Token::Match(parent, "%comp%|!|,|%oror%|&&")) { if (!(parent->astParent() && parent->str() == ",")) returnValueNotUsedError(tok, tok->str()); } diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 6d845c4e9..717212db8 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2601,6 +2601,12 @@ private: " C{ new QWidget, 1 };\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("void f(bool b) { if (b && malloc(42)) {} }\n" // // #10858 + "void g(bool b) { if (b || malloc(42)) {} }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'malloc' is not stored.\n" + "[test.cpp:2]: (error) Return value of allocation function 'malloc' is not stored.\n", + errout.str()); } void smartPointerFunctionParam() { From cdeebc15ea9d76dadb44a7a9fc2d8a59ecfd657b Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 27 Jun 2022 20:55:09 +0200 Subject: [PATCH 09/36] Fix #11155 FN: leakNoVarFunctionCall (ternary operator) (#4239) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) * #11155 FN: leakNoVarFunctionCall (ternary operator) --- lib/checkmemoryleak.cpp | 11 ++++++++--- test/testmemleak.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index b9eb70d62..d074df2cb 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1070,9 +1070,14 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link()))) continue; returnValueNotUsedError(tok, tok->str()); - } else if (Token::Match(parent, "%comp%|!|,|%oror%|&&")) { - if (!(parent->astParent() && parent->str() == ",")) - returnValueNotUsedError(tok, tok->str()); + } else if (Token::Match(parent, "%comp%|!|,|%oror%|&&|:")) { + if (parent->astParent() && parent->str() == ",") + continue; + if (parent->str() == ":") { + if (!(Token::simpleMatch(parent->astParent(), "?") && !parent->astParent()->astParent())) + continue; + } + returnValueNotUsedError(tok, tok->str()); } } } diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 717212db8..659596e58 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2607,6 +2607,14 @@ private: ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'malloc' is not stored.\n" "[test.cpp:2]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); + + check("void f0(const bool b) { b ? new int : nullptr; }\n" // #11155 + "void f1(const bool b) { b ? nullptr : new int; }\n" + "int* g0(const bool b) { return b ? new int : nullptr; }\n" + "void g1(const bool b) { h(b, b ? nullptr : new int); }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n", + errout.str()); } void smartPointerFunctionParam() { From 2223cd24b99f8100ac405bce74aef0103ff314af Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 28 Jun 2022 14:02:02 +0200 Subject: [PATCH 10/36] Fix #11157 FN: leakNoVarFunctionCall (switch condition) (#4240) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) * #11155 FN: leakNoVarFunctionCall (ternary operator) * Fix #11157 FN: leakNoVarFunctionCall (switch condition) * Fix FN constStatement * Fix FP leakNoVarFunctionCall --- lib/checkmemoryleak.cpp | 11 ++++++----- lib/checkother.cpp | 2 +- test/testincompletestatement.cpp | 5 +++++ test/testmemleak.cpp | 13 +++++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index d074df2cb..e6d3af291 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -991,7 +991,7 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) // parse the executable scope until tok is reached... for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { // allocating memory in parameter for function call.. - if (!Token::Match(tok, "%name% (")) + if (tok->varId() || !Token::Match(tok, "%name% (")) continue; // check if the output of the function is assigned @@ -1014,11 +1014,12 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) const std::vector args = getArguments(tok); for (const Token* arg : args) { - if (arg->isOp()) + if (arg->isOp() && !(tok->isKeyword() && arg->str() == "*")) // e.g. switch (*new int) continue; - if (!(mTokenizer->isCPP() && Token::simpleMatch(arg, "new"))) { - while (arg->astOperand1()) - arg = arg->astOperand1(); + while (arg->astOperand1()) { + if (mTokenizer->isCPP() && Token::simpleMatch(arg, "new")) + break; + arg = arg->astOperand1(); } if (getAllocationType(arg, 0) == No) continue; diff --git a/lib/checkother.cpp b/lib/checkother.cpp index b63e5820f..232630992 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1722,7 +1722,7 @@ void CheckOther::charBitOpError(const Token *tok) static bool isType(const Token * tok, bool unknown) { - if (Token::Match(tok, "%type%")) + if (tok && (tok->isStandardType() || (!tok->isKeyword() && Token::Match(tok, "%type%")))) return true; if (Token::simpleMatch(tok, "::")) return isType(tok->astOperand2(), unknown); diff --git a/test/testincompletestatement.cpp b/test/testincompletestatement.cpp index 8485db656..755ae7983 100644 --- a/test/testincompletestatement.cpp +++ b/test/testincompletestatement.cpp @@ -662,6 +662,11 @@ private: " +\" y = \" + b;\n" "}\n", /*inconclusive*/ true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n", errout.str()); + + check("void f() {\n" + " *new int;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n", errout.str()); } void vardecl() { diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 659596e58..e45b8a9d0 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2615,6 +2615,19 @@ private: ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'new' is not stored.\n" "[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n", errout.str()); + + check("void f() {\n" // #11157 + " switch (*new int) { case 42: break; }\n" + " switch (*malloc(42)) { case 42: break; }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with new, switch doesn't release it.\n" + "[test.cpp:3]: (error) Allocation with malloc, switch doesn't release it.\n", + errout.str()); + + check("void f() {\n" + " Ref remove(new StringBuffer());\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void smartPointerFunctionParam() { From a2f269908806e1414a4532c0de716d3c3773b65c Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Tue, 28 Jun 2022 15:40:59 -0500 Subject: [PATCH 11/36] Refactor alias check in isExpressionChangeAt into isAlias function (#4223) --- lib/astutils.cpp | 47 ++++++++++++++++++++++++++++++----------------- lib/astutils.h | 2 ++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 1629d6239..dc1f6ac7a 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -865,6 +865,34 @@ bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive) return false; } +bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive) +{ + const bool pointer = astIsPointer(tok); + const ValueFlow::Value* value = nullptr; + const Token* r = findAstNode(expr, [&](const Token* childTok) { + for (const ValueFlow::Value& val : tok->values()) { + if (val.isImpossible()) + continue; + if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { + if (findAstNode(val.tokvalue, + [&](const Token* aliasTok) { + return aliasTok->exprId() == childTok->exprId(); + })) { + if (val.isInconclusive() && inconclusive) { // NOLINT + value = &val; + } else { + return true; + } + } + } + } + return false; + }); + if (!r && value && inconclusive) + *inconclusive = true; + return r || value; +} + static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid) { if (!precedes(startTok, endTok)) @@ -2452,27 +2480,12 @@ static bool isExpressionChangedAt(const F& getExprTok, if (globalvar && !tok->isKeyword() && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure())) // TODO: Is global variable really changed by function call? return true; - const bool pointer = astIsPointer(tok); bool aliased = false; // If we can't find the expression then assume it is an alias if (!getExprTok()) aliased = true; - if (!aliased) { - aliased = findAstNode(getExprTok(), [&](const Token* childTok) { - for (const ValueFlow::Value& val : tok->values()) { - if (val.isImpossible()) - continue; - if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { - if (findAstNode(val.tokvalue, - [&](const Token* aliasTok) { - return aliasTok->exprId() == childTok->exprId(); - })) - return true; - } - } - return false; - }); - } + if (!aliased) + aliased = isAliasOf(tok, getExprTok()); if (!aliased) return false; if (isVariableChanged(tok, 1, settings, cpp, depth)) diff --git a/lib/astutils.h b/lib/astutils.h index b82c922b9..c30242f3b 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -335,6 +335,8 @@ bool isExpressionChangedAt(const Token* expr, /// If token is an alias if another variable bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr); +bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive = nullptr); + bool isAliased(const Variable *var); const Token* getArgumentStart(const Token* ftok); From f8b796403b07be80d92e9bd31044dbc8ae3a8d5e Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 28 Jun 2022 22:43:34 +0200 Subject: [PATCH 12/36] Fix #11146 Crash in expandTemplate() (#4238) --- lib/templatesimplifier.cpp | 4 +++- test/testsimplifytemplate.cpp | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 2bb695933..107ad43c1 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -3791,8 +3791,10 @@ void TemplateSimplifier::simplifyTemplates( specializations, maxtime, expandedtemplates); - if (instantiated) + if (instantiated) { mInstantiatedTemplates.push_back(*iter1); + mTemplateNamePos.clear(); // positions might be invalid after instantiations + } } for (std::list::const_iterator it = mInstantiatedTemplates.begin(); it != mInstantiatedTemplates.end(); ++it) { diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 561ba9d56..be25ac053 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -218,6 +218,7 @@ private: TEST_CASE(template173); // #10332 crash TEST_CASE(template174); // #10506 hang TEST_CASE(template175); // #10908 + TEST_CASE(template176); // #11146 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) @@ -4458,7 +4459,7 @@ private: ASSERT_EQUALS(exp, tok(code)); } - void template175() + void template175() // #10908 { const char code[] = "template T Get() {return value;}\n" "char f() { Get(); }\n"; @@ -4468,6 +4469,27 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template176() // #11146 don't crash + { + const char code[] = "struct a {\n" + " template class b {};\n" + "};\n" + "struct c {\n" + " template a::b d();\n" + " ;\n" + "};\n" + "template a::b c::d() {}\n" + "template <> class a::b c::d() { return {}; };\n"; + const char exp[] = "struct a { " + "class b c :: d ( ) ; " + "template < typename > class b { } ; " + "} ; " + "struct c { a :: b d ( ) ; } ; " + "class a :: b c :: d ( ) { return { } ; } ; " + "a :: b c :: 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" From 185294499c25d76881da198b4ce24319a7089ea1 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Tue, 28 Jun 2022 15:52:35 -0500 Subject: [PATCH 13/36] Adjust the severity of the uninitvar (#4234) --- lib/checkuninitvar.cpp | 12 ++++++++---- test/testuninitvar.cpp | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 52d2ee51b..a0db23c49 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1518,6 +1518,8 @@ void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) { + if (!mSettings->isEnabled(&v)) + return; if (diag(tok)) return; const Token* ltok = tok; @@ -1526,13 +1528,15 @@ void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) const std::string& varname = ltok ? ltok->expressionString() : "x"; ErrorPath errorPath = v.errorPath; errorPath.emplace_back(tok, ""); + auto severity = v.isKnown() ? Severity::error : Severity::warning; + auto certainty = v.isInconclusive() ? Certainty::inconclusive : Certainty::normal; if (v.subexpressions.empty()) { reportError(errorPath, - Severity::error, + severity, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, - Certainty::normal); + certainty); return; } std::string vars = v.subexpressions.size() == 1 ? "variable: " : "variables: "; @@ -1542,11 +1546,11 @@ void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) prefix = ", "; } reportError(errorPath, - Severity::error, + severity, "uninitvar", "$symbol:" + varname + "\nUninitialized " + vars, CWE_USE_OF_UNINITIALIZED_VARIABLE, - Certainty::normal); + certainty); } void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 88eb40372..6a33395ae 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -3525,7 +3525,7 @@ private: " if (!x) i = 0;\n" " if (!x || i>0) {}\n" // <- error "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Uninitialized variable: i\n", errout.str()); valueFlowUninit("void f(int x) {\n" " int i;\n" @@ -3540,7 +3540,7 @@ private: " else i = 0;\n" " if (x || i>0) {}\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Uninitialized variable: i\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Uninitialized variable: i\n", errout.str()); valueFlowUninit("void f(int x) {\n" " int i;\n" @@ -3573,7 +3573,7 @@ private: " a = y;\n" " return y ? 2*a : 3*a;\n" "}"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Uninitialized variable: a\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Uninitialized variable: a\n", errout.str()); valueFlowUninit("void f() {\n" // Don't crash " int a;\n" @@ -4675,7 +4675,7 @@ private: " s.x = 42;\n" " bar(&s);\n" "}"); - ASSERT_EQUALS("[test.cpp:18] -> [test.cpp:12] -> [test.cpp:8]: (error) Uninitialized variable: s->flag\n", + ASSERT_EQUALS("[test.cpp:18] -> [test.cpp:12] -> [test.cpp:8]: (warning) Uninitialized variable: s->flag\n", errout.str()); // Ticket #2207 - False negative @@ -4820,7 +4820,7 @@ private: " p = new S(io);\n" " p->Write();\n" "}"); - ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:10]: (error) Uninitialized variable: p.rIo\n", errout.str()); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:10]: (warning) Uninitialized variable: p.rIo\n", errout.str()); // Unknown types { @@ -5140,7 +5140,7 @@ private: " }\n" " printf(\"\", value);\n" "}\n"); - ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:9]: (error) Uninitialized variable: value\n", errout.str()); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:9]: (warning) Uninitialized variable: value\n", errout.str()); valueFlowUninit("void f(int x)\n" "{\n" @@ -5242,7 +5242,7 @@ private: " else\n" " return -1;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Uninitialized variable: a\n", errout.str()); // #9772 valueFlowUninit("int func(void) {\n" @@ -5366,7 +5366,7 @@ private: " increment(n);\n" " return n;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Uninitialized variable: i\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (warning) Uninitialized variable: i\n", errout.str()); } void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value @@ -5418,7 +5418,7 @@ private: " someType_t gVar;\n" " bar(&gVar);\n" "}"); - ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5]: (error) Uninitialized variable: p->flags\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5]: (warning) Uninitialized variable: p->flags\n", errout.str()); valueFlowUninit("typedef struct\n" "{\n" @@ -5619,7 +5619,7 @@ private: " bool copied_all = true;\n" " g(&copied_all, 5, 6, &bytesCopied);\n" "}"); - ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (error) Uninitialized variable: *buflen\n", errout.str()); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (warning) Uninitialized variable: *buflen\n", errout.str()); // # 9953 valueFlowUninit("uint32_t f(uint8_t *mem) {\n" @@ -5657,7 +5657,7 @@ private: " ab.a = 0;\n" " do_something(ab);\n" "}"); - ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Uninitialized variable: ab.b\n", errout.str()); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (warning) Uninitialized variable: ab.b\n", errout.str()); valueFlowUninit("struct AB { int a; int b; };\n" "void f(void) {\n" @@ -6187,7 +6187,7 @@ private: "}\n"); TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->b\n" "[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->c\n", - "[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->b\n", + "[test.cpp:8] -> [test.cpp:3]: (warning) Uninitialized variable: abc->b\n", errout.str()); valueFlowUninit("struct S { int* p; };\n" // #10463 From b90a726441299becac5d62d5cf344c0e5a1fadf5 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 29 Jun 2022 07:22:26 +0200 Subject: [PATCH 14/36] Fix FP leakNoVarFunctionCall (#4241) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) * #11155 FN: leakNoVarFunctionCall (ternary operator) * Fix #11157 FN: leakNoVarFunctionCall (switch condition) * Fix FN constStatement * Fix FP leakNoVarFunctionCall * Fix FP leakNoVarFunctionCall * Format --- lib/checkmemoryleak.cpp | 4 +++- test/testmemleak.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index e6d3af291..cbb2a20ef 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -998,7 +998,9 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) const Token* tok2 = tok->next()->astParent(); while (tok2 && tok2->isCast()) tok2 = tok2->astParent(); - if (Token::Match(tok2, "%assign%|return")) + if (Token::Match(tok2, "%assign%")) + continue; + if (Token::simpleMatch(tok->astTop(), "return")) continue; const std::string& functionName = tok->str(); diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index e45b8a9d0..176482c7b 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2407,6 +2407,14 @@ private: ASSERT_EQUALS("[test.cpp:1]: (error) Allocation with new, if doesn't release it.\n" "[test.cpp:2]: (error) Allocation with malloc, if doesn't release it.\n", errout.str()); + + check("const char* string(const char* s) {\n" + " StringSet::iterator it = strings_.find(s);\n" + " if (it != strings_.end())\n" + " return *it;\n" + " return *strings_.insert(it, std::strcpy(new char[std::strlen(s) + 1], s));\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void missingAssignment() { From 06b408ea205d0b056d83855dd420b77314bfdae3 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:43:17 +0200 Subject: [PATCH 15/36] Fix FP leakNoVarFunctionCall with unknown function (#4242) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) * #11155 FN: leakNoVarFunctionCall (ternary operator) * Fix #11157 FN: leakNoVarFunctionCall (switch condition) * Fix FN constStatement * Fix FP leakNoVarFunctionCall * Fix FP leakNoVarFunctionCall * Format * Fix FP leakNoVarFunctionCall with unknown function --- lib/checkmemoryleak.cpp | 2 ++ test/testmemleak.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index cbb2a20ef..517d9fddc 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1011,6 +1011,8 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) functionName == "return") continue; + if (!tok->isKeyword() && mSettings->library.isNotLibraryFunction(tok)) + continue; if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP())) continue; diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 176482c7b..fbcba2795 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2415,6 +2415,18 @@ private: " return *strings_.insert(it, std::strcpy(new char[std::strlen(s) + 1], s));\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("struct S {\n" + " static void load(const QString & projPath) {\n" + " if (proj_)\n" + " return;\n" + " proj_ = new ProjectT(projPath);\n" + " proj_->open(new OpenCallback());\n" + " }\n" + "private:\n" + " static Core::ProjectBase* proj_;\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); } void missingAssignment() { From d8e64b4cbb97d2501b366ae5e0e87954f4573fa9 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:05:28 +0200 Subject: [PATCH 16/36] Fix FP leakNoVarFunctionCall (#4243) * Fix #10857 FN: leakNoVarFunctionCall * Fix TODO * Fix #10858 FN: leakNoVarFunctionCall (if ( b && malloc ) ) * #11155 FN: leakNoVarFunctionCall (ternary operator) * Fix #11157 FN: leakNoVarFunctionCall (switch condition) * Fix FN constStatement * Fix FP leakNoVarFunctionCall * Fix FP leakNoVarFunctionCall * Format * Fix FP leakNoVarFunctionCall with unknown function * Fix FP leakNoVarFunctionCall --- lib/checkmemoryleak.cpp | 2 ++ test/testmemleak.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 517d9fddc..6ce4fa872 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1011,6 +1011,8 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) functionName == "return") continue; + if (Token::simpleMatch(tok->next()->astParent(), "(")) // passed to another function + continue; if (!tok->isKeyword() && mSettings->library.isNotLibraryFunction(tok)) continue; if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP())) diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index fbcba2795..9873a1517 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2417,7 +2417,7 @@ private: ASSERT_EQUALS("", errout.str()); check("struct S {\n" - " static void load(const QString & projPath) {\n" + " static void load(const QString& projPath) {\n" " if (proj_)\n" " return;\n" " proj_ = new ProjectT(projPath);\n" @@ -2427,6 +2427,12 @@ private: " static Core::ProjectBase* proj_;\n" "};\n"); ASSERT_EQUALS("", errout.str()); + + check("void f(const std::string& s, int n) {\n" + " std::unique_ptr u;\n" + " u.reset(strcpy(new char[n], s.c_str()));\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); } void missingAssignment() { From 27578e9c4c1f90c62b6938867735a054082e178e Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:50:31 +0200 Subject: [PATCH 17/36] Fix FP returnStdMoveLocal (#4244) --- lib/checkfunctions.cpp | 2 +- test/testfunctions.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index 8fdf20d29..e831fb5d8 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -658,7 +658,7 @@ void CheckFunctions::returnLocalStdMove() if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile()) copyElisionError(retval); // RVO - if (Token::Match(retval, "(|{") && !retval->isCast()) + if (Token::Match(retval, "(|{") && !retval->isCast() && !(retval->valueType() && retval->valueType()->reference != Reference::None)) copyElisionError(retval); } } diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index eef345b5d..1503ae27f 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -1731,6 +1731,18 @@ private: check("struct A{} a; A f1() { return std::move(a); }\n" "A f2() { volatile A var; return std::move(var); }"); ASSERT_EQUALS("", errout.str()); + + check("struct S { std::string msg{ \"abc\" }; };\n" + "std::unique_ptr get(std::vector>& v) {\n" + " return std::move(v.front());\n" + "}\n" + "int main() {\n" + " std::vector> v;\n" + " v.emplace_back(std::make_unique());\n" + " auto p = get(v);\n" + " std::cout << p->msg;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void negativeMemoryAllocationSizeError() { // #389 From 596f75e2afb87bc7eefa44bec06068582a2d48d2 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:48:32 +0200 Subject: [PATCH 18/36] Fix FP compareValueOutOfTypeRangeError (#4245) * Fix FP compareValueOutOfTypeRangeError * Remove suppressions --- lib/checkcondition.cpp | 2 ++ test/cfg/googletest.cpp | 4 ++-- test/testcondition.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index cf082e1dc..63438d5fe 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -1803,6 +1803,8 @@ void CheckCondition::checkCompareValueOutOfTypeRange() continue; if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED) continue; + if (valueTok->valueType() && valueTok->valueType()->isTypeEqual(typeTok->valueType())) + continue; int bits = 0; switch (typeTok->valueType()->type) { case ValueType::Type::BOOL: diff --git a/test/cfg/googletest.cpp b/test/cfg/googletest.cpp index 26b671e98..37234a703 100644 --- a/test/cfg/googletest.cpp +++ b/test/cfg/googletest.cpp @@ -71,8 +71,8 @@ TEST(Test, warning_in_assert_macros) ASSERT_GE(i, i); unsigned int u = errno; - // cppcheck-suppress [unsignedPositive, compareValueOutOfTypeRangeError] + // cppcheck-suppress [unsignedPositive] ASSERT_GE(u, 0); - // cppcheck-suppress [unsignedLessThanZero, compareValueOutOfTypeRangeError] + // cppcheck-suppress [unsignedLessThanZero] ASSERT_LT(u, 0); } diff --git a/test/testcondition.cpp b/test/testcondition.cpp index b3176619d..5a2fb09a8 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -5258,6 +5258,12 @@ private: "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); + check("int f(int x) {\n" + " const int i = 0xFFFFFFFF;\n" + " if (x == i) {}\n" + "}", &settingsUnix64); + ASSERT_EQUALS("", errout.str()); + check("void f() {\n" " char c;\n" " if ((c = foo()) != -1) {}\n" From b170a9c61e18a52084b075f46d642f235f6bf78a Mon Sep 17 00:00:00 2001 From: orbitcowboy Date: Sat, 2 Jul 2022 09:24:22 +0200 Subject: [PATCH 19/36] wxwidgets.cfg: Fixed wrong configuration of wxRegEx::GetMatch(). --- cfg/wxwidgets.cfg | 4 ++-- test/cfg/wxwidgets.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cfg/wxwidgets.cfg b/cfg/wxwidgets.cfg index 67e0dda50..e561a6709 100644 --- a/cfg/wxwidgets.cfg +++ b/cfg/wxwidgets.cfg @@ -13679,8 +13679,8 @@ wxItemKind kind = wxITEM_NORMAL) --> - - + + diff --git a/test/cfg/wxwidgets.cpp b/test/cfg/wxwidgets.cpp index 229ce5384..7180950ea 100644 --- a/test/cfg/wxwidgets.cpp +++ b/test/cfg/wxwidgets.cpp @@ -22,12 +22,27 @@ #include #include #include +#include #include #include #include #include #include +void uninitvar_wxRegEx_GetMatch(wxRegEx &obj, size_t *start, size_t *len, size_t index) +{ + size_t s,l; + size_t *sPtr,*lPtr; + // cppcheck-suppress uninitvar + (void)obj.GetMatch(&s,lPtr); + // TODO cppcheck-suppress uninitvar + (void)obj.GetMatch(sPtr,&l); + (void)obj.GetMatch(&s,&l); + (void)obj.GetMatch(start,len); + (void)obj.GetMatch(start,len,0); + (void)obj.GetMatch(start,len,index); +} + #ifdef __VISUALC__ // Ensure no duplicateBreak warning is issued after wxLogApiError() calls. // This function does not terminate execution. From 22aeeb178860fffc57c3c91801fbb85da40f0dc4 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 2 Jul 2022 17:43:52 +0200 Subject: [PATCH 20/36] Fix ClangImport crash on Windows paths (#4246) * Fix crash on Windows paths * Fix test * Enable fix * Cut trailing ':' --- lib/clangimport.cpp | 18 +++++++++++------- test/testclangimport.cpp | 5 ++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 5be38e6f0..3cf4a99a5 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -497,17 +497,21 @@ void clangimport::AstNode::dumpAst(int num, int indent) const void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line, int col) { for (const std::string &ext: mExtTokens) { - if (ext.compare(0,5,"appendFileIfNew(ext.substr(1, sep1 - 1)); - line = MathLib::toLongNumber(ext.substr(sep1+1, sep2-sep1)); + } else if (ext[0] == '<') { + const std::string::size_type colon = ext.find(':'); + if (colon != std::string::npos) { + const bool windowsPath = colon == 2 && ext.size() > 4 && ext[3] == '\\'; + std::string::size_type sep1 = windowsPath ? ext.find(':', 4) : colon; + std::string::size_type sep2 = ext.find(':', sep1 + 1); + file = tokenList->appendFileIfNew(ext.substr(1, sep1 - 1)); + line = MathLib::toLongNumber(ext.substr(sep1 + 1, sep2 - sep1 - 1)); + } } } mFile = file; diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index ec6dc43c0..40b021014 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -584,7 +584,10 @@ private: } void cxxRecordDecl1() { - const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo"; + const char* clang = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo"; + ASSERT_EQUALS("class Foo ;", parse(clang)); + + clang = "`-CXXRecordDecl 0x34cc5f8 col:7 class Foo"; ASSERT_EQUALS("class Foo ;", parse(clang)); } From c897c0474bcac1c8f96395ac30a973fbec3813f2 Mon Sep 17 00:00:00 2001 From: Mathias Schmid <50880400+matzeschmid@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:23:20 +0200 Subject: [PATCH 21/36] Library Data: Add support for optional smart pointer element 'unique' (#4132) --- gui/cppchecklibrarydata.cpp | 78 +++++++++++++++++-- gui/cppchecklibrarydata.h | 21 ++++- .../files/container_valid.cfg | 9 +++ .../files/smartptr_valid.cfg | 4 +- gui/test/cppchecklibrarydata/resources.qrc | 1 + .../testcppchecklibrarydata.cpp | 54 ++++++++++++- .../testcppchecklibrarydata.h | 1 + 7 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 gui/test/cppchecklibrarydata/files/container_valid.cfg diff --git a/gui/cppchecklibrarydata.cpp b/gui/cppchecklibrarydata.cpp index a8f2aaf9a..7c6bf8636 100644 --- a/gui/cppchecklibrarydata.cpp +++ b/gui/cppchecklibrarydata.cpp @@ -61,7 +61,7 @@ static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader) if (elementName == "type") { container.type.templateParameter = xmlReader.attributes().value("templateParameter").toString(); container.type.string = xmlReader.attributes().value("string").toString(); - } else if (elementName == "size" || elementName == "access" || elementName == "other") { + } else if (elementName == "size" || elementName == "access" || elementName == "other" || elementName == "rangeItemRecordType") { const QString indexOperator = xmlReader.attributes().value("indexOperator").toString(); if (elementName == "access" && indexOperator == "array-like") container.access_arrayLike = true; @@ -82,7 +82,12 @@ static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader) container.sizeFunctions.append(function); else if (elementName == "access") container.accessFunctions.append(function); - else + else if (elementName == "rangeItemRecordType") { + struct CppcheckLibraryData::Container::RangeItemRecordType rangeItemRecordType; + rangeItemRecordType.name = xmlReader.attributes().value("name").toString(); + rangeItemRecordType.templateParameter = xmlReader.attributes().value("templateParameter").toString(); + container.rangeItemRecordTypeList.append(rangeItemRecordType); + } else container.otherFunctions.append(function); } } else { @@ -105,9 +110,23 @@ static QString loadUndefine(const QXmlStreamReader &xmlReader) return xmlReader.attributes().value("name").toString(); } -static QString loadSmartPointer(const QXmlStreamReader &xmlReader) +static CppcheckLibraryData::SmartPointer loadSmartPointer(QXmlStreamReader &xmlReader) { - return xmlReader.attributes().value("class-name").toString(); + CppcheckLibraryData::SmartPointer smartPointer; + smartPointer.name = xmlReader.attributes().value("class-name").toString(); + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "smart-pointer") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "unique") { + smartPointer.unique = true; + } else { + unhandledElement(xmlReader); + } + } + return smartPointer; } static CppcheckLibraryData::TypeChecks loadTypeChecks(QXmlStreamReader &xmlReader) @@ -210,6 +229,20 @@ static CppcheckLibraryData::Function loadFunction(QXmlStreamReader &xmlReader, c function.warn.reason = xmlReader.attributes().value("reason").toString(); function.warn.alternatives = xmlReader.attributes().value("alternatives").toString(); function.warn.msg = xmlReader.readElementText(); + } else if (elementName == "not-overlapping-data") { + const QStringList attributeList {"ptr1-arg", "ptr2-arg", "size-arg", "strlen-arg"}; + for (const QString &attr : attributeList) { + if (xmlReader.attributes().hasAttribute(attr)) { + function.notOverlappingDataArgs[attr] = xmlReader.attributes().value(attr).toString(); + } + } + } else if (elementName == "container") { + const QStringList attributeList {"action", "yields"}; + for (const QString &attr : attributeList) { + if (xmlReader.attributes().hasAttribute(attr)) { + function.containerAttributes[attr] = xmlReader.attributes().value(attr).toString(); + } + } } else { unhandledElement(xmlReader); } @@ -493,6 +526,20 @@ static void writeContainerFunctions(QXmlStreamWriter &xmlWriter, const QString & xmlWriter.writeEndElement(); } +static void writeContainerRangeItemRecords(QXmlStreamWriter &xmlWriter, const QList &rangeItemRecords) +{ + if (rangeItemRecords.isEmpty()) + return; + xmlWriter.writeStartElement("rangeItemRecordType"); + for (const CppcheckLibraryData::Container::RangeItemRecordType &item : rangeItemRecords) { + xmlWriter.writeStartElement("member"); + xmlWriter.writeAttribute("name", item.name); + xmlWriter.writeAttribute("templateParameter", item.templateParameter); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Container &container) { xmlWriter.writeStartElement("container"); @@ -519,6 +566,7 @@ static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryDat writeContainerFunctions(xmlWriter, "size", container.size_templateParameter, container.sizeFunctions); writeContainerFunctions(xmlWriter, "access", container.access_arrayLike?1:-1, container.accessFunctions); writeContainerFunctions(xmlWriter, "other", -1, container.otherFunctions); + writeContainerRangeItemRecords(xmlWriter, container.rangeItemRecordTypeList); xmlWriter.writeEndElement(); } @@ -632,7 +680,20 @@ static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData xmlWriter.writeEndElement(); } - + if (!function.notOverlappingDataArgs.isEmpty()) { + xmlWriter.writeStartElement("not-overlapping-data"); + foreach (const QString value, function.notOverlappingDataArgs) { + xmlWriter.writeAttribute(function.notOverlappingDataArgs.key(value), value); + } + xmlWriter.writeEndElement(); + } + if (!function.containerAttributes.isEmpty()) { + xmlWriter.writeStartElement("container"); + foreach (const QString value, function.containerAttributes) { + xmlWriter.writeAttribute(function.containerAttributes.key(value), value); + } + xmlWriter.writeEndElement(); + } xmlWriter.writeEndElement(); } @@ -835,9 +896,12 @@ QString CppcheckLibraryData::toString() const writeTypeChecks(xmlWriter, check); } - for (const QString &smartPtr : smartPointers) { + for (const SmartPointer &smartPtr : smartPointers) { xmlWriter.writeStartElement("smart-pointer"); - xmlWriter.writeAttribute("class-name", smartPtr); + xmlWriter.writeAttribute("class-name", smartPtr.name); + if (smartPtr.unique) { + xmlWriter.writeEmptyElement("unique"); + } xmlWriter.writeEndElement(); } diff --git a/gui/cppchecklibrarydata.h b/gui/cppchecklibrarydata.h index ff688d9c2..19e9bf4bb 100644 --- a/gui/cppchecklibrarydata.h +++ b/gui/cppchecklibrarydata.h @@ -22,6 +22,7 @@ #include #include #include +#include class QIODevice; @@ -47,6 +48,11 @@ public: QString string; } type; + struct RangeItemRecordType { + QString name; + QString templateParameter; + }; + struct Function { QString name; QString yields; @@ -55,6 +61,7 @@ public: QList accessFunctions; QList otherFunctions; QList sizeFunctions; + QList rangeItemRecordTypeList; }; struct Define { @@ -130,6 +137,9 @@ public: msg.isEmpty(); } } warn; + + QMap notOverlappingDataArgs; + QMap containerAttributes; }; struct MemoryResource { @@ -219,6 +229,15 @@ public: QList exporter; }; + struct SmartPointer { + SmartPointer() : + unique {false} + {} + + QString name; + bool unique; + }; + void clear() { containers.clear(); defines.clear(); @@ -258,7 +277,7 @@ public: QList typeChecks; QList platformTypes; QStringList undefines; - QStringList smartPointers; + QList smartPointers; QList reflections; QList markups; }; diff --git a/gui/test/cppchecklibrarydata/files/container_valid.cfg b/gui/test/cppchecklibrarydata/files/container_valid.cfg new file mode 100644 index 000000000..494821c9f --- /dev/null +++ b/gui/test/cppchecklibrarydata/files/container_valid.cfg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg b/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg index 1ae007ac6..e0b9245cb 100644 --- a/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg +++ b/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg @@ -1,6 +1,8 @@ - + + + \ No newline at end of file diff --git a/gui/test/cppchecklibrarydata/resources.qrc b/gui/test/cppchecklibrarydata/resources.qrc index c6394211e..57625f3b2 100644 --- a/gui/test/cppchecklibrarydata/resources.qrc +++ b/gui/test/cppchecklibrarydata/resources.qrc @@ -19,5 +19,6 @@ files/markup_mandatory_attribute_missing.cfg files/markup_valid.cfg files/markup_unhandled_element.cfg + files/container_valid.cfg diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp index 973260870..df8d0c471 100644 --- a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp @@ -182,9 +182,12 @@ void TestCppcheckLibraryData::smartPointerValid() // Do size and content checks against swapped data. QCOMPARE(libraryData.smartPointers.size(), 3); - QCOMPARE(libraryData.smartPointers[0], QString("wxObjectDataPtr")); - QCOMPARE(libraryData.smartPointers[1], QString("wxScopedArray")); - QCOMPARE(libraryData.smartPointers[2], QString("wxScopedPtr")); + QCOMPARE(libraryData.smartPointers[0].name, QString("wxObjectDataPtr")); + QCOMPARE(libraryData.smartPointers[0].unique, false); + QCOMPARE(libraryData.smartPointers[1].name, QString("wxScopedArray")); + QCOMPARE(libraryData.smartPointers[1].unique, true); + QCOMPARE(libraryData.smartPointers[2].name, QString("wxScopedPtr")); + QCOMPARE(libraryData.smartPointers[2].unique, false); // Save library data to file saveCfgFile(TempCfgFile, libraryData); @@ -199,7 +202,10 @@ void TestCppcheckLibraryData::smartPointerValid() // Verify no data got lost or modified QCOMPARE(libraryData.smartPointers.size(), fileLibraryData.smartPointers.size()); QCOMPARE(libraryData.smartPointers.size(), 3); - QCOMPARE(libraryData.smartPointers, fileLibraryData.smartPointers); + for (int idx=0; idx < libraryData.smartPointers.size(); idx++) { + QCOMPARE(libraryData.smartPointers[idx].name, fileLibraryData.smartPointers[idx].name); + QCOMPARE(libraryData.smartPointers[idx].unique, fileLibraryData.smartPointers[idx].unique); + } } void TestCppcheckLibraryData::platformTypeValid() @@ -543,6 +549,46 @@ void TestCppcheckLibraryData::markupValid() } } +void TestCppcheckLibraryData::containerValid() +{ + // Load library data from file + loadCfgFile(":/files/container_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.containers.size(), 1); + + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList.size(), 2); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].name, QString("first")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].templateParameter, QString("0")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].name, QString("second")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].templateParameter, QString("1")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.containers.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.containers.size(), fileLibraryData.containers.size()); + for (int idx=0; idx < libraryData.containers.size(); idx++) { + CppcheckLibraryData::Container lhs = libraryData.containers[idx]; + CppcheckLibraryData::Container rhs = fileLibraryData.containers[idx]; + for (int num=0; num < lhs.rangeItemRecordTypeList.size(); num++) { + QCOMPARE(lhs.rangeItemRecordTypeList[num].name, rhs.rangeItemRecordTypeList[num].name); + QCOMPARE(lhs.rangeItemRecordTypeList[num].templateParameter, rhs.rangeItemRecordTypeList[num].templateParameter); + } + } +} + void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &res, bool removeFile) { QFile file(filename); diff --git a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h index 64cd1d28c..12a9efd5a 100644 --- a/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h +++ b/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h @@ -39,6 +39,7 @@ private slots: void undefineValid(); void reflectionValid(); void markupValid(); + void containerValid(); private: void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &res, bool removeFile = false); From d8239b26f1cacd0f1d17e8f78251b2d8a6d4aafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 4 Jul 2022 22:54:43 +0200 Subject: [PATCH 22/36] Tokenizer: Remove simplifyCalculations from simplifyTokenList2 --- lib/tokenize.cpp | 9 --- lib/tokenize.h | 7 -- test/testsimplifytokens.cpp | 133 +----------------------------------- 3 files changed, 1 insertion(+), 148 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 3cdb03998..406cc766a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5168,9 +5168,6 @@ bool Tokenizer::simplifyTokenList2() if (Settings::terminated()) return false; - // Simplify simple calculations before replace constants, this allows the replacement of constants that are calculated - // e.g. const static int value = sizeof(X)/sizeof(Y); - simplifyCalculations(); bool modified = true; while (modified) { @@ -5179,7 +5176,6 @@ bool Tokenizer::simplifyTokenList2() modified = false; modified |= simplifyConstTernaryOp(); - modified |= simplifyCalculations(); validate(); } @@ -7248,11 +7244,6 @@ void Tokenizer::simplifyTypeIntrinsics() } } -bool Tokenizer::simplifyCalculations() -{ - return mTemplateSimplifier->simplifyCalculations(nullptr, nullptr, false); -} - //--------------------------------------------------------------------------- // Helper functions for handling the tokens list //--------------------------------------------------------------------------- diff --git a/lib/tokenize.h b/lib/tokenize.h index a919170b2..b8992ef46 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -179,13 +179,6 @@ public: /** Simplify assignment where rhs is a block : "x=({123;});" => "{x=123;}" */ void simplifyAssignmentBlock(); - /** - * Simplify constant calculations such as "1+2" => "3" - * @return true if modifications to token-list are done. - * false if no modifications are done. - */ - bool simplifyCalculations(); - /** Insert array size where it isn't given */ void arraySize(); diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 38f53c57c..da66896de 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -62,7 +62,6 @@ private: TEST_CASE(double_plus); TEST_CASE(redundant_plus); TEST_CASE(redundant_plus_numbers); - TEST_CASE(parentheses1); TEST_CASE(declareVar); TEST_CASE(declareArray); @@ -90,11 +89,6 @@ private: // Simplify "?:" TEST_CASE(simplifyConditionOperator); - // Simplify calculations - TEST_CASE(calculations); - TEST_CASE(comparisons); - TEST_CASE(simplifyCalculations); - TEST_CASE(simplifyOperator1); TEST_CASE(simplifyOperator2); @@ -162,7 +156,6 @@ private: TEST_CASE(simplifyKnownVariables51); // #4409 hang TEST_CASE(simplifyKnownVariables54); // #4913 'x' is not 0 after *--x=0; TEST_CASE(simplifyKnownVariables56); // ticket #5301 - >> - TEST_CASE(simplifyKnownVariables57); // ticket #4724 TEST_CASE(simplifyKnownVariables58); // ticket #5268 TEST_CASE(simplifyKnownVariables59); // skip for header TEST_CASE(simplifyKnownVariables61); // #7805 @@ -1860,11 +1853,6 @@ private: } - void parentheses1() { - ASSERT_EQUALS("a <= 110 ;", tok("a <= (10+100);")); - ASSERT_EQUALS("{ while ( x ( ) == -1 ) { } }", tok("{while((x()) == -1){ }}")); - } - void declareVar() { const char code[] = "void f ( ) { char str [ 100 ] = \"100\" ; }"; ASSERT_EQUALS(code, tok(code)); @@ -2307,7 +2295,7 @@ private: { const char code[] = "tr = (struct reg){ .a = (1), .c = (2) };"; - const char expected[] = "tr = ( struct reg ) { . a = 1 , . c = 2 } ;"; + const char expected[] = "tr = ( struct reg ) { . a = 1 , . c = ( 2 ) } ;"; ASSERT_EQUALS(expected, tok(code)); } } @@ -2387,120 +2375,6 @@ private: } } - void calculations() { - { - const char code[] = "a[i+8+2];"; - ASSERT_EQUALS("a [ i + 10 ] ;", tok(code)); - } - { - const char code[] = "a[8+2+i];"; - ASSERT_EQUALS("a [ 10 + i ] ;", tok(code)); - } - { - const char code[] = "a[i + 2 * (2 * 4)];"; - ASSERT_EQUALS("a [ i + 16 ] ;", tok(code)); - } - { - const char code[] = "a[i + 100 - 90];"; - ASSERT_EQUALS("a [ i + 10 ] ;", tok(code)); - } - { - const char code[] = "a[1+1+1+1+1+1+1+1+1+1-2+5-3];"; - ASSERT_EQUALS("a [ 10 ] ;", tok(code)); - } - { - const char code[] = "a[10+10-10-10];"; - ASSERT_EQUALS("a [ 0 ] ;", tok(code)); - } - - ASSERT_EQUALS("a [ 4 ] ;", tok("a[1+3|4];")); - ASSERT_EQUALS("a [ 4U ] ;", tok("a[1+3|4U];")); - ASSERT_EQUALS("a [ 3 ] ;", tok("a[1+2&3];")); - ASSERT_EQUALS("a [ 3U ] ;", tok("a[1+2&3U];")); - ASSERT_EQUALS("a [ 5 ] ;", tok("a[1-0^4];")); - ASSERT_EQUALS("a [ 5U ] ;", tok("a[1-0^4U];")); - - ASSERT_EQUALS("x = 1 + 2 * y ;", tok("x=1+2*y;")); - ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;")); - ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);")); - ASSERT_EQUALS("x = 1500000 / ( ( 90000 ) * 1000 / 54000 ) ;", tok("x = 1500000 / ((145000 - 55000) * 1000 / 54000);")); - ASSERT_EQUALS("int a [ 8 ] ;", tok("int a[5+6/2];")); - ASSERT_EQUALS("int a [ 4 ] ;", tok("int a[(10)-1-5];")); - ASSERT_EQUALS("int a [ i - 9 ] ;", tok("int a[i - 10 + 1];")); - ASSERT_EQUALS("int a [ i - 11 ] ;", tok("int a[i - 10 - 1];")); - - ASSERT_EQUALS("x = y ;", tok("x=0+y+0-0;")); - ASSERT_EQUALS("x = 0 ;", tok("x=0*y;")); - - ASSERT_EQUALS("x = 501 ;", tok("x = 1000 + 2 >> 1;")); - ASSERT_EQUALS("x = 125 ;", tok("x = 1000 / 2 >> 2;")); - - { - // Ticket #1997 - const char code[] = "void * operator new[](size_t);"; - ASSERT_EQUALS("void * operatornew[] ( long ) ;", tok(code, true, Settings::Win32A)); - } - - ASSERT_EQUALS("; a [ 0 ] ;", tok(";a[0*(*p)];")); - - ASSERT_EQUALS(";", tok("; x = x + 0;")); - - ASSERT_EQUALS("{ if ( a == 2 ) { } }", tok("{if (a==1+1){}}")); - ASSERT_EQUALS("{ if ( a + 2 != 6 ) { } }", tok("{if (a+1+1!=1+2+3){}}")); - ASSERT_EQUALS("{ if ( 4 < a ) { } }", tok("{if (14-2*5>1+0%2*1);")); // #4931 - ASSERT_EQUALS("x ( 0 & 4 != 1 ) ;", tok("x(4%1<<1&4!=1);")); // #4931 (can be simplified further but it's not a problem) - ASSERT_EQUALS("x ( 1 ) ;", tok("x(0&&4>0==2||4);")); // #4931 - - // don't remove these spaces.. - ASSERT_EQUALS("new ( auto ) ( 4 ) ;", tok("new (auto)(4);")); - } - - void comparisons() { - ASSERT_EQUALS("( 1 ) ;", tok("( 1 < 2 );")); - ASSERT_EQUALS("( x && 1 ) ;", tok("( x && 1 < 2 );")); - ASSERT_EQUALS("( 5 ) ;", tok("( 1 < 2 && 3 < 4 ? 5 : 6 );")); - ASSERT_EQUALS("( 6 ) ;", tok("( 1 > 2 && 3 > 4 ? 5 : 6 );")); - } - - void simplifyCalculations() { - ASSERT_EQUALS("void foo ( char str [ ] ) { char x ; x = ( * str ) ; }", - tok("void foo ( char str [ ] ) { char x = 0 | ( * str ) ; }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (b + 0) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (0 + b) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (b - 0) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (b * 1) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (1 * b) { } }")); - //ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - // tok("void foo ( ) { if (b / 1) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (b | 0) { } }")); - ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", - tok("void foo ( ) { if (0 | b) { } }")); - ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }", - tok("void foo ( int b ) { int a = b | 0 ; bar ( a ) ; }")); - ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }", - tok("void foo ( int b ) { int a = 0 | b ; bar ( a ) ; }")); - } - - void simplifyOperator1() { // #3237 - error merging namespaces with operators const char code[] = "class c {\n" @@ -3920,11 +3794,6 @@ private: tokenizeAndStringify("void f() { int a=0,b=0; *p>>a>>b; return a/b; }", true)); } - void simplifyKnownVariables57() { // #4724 - ASSERT_EQUALS("unsigned long long x ; x = 9223372036854775808UL ;", tokenizeAndStringify("unsigned long long x = 1UL << 63 ;", true)); - ASSERT_EQUALS("long long x ; x = -9223372036854775808L ;", tokenizeAndStringify("long long x = 1L << 63 ;", true)); - } - void simplifyKnownVariables58() { // #5268 const char code[] = "enum e { VAL1 = 1, VAL2 }; " "typedef char arr_t[VAL2]; " From 29402b4d1ab7cd9ed7bdc2c40271ff39cb3ce055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 5 Jul 2022 07:46:39 +0200 Subject: [PATCH 23/36] Fix Coverity CID1490221 warning. settings is dereferenced and then checked. --- lib/valueflow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 873c5cbfe..fd470ba3a 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -986,8 +986,7 @@ static void setTokenValue(Token* tok, ValueFlow::Value v(val); v.intvalue = ~v.intvalue; int bits = 0; - if (settings && - tok->valueType() && + if (tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED && tok->valueType()->pointer == 0) { if (tok->valueType()->type == ValueType::Type::INT) From 41f6702cc83874c2c4a54b069ef7b9bf673e0761 Mon Sep 17 00:00:00 2001 From: Andy Holmes <1265208+andyholmes@users.noreply.github.com> Date: Wed, 6 Jul 2022 07:48:50 -0700 Subject: [PATCH 24/36] gtk.cfg: add more type definition macros (#4247) Add a number of missing macros from the configuration, including: * `G_DEFINE_TYPE()` * `G_DEFINE_FINAL_TYPE()` * `G_DEFINE_FINAL_TYPE_WITH_PRIVATE()` * `G_DEFINE_FINAL_TYPE_WITH_CODE()` * `G_DEFINE_DYNAMIC_TYPE()` * `G_DEFINE_DYNAMIC_TYPE_EXTENDED()` * `G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC()` * `G_DEFINE_AUTO_CLEANUP_FREE_FUNC()` * `G_DEFINE_AUTOPTR_CLEANUP_FUNC()` --- cfg/gtk.cfg | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cfg/gtk.cfg b/cfg/gtk.cfg index 494e792ec..e756f3c85 100644 --- a/cfg/gtk.cfg +++ b/cfg/gtk.cfg @@ -36,11 +36,17 @@ + + + + + + @@ -51,6 +57,9 @@ + + + From b888f9cf922143a4c9b0ffba44786810c080e98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 7 Jul 2022 12:16:01 +0200 Subject: [PATCH 25/36] use hash instead of checksum to check if content is changed --- lib/analyzerinfo.cpp | 12 ++++---- lib/analyzerinfo.h | 2 +- lib/cppcheck.cpp | 16 +++++------ lib/preprocessor.cpp | 65 ++++---------------------------------------- lib/preprocessor.h | 6 ++-- lib/tokenlist.cpp | 26 ++++++------------ lib/tokenlist.h | 6 ++-- 7 files changed, 35 insertions(+), 98 deletions(-) diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index dde7303e6..882820c47 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -72,7 +72,7 @@ void AnalyzerInformation::close() } } -static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long checksum, std::list *errors) +static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, std::list *errors) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str()); @@ -83,8 +83,8 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long if (rootNode == nullptr) return false; - const char *attr = rootNode->Attribute("checksum"); - if (!attr || attr != std::to_string(checksum)) + const char *attr = rootNode->Attribute("hash"); + if (!attr || attr != std::to_string(hash)) return false; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { @@ -125,7 +125,7 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir return filename; } -bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors) +bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list *errors) { if (buildDir.empty() || sourcefile.empty()) return true; @@ -133,13 +133,13 @@ bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::st mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); - if (skipAnalysis(mAnalyzerInfoFile, checksum, errors)) + if (skipAnalysis(mAnalyzerInfoFile, hash, errors)) return false; mOutputStream.open(mAnalyzerInfoFile); if (mOutputStream.is_open()) { mOutputStream << "\n"; - mOutputStream << "\n"; + mOutputStream << "\n"; } else { mAnalyzerInfoFile.clear(); } diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index b5ed8879e..13e9c0a68 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -54,7 +54,7 @@ public: /** Close current TU.analyzerinfo file */ void close(); - bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors); + bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list *errors); void reportErr(const ErrorMessage &msg, bool verbose); void setFileInfo(const std::string &check, const std::string &fileInfo); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 1a4100c8c..dec52a4ce 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -707,10 +707,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string toolinfo << mSettings.userDefines; mSettings.nomsg.dump(toolinfo); - // Calculate checksum so it can be compared with old checksum / future checksums - const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str()); + // Calculate hash so it can be compared with old hash / future hashes + const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str()); std::list errors; - if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &errors)) { + if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, &errors)) { while (!errors.empty()) { reportErr(errors.front()); errors.pop_front(); @@ -775,7 +775,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string } } - std::set checksums; + std::set hashes; int checkCount = 0; bool hasValidConfig = false; std::list configurationError; @@ -871,18 +871,18 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string fdump << "" << std::endl; } - // Need to call this even if the checksum will skip this configuration + // Need to call this even if the hash will skip this configuration mSettings.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer); // Skip if we already met the same simplified token list if (mSettings.force || mSettings.maxConfigs > 1) { - const unsigned long long checksum = tokenizer.list.calculateChecksum(); - if (checksums.find(checksum) != checksums.end()) { + const std::size_t hash = tokenizer.list.calculateHash(); + if (hashes.find(hash) != hashes.end()) { if (mSettings.debugwarnings) purgedConfigurationMessage(filename, mCurrentConfig); continue; } - checksums.insert(checksum); + hashes.insert(hash); } // Check normal tokens diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index b73d63a7b..148932380 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include // back_inserter #include #include @@ -971,74 +972,20 @@ void Preprocessor::dump(std::ostream &out) const } } -static const std::uint32_t crc32Table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -static void crc32(const std::string &data, uint32_t& crc) +std::size_t Preprocessor::calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const { - for (char c : data) { - crc = crc32Table[(crc ^ (unsigned char)c) & 0xFF] ^ (crc >> 8); - } -} - -uint32_t Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const -{ - std::uint32_t crc = ~0U; - crc32(toolinfo, crc); + std::string hashData = toolinfo; for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { if (!tok->comment) - crc32(tok->str(), crc); + hashData += tok->str(); } for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) { if (!tok->comment) - crc32(tok->str(), crc); + hashData += tok->str(); } } - return crc ^ ~0U; + return (std::hash{})(hashData); } void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) const diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 66b9d4fb6..48ce2cb55 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -164,13 +164,13 @@ public: void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o); /** - * Calculate CRC32 checksum. Using toolinfo, tokens1, filedata. + * Calculate HASH. Using toolinfo, tokens1, filedata. * * @param tokens1 Sourcefile tokens * @param toolinfo Arbitrary extra toolinfo - * @return CRC32 checksum + * @return HASH */ - uint32_t calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const; + std::size_t calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const; void simplifyPragmaAsm(simplecpp::TokenList *tokenList) const; diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 1a6feb1de..2239b674a 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -461,27 +461,17 @@ void TokenList::createTokens(simplecpp::TokenList&& tokenList) //--------------------------------------------------------------------------- -uint64_t TokenList::calculateChecksum() const +std::size_t TokenList::calculateHash() const { - uint64_t checksum = 0; + std::string hashData; for (const Token* tok = front(); tok; tok = tok->next()) { - const uint32_t subchecksum1 = tok->flags() + tok->varId() + tok->tokType(); - uint32_t subchecksum2 = 0; - for (char i : tok->str()) - subchecksum2 += (uint32_t)i; - if (!tok->originalName().empty()) { - for (char i : tok->originalName()) - subchecksum2 += (uint32_t)i; - } - - checksum ^= ((static_cast(subchecksum1) << 32) | subchecksum2); - - const bool bit1 = (checksum & 1) != 0; - checksum >>= 1; - if (bit1) - checksum |= (1ULL << 63); + hashData += MathLib::toString(tok->flags()); + hashData += MathLib::toString(tok->varId()); + hashData += MathLib::toString(tok->tokType()); + hashData += tok->str(); + hashData += tok->originalName(); } - return checksum; + return (std::hash{})(hashData); } diff --git a/lib/tokenlist.h b/lib/tokenlist.h index ed73d0a45..0167fd87f 100644 --- a/lib/tokenlist.h +++ b/lib/tokenlist.h @@ -153,10 +153,10 @@ public: std::string fileLine(const Token *tok) const; /** - * Calculates a 64-bit checksum of the token list used to compare - * multiple token lists with each other as quickly as possible. + * Calculates a hash of the token list used to compare multiple + * token lists with each other as quickly as possible. */ - uint64_t calculateChecksum() const; + std::size_t calculateHash() const; /** * Create abstract syntax tree. From 68bb724dcc878f964fd074f17b479ff92594beb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 7 Jul 2022 15:07:17 +0200 Subject: [PATCH 26/36] ErrorLogger: do not allocate huge std::string when column is -1 --- lib/errorlogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 294ff8708..e0c881543 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -489,7 +489,7 @@ static std::string readCode(const std::string &file, int linenr, int column, con std::string::size_type pos = 0; while ((pos = line.find('\t', pos)) != std::string::npos) line[pos] = ' '; - return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^'; + return line + endl + std::string((column>0 ? column-1 : 0), ' ') + '^'; } static void replaceColors(std::string& source) From d1812c80221ae17e3b50abbef899663a2263b975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 7 Jul 2022 17:35:13 +0200 Subject: [PATCH 27/36] Cppcheck: Write product name if provided in xml output --- cli/cppcheckexecutor.cpp | 4 ++-- lib/errorlogger.cpp | 4 +++- lib/errorlogger.h | 2 +- test/testerrorlogger.cpp | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index af227c891..69aea810c 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -122,7 +122,7 @@ bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* c if (parser.getShowErrorMessages()) { mShowAllErrors = true; - std::cout << ErrorMessage::getXMLHeader(); + std::cout << ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName); cppcheck->getErrorMessages(); std::cout << ErrorMessage::getXMLFooter() << std::endl; } @@ -908,7 +908,7 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck) } if (settings.xml) { - reportErr(ErrorMessage::getXMLHeader()); + reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName)); } if (!settings.buildDir.empty()) { diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index e0c881543..180929b2e 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -368,7 +368,7 @@ bool ErrorMessage::deserialize(const std::string &data) return true; } -std::string ErrorMessage::getXMLHeader() +std::string ErrorMessage::getXMLHeader(const std::string& productName) { tinyxml2::XMLPrinter printer; @@ -380,6 +380,8 @@ std::string ErrorMessage::getXMLHeader() printer.PushAttribute("version", 2); printer.OpenElement("cppcheck", false); + if (!productName.empty()) + printer.PushAttribute("product-name", productName.c_str()); printer.PushAttribute("version", CppCheck::version()); printer.CloseElement(false); printer.OpenElement("errors", false); diff --git a/lib/errorlogger.h b/lib/errorlogger.h index e81e541d7..702f10436 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -160,7 +160,7 @@ public: */ std::string toXML() const; - static std::string getXMLHeader(); + static std::string getXMLHeader(const std::string& productName); static std::string getXMLFooter(); /** diff --git a/test/testerrorlogger.cpp b/test/testerrorlogger.cpp index 7376614ea..0282fb430 100644 --- a/test/testerrorlogger.cpp +++ b/test/testerrorlogger.cpp @@ -192,7 +192,7 @@ private: header += " \n "; - ASSERT_EQUALS(header, ErrorMessage::getXMLHeader()); + ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("")); ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); std::string message(" \n"; @@ -208,7 +208,7 @@ private: header += " \n "; - ASSERT_EQUALS(header, ErrorMessage::getXMLHeader()); + ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("")); ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); std::string message(" \n"; From e1d3e6f72e0df274358a4860356cf8e812501779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 7 Jul 2022 19:14:18 +0200 Subject: [PATCH 28/36] Catch internal error during whole program analysis --- lib/cppcheck.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index dec52a4ce..fc3eba76d 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1432,7 +1432,12 @@ void CppCheck::executeAddonsWholeProgram(const std::map Date: Thu, 7 Jul 2022 19:45:47 +0200 Subject: [PATCH 29/36] Fix syntax error --- lib/cppcheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index fc3eba76d..0dfd6fbdb 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1434,8 +1434,8 @@ void CppCheck::executeAddonsWholeProgram(const std::map Date: Fri, 8 Jul 2022 12:35:21 +0200 Subject: [PATCH 30/36] Fix #9944 FP: terminateStrncpy doesn't account for size check (#4252) * Fix #9944 FP: terminateStrncpy doesn't account for size check * Fix container size check * Undo * Format * Rebuild * Rebuild --- lib/checkbufferoverrun.cpp | 15 ++++++++++++--- test/testbufferoverrun.cpp | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 499813323..68b42a1d1 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -739,9 +739,18 @@ void CheckBufferOverrun::stringNotZeroTerminated() const ValueFlow::Value &bufferSize = getBufferSize(args[0]); if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue) continue; - const Token *srcValue = args[1]->getValueTokenMaxStrLength(); - if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue()) - continue; + if (Token::simpleMatch(args[1], "(") && Token::simpleMatch(args[1]->astOperand1(), ". c_str") && args[1]->astOperand1()->astOperand1()) { + const std::list& contValues = args[1]->astOperand1()->astOperand1()->values(); + auto it = std::find_if(contValues.begin(), contValues.end(), [](const ValueFlow::Value& value) { + return value.isContainerSizeValue() && !value.isImpossible(); + }); + if (it != contValues.end() && it->intvalue < sizeToken->getKnownIntValue()) + continue; + } else { + const Token* srcValue = args[1]->getValueTokenMaxStrLength(); + if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue()) + continue; + } // Is the buffer zero terminated after the call? bool isZeroTerminated = false; for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) { diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 6d34d4a12..e09c0732b 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -289,6 +289,7 @@ private: TEST_CASE(terminateStrncpy2); TEST_CASE(terminateStrncpy3); TEST_CASE(terminateStrncpy4); + TEST_CASE(terminateStrncpy5); // #9944 TEST_CASE(recursive_long_time); TEST_CASE(crash1); // Ticket #1587 - crash @@ -4342,6 +4343,23 @@ private: "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' may not be null-terminated after the call to strncpy().\n", errout.str()); } + + void terminateStrncpy5() { // #9944 + check("void f(const std::string& buf) {\n" + " char v[255];\n" + " if (buf.size() >= sizeof(v))\n" + " return;\n" + " strncpy(v, buf.c_str(), sizeof(v));\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("void f(const std::string& buf) {\n" + " char v[255];\n" + " if (buf.size() >= sizeof(v))\n" + " strncpy(v, buf.c_str(), sizeof(v));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'v' may not be null-terminated after the call to strncpy().\n", errout.str()); + } // extracttests.enable void recursive_long_time() { From a65f6952dc8e22bd29bc95eabd9352ae1be365e3 Mon Sep 17 00:00:00 2001 From: Maksim Derbasov Date: Fri, 8 Jul 2022 13:37:05 +0300 Subject: [PATCH 31/36] Codeeditor: multiline comment fix (#4253) --- gui/codeeditor.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/gui/codeeditor.cpp b/gui/codeeditor.cpp index 832dcf609..d5332befb 100644 --- a/gui/codeeditor.cpp +++ b/gui/codeeditor.cpp @@ -45,26 +45,26 @@ Highlighter::Highlighter(QTextDocument *parent, << "case" << "catch" << "char" - << "char8_­t" - << "char16_­t" - << "char32_­t" + << "char8_t" + << "char16_t" + << "char32_t" << "class" << "concept" << "const" << "consteval" << "constexpr" << "constinit" - << "const_­cast" + << "const_cast" << "continue" - << "co_­await" - << "co_­return" - << "co_­yield" + << "co_await" + << "co_return" + << "co_yield" << "decltype" << "default" << "delete" << "do" << "double" - << "dynamic_­cast" + << "dynamic_cast" << "else" << "enum" << "explicit" @@ -92,19 +92,19 @@ Highlighter::Highlighter(QTextDocument *parent, << "private" << "protected" << "public" - << "reinterpret_­cast" + << "reinterpret_cast" << "requires" << "return" << "short" << "signed" << "static" - << "static_­assert" - << "static_­cast" + << "static_assert" + << "static_cast" << "struct" << "switch" << "template" << "this" - << "thread_­local" + << "thread_local" << "throw" << "true" << "try" @@ -116,7 +116,7 @@ Highlighter::Highlighter(QTextDocument *parent, << "virtual" << "void" << "volatile" - << "wchar_­t" + << "wchar_t" << "while"; for (const QString &pattern : keywordPatterns) { rule.pattern = QRegularExpression("\\b" + pattern + "\\b"); @@ -157,7 +157,9 @@ Highlighter::Highlighter(QTextDocument *parent, mSymbolFormat.setBackground(mWidgetStyle->symbolBGColor); mSymbolFormat.setFontWeight(mWidgetStyle->symbolWeight); - mCommentStartExpression = QRegularExpression("/\\*"); + // We use negative lookbehind assertion `(? Date: Fri, 8 Jul 2022 12:37:53 +0200 Subject: [PATCH 32/36] Fix #11163 Hang on smart pointer with auto type deduction (#4250) * Fix #11163 Hang on smart pointer with auto type deduction * Format * Format --- lib/symboldatabase.cpp | 2 +- test/testsymboldatabase.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 952e692bc..2033349ee 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -6508,7 +6508,7 @@ static const Token* parsedecl(const Token* type, } else if (const Library::SmartPointer* smartPointer = settings->library.detectSmartPointer(type)) { const Token* argTok = Token::findsimplematch(type, "<"); if (!argTok) - continue; + break; valuetype->smartPointer = smartPointer; valuetype->smartPointerTypeToken = argTok->next(); valuetype->smartPointerType = argTok->next()->type(); diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 4030da2c1..959d19541 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -490,6 +490,7 @@ private: TEST_CASE(auto14); TEST_CASE(auto15); // C++17 auto deduction from braced-init-list TEST_CASE(auto16); + TEST_CASE(auto17); // #11163 TEST_CASE(unionWithConstructor); @@ -8601,6 +8602,18 @@ private: ASSERT_EQUALS(ValueType::Type::RECORD, i->valueType()->type); } + void auto17() { // #11163 don't hang + GET_SYMBOL_DB("void f() {\n" + " std::shared_ptr s1;\n" + " auto s2 = std::shared_ptr(s1);\n" + "}\n" + "void g() {\n" + " std::shared_ptr s;\n" + " auto w = std::weak_ptr(s);\n" + "}\n"); + ASSERT_EQUALS(5, db->variableList().size()); + } + void unionWithConstructor() { GET_SYMBOL_DB("union Fred {\n" " Fred(int x) : i(x) { }\n" From c17e2b9826c06ec5a5e9668197ca888ff15f3d30 Mon Sep 17 00:00:00 2001 From: Tau Date: Fri, 8 Jul 2022 10:39:29 +0000 Subject: [PATCH 33/36] Correctly interpret boolean literals in `execute()` (#4251) * Correctly interpret boolean literals in `execute()` * Add regeression test case for boolean literals --- lib/programmemory.cpp | 2 ++ test/testuninitvar.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index 165f41cd4..ddc5f73dd 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -632,6 +632,8 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const if (MathLib::isFloat(expr->str())) return unknown; return ValueFlow::Value{MathLib::toLongNumber(expr->str())}; + } else if (expr->isBoolean()) { + return ValueFlow::Value{ expr->str() == "true" }; } else if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { const Token* containerTok = expr->tokAt(-2)->astOperand1(); Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 6a33395ae..a3255cd6c 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -3623,6 +3623,17 @@ private: " if (a[i]) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + valueFlowUninit("void f(void) {\n" + " char *c;\n" + " char x;\n" + " while (true) {\n" + " c = &x;\n" + " break;\n" + " }\n" + " ++c;\n" + "}", "test.c"); + ASSERT_EQUALS("", errout.str()); } void uninitStructMember() { // struct members From e64e5811abcf1deeb4532bb6f98f0461d4933ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 8 Jul 2022 14:06:13 +0200 Subject: [PATCH 34/36] misra.py: better handling of --get-rule-text --- addons/misra.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/misra.py b/addons/misra.py index 2cced8d14..d24ea0d3f 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -4063,8 +4063,9 @@ class MisraChecker: errmsg = 'misra violation (use --rule-texts= to get proper output)' if self.path_premium_addon: for line in cppcheckdata.cmd_output([self.path_premium_addon, '--cli', '--get-rule-text=' + errorId]).split('\n'): - if not line.startswith('{'): + if len(line) > 1 and not line.startswith('{'): errmsg = line + break else: errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum) From e0bc871bae20d0b6ca82e6720453555a1ed11d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 8 Jul 2022 16:11:15 +0200 Subject: [PATCH 35/36] misra.py: strip error message so it does not end with \r --- addons/misra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/misra.py b/addons/misra.py index d24ea0d3f..48dcf2d13 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -4064,7 +4064,7 @@ class MisraChecker: if self.path_premium_addon: for line in cppcheckdata.cmd_output([self.path_premium_addon, '--cli', '--get-rule-text=' + errorId]).split('\n'): if len(line) > 1 and not line.startswith('{'): - errmsg = line + errmsg = line.strip() break else: errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum) From b51aea5531a1eeb49f6dad160142312186e375bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 8 Jul 2022 16:42:57 +0200 Subject: [PATCH 36/36] separated process execution code into `ProcessExecutor` (#4249) --- Makefile | 24 ++- cli/CMakeLists.txt | 4 +- cli/cli.vcxproj | 4 + cli/cppcheckexecutor.cpp | 11 +- cli/executor.cpp | 30 +++ cli/executor.h | 60 ++++++ cli/processexecutor.cpp | 383 +++++++++++++++++++++++++++++++++++ cli/processexecutor.h | 72 +++++++ cli/threadexecutor.cpp | 375 ++-------------------------------- cli/threadexecutor.h | 48 +---- cmake/findDependencies.cmake | 4 +- cmake/printInfo.cmake | 4 +- lib/checkother.cpp | 1 + lib/checkunusedvar.cpp | 1 + lib/config.h | 1 + lib/ctu.cpp | 1 + lib/ctu.h | 1 - lib/path.cpp | 1 - lib/pathmatch.cpp | 2 - test/CMakeLists.txt | 4 +- test/testprocessexecutor.cpp | 142 +++++++++++++ test/testrunner.vcxproj | 3 + tools/dmake.cpp | 4 +- 23 files changed, 752 insertions(+), 428 deletions(-) create mode 100644 cli/executor.cpp create mode 100644 cli/executor.h create mode 100644 cli/processexecutor.cpp create mode 100644 cli/processexecutor.h create mode 100644 test/testprocessexecutor.cpp diff --git a/Makefile b/Makefile index 22cb89c9e..1d8603454 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,8 @@ else # !WINNT endif # !CPPCHK_GLIBCXX_DEBUG endif # GNU/kFreeBSD + LDFLAGS=-pthread + endif # WINNT ifdef CYGWIN @@ -207,8 +209,10 @@ EXTOBJ = externals/simplecpp/simplecpp.o \ CLIOBJ = cli/cmdlineparser.o \ cli/cppcheckexecutor.o \ + cli/executor.o \ cli/filelister.o \ cli/main.o \ + cli/processexecutor.o \ cli/threadexecutor.o TESTOBJ = test/options.o \ @@ -247,6 +251,7 @@ TESTOBJ = test/options.o \ test/testplatform.o \ test/testpostfixoperator.o \ test/testpreprocessor.o \ + test/testprocessexecutor.o \ test/testrunner.o \ test/testsimplifytemplate.o \ test/testsimplifytokens.o \ @@ -285,7 +290,7 @@ cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ) all: cppcheck testrunner -testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o +testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) test: all @@ -567,16 +572,22 @@ $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/ca cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp -cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h +cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp +cli/executor.o: cli/executor.cpp cli/executor.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/executor.o cli/executor.cpp + cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/filelister.o cli/filelister.cpp cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/main.o cli/main.cpp -cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h +cli/processexecutor.o: cli/processexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/processexecutor.o cli/processexecutor.cpp + +cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp test/options.o: test/options.cpp test/options.h @@ -687,6 +698,9 @@ test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/check.h lib/checkpo test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpreprocessor.o test/testpreprocessor.cpp +test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testprocessexecutor.o test/testprocessexecutor.cpp + test/testrunner.o: test/testrunner.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/preprocessor.h lib/suppressions.h test/options.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testrunner.o test/testrunner.cpp @@ -717,13 +731,13 @@ test/testsuite.o: test/testsuite.cpp lib/color.h lib/config.h lib/errorlogger.h test/testsummaries.o: test/testsummaries.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsummaries.o test/testsummaries.cpp -test/testsuppressions.o: test/testsuppressions.cpp cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h +test/testsuppressions.o: test/testsuppressions.cpp cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuppressions.o test/testsuppressions.cpp test/testsymboldatabase.o: test/testsymboldatabase.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp -test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/threadexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h +test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp test/testtimer.o: test/testtimer.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/timer.h test/testsuite.h diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 20915ae4e..3b99ac1b2 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -39,9 +39,7 @@ endif() if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) target_link_libraries(cppcheck ${tinyxml2_LIBRARIES}) endif() -if (USE_THREADS) - target_link_libraries(cppcheck ${CMAKE_THREAD_LIBS_INIT}) -endif() +target_link_libraries(cppcheck ${CMAKE_THREAD_LIBS_INIT}) add_dependencies(cppcheck copy_cfg) add_dependencies(cppcheck copy_addons) diff --git a/cli/cli.vcxproj b/cli/cli.vcxproj index c242bad09..9a2db8671 100644 --- a/cli/cli.vcxproj +++ b/cli/cli.vcxproj @@ -407,7 +407,9 @@ + + @@ -418,8 +420,10 @@ + + diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 69aea810c..14d40bbb5 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -36,6 +36,12 @@ #include "utils.h" #include "checkunusedfunctions.h" +#if defined(THREADING_MODEL_THREAD) +#include "threadexecutor.h" +#elif defined(THREADING_MODEL_FORK) +#include "processexecutor.h" +#endif + #include #include #include @@ -972,8 +978,11 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck) } else if (!ThreadExecutor::isEnabled()) { std::cout << "No thread support yet implemented for this platform." << std::endl; } else { - // Multiple processes +#if defined(THREADING_MODEL_THREAD) ThreadExecutor executor(mFiles, settings, *this); +#elif defined(THREADING_MODEL_FORK) + ProcessExecutor executor(mFiles, settings, *this); +#endif returnValue = executor.check(); } diff --git a/cli/executor.cpp b/cli/executor.cpp new file mode 100644 index 000000000..762a39847 --- /dev/null +++ b/cli/executor.cpp @@ -0,0 +1,30 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "executor.h" + +Executor::Executor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) + : mFiles(files), mSettings(settings), mErrorLogger(errorLogger) +{} + +Executor::~Executor() +{} + +bool Executor::isEnabled() { + return true; +} diff --git a/cli/executor.h b/cli/executor.h new file mode 100644 index 000000000..866097c0b --- /dev/null +++ b/cli/executor.h @@ -0,0 +1,60 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef EXECUTOR_H +#define EXECUTOR_H + +#include +#include +#include +#include + +class Settings; +class ErrorLogger; + +/// @addtogroup CLI +/// @{ + +/** + * This class will take a list of filenames and settings and check then + * all files using threads. + */ +class Executor { +public: + Executor(const std::map &files, Settings &settings, ErrorLogger &errorLogger); + Executor(const Executor &) = delete; + virtual ~Executor(); + void operator=(const Executor &) = delete; + + virtual unsigned int check() = 0; + + /** + * @return true if support for threads exist. + */ + static bool isEnabled(); + +protected: + const std::map &mFiles; + Settings &mSettings; + ErrorLogger &mErrorLogger; + std::list mErrorList; +}; + +/// @} + +#endif // EXECUTOR_H diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp new file mode 100644 index 000000000..a248e078d --- /dev/null +++ b/cli/processexecutor.cpp @@ -0,0 +1,383 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "processexecutor.h" + +#if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) + +#include "color.h" +#include "config.h" +#include "cppcheck.h" +#include "cppcheckexecutor.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "importproject.h" +#include "settings.h" +#include "suppressions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SVR4 // Solaris +#include +#endif + +#if defined(__linux__) +#include +#endif + +// NOLINTNEXTLINE(misc-unused-using-decls) - required for FD_ZERO +using std::memset; + + +ProcessExecutor::ProcessExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) + : Executor(files, settings, errorLogger) +{} + +ProcessExecutor::~ProcessExecutor() +{} + +class PipeWriter : public ErrorLogger { +public: + enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', REPORT_VERIFICATION='4', CHILD_END='5'}; + + explicit PipeWriter(int pipe) : mWpipe(pipe) {} + + void reportOut(const std::string &outmsg, Color c) override { + writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset)); + } + + void reportErr(const ErrorMessage &msg) override { + report(msg, MessageType::REPORT_ERROR); + } + + void reportInfo(const ErrorMessage &msg) override { + report(msg, MessageType::REPORT_INFO); + } + + void writeEnd(const std::string& str) { + writeToPipe(CHILD_END, str); + } + +private: + enum class MessageType {REPORT_ERROR, REPORT_INFO}; + + void report(const ErrorMessage &msg, MessageType msgType) { + PipeSignal pipeSignal; + switch (msgType) { + case MessageType::REPORT_ERROR: + pipeSignal = REPORT_ERROR; + break; + case MessageType::REPORT_INFO: + pipeSignal = REPORT_INFO; + break; + } + + writeToPipe(pipeSignal, msg.serialize()); + } + + void writeToPipe(PipeSignal type, const std::string &data) + { + unsigned int len = static_cast(data.length() + 1); + char *out = new char[len + 1 + sizeof(len)]; + out[0] = static_cast(type); + std::memcpy(&(out[1]), &len, sizeof(len)); + std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len); + if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) { + delete[] out; + out = nullptr; + std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl; + std::exit(EXIT_FAILURE); + } + + delete[] out; + } + + const int mWpipe; +}; + +int ProcessExecutor::handleRead(int rpipe, unsigned int &result) +{ + char type = 0; + if (read(rpipe, &type, 1) <= 0) { + if (errno == EAGAIN) + return 0; + + // need to increment so a missing pipe (i.e. premature exit of forked process) results in an error exitcode + ++result; + return -1; + } + + if (type != PipeWriter::REPORT_OUT && type != PipeWriter::REPORT_ERROR && type != PipeWriter::REPORT_INFO && type != PipeWriter::CHILD_END) { + std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; + std::exit(EXIT_FAILURE); + } + + unsigned int len = 0; + if (read(rpipe, &len, sizeof(len)) <= 0) { + std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; + std::exit(EXIT_FAILURE); + } + + // Don't rely on incoming data being null-terminated. + // Allocate +1 element and null-terminate the buffer. + char *buf = new char[len + 1]; + const ssize_t readIntoBuf = read(rpipe, buf, len); + if (readIntoBuf <= 0) { + std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; + std::exit(EXIT_FAILURE); + } + buf[readIntoBuf] = 0; + + if (type == PipeWriter::REPORT_OUT) { + mErrorLogger.reportOut(buf); + } else if (type == PipeWriter::REPORT_ERROR || type == PipeWriter::REPORT_INFO) { + ErrorMessage msg; + try { + msg.deserialize(buf); + } catch (const InternalError& e) { + std::cerr << "#### ThreadExecutor::handleRead error, internal error:" << e.errorMessage << std::endl; + std::exit(EXIT_FAILURE); + } + + if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) { + // Alert only about unique errors + std::string errmsg = msg.toString(mSettings.verbose); + if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { + mErrorList.emplace_back(errmsg); + if (type == PipeWriter::REPORT_ERROR) + mErrorLogger.reportErr(msg); + else + mErrorLogger.reportInfo(msg); + } + } + } else if (type == PipeWriter::CHILD_END) { + std::istringstream iss(buf); + unsigned int fileResult = 0; + iss >> fileResult; + result += fileResult; + delete[] buf; + return -1; + } + + delete[] buf; + return 1; +} + +bool ProcessExecutor::checkLoadAverage(size_t nchildren) +{ +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__HAIKU__) // getloadavg() is unsupported on Cygwin, Qnx, Haiku. + (void)nchildren; + return true; +#else + if (!nchildren || !mSettings.loadAverage) { + return true; + } + + double sample(0); + if (getloadavg(&sample, 1) != 1) { + // disable load average checking on getloadavg error + return true; + } else if (sample < mSettings.loadAverage) { + return true; + } + return false; +#endif +} + +unsigned int ProcessExecutor::check() +{ + unsigned int fileCount = 0; + unsigned int result = 0; + + std::size_t totalfilesize = 0; + for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { + totalfilesize += i->second; + } + + std::list rpipes; + std::map childFile; + std::map pipeFile; + std::size_t processedsize = 0; + std::map::const_iterator iFile = mFiles.begin(); + std::list::const_iterator iFileSettings = mSettings.project.fileSettings.begin(); + for (;;) { + // Start a new child + size_t nchildren = childFile.size(); + if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) { + int pipes[2]; + if (pipe(pipes) == -1) { + std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl; + std::exit(EXIT_FAILURE); + } + + int flags = 0; + if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) { + std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl; + std::exit(EXIT_FAILURE); + } + + if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) { + std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; + std::exit(EXIT_FAILURE); + } + + pid_t pid = fork(); + if (pid < 0) { + // Error + std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl; + std::exit(EXIT_FAILURE); + } else if (pid == 0) { +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif + close(pipes[0]); + + PipeWriter pipewriter(pipes[1]); + CppCheck fileChecker(pipewriter, false, CppCheckExecutor::executeCommand); + fileChecker.settings() = mSettings; + unsigned int resultOfCheck = 0; + + if (iFileSettings != mSettings.project.fileSettings.end()) { + resultOfCheck = fileChecker.check(*iFileSettings); + } else { + // Read file from a file + resultOfCheck = fileChecker.check(iFile->first); + } + + std::ostringstream oss; + oss << resultOfCheck; + pipewriter.writeEnd(oss.str()); + std::exit(EXIT_SUCCESS); + } + + close(pipes[1]); + rpipes.push_back(pipes[0]); + if (iFileSettings != mSettings.project.fileSettings.end()) { + childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg; + pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg; + ++iFileSettings; + } else { + childFile[pid] = iFile->first; + pipeFile[pipes[0]] = iFile->first; + ++iFile; + } + } + if (!rpipes.empty()) { + fd_set rfds; + FD_ZERO(&rfds); + for (std::list::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp) + FD_SET(*rp, &rfds); + struct timeval tv; // for every second polling of load average condition + tv.tv_sec = 1; + tv.tv_usec = 0; + int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv); + + if (r > 0) { + std::list::iterator rp = rpipes.begin(); + while (rp != rpipes.end()) { + if (FD_ISSET(*rp, &rfds)) { + int readRes = handleRead(*rp, result); + if (readRes == -1) { + std::size_t size = 0; + std::map::iterator p = pipeFile.find(*rp); + if (p != pipeFile.end()) { + std::string name = p->second; + pipeFile.erase(p); + std::map::const_iterator fs = mFiles.find(name); + if (fs != mFiles.end()) { + size = fs->second; + } + } + + fileCount++; + processedsize += size; + if (!mSettings.quiet) + CppCheckExecutor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize); + + close(*rp); + rp = rpipes.erase(rp); + } else + ++rp; + } else + ++rp; + } + } + } + if (!childFile.empty()) { + int stat = 0; + pid_t child = waitpid(0, &stat, WNOHANG); + if (child > 0) { + std::string childname; + std::map::iterator c = childFile.find(child); + if (c != childFile.end()) { + childname = c->second; + childFile.erase(c); + } + + if (WIFEXITED(stat)) { + const int exitstatus = WEXITSTATUS(stat); + if (exitstatus != EXIT_SUCCESS) { + std::ostringstream oss; + oss << "Child process exited with " << exitstatus; + reportInternalChildErr(childname, oss.str()); + } + } else if (WIFSIGNALED(stat)) { + std::ostringstream oss; + oss << "Child process crashed with signal " << WTERMSIG(stat); + reportInternalChildErr(childname, oss.str()); + } + } + } + if (iFile == mFiles.end() && iFileSettings == mSettings.project.fileSettings.end() && rpipes.empty() && childFile.empty()) { + // All done + break; + } + } + + + return result; +} + +void ProcessExecutor::reportInternalChildErr(const std::string &childname, const std::string &msg) +{ + std::list locations; + locations.emplace_back(childname, 0, 0); + const ErrorMessage errmsg(locations, + emptyString, + Severity::error, + "Internal error: " + msg, + "cppcheckError", + Certainty::normal); + + if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage())) + mErrorLogger.reportErr(errmsg); +} + +#endif // !WIN32 diff --git a/cli/processexecutor.h b/cli/processexecutor.h new file mode 100644 index 000000000..b8ed621a4 --- /dev/null +++ b/cli/processexecutor.h @@ -0,0 +1,72 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PROCESSEXECUTOR_H +#define PROCESSEXECUTOR_H + +#include "executor.h" + +#include +#include +#include + +class Settings; +class ErrorLogger; + +/// @addtogroup CLI +/// @{ + +/** + * This class will take a list of filenames and settings and check then + * all files using threads. + */ +class ProcessExecutor : public Executor { +public: + ProcessExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger); + ProcessExecutor(const ProcessExecutor &) = delete; + ~ProcessExecutor(); + void operator=(const ProcessExecutor &) = delete; + + unsigned int check() override; + +private: + /** + * Read from the pipe, parse and handle what ever is in there. + *@return -1 in case of error + * 0 if there is nothing in the pipe to be read + * 1 if we did read something + */ + int handleRead(int rpipe, unsigned int &result); + + /** + * @brief Check load average condition + * @param nchildren - count of currently ran children + * @return true - if new process can be started + */ + bool checkLoadAverage(size_t nchildren); + + /** + * @brief Reports internal errors related to child processes + * @param msg The error message + */ + void reportInternalChildErr(const std::string &childname, const std::string &msg); +}; + +/// @} + +#endif // PROCESSEXECUTOR_H diff --git a/cli/threadexecutor.cpp b/cli/threadexecutor.cpp index 134155d41..ab5777450 100644 --- a/cli/threadexecutor.cpp +++ b/cli/threadexecutor.cpp @@ -29,379 +29,33 @@ #include #include #include -#include -#include - -#ifdef __SVR4 // Solaris -#include -#endif - -#ifdef THREADING_MODEL_FORK -#include "config.h" -#include "errortypes.h" - -#if defined(__linux__) -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -// NOLINTNEXTLINE(misc-unused-using-decls) - required for FD_ZERO -using std::memset; -#endif - -#ifdef THREADING_MODEL_THREAD #include +#include +#include #include -#endif +#include +#include +#include +#include ThreadExecutor::ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) - : mFiles(files), mSettings(settings), mErrorLogger(errorLogger) + : Executor(files, settings, errorLogger) {} ThreadExecutor::~ThreadExecutor() {} -/////////////////////////////////////////////////////////////////////////////// -////// This code is for platforms that support fork() only //////////////////// -/////////////////////////////////////////////////////////////////////////////// - -#if defined(THREADING_MODEL_FORK) - -class PipeWriter : public ErrorLogger { -public: - enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', REPORT_VERIFICATION='4', CHILD_END='5'}; - - explicit PipeWriter(int pipe) : mWpipe(pipe) {} - - void reportOut(const std::string &outmsg, Color c) override { - writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset)); - } - - void reportErr(const ErrorMessage &msg) override { - report(msg, MessageType::REPORT_ERROR); - } - - void reportInfo(const ErrorMessage &msg) override { - report(msg, MessageType::REPORT_INFO); - } - - void writeEnd(const std::string& str) { - writeToPipe(CHILD_END, str); - } - -private: - enum class MessageType {REPORT_ERROR, REPORT_INFO}; - - void report(const ErrorMessage &msg, MessageType msgType) { - PipeSignal pipeSignal; - switch (msgType) { - case MessageType::REPORT_ERROR: - pipeSignal = REPORT_ERROR; - break; - case MessageType::REPORT_INFO: - pipeSignal = REPORT_INFO; - break; - } - - writeToPipe(pipeSignal, msg.serialize()); - } - - void writeToPipe(PipeSignal type, const std::string &data) - { - unsigned int len = static_cast(data.length() + 1); - char *out = new char[len + 1 + sizeof(len)]; - out[0] = static_cast(type); - std::memcpy(&(out[1]), &len, sizeof(len)); - std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len); - if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) { - delete[] out; - out = nullptr; - std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl; - std::exit(EXIT_FAILURE); - } - - delete[] out; - } - - const int mWpipe; -}; - -int ThreadExecutor::handleRead(int rpipe, unsigned int &result) -{ - char type = 0; - if (read(rpipe, &type, 1) <= 0) { - if (errno == EAGAIN) - return 0; - - // need to increment so a missing pipe (i.e. premature exit of forked process) results in an error exitcode - ++result; - return -1; - } - - if (type != PipeWriter::REPORT_OUT && type != PipeWriter::REPORT_ERROR && type != PipeWriter::REPORT_INFO && type != PipeWriter::CHILD_END) { - std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; - std::exit(EXIT_FAILURE); - } - - unsigned int len = 0; - if (read(rpipe, &len, sizeof(len)) <= 0) { - std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; - std::exit(EXIT_FAILURE); - } - - // Don't rely on incoming data being null-terminated. - // Allocate +1 element and null-terminate the buffer. - char *buf = new char[len + 1]; - const ssize_t readIntoBuf = read(rpipe, buf, len); - if (readIntoBuf <= 0) { - std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; - std::exit(EXIT_FAILURE); - } - buf[readIntoBuf] = 0; - - if (type == PipeWriter::REPORT_OUT) { - mErrorLogger.reportOut(buf); - } else if (type == PipeWriter::REPORT_ERROR || type == PipeWriter::REPORT_INFO) { - ErrorMessage msg; - try { - msg.deserialize(buf); - } catch (const InternalError& e) { - std::cerr << "#### ThreadExecutor::handleRead error, internal error:" << e.errorMessage << std::endl; - std::exit(EXIT_FAILURE); - } - - if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) { - // Alert only about unique errors - std::string errmsg = msg.toString(mSettings.verbose); - if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { - mErrorList.emplace_back(errmsg); - if (type == PipeWriter::REPORT_ERROR) - mErrorLogger.reportErr(msg); - else - mErrorLogger.reportInfo(msg); - } - } - } else if (type == PipeWriter::CHILD_END) { - std::istringstream iss(buf); - unsigned int fileResult = 0; - iss >> fileResult; - result += fileResult; - delete[] buf; - return -1; - } - - delete[] buf; - return 1; -} - -bool ThreadExecutor::checkLoadAverage(size_t nchildren) -{ -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__HAIKU__) // getloadavg() is unsupported on Cygwin, Qnx, Haiku. - return true; -#else - if (!nchildren || !mSettings.loadAverage) { - return true; - } - - double sample(0); - if (getloadavg(&sample, 1) != 1) { - // disable load average checking on getloadavg error - return true; - } else if (sample < mSettings.loadAverage) { - return true; - } - return false; -#endif -} - -unsigned int ThreadExecutor::check() -{ - unsigned int fileCount = 0; - unsigned int result = 0; - - std::size_t totalfilesize = 0; - for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { - totalfilesize += i->second; - } - - std::list rpipes; - std::map childFile; - std::map pipeFile; - std::size_t processedsize = 0; - std::map::const_iterator iFile = mFiles.begin(); - std::list::const_iterator iFileSettings = mSettings.project.fileSettings.begin(); - for (;;) { - // Start a new child - size_t nchildren = childFile.size(); - if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) { - int pipes[2]; - if (pipe(pipes) == -1) { - std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl; - std::exit(EXIT_FAILURE); - } - - int flags = 0; - if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) { - std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl; - std::exit(EXIT_FAILURE); - } - - if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) { - std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; - std::exit(EXIT_FAILURE); - } - - pid_t pid = fork(); - if (pid < 0) { - // Error - std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl; - std::exit(EXIT_FAILURE); - } else if (pid == 0) { -#if defined(__linux__) - prctl(PR_SET_PDEATHSIG, SIGHUP); -#endif - close(pipes[0]); - - PipeWriter pipewriter(pipes[1]); - CppCheck fileChecker(pipewriter, false, CppCheckExecutor::executeCommand); - fileChecker.settings() = mSettings; - unsigned int resultOfCheck = 0; - - if (iFileSettings != mSettings.project.fileSettings.end()) { - resultOfCheck = fileChecker.check(*iFileSettings); - } else { - // Read file from a file - resultOfCheck = fileChecker.check(iFile->first); - } - - std::ostringstream oss; - oss << resultOfCheck; - pipewriter.writeEnd(oss.str()); - std::exit(EXIT_SUCCESS); - } - - close(pipes[1]); - rpipes.push_back(pipes[0]); - if (iFileSettings != mSettings.project.fileSettings.end()) { - childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg; - pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg; - ++iFileSettings; - } else { - childFile[pid] = iFile->first; - pipeFile[pipes[0]] = iFile->first; - ++iFile; - } - } - if (!rpipes.empty()) { - fd_set rfds; - FD_ZERO(&rfds); - for (std::list::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp) - FD_SET(*rp, &rfds); - struct timeval tv; // for every second polling of load average condition - tv.tv_sec = 1; - tv.tv_usec = 0; - int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv); - - if (r > 0) { - std::list::iterator rp = rpipes.begin(); - while (rp != rpipes.end()) { - if (FD_ISSET(*rp, &rfds)) { - int readRes = handleRead(*rp, result); - if (readRes == -1) { - std::size_t size = 0; - std::map::iterator p = pipeFile.find(*rp); - if (p != pipeFile.end()) { - std::string name = p->second; - pipeFile.erase(p); - std::map::const_iterator fs = mFiles.find(name); - if (fs != mFiles.end()) { - size = fs->second; - } - } - - fileCount++; - processedsize += size; - if (!mSettings.quiet) - CppCheckExecutor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize); - - close(*rp); - rp = rpipes.erase(rp); - } else - ++rp; - } else - ++rp; - } - } - } - if (!childFile.empty()) { - int stat = 0; - pid_t child = waitpid(0, &stat, WNOHANG); - if (child > 0) { - std::string childname; - std::map::iterator c = childFile.find(child); - if (c != childFile.end()) { - childname = c->second; - childFile.erase(c); - } - - if (WIFEXITED(stat)) { - const int exitstatus = WEXITSTATUS(stat); - if (exitstatus != EXIT_SUCCESS) { - std::ostringstream oss; - oss << "Child process exited with " << exitstatus; - reportInternalChildErr(childname, oss.str()); - } - } else if (WIFSIGNALED(stat)) { - std::ostringstream oss; - oss << "Child process crashed with signal " << WTERMSIG(stat); - reportInternalChildErr(childname, oss.str()); - } - } - } - if (iFile == mFiles.end() && iFileSettings == mSettings.project.fileSettings.end() && rpipes.empty() && childFile.empty()) { - // All done - break; - } - } - - - return result; -} - -void ThreadExecutor::reportInternalChildErr(const std::string &childname, const std::string &msg) -{ - std::list locations; - locations.emplace_back(childname, 0, 0); - const ErrorMessage errmsg(locations, - emptyString, - Severity::error, - "Internal error: " + msg, - "cppcheckError", - Certainty::normal); - - if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage())) - mErrorLogger.reportErr(errmsg); -} - -#elif defined(THREADING_MODEL_THREAD) - class ThreadExecutor::SyncLogForwarder : public ErrorLogger { public: - SyncLogForwarder(ThreadExecutor &threadExecutor) + explicit SyncLogForwarder(ThreadExecutor &threadExecutor) : mThreadExecutor(threadExecutor), mProcessedFiles(0), mTotalFiles(0), mProcessedSize(0), mTotalFileSize(0) { - mItNextFile = threadExecutor.mFiles.begin(); - mItNextFileSettings = threadExecutor.mSettings.project.fileSettings.begin(); + mItNextFile = mThreadExecutor.mFiles.begin(); + mItNextFileSettings = mThreadExecutor.mSettings.project.fileSettings.begin(); - mTotalFiles = threadExecutor.mFiles.size() + threadExecutor.mSettings.project.fileSettings.size(); - for (std::map::const_iterator i = threadExecutor.mFiles.begin(); i != threadExecutor.mFiles.end(); ++i) { + mTotalFiles = mThreadExecutor.mFiles.size() + mThreadExecutor.mSettings.project.fileSettings.size(); + for (std::map::const_iterator i = mThreadExecutor.mFiles.begin(); i != mThreadExecutor.mFiles.end(); ++i) { mTotalFileSize += i->second; } } @@ -541,8 +195,3 @@ unsigned int STDCALL ThreadExecutor::threadProc(SyncLogForwarder* logForwarder) } return result; } -#endif - -bool ThreadExecutor::isEnabled() { - return true; -} diff --git a/cli/threadexecutor.h b/cli/threadexecutor.h index 6bcf248ec..1637854bf 100644 --- a/cli/threadexecutor.h +++ b/cli/threadexecutor.h @@ -21,8 +21,9 @@ #include "config.h" +#include "executor.h" + #include -#include #include #include @@ -36,55 +37,18 @@ class ErrorLogger; * This class will take a list of filenames and settings and check then * all files using threads. */ -class ThreadExecutor { +class ThreadExecutor : public Executor { public: ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger); ThreadExecutor(const ThreadExecutor &) = delete; ~ThreadExecutor(); void operator=(const ThreadExecutor &) = delete; - unsigned int check(); + + unsigned int check() override; private: - const std::map &mFiles; - Settings &mSettings; - ErrorLogger &mErrorLogger; - std::list mErrorList; - -#if defined(THREADING_MODEL_FORK) - - /** - * Read from the pipe, parse and handle what ever is in there. - *@return -1 in case of error - * 0 if there is nothing in the pipe to be read - * 1 if we did read something - */ - int handleRead(int rpipe, unsigned int &result); - - /** - * @brief Check load average condition - * @param nchildren - count of currently ran children - * @return true - if new process can be started - */ - bool checkLoadAverage(size_t nchildren); - - /** - * @brief Reports internal errors related to child processes - * @param msg The error message - */ - void reportInternalChildErr(const std::string &childname, const std::string &msg); - -#elif defined(THREADING_MODEL_THREAD) - class SyncLogForwarder; - static unsigned int STDCALL threadProc(SyncLogForwarder *logforwarder); - -#endif - -public: - /** - * @return true if support for threads exist. - */ - static bool isEnabled(); + static unsigned int STDCALL threadProc(SyncLogForwarder *logForwarder); }; /// @} diff --git a/cmake/findDependencies.cmake b/cmake/findDependencies.cmake index 14cbd235a..b9d5052e4 100644 --- a/cmake/findDependencies.cmake +++ b/cmake/findDependencies.cmake @@ -68,9 +68,7 @@ if (NOT USE_BUNDLED_TINYXML2) endif() endif() -if (USE_THREADS) - find_package(Threads REQUIRED) -endif() +find_package(Threads REQUIRED) if (USE_BOOST) find_package(Boost COMPONENTS container QUIET) diff --git a/cmake/printInfo.cmake b/cmake/printInfo.cmake index 159d65f36..85a955016 100644 --- a/cmake/printInfo.cmake +++ b/cmake/printInfo.cmake @@ -52,9 +52,7 @@ if (HAVE_RULES) endif() message( STATUS ) message( STATUS "USE_THREADS = ${USE_THREADS}" ) -if (USE_THREADS) - message( STATUS "CMAKE_THREAD_LIBS_INIT = ${CMAKE_THREAD_LIBS_INIT}" ) -endif() +message( STATUS "CMAKE_THREAD_LIBS_INIT = ${CMAKE_THREAD_LIBS_INIT}" ) if (NOT USE_MATCHCOMPILER_OPT MATCHES "Off") message( STATUS ) message( STATUS "PYTHON_VERSION_STRING = ${PYTHON_VERSION_STRING}" ) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 232630992..39c3ff06b 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -28,6 +28,7 @@ #include "symboldatabase.h" #include "token.h" #include "tokenize.h" +#include "utils.h" #include "valueflow.h" #include "checkuninitvar.h" // CheckUninitVar::isVariableUsage diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 0b30b0376..da2587cd4 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -30,6 +30,7 @@ #include "tokenize.h" #include "tokenlist.h" #include "utils.h" +#include "valueflow.h" #include #include diff --git a/lib/config.h b/lib/config.h index dbe3aa908..87451e2a9 100644 --- a/lib/config.h +++ b/lib/config.h @@ -112,6 +112,7 @@ static const std::string emptyString; #define STDCALL #elif ((defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__) && !defined(__CYGWIN__)) || defined(__CPPCHECK__) #define THREADING_MODEL_FORK +#define STDCALL #else #error "No threading model defined" #endif diff --git a/lib/ctu.cpp b/lib/ctu.cpp index 08b3472f7..06dce57bc 100644 --- a/lib/ctu.cpp +++ b/lib/ctu.cpp @@ -29,6 +29,7 @@ #include "tokenlist.h" #include "utils.h" +#include #include #include #include // back_inserter diff --git a/lib/ctu.h b/lib/ctu.h index 7379fc398..1ab2a2f17 100644 --- a/lib/ctu.h +++ b/lib/ctu.h @@ -28,7 +28,6 @@ #include "mathlib.h" #include "valueflow.h" -#include #include #include #include diff --git a/lib/path.cpp b/lib/path.cpp index dc1642e90..b76ebaa88 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -23,7 +23,6 @@ #include "utils.h" #include -#include #include #include diff --git a/lib/pathmatch.cpp b/lib/pathmatch.cpp index 61a6b7f44..4c56db8f6 100644 --- a/lib/pathmatch.cpp +++ b/lib/pathmatch.cpp @@ -21,8 +21,6 @@ #include "path.h" #include "utils.h" -#include -#include #include PathMatch::PathMatch(const std::vector &excludedPaths, bool caseSensitive) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9e3de5f74..962874bfe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,9 +25,7 @@ if (BUILD_TESTS) if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) target_link_libraries(testrunner ${tinyxml2_LIBRARIES}) endif() - if (USE_THREADS) - target_link_libraries(testrunner ${CMAKE_THREAD_LIBS_INIT}) - endif() + target_link_libraries(testrunner ${CMAKE_THREAD_LIBS_INIT}) if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) target_precompile_headers(testrunner PRIVATE precompiled.h) diff --git a/test/testprocessexecutor.cpp b/test/testprocessexecutor.cpp new file mode 100644 index 000000000..8fc5459db --- /dev/null +++ b/test/testprocessexecutor.cpp @@ -0,0 +1,142 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "processexecutor.h" +#include "settings.h" +#include "testsuite.h" +#include "testutils.h" + +#include +#include +#include +#include +#include +#include +#include + +class TestProcessExecutor : public TestFixture { +public: + TestProcessExecutor() : TestFixture("TestProcessExecutor") {} + +private: + Settings settings; + + /** + * Execute check using n jobs for y files which are have + * identical data, given within data. + */ + void check(unsigned int jobs, int files, int result, const std::string &data) { + errout.str(""); + output.str(""); + + std::map filemap; + for (int i = 1; i <= files; ++i) { + std::ostringstream oss; + oss << "file_" << i << ".cpp"; + filemap[oss.str()] = data.size(); + } + + settings.jobs = jobs; + ProcessExecutor executor(filemap, settings, *this); + std::vector scopedfiles; + scopedfiles.reserve(filemap.size()); + for (std::map::const_iterator i = filemap.begin(); i != filemap.end(); ++i) + scopedfiles.emplace_back(i->first, data); + + ASSERT_EQUALS(result, executor.check()); + } + + void run() override { +#if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) + LOAD_LIB_2(settings.library, "std.cfg"); + + TEST_CASE(deadlock_with_many_errors); + TEST_CASE(many_threads); + TEST_CASE(no_errors_more_files); + TEST_CASE(no_errors_less_files); + TEST_CASE(no_errors_equal_amount_files); + TEST_CASE(one_error_less_files); + TEST_CASE(one_error_several_files); +#endif // !WIN32 + } + + void deadlock_with_many_errors() { + std::ostringstream oss; + oss << "int main()\n" + << "{\n"; + for (int i = 0; i < 500; i++) + oss << " {char *a = malloc(10);}\n"; + + oss << " return 0;\n" + << "}\n"; + check(2, 3, 3, oss.str()); + } + + void many_threads() { + check(16, 100, 100, + "int main()\n" + "{\n" + " char *a = malloc(10);\n" + " return 0;\n" + "}"); + } + + void no_errors_more_files() { + check(2, 3, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void no_errors_less_files() { + check(2, 1, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void no_errors_equal_amount_files() { + check(2, 2, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void one_error_less_files() { + check(2, 1, 1, + "int main()\n" + "{\n" + " {char *a = malloc(10);}\n" + " return 0;\n" + "}"); + } + + void one_error_several_files() { + check(2, 20, 20, + "int main()\n" + "{\n" + " {char *a = malloc(10);}\n" + " return 0;\n" + "}"); + } +}; + +REGISTER_TEST(TestProcessExecutor) diff --git a/test/testrunner.vcxproj b/test/testrunner.vcxproj index 10994701a..14bc3a539 100755 --- a/test/testrunner.vcxproj +++ b/test/testrunner.vcxproj @@ -27,6 +27,8 @@ + + @@ -64,6 +66,7 @@ + diff --git a/tools/dmake.cpp b/tools/dmake.cpp index a7d0df3b4..4c03bab41 100644 --- a/tools/dmake.cpp +++ b/tools/dmake.cpp @@ -315,6 +315,8 @@ int main(int argc, char **argv) << " endif # !CPPCHK_GLIBCXX_DEBUG\n" << " endif # GNU/kFreeBSD\n" << "\n" + << " LDFLAGS=-pthread\n" + << "\n" << "endif # WINNT\n" << "\n"; @@ -413,7 +415,7 @@ int main(int argc, char **argv) fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "all:\tcppcheck testrunner\n\n"; - fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n"; + fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "test:\tall\n"; fout << "\t./testrunner\n\n";