FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

/* * 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"; }

Back | FazBrowse Home | New Git URL