/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2026 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 "astutils.h"
#include "config.h"
#include "errortypes.h"
#include "findtoken.h"
#include "infer.h"
#include "library.h"
#include "mathlib.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
#include "utils.h"
#include "valueflow.h"
#include "valueptr.h"
#include "vfvalue.h"
#include "checkclass.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const Token* findExpression(const nonneg int exprid,
const Token* start,
const Token* end,
const std::function& pred)
{
if (exprid == 0)
return nullptr;
if (!precedes(start, end))
return nullptr;
for (const Token* tok = start; tok != end; tok = tok->next()) {
if (tok->exprId() != exprid)
continue;
if (pred(tok))
return tok;
}
return nullptr;
}
static int findArgumentPosRecursive(const Token* tok, const Token* tokToFind, bool &found, nonneg int depth=0)
{
++depth;
if (!tok || depth >= 100)
return -1;
if (tok->str() == ",") {
int res = findArgumentPosRecursive(tok->astOperand1(), tokToFind, found, depth);
if (res == -1)
return -1;
if (found)
return res;
const int argn = res;
res = findArgumentPosRecursive(tok->astOperand2(), tokToFind, found, depth);
if (res == -1)
return -1;
return argn + res;
}
if (tokToFind == tok)
found = true;
return 1;
}
static int findArgumentPos(const Token* tok, const Token* tokToFind){
bool found = false;
const int argn = findArgumentPosRecursive(tok, tokToFind, found, 0);
if (found)
return argn - 1;
return -1;
}
static int getArgumentPos(const Token* ftok, const Token* tokToFind){
const Token* tok = ftok;
if (Token::Match(tok, "%name% (|{"))
tok = ftok->next();
if (!Token::Match(tok, "(|{|["))
return -1;
const Token* startTok = tok->astOperand2();
if (!startTok && tok->next() != tok->link())
startTok = tok->astOperand1();
return findArgumentPos(startTok, tokToFind);
}
template
static void astFlattenCopy(T* tok, const char* op, OuputIterator out, int depth = 100)
{
--depth;
if (!tok || depth < 0)
return;
if (strcmp(tok->str().c_str(), op) == 0) {
astFlattenCopy(tok->astOperand1(), op, out, depth);
astFlattenCopy(tok->astOperand2(), op, out, depth);
} else {
*out = tok;
++out;
}
}
std::vector astFlatten(const Token* tok, const char* op)
{
std::vector result;
astFlattenCopy(tok, op, std::back_inserter(result));
return result;
}
std::vector astFlatten(Token* tok, const char* op)
{
std::vector result;
astFlattenCopy(tok, op, std::back_inserter(result));
return result;
}
nonneg int astCount(const Token* tok, const char* op, int depth)
{
--depth;
if (!tok || depth < 0)
return 0;
if (strcmp(tok->str().c_str(), op) == 0)
return astCount(tok->astOperand1(), op, depth) + astCount(tok->astOperand2(), op, depth);
return 1;
}
bool astHasToken(const Token* root, const Token * tok)
{
if (!root)
return false;
while (tok->astParent() && tok != root)
tok = tok->astParent();
return root == tok;
}
bool astHasVar(const Token * tok, nonneg int varid)
{
if (!tok)
return false;
if (tok->varId() == varid)
return true;
return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid);
}
bool astHasExpr(const Token* tok, nonneg int exprid)
{
if (!tok)
return false;
if (tok->exprId() == exprid)
return true;
return astHasExpr(tok->astOperand1(), exprid) || astHasExpr(tok->astOperand2(), exprid);
}
static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign)
{
if (!tok)
return false;
const ValueType *valueType = tok->valueType();
if (!valueType)
return false;
return valueType->type == ValueType::Type::CHAR && valueType->pointer == 0U && valueType->sign == sign;
}
bool astIsSignedChar(const Token *tok)
{
return astIsCharWithSign(tok, ValueType::Sign::SIGNED);
}
bool astIsUnknownSignChar(const Token *tok)
{
return astIsCharWithSign(tok, ValueType::Sign::UNKNOWN_SIGN);
}
bool astIsGenericChar(const Token* tok)
{
return !astIsPointer(tok) && tok && tok->valueType() && (tok->valueType()->type == ValueType::Type::CHAR || tok->valueType()->type == ValueType::Type::WCHAR_T);
}
bool astIsPrimitive(const Token* tok)
{
const ValueType* vt = tok ? tok->valueType() : nullptr;
if (!vt)
return false;
return vt->isPrimitive();
}
bool astIsIntegral(const Token *tok, bool unknown)
{
const ValueType *vt = tok ? tok->valueType() : nullptr;
if (!vt)
return unknown;
return vt->isIntegral() && vt->pointer == 0U;
}
bool astIsUnsigned(const Token* tok)
{
return tok && tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED;
}
bool astIsFloat(const Token *tok, bool unknown)
{
const ValueType *vt = tok ? tok->valueType() : nullptr;
if (!vt)
return unknown;
return vt->type >= ValueType::Type::FLOAT && vt->pointer == 0U;
}
bool astIsBool(const Token *tok)
{
return tok && (tok->isBoolean() || (tok->valueType() && tok->valueType()->type == ValueType::Type::BOOL && !tok->valueType()->pointer));
}
bool astIsPointer(const Token *tok)
{
return tok && tok->valueType() && tok->valueType()->pointer;
}
bool astIsSmartPointer(const Token* tok)
{
return tok && tok->valueType() && tok->valueType()->smartPointerTypeToken;
}
bool astIsUniqueSmartPointer(const Token* tok)
{
if (!astIsSmartPointer(tok))
return false;
if (!tok->valueType()->smartPointer)
return false;
return tok->valueType()->smartPointer->unique;
}
bool astIsIterator(const Token *tok)
{
return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR;
}
bool astIsContainer(const Token* tok) {
return getLibraryContainer(tok) != nullptr && !astIsIterator(tok);
}
bool astIsNonStringContainer(const Token* tok)
{
const Library::Container* container = getLibraryContainer(tok);
return container && !container->stdStringLike && !astIsIterator(tok);
}
bool astIsContainerView(const Token* tok)
{
const Library::Container* container = getLibraryContainer(tok);
return container && !astIsIterator(tok) && container->view;
}
bool astIsContainerOwned(const Token* tok) {
return astIsContainer(tok) && !astIsContainerView(tok);
}
bool astIsContainerString(const Token* tok)
{
if (!tok)
return false;
if (!tok->valueType())
return false;
const Library::Container* container = tok->valueType()->container;
if (!container)
return false;
return container->stdStringLike;
}
static std::pair getContainerFunction(const Token* tok, const Library& library)
{
const Library::Container* cont{};
if (!tok || !tok->valueType() || (!tok->valueType()->container && (!(cont = library.detectContainerOrIterator(tok->valueType()->smartPointerTypeToken)))))
return {};
const Token* parent = tok->astParent();
if (Token::Match(parent, ". %name% [( ::"));
}
bool isVoidCast(const Token* tok)
{
return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() &&
tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0;
}
bool isTemporary(const Token* tok, const Library* library, bool unknown)
{
if (!tok)
return false;
if (Token::simpleMatch(tok, "."))
return (tok->originalName() != "->" && isTemporary(tok->astOperand1(), library)) ||
isTemporary(tok->astOperand2(), library);
if (Token::Match(tok, ",|::"))
return isTemporary(tok->astOperand2(), library);
if (tok->isCast() || (tok->isCpp() && isCPPCast(tok)))
return isTemporary(tok->astOperand2(), library);
if (findLambdaEndToken(tok) != nullptr)
return true;
if (Token::Match(tok, ".|[|++|--|%name%|%assign%"))
return false;
if (tok->isUnaryOp("*"))
return false;
if (Token::Match(tok, "&|") && isLikelyStream(tok->astOperand1()))
return false;
if (Token::simpleMatch(tok, "?")) {
const Token* branchTok = tok->astOperand2();
if (!branchTok->astOperand1() || !branchTok->astOperand1()->valueType())
return false;
if (!branchTok->astOperand2()->valueType())
return false;
return !branchTok->astOperand1()->valueType()->isTypeEqual(branchTok->astOperand2()->valueType());
}
if (Token::Match(tok, "(|{") && tok->astOperand1() &&
(tok->astOperand2() || Token::simpleMatch(tok->next(), ")"))) {
if (Token::simpleMatch(tok->astOperand1(), "typeid"))
return false;
if (tok->valueType()) {
if (tok->valueType()->pointer > 0) {
const Token* const parent = tok->astParent();
if (Token::simpleMatch(parent, "&"))
return true;
if (Token::simpleMatch(parent, "return") && parent->valueType()->reference != Reference::None &&
parent->valueType()->container && parent->valueType()->container->stdStringLike)
return true;
}
return tok->valueType()->reference == Reference::None && tok->valueType()->pointer == 0;
}
const Token* ftok = nullptr;
if (Token::simpleMatch(tok->previous(), ">") && tok->linkAt(-1))
ftok = tok->linkAt(-1)->previous();
else
ftok = tok->previous();
if (!ftok)
return false;
if (const Function * f = ftok->function())
return !Function::returnsReference(f, true);
if (ftok->type())
return true;
if (library) {
const std::string& returnType = library->returnValueType(ftok);
return !returnType.empty() && returnType.back() != '&';
}
return unknown;
}
// Currying a function is unknown in cppcheck
if (Token::simpleMatch(tok, "(") && Token::simpleMatch(tok->astOperand1(), "("))
return unknown;
return true;
}
static bool isFunctionCall(const Token* tok)
{
if (Token::Match(tok, "%name% ("))
return true;
if (Token::Match(tok, "%name% ("))
return true;
if (Token::Match(tok, "%name% ::"))
return isFunctionCall(tok->tokAt(2));
return false;
}
static bool hasToken(const Token * startTok, const Token * stopTok, const Token * tok)
{
for (const Token * tok2 = startTok; tok2 != stopTok; tok2 = tok2->next()) {
if (tok2 == tok)
return true;
}
return false;
}
template
static T* previousBeforeAstLeftmostLeafGeneric(T* tok)
{
if (!tok)
return nullptr;
T* leftmostLeaf = tok;
while (leftmostLeaf->astOperand1())
leftmostLeaf = leftmostLeaf->astOperand1();
return leftmostLeaf->previous();
}
const Token* previousBeforeAstLeftmostLeaf(const Token* tok)
{
return previousBeforeAstLeftmostLeafGeneric(tok);
}
Token* previousBeforeAstLeftmostLeaf(Token* tok)
{
return previousBeforeAstLeftmostLeafGeneric(tok);
}
template
static T* nextAfterAstRightmostLeafGeneric(T* tok)
{
T * rightmostLeaf = tok;
if (!rightmostLeaf || !rightmostLeaf->astOperand1())
return nullptr;
do {
if (T* lam = findLambdaEndToken(rightmostLeaf)) {
rightmostLeaf = lam;
break;
}
if (rightmostLeaf->astOperand2() && precedes(rightmostLeaf, rightmostLeaf->astOperand2()))
rightmostLeaf = rightmostLeaf->astOperand2();
else if (rightmostLeaf->astOperand1() && precedes(rightmostLeaf, rightmostLeaf->astOperand1()))
rightmostLeaf = rightmostLeaf->astOperand1();
else
break;
} while (rightmostLeaf->astOperand1() || rightmostLeaf->astOperand2());
while (Token::Match(rightmostLeaf->next(), "]|)") && !hasToken(rightmostLeaf->linkAt(1), rightmostLeaf->next(), tok))
rightmostLeaf = rightmostLeaf->next();
if (Token::Match(rightmostLeaf, "{|(|[") && rightmostLeaf->link())
rightmostLeaf = rightmostLeaf->link();
return rightmostLeaf->next();
}
const Token* nextAfterAstRightmostLeaf(const Token* tok)
{
return nextAfterAstRightmostLeafGeneric(tok);
}
Token* nextAfterAstRightmostLeaf(Token* tok)
{
return nextAfterAstRightmostLeafGeneric(tok);
}
const Token* astParentSkipParens(const Token* tok)
{
return astParentSkipParens(const_cast(tok));
}
Token* astParentSkipParens(Token* tok)
{
if (!tok)
return nullptr;
Token * parent = tok->astParent();
if (!Token::simpleMatch(parent, "("))
return parent;
if (parent->link() != nextAfterAstRightmostLeaf(tok))
return parent;
if (Token::Match(parent->previous(), "%name% (") ||
(Token::simpleMatch(parent->previous(), "> (") && parent->linkAt(-1)))
return parent;
return astParentSkipParens(parent);
}
const Token* getParentMember(const Token * tok)
{
if (!tok)
return tok;
const Token * parent = tok->astParent();
if (!Token::simpleMatch(parent, "."))
return tok;
if (astIsRHS(tok)) {
if (Token::simpleMatch(parent->astOperand1(), "."))
return parent->astOperand1()->astOperand2();
return parent->astOperand1();
}
const Token * gparent = parent->astParent();
if (!Token::simpleMatch(gparent, ".") || gparent->astOperand2() != parent)
return tok;
if (gparent->astOperand1())
return gparent->astOperand1();
return tok;
}
const Token* getParentLifetime(const Token* tok)
{
if (!tok)
return tok;
// Skipping checking for variable if its a pointer-to-member
if (!Token::simpleMatch(tok->previous(), ". *")) {
const Variable* var = tok->variable();
// TODO: Call getLifetimeVariable for deeper analysis
if (!var)
return tok;
if (var->isLocal() || var->isArgument())
return tok;
}
const Token* parent = getParentMember(tok);
if (parent != tok)
return getParentLifetime(parent);
return tok;
}
static std::vector getParentMembers(const Token* tok)
{
if (!tok)
return {};
if (!Token::simpleMatch(tok->astParent(), "."))
return {tok};
const Token* parent = tok->astParent();
while (Token::simpleMatch(parent->astParent(), "."))
parent = parent->astParent();
std::vector result;
for (const Token* tok2 : astFlatten(parent, ".")) {
if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->astOperand1(), ".")) {
std::vector sub = getParentMembers(tok2->astOperand1());
result.insert(result.end(), sub.cbegin(), sub.cend());
}
result.push_back(tok2);
}
return result;
}
static const Token* getParentLifetimeObject(const Token* tok)
{
while (Token::simpleMatch(tok, "["))
tok = tok->astOperand1();
return tok;
}
const Token* getParentLifetime(const Token* tok, const Library& library)
{
std::vector members = getParentMembers(tok);
if (members.size() < 2)
return tok;
// Find the first local variable, temporary, or array
auto it = std::find_if(members.crbegin(), members.crend(), [&](const Token* tok2) {
const Variable* var = tok2->variable();
if (var)
return var->isLocal() || var->isArgument();
if (Token::simpleMatch(tok2, "["))
return true;
return isTemporary(tok2, &library);
});
if (it == members.rend())
return tok;
// If any of the submembers are borrowed types then stop
if (std::any_of(it.base() - 1, members.cend() - 1, [&](const Token* tok2) {
const Token* obj = getParentLifetimeObject(tok2);
if (!obj)
return false;
const Variable* var = obj->variable();
// Check for arrays first since astIsPointer will return true, but an array is not a borrowed type
if (var && var->isArray())
return false;
if (astIsPointer(obj) || astIsContainerView(obj) || astIsIterator(obj))
return true;
if (!astIsUniqueSmartPointer(obj)) {
if (astIsSmartPointer(obj))
return true;
const Token* dotTok = obj->next();
if (!Token::simpleMatch(dotTok, ".")) {
const Token* endTok = nextAfterAstRightmostLeaf(obj);
if (!endTok)
dotTok = obj->next();
else if (Token::simpleMatch(endTok, "."))
dotTok = endTok;
else if (Token::simpleMatch(endTok->next(), "."))
dotTok = endTok->next();
}
// If we are dereferencing the member variable then treat it as borrowed
if (Token::simpleMatch(dotTok, ".") && dotTok->originalName() == "->")
return true;
}
return var && var->isReference();
}))
return nullptr;
const Token* result = getParentLifetimeObject(*it);
if (result != *it)
return getParentLifetime(result);
return result;
}
static bool isInConstructorList(const Token* tok)
{
if (!tok)
return false;
if (!astIsRHS(tok))
return false;
const Token* parent = tok->astParent();
if (!Token::Match(parent, "{|("))
return false;
if (!Token::Match(parent->previous(), "%var% {|("))
return false;
if (!parent->astOperand1() || !parent->astOperand2())
return false;
do {
parent = parent->astParent();
} while (Token::simpleMatch(parent, ","));
return Token::simpleMatch(parent, ":") && !Token::simpleMatch(parent->astParent(), "?");
}
std::vector getParentValueTypes(const Token* tok, const Settings& settings, const Token** parent)
{
if (!tok)
return {};
if (!tok->astParent())
return {};
if (isInConstructorList(tok)) {
if (parent)
*parent = tok->astParent()->astOperand1();
if (tok->astParent()->astOperand1()->valueType())
return {*tok->astParent()->astOperand1()->valueType()};
return {};
}
const Token* ftok = nullptr;
if (Token::Match(tok->astParent(), "(|{|,")) {
int argn = -1;
ftok = getTokenArgumentFunction(tok, argn);
const Token* typeTok = nullptr;
if (ftok && argn >= 0) {
if (ftok->function()) {
std::vector result;
const Token* nameTok = nullptr;
for (const Variable* var : getArgumentVars(ftok, argn)) {
if (!var)
continue;
if (!var->valueType())
continue;
nameTok = var->nameToken();
result.push_back(*var->valueType());
if (var->isArray())
result.back().pointer += var->dimensions().size();
}
if (result.size() == 1 && nameTok && parent) {
*parent = nameTok;
}
return result;
}
if (const Type* t = Token::typeOf(ftok, &typeTok)) {
if (astIsPointer(typeTok))
return {*typeTok->valueType()};
const Scope* scope = t->classScope;
// Check for aggregate constructors
if (scope && scope->numConstructors == 0 && t->derivedFrom.empty() &&
(t->isClassType() || t->isStructType()) && numberOfArguments(ftok) varlist.size() &&
!scope->varlist.empty()) {
if (argn < scope->varlist.size()) {
auto it = std::next(scope->varlist.cbegin(), argn);
if (it->valueType())
return { *it->valueType() };
}
}
}
}
}
if (Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") &&
astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) {
const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1();
const ValueType* vtCont = contTok->valueType();
if (!vtCont->containerTypeToken)
return {};
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings);
return {std::move(vtParent)};
}
// The return type of a function is not the parent valuetype
if (Token::simpleMatch(tok->astParent(), "(") && ftok && !tok->astParent()->isCast() &&
ftok->tokType() != Token::eType)
return {};
if (parent && Token::Match(tok->astParent(), "return|(|{|%assign%")) {
*parent = tok->astParent();
}
if (tok->astParent()->valueType())
return {*tok->astParent()->valueType()};
return {};
}
bool astIsLHS(const Token* tok)
{
if (!tok)
return false;
const Token* parent = tok->astParent();
if (!parent)
return false;
if (!parent->astOperand1())
return false;
if (!parent->astOperand2())
return false;
return parent->astOperand1() == tok;
}
bool astIsRHS(const Token* tok)
{
if (!tok)
return false;
const Token* parent = tok->astParent();
if (!parent)
return false;
if (!parent->astOperand1())
return false;
if (!parent->astOperand2())
return false;
return parent->astOperand2() == tok;
}
template
static T* getCondTokImpl(T* tok)
{
if (!tok)
return nullptr;
if (Token::simpleMatch(tok, "("))
return getCondTok(tok->previous());
if (Token::simpleMatch(tok, "do {")) {
T* endTok = tok->linkAt(1);
if (Token::simpleMatch(endTok, "} while ("))
return endTok->tokAt(2)->astOperand2();
}
if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") &&
tok->next()->astOperand2()->astOperand2())
return tok->next()->astOperand2()->astOperand2()->astOperand1();
if (Token::simpleMatch(tok->next()->astOperand2(), ";"))
return tok->next()->astOperand2()->astOperand1();
if (tok->isName() && !tok->isControlFlowKeyword())
return nullptr;
return tok->next()->astOperand2();
}
template
static T* getCondTokFromEndImpl(T* endBlock)
{
if (!Token::simpleMatch(endBlock, "}"))
return nullptr;
T* startBlock = endBlock->link();
if (!Token::simpleMatch(startBlock, "{"))
return nullptr;
if (Token::simpleMatch(startBlock->previous(), "do"))
return getCondTok(startBlock->previous());
if (Token::simpleMatch(startBlock->previous(), ")"))
return getCondTok(startBlock->linkAt(-1));
if (Token::simpleMatch(startBlock->tokAt(-2), "} else {"))
return getCondTokFromEnd(startBlock->tokAt(-2));
return nullptr;
}
template
static T* getInitTokImpl(T* tok)
{
if (!tok)
return nullptr;
if (Token::Match(tok, "%name% ("))
return getInitTokImpl(tok->next());
if (tok->str() != "(")
return nullptr;
if (!Token::simpleMatch(tok->astOperand2(), ";"))
return nullptr;
if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";"))
return nullptr;
return tok->astOperand2()->astOperand1();
}
template
static T* getStepTokImpl(T* tok)
{
if (!tok)
return nullptr;
if (Token::Match(tok, "%name% ("))
return getStepTokImpl(tok->next());
if (tok->str() != "(")
return nullptr;
if (!Token::simpleMatch(tok->astOperand2(), ";"))
return nullptr;
if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";"))
return nullptr;
return tok->astOperand2()->astOperand2()->astOperand2();
}
Token* getCondTok(Token* tok)
{
return getCondTokImpl(tok);
}
const Token* getCondTok(const Token* tok)
{
return getCondTokImpl(tok);
}
Token* getCondTokFromEnd(Token* endBlock)
{
return getCondTokFromEndImpl(endBlock);
}
const Token* getCondTokFromEnd(const Token* endBlock)
{
return getCondTokFromEndImpl(endBlock);
}
Token* getInitTok(Token* tok) {
return getInitTokImpl(tok);
}
const Token* getInitTok(const Token* tok) {
return getInitTokImpl(tok);
}
Token* getStepTok(Token* tok) {
return getStepTokImpl(tok);
}
const Token* getStepTok(const Token* tok) {
return getStepTokImpl(tok);
}
const Token *findNextTokenFromBreak(const Token *breakToken)
{
const Scope *scope = breakToken->scope();
while (scope) {
if (scope->isLoopScope() || scope->type == ScopeType::eSwitch) {
if (scope->type == ScopeType::eDo && Token::simpleMatch(scope->bodyEnd, "} while ("))
return scope->bodyEnd->linkAt(2)->next();
return scope->bodyEnd;
}
scope = scope->nestedIn;
}
return nullptr;
}
bool extractForLoopValues(const Token *forToken,
nonneg int &varid,
bool &knownInitValue,
MathLib::bigint &initValue,
bool &partialCond,
MathLib::bigint &stepValue,
MathLib::bigint &lastValue)
{
if (!Token::simpleMatch(forToken, "for (") || !Token::simpleMatch(forToken->next()->astOperand2(), ";"))
return false;
const Token *initExpr = forToken->next()->astOperand2()->astOperand1();
const Token *condExpr = forToken->next()->astOperand2()->astOperand2()->astOperand1();
const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2();
if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%"))
return false;
std::vector minInitValue =
getMinValue(makeIntegralInferModel(), initExpr->astOperand2()->values());
if (minInitValue.empty()) {
const ValueFlow::Value* v = initExpr->astOperand2()->getMinValue(true);
if (v)
minInitValue.push_back(v->intvalue);
}
if (minInitValue.empty())
return false;
varid = initExpr->astOperand1()->varId();
knownInitValue = initExpr->astOperand2()->hasKnownIntValue();
initValue = minInitValue.front();
partialCond = Token::Match(condExpr, "%oror%|&&");
visitAstNodes(condExpr, [varid, &condExpr](const Token *tok) {
if (Token::Match(tok, "%oror%|&&"))
return ChildrenToVisit::op1_and_op2;
if (Token::Match(tok, " (") &&
!(tok1->function() && tok1->function()->isConst()) &&
tok1->str() != "dynamic_cast")
return false;
// some template/cast stuff.. check that the template arguments are same
const Token *t1 = tok1->next();
const Token *t2 = tok2->next();
const Token *end1 = t1->link();
const Token *end2 = t2->link();
while (t1 && t2 && t1 != end1 && t2 != end2) {
if (t1->str() != t2->str() || !compareTokenFlags(t1, t2, macro))
return false;
t1 = t1->next();
t2 = t2->next();
}
if (t1 != end1 || t2 != end2)
return false;
}
if (tok1->tokType() == Token::eIncDecOp || tok1->isAssignmentOp())
return false;
// bailout when we see ({..})
if (tok1->str() == "{")
return false;
// cast => assert that the casts are equal
if (tok1->str() == "(" && tok1->previous() &&
!tok1->previous()->isName() &&
!(tok1->strAt(-1) == ">" && tok1->linkAt(-1))) {
const Token *t1 = tok1->next();
const Token *t2 = tok2->next();
while (t1 && t2 &&
t1->str() == t2->str() &&
compareTokenFlags(t1, t2, macro) &&
(t1->isName() || t1->str() == "*")) {
t1 = t1->next();
t2 = t2->next();
}
if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")")
return false;
}
bool noncommutativeEquals =
isSameExpression(macro, tok1->astOperand1(), tok2->astOperand1(), settings, pure, followVar, errors);
noncommutativeEquals = noncommutativeEquals &&
isSameExpression(macro, tok1->astOperand2(), tok2->astOperand2(), settings, pure, followVar, errors);
if (noncommutativeEquals)
return true;
// in c++, a+b might be different to b+a, depending on the type of a and b
if (tok1->isCpp() && tok1->str() == "+" && tok1->isBinaryOp()) {
const ValueType* vt1 = tok1->astOperand1()->valueType();
const ValueType* vt2 = tok1->astOperand2()->valueType();
if (!(vt1 && (vt1->type >= ValueType::VOID || vt1->pointer) && vt2 && (vt2->type >= ValueType::VOID || vt2->pointer)))
return false;
}
const bool commutative = tok1->isBinaryOp() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!=");
bool commutativeEquals = commutative &&
isSameExpression(macro, tok1->astOperand2(), tok2->astOperand1(), settings, pure, followVar, errors);
commutativeEquals = commutativeEquals &&
isSameExpression(macro, tok1->astOperand1(), tok2->astOperand2(), settings, pure, followVar, errors);
return commutativeEquals;
}
static bool isZeroBoundCond(const Token * const cond, bool reverse)
{
if (cond == nullptr || !cond->isBinaryOp())
return false;
const Token* op = reverse ? cond->astOperand1() : cond->astOperand2();
if (!op->hasKnownIntValue())
return true;
// Assume unsigned
const bool isZero = op->getKnownIntValue() == 0;
std::string cmp = cond->str();
if (reverse) {
if (cmp[0] == '>')
cmp[0] = '')
comp2[0] = '')
op1[0] = '')
op2[0] = 'tokType() == Token::eType && astIsPrimitive(tok->next()))
return false;
const Token * parenTok = tok->next();
if (Token::simpleMatch(parenTok, "") ||
(Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1()) ||
(Token::simpleMatch(tok2->astParent(), "(") && tok2->astParent()->isCast())) {
if (tok2->astParent() && (tok2->astParent()->isUnaryOp("*") || (astIsLHS(tok2) && tok2->astParent()->originalName() == "->" && !hasOverloadedMemberAccess(tok2))))
derefs++;
if (derefs > indirect)
break;
if (tok2->astParent() && tok2->astParent()->isUnaryOp("&") && Token::simpleMatch(tok2->astParent()->astParent(), ".") && tok2->astParent()->astParent()->originalName()=="->")
tok2 = tok2->astParent();
tok2 = tok2->astParent();
}
if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) {
const Token* parent = tok2->astParent();
while (parent->astParent() && parent->astParent()->isCast())
parent = parent->astParent();
if (parent->astParent() && parent->astParent()->isUnaryOp("*"))
tok2 = parent->astParent();
}
while ((Token::simpleMatch(tok2, ":") && Token::simpleMatch(tok2->astParent(), "?")) ||
(Token::simpleMatch(tok2->astParent(), ":") && Token::simpleMatch(tok2->astParent()->astParent(), "?")))
tok2 = tok2->astParent();
if (indirect == 0 && tok2->astParent() && tok2->astParent()->tokType() == Token::eIncDecOp)
return true;
auto skipRedundantPtrOp = [](const Token* tok, const Token* parent) {
const Token* gparent = parent ? parent->astParent() : nullptr;
while (parent && gparent && ((parent->isUnaryOp("*") && gparent->isUnaryOp("&")) || (parent->isUnaryOp("&") && gparent->isUnaryOp("*")))) {
tok = gparent;
parent = gparent->astParent();
if (parent)
gparent = parent->astParent();
}
return tok;
};
tok2 = skipRedundantPtrOp(tok2, tok2->astParent());
if (tok2->astParent() && tok2->astParent()->isAssignmentOp()) {
if (astIsLHS(tok2))
return true;
// Check if assigning to a non-const lvalue
const Variable * var = getLHSVariable(tok2->astParent());
if (var && var->isReference() && !var->isConst() && var->nameToken() &&
var->nameToken()->next() == tok2->astParent()) {
if (!var->isLocal() || isVariableChanged(var, settings, depth - 1))
return true;
}
}
const ValueType* vt = tok->variable() ? tok->variable()->valueType() : tok->valueType();
// Check addressof
if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) {
if (isVariableChanged(tok2->astParent(), indirect + 1, settings, depth - 1))
return true;
} else {
// If its already const then it can't be modified
if (vt && vt->isConst(indirect))
return false;
}
if (tok2->isCpp() && Token::Match(tok2->astParent(), ">>|&") && astIsRHS(tok2) && isLikelyStreamRead(tok2->astParent()))
return true;
if (isLikelyStream(tok2))
return true;
// Member function call
if (Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) &&
tok2->astParent()->astOperand1() == tok2) {
// Member function cannot change what `this` points to
if (indirect == 0 && astIsPointer(tok))
return false;
const Token *ftok = tok2->astParent()->astOperand2();
const Token* const ctok = tok2->str() == "." ? tok2->astOperand2() : tok2;
if (astIsContainer(ctok) && ctok->valueType() && ctok->valueType()->container) {
const Library::Container* c = ctok->valueType()->container;
const Library::Container::Action action = c->getAction(ftok->str());
if (contains({Library::Container::Action::INSERT,
Library::Container::Action::ERASE,
Library::Container::Action::APPEND,
Library::Container::Action::CHANGE,
Library::Container::Action::CHANGE_CONTENT,
Library::Container::Action::CHANGE_INTERNAL,
Library::Container::Action::CLEAR,
Library::Container::Action::FIND,
Library::Container::Action::PUSH,
Library::Container::Action::POP,
Library::Container::Action::RESIZE},
action))
return true;
const Library::Container::Yield yield = c->getYield(ftok->str());
// If accessing element check if the element is changed
if (contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield))
return isVariableChanged(ftok->next(), indirect, settings, depth - 1);
if (contains({Library::Container::Yield::BUFFER,
Library::Container::Yield::BUFFER_NT,
Library::Container::Yield::START_ITERATOR,
Library::Container::Yield::ITERATOR},
yield)) {
return isVariableChanged(ftok->next(), indirect + 1, settings, depth - 1);
}
if (contains({Library::Container::Yield::SIZE,
Library::Container::Yield::EMPTY,
Library::Container::Yield::END_ITERATOR},
yield)) {
return false;
}
}
if (settings.library.isFunctionConst(ftok) || (astIsSmartPointer(tok) && ftok->str() == "get")) // TODO: replace with action/yield?
return false;
const Function * fun = ftok->function();
if (!fun)
return true;
return !fun->isConst();
}
// Member pointer
if (Token::Match(tok2->astParent(), ". * ( & %name% ::")) {
const Token* ftok = tok2->astParent()->linkAt(2)->previous();
// TODO: Check for pointer to member variable
if (!ftok->function() || !ftok->function()->isConst())
return true;
}
if (Token::Match(tok2->astParent(), ". * %name%")) // bailout
return true;
if (Token::simpleMatch(tok2, "[") && astIsContainer(tok) && vt && vt->container && vt->container->stdAssociativeLike)
return true;
const Token *ftok = tok2;
while (ftok && (!Token::Match(ftok, "[({]") || ftok->isCast()))
ftok = ftok->astParent();
if (ftok && Token::Match(ftok->link(), ")|} !!{")) {
if (ftok->str() == "(" && Token::simpleMatch(ftok->astOperand1(), "[")) // operator() on array element, bail out
return true;
const Token * ptok = tok2;
while (Token::Match(ptok->astParent(), ".|::"))
ptok = ptok->astParent();
int pindirect = indirect;
if (indirect == 0 && astIsLHS(tok2) && Token::Match(ptok, ". %var%") && astIsPointer(ptok->next()))
pindirect = 1;
bool inconclusive = false;
bool isChanged = isVariableChangedByFunctionCall(ptok, pindirect, settings.library, &inconclusive);
isChanged |= inconclusive;
if (isChanged)
return true;
}
const Token *parent = tok2->astParent();
while (Token::Match(parent, ".|::"))
parent = parent->astParent();
if (parent && parent->tokType() == Token::eIncDecOp && (indirect == 0 || tok2 != tok))
return true;
// structured binding, nonconst reference variable in lhs
if (Token::Match(tok2->astParent(), ":|=") && tok2 == tok2->astParent()->astOperand2() && Token::simpleMatch(tok2->astParent()->previous(), "]")) {
const Token *typeStart = tok2->astParent()->linkAt(-1)->previous();
if (Token::simpleMatch(typeStart, "&"))
typeStart = typeStart->previous();
if (typeStart && Token::Match(typeStart->previous(), "[;{}(] auto &| [")) {
for (const Token *vartok = typeStart->tokAt(2); vartok != tok2; vartok = vartok->next()) {
if (vartok->varId()) {
const Variable* refvar = vartok->variable();
if (!refvar || (!refvar->isConst() && refvar->isReference()))
return true;
}
}
}
}
if (Token::simpleMatch(tok2->astParent(), ":") && tok2->astParent()->astParent() && Token::simpleMatch(tok2->astParent()->astParent()->previous(), "for (")) {
// TODO: Check if container is empty or not
if (astIsLHS(tok2))
return true;
const Token * varTok = tok2->astParent()->previous();
if (!varTok)
return false;
const Variable * loopVar = varTok->variable();
if (!loopVar)
return false;
if (!loopVar->isConst() && loopVar->isReference() && isVariableChanged(loopVar, settings, depth - 1))
return true;
return false;
}
if (indirect > 0) {
// check for `*(ptr + 1) = new_value` case
parent = tok2->astParent();
while (parent && ((parent->isArithmeticalOp() && parent->isBinaryOp()) || parent->isIncDecOp())) {
parent = parent->astParent();
}
if (Token::simpleMatch(parent, "*")) {
if (parent->astParent() && parent->astParent()->isAssignmentOp() &&
(parent->astParent()->astOperand1() == parent)) {
return true;
}
}
}
return false;
}
bool isVariableChanged(const Token *start, const Token *end, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
{
return findVariableChanged(start, end, 0, exprid, globalvar, settings, depth) != nullptr;
}
bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
{
return findVariableChanged(start, end, indirect, exprid, globalvar, settings, depth) != nullptr;
}
const Token* findExpression(const Token* start, const nonneg int exprid)
{
const Function* f = Scope::nestedInFunction(start->scope());
if (!f)
return nullptr;
const Scope* scope = f->functionScope;
if (!scope)
return nullptr;
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
if (tok->exprId() != exprid)
continue;
return tok;
}
return nullptr;
}
const Token* findEscapeStatement(const Scope* scope, const Library& library)
{
if (!scope)
return nullptr;
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
const Scope* escapeScope = tok->scope();
if (!escapeScope->isExecutable()) { // skip type definitions
tok = escapeScope->bodyEnd;
continue;
}
if (const Token* lambdaEnd = findLambdaEndToken(tok)) { // skip lambdas
tok = lambdaEnd;
continue;
}
if (!tok->isName())
continue;
if (isEscapeFunction(tok, library))
return tok;
if (!tok->isKeyword())
continue;
if (Token::Match(tok, "goto|return|throw")) // TODO: check try/catch, labels?
return tok;
if (!Token::Match(tok, "break|continue"))
continue;
const bool isBreak = tok->str()[0] == 'b';
while (escapeScope && escapeScope != scope) {
if (escapeScope->isLoopScope() || (isBreak && escapeScope->type == ScopeType::eSwitch))
return nullptr;
escapeScope = escapeScope->nestedIn;
}
return tok;
}
return nullptr;
}
template
static bool isExpressionChangedAt(const F& getExprTok,
const Token* tok,
int indirect,
const nonneg int exprid,
bool globalvar,
const Settings& settings,
int depth)
{
if (depth < 0)
return true;
if (!tok)
return false;
if (!tok->isMutableExpr())
return false;
if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) {
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(), settings.library);
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;
}
nonneg int i = 1;
bool aliased = false;
// If we can't find the expression then assume it is an alias
auto expr = getExprTok();
if (!expr && !(tok->valueType() && tok->valueType()->pointer == 0 && tok->valueType()->reference == Reference::None))
aliased = true;
if (!aliased && expr && expr->varId() && tok->isCast() && tok->valueType() && tok->valueType()->reference != Reference::None &&
Token::Match(tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(), "%varid%", expr->varId()))
aliased = true;
if (!aliased)
aliased = isAliasOf(tok, expr, &i);
if (!aliased)
return false;
i += indirect;
if (tok->valueType() && tok->valueType()->pointer)
i = std::min(i, tok->valueType()->pointer);
if (isVariableChanged(tok, i, settings, depth))
return true;
// TODO: Try to traverse the lambda function
if (Token::Match(tok, "%var% ("))
return true;
return false;
}
return (isVariableChanged(tok, indirect, settings, depth));
}
bool isExpressionChangedAt(const Token* expr,
const Token* tok,
int indirect,
bool globalvar,
const Settings& settings,
int depth)
{
return isExpressionChangedAt([&] {
return expr;
}, tok, indirect, expr->exprId(), globalvar, settings, depth);
}
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
{
if (!precedes(start, end))
return nullptr;
if (depth < 0)
return start;
auto getExprTok = utils::memoize([&] {
return findExpression(start, exprid);
});
for (Token *tok = start; tok != end; tok = tok->next()) {
if (isExpressionChangedAt(getExprTok, tok, indirect, exprid, globalvar, settings, depth))
return tok;
}
return nullptr;
}
const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
{
return findVariableChanged(const_cast(start), end, indirect, exprid, globalvar, settings, depth);
}
bool isVariableChanged(const Variable * var, const Settings &settings, int depth)
{
if (!var)
return false;
if (!var->scope())
return false;
const Token * start = var->declEndToken();
if (!start)
return false;
if (start->isSplittedVarDeclEq() && Token::Match(start, "; %varid% =", var->declarationId()))
start = start->tokAt(2);
if (Token::simpleMatch(start, "=")) {
const Token* next = nextAfterAstRightmostLeafGeneric(start);
if (next)
start = next;
}
return findExpressionChanged(var->nameToken(), start->next(), var->scope()->bodyEnd, settings, depth);
}
bool isVariablesChanged(const Token* start,
const Token* end,
int indirect,
const std::vector &vars,
const Settings& settings)
{
std::set varids;
std::transform(vars.cbegin(), vars.cend(), std::inserter(varids, varids.begin()), [](const Variable* var) {
return var->declarationId();
});
const bool globalvar = std::any_of(vars.cbegin(), vars.cend(), [](const Variable* var) {
return var->isGlobal();
});
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
if (tok->varId() == 0 || varids.count(tok->varId()) == 0) {
if (globalvar && Token::Match(tok, "%name% ("))
// TODO: Is global variable really changed by function call?
return true;
continue;
}
if (isVariableChanged(tok, indirect, settings))
return true;
}
return false;
}
bool isThisChanged(const Token* tok, int indirect, const Settings& settings)
{
if ((Token::Match(tok->previous(), "%name% (") && !Token::simpleMatch(tok->astOperand1(), ".")) ||
Token::Match(tok->tokAt(-3), "this . %name% (")) {
if (tok->previous()->function()) {
return (!tok->previous()->function()->isConst() && !tok->previous()->function()->isStatic());
}
if (!tok->previous()->isKeyword() || tok->previous()->isOperatorKeyword()) {
return true;
}
}
if (isVariableChanged(tok, indirect, settings))
return true;
return false;
}
static const Token* findThisChanged(const Token* start, const Token* end, int indirect, const Settings& settings)
{
if (!precedes(start, end))
return nullptr;
for (const Token* tok = start; tok != end; tok = tok->next()) {
if (!exprDependsOnThis(tok))
continue;
if (isThisChanged(tok, indirect, settings))
return tok;
}
return nullptr;
}
template
static const Token* findExpressionChangedImpl(const Token* expr,
const Token* start,
const Token* end,
const Settings& settings,
int depth,
Find find)
{
if (depth < 0)
return start;
if (!precedes(start, end))
return nullptr;
const Token* result = nullptr;
findAstNode(expr, [&](const Token* tok) {
if (exprDependsOnThis(tok)) {
result = findThisChanged(start, end, /*indirect*/ 0, settings);
if (result)
return true;
}
bool global = false;
if (tok->variable()) {
global = !tok->variable()->isLocal() && !tok->variable()->isArgument();
} else if (tok->isIncompleteVar() && !tok->isIncompleteConstant()) {
global = true;
}
if (tok->exprId() > 0 || global) {
const Token* modifedTok = find(start, end, [&](const Token* tok2) {
int indirect = 0;
if (const ValueType* vt = tok->valueType()) {
indirect = vt->pointer;
if (vt->type == ValueType::ITERATOR)
++indirect;
}
if (indirect == 0 && tok2->astParent() && tok2->astParent()->isUnaryOp("*"))
++indirect;
for (int i = 0; i next();
while (Token::simpleMatch(tok, ")"))
tok = tok->next();
if (!Token::Match(tok, "(|{|["))
return nullptr;
const Token* startTok = tok->astOperand2();
if (!startTok && tok->next() != tok->link())
startTok = tok->astOperand1();
return startTok;
}
int numberOfArguments(const Token* ftok) {
return astCount(getArgumentStart(ftok), ",");
}
int numberOfArgumentsWithoutAst(const Token* start)
{
int arguments = 0;
const Token* openBracket = start->next();
while (Token::simpleMatch(openBracket, ")"))
openBracket = openBracket->next();
if (openBracket && openBracket->str()=="(" && openBracket->next() && openBracket->strAt(1)!=")") {
const Token* argument=openBracket->next();
while (argument) {
++arguments;
argument = argument->nextArgument();
}
}
return arguments;
}
std::vector getArguments(const Token* ftok) {
return astFlatten(getArgumentStart(ftok), ",");
}
int getArgumentPos(const Variable* var, const Function* f)
{
auto arg_it = std::find_if(f->argumentList.cbegin(), f->argumentList.cend(), [&](const Variable& v) {
return v.nameToken() == var->nameToken();
});
if (arg_it == f->argumentList.end())
return -1;
return std::distance(f->argumentList.cbegin(), arg_it);
}
const Token* getIteratorExpression(const Token* tok)
{
if (!tok)
return nullptr;
if (tok->isUnaryOp("*"))
return nullptr;
if (!tok->isName()) {
const Token* iter1 = getIteratorExpression(tok->astOperand1());
if (iter1)
return iter1;
if (tok->str() == "(")
return nullptr;
const Token* iter2 = getIteratorExpression(tok->astOperand2());
if (iter2)
return iter2;
} else if (Token::Match(tok, "begin|cbegin|rbegin|crbegin|end|cend|rend|crend (")) {
if (Token::Match(tok->previous(), ". %name% ( ) !!."))
return tok->previous()->astOperand1();
if (!Token::simpleMatch(tok->previous(), ".") && Token::Match(tok, "%name% ( !!)") &&
!Token::simpleMatch(tok->linkAt(1), ") ."))
return tok->next()->astOperand2();
}
return nullptr;
}
bool isIteratorPair(const std::vector& args)
{
if (args.size() != 2)
return false;
if (astIsPointer(args[0]) && astIsPointer(args[1]))
return true;
// Check if iterator is from same container
const Token* tok1 = nullptr;
const Token* tok2 = nullptr;
if (astIsIterator(args[0]) && astIsIterator(args[1])) {
tok1 = ValueFlow::getLifetimeObjValue(args[0]).tokvalue;
tok2 = ValueFlow::getLifetimeObjValue(args[1]).tokvalue;
if (!tok1 || !tok2)
return true;
} else {
tok1 = getIteratorExpression(args[0]);
tok2 = getIteratorExpression(args[1]);
}
if (tok1 && tok2)
return tok1->exprId() == tok2->exprId();
return tok1 || tok2;
}
const Token *findLambdaStartToken(const Token *last)
{
if (!last || !last->isCpp() || last->str() != "}")
return nullptr;
const Token* tok = last->link();
if (Token::simpleMatch(tok->astParent(), "("))
tok = tok->astParent();
if (Token::simpleMatch(tok->astParent(), "["))
return tok->astParent();
return nullptr;
}
template
static T* findLambdaEndTokenGeneric(T* first)
{
auto maybeLambda = [](T* tok) -> bool {
while (Token::Match(tok, "*|%name%|::|>")) {
if (tok->link())
tok = tok->link()->previous();
else {
if (tok->str() == ">")
return true;
if (tok->str() == "new")
return false;
tok = tok->previous();
}
}
return true;
};
if (!first || !first->isCpp() || first->str() != "[")
return nullptr;
if (!maybeLambda(first->previous()))
return nullptr;
if (!Token::Match(first->link(), "] (|{|>") || !op->isBinaryOp())
return false;
if (!Token::Match(op->astOperand2(), "%name%|.|*|[") && op->str() != op->astOperand2()->str())
return false;
const Token *parent = op;
while (parent->astParent() && parent->astParent()->str() == op->str())
parent = parent->astParent();
if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|.|!|;|return"))
return false;
if (op->str() == "&" && parent->astParent())
return false;
if (!parent->astOperand1() || !parent->astOperand2())
return false;
return (!parent->astOperand1()->valueType() || !parent->astOperand1()->valueType()->isIntegral());
}
bool isCPPCast(const Token* tok)
{
return tok && Token::simpleMatch(tok->previous(), "> (") && tok->astOperand2() && tok->astOperand1() && isCPPCastKeyword(tok->astOperand1());
}
bool isConstVarExpression(const Token *tok, const std::function& skipPredicate)
{
if (!tok)
return false;
if (tok->str() == "?" && tok->astOperand2() && tok->astOperand2()->str() == ":") // ternary operator
return isConstVarExpression(tok->astOperand2()->astOperand1()) || isConstVarExpression(tok->astOperand2()->astOperand2()); // left and right of ":"
if (skipPredicate && skipPredicate(tok))
return false;
if (Token::simpleMatch(tok->previous(), "sizeof ("))
return true;
if (Token::Match(tok->previous(), "%name% (")) {
if (Token::simpleMatch(tok->astOperand1(), ".") && !isConstVarExpression(tok->astOperand1(), skipPredicate))
return false;
std::vector args = getArguments(tok);
if (args.empty() && tok->previous()->function() && tok->previous()->function()->isConstexpr())
return true;
return !args.empty() && std::all_of(args.cbegin(), args.cend(), [&](const Token* t) {
return isConstVarExpression(t, skipPredicate);
});
}
if (isCPPCast(tok)) {
return isConstVarExpression(tok->astOperand2(), skipPredicate);
}
if (Token::Match(tok, "( %type%"))
return isConstVarExpression(tok->astOperand1(), skipPredicate);
if (tok->str() == "::" && tok->hasKnownValue())
return isConstVarExpression(tok->astOperand2(), skipPredicate);
if (Token::Match(tok, "%cop%|[|.")) {
if (tok->astOperand1() && !isConstVarExpression(tok->astOperand1(), skipPredicate))
return false;
if (tok->astOperand2() && !isConstVarExpression(tok->astOperand2(), skipPredicate))
return false;
return true;
}
if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL"))
return true;
if (tok->isEnumerator())
return true;
if (tok->variable())
return tok->variable()->isConst() && tok->variable()->nameToken() && tok->variable()->nameToken()->hasKnownValue();
return false;
}
static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Library& library)
{
const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&");
int argnr;
const Token* ftok = getTokenArgumentFunction(tok, argnr);
if (!ftok)
return ExprUsage::None;
const Function* func = ftok->function();
// variable init/constructor call?
if (!func && ftok->variable() && ftok == ftok->variable()->nameToken()) {
// STL types or containers don't initialize external variables
if (indirect == 0 && (ftok->variable()->isStlType() || (ftok->variable()->valueType() && ftok->variable()->valueType()->container)))
return ExprUsage::Used;
// TODO: resolve multiple constructors
if (ftok->variable()->type() && ftok->variable()->type()->classScope) {
const int nCtor = ftok->variable()->type()->classScope->numConstructors;
if (nCtor == 0) {
if (indirect > 0) {
const std::vector argvar = getArgumentVars(ftok->astParent(), argnr);
if (argvar.size() == 1 && argvar[0]->valueType() && argvar[0]->valueType()->pointer == indirect)
return ExprUsage::NotUsed;
}
return ExprUsage::Used;
}
if (nCtor == 1) {
const Scope* scope = ftok->variable()->type()->classScope;
auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [](const Function& f) {
return f.isConstructor();
});
if (it != scope->functionList.end())
func = &*it;
}
}
}
if (func) {
std::vector args = getArgumentVars(ftok, argnr);
for (const Variable* arg : args) {
if (!arg)
continue;
if (arg->isReference() || (arg->isPointer() && indirect == 1)) {
if (!func->hasBody())
return ExprUsage::PassedByReference;
for (const Token* bodytok = func->functionScope->bodyStart; bodytok != func->functionScope->bodyEnd; bodytok = bodytok->next()) {
if (bodytok->variable() == arg) {
if (arg->isReference())
return ExprUsage::PassedByReference;
if (Token::Match(bodytok->astParent(), "%comp%|!"))
return ExprUsage::NotUsed;
return ExprUsage::PassedByReference;
}
}
return ExprUsage::NotUsed;
}
}
if (!args.empty() && indirect == 0 && !addressOf)
return ExprUsage::Used;
} else if (ftok->isControlFlowKeyword()) {
return ExprUsage::Used;
} else if (ftok->str() == "{") {
return indirect == 0 ? ExprUsage::Used : ExprUsage::Inconclusive;
} else {
const bool isnullbad = library.isnullargbad(ftok, argnr + 1);
if (indirect == 0 && astIsPointer(tok) && !addressOf && isnullbad)
return ExprUsage::Used;
bool hasIndirect = false;
const bool isuninitbad = library.isuninitargbad(ftok, argnr + 1, indirect, &hasIndirect);
if (isuninitbad && (!addressOf || isnullbad))
return ExprUsage::Used;
const Library::ArgumentChecks::Direction argDirection = library.getArgDirection(ftok, argnr + 1, indirect);
if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) // TODO: DIR_INOUT?
return ExprUsage::Used;
if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT)
return ExprUsage::NotUsed;
}
return ExprUsage::Inconclusive;
}
bool isLeafDot(const Token* tok)
{
if (!tok)
return false;
const Token * parent = tok->astParent();
if (!Token::simpleMatch(parent, "."))
return false;
if (parent->astOperand2() == tok && !Token::simpleMatch(parent->astParent(), "."))
return true;
return isLeafDot(parent);
}
ExprUsage getExprUsage(const Token* tok, int indirect, const Settings& settings)
{
const Token* parent = tok->astParent();
if (indirect > 0 && parent) {
while (Token::simpleMatch(parent, "[") && parent->astParent())
parent = parent->astParent();
if (Token::Match(parent, "%assign%") && (astIsRHS(tok) || astIsLHS(parent->astOperand1())))
return ExprUsage::NotUsed;
if (Token::Match(parent, "++|--"))
return ExprUsage::NotUsed;
if (parent->isConstOp())
return ExprUsage::NotUsed;
if (parent->isCast())
return ExprUsage::NotUsed;
if (Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "?"))
return getExprUsage(parent->astParent(), indirect, settings);
if (isUsedAsBool(tok, settings))
return ExprUsage::NotUsed;
}
if (tok->isUnaryOp("&") && !parent)
return ExprUsage::NotUsed;
if (indirect == 0) {
if (Token::Match(parent, "%cop%|%assign%|++|--") && parent->str() != "=" &&
!parent->isUnaryOp("&") &&
!(astIsRHS(tok) && isLikelyStreamRead(parent)))
return ExprUsage::Used;
if (isLeafDot(tok)) {
const Token* op = parent->astParent();
while (Token::simpleMatch(op, "."))
op = op->astParent();
if (Token::Match(op, "%assign%|++|--")) {
if (op->str() == "=") {
if (precedes(tok, op))
return ExprUsage::NotUsed;
} else
return ExprUsage::Used;
}
}
if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) {
const Token* const lhs = parent->astOperand1();
if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs == lhs->variable()->nameToken())
return ExprUsage::NotUsed;
return ExprUsage::Used;
}
// Function call or index
if (((Token::simpleMatch(parent, "(") && !parent->isCast()) || (Token::simpleMatch(parent, "[") && tok->valueType())) &&
(astIsLHS(tok) || Token::simpleMatch(parent, "( )")))
return ExprUsage::Used;
}
return getFunctionUsage(tok, indirect, settings.library);
}
static void getLHSVariablesRecursive(std::vector& vars, const Token* tok)
{
if (!tok)
return;
if (vars.empty() && Token::Match(tok, "*|&|&&|[")) {
getLHSVariablesRecursive(vars, tok->astOperand1());
if (!vars.empty() || Token::simpleMatch(tok, "["))
return;
getLHSVariablesRecursive(vars, tok->astOperand2());
} else if (Token::Match(tok->previous(), "this . %var%")) {
getLHSVariablesRecursive(vars, tok->next());
} else if (Token::simpleMatch(tok, ".")) {
getLHSVariablesRecursive(vars, tok->astOperand1());
getLHSVariablesRecursive(vars, tok->astOperand2());
} else if (Token::simpleMatch(tok, "::")) {
getLHSVariablesRecursive(vars, tok->astOperand2());
} else if (tok->variable()) {
vars.push_back(tok->variable());
}
}
std::vector getLHSVariables(const Token* tok)
{
if (!Token::Match(tok, "%assign%|(|{"))
return {};
if (!tok->astOperand1())
return {};
if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable())
return {tok->astOperand1()->variable()};
std::vector result;
getLHSVariablesRecursive(result, tok->astOperand1());
return result;
}
static const Token* getLHSVariableRecursive(const Token* tok)
{
if (!tok)
return nullptr;
if (Token::Match(tok, "*|&|&&|[")) {
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
if ((vartok && vartok->variable()) || Token::simpleMatch(tok, "["))
return vartok;
return getLHSVariableRecursive(tok->astOperand2());
}
if (Token::Match(tok->previous(), "this . %var%"))
return tok->next();
return tok;
}
const Variable *getLHSVariable(const Token *tok)
{
if (!tok || !tok->isAssignmentOp())
return nullptr;
if (!tok->astOperand1())
return nullptr;
if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable())
return tok->astOperand1()->variable();
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
if (!vartok)
return nullptr;
return vartok->variable();
}
const Token* getLHSVariableToken(const Token* tok)
{
if (!Token::Match(tok, "%assign%"))
return nullptr;
if (!tok->astOperand1())
return nullptr;
if (tok->astOperand1()->varId() > 0)
return tok->astOperand1();
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok)
return vartok;
return tok->astOperand1();
}
const Token* findAllocFuncCallToken(const Token *expr, const Library &library)
{
if (!expr)
return nullptr;
if (Token::Match(expr, "[+-]")) {
const Token *tok1 = findAllocFuncCallToken(expr->astOperand1(), library);
return tok1 ? tok1 : findAllocFuncCallToken(expr->astOperand2(), library);
}
if (expr->isCast())
return findAllocFuncCallToken(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1(), library);
if (Token::Match(expr->previous(), "%name% (") && library.getAllocFuncInfo(expr->astOperand1()))
return expr->astOperand1();
return (Token::simpleMatch(expr, "new") && expr->astOperand1()) ? expr : nullptr;
}
bool isNullOperand(const Token *expr)
{
if (!expr)
return false;
if (expr->isCpp() && Token::Match(expr, "static_cast|const_cast|dynamic_cast|reinterpret_cast ") {
// TODO check if pointer points at local data
globalData = true;
return ChildrenToVisit::none;
}
if (Token::Match(tok, "[*[]") && tok->astOperand1()) {
// TODO check if pointer points at local data
const Token *lhs = tok->astOperand1();
if (lhs->isCast()) {
lhs = lhs->astOperand2() ? lhs->astOperand2() : lhs->astOperand1();
}
if (lhs && lhs->variable()) {
const Variable *lhsvar = lhs->variable();
const ValueType *lhstype = lhs->valueType();
if (lhsvar->isPointer() || !lhstype || lhstype->type == ValueType::Type::ITERATOR) {
globalData = true;
return ChildrenToVisit::none;
}
if (lhsvar->isArgument() && lhsvar->isArray()) {
globalData = true;
return ChildrenToVisit::none;
}
if (lhsvar->isArgument() && lhstype->type container) {
globalData = true;
return ChildrenToVisit::none;
}
}
}
if (tok->varId() == 0 && tok->isName() && tok->strAt(-1) != ".") {
globalData = true;
return ChildrenToVisit::none;
}
if (tok->variable()) {
// TODO : Check references
if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) {
globalData = true;
return ChildrenToVisit::none;
}
if (tok->variable()->isExtern()) {
globalData = true;
return ChildrenToVisit::none;
}
if (tok->strAt(-1) != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) {
globalData = true;
return ChildrenToVisit::none;
}
if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) {
globalData = true;
return ChildrenToVisit::none;
}
if (tok->variable()->isPointerArray()) {
globalData = true;
return ChildrenToVisit::none;
}
}
// Unknown argument type => it might be some reference type..
if (tok->isCpp() && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) {
globalData = true;
return ChildrenToVisit::none;
}
if (Token::Match(tok, ".|["))
return ChildrenToVisit::op1;
return ChildrenToVisit::op1_and_op2;
});
return globalData || !var;
}
bool isUnevaluated(const Token *tok)
{
return Token::Match(tok, "alignof|_Alignof|_alignof|__alignof|__alignof__|decltype|offsetof|sizeof|typeid|typeof|__typeof__ (");
}
static std::set getSwitchValues(const Token *startbrace, bool &hasDefault)
{
std::set values;
const Token *endbrace = startbrace->link();
if (!endbrace)
return values;
hasDefault = false;
for (const Token *tok = startbrace->next(); tok && tok != endbrace; tok = tok->next()) {
if (Token::simpleMatch(tok, "{") && tok->scope()->type == ScopeType::eSwitch) {
tok = tok->link();
continue;
}
if (Token::simpleMatch(tok, "default")) {
hasDefault = true;
break;
}
if (Token::simpleMatch(tok, "case")) {
const Token *valueTok = tok->astOperand1();
if (const ValueFlow::Value* v = valueTok->getKnownValue(ValueFlow::Value::ValueType::INT))
values.insert(v->intvalue);
continue;
}
}
return values;
}
bool isExhaustiveSwitch(const Token *startbrace)
{
if (!startbrace || !Token::simpleMatch(startbrace->previous(), ") {") || startbrace->scope()->type != ScopeType::eSwitch)
return false;
const Token *rpar = startbrace->previous();
const Token *lpar = rpar->link();
const Token *condition = lpar->astOperand2();
if (!condition->valueType())
return true;
bool hasDefault = false;
const std::set switchValues = getSwitchValues(startbrace, hasDefault);
if (hasDefault)
return true;
if (condition->valueType()->type == ValueType::Type::BOOL)
return switchValues.count(0) && switchValues.count(1);
if (condition->valueType()->isEnum()) {
const std::vector &enumList = condition->valueType()->typeScope->enumeratorList;
return std::all_of(enumList.cbegin(), enumList.cend(), [&](const Enumerator &e) {
return !e.value_known || switchValues.count(e.value);
});
}
return false;
}
bool isUnreachableOperand(const Token *tok)
{
for (;;)
{
const Token *parent = tok->astParent();
if (!parent)
break;
if (parent->isBinaryOp()) {
const bool left = tok == parent->astOperand1();
const Token *sibling = left ? parent->astOperand2() : parent->astOperand1();
// logical and
if (Token::simpleMatch(parent, "&&") && !left && sibling->hasKnownIntValue()
&& !sibling->getKnownIntValue())
return true;
// logical or
if (Token::simpleMatch(parent, "||") && !left && sibling->hasKnownIntValue()
&& sibling->getKnownIntValue())
return true;
// ternary
if (Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "?")) {
const Token *condTok = parent->astParent()->astOperand1();
if (condTok->hasKnownIntValue() && static_cast(condTok->getKnownIntValue()) != left)
return true;
}
}
tok = parent;
}
return false;
}
static bool unknownLeafValuesAreTemplateArgs(const Token *tok)
{
if (!tok)
return true;
if (!tok->astOperand1() && !tok->astOperand2())
return tok->isTemplateArg() || tok->hasKnownIntValue();
return unknownLeafValuesAreTemplateArgs(tok->astOperand1())
&& unknownLeafValuesAreTemplateArgs(tok->astOperand2());
}
static const Token *skipUnreachableIfBranch(const Token *tok)
{
const Token *condTok = tok->linkAt(-1);
if (!condTok)
return tok;
if (!Token::simpleMatch(condTok->tokAt(-1), "if") && !Token::simpleMatch(condTok->tokAt(-2), "if constexpr"))
return tok;
condTok = condTok->astOperand2();
if (!condTok)
return tok;
if ((condTok->hasKnownIntValue() && condTok->getKnownIntValue() == 0)
|| (unknownLeafValuesAreTemplateArgs(condTok) && condTok->getValue(0))) {
tok = tok->link();
}
return tok;
}
static const Token *skipUnreachableElseBranch(const Token *tok)
{
if (!Token::simpleMatch(tok->tokAt(-2), "} else {"))
return tok;
const Token *condTok = tok->linkAt(-2);
if (!condTok)
return tok;
condTok = condTok->linkAt(-1);
if (!condTok)
return tok;
if (!Token::simpleMatch(condTok->tokAt(-1), "if (") && !Token::simpleMatch(condTok->tokAt(-2), "if constexpr ("))
return tok;
condTok = condTok->astOperand2();
if ((condTok->hasKnownIntValue() && condTok->getKnownIntValue() != 0)
|| (unknownLeafValuesAreTemplateArgs(condTok) && condTok->getValueNE(0))) {
tok = tok->link();
}
return tok;
}
const Token *skipUnreachableBranch(const Token *tok)
{
if (!Token::simpleMatch(tok, "{"))
return tok;
if (tok->scope()->type == ScopeType::eIf) {
return skipUnreachableIfBranch(tok);
}
if (tok->scope()->type == ScopeType::eElse) {
return skipUnreachableElseBranch(tok);
}
return tok;
}
bool isEscapeKeyword(const Token *tok, const Settings &settings)
{
if (!tok)
return false;
if (tok->str() == "return")
return true;
if (!tok->isCpp())
return false;
if (tok->str() == "throw")
return true;
if (settings.standards.cpp < Standards::CPP20)
return false;
return tok->str() == "co_return";
}