diff --git a/.github/workflows/cppcheck-premium.yml b/.github/workflows/cppcheck-premium.yml index 4a3ddf088e5..20c4fcf65ec 100644 --- a/.github/workflows/cppcheck-premium.yml +++ b/.github/workflows/cppcheck-premium.yml @@ -22,7 +22,8 @@ jobs: build: runs-on: ubuntu-22.04 # run on the latest image only - + # FIXME: enable after update + if: false steps: - uses: actions/checkout@v3 diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng index 5010c590ecf..1651a740c80 100644 --- a/cfg/cppcheck-cfg.rng +++ b/cfg/cppcheck-cfg.rng @@ -252,6 +252,11 @@ + + + + + diff --git a/cfg/std.cfg b/cfg/std.cfg index 0123a3aac12..0c9293c1cfa 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -6729,7 +6729,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun false - + diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 0493b1ab668..b2fc47fe4c7 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2500,7 +2500,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti if (!tok->function() && !tok->variable() && tok->isName()) { // Check if direction (in, out, inout) is specified in the library configuration and use that - const Library::ArgumentChecks::Direction argDirection = settings.library.getArgDirection(tok, 1 + argnr); + const Library::ArgumentChecks::Direction argDirection = settings.library.getArgDirection(tok, 1 + argnr, indirect); if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) return false; @@ -2843,9 +2843,16 @@ static bool isExpressionChangedAt(const F& getExprTok, if (!isMutableExpression(tok)) return false; if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) { - if (globalvar && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure())) - // TODO: Is global variable really changed by function call? - return true; + if (globalvar && Token::Match(tok, "%name% (") && + (!(tok->function() && (tok->function()->isAttributePure() || tok->function()->isAttributeConst())))) { + if (!Token::simpleMatch(tok->astParent(), ".")) + return true; + const auto yield = astContainerYield(tok->astParent()->astOperand1()); + if (yield != Library::Container::Yield::SIZE && yield != Library::Container::Yield::EMPTY && + yield != Library::Container::Yield::BUFFER && yield != Library::Container::Yield::BUFFER_NT) + // TODO: Is global variable really changed by function call? + return true; + } int i = 1; bool aliased = false; // If we can't find the expression then assume it is an alias diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 815259f775c..9e6638eb18d 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1638,6 +1638,9 @@ void CheckOther::checkConstPointer() if (p->isMaybeUnused()) continue; } + if (const Function* func = Scope::nestedInFunction(p->scope())) + if (func->templateDef) + continue; if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), p) == nonConstPointers.cend()) { // const Token *start = getVariableChangedStart(p); // const int indirect = p->isArray() ? p->dimensions().size() : 1; @@ -2912,7 +2915,7 @@ void CheckOther::checkRedundantCopy() if (Token::simpleMatch(dot, ".")) { const Token* varTok = dot->astOperand1(); const int indirect = varTok->valueType() ? varTok->valueType()->pointer : 0; - if (isVariableChanged(tok, tok->scope()->bodyEnd, varTok->varId(), indirect, /*globalvar*/ false, *mSettings)) + if (isVariableChanged(tok, tok->scope()->bodyEnd, indirect, varTok->varId(), /*globalvar*/ true, *mSettings)) continue; if (isTemporary(dot, &mSettings->library, /*unknown*/ true)) continue; diff --git a/lib/fwdanalysis.cpp b/lib/fwdanalysis.cpp index 990cb173591..500538efee3 100644 --- a/lib/fwdanalysis.cpp +++ b/lib/fwdanalysis.cpp @@ -361,12 +361,8 @@ FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token * while (argnr < args.size() && args[argnr] != parent) argnr++; if (argnr < args.size()) { - const Library::Function* functionInfo = mSettings.library.getFunction(ftok->astOperand1()); - if (functionInfo) { - const auto it = functionInfo->argumentChecks.find(argnr + 1); - if (it != functionInfo->argumentChecks.end() && it->second.direction == Library::ArgumentChecks::Direction::DIR_OUT) - continue; - } + if (mSettings.library.getArgDirection(ftok->astOperand1(), argnr + 1) == Library::ArgumentChecks::Direction::DIR_OUT) + continue; } } return Result(Result::Type::BAILOUT, parent->astParent()); diff --git a/lib/library.cpp b/lib/library.cpp index 0c2df2145b2..4d6ae5a4eb3 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -738,13 +738,20 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co const char * const argDirection = functionnode->Attribute("direction"); if (argDirection) { const size_t argDirLen = strlen(argDirection); + ArgumentChecks::Direction dir = ArgumentChecks::Direction::DIR_UNKNOWN; if (!strncmp(argDirection, "in", argDirLen)) { - ac.direction = ArgumentChecks::Direction::DIR_IN; + dir = ArgumentChecks::Direction::DIR_IN; } else if (!strncmp(argDirection, "out", argDirLen)) { - ac.direction = ArgumentChecks::Direction::DIR_OUT; + dir = ArgumentChecks::Direction::DIR_OUT; } else if (!strncmp(argDirection, "inout", argDirLen)) { - ac.direction = ArgumentChecks::Direction::DIR_INOUT; + dir = ArgumentChecks::Direction::DIR_INOUT; } + if (const char* const argIndirect = functionnode->Attribute("indirect")) { + const int indirect = strToInt(argIndirect); + ac.direction[indirect] = dir; // TODO: handle multiple directions/indirect levels + } + else + ac.direction.fill(dir); } for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { const std::string argnodename = argnode->Name(); @@ -1500,11 +1507,14 @@ bool Library::hasminsize(const Token *ftok) const }); } -Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const +Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr, int indirect) const { const ArgumentChecks* arg = getarg(ftok, argnr); - if (arg) - return arg->direction; + if (arg) { + if (indirect < 0 || indirect >= arg->direction.size()) + throw InternalError(ftok, "Bad indirect value: " + std::to_string(indirect)); + return arg->direction[indirect]; + } if (formatstr_function(ftok)) { const int fs_argno = formatstr_argno(ftok); if (fs_argno >= 0 && argnr >= fs_argno) { diff --git a/lib/library.h b/lib/library.h index a8b9d61b9d8..0dc600b994b 100644 --- a/lib/library.h +++ b/lib/library.h @@ -291,7 +291,8 @@ class CPPCHECKLIB Library { DIR_INOUT, ///< Input to called function, and output to caller. Data is passed by reference or address and is potentially modified. DIR_UNKNOWN ///< direction not known / specified }; - Direction direction = Direction::DIR_UNKNOWN; + // argument directions up to ** indirect level (only one can be configured explicitly at the moment) + std::array direction = { { Direction::DIR_UNKNOWN, Direction::DIR_UNKNOWN, Direction::DIR_UNKNOWN } }; }; struct Function { @@ -355,7 +356,7 @@ class CPPCHECKLIB Library { return arg ? &arg->minsizes : nullptr; } - ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr) const; + ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr, int indirect = 0) const; bool markupFile(const std::string &path) const; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index b78b280ac1d..d22b4be75df 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -1747,6 +1747,12 @@ struct ValueFlowAnalyzer : Analyzer { indirect = vt->pointer; if (vt->type == ValueType::ITERATOR) ++indirect; + const Token* tok2 = tok; + while (Token::simpleMatch(tok2->astParent(), "[")) { + tok2 = tok2->astParent(); + --indirect; + } + indirect = std::max(indirect, 0); } } for (int i = 0; i <= indirect; ++i) diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 7110600e738..0e1f205d2cb 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -5053,3 +5053,20 @@ void assertWithSideEffect_std_prev_next(const std::vector& v, std::vector containerOutOfBounds_push_back() { // #12775 + std::vector v; + for (int i = 0; i < 4; ++i) { + v.push_back(i); + (void)v[i]; + } + return v; +} + +template +void constVariablePointer_push_back(std::vector& d, const std::vector& s) { + for (const auto& e : s) { + T* newE = new T(*e); + d.push_back(newE); + } +}