cppcheck/lib/valueflow.cpp

6849 lines
275 KiB
C++
Raw Normal View History

/*
* Cppcheck - A tool for static C/C++ code analysis
2021-03-21 20:58:32 +01:00
* Copyright (C) 2007-2021 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 <http://www.gnu.org/licenses/>.
*/
2018-05-05 23:03:04 +02:00
/**
* @brief This is the ValueFlow component in Cppcheck.
*
2018-05-07 22:06:23 +02:00
* Each @sa Token in the token list has a list of values. These are
2018-05-05 23:03:04 +02:00
* the "possible" values for the Token at runtime.
*
* In the --debug and --debug-normal output you can see the ValueFlow data. For example:
*
* int f()
* {
* int x = 10;
* return 4 * x + 2;
* }
*
* The --debug-normal output says:
*
* ##Value flow
* Line 3
* 10 always 10
* Line 4
* 4 always 4
* * always 40
* x always 10
* + always 42
* 2 always 2
*
* All value flow analysis is executed in the ValueFlow::setValues() function. The ValueFlow analysis is executed after
* the tokenizer/ast/symboldatabase/etc.. The ValueFlow analysis is done in a series of valueFlow* function calls, where
* each such function call can only use results from previous function calls. The function calls should be arranged so
* that valueFlow* that do not require previous ValueFlow information should be first.
2018-05-05 23:03:04 +02:00
*
* Type of analysis
* ================
*
* This is "flow sensitive" value flow analysis. We _usually_ track the value for 1 variable at a time.
*
* How are calculations handled
* ============================
*
* Here is an example code:
*
* x = 3 + 4;
*
* The valueFlowNumber set the values for the "3" and "4" tokens by calling setTokenValue().
* The setTokenValue() handle the calculations automatically. When both "3" and "4" have values, the "+" can be
* calculated. setTokenValue() recursively calls itself when parents in calculations can be calculated.
2018-05-05 23:03:04 +02:00
*
* Forward / Reverse flow analysis
* ===============================
*
* In forward value flow analysis we know a value and see what happens when we are stepping the program forward. Like
* normal execution. The valueFlowForwardVariable is used in this analysis.
2018-05-05 23:03:04 +02:00
*
* In reverse value flow analysis we know the value of a variable at line X. And try to "execute backwards" to determine
* possible values before line X. The valueFlowReverse is used in this analysis.
2018-05-05 23:03:04 +02:00
*
*
*/
#include "valueflow.h"
2017-05-27 04:33:47 +02:00
2020-11-10 16:00:55 +01:00
#include "analyzer.h"
#include "astutils.h"
#include "errorlogger.h"
#include "errortypes.h"
#include "forwardanalyzer.h"
2017-05-27 04:33:47 +02:00
#include "library.h"
#include "mathlib.h"
#include "path.h"
2017-05-27 04:33:47 +02:00
#include "platform.h"
#include "programmemory.h"
2020-11-10 16:00:55 +01:00
#include "reverseanalyzer.h"
#include "settings.h"
2017-05-27 04:33:47 +02:00
#include "standards.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenlist.h"
2017-05-23 15:01:56 +02:00
#include "utils.h"
#include "valueptr.h"
2017-05-27 04:33:47 +02:00
#include <algorithm>
#include <cassert>
2017-05-27 04:33:47 +02:00
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <iterator>
2017-05-27 04:33:47 +02:00
#include <limits>
#include <map>
#include <set>
#include <stack>
#include <stdexcept>
#include <string>
#include <tuple>
2017-05-27 04:33:47 +02:00
#include <vector>
static void bailoutInternal(const std::string& type, TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, std::string function)
{
if (function.find("operator") != std::string::npos)
function = "(valueFlow)";
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> callstack(1, ErrorMessage::FileLocation(tok, tokenlist));
ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::debug,
Path::stripDirectoryPart(file) + ":" + MathLib::toString(line) + ":" + function + " bailout: " + what, type, Certainty::normal);
errorLogger->reportErr(errmsg);
}
#if (defined __cplusplus) && __cplusplus >= 201103L
#define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __func__)
#elif (defined __GNUC__) || (defined __clang__) || (defined _MSC_VER)
#define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __FUNCTION__)
#else
#define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, "(valueFlow)")
#endif
#define bailout(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailout", tokenlist, errorLogger, tok, what)
#define bailoutIncompleteVar(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailoutIncompleteVar", tokenlist, errorLogger, tok, what)
static void changeKnownToPossible(std::list<ValueFlow::Value> &values, int indirect=-1)
{
for (ValueFlow::Value& v: values) {
if (indirect >= 0 && v.indirect != indirect)
continue;
v.changeKnownToPossible();
}
}
static void removeImpossible(std::list<ValueFlow::Value>& values, int indirect = -1)
{
values.remove_if([&](const ValueFlow::Value& v) {
if (indirect >= 0 && v.indirect != indirect)
return false;
return v.isImpossible();
});
}
static void lowerToPossible(std::list<ValueFlow::Value>& values, int indirect = -1)
{
changeKnownToPossible(values, indirect);
removeImpossible(values, indirect);
}
static void changePossibleToKnown(std::list<ValueFlow::Value>& values, int indirect = -1)
{
for (ValueFlow::Value& v : values) {
if (indirect >= 0 && v.indirect != indirect)
continue;
if (!v.isPossible())
continue;
if (v.bound != ValueFlow::Value::Bound::Point)
continue;
v.setKnown();
}
}
2019-09-28 20:50:56 +02:00
static void setValueUpperBound(ValueFlow::Value& value, bool upper)
{
if (upper)
value.bound = ValueFlow::Value::Bound::Upper;
else
value.bound = ValueFlow::Value::Bound::Lower;
}
2019-09-28 20:50:56 +02:00
static void setValueBound(ValueFlow::Value& value, const Token* tok, bool invert)
{
if (Token::Match(tok, "<|<=")) {
setValueUpperBound(value, !invert);
} else if (Token::Match(tok, ">|>=")) {
setValueUpperBound(value, invert);
}
}
static void setConditionalValues(const Token *tok,
bool invert,
MathLib::bigint value,
ValueFlow::Value &true_value,
ValueFlow::Value &false_value)
{
if (Token::Match(tok, "==|!=|>=|<=")) {
true_value = ValueFlow::Value{tok, value};
const char* greaterThan = ">=";
const char* lessThan = "<=";
if (invert)
std::swap(greaterThan, lessThan);
if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) {
false_value = ValueFlow::Value{tok, value - 1};
} else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) {
false_value = ValueFlow::Value{tok, value + 1};
} else {
false_value = ValueFlow::Value{tok, value};
}
} else {
const char* greaterThan = ">";
const char* lessThan = "<";
if (invert)
std::swap(greaterThan, lessThan);
if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) {
true_value = ValueFlow::Value{tok, value + 1};
false_value = ValueFlow::Value{tok, value};
} else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) {
true_value = ValueFlow::Value{tok, value - 1};
false_value = ValueFlow::Value{tok, value};
}
}
setValueBound(true_value, tok, invert);
setValueBound(false_value, tok, !invert);
}
static bool isSaturated(MathLib::bigint value)
{
return value == std::numeric_limits<MathLib::bigint>::max() || value == std::numeric_limits<MathLib::bigint>::min();
}
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value, const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate)
{
if (!tok->astOperand1() || !tok->astOperand2())
return nullptr;
if (tok->isComparisonOp()) {
std::vector<MathLib::bigint> value1 = evaluate(tok->astOperand1());
std::vector<MathLib::bigint> value2 = evaluate(tok->astOperand2());
if (!value1.empty()) {
if (isSaturated(value1.front()))
return nullptr;
setConditionalValues(tok, true, value1.front(), true_value, false_value);
return tok->astOperand2();
} else if (!value2.empty()) {
if (isSaturated(value2.front()))
return nullptr;
setConditionalValues(tok, false, value2.front(), true_value, false_value);
return tok->astOperand1();
}
}
return nullptr;
}
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value)
{
return parseCompareInt(tok, true_value, false_value, [](const Token* t) -> std::vector<MathLib::bigint> {
if (t->hasKnownIntValue())
return {t->values().front().intvalue};
return std::vector<MathLib::bigint>{};
});
}
static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false)
{
if (!Token::simpleMatch(tok, "{"))
return false;
2019-08-26 06:56:29 +02:00
// TODO this search for termTok in all subscopes. It should check the end of the scope.
const Token * termTok = Token::findmatch(tok, "return|continue|break|throw|goto", tok->link());
2018-11-12 06:32:02 +01:00
if (termTok && termTok->scope() == tok->scope())
return true;
std::string unknownFunction;
2018-11-12 06:32:02 +01:00
if (tokenlist && tokenlist->getSettings()->library.isScopeNoReturn(tok->link(), &unknownFunction))
return unknownFunction.empty() || unknown;
return false;
}
static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign sign, nonneg int bit)
2016-12-20 19:32:21 +01:00
{
if (value.isFloatValue()) {
value.valueType = ValueFlow::Value::ValueType::INT;
if (value.floatValue >= std::numeric_limits<int>::min() && value.floatValue <= std::numeric_limits<int>::max()) {
value.intvalue = value.floatValue;
} else { // don't perform UB
value.intvalue = 0;
}
2016-12-20 19:32:21 +01:00
}
if (bit < MathLib::bigint_bits) {
2017-10-21 22:08:34 +02:00
const MathLib::biguint one = 1;
value.intvalue &= (one << bit) - 1;
if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) {
value.intvalue |= ~((one << bit) - 1ULL);
2016-12-20 19:32:21 +01:00
}
}
return value;
}
static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result)
{
if (value1.isKnown() && value2.isKnown())
result->setKnown();
else if (value1.isImpossible() || value2.isImpossible())
result->setImpossible();
else if (value1.isInconclusive() || value2.isInconclusive())
result->setInconclusive();
else
result->setPossible();
if (value1.isIteratorValue())
result->valueType = value1.valueType;
if (value2.isIteratorValue())
result->valueType = value2.valueType;
result->condition = value1.condition ? value1.condition : value2.condition;
result->varId = (value1.varId != 0) ? value1.varId : value2.varId;
result->varvalue = (result->varId == value1.varId) ? value1.varvalue : value2.varvalue;
result->errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath;
2019-07-25 17:19:51 +02:00
result->safe = value1.safe || value2.safe;
if (value1.bound == ValueFlow::Value::Bound::Point || value2.bound == ValueFlow::Value::Bound::Point) {
if (value1.bound == ValueFlow::Value::Bound::Upper || value2.bound == ValueFlow::Value::Bound::Upper)
result->bound = ValueFlow::Value::Bound::Upper;
if (value1.bound == ValueFlow::Value::Bound::Lower || value2.bound == ValueFlow::Value::Bound::Lower)
result->bound = ValueFlow::Value::Bound::Lower;
}
if (value1.path != value2.path)
result->path = -1;
else
result->path = value1.path;
}
static const Token *getCastTypeStartToken(const Token *parent)
{
// TODO: This might be a generic utility function?
if (!parent || parent->str() != "(")
return nullptr;
if (!parent->astOperand2() && Token::Match(parent,"( %name%"))
return parent->next();
if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <"))
return parent->astOperand1()->tokAt(2);
return nullptr;
}
static bool isComputableValue(const Token* parent, const ValueFlow::Value& value)
{
const bool noninvertible = parent->isComparisonOp() || Token::Match(parent, "%|/|&|%or%");
if (noninvertible && value.isImpossible())
return false;
if (!value.isIntValue() && !value.isFloatValue() && !value.isTokValue() && !value.isIteratorValue())
return false;
if (value.isIteratorValue() && !Token::Match(parent, "+|-"))
return false;
if (value.isTokValue() && (!parent->isComparisonOp() || value.tokvalue->tokType() != Token::eString))
return false;
return true;
}
2021-01-10 15:27:42 +01:00
template<class T>
2021-01-12 21:28:56 +01:00
static bool isEqual(T x, T y)
{
2021-01-10 16:52:11 +01:00
return x == y;
}
template<>
2021-01-12 21:28:56 +01:00
bool isEqual<double>(double x, double y)
{
2021-01-10 16:52:11 +01:00
const double diff = (x > y) ? x - y : y - x;
2021-01-10 15:27:42 +01:00
return !((diff / 2) < diff);
}
2021-01-10 15:27:42 +01:00
template<class T>
2021-01-12 21:28:56 +01:00
static bool isZero(T x)
{
2021-01-10 15:27:42 +01:00
return isEqual<T>(x, T(0));
2021-01-10 14:46:19 +01:00
}
template <class R, class T>
static R calculate(const std::string& s, const T& x, const T& y)
{
auto wrap = [](T z) { return R{z}; };
switch (MathLib::encodeMultiChar(s)) {
case '+':
return wrap(x + y);
case '-':
return wrap(x - y);
case '*':
return wrap(x * y);
case '/':
return isZero(y) ? R{} : wrap(x / y);
case '%':
return isZero(y) ? R{} : wrap(MathLib::bigint(x) % MathLib::bigint(y));
case '&':
return wrap(MathLib::bigint(x) & MathLib::bigint(y));
case '|':
return wrap(MathLib::bigint(x) | MathLib::bigint(y));
case '^':
return wrap(MathLib::bigint(x) ^ MathLib::bigint(y));
case '>':
return wrap(x > y);
case '<':
return wrap(x < y);
case '<<':
return (y >= sizeof(MathLib::bigint) * 8) ? R{} : wrap(MathLib::bigint(x) << MathLib::bigint(y));
case '>>':
return (y >= sizeof(MathLib::bigint) * 8) ? R{} : wrap(MathLib::bigint(x) >> MathLib::bigint(y));
case '&&':
return wrap(!isZero(x) && !isZero(y));
case '||':
return wrap(!isZero(x) || !isZero(y));
case '==':
return wrap(isEqual(x, y));
case '!=':
return wrap(!isEqual(x, y));
case '>=':
return wrap(x >= y);
case '<=':
return wrap(x <= y);
}
throw InternalError(nullptr, "Unknown operator: " + s);
}
template <class T>
static T calculate(const std::string& s, const T& x, const T& y)
{
return calculate<T, T>(s, x, y);
}
2018-12-27 21:33:01 +01:00
/** Set token value for cast */
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings);
2015-07-17 09:46:31 +02:00
/** set ValueFlow value and perform calculations if possible */
2016-11-05 09:59:48 +01:00
static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Settings *settings)
2015-07-17 09:46:31 +02:00
{
// Skip setting values that are too big since its ambiguous
if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) &&
ValueFlow::getSizeOf(*tok->valueType(), settings) >= sizeof(MathLib::bigint))
return;
if (!tok->addValue(value))
2015-07-17 09:46:31 +02:00
return;
if (value.path < 0)
return;
Token *parent = tok->astParent();
if (!parent)
return;
2014-01-20 22:26:55 +01:00
if (Token::simpleMatch(parent, "=") && astIsRHS(tok) && !value.isLifetimeValue()) {
setTokenValue(parent, value, settings);
return;
}
if (value.isContainerSizeValue()) {
// .empty, .size, +"abc", +'a'
if (Token::Match(parent, "+|==|!=") && parent->astOperand1() && parent->astOperand2()) {
for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
if (value1.isImpossible())
continue;
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
if (value2.isImpossible())
continue;
if (value1.path != value2.path)
continue;
ValueFlow::Value result;
if (Token::Match(parent, "%comp%"))
result.valueType = ValueFlow::Value::ValueType::INT;
else
result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
if (value1.isContainerSizeValue() && value2.isContainerSizeValue())
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString)
2021-01-10 15:27:42 +01:00
result.intvalue = calculate(parent->str(), value1.intvalue, MathLib::bigint(Token::getStrLength(value2.tokvalue)));
else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString)
2021-01-10 15:27:42 +01:00
result.intvalue = calculate(parent->str(), MathLib::bigint(Token::getStrLength(value1.tokvalue)), value2.intvalue);
else
continue;
combineValueProperties(value1, value2, &result);
if (Token::simpleMatch(parent, "==") && result.intvalue)
continue;
if (Token::simpleMatch(parent, "!=") && !result.intvalue)
continue;
setTokenValue(parent, result, settings);
}
}
}
else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) &&
parent->astOperand1() && parent->astOperand1()->valueType()) {
const Library::Container *c = parent->astOperand1()->valueType()->container;
const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD;
if (yields == Library::Container::Yield::SIZE) {
ValueFlow::Value v(value);
v.valueType = ValueFlow::Value::ValueType::INT;
setTokenValue(parent->astParent(), v, settings);
} else if (yields == Library::Container::Yield::EMPTY) {
ValueFlow::Value v(value);
v.intvalue = !v.intvalue;
v.valueType = ValueFlow::Value::ValueType::INT;
setTokenValue(parent->astParent(), v, settings);
}
}
return;
}
2018-11-12 10:08:57 +01:00
if (value.isLifetimeValue()) {
if (!isLifetimeBorrowed(parent, settings))
return;
2019-07-16 10:51:07 +02:00
if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) {
setTokenValue(parent,value,settings);
} else if (astIsPointer(tok) && astIsPointer(parent) &&
(parent->isArithmeticalOp() || Token::Match(parent, "( %type%"))) {
setTokenValue(parent,value,settings);
}
return;
}
if (value.isUninitValue()) {
ValueFlow::Value pvalue = value;
if (parent->isUnaryOp("&")) {
pvalue.indirect++;
setTokenValue(parent, pvalue, settings);
} else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok) {
if (parent->originalName() == "->" && pvalue.indirect > 0)
pvalue.indirect--;
setTokenValue(parent->astOperand2(), pvalue, settings);
} else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) {
if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0)
pvalue.indirect--;
setTokenValue(parent->astParent()->astOperand2(), pvalue, settings);
} else if (parent->isUnaryOp("*") && pvalue.indirect > 0) {
pvalue.indirect--;
setTokenValue(parent, pvalue, settings);
}
return;
}
2018-12-27 21:33:01 +01:00
// cast..
if (const Token *castType = getCastTypeStartToken(parent)) {
if (((tok->valueType() == nullptr && value.isImpossible()) || astIsPointer(tok)) && value.valueType == ValueFlow::Value::ValueType::INT &&
Token::simpleMatch(parent->astOperand1(), "dynamic_cast"))
return;
const ValueType &valueType = ValueType::parseDecl(castType, settings);
2018-12-27 21:33:01 +01:00
setTokenValueCast(parent, valueType, value, settings);
2016-12-20 19:32:21 +01:00
}
2014-01-20 22:26:55 +01:00
2016-12-20 19:32:21 +01:00
else if (parent->str() == ":") {
2016-11-05 09:29:22 +01:00
setTokenValue(parent,value,settings);
2015-07-02 20:11:27 +02:00
}
2016-08-21 11:06:48 +02:00
else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) {
// is condition always true/false?
if (parent->astOperand1()->hasKnownValue()) {
const ValueFlow::Value &condvalue = parent->astOperand1()->values().front();
2016-11-13 22:33:39 +01:00
const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0));
if (cond && !tok->astOperand1()) { // true condition, no second operator
2016-11-05 09:29:22 +01:00
setTokenValue(parent, condvalue, settings);
} else {
const Token *op = cond ? tok->astOperand1() : tok->astOperand2();
if (!op) // #7769 segmentation fault at setTokenValue()
return;
const std::list<ValueFlow::Value> &values = op->values();
if (std::find(values.begin(), values.end(), value) != values.end())
2016-11-05 09:29:22 +01:00
setTokenValue(parent, value, settings);
}
} else {
// is condition only depending on 1 variable?
nonneg int varId = 0;
2018-11-23 19:16:19 +01:00
bool ret = false;
visitAstNodes(parent->astOperand1(),
[&](const Token *t) {
if (t->varId()) {
if (varId > 0 || value.varId != 0)
2018-11-23 19:16:19 +01:00
ret = true;
varId = t->varId();
} else if (t->str() == "(" && Token::Match(t->previous(), "%name%"))
2018-11-23 19:16:19 +01:00
ret = true; // function call
return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2;
});
if (ret)
return;
2015-07-02 20:11:27 +02:00
ValueFlow::Value v(value);
v.conditional = true;
v.changeKnownToPossible();
2015-07-17 15:22:24 +02:00
if (varId)
v.varId = varId;
2015-07-02 20:11:27 +02:00
2016-11-05 09:29:22 +01:00
setTokenValue(parent, v, settings);
}
2015-07-02 20:11:27 +02:00
}
else if (parent->str() == "?" && value.isIntValue() && tok == parent->astOperand1() && value.isKnown() &&
parent->astOperand2() && parent->astOperand2()->astOperand1() && parent->astOperand2()->astOperand2()) {
const std::list<ValueFlow::Value> &values = (value.intvalue == 0
? parent->astOperand2()->astOperand2()->values()
: parent->astOperand2()->astOperand1()->values());
for (const ValueFlow::Value &v : values)
setTokenValue(parent, v, settings);
}
2014-01-20 22:26:55 +01:00
// Calculations..
else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) &&
parent->astOperand1() &&
parent->astOperand2()) {
const bool noninvertible = parent->isComparisonOp() || Token::Match(parent, "%|/|&|%or%");
// Skip operators with impossible values that are not invertible
if (noninvertible && value.isImpossible())
return;
// known result when a operand is 0.
2016-11-13 22:33:39 +01:00
if (Token::Match(parent, "[&*]") && value.isKnown() && value.isIntValue() && value.intvalue==0) {
2016-11-05 09:29:22 +01:00
setTokenValue(parent, value, settings);
return;
}
// known result when a operand is true.
if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) {
setTokenValue(parent, value, settings);
return;
}
// known result when a operand is false.
if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) {
setTokenValue(parent, value, settings);
return;
}
2018-04-24 18:02:36 +02:00
for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
if (!isComputableValue(parent, value1))
continue;
2018-04-24 18:02:36 +02:00
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
if (value1.path != value2.path)
continue;
if (!isComputableValue(parent, value2))
continue;
if (value1.isIteratorValue() && value2.isIteratorValue())
continue;
if (value1.isKnown() || value2.isKnown() || value1.varId == 0 || value2.varId == 0 ||
(value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() &&
value2.isIntValue())) {
ValueFlow::Value result(0);
combineValueProperties(value1, value2, &result);
const double floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue;
const double floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue;
const bool isFloat = value1.isFloatValue() || value2.isFloatValue();
if (isFloat && Token::Match(parent, "&|^|%|<<|>>|%or%"))
continue;
if (Token::Match(parent, "<<|>>") &&
!(value1.intvalue >= 0 && value2.intvalue >= 0 && value2.intvalue < MathLib::bigint_bits))
continue;
2021-01-10 15:27:42 +01:00
if (Token::Match(parent, "/|%") && isZero<double>(floatValue2))
continue;
if (Token::Match(parent, "==|!=")) {
if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) {
if (parent->str() == "==")
result.intvalue = 0;
else if (parent->str() == "!=")
result.intvalue = 1;
} else if (value1.isIntValue() && value2.isIntValue()) {
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
} else {
continue;
}
2016-11-05 09:29:22 +01:00
setTokenValue(parent, result, settings);
} else if (Token::Match(parent, "%comp%")) {
if (!isFloat && !value1.isIntValue() && !value2.isIntValue())
continue;
if (isFloat)
result.intvalue = calculate(parent->str(), floatValue1, floatValue2);
else
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
2016-11-05 09:29:22 +01:00
setTokenValue(parent, result, settings);
} else if (Token::Match(parent, "%op%")) {
2018-04-24 18:02:36 +02:00
if (value1.isTokValue() || value2.isTokValue())
break;
if (isFloat) {
result.valueType = ValueFlow::Value::ValueType::FLOAT;
result.floatValue = calculate(parent->str(), floatValue1, floatValue2);
} else {
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
}
// If the bound comes from the second value then invert the bound when subtracting
if (Token::simpleMatch(parent, "-") && value2.bound == result.bound &&
value2.bound != ValueFlow::Value::Bound::Point)
result.invertBound();
2016-11-05 09:29:22 +01:00
setTokenValue(parent, result, settings);
}
}
}
}
}
2015-07-16 21:17:44 +02:00
// !
else if (parent->str() == "!") {
2018-07-14 13:19:41 +02:00
for (const ValueFlow::Value &val : tok->values()) {
if (!val.isIntValue())
2015-07-16 21:17:44 +02:00
continue;
2018-07-14 13:19:41 +02:00
ValueFlow::Value v(val);
2015-07-16 21:17:44 +02:00
v.intvalue = !v.intvalue;
2016-11-05 09:29:22 +01:00
setTokenValue(parent, v, settings);
}
}
// ~
else if (parent->str() == "~") {
2018-07-14 13:19:41 +02:00
for (const ValueFlow::Value &val : tok->values()) {
if (!val.isIntValue())
2016-11-05 09:29:22 +01:00
continue;
2018-07-14 13:19:41 +02:00
ValueFlow::Value v(val);
2016-11-05 09:29:22 +01:00
v.intvalue = ~v.intvalue;
int bits = 0;
2016-11-05 09:59:48 +01:00
if (settings &&
tok->valueType() &&
2016-11-05 09:29:22 +01:00
tok->valueType()->sign == ValueType::Sign::UNSIGNED &&
tok->valueType()->pointer == 0) {
if (tok->valueType()->type == ValueType::Type::INT)
bits = settings->int_bit;
else if (tok->valueType()->type == ValueType::Type::LONG)
bits = settings->long_bit;
}
if (bits > 0 && bits < MathLib::bigint_bits)
v.intvalue &= (((MathLib::biguint)1)<<bits) - 1;
2016-11-05 09:29:22 +01:00
setTokenValue(parent, v, settings);
2015-07-16 21:17:44 +02:00
}
}
// unary minus
2018-07-14 13:19:41 +02:00
else if (parent->isUnaryOp("-")) {
for (const ValueFlow::Value &val : tok->values()) {
if (!val.isIntValue() && !val.isFloatValue())
continue;
2018-07-14 13:19:41 +02:00
ValueFlow::Value v(val);
2020-09-05 21:09:11 +02:00
if (v.isIntValue()) {
if (v.intvalue == LLONG_MIN)
// Value can't be inverted
continue;
v.intvalue = -v.intvalue;
2020-09-06 07:47:17 +02:00
} else
v.floatValue = -v.floatValue;
v.invertBound();
2016-11-05 09:29:22 +01:00
setTokenValue(parent, v, settings);
}
}
// increment
else if (parent->str() == "++") {
for (const ValueFlow::Value &val : tok->values()) {
if (!val.isIntValue() && !val.isFloatValue())
continue;
ValueFlow::Value v(val);
if (parent == tok->previous()) {
if (v.isIntValue())
v.intvalue = v.intvalue + 1;
else
v.floatValue = v.floatValue + 1.0;
}
setTokenValue(parent, v, settings);
}
}
// decrement
else if (parent->str() == "--") {
for (const ValueFlow::Value &val : tok->values()) {
if (!val.isIntValue() && !val.isFloatValue())
continue;
ValueFlow::Value v(val);
if (parent == tok->previous()) {
if (v.isIntValue())
v.intvalue = v.intvalue - 1;
else
v.floatValue = v.floatValue - 1.0;
}
setTokenValue(parent, v, settings);
}
}
// Array element
2018-07-14 13:19:41 +02:00
else if (parent->str() == "[" && parent->isBinaryOp()) {
for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
if (!value1.isTokValue())
continue;
2018-07-14 13:19:41 +02:00
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
if (!value2.isIntValue())
continue;
if (value1.varId == 0 || value2.varId == 0 ||
2018-07-14 13:19:41 +02:00
(value1.varId == value2.varId && value1.varvalue == value2.varvalue)) {
ValueFlow::Value result(0);
2018-07-14 13:19:41 +02:00
result.condition = value1.condition ? value1.condition : value2.condition;
result.setInconclusive(value1.isInconclusive() | value2.isInconclusive());
result.varId = (value1.varId != 0) ? value1.varId : value2.varId;
2018-07-14 13:19:41 +02:00
result.varvalue = (result.varId == value1.varId) ? value1.intvalue : value2.intvalue;
if (value1.valueKind == value2.valueKind)
result.valueKind = value1.valueKind;
if (value1.tokvalue->tokType() == Token::eString) {
const std::string s = value1.tokvalue->strValue();
const MathLib::bigint index = value2.intvalue;
2015-11-12 16:14:27 +01:00
if (index == s.size()) {
result.intvalue = 0;
2016-11-05 09:59:48 +01:00
setTokenValue(parent, result, settings);
2015-11-12 16:14:27 +01:00
} else if (index >= 0 && index < s.size()) {
result.intvalue = s[index];
2016-11-05 09:29:22 +01:00
setTokenValue(parent, result, settings);
}
2018-07-14 13:19:41 +02:00
} else if (value1.tokvalue->str() == "{") {
MathLib::bigint index = value2.intvalue;
const Token *element = value1.tokvalue->next();
while (index > 0 && element->str() != "}") {
if (element->str() == ",")
--index;
if (Token::Match(element, "[{}()[]]"))
break;
element = element->next();
}
if (Token::Match(element, "%num% [,}]")) {
result.intvalue = MathLib::toLongNumber(element->str());
2016-11-05 09:29:22 +01:00
setTokenValue(parent, result, settings);
}
}
}
}
}
}
2019-10-19 21:08:59 +02:00
else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) {
setTokenValue(parent, value, settings);
}
}
2018-12-27 21:33:01 +01:00
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings)
{
if (valueType.pointer)
setTokenValue(parent,value,settings);
else if (valueType.type == ValueType::Type::CHAR)
setTokenValue(parent, castValue(value, valueType.sign, settings->char_bit), settings);
else if (valueType.type == ValueType::Type::SHORT)
setTokenValue(parent, castValue(value, valueType.sign, settings->short_bit), settings);
else if (valueType.type == ValueType::Type::INT)
setTokenValue(parent, castValue(value, valueType.sign, settings->int_bit), settings);
else if (valueType.type == ValueType::Type::LONG)
setTokenValue(parent, castValue(value, valueType.sign, settings->long_bit), settings);
else if (valueType.type == ValueType::Type::LONGLONG)
setTokenValue(parent, castValue(value, valueType.sign, settings->long_long_bit), settings);
else if (value.isIntValue()) {
const long long charMax = settings->signedCharMax();
const long long charMin = settings->signedCharMin();
if (charMin <= value.intvalue && value.intvalue <= charMax) {
// unknown type, but value is small so there should be no truncation etc
setTokenValue(parent,value,settings);
}
}
}
static nonneg int getSizeOfType(const Token *typeTok, const Settings *settings)
2017-10-18 23:20:04 +02:00
{
2020-05-28 21:24:48 +02:00
const ValueType &valueType = ValueType::parseDecl(typeTok, settings);
if (valueType.pointer > 0)
return settings->sizeof_pointer;
if (valueType.type == ValueType::Type::BOOL || valueType.type == ValueType::Type::CHAR)
2017-10-18 23:20:04 +02:00
return 1;
2020-05-28 21:24:48 +02:00
if (valueType.type == ValueType::Type::SHORT)
2017-10-18 23:20:04 +02:00
return settings->sizeof_short;
2020-05-28 21:24:48 +02:00
if (valueType.type == ValueType::Type::INT)
2017-10-18 23:20:04 +02:00
return settings->sizeof_int;
2020-05-28 21:24:48 +02:00
if (valueType.type == ValueType::Type::LONG)
return settings->sizeof_long;
if (valueType.type == ValueType::Type::LONGLONG)
return settings->sizeof_long_long;
if (valueType.type == ValueType::Type::WCHAR_T)
return settings->sizeof_wchar_t;
2020-05-28 21:24:48 +02:00
return 0;
2017-10-18 23:20:04 +02:00
}
2016-05-07 20:18:07 +02:00
size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings)
2019-03-11 20:32:24 +01:00
{
if (vt.pointer)
return settings->sizeof_pointer;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::CHAR)
2019-03-11 20:32:24 +01:00
return 1;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::SHORT)
2019-03-11 20:32:24 +01:00
return settings->sizeof_short;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::WCHAR_T)
return settings->sizeof_wchar_t;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::INT)
2019-03-11 20:32:24 +01:00
return settings->sizeof_int;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::LONG)
2019-03-11 20:32:24 +01:00
return settings->sizeof_long;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::LONGLONG)
2019-03-11 20:32:24 +01:00
return settings->sizeof_long_long;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::FLOAT)
2019-03-11 20:32:24 +01:00
return settings->sizeof_float;
2020-05-28 21:24:48 +02:00
if (vt.type == ValueType::Type::DOUBLE)
2019-03-11 20:32:24 +01:00
return settings->sizeof_double;
2020-11-11 22:51:17 +01:00
if (vt.type == ValueType::Type::LONGDOUBLE)
return settings->sizeof_long_double;
2019-03-11 20:32:24 +01:00
return 0;
}
2016-05-07 20:18:07 +02:00
// Handle various constants..
static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, bool cpp)
2016-05-07 20:18:07 +02:00
{
if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) {
ValueFlow::Value value(MathLib::toLongNumber(tok->str()));
if (!tok->isTemplateArg())
value.setKnown();
setTokenValue(tok, value, settings);
2016-11-13 22:59:56 +01:00
} else if (tok->isNumber() && MathLib::isFloat(tok->str())) {
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::FLOAT;
2016-11-13 22:59:56 +01:00
value.floatValue = MathLib::toDoubleNumber(tok->str());
if (!tok->isTemplateArg())
value.setKnown();
setTokenValue(tok, value, settings);
} else if (tok->enumerator() && tok->enumerator()->value_known) {
2016-05-07 20:18:07 +02:00
ValueFlow::Value value(tok->enumerator()->value);
if (!tok->isTemplateArg())
value.setKnown();
setTokenValue(tok, value, settings);
2016-10-18 21:44:02 +02:00
} else if (tok->str() == "NULL" || (cpp && tok->str() == "nullptr")) {
ValueFlow::Value value(0);
if (!tok->isTemplateArg())
value.setKnown();
setTokenValue(tok, value, settings);
2016-12-18 14:03:48 +01:00
} else if (Token::simpleMatch(tok, "sizeof (")) {
const Token *tok2 = tok->tokAt(2);
// skip over tokens to find variable or type
while (Token::Match(tok2, "%name% ::|.|[")) {
if (tok2->next()->str() == "[")
tok2 = tok2->linkAt(1)->next();
else
tok2 = tok2->tokAt(2);
}
2019-04-06 06:55:46 +02:00
if (Token::simpleMatch(tok, "sizeof ( *")) {
2019-03-11 20:32:24 +01:00
const ValueType *vt = tok->tokAt(2)->valueType();
const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0;
2019-03-11 20:32:24 +01:00
if (sz > 0) {
ValueFlow::Value value(sz);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok->next(), value, settings);
2019-03-11 20:32:24 +01:00
}
} else if (tok2->enumerator() && tok2->enumerator()->scope) {
long long size = settings->sizeof_int;
const Token * type = tok2->enumerator()->scope->enumType;
if (type) {
2017-10-18 23:20:04 +02:00
size = getSizeOfType(type, settings);
2020-05-28 21:24:48 +02:00
if (size == 0)
tok->linkAt(1);
}
ValueFlow::Value value(size);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok, value, settings);
setTokenValue(tok->next(), value, settings);
} else if (tok2->type() && tok2->type()->isEnumType()) {
long long size = settings->sizeof_int;
if (tok2->type()->classScope) {
const Token * type = tok2->type()->classScope->enumType;
if (type) {
2017-10-18 23:20:04 +02:00
size = getSizeOfType(type, settings);
}
}
ValueFlow::Value value(size);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok, value, settings);
setTokenValue(tok->next(), value, settings);
2016-12-18 14:03:48 +01:00
} else if (Token::Match(tok, "sizeof ( %var% ) / sizeof (") && tok->next()->astParent() == tok->tokAt(4)) {
// Get number of elements in array
const Token *sz1 = tok->tokAt(2);
const Token *sz2 = tok->tokAt(7);
const nonneg int varid1 = sz1->varId();
2016-12-18 14:03:48 +01:00
if (varid1 &&
sz1->variable() &&
sz1->variable()->isArray() &&
!sz1->variable()->dimensions().empty() &&
sz1->variable()->dimensionKnown(0) &&
(Token::Match(sz2, "* %varid% )", varid1) || Token::Match(sz2, "%varid% [ 0 ] )", varid1))) {
ValueFlow::Value value(sz1->variable()->dimension(0));
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok->tokAt(4), value, settings);
2016-12-18 14:03:48 +01:00
}
} else if (Token::Match(tok2, "%var% )")) {
const Variable *var = tok2->variable();
// only look for single token types (no pointers or references yet)
if (var && var->typeStartToken() == var->typeEndToken()) {
// find the size of the type
size_t size = 0;
if (var->isEnumType()) {
size = settings->sizeof_int;
if (var->type()->classScope && var->type()->classScope->enumType)
size = getSizeOfType(var->type()->classScope->enumType, settings);
} else if (var->valueType()) {
size = ValueFlow::getSizeOf(*var->valueType(), settings);
} else if (!var->type()) {
size = getSizeOfType(var->typeStartToken(), settings);
}
// find the number of elements
size_t count = 1;
for (size_t i = 0; i < var->dimensions().size(); ++i) {
if (var->dimensionKnown(i))
count *= var->dimension(i);
else
count = 0;
}
if (size && count > 0) {
ValueFlow::Value value(count * size);
if (settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok, value, settings);
setTokenValue(tok->next(), value, settings);
}
}
} else if (tok2->tokType() == Token::eString) {
size_t sz = Token::getStrSize(tok2, settings);
if (sz > 0) {
ValueFlow::Value value(sz);
value.setKnown();
setTokenValue(const_cast<Token *>(tok->next()), value, settings);
}
} else if (tok2->tokType() == Token::eChar) {
nonneg int sz = 0;
if (cpp && settings->standards.cpp >= Standards::CPP20 && tok2->isUtf8())
sz = 1;
else if (tok2->isUtf16())
sz = 2;
else if (tok2->isUtf32())
sz = 4;
else if (tok2->isLong())
sz = settings->sizeof_wchar_t;
else if ((tok2->isCChar() && !cpp) || (tok2->isCMultiChar()))
sz = settings->sizeof_int;
else
sz = 1;
if (sz > 0) {
ValueFlow::Value value(sz);
value.setKnown();
setTokenValue(tok->next(), value, settings);
}
2016-12-18 14:03:48 +01:00
} else if (!tok2->type()) {
const ValueType &vt = ValueType::parseDecl(tok2,settings);
const size_t sz = ValueFlow::getSizeOf(vt, settings);
2019-03-11 20:32:24 +01:00
if (sz > 0) {
ValueFlow::Value value(sz);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
value.setKnown();
setTokenValue(tok->next(), value, settings);
2016-12-18 14:03:48 +01:00
}
}
// skip over enum
tok = tok->linkAt(1);
2016-05-07 20:18:07 +02:00
}
return tok->next();
2016-05-07 20:18:07 +02:00
}
static void valueFlowNumber(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok;) {
2016-10-18 21:44:02 +02:00
tok = valueFlowSetConstantValue(tok, tokenlist->getSettings(), tokenlist->isCPP());
}
2015-07-14 18:02:26 +02:00
if (tokenlist->isCPP()) {
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) {
ValueFlow::Value value(tok->str() == "true");
if (!tok->isTemplateArg())
value.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok, value, tokenlist->getSettings());
} else if (Token::Match(tok, "[(,] NULL [,)]")) {
// NULL function parameters are not simplified in the
// normal tokenlist
ValueFlow::Value value(0);
if (!tok->isTemplateArg())
value.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok->next(), value, tokenlist->getSettings());
}
2015-07-14 18:02:26 +02:00
}
}
}
static void valueFlowString(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->tokType() == Token::eString) {
ValueFlow::Value strvalue;
strvalue.valueType = ValueFlow::Value::ValueType::TOK;
strvalue.tokvalue = tok;
strvalue.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok, strvalue, tokenlist->getSettings());
}
}
}
static void valueFlowArray(TokenList *tokenlist)
{
std::map<nonneg int, const Token *> constantArrays;
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->varId() > 0) {
// array
const std::map<nonneg int, const Token *>::const_iterator it = constantArrays.find(tok->varId());
2015-08-28 14:27:56 +02:00
if (it != constantArrays.end()) {
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::TOK;
2015-08-28 14:27:56 +02:00
value.tokvalue = it->second;
value.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok, value, tokenlist->getSettings());
}
// pointer = array
else if (tok->variable() &&
tok->variable()->isArray() &&
Token::simpleMatch(tok->astParent(), "=") &&
tok == tok->astParent()->astOperand2() &&
tok->astParent()->astOperand1() &&
tok->astParent()->astOperand1()->variable() &&
tok->astParent()->astOperand1()->variable()->isPointer()) {
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::TOK;
value.tokvalue = tok;
value.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok, value, tokenlist->getSettings());
2015-08-28 14:27:56 +02:00
}
continue;
}
if (Token::Match(tok, "const %type% %var% [ %num%| ] = {")) {
const Token *vartok = tok->tokAt(2);
const Token *rhstok = vartok->next()->link()->tokAt(2);
constantArrays[vartok->varId()] = rhstok;
tok = rhstok->link();
2015-08-28 14:27:56 +02:00
continue;
}
else if (Token::Match(tok, "const char %var% [ %num%| ] = %str% ;")) {
const Token *vartok = tok->tokAt(2);
2015-02-09 08:23:36 +01:00
const Token *strtok = vartok->next()->link()->tokAt(2);
constantArrays[vartok->varId()] = strtok;
tok = strtok->next();
2015-08-28 14:27:56 +02:00
continue;
}
}
}
static bool isNonZero(const Token *tok)
{
return tok && (!tok->hasKnownIntValue() || tok->values().front().intvalue != 0);
}
static const Token *getOtherOperand(const Token *tok)
{
if (!tok)
return nullptr;
if (!tok->astParent())
return nullptr;
if (tok->astParent()->astOperand1() != tok)
return tok->astParent()->astOperand1();
if (tok->astParent()->astOperand2() != tok)
return tok->astParent()->astOperand2();
return nullptr;
}
static void valueFlowArrayBool(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->hasKnownIntValue())
continue;
const Variable *var = nullptr;
bool known = false;
std::list<ValueFlow::Value>::const_iterator val =
std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue));
if (val == tok->values().end()) {
var = tok->variable();
known = true;
} else {
var = val->tokvalue->variable();
known = val->isKnown();
}
if (!var)
continue;
if (!var->isArray() || var->isArgument() || var->isStlType())
continue;
if (isNonZero(getOtherOperand(tok)) && Token::Match(tok->astParent(), "%comp%"))
continue;
// TODO: Check for function argument
if ((astIsBool(tok->astParent()) && !Token::Match(tok->astParent(), "(|%name%")) ||
(tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|for ("))) {
ValueFlow::Value value{1};
if (known)
value.setKnown();
setTokenValue(tok, value, tokenlist->getSettings());
}
}
}
static void valueFlowPointerAlias(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
// not address of
2018-07-14 22:36:08 +02:00
if (!tok->isUnaryOp("&"))
continue;
// parent should be a '='
if (!Token::simpleMatch(tok->astParent(), "="))
continue;
// child should be some buffer or variable
const Token *vartok = tok->astOperand1();
while (vartok) {
if (vartok->str() == "[")
vartok = vartok->astOperand1();
else if (vartok->str() == "." || vartok->str() == "::")
vartok = vartok->astOperand2();
else
break;
}
if (!(vartok && vartok->variable() && !vartok->variable()->isPointer()))
continue;
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::TOK;
value.tokvalue = tok;
2016-11-05 09:59:48 +01:00
setTokenValue(tok, value, tokenlist->getSettings());
}
}
static void valueFlowPointerAliasDeref(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->isUnaryOp("*"))
continue;
if (!astIsPointer(tok->astOperand1()))
continue;
const Token* lifeTok = nullptr;
ErrorPath errorPath;
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
if (!v.isLocalLifetimeValue())
continue;
lifeTok = v.tokvalue;
errorPath = v.errorPath;
}
if (!lifeTok)
continue;
if (lifeTok->varId() == 0)
continue;
const Variable * var = lifeTok->variable();
if (!var)
continue;
if (!var->isConst() && isVariableChanged(lifeTok->next(), tok, lifeTok->varId(), !var->isLocal(), tokenlist->getSettings(), tokenlist->isCPP()))
continue;
for (const ValueFlow::Value& v:lifeTok->values()) {
// TODO: Move container size values to generic forward
// Forward uninit values since not all values can be forwarded directly
if (!(v.isContainerSizeValue() || v.isUninitValue()))
continue;
ValueFlow::Value value = v;
value.errorPath.insert(value.errorPath.begin(), errorPath.begin(), errorPath.end());
setTokenValue(tok, value, tokenlist->getSettings());
}
}
}
static void valueFlowBitAnd(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->str() != "&")
continue;
if (tok->hasKnownValue())
continue;
if (!tok->astOperand1() || !tok->astOperand2())
continue;
MathLib::bigint number;
if (MathLib::isInt(tok->astOperand1()->str()))
number = MathLib::toLongNumber(tok->astOperand1()->str());
else if (MathLib::isInt(tok->astOperand2()->str()))
number = MathLib::toLongNumber(tok->astOperand2()->str());
else
continue;
int bit = 0;
2017-10-21 22:08:34 +02:00
while (bit <= (MathLib::bigint_bits - 2) && ((((MathLib::bigint)1) << bit) < number))
++bit;
2017-10-21 22:17:07 +02:00
if ((((MathLib::bigint)1) << bit) == number) {
2016-11-05 09:59:48 +01:00
setTokenValue(tok, ValueFlow::Value(0), tokenlist->getSettings());
setTokenValue(tok, ValueFlow::Value(number), tokenlist->getSettings());
}
}
}
static void valueFlowSameExpressions(TokenList *tokenlist)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (tok->hasKnownValue())
continue;
if (!tok->astOperand1() || !tok->astOperand2())
continue;
if (tok->astOperand1()->isLiteral() || tok->astOperand2()->isLiteral())
continue;
if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false))
continue;
ValueFlow::Value val;
if (Token::Match(tok, "==|>=|<=|/")) {
val = ValueFlow::Value(1);
val.setKnown();
}
if (Token::Match(tok, "!=|>|<|%|-")) {
val = ValueFlow::Value(0);
val.setKnown();
}
if (!val.isKnown())
continue;
if (isSameExpression(tokenlist->isCPP(), false, tok->astOperand1(), tok->astOperand2(), tokenlist->getSettings()->library, true, true, &val.errorPath)) {
setTokenValue(tok, val, tokenlist->getSettings());
}
}
}
static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue)
{
if (expr->hasKnownIntValue()) {
if (minvalue)
*minvalue = expr->values().front().intvalue;
if (maxvalue)
*maxvalue = expr->values().front().intvalue;
return true;
}
if (expr->str() == "&" && expr->astOperand1() && expr->astOperand2()) {
MathLib::bigint vals[4];
bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]);
bool rhsHasKnownRange = getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]);
if (!lhsHasKnownRange && !rhsHasKnownRange)
return false;
if (!lhsHasKnownRange || !rhsHasKnownRange) {
if (minvalue)
*minvalue = lhsHasKnownRange ? vals[0] : vals[2];
if (maxvalue)
*maxvalue = lhsHasKnownRange ? vals[1] : vals[3];
} else {
if (minvalue)
*minvalue = vals[0] & vals[2];
if (maxvalue)
*maxvalue = vals[1] & vals[3];
}
return true;
}
if (expr->str() == "%" && expr->astOperand1() && expr->astOperand2()) {
MathLib::bigint vals[4];
if (!getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]))
return false;
if (vals[2] <= 0)
return false;
bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]);
if (lhsHasKnownRange && vals[0] < 0)
return false;
// If lhs has unknown value, it must be unsigned
if (!lhsHasKnownRange && (!expr->astOperand1()->valueType() || expr->astOperand1()->valueType()->sign != ValueType::Sign::UNSIGNED))
return false;
if (minvalue)
*minvalue = 0;
if (maxvalue)
*maxvalue = vals[3] - 1;
return true;
}
return false;
}
static void valueFlowRightShift(TokenList *tokenList, const Settings* settings)
{
for (Token *tok = tokenList->front(); tok; tok = tok->next()) {
if (tok->str() != ">>")
continue;
if (tok->hasKnownValue())
continue;
if (!tok->astOperand1() || !tok->astOperand2())
continue;
if (!tok->astOperand2()->hasKnownValue())
continue;
const MathLib::bigint rhsvalue = tok->astOperand2()->values().front().intvalue;
if (rhsvalue < 0)
continue;
if (!tok->astOperand1()->valueType() || !tok->astOperand1()->valueType()->isIntegral())
continue;
if (!tok->astOperand2()->valueType() || !tok->astOperand2()->valueType()->isIntegral())
continue;
MathLib::bigint lhsmax=0;
if (!getExpressionRange(tok->astOperand1(), nullptr, &lhsmax))
continue;
if (lhsmax < 0)
continue;
int lhsbits;
if ((tok->astOperand1()->valueType()->type == ValueType::Type::CHAR) ||
(tok->astOperand1()->valueType()->type == ValueType::Type::SHORT) ||
(tok->astOperand1()->valueType()->type == ValueType::Type::WCHAR_T) ||
(tok->astOperand1()->valueType()->type == ValueType::Type::BOOL) ||
(tok->astOperand1()->valueType()->type == ValueType::Type::INT))
lhsbits = settings->int_bit;
else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONG)
lhsbits = settings->long_bit;
else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONGLONG)
lhsbits = settings->long_long_bit;
else
continue;
if (rhsvalue >= lhsbits || rhsvalue >= MathLib::bigint_bits || (1ULL << rhsvalue) <= lhsmax)
continue;
ValueFlow::Value val(0);
val.setKnown();
setTokenValue(tok, val, tokenList->getSettings());
}
}
static std::vector<MathLib::bigint> minUnsignedValue(const Token* tok, int depth = 8)
{
std::vector<MathLib::bigint> result = {};
if (!tok)
return result;
if (depth < 0)
return result;
if (tok->hasKnownIntValue()) {
result = {tok->values().front().intvalue};
} else if (tok->isConstOp() && tok->astOperand1() && tok->astOperand2()) {
std::vector<MathLib::bigint> op1 = minUnsignedValue(tok->astOperand1(), depth - 1);
std::vector<MathLib::bigint> op2 = minUnsignedValue(tok->astOperand2(), depth - 1);
if (!op1.empty() && !op2.empty()) {
result = calculate<std::vector<MathLib::bigint>>(tok->str(), op1.front(), op2.front());
}
}
if (result.empty() && astIsUnsigned(tok))
result = {0};
return result;
}
static void valueFlowImpossibleValues(TokenList* tokenList, const Settings* settings)
{
for (Token* tok = tokenList->front(); tok; tok = tok->next()) {
if (tok->hasKnownIntValue())
continue;
if (astIsUnsigned(tok)) {
std::vector<MathLib::bigint> minvalue = minUnsignedValue(tok);
if (minvalue.empty())
continue;
ValueFlow::Value value{std::max<MathLib::bigint>(0, minvalue.front()) - 1};
value.bound = ValueFlow::Value::Bound::Upper;
value.setImpossible();
setTokenValue(tok, value, settings);
}
if (Token::simpleMatch(tok, "%") && tok->astOperand2() && tok->astOperand2()->hasKnownIntValue()) {
ValueFlow::Value value{tok->astOperand2()->values().front()};
value.bound = ValueFlow::Value::Bound::Lower;
value.setImpossible();
setTokenValue(tok, value, settings);
}
}
}
Fix #9647: Set correct enum value (#2856) * Tokenize: Set varId for variables in enum Set varIds in enum values. It was previously disabled in 5119ae84b879fad to avoid issues with enums named the same as global variables. Take care to only set varids to variables used to set the value of an enumerator, not the enumerator itself. This is somewhat complicated by the fact that at the time this happens, astOperand1(), astOperand2(), astParent() etc are not set. The current implementation is not perfect, for example in the code below, y will not have a varid set, but x and z will. This is deemed sufficient for now. int x, y, z; enum E { a = f(x, y, z); }; * Fix #9647: Value of enums with variables as init values C++ allows enum values to be set using constexprs, which cppcheck did not handle before. To solve this, add a new pass to valueflow to update enum values after global consts have been processed. In order to do so, I moved all settings of enum values to valueflow. After setting the enum values, we need another call to valueFlowNumber() to actually set users of the enums. There is still room for improvements, since each pass of valueFlowGlobalConstVar() and valueFlowEnumValue() only sets variables that are possible to set directly, and not if setting the value of a variable allows us to set the value of another. For example constexpr int a = 5; constexpr int b = a + 5; enum E { X = a }; constexpr E e = X; Here both b and e will not have their values set, even though cppcheck should be possible to figure out their values. That's for another PR though. This was tested by running test-my-pr.py with 500 packages. The only difference was one error message in fairy-stockfish_11.1, where cppcheck now printed the correct size of an array instead of 2147483648 which I assume is some kind of default value. In that package, using a constexpr when setting enum values is common, but as mentioned, there was no change in the number of warnings.
2020-10-22 07:45:04 +02:00
static void valueFlowEnumValue(SymbolDatabase * symboldatabase, const Settings * settings)
{
for (Scope & scope : symboldatabase->scopeList) {
if (scope.type != Scope::eEnum)
continue;
MathLib::bigint value = 0;
bool prev_enum_is_known = true;
for (Enumerator & enumerator : scope.enumeratorList) {
if (enumerator.start) {
Token *rhs = enumerator.start->previous()->astOperand2();
ValueFlow::valueFlowConstantFoldAST(rhs, settings);
if (rhs && rhs->hasKnownIntValue()) {
enumerator.value = rhs->values().front().intvalue;
enumerator.value_known = true;
value = enumerator.value + 1;
prev_enum_is_known = true;
} else
prev_enum_is_known = false;
} else if (prev_enum_is_known) {
enumerator.value = value++;
enumerator.value_known = true;
}
}
}
}
static void valueFlowGlobalConstVar(TokenList* tokenList, const Settings *settings)
{
// Get variable values...
std::map<const Variable*, ValueFlow::Value> vars;
for (const Token* tok = tokenList->front(); tok; tok = tok->next()) {
if (!tok->variable())
continue;
// Initialization...
if (tok == tok->variable()->nameToken() &&
!tok->variable()->isVolatile() &&
!tok->variable()->isArgument() &&
tok->variable()->isConst() &&
tok->valueType() &&
tok->valueType()->isIntegral() &&
tok->valueType()->pointer == 0 &&
tok->valueType()->constness == 1 &&
Token::Match(tok, "%name% =") &&
tok->next()->astOperand2() &&
tok->next()->astOperand2()->hasKnownIntValue()) {
vars[tok->variable()] = tok->next()->astOperand2()->values().front();
}
}
// Set values..
for (Token* tok = tokenList->front(); tok; tok = tok->next()) {
if (!tok->variable())
continue;
std::map<const Variable*, ValueFlow::Value>::const_iterator var = vars.find(tok->variable());
if (var == vars.end())
continue;
setTokenValue(tok, var->second, settings);
}
}
static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *settings)
{
// Get variable values...
std::map<const Variable *, ValueFlow::Value> vars;
for (const Token *tok = tokenList->front(); tok; tok = tok->next()) {
if (!tok->variable())
continue;
// Initialization...
if (tok == tok->variable()->nameToken() &&
tok->variable()->isStatic() &&
!tok->variable()->isConst() &&
tok->valueType() &&
tok->valueType()->isIntegral() &&
tok->valueType()->pointer == 0 &&
tok->valueType()->constness == 0 &&
Token::Match(tok, "%name% =") &&
tok->next()->astOperand2() &&
tok->next()->astOperand2()->hasKnownIntValue()) {
vars[tok->variable()] = tok->next()->astOperand2()->values().front();
} else {
// If variable is written anywhere in TU then remove it from vars
if (!tok->astParent())
continue;
if (Token::Match(tok->astParent(), "++|--|&") && !tok->astParent()->astOperand2())
vars.erase(tok->variable());
else if (tok->astParent()->isAssignmentOp()) {
if (tok == tok->astParent()->astOperand1())
vars.erase(tok->variable());
else if (tokenList->isCPP() && Token::Match(tok->astParent()->tokAt(-2), "& %name% ="))
vars.erase(tok->variable());
2018-04-17 19:51:27 +02:00
} else if (isLikelyStreamRead(tokenList->isCPP(), tok->astParent())) {
vars.erase(tok->variable());
} else if (Token::Match(tok->astParent(), "[(,]"))
vars.erase(tok->variable());
}
}
// Set values..
for (Token *tok = tokenList->front(); tok; tok = tok->next()) {
if (!tok->variable())
continue;
std::map<const Variable *, ValueFlow::Value>::const_iterator var = vars.find(tok->variable());
if (var == vars.end())
continue;
setTokenValue(tok, var->second, settings);
}
}
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
2020-11-11 09:15:36 +01:00
const Token* const endToken,
const Variable* const var,
std::list<ValueFlow::Value> values,
TokenList* const tokenlist,
const Settings* const settings);
2020-11-10 16:00:55 +01:00
static void valueFlowReverse(TokenList* tokenlist,
Token* tok,
const Token* const varToken,
ValueFlow::Value val,
ValueFlow::Value val2,
2020-11-10 16:00:55 +01:00
ErrorLogger* errorLogger,
const Settings* settings);
static bool isConditionKnown(const Token* tok, bool then)
{
const char* op = "||";
if (then)
op = "&&";
const Token* parent = tok->astParent();
while (parent && (parent->str() == op || parent->str() == "!"))
parent = parent->astParent();
return (parent && parent->str() == "(");
}
2020-11-10 16:00:55 +01:00
static const std::string& invertAssign(const std::string& assign)
{
static std::unordered_map<std::string, std::string> lookup = {
2020-11-11 09:15:36 +01:00
{"+=", "-="}, {"-=", "+="}, {"*=", "/="}, {"/=", "*="}, {"<<=", ">>="}, {">>=", "<<="}, {"^=", "^="}
};
static std::string empty;
2020-11-10 16:00:55 +01:00
auto it = lookup.find(assign);
if (it == lookup.end())
return empty;
else
return it->second;
}
static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign, const ValueFlow::Value &rhsValue)
{
if (lhsValue.isIntValue()) {
if (assign == "=")
lhsValue.intvalue = rhsValue.intvalue;
else if (assign == "+=")
lhsValue.intvalue += rhsValue.intvalue;
else if (assign == "-=")
lhsValue.intvalue -= rhsValue.intvalue;
else if (assign == "*=")
lhsValue.intvalue *= rhsValue.intvalue;
else if (assign == "/=") {
if (rhsValue.intvalue == 0)
return false;
lhsValue.intvalue /= rhsValue.intvalue;
} else if (assign == "%=") {
if (rhsValue.intvalue == 0)
return false;
lhsValue.intvalue %= rhsValue.intvalue;
} else if (assign == "&=")
lhsValue.intvalue &= rhsValue.intvalue;
else if (assign == "|=")
lhsValue.intvalue |= rhsValue.intvalue;
else if (assign == "^=")
lhsValue.intvalue ^= rhsValue.intvalue;
else if (assign == "<<=") {
if (rhsValue.intvalue < 0)
return false;
lhsValue.intvalue <<= rhsValue.intvalue;
} else if (assign == ">>=") {
if (rhsValue.intvalue < 0)
return false;
lhsValue.intvalue >>= rhsValue.intvalue;
} else
return false;
} else if (lhsValue.isFloatValue()) {
if (assign == "=")
lhsValue.intvalue = rhsValue.intvalue;
else if (assign == "+=")
lhsValue.floatValue += rhsValue.intvalue;
else if (assign == "-=")
lhsValue.floatValue -= rhsValue.intvalue;
else if (assign == "*=")
lhsValue.floatValue *= rhsValue.intvalue;
else if (assign == "/=")
lhsValue.floatValue /= rhsValue.intvalue;
else
return false;
} else {
return false;
}
return true;
}
template<class T>
struct SingleRange {
T* x;
T* begin() const {
return x;
}
T* end() const {
return x+1;
}
};
template<class T>
SingleRange<T> MakeSingleRange(T& x)
{
return {&x};
}
class SelectValueFromVarIdMapRange {
using M = std::unordered_map<nonneg int, ValueFlow::Value>;
struct Iterator {
using iterator_category = std::forward_iterator_tag;
using value_type = const ValueFlow::Value;
using pointer = value_type *;
using reference = value_type &;
explicit Iterator(const M::const_iterator &it)
: mIt(it) {
}
reference operator*() const {
return mIt->second;
}
pointer operator->() {
return &mIt->second;
}
Iterator &operator++() {
// cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment
mIt++;
return *this;
}
friend bool operator==(const Iterator &a, const Iterator &b) {
return a.mIt == b.mIt;
}
friend bool operator!=(const Iterator &a, const Iterator &b) {
return a.mIt != b.mIt;
}
private:
M::const_iterator mIt;
};
public:
explicit SelectValueFromVarIdMapRange(const M *m)
: mMap(m) {
}
Iterator begin() const {
return Iterator(mMap->begin());
}
Iterator end() const {
return Iterator(mMap->end());
}
private:
const M *mMap;
};
// Check if its an alias of the variable or is being aliased to this variable
template<typename V>
static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr)
{
if (tok->varId() == varid)
return false;
if (tok->varId() == 0)
return false;
if (isAliasOf(tok, varid, inconclusive))
return true;
if (var && !var->isPointer())
return false;
// Search through non value aliases
for (const ValueFlow::Value &val : values) {
if (!val.isNonValue())
continue;
if (val.isInconclusive())
continue;
if (val.isLifetimeValue() && !val.isLocalLifetimeValue())
continue;
if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
continue;
if (!Token::Match(val.tokvalue, ".|&|*|%var%"))
continue;
if (astHasVar(val.tokvalue, tok->varId()))
return true;
}
return false;
}
static const ValueFlow::Value* getKnownValue(const Token* tok, ValueFlow::Value::ValueType type)
{
if (!tok)
return nullptr;
auto it = std::find_if(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) {
return v.isKnown() && v.valueType == type;
});
if (it != tok->values().end())
return &*it;
return nullptr;
}
static bool bifurcate(const Token* tok, const std::set<nonneg int>& varids, const Settings* settings, int depth = 20);
static bool bifurcateVariableChanged(const Variable* var,
const std::set<nonneg int>& varids,
const Token* start,
const Token* end,
const Settings* settings,
int depth = 20)
{
bool result = false;
const Token* tok = start;
while ((tok = findVariableChanged(
2021-01-22 21:47:24 +01:00
tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings, true))) {
if (Token::Match(tok->astParent(), "%assign%")) {
if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1))
return true;
} else {
result = true;
}
}
return result;
}
static bool bifurcate(const Token* tok, const std::set<nonneg int>& varids, const Settings* settings, int depth)
{
if (depth < 0)
return false;
if (!tok)
return true;
if (tok->hasKnownIntValue())
return true;
if (Token::Match(tok, "%cop%"))
return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth);
if (Token::Match(tok, "%var%")) {
if (varids.count(tok->varId()) > 0)
return true;
const Variable* var = tok->variable();
if (!var)
return false;
const Token* start = var->declEndToken();
if (!start)
return false;
if (start->strAt(-1) == ")" || start->strAt(-1) == "}")
return false;
if (Token::Match(start, "; %varid% =", var->declarationId()))
start = start->tokAt(2);
if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth))
return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1);
return false;
}
return false;
}
2020-11-10 16:00:55 +01:00
struct ValueFlowAnalyzer : Analyzer {
const TokenList* tokenlist;
ProgramMemoryState pms;
2020-11-10 16:00:55 +01:00
ValueFlowAnalyzer() : tokenlist(nullptr), pms() {}
2020-11-10 16:00:55 +01:00
ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms() {}
virtual const ValueFlow::Value* getValue(const Token* tok) const = 0;
virtual ValueFlow::Value* getValue(const Token* tok) = 0;
virtual void makeConditional() = 0;
virtual void addErrorPath(const Token* tok, const std::string& s) = 0;
virtual bool match(const Token* tok) const = 0;
virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0;
using ProgramState = std::unordered_map<nonneg int, ValueFlow::Value>;
virtual ProgramState getProgramState() const = 0;
virtual const ValueType* getValueType(const Token*) const {
return nullptr;
}
virtual int getIndirect(const Token* tok) const {
const ValueFlow::Value* value = getValue(tok);
if (value)
return value->indirect;
return 0;
}
virtual bool isGlobal() const {
return false;
}
virtual bool invalid() const {
return false;
}
bool isCPP() const {
return tokenlist->isCPP();
}
const Settings* getSettings() const {
return tokenlist->getSettings();
}
virtual Action isModified(const Token* tok) const {
Action read = Action::Read;
bool inconclusive = false;
if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive))
return read | Action::Invalid;
if (inconclusive)
return read | Action::Inconclusive;
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
return read | Action::Invalid;
2020-11-10 16:00:55 +01:00
const ValueFlow::Value* value = getValue(tok);
// Check if its assigned to the same value
if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) &&
astIsIntegral(tok->astParent()->astOperand2(), false)) {
std::vector<int> result = evaluate(tok->astParent()->astOperand2());
if (!result.empty() && value->equalTo(result.front()))
return Action::Idempotent;
}
return Action::Invalid;
}
return read;
}
virtual Action isAliasModified(const Token* tok) const {
int indirect = 0;
int baseIndirect = 0;
const ValueType* vt = getValueType(tok);
if (vt)
baseIndirect = vt->pointer;
if (tok->valueType())
indirect = std::max<int>(0, tok->valueType()->pointer - baseIndirect);
if (isVariableChanged(tok, indirect, getSettings(), isCPP()))
return Action::Invalid;
return Action::None;
2020-02-13 17:04:05 +01:00
}
2020-11-11 09:15:36 +01:00
virtual Action isWritable(const Token* tok, Direction d) const {
const ValueFlow::Value* value = getValue(tok);
if (!value)
return Action::None;
if (!(value->isIntValue() || value->isFloatValue()))
return Action::None;
const Token* parent = tok->astParent();
if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
parent->astOperand2()->hasKnownValue()) {
2020-11-10 16:00:55 +01:00
// If the operator is invertible
if (d == Direction::Reverse && (parent->str() == "&=" || parent->str() == "|=" || parent->str() == "%="))
return Action::None;
const Token* rhs = parent->astOperand2();
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
Action a;
if (!rhsValue)
a = Action::Invalid;
else
a = Action::Write;
2020-11-10 16:00:55 +01:00
if (parent->str() != "=") {
a |= Action::Read;
2020-11-10 16:00:55 +01:00
} else {
if (rhsValue && !value->isImpossible() && value->equalValue(*rhsValue))
a = Action::Idempotent;
a |= Action::Incremental;
2020-11-10 16:00:55 +01:00
}
return a;
}
// increment/decrement
if (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--")) {
return Action::Read | Action::Write | Action::Incremental;
}
return Action::None;
}
2020-11-11 09:15:36 +01:00
static const std::string& getAssign(const Token* tok, Direction d) {
2020-11-10 16:00:55 +01:00
if (d == Direction::Forward)
return tok->str();
else
return invertAssign(tok->str());
}
2020-11-11 09:15:36 +01:00
virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const {
if (!value)
return;
if (!tok->astParent())
return;
if (tok->astParent()->isAssignmentOp()) {
// TODO: Check result
if (evalAssignment(*value,
2020-11-10 16:00:55 +01:00
getAssign(tok->astParent(), d),
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
value->infoString());
if (tok->astParent()->str() == "=")
value->errorPath.clear();
value->errorPath.emplace_back(tok, info);
} else {
// TODO: Don't set to zero
value->intvalue = 0;
}
} else if (tok->astParent()->tokType() == Token::eIncDecOp) {
2020-11-10 16:00:55 +01:00
bool inc = tok->astParent()->str() == "++";
std::string opName(inc ? "incremented" : "decremented");
if (d == Direction::Reverse)
inc = !inc;
value->intvalue += (inc ? 1 : -1);
2020-11-10 16:00:55 +01:00
const std::string info(tok->str() + " is " + opName + "', new value is " + value->infoString());
value->errorPath.emplace_back(tok, info);
}
}
Action analyzeMatch(const Token* tok, Direction d) const {
const Token* parent = tok->astParent();
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
return Action::Read;
Action w = isWritable(tok, d);
if (w != Action::None)
return w;
// Check for modifications by function calls
return isModified(tok);
}
2021-02-10 11:42:00 +01:00
Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const {
if (!ref)
return Action::None;
// If its an inconclusiveRef then ref != tok
assert(!inconclusiveRef || ref != tok);
bool inconclusive = false;
if (match(ref)) {
if (inconclusiveRef) {
Action a = isModified(tok);
if (a.isModified() || a.isInconclusive())
return Action::Inconclusive;
} else {
return analyzeMatch(tok, d) | Action::Match;
}
} else if (ref->isUnaryOp("*")) {
const Token* lifeTok = nullptr;
for (const ValueFlow::Value& v:ref->astOperand1()->values()) {
if (!v.isLocalLifetimeValue())
continue;
if (lifeTok)
return Action::None;
lifeTok = v.tokvalue;
}
if (lifeTok && match(lifeTok)) {
Action a = Action::Read;
if (isModified(tok).isModified())
a = Action::Invalid;
if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok))
a |= Action::Invalid;
if (inconclusiveRef && a.isModified())
return Action::Inconclusive;
return a;
}
return Action::None;
} else if (isAlias(ref, inconclusive)) {
inconclusive |= inconclusiveRef;
Action a = isAliasModified(tok);
if (inconclusive && a.isModified())
return Action::Inconclusive;
else
return a;
}
return Action::None;
}
virtual Action analyze(const Token* tok, Direction d) const OVERRIDE {
if (invalid())
return Action::Invalid;
// Follow references
std::vector<ReferenceToken> refs = followAllReferences(tok);
const bool inconclusiveRefs = refs.size() != 1;
2021-02-10 11:42:00 +01:00
for (const ReferenceToken& ref:refs) {
Action a = analyzeToken(ref.token, tok, d, inconclusiveRefs);
if (a != Action::None)
return a;
}
// TODO: Check if function is pure
if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
// bailout: global non-const variables
if (isGlobal()) {
return Action::Invalid;
}
}
return Action::None;
}
2020-02-13 17:04:05 +01:00
virtual std::vector<int> evaluate(const Token* tok) const OVERRIDE {
if (tok->hasKnownIntValue())
return {static_cast<int>(tok->values().front().intvalue)};
std::vector<int> result;
ProgramMemory pm = pms.get(tok, getProgramState());
2020-11-10 16:00:55 +01:00
if (Token::Match(tok, "&&|%oror%")) {
if (conditionIsTrue(tok, pm))
result.push_back(1);
if (conditionIsFalse(tok, pm))
result.push_back(0);
} else {
MathLib::bigint out = 0;
bool error = false;
execute(tok, &pm, &out, &error);
if (!error)
result.push_back(out);
}
return result;
}
virtual void assume(const Token* tok, bool state, const Token* at) OVERRIDE {
// Update program state
pms.removeModifiedVars(tok);
pms.addState(tok, getProgramState());
pms.assume(tok, state);
const bool isAssert = Token::Match(at, "assert|ASSERT");
if (!isAssert && !Token::simpleMatch(at, "}")) {
std::string s = state ? "true" : "false";
addErrorPath(tok, "Assuming condition is " + s);
}
if (!isAssert)
makeConditional();
}
2020-11-11 09:15:36 +01:00
virtual void update(Token* tok, Action a, Direction d) OVERRIDE {
ValueFlow::Value* value = getValue(tok);
if (!value)
return;
2020-11-10 16:00:55 +01:00
// Read first when moving forward
if (d == Direction::Forward && a.isRead())
setTokenValue(tok, *value, getSettings());
if (a.isInconclusive())
lowerToInconclusive();
if (a.isWrite() && tok->astParent()) {
2020-11-10 16:00:55 +01:00
writeValue(value, tok, d);
}
2020-11-10 16:00:55 +01:00
// Read last when moving in reverse
if (d == Direction::Reverse && a.isRead())
setTokenValue(tok, *value, getSettings());
}
2020-11-10 16:00:55 +01:00
2020-11-11 09:15:36 +01:00
virtual ValuePtr<Analyzer> reanalyze(Token*, const std::string&) const OVERRIDE {
return {};
}
};
2020-11-10 16:00:55 +01:00
ValuePtr<Analyzer> makeAnalyzer(Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist);
struct SingleValueFlowAnalyzer : ValueFlowAnalyzer {
std::unordered_map<nonneg int, const Variable*> varids;
std::unordered_map<nonneg int, const Variable*> aliases;
ValueFlow::Value value;
2020-11-10 16:00:55 +01:00
SingleValueFlowAnalyzer() : ValueFlowAnalyzer() {}
2020-11-10 16:00:55 +01:00
SingleValueFlowAnalyzer(const ValueFlow::Value& v, const TokenList* t) : ValueFlowAnalyzer(t), value(v) {}
const std::unordered_map<nonneg int, const Variable*>& getVars() const {
return varids;
}
const std::unordered_map<nonneg int, const Variable*>& getAliasedVars() const {
return aliases;
}
virtual const ValueFlow::Value* getValue(const Token*) const OVERRIDE {
return &value;
}
virtual ValueFlow::Value* getValue(const Token*) OVERRIDE {
return &value;
}
virtual void makeConditional() OVERRIDE {
value.conditional = true;
}
virtual void addErrorPath(const Token* tok, const std::string& s) OVERRIDE {
value.errorPath.emplace_back(tok, s);
}
virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE {
if (value.isLifetimeValue())
return false;
2020-07-23 10:09:06 +02:00
for (const auto& m: {
std::ref(getVars()), std::ref(getAliasedVars())
}) {
2020-07-21 20:18:45 +02:00
for (const auto& p:m.get()) {
nonneg int varid = p.first;
const Variable* var = p.second;
if (tok->varId() == varid)
return true;
if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive))
return true;
}
}
return false;
}
virtual bool isGlobal() const OVERRIDE {
2020-02-16 19:58:09 +01:00
for (const auto&p:getVars()) {
const Variable* var = p.second;
if (!var->isLocal() && !var->isArgument() && !var->isConst())
return true;
}
return false;
}
2020-02-13 17:04:05 +01:00
virtual bool lowerToPossible() OVERRIDE {
if (value.isImpossible())
return false;
value.changeKnownToPossible();
return true;
}
2020-02-13 17:04:05 +01:00
virtual bool lowerToInconclusive() OVERRIDE {
if (value.isImpossible())
return false;
value.setInconclusive();
return true;
}
2020-02-13 17:04:05 +01:00
virtual bool isConditional() const OVERRIDE {
if (value.conditional)
return true;
if (value.condition)
return !value.isKnown() && !value.isImpossible();
return false;
}
2020-02-13 17:04:05 +01:00
virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE {
const Scope* scope = endBlock->scope();
if (!scope)
return false;
if (scope->type == Scope::eLambda) {
return value.isLifetimeValue();
} else if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile ||
scope->type == Scope::eFor) {
if (value.isKnown() || value.isImpossible())
return true;
if (value.isLifetimeValue())
return true;
if (isConditional())
return false;
const Token* condTok = getCondTokFromEnd(endBlock);
2020-05-19 21:55:28 +02:00
std::set<nonneg int> varids2;
std::transform(getVars().begin(), getVars().end(), std::inserter(varids2, varids2.begin()), SelectMapKeys{});
return bifurcate(condTok, varids2, getSettings());
}
return false;
}
2020-11-10 16:00:55 +01:00
2020-11-11 09:15:36 +01:00
virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg) const OVERRIDE {
2020-11-10 16:00:55 +01:00
ValueFlow::Value newValue = value;
newValue.errorPath.emplace_back(tok, msg);
return makeAnalyzer(tok, newValue, tokenlist);
}
};
2020-11-10 16:00:55 +01:00
struct VariableAnalyzer : SingleValueFlowAnalyzer {
const Variable* var;
2020-11-10 16:00:55 +01:00
VariableAnalyzer() : SingleValueFlowAnalyzer(), var(nullptr) {}
2020-11-10 16:00:55 +01:00
VariableAnalyzer(const Variable* v,
const ValueFlow::Value& val,
std::vector<const Variable*> paliases,
const TokenList* t)
2020-11-11 09:15:36 +01:00
: SingleValueFlowAnalyzer(val, t), var(v) {
varids[var->declarationId()] = var;
2020-07-23 10:09:06 +02:00
for (const Variable* av:paliases) {
if (!av)
continue;
aliases[av->declarationId()] = av;
}
}
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
return var->valueType();
}
virtual bool match(const Token* tok) const OVERRIDE {
return tok->varId() == var->declarationId();
}
virtual ProgramState getProgramState() const OVERRIDE {
ProgramState ps;
ps[var->declarationId()] = value;
return ps;
}
};
static std::vector<const Variable*> getAliasesFromValues(std::list<ValueFlow::Value> values, bool address=false)
{
std::vector<const Variable*> aliases;
for (const ValueFlow::Value& v : values) {
if (!v.tokvalue)
continue;
const Token* lifeTok = nullptr;
for (const ValueFlow::Value& lv:v.tokvalue->values()) {
if (!lv.isLocalLifetimeValue())
continue;
if (address && lv.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
continue;
if (lifeTok) {
lifeTok = nullptr;
break;
}
lifeTok = lv.tokvalue;
}
if (lifeTok && lifeTok->variable()) {
aliases.push_back(lifeTok->variable());
}
}
return aliases;
}
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
2020-11-11 09:15:36 +01:00
const Token* const endToken,
const Variable* const var,
std::list<ValueFlow::Value> values,
std::vector<const Variable*> aliases,
TokenList* const tokenlist,
const Settings* const settings)
{
2020-11-10 16:00:55 +01:00
Analyzer::Action actions;
for (ValueFlow::Value& v : values) {
2020-11-10 16:00:55 +01:00
VariableAnalyzer a(var, v, aliases, tokenlist);
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
}
return actions;
}
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
2020-11-11 09:15:36 +01:00
const Token* const endToken,
const Variable* const var,
std::list<ValueFlow::Value> values,
TokenList* const tokenlist,
const Settings* const settings)
{
auto aliases = getAliasesFromValues(values);
2020-09-10 04:32:07 +02:00
return valueFlowForwardVariable(
startToken, endToken, var, std::move(values), std::move(aliases), tokenlist, settings);
}
// Old deprecated version
static bool valueFlowForwardVariable(Token* const startToken,
const Token* const endToken,
const Variable* const var,
const nonneg int,
std::list<ValueFlow::Value> values,
const bool,
const bool,
TokenList* const tokenlist,
ErrorLogger* const,
const Settings* const settings)
{
valueFlowForwardVariable(startToken, endToken, var, std::move(values), tokenlist, settings);
return true;
}
2020-11-10 16:00:55 +01:00
struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
const Token* expr;
bool local;
bool unknown;
2020-11-10 16:00:55 +01:00
ExpressionAnalyzer() : SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false) {}
2020-11-10 16:00:55 +01:00
ExpressionAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
2020-11-11 09:15:36 +01:00
: SingleValueFlowAnalyzer(val, t), expr(e), local(true), unknown(false) {
setupExprVarIds();
}
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
return expr->valueType();
}
2020-02-16 19:58:09 +01:00
static bool nonLocal(const Variable* var, bool deref) {
return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern();
}
2020-02-16 19:58:09 +01:00
void setupExprVarIds() {
visitAstNodes(expr,
[&](const Token *tok) {
if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") {
// unknown variable
unknown = true;
return ChildrenToVisit::none;
}
if (tok->varId() > 0) {
varids[tok->varId()] = tok->variable();
if (!Token::simpleMatch(tok->previous(), ".")) {
const Variable *var = tok->variable();
if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2(), isCPP()))
return ChildrenToVisit::none;
const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1()));
local &= !nonLocal(tok->variable(), deref);
}
}
return ChildrenToVisit::op1_and_op2;
});
}
virtual bool invalid() const OVERRIDE {
return unknown;
}
2021-01-22 21:47:24 +01:00
virtual ProgramState getProgramState() const OVERRIDE {
ProgramState ps;
ps[expr->exprId()] = value;
return ps;
}
virtual bool match(const Token* tok) const OVERRIDE {
return isSameExpression(isCPP(), true, expr, tok, getSettings()->library, true, true);
}
virtual bool isGlobal() const OVERRIDE {
return !local;
}
};
struct OppositeExpressionAnalyzer : ExpressionAnalyzer {
bool isNot;
OppositeExpressionAnalyzer() : ExpressionAnalyzer(), isNot(false) {}
OppositeExpressionAnalyzer(bool pIsNot, const Token* e, const ValueFlow::Value& val, const TokenList* t)
2021-01-12 21:28:56 +01:00
: ExpressionAnalyzer(e, val, t), isNot(pIsNot)
{}
virtual bool match(const Token* tok) const OVERRIDE {
return isOppositeCond(isNot, isCPP(), expr, tok, getSettings()->library, true, true);
}
};
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowForwardExpression(Token* startToken,
2020-11-11 09:15:36 +01:00
const Token* endToken,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
const TokenList* const tokenlist,
const Settings* settings)
{
2020-11-10 16:00:55 +01:00
Analyzer::Action actions;
for (const ValueFlow::Value& v : values) {
2020-11-10 16:00:55 +01:00
ExpressionAnalyzer a(exprTok, v, tokenlist);
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
}
return actions;
}
2019-09-28 20:50:56 +02:00
static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known)
{
if (!expr)
return nullptr;
if (!expr->astOperand1() || !expr->astOperand2())
return nullptr;
const Token* knownTok = nullptr;
const Token* varTok = nullptr;
if (expr->astOperand1()->hasKnownIntValue() && !expr->astOperand2()->hasKnownIntValue()) {
varTok = expr->astOperand2();
knownTok = expr->astOperand1();
} else if (expr->astOperand2()->hasKnownIntValue() && !expr->astOperand1()->hasKnownIntValue()) {
varTok = expr->astOperand1();
knownTok = expr->astOperand2();
}
if (knownTok)
known = knownTok->values().front().intvalue;
return varTok;
}
template <class F>
void transformIntValues(std::list<ValueFlow::Value>& values, F f)
{
std::transform(values.begin(), values.end(), values.begin(), [&](ValueFlow::Value x) {
if (x.isIntValue() || x.isIteratorValue())
x.intvalue = f(x.intvalue);
return x;
});
}
2019-09-28 20:50:56 +02:00
static const Token* solveExprValues(const Token* expr, std::list<ValueFlow::Value>& values)
{
MathLib::bigint intval;
const Token* binaryTok = parseBinaryIntOp(expr, intval);
if (binaryTok && expr->str().size() == 1) {
switch (expr->str()[0]) {
case '+': {
2019-09-26 10:32:49 +02:00
transformIntValues(values, [&](MathLib::bigint x) {
return x - intval;
});
return solveExprValues(binaryTok, values);
}
case '*': {
if (intval == 0)
break;
2019-09-26 10:32:49 +02:00
transformIntValues(values, [&](MathLib::bigint x) {
return x / intval;
});
return solveExprValues(binaryTok, values);
}
case '^': {
2019-09-26 10:32:49 +02:00
transformIntValues(values, [&](MathLib::bigint x) {
return x ^ intval;
});
return solveExprValues(binaryTok, values);
}
}
}
return expr;
}
2020-11-10 16:00:55 +01:00
ValuePtr<Analyzer> makeAnalyzer(Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist)
{
std::list<ValueFlow::Value> values = {value};
const Token* expr = solveExprValues(exprTok, values);
if (expr->variable()) {
return VariableAnalyzer(expr->variable(), value, getAliasesFromValues(values), tokenlist);
} else {
return ExpressionAnalyzer(expr, value, tokenlist);
}
}
static Analyzer::Action valueFlowForward(Token* startToken,
2020-11-11 09:15:36 +01:00
const Token* endToken,
const Token* exprTok,
std::list<ValueFlow::Value> values,
TokenList* const tokenlist,
const Settings* settings)
{
const Token* expr = solveExprValues(exprTok, values);
if (expr->variable()) {
2020-09-10 04:32:07 +02:00
return valueFlowForwardVariable(startToken, endToken, expr->variable(), values, tokenlist, settings);
} else {
return valueFlowForwardExpression(startToken, endToken, expr, values, tokenlist, settings);
}
}
2020-11-10 16:00:55 +01:00
static void valueFlowReverse(TokenList* tokenlist,
Token* tok,
const Token* const varToken,
ValueFlow::Value val,
ValueFlow::Value val2,
2020-11-18 20:27:51 +01:00
ErrorLogger* /*errorLogger*/,
2020-11-10 16:00:55 +01:00
const Settings* settings)
{
std::list<ValueFlow::Value> values = {val};
if (val2.varId != 0)
values.push_back(val2);
const Variable* var = varToken->variable();
auto aliases = getAliasesFromValues(values);
for (ValueFlow::Value& v : values) {
VariableAnalyzer a(var, v, aliases, tokenlist);
valueFlowGenericReverse(tok, a, settings);
}
}
static void valueFlowReverse(Token* tok,
const Token* const endToken,
const Token* const varToken,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
const Settings* settings)
{
const Variable* var = varToken->variable();
if (var) {
auto aliases = getAliasesFromValues(values);
for (const ValueFlow::Value& v : values) {
VariableAnalyzer a(var, v, aliases, tokenlist);
valueFlowGenericReverse(tok, endToken, a, settings);
}
} else {
for (const ValueFlow::Value& v : values) {
ExpressionAnalyzer a(varToken, v, tokenlist);
valueFlowGenericReverse(tok, endToken, a, settings);
}
}
}
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
{
std::string result;
if (!val)
return "object";
switch (val->lifetimeKind) {
2019-07-16 10:51:07 +02:00
case ValueFlow::Value::LifetimeKind::Lambda:
result = "lambda";
break;
2019-07-16 10:51:07 +02:00
case ValueFlow::Value::LifetimeKind::Iterator:
result = "iterator";
break;
2019-07-16 10:51:07 +02:00
case ValueFlow::Value::LifetimeKind::Object:
case ValueFlow::Value::LifetimeKind::SubObject:
2019-07-16 10:51:07 +02:00
case ValueFlow::Value::LifetimeKind::Address:
if (astIsPointer(tok))
result = "pointer";
else
result = "object";
break;
}
return result;
}
std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath)
{
const Token *tokvalue = val ? val->tokvalue : nullptr;
const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr;
const Token *vartok = tokvar ? tokvar->nameToken() : nullptr;
std::string type = lifetimeType(tok, val);
std::string msg = type;
if (vartok) {
errorPath.emplace_back(vartok, "Variable created here.");
const Variable * var = vartok->variable();
if (var) {
switch (val->lifetimeKind) {
case ValueFlow::Value::LifetimeKind::SubObject:
case ValueFlow::Value::LifetimeKind::Object:
case ValueFlow::Value::LifetimeKind::Address:
if (type == "pointer")
msg += " to local variable";
else
msg += " that points to local variable";
break;
case ValueFlow::Value::LifetimeKind::Lambda:
msg += " that captures local variable";
break;
case ValueFlow::Value::LifetimeKind::Iterator:
msg += " to local container";
break;
}
msg += " '" + var->name() + "'";
}
}
return msg;
}
std::vector<ValueFlow::Value> getLifetimeObjValues(const Token *tok, bool inconclusive, bool subfunction)
{
std::vector<ValueFlow::Value> result;
auto pred = [&](const ValueFlow::Value &v) {
if (!v.isLocalLifetimeValue() && !(subfunction && v.isSubFunctionLifetimeValue()))
return false;
if (!inconclusive && v.isInconclusive())
return false;
if (!v.tokvalue->variable())
return false;
return true;
};
std::copy_if(tok->values().begin(), tok->values().end(), std::back_inserter(result), pred);
return result;
}
ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive)
{
std::vector<ValueFlow::Value> values = getLifetimeObjValues(tok, inconclusive, false);
// There should only be one lifetime
if (values.size() != 1)
return ValueFlow::Value{};
return values.front();
}
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath, int depth)
{
if (!tok)
return std::vector<LifetimeToken> {};
const Variable *var = tok->variable();
if (depth < 0)
return {{tok, std::move(errorPath)}};
if (var && var->declarationId() == tok->varId()) {
if (var->isReference() || var->isRValueReference()) {
if (!var->declEndToken())
return {{tok, true, std::move(errorPath)}};
if (var->isArgument()) {
errorPath.emplace_back(var->declEndToken(), "Passed to reference.");
return {{tok, true, std::move(errorPath)}};
} else if (Token::simpleMatch(var->declEndToken(), "=")) {
errorPath.emplace_back(var->declEndToken(), "Assigned to reference.");
const Token *vartok = var->declEndToken()->astOperand2();
const bool temporaryDefault = false; //If we can't tell then assume the value is not temporary as this will result in fewer false positives.
const bool temporary = isTemporary(true, vartok, nullptr, temporaryDefault);
const bool nonlocal = var->isStatic() || var->isGlobal();
if (vartok == tok || (nonlocal && temporary) || (!escape && (var->isConst() || var->isRValueReference()) && temporary))
return {{tok, true, std::move(errorPath)}};
if (vartok)
return getLifetimeTokens(vartok, escape, std::move(errorPath), depth - 1);
} else if (Token::simpleMatch(var->nameToken()->astParent(), ":") &&
var->nameToken()->astParent()->astParent() &&
Token::simpleMatch(var->nameToken()->astParent()->astParent()->previous(), "for (")) {
errorPath.emplace_back(var->nameToken(), "Assigned to reference.");
const Token* vartok = var->nameToken();
if (vartok == tok)
return {{tok, true, std::move(errorPath)}};
const Token* contok = var->nameToken()->astParent()->astOperand2();
if (contok)
return getLifetimeTokens(contok, escape, std::move(errorPath), depth - 1);
} else {
return std::vector<LifetimeToken> {};
}
}
} else if (Token::Match(tok->previous(), "%name% (")) {
const Function *f = tok->previous()->function();
if (f) {
if (!Function::returnsReference(f))
return {{tok, std::move(errorPath)}};
std::vector<LifetimeToken> result;
std::vector<const Token*> returns = Function::findReturns(f);
for (const Token* returnTok : returns) {
if (returnTok == tok)
continue;
for (LifetimeToken& lt : getLifetimeTokens(returnTok, escape, errorPath, depth - returns.size())) {
const Token* argvarTok = lt.token;
const Variable* argvar = argvarTok->variable();
if (!argvar)
continue;
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
int n = getArgumentPos(argvar, f);
if (n < 0)
return std::vector<LifetimeToken> {};
std::vector<const Token*> args = getArguments(tok->previous());
// TODO: Track lifetimes of default parameters
if (n >= args.size())
return std::vector<LifetimeToken> {};
const Token* argTok = args[n];
lt.errorPath.emplace_back(returnTok, "Return reference.");
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), depth - returns.size()), returns.size() > 1);
result.insert(result.end(), arglts.begin(), arglts.end());
}
}
}
return result;
} else if (Token::Match(tok->tokAt(-2), ". %name% (") && tok->tokAt(-2)->originalName() != "->" && astIsContainer(tok->tokAt(-2)->astOperand1())) {
const Library::Container* library = getLibraryContainer(tok->tokAt(-2)->astOperand1());
Library::Container::Yield y = library->getYield(tok->previous()->str());
if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) {
errorPath.emplace_back(tok->previous(), "Accessing container.");
return LifetimeToken::setAddressOf(
2020-09-07 10:56:02 +02:00
getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), depth - 1), false);
}
}
} else if (Token::Match(tok, ".|::|[")) {
const Token *vartok = tok;
while (vartok) {
if (vartok->str() == "[" || vartok->originalName() == "->")
vartok = vartok->astOperand1();
else if (vartok->str() == "." || vartok->str() == "::")
vartok = vartok->astOperand2();
else
break;
}
if (!vartok)
return {{tok, std::move(errorPath)}};
const Variable *tokvar = vartok->variable();
if (!astIsContainer(vartok) && !(tokvar && tokvar->isArray() && !tokvar->isArgument()) &&
(Token::Match(vartok->astParent(), "[|*") || vartok->astParent()->originalName() == "->")) {
for (const ValueFlow::Value &v : vartok->values()) {
if (!v.isLocalLifetimeValue())
continue;
if (v.tokvalue == tok)
continue;
errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end());
return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath), depth - 1);
}
} else {
return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), depth - 1),
!(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "[")));
}
}
return {{tok, std::move(errorPath)}};
}
static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr)
{
std::vector<LifetimeToken> lts = getLifetimeTokens(tok);
if (lts.size() != 1)
return nullptr;
if (lts.front().inconclusive)
return nullptr;
if (addressOf)
*addressOf = lts.front().addressOf;
errorPath.insert(errorPath.end(), lts.front().errorPath.begin(), lts.front().errorPath.end());
return lts.front().token;
}
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf)
{
const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf);
if (tok2 && tok2->variable())
return tok2->variable();
return nullptr;
}
const Variable* getLifetimeVariable(const Token* tok)
{
ValueFlow::Value::ErrorPath errorPath;
return getLifetimeVariable(tok, errorPath, nullptr);
}
static bool isNotLifetimeValue(const ValueFlow::Value& val)
{
return !val.isLifetimeValue();
}
static bool isLifetimeOwned(const ValueType *vt, const ValueType *vtParent)
{
if (!vtParent)
return false;
if (!vt) {
if (vtParent->type == ValueType::CONTAINER)
return true;
return false;
}
if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) {
if (vt->pointer != vtParent->pointer)
return true;
if (vt->type != vtParent->type) {
if (vtParent->type == ValueType::RECORD)
return true;
if (vtParent->type == ValueType::CONTAINER)
return true;
}
}
return false;
}
static bool isLifetimeBorrowed(const ValueType *vt, const ValueType *vtParent)
{
if (!vtParent)
return false;
if (!vt)
return false;
if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE && vtParent->container == vt->container) {
if (vtParent->pointer > vt->pointer)
return true;
if (vtParent->pointer < vt->pointer && vtParent->isIntegral())
return true;
if (vtParent->str() == vt->str())
return true;
2020-06-13 07:45:31 +02:00
if (vtParent->pointer == vt->pointer && vtParent->type == vt->type && vtParent->isIntegral())
// sign conversion
return true;
}
return false;
}
static const Token* skipCVRefs(const Token* tok, const Token* endTok)
{
2020-05-24 10:52:58 +02:00
while (tok != endTok && Token::Match(tok, "const|volatile|auto|&|&&"))
tok = tok->next();
return tok;
}
static bool isNotEqual(std::pair<const Token*, const Token*> x, std::pair<const Token*, const Token*> y)
{
const Token* start1 = x.first;
const Token* start2 = y.first;
if (start1 == nullptr || start2 == nullptr)
return false;
2020-05-24 10:52:58 +02:00
while (start1 != x.second && start2 != y.second) {
const Token* tok1 = skipCVRefs(start1, x.second);
if (tok1 != start1) {
start1 = tok1;
continue;
}
const Token* tok2 = skipCVRefs(start2, y.second);
if (tok2 != start2) {
start2 = tok2;
continue;
}
if (start1->str() != start2->str())
return true;
start1 = start1->next();
start2 = start2->next();
}
start1 = skipCVRefs(start1, x.second);
start2 = skipCVRefs(start2, y.second);
return !(start1 == x.second && start2 == y.second);
}
static bool isNotEqual(std::pair<const Token*, const Token*> x, const std::string& y)
{
TokenList tokenList(nullptr);
std::istringstream istr(y);
tokenList.createTokens(istr);
return isNotEqual(x, std::make_pair(tokenList.front(), tokenList.back()));
}
static bool isNotEqual(std::pair<const Token*, const Token*> x, const ValueType* y)
{
if (y == nullptr)
return false;
if (y->originalTypeName.empty())
return false;
return isNotEqual(x, y->originalTypeName);
}
bool isLifetimeBorrowed(const Token *tok, const Settings *settings)
{
if (!tok)
return true;
if (tok->str() == ",")
return true;
if (!tok->astParent())
return true;
if (!Token::Match(tok->astParent()->previous(), "%name% (") && !Token::simpleMatch(tok->astParent(), ",")) {
if (!Token::simpleMatch(tok, "{")) {
const ValueType *vt = tok->valueType();
const ValueType *vtParent = tok->astParent()->valueType();
if (isLifetimeBorrowed(vt, vtParent))
return true;
if (isLifetimeOwned(vt, vtParent))
return false;
}
if (Token::Match(tok->astParent(), "return|(|{|%assign%")) {
const Type *t = Token::typeOf(tok);
const Type *parentT = Token::typeOf(tok->astParent());
if (t && parentT) {
if (t->classDef && parentT->classDef && t->classDef != parentT->classDef)
return false;
} else {
std::pair<const Token*, const Token*> decl = Token::typeDecl(tok);
std::pair<const Token*, const Token*> parentdecl = Token::typeDecl(tok->astParent());
if (isNotEqual(decl, parentdecl))
return false;
if (isNotEqual(decl, tok->astParent()->valueType()))
return false;
if (isNotEqual(parentdecl, tok->valueType()))
return false;
}
}
} else if (Token::Match(tok->astParent()->tokAt(-3), "%var% . push_back|push_front|insert|push (") &&
astIsContainer(tok->astParent()->tokAt(-3))) {
const ValueType *vt = tok->valueType();
const ValueType *vtCont = tok->astParent()->tokAt(-3)->valueType();
if (!vtCont->containerTypeToken)
return true;
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings);
if (isLifetimeBorrowed(vt, &vtParent))
return true;
if (isLifetimeOwned(vt, &vtParent))
return false;
}
return true;
}
static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings);
static void valueFlowLifetimeConstructor(Token *tok,
TokenList *tokenlist,
ErrorLogger *errorLogger,
const Settings *settings);
static const Token* getEndOfVarScope(const Token* tok, const std::vector<const Variable*>& vars)
{
const Token* endOfVarScope = nullptr;
for (const Variable* var : vars) {
if (var && (var->isLocal() || var->isArgument()))
endOfVarScope = var->typeStartToken()->scope()->bodyEnd;
else if (!endOfVarScope)
endOfVarScope = tok->scope()->bodyEnd;
}
return endOfVarScope;
}
static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
// Forward lifetimes to constructed variable
if (Token::Match(tok->previous(), "%var% {")) {
std::list<ValueFlow::Value> values = tok->values();
values.remove_if(&isNotLifetimeValue);
valueFlowForward(nextAfterAstRightmostLeaf(tok),
getEndOfVarScope(tok, {tok->variable()}),
tok->previous(),
values,
tokenlist,
settings);
return;
}
Token *parent = tok->astParent();
while (parent && (parent->isArithmeticalOp() || parent->str() == ","))
parent = parent->astParent();
if (!parent)
return;
// Assignment
if (parent->str() == "=" && (!parent->astParent() || Token::simpleMatch(parent->astParent(), ";"))) {
// Rhs values..
if (!parent->astOperand2() || parent->astOperand2()->values().empty())
return;
if (!isLifetimeBorrowed(parent->astOperand2(), settings))
return;
std::vector<const Variable*> vars = getLHSVariables(parent);
const Token* endOfVarScope = getEndOfVarScope(tok, vars);
// Only forward lifetime values
std::list<ValueFlow::Value> values = parent->astOperand2()->values();
values.remove_if(&isNotLifetimeValue);
// Skip RHS
const Token *nextExpression = nextAfterAstRightmostLeaf(parent);
if (Token::Match(parent->astOperand1(), ".|[|(")) {
valueFlowForwardExpression(
const_cast<Token*>(nextExpression), endOfVarScope, parent->astOperand1(), values, tokenlist, settings);
for (ValueFlow::Value& val : values) {
if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address)
val.lifetimeKind = ValueFlow::Value::LifetimeKind::SubObject;
}
}
for (const Variable* var : vars) {
valueFlowForwardVariable(const_cast<Token*>(nextExpression),
endOfVarScope,
var,
var->declarationId(),
values,
false,
false,
tokenlist,
errorLogger,
settings);
if (tok->astTop() && Token::simpleMatch(tok->astTop()->previous(), "for (") &&
Token::simpleMatch(tok->astTop()->link(), ") {")) {
Token* start = tok->astTop()->link()->next();
valueFlowForwardVariable(
start, start->link(), var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings);
}
}
// Constructor
} else if (Token::simpleMatch(parent, "{") && !isScopeBracket(parent)) {
valueFlowLifetimeConstructor(parent, tokenlist, errorLogger, settings);
valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings);
// Function call
} else if (Token::Match(parent->previous(), "%name% (")) {
valueFlowLifetimeFunction(parent->previous(), tokenlist, errorLogger, settings);
valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings);
// Variable
} else if (tok->variable()) {
const Variable *var = tok->variable();
2020-01-11 09:17:32 +01:00
const Token *endOfVarScope = var->scope()->bodyEnd;
std::list<ValueFlow::Value> values = tok->values();
const Token *nextExpression = nextAfterAstRightmostLeaf(parent);
// Only forward lifetime values
values.remove_if(&isNotLifetimeValue);
valueFlowForwardVariable(const_cast<Token*>(nextExpression),
endOfVarScope,
var,
var->declarationId(),
values,
false,
false,
tokenlist,
errorLogger,
settings);
// Cast
} else if (parent->isCast()) {
std::list<ValueFlow::Value> values = tok->values();
// Only forward lifetime values
values.remove_if(&isNotLifetimeValue);
2020-12-19 08:56:46 +01:00
for (const ValueFlow::Value& value:values)
setTokenValue(parent, value, tokenlist->getSettings());
valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings);
}
}
struct LifetimeStore {
const Token *argtok;
std::string message;
ValueFlow::Value::LifetimeKind type;
ErrorPath errorPath;
bool inconclusive;
2020-10-03 11:01:53 +02:00
bool forward;
struct Context {
Token* tok;
TokenList* tokenlist;
ErrorLogger* errorLogger;
const Settings* settings;
};
LifetimeStore()
2020-10-03 11:01:53 +02:00
: argtok(nullptr), message(), type(), errorPath(), inconclusive(false), forward(true), mContext(nullptr)
{}
2020-10-03 11:01:53 +02:00
LifetimeStore(const Token* argtok,
const std::string& message,
ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object,
bool inconclusive = false)
2020-10-03 11:01:53 +02:00
: argtok(argtok),
message(message),
type(type),
errorPath(),
inconclusive(inconclusive),
forward(true),
mContext(nullptr)
{}
2020-10-03 11:01:53 +02:00
template <class F>
static void forEach(const std::vector<const Token*>& argtoks,
const std::string& message,
ValueFlow::Value::LifetimeKind type,
2020-10-03 11:02:11 +02:00
F f) {
2020-10-03 11:01:53 +02:00
std::map<const Token*, Context> forwardToks;
for (const Token* arg : argtoks) {
LifetimeStore ls{arg, message, type};
Context c{};
ls.mContext = &c;
ls.forward = false;
f(ls);
if (c.tok)
forwardToks[c.tok] = c;
}
for (const auto& p : forwardToks) {
const Context& c = p.second;
valueFlowForwardLifetime(c.tok, c.tokenlist, c.errorLogger, c.settings);
}
}
static LifetimeStore fromFunctionArg(const Function * f, Token *tok, const Variable *var, TokenList *tokenlist, ErrorLogger *errorLogger) {
if (!var)
return LifetimeStore{};
if (!var->isArgument())
return LifetimeStore{};
int n = getArgumentPos(var, f);
if (n < 0)
return LifetimeStore{};
std::vector<const Token *> args = getArguments(tok);
if (n >= args.size()) {
if (tokenlist->getSettings()->debugwarnings)
bailout(tokenlist,
errorLogger,
tok,
"Argument mismatch: Function '" + tok->str() + "' returning lifetime from argument index " +
std::to_string(n) + " but only " + std::to_string(args.size()) +
" arguments are available.");
return LifetimeStore{};
}
const Token *argtok2 = args[n];
return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object};
}
template <class Predicate>
2020-10-03 11:02:11 +02:00
bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const {
if (!argtok)
2020-10-03 11:01:53 +02:00
return false;
bool update = false;
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
continue;
ErrorPath er = errorPath;
er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end());
if (!lt.token)
2020-10-03 11:01:53 +02:00
return false;
if (!pred(lt.token))
2020-10-03 11:01:53 +02:00
return false;
er.emplace_back(argtok, message);
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local;
value.tokvalue = lt.token;
value.errorPath = std::move(er);
value.lifetimeKind = type;
value.setInconclusive(lt.inconclusive || inconclusive);
// Don't add the value a second time
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
2020-10-03 11:01:53 +02:00
return false;
setTokenValue(tok, value, tokenlist->getSettings());
2020-10-03 11:01:53 +02:00
update = true;
}
2020-10-03 11:01:53 +02:00
if (update && forward)
forwardLifetime(tok, tokenlist, errorLogger, settings);
return update;
}
2020-10-03 11:02:11 +02:00
bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const {
return byRef(tok, tokenlist, errorLogger, settings, [](const Token*) {
return true;
});
}
template <class Predicate>
2020-10-03 11:02:11 +02:00
bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const {
if (!argtok)
2020-10-03 11:01:53 +02:00
return false;
bool update = false;
if (argtok->values().empty()) {
ErrorPath er;
er.emplace_back(argtok, message);
const Variable *var = getLifetimeVariable(argtok, er);
if (var && var->isArgument()) {
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
2019-07-16 11:12:35 +02:00
value.lifetimeScope = ValueFlow::Value::LifetimeScope::Argument;
value.tokvalue = var->nameToken();
value.errorPath = er;
value.lifetimeKind = type;
value.setInconclusive(inconclusive);
// Don't add the value a second time
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
2020-10-03 11:01:53 +02:00
return false;
setTokenValue(tok, value, tokenlist->getSettings());
2020-10-03 11:01:53 +02:00
update = true;
}
}
for (const ValueFlow::Value &v : argtok->values()) {
if (!v.isLifetimeValue())
continue;
const Token *tok3 = v.tokvalue;
for (const LifetimeToken& lt : getLifetimeTokens(tok3)) {
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
continue;
ErrorPath er = v.errorPath;
er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end());
if (!lt.token)
2020-10-03 11:01:53 +02:00
return false;
if (!pred(lt.token))
2020-10-03 11:01:53 +02:00
return false;
er.emplace_back(argtok, message);
er.insert(er.end(), errorPath.begin(), errorPath.end());
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
value.lifetimeScope = v.lifetimeScope;
value.path = v.path;
value.tokvalue = lt.token;
value.errorPath = std::move(er);
value.lifetimeKind = type;
value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive);
// Don't add the value a second time
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
continue;
setTokenValue(tok, value, tokenlist->getSettings());
2020-10-03 11:01:53 +02:00
update = true;
}
}
2020-10-03 11:01:53 +02:00
if (update && forward)
forwardLifetime(tok, tokenlist, errorLogger, settings);
return update;
}
2020-10-03 11:02:11 +02:00
bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const {
return byVal(tok, tokenlist, errorLogger, settings, [](const Token*) {
return true;
});
}
template <class Predicate>
void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const {
if (!settings->certainty.isEnabled(Certainty::inconclusive) && inconclusive)
return;
if (!argtok)
return;
for (const ValueFlow::Value &v : argtok->values()) {
if (!v.isLifetimeValue())
continue;
const Token *tok2 = v.tokvalue;
ErrorPath er = v.errorPath;
const Variable *var = getLifetimeVariable(tok2, er);
er.insert(er.end(), errorPath.begin(), errorPath.end());
if (!var)
continue;
for (const Token *tok3 = tok; tok3 && tok3 != var->declEndToken(); tok3 = tok3->previous()) {
if (tok3->varId() == var->declarationId()) {
LifetimeStore{tok3, message, type, inconclusive} .byVal(tok, tokenlist, errorLogger, settings, pred);
break;
}
}
}
}
void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const {
byDerefCopy(tok, tokenlist, errorLogger, settings, [](const Token *) {
return true;
});
}
2020-10-03 11:01:53 +02:00
2020-10-03 11:02:11 +02:00
private:
2020-10-03 11:01:53 +02:00
Context* mContext;
2020-10-03 11:02:11 +02:00
void forwardLifetime(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const {
2020-10-03 11:01:53 +02:00
if (mContext) {
mContext->tok = tok;
mContext->tokenlist = tokenlist;
mContext->errorLogger = errorLogger;
mContext->settings = settings;
}
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
};
static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
if (!Token::Match(tok, "%name% ("))
return;
int returnContainer = settings->library.returnValueContainer(tok);
if (returnContainer >= 0) {
std::vector<const Token *> args = getArguments(tok);
for (int argnr = 1; argnr <= args.size(); ++argnr) {
const Library::ArgumentChecks::IteratorInfo *i = settings->library.getArgIteratorInfo(tok, argnr);
if (!i)
continue;
if (i->container != returnContainer)
continue;
const Token * const argTok = args[argnr - 1];
// Check if lifetime is available to avoid adding the lifetime twice
ValueFlow::Value val = getLifetimeObjValue(argTok);
if (val.tokvalue) {
LifetimeStore{argTok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Iterator} .byVal(
tok->next(), tokenlist, errorLogger, settings);
break;
}
}
} else if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) {
for (const Token *argtok : getArguments(tok)) {
2019-07-16 10:51:07 +02:00
LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byRef(
tok->next(), tokenlist, errorLogger, settings);
}
} else if (Token::Match(tok->tokAt(-2), "std :: make_tuple|tuple_cat|make_pair|make_reverse_iterator|next|prev|move|bind")) {
for (const Token *argtok : getArguments(tok)) {
2019-07-16 10:51:07 +02:00
LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byVal(
tok->next(), tokenlist, errorLogger, settings);
}
} else if (Token::Match(tok->tokAt(-2), "%var% . push_back|push_front|insert|push|assign") &&
astIsContainer(tok->tokAt(-2))) {
Token *vartok = tok->tokAt(-2);
std::vector<const Token *> args = getArguments(tok);
std::size_t n = args.size();
if (n > 1 && Token::typeStr(args[n - 2]) == Token::typeStr(args[n - 1]) &&
(((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) ||
(astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) {
2019-07-16 10:51:07 +02:00
LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byDerefCopy(
vartok, tokenlist, errorLogger, settings);
} else if (!args.empty() && isLifetimeBorrowed(args.back(), settings)) {
2019-07-16 10:51:07 +02:00
LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byVal(
vartok, tokenlist, errorLogger, settings);
}
} else if (tok->function()) {
const Function *f = tok->function();
if (Function::returnsReference(f))
return;
std::vector<const Token*> returns = Function::findReturns(f);
const bool inconclusive = returns.size() > 1;
2020-10-03 11:01:53 +02:00
bool update = false;
for (const Token* returnTok : returns) {
if (returnTok == tok)
continue;
const Variable *returnVar = getLifetimeVariable(returnTok);
if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) {
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger);
ls.inconclusive = inconclusive;
2020-10-03 11:01:53 +02:00
ls.forward = false;
update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings);
}
for (const ValueFlow::Value &v : returnTok->values()) {
if (!v.isLifetimeValue())
continue;
if (!v.tokvalue)
continue;
const Variable *var = v.tokvalue->variable();
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger);
if (!ls.argtok)
continue;
2020-10-03 11:01:53 +02:00
ls.forward = false;
ls.inconclusive = inconclusive;
ls.errorPath = v.errorPath;
ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + ".");
if (!v.isArgumentLifetimeValue() && (var->isReference() || var->isRValueReference())) {
2020-10-03 11:01:53 +02:00
update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings);
} else if (v.isArgumentLifetimeValue()) {
2020-10-03 11:01:53 +02:00
update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings);
}
}
}
2020-10-03 11:01:53 +02:00
if (update)
valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings);
}
}
static void valueFlowLifetimeConstructor(Token* tok,
2019-09-10 19:42:17 +02:00
const Type* t,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
{
if (!Token::Match(tok, "(|{"))
return;
if (!t) {
if (tok->valueType() && tok->valueType()->type != ValueType::RECORD)
return;
// If the type is unknown then assume it captures by value in the
// constructor, but make each lifetime inconclusive
std::vector<const Token*> args = getArguments(tok);
2020-10-03 11:01:53 +02:00
LifetimeStore::forEach(
2020-10-03 11:02:11 +02:00
args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object, [&](LifetimeStore& ls) {
ls.inconclusive = true;
ls.byVal(tok, tokenlist, errorLogger, settings);
});
return;
}
const Scope* scope = t->classScope;
if (!scope)
return;
// Only support aggregate constructors for now
if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) {
std::vector<const Token*> args = getArguments(tok);
2020-10-03 11:01:53 +02:00
auto it = scope->varlist.begin();
LifetimeStore::forEach(args,
"Passed to constructor of '" + t->name() + "'.",
ValueFlow::Value::LifetimeKind::Object,
2020-10-03 11:02:11 +02:00
[&](const LifetimeStore& ls) {
if (it == scope->varlist.end())
return;
const Variable& var = *it;
if (var.isReference() || var.isRValueReference()) {
ls.byRef(tok, tokenlist, errorLogger, settings);
} else {
ls.byVal(tok, tokenlist, errorLogger, settings);
}
it++;
});
}
}
static bool hasInitList(const Token* tok)
{
if (astIsPointer(tok))
return true;
if (astIsContainer(tok)) {
const Library::Container * library = getLibraryContainer(tok);
if (!library)
return false;
return library->hasInitializerListConstructor;
}
return false;
}
static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings)
{
if (!Token::Match(tok, "(|{"))
return;
Token* parent = tok->astParent();
while (Token::simpleMatch(parent, ","))
parent = parent->astParent();
if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) {
valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings);
} else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) {
std::vector<const Token *> args = getArguments(tok);
// Assume range constructor if passed a pair of iterators
if (astIsContainer(parent) && args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) {
2020-10-03 11:01:53 +02:00
LifetimeStore::forEach(
2020-10-03 11:02:11 +02:00
args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object, [&](const LifetimeStore& ls) {
ls.byDerefCopy(tok, tokenlist, errorLogger, settings);
});
} else {
2020-10-03 11:01:53 +02:00
LifetimeStore::forEach(args,
"Passed to initializer list.",
ValueFlow::Value::LifetimeKind::Object,
2020-10-03 11:02:11 +02:00
[&](const LifetimeStore& ls) {
ls.byVal(tok, tokenlist, errorLogger, settings);
});
}
} else {
valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings);
}
}
struct Lambda {
enum class Capture {
Undefined,
ByValue,
ByReference
};
explicit Lambda(const Token * tok)
: capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr), explicitCaptures(), implicitCapture(Capture::Undefined) {
if (!Token::simpleMatch(tok, "[") || !tok->link())
return;
capture = tok;
if (Token::simpleMatch(capture->link(), "] (")) {
arguments = capture->link()->next();
}
const Token * afterArguments = arguments ? arguments->link()->next() : capture->link()->next();
if (afterArguments && afterArguments->originalName() == "->") {
returnTok = afterArguments->next();
bodyTok = Token::findsimplematch(returnTok, "{");
} else if (Token::simpleMatch(afterArguments, "{")) {
bodyTok = afterArguments;
}
2020-09-05 08:00:51 +02:00
for (const Token* c:getCaptures()) {
if (c->variable()) {
explicitCaptures[c->variable()] = std::make_pair(c, Capture::ByValue);
} else if (c->isUnaryOp("&") && Token::Match(c->astOperand1(), "%var%")) {
explicitCaptures[c->astOperand1()->variable()] = std::make_pair(c->astOperand1(), Capture::ByReference);
} else {
const std::string& s = c->expressionString();
if (s == "=")
implicitCapture = Capture::ByValue;
else if (s == "&")
implicitCapture = Capture::ByReference;
}
}
}
const Token * capture;
const Token * arguments;
const Token * returnTok;
const Token * bodyTok;
std::unordered_map<const Variable*, std::pair<const Token*, Capture>> explicitCaptures;
Capture implicitCapture;
std::vector<const Token*> getCaptures() {
return getArguments(capture);
}
bool isLambda() const {
return capture && bodyTok;
}
};
static bool isDecayedPointer(const Token *tok)
{
if (!tok)
return false;
if (!tok->astParent())
return false;
if (astIsPointer(tok->astParent()) && !Token::simpleMatch(tok->astParent(), "return"))
return true;
if (tok->astParent()->isConstOp())
return true;
if (!Token::simpleMatch(tok->astParent(), "return"))
return false;
return astIsPointer(tok->astParent());
}
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->scope())
continue;
if (tok->scope()->type == Scope::eGlobal)
continue;
Lambda lam(tok);
// Lamdas
if (lam.isLambda()) {
const Scope * bodyScope = lam.bodyTok->scope();
std::set<const Scope *> scopes;
// Avoid capturing a variable twice
std::set<nonneg int> varids;
auto isImplicitCapturingVariable = [&](const Token *varTok) {
const Variable *var = varTok->variable();
if (!var)
return false;
if (varids.count(var->declarationId()) > 0)
return false;
if (!var->isLocal() && !var->isArgument())
return false;
const Scope *scope = var->scope();
if (!scope)
return false;
if (scopes.count(scope) > 0)
return false;
if (scope->isNestedIn(bodyScope))
return false;
scopes.insert(scope);
varids.insert(var->declarationId());
return true;
};
2020-10-03 11:01:53 +02:00
bool update = false;
auto captureVariable = [&](const Token* tok2, Lambda::Capture c, std::function<bool(const Token*)> pred) {
if (varids.count(tok->varId()) > 0)
return;
ErrorPath errorPath;
if (c == Lambda::Capture::ByReference) {
2020-10-03 11:01:53 +02:00
LifetimeStore ls{
tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda};
ls.forward = false;
update |= ls.byRef(tok, tokenlist, errorLogger, settings, pred);
} else if (c == Lambda::Capture::ByValue) {
2020-10-03 11:01:53 +02:00
LifetimeStore ls{
tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda};
ls.forward = false;
update |= ls.byVal(tok, tokenlist, errorLogger, settings, pred);
}
};
// Handle explicit capture
2020-09-05 08:00:51 +02:00
for (const auto& p:lam.explicitCaptures) {
const Variable* var = p.first;
if (!var)
continue;
const Token* tok2 = p.second.first;
Lambda::Capture c = p.second.second;
2020-09-05 08:00:51 +02:00
captureVariable(tok2, c, [](const Token*) {
return true;
});
varids.insert(var->declarationId());
}
for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) {
if (!tok2->variable())
continue;
captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable);
}
2020-10-03 11:01:53 +02:00
if (update)
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
// address of
else if (tok->isUnaryOp("&")) {
for (const LifetimeToken& lt : getLifetimeTokens(tok->astOperand1())) {
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
continue;
ErrorPath errorPath = lt.errorPath;
errorPath.emplace_back(tok, "Address of variable taken here.");
ValueFlow::Value value;
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local;
value.tokvalue = lt.token;
value.errorPath = std::move(errorPath);
if (lt.addressOf || astIsPointer(lt.token) || !Token::Match(lt.token->astParent(), ".|["))
value.lifetimeKind = ValueFlow::Value::LifetimeKind::Address;
value.setInconclusive(lt.inconclusive);
setTokenValue(tok, value, tokenlist->getSettings());
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
}
// container lifetimes
else if (astIsContainer(tok)) {
Token * parent = astParentSkipParens(tok);
if (!Token::Match(parent, ". %name% ("))
continue;
bool isContainerOfPointers = true;
const Token* containerTypeToken = tok->valueType()->containerTypeToken;
2020-07-16 21:33:39 +02:00
if (containerTypeToken) {
ValueType vt = ValueType::parseDecl(containerTypeToken, settings);
isContainerOfPointers = vt.pointer > 0;
}
LifetimeStore ls;
if (astIsIterator(parent->tokAt(2)))
ls = LifetimeStore{tok, "Iterator to container is created here.", ValueFlow::Value::LifetimeKind::Iterator};
else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers) || Token::Match(parent->next(), "data|c_str"))
ls = LifetimeStore{tok, "Pointer to container is created here.", ValueFlow::Value::LifetimeKind::Object};
else
continue;
// Dereferencing
if (tok->isUnaryOp("*") || parent->originalName() == "->")
ls.byDerefCopy(parent->tokAt(2), tokenlist, errorLogger, settings);
else
ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings);
}
// Check constructors
else if (Token::Match(tok, "=|return|%type%|%var% {")) {
valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings);
}
// Check function calls
else if (Token::Match(tok, "%name% (")) {
valueFlowLifetimeFunction(tok, tokenlist, errorLogger, settings);
}
// Check variables
else if (tok->variable()) {
ErrorPath errorPath;
const Variable * var = getLifetimeVariable(tok, errorPath);
if (!var)
continue;
if (var->nameToken() == tok)
continue;
if (var->isArray() && !var->isStlType() && !var->isArgument() && isDecayedPointer(tok)) {
errorPath.emplace_back(tok, "Array decayed to pointer here.");
ValueFlow::Value value;
2019-07-16 11:12:35 +02:00
value.valueType = ValueFlow::Value::ValueType::LIFETIME;
value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local;
value.tokvalue = var->nameToken();
value.errorPath = errorPath;
setTokenValue(tok, value, tokenlist->getSettings());
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
}
2020-09-04 18:56:34 +02:00
// Forward any lifetimes
else if (std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
}
}
static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr)
{
if (tok->str() != "std")
return false;
2019-07-10 15:27:07 +02:00
ValueFlow::Value::MoveKind kind = ValueFlow::Value::MoveKind::NonMovedVariable;
Token * variableToken = nullptr;
if (Token::Match(tok, "std :: move ( %var% )")) {
variableToken = tok->tokAt(4);
2019-07-10 15:27:07 +02:00
kind = ValueFlow::Value::MoveKind::MovedVariable;
} else if (Token::simpleMatch(tok, "std :: forward <")) {
const Token * const leftAngle = tok->tokAt(3);
Token * rightAngle = leftAngle->link();
if (Token::Match(rightAngle, "> ( %var% )")) {
variableToken = rightAngle->tokAt(2);
2019-07-10 15:27:07 +02:00
kind = ValueFlow::Value::MoveKind::ForwardedVariable;
}
}
2017-09-01 17:19:25 +02:00
if (!variableToken)
return false;
if (variableToken->strAt(2) == ".") // Only partially moved
return false;
if (variableToken->valueType() && variableToken->valueType()->type >= ValueType::Type::VOID)
return false;
if (moveKind != nullptr)
*moveKind = kind;
if (varTok != nullptr)
*varTok = variableToken;
return true;
}
static bool isOpenParenthesisMemberFunctionCallOfVarId(const Token * openParenthesisToken, nonneg int varId)
{
2017-09-01 17:16:08 +02:00
const Token * varTok = openParenthesisToken->tokAt(-3);
return Token::Match(varTok, "%varid% . %name% (", varId) &&
varTok->next()->originalName() == emptyString;
}
static const Token * findOpenParentesisOfMove(const Token * moveVarTok)
{
const Token * tok = moveVarTok;
while (tok && tok->str() != "(")
tok = tok->previous();
return tok;
}
static const Token * findEndOfFunctionCallForParameter(const Token * parameterToken)
{
if (!parameterToken)
return nullptr;
const Token * parent = parameterToken->astParent();
2016-12-17 18:51:16 +01:00
while (parent && !parent->isOp() && parent->str() != "(")
parent = parent->astParent();
if (!parent)
return nullptr;
return nextAfterAstRightmostLeaf(parent);
}
static void valueFlowAfterMove(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
if (!tokenlist->isCPP() || settings->standards.cpp < Standards::CPP11)
return;
2018-07-14 22:26:22 +02:00
for (const Scope * scope : symboldatabase->functionScopes) {
if (!scope)
continue;
const Token * start = scope->bodyStart;
if (scope->function) {
const Token * memberInitializationTok = scope->function->constructorMemberInitialization();
if (memberInitializationTok)
start = memberInitializationTok;
}
for (Token* tok = const_cast<Token*>(start); tok != scope->bodyEnd; tok = tok->next()) {
Token * varTok;
if (Token::Match(tok, "%var% . reset|clear (") && tok->next()->originalName() == emptyString) {
varTok = tok;
ValueFlow::Value value;
2019-07-10 15:27:07 +02:00
value.valueType = ValueFlow::Value::ValueType::MOVED;
value.moveKind = ValueFlow::Value::MoveKind::NonMovedVariable;
value.errorPath.emplace_back(tok, "Calling " + tok->next()->expressionString() + " makes " + tok->str() + " 'non-moved'");
value.setKnown();
std::list<ValueFlow::Value> values;
values.push_back(value);
const Variable *var = varTok->variable();
if (!var || (!var->isLocal() && !var->isArgument()))
continue;
const nonneg int varId = varTok->varId();
2020-01-11 09:17:32 +01:00
const Token * const endOfVarScope = var->scope()->bodyEnd;
setTokenValue(varTok, value, settings);
valueFlowForwardVariable(
varTok->next(), endOfVarScope, var, varId, values, false, false, tokenlist, errorLogger, settings);
continue;
}
ValueFlow::Value::MoveKind moveKind;
if (!isStdMoveOrStdForwarded(tok, &moveKind, &varTok))
continue;
const nonneg int varId = varTok->varId();
// x is not MOVED after assignment if code is: x = ... std::move(x) .. ;
const Token *parent = tok->astParent();
while (parent && parent->str() != "=" && parent->str() != "return" &&
!(parent->str() == "(" && isOpenParenthesisMemberFunctionCallOfVarId(parent, varId)))
parent = parent->astParent();
if (parent &&
(parent->str() == "return" || // MOVED in return statement
parent->str() == "(")) // MOVED in self assignment, isOpenParenthesisMemberFunctionCallOfVarId == true
continue;
if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId)
continue;
const Variable *var = varTok->variable();
if (!var)
continue;
2020-01-11 09:17:32 +01:00
const Token * const endOfVarScope = var->scope()->bodyEnd;
ValueFlow::Value value;
2019-07-10 15:27:07 +02:00
value.valueType = ValueFlow::Value::ValueType::MOVED;
value.moveKind = moveKind;
2019-07-10 15:27:07 +02:00
if (moveKind == ValueFlow::Value::MoveKind::MovedVariable)
value.errorPath.emplace_back(tok, "Calling std::move(" + varTok->str() + ")");
2017-10-10 15:49:15 +02:00
else // if (moveKind == ValueFlow::Value::ForwardedVariable)
value.errorPath.emplace_back(tok, "Calling std::forward(" + varTok->str() + ")");
value.setKnown();
std::list<ValueFlow::Value> values;
values.push_back(value);
const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok);
const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove);
if (endOfFunctionCall)
valueFlowForwardVariable(const_cast<Token*>(endOfFunctionCall),
endOfVarScope,
var,
varId,
values,
false,
false,
tokenlist,
errorLogger,
settings);
}
}
}
static const Token* findIncompleteVar(const Token* start, const Token* end)
{
for (const Token* tok = start; tok != end; tok = tok->next()) {
if (tok->isIncompleteVar())
return tok;
}
return nullptr;
}
static ValueFlow::Value makeConditionValue(long long val, const Token* condTok, bool assume)
{
ValueFlow::Value v(val);
v.setKnown();
v.condition = condTok;
if (assume)
v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is true");
else
v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is false");
return v;
}
2021-01-25 22:51:50 +01:00
//
static void valueFlowConditionExpressions(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
for (const Scope * scope : symboldatabase->functionScopes) {
if (const Token* incompleteTok = findIncompleteVar(scope->bodyStart, scope->bodyEnd)) {
if (incompleteTok->isIncompleteVar()) {
if (settings->debugwarnings)
bailoutIncompleteVar(tokenlist, errorLogger, incompleteTok, "Skipping function due to incomplete variable " + incompleteTok->str());
break;
}
}
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
if (!Token::simpleMatch(tok, "if ("))
continue;
// Skip known values
if (tok->next()->hasKnownValue())
continue;
Token * parenTok = tok->next();
if (!Token::simpleMatch(parenTok->link(), ") {"))
continue;
Token * blockTok = parenTok->link()->tokAt(1);
const Token* condTok = parenTok->astOperand2();
if (condTok->hasKnownIntValue())
continue;
Token* startTok = blockTok;
// Inner condition
{
std::vector<const Token*> conds = {condTok};
if (Token::simpleMatch(condTok, "&&")) {
std::vector<const Token*> args = astFlatten(condTok, "&&");
conds.insert(conds.end(), args.begin(), args.end());
}
2021-01-25 22:51:50 +01:00
for (const Token* condTok2:conds) {
ExpressionAnalyzer a1(condTok2, makeConditionValue(1, condTok2, true), tokenlist);
valueFlowGenericForward(startTok, startTok->link(), a1, settings);
OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(0, condTok2, true), tokenlist);
valueFlowGenericForward(startTok, startTok->link(), a2, settings);
}
}
std::vector<const Token*> conds = {condTok};
if (Token::simpleMatch(condTok, "||")) {
std::vector<const Token*> args = astFlatten(condTok, "||");
conds.insert(conds.end(), args.begin(), args.end());
}
// Check else block
if (Token::simpleMatch(startTok->link(), "} else {")) {
startTok = startTok->link()->tokAt(2);
2021-01-25 22:51:50 +01:00
for (const Token* condTok2:conds) {
ExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist);
valueFlowGenericForward(startTok, startTok->link(), a1, settings);
OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist);
valueFlowGenericForward(startTok, startTok->link(), a2, settings);
}
}
// Check if the block terminates early
if (isEscapeScope(blockTok, tokenlist)) {
2021-01-25 22:51:50 +01:00
for (const Token* condTok2:conds) {
ExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist);
valueFlowGenericForward(startTok->link()->next(), scope->bodyEnd, a1, settings);
OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist);
valueFlowGenericForward(startTok->link()->next(), scope->bodyEnd, a2, settings);
}
}
}
}
}
static void valueFlowForwardAssign(Token* const tok,
const Token* expr,
std::vector<const Variable*> vars,
2019-04-26 11:30:35 +02:00
std::list<ValueFlow::Value> values,
const bool init,
TokenList* const tokenlist,
ErrorLogger* const errorLogger,
const Settings* const settings)
{
const Token* endOfVarScope = getEndOfVarScope(tok, vars);
if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue));
}
if (std::all_of(
2021-01-25 22:51:50 +01:00
vars.begin(), vars.end(), [&](const Variable* var) {
return !var->isPointer() && !var->isSmartPointer();
}))
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
if (tok->astParent()) {
for (ValueFlow::Value& value : values) {
const std::string info = "Assignment '" + tok->astParent()->expressionString() + "', assigned value is " + value.infoString();
value.errorPath.emplace_back(tok, info);
2019-04-26 11:30:35 +02:00
}
}
if (tokenlist->isCPP() && vars.size() == 1 && Token::Match(vars.front()->typeStartToken(), "bool|_Bool")) {
std::list<ValueFlow::Value>::iterator it;
for (it = values.begin(); it != values.end(); ++it) {
if (it->isIntValue())
it->intvalue = (it->intvalue != 0);
if (it->isTokValue())
it ->intvalue = (it->tokvalue != nullptr);
}
}
// Static variable initialisation?
if (vars.size() == 1 && vars.front()->isStatic() && init)
lowerToPossible(values);
// Skip RHS
const Token * nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next();
if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isTokValue))) {
std::list<ValueFlow::Value> tokvalues;
std::copy_if(values.begin(),
values.end(),
std::back_inserter(tokvalues),
std::mem_fn(&ValueFlow::Value::isTokValue));
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
}
for (ValueFlow::Value& value:values)
value.tokvalue = tok;
valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings);
}
static void valueFlowForwardAssign(Token* const tok,
const Variable* const var,
const std::list<ValueFlow::Value>& values,
const bool,
const bool init,
TokenList* const tokenlist,
ErrorLogger* const errorLogger,
const Settings* const settings)
{
valueFlowForwardAssign(tok, var->nameToken(), {var}, values, init, tokenlist, errorLogger, settings);
}
static std::list<ValueFlow::Value> truncateValues(std::list<ValueFlow::Value> values, const ValueType *valueType, const Settings *settings)
{
if (!valueType || !valueType->isIntegral())
return values;
const size_t sz = ValueFlow::getSizeOf(*valueType, settings);
for (ValueFlow::Value &value : values) {
// Dont truncate impossible values since those can be outside of the valid range
if (value.isImpossible())
continue;
if (value.isFloatValue()) {
value.intvalue = value.floatValue;
value.valueType = ValueFlow::Value::ValueType::INT;
}
if (value.isIntValue() && sz > 0 && sz < 8) {
const MathLib::biguint unsignedMaxValue = (1ULL << (sz * 8)) - 1ULL;
const MathLib::biguint signBit = 1ULL << (sz * 8 - 1);
value.intvalue &= unsignedMaxValue;
if (valueType->sign == ValueType::Sign::SIGNED && (value.intvalue & signBit))
value.intvalue |= ~unsignedMaxValue;
}
}
return values;
}
static bool isVariableInit(const Token *tok)
{
return tok->str() == "(" &&
tok->isBinaryOp() &&
tok->astOperand1()->variable() &&
tok->astOperand1()->variable()->nameToken() == tok->astOperand1() &&
tok->astOperand1()->variable()->valueType() &&
tok->astOperand1()->variable()->valueType()->type >= ValueType::Type::VOID &&
!Token::simpleMatch(tok->astOperand2(), ",");
}
static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
2018-07-14 22:26:22 +02:00
for (const Scope * scope : symboldatabase->functionScopes) {
std::set<nonneg int> aliased;
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
// Alias
2018-07-14 22:36:08 +02:00
if (tok->isUnaryOp("&")) {
aliased.insert(tok->astOperand1()->exprId());
continue;
}
// Assignment
if ((tok->str() != "=" && !isVariableInit(tok)) || (tok->astParent()))
continue;
// Lhs should be a variable
if (!tok->astOperand1() || !tok->astOperand1()->exprId())
continue;
const nonneg int exprid = tok->astOperand1()->exprId();
if (aliased.find(exprid) != aliased.end())
continue;
std::vector<const Variable*> vars = getLHSVariables(tok);
2014-07-15 10:36:13 +02:00
// Rhs values..
if (!tok->astOperand2() || tok->astOperand2()->values().empty())
continue;
std::list<ValueFlow::Value> values = truncateValues(tok->astOperand2()->values(), tok->astOperand1()->valueType(), settings);
// Remove known values
std::set<ValueFlow::Value::ValueType> types;
if (tok->astOperand1()->hasKnownValue()) {
2020-08-28 19:29:33 +02:00
for (const ValueFlow::Value& value:tok->astOperand1()->values()) {
if (value.isKnown())
types.insert(value.valueType);
}
}
2020-08-28 19:29:33 +02:00
values.remove_if([&](const ValueFlow::Value& value) {
return types.count(value.valueType) > 0;
});
// Remove container size if its not a container
if (!astIsContainer(tok->astOperand2()))
2020-08-28 19:29:33 +02:00
values.remove_if([&](const ValueFlow::Value& value) {
return value.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE;
2020-08-28 19:29:33 +02:00
});
if (values.empty())
continue;
const bool init = vars.size() == 1 && vars.front()->nameToken() == tok->astOperand1();
valueFlowForwardAssign(
tok->astOperand2(), tok->astOperand1(), vars, values, init, tokenlist, errorLogger, settings);
}
}
}
static void valueFlowSetConditionToKnown(const Token* tok, std::list<ValueFlow::Value>& values, bool then)
{
if (values.empty())
return;
if (then && !Token::Match(tok, "==|!|("))
return;
if (!then && !Token::Match(tok, "!=|%var%|("))
return;
if (isConditionKnown(tok, then))
changePossibleToKnown(values);
}
static bool isBreakScope(const Token* const endToken)
{
if (!Token::simpleMatch(endToken, "}"))
return false;
if (!Token::simpleMatch(endToken->link(), "{"))
return false;
return Token::findmatch(endToken->link(), "break|goto", endToken);
}
static ValueFlow::Value asImpossible(ValueFlow::Value v)
{
v.invertRange();
v.setImpossible();
return v;
}
2019-09-28 20:50:56 +02:00
static void insertImpossible(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
{
std::transform(input.begin(), input.end(), std::back_inserter(values), &asImpossible);
}
static void insertNegateKnown(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
{
2020-08-26 10:15:09 +02:00
for (ValueFlow::Value value:input) {
if (!value.isIntValue() && !value.isContainerSizeValue())
continue;
value.intvalue = !value.intvalue;
value.setKnown();
values.push_back(value);
}
}
static std::vector<const Variable*> getExprVariables(const Token* expr,
2019-09-26 10:32:49 +02:00
const TokenList* tokenlist,
const SymbolDatabase* symboldatabase,
const Settings* settings)
{
std::vector<const Variable*> result;
FwdAnalysis fwdAnalysis(tokenlist->isCPP(), settings->library);
std::set<nonneg int> varids = fwdAnalysis.getExprVarIds(expr);
std::transform(varids.begin(), varids.end(), std::back_inserter(result), [&](int id) {
return symboldatabase->getVariableFromVarId(id);
});
return result;
}
struct ConditionHandler {
struct Condition {
const Token *vartok;
std::list<ValueFlow::Value> true_values;
std::list<ValueFlow::Value> false_values;
bool inverted = false;
Condition() : vartok(nullptr), true_values(), false_values(), inverted(false) {}
};
virtual bool forward(Token* start,
const Token* stop,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
const Settings* settings) const = 0;
virtual void reverse(Token* start,
const Token* endToken,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
const Settings* settings) const = 0;
virtual Condition parse(const Token* tok, const Settings* settings) const = 0;
void traverseCondition(
TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings,
const std::function<
2020-12-25 08:58:12 +01:00
void(const Condition& cond, Token* tok, const Scope* scope, const std::vector<const Variable*>& vars)>& f) const {
for (const Scope *scope : symboldatabase->functionScopes) {
std::set<nonneg int> aliased;
for (Token *tok = const_cast<Token *>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
if (Token::Match(tok, "if|while|for ("))
continue;
if (Token::Match(tok, "= & %var% ;"))
aliased.insert(tok->tokAt(2)->varId());
const Token* top = tok->astTop();
if (!top)
continue;
if (!Token::Match(top->previous(), "if|while|for (") && !Token::Match(tok->astParent(), "&&|%oror%|?"))
continue;
Condition cond = parse(tok, settings);
if (!cond.vartok)
continue;
if (cond.vartok->variable() && cond.vartok->variable()->isVolatile())
continue;
if (cond.true_values.empty() || cond.false_values.empty())
continue;
if (exprDependsOnThis(cond.vartok))
continue;
std::vector<const Variable*> vars = getExprVariables(cond.vartok, tokenlist, symboldatabase, settings);
if (std::any_of(vars.begin(), vars.end(), [](const Variable* var) {
return !var;
}))
continue;
if (!vars.empty() && (vars.front()))
if (std::any_of(vars.begin(), vars.end(), [&](const Variable* var) {
return var && aliased.find(var->declarationId()) != aliased.end();
})) {
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
cond.vartok,
"variable is aliased so we just skip all valueflow after condition");
continue;
}
f(cond, tok, scope, vars);
}
}
}
void beforeCondition(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
2021-01-10 14:46:19 +01:00
const Settings* settings) const {
traverseCondition(
tokenlist,
symboldatabase,
errorLogger,
settings,
2021-01-10 14:46:19 +01:00
[&](const Condition& cond, Token* tok, const Scope*, const std::vector<const Variable*>&) {
if (cond.vartok->exprId() == 0)
return;
// If condition is known then don't propagate value
2021-01-10 14:46:19 +01:00
if (tok->hasKnownIntValue())
return;
2021-01-10 14:46:19 +01:00
const Token* top = tok->astTop();
2021-01-10 14:46:19 +01:00
if (Token::Match(top, "%assign%"))
return;
if (Token::Match(cond.vartok->astParent(), "%assign%|++|--"))
return;
2021-01-10 14:46:19 +01:00
if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) {
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
tok,
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
return;
}
2021-01-10 14:46:19 +01:00
// if,macro => bailout
if (Token::simpleMatch(top->previous(), "if (") && top->previous()->isExpandedMacro()) {
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
tok,
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
return;
}
2020-12-25 08:58:12 +01:00
std::list<ValueFlow::Value> values = cond.true_values;
if (cond.true_values != cond.false_values)
values.insert(values.end(), cond.false_values.begin(), cond.false_values.end());
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
if (Token::Match(tok, "<|>")) {
values.remove_if([](const ValueFlow::Value& v) {
if (v.isIntValue())
return v.intvalue != 0;
return false;
});
if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED)
return;
}
if (values.empty())
return;
2021-01-10 14:46:19 +01:00
// bailout: for/while-condition, variable is changed in while loop
if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) {
2021-01-10 14:46:19 +01:00
// Variable changed in 3rd for-expression
if (Token::simpleMatch(top->previous(), "for (")) {
if (top->astOperand2() && top->astOperand2()->astOperand2() &&
isExpressionChanged(
cond.vartok, top->astOperand2()->astOperand2(), top->link(), settings, tokenlist->isCPP())) {
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
tok,
"variable '" + cond.vartok->expressionString() + "' used in loop");
return;
2014-06-19 17:29:41 +02:00
}
2021-01-10 14:46:19 +01:00
}
2021-01-10 14:46:19 +01:00
// Variable changed in loop code
const Token* const start = top;
const Token* const block = top->link()->next();
const Token* const end = block->link();
if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) {
// If its reassigned in loop then analyze from the end
if (!Token::Match(tok, "%assign%|++|--") &&
2021-01-25 22:51:50 +01:00
findExpression(cond.vartok->exprId(), start, end, [&](const Token* tok2) {
return Token::Match(tok2->astParent(), "%assign%") && astIsLHS(tok2);
})) {
// Start at the end of the loop body
Token* bodyTok = top->link()->next();
reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist, settings);
2020-12-25 08:58:12 +01:00
}
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
tok,
"variable '" + cond.vartok->expressionString() + "' used in loop");
return;
2014-06-19 17:29:41 +02:00
}
2021-01-10 14:46:19 +01:00
}
Token* startTok = nullptr;
if (astIsRHS(tok))
startTok = tok->astParent();
else if (astIsLHS(tok))
startTok = previousBeforeAstLeftmostLeaf(tok->astParent());
if (!startTok)
startTok = tok->previous();
reverse(startTok, nullptr, cond.vartok, values, tokenlist, settings);
2021-01-10 14:46:19 +01:00
});
}
2020-12-25 08:58:12 +01:00
void afterCondition(TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings) const {
traverseCondition(
tokenlist,
symboldatabase,
errorLogger,
settings,
2021-01-10 14:46:19 +01:00
[&](const Condition& cond, Token* tok, const Scope* scope, const std::vector<const Variable*>& vars) {
if (Token::simpleMatch(tok->astParent(), "?"))
return;
const Token* top = tok->astTop();
2021-01-10 14:46:19 +01:00
std::list<ValueFlow::Value> thenValues;
std::list<ValueFlow::Value> elseValues;
2021-01-10 14:46:19 +01:00
if (!Token::Match(tok, "!=|=|(|.") && tok != cond.vartok) {
thenValues.insert(thenValues.end(), cond.true_values.begin(), cond.true_values.end());
if (isConditionKnown(tok, false))
insertImpossible(elseValues, cond.false_values);
}
if (!Token::Match(tok, "==|!")) {
elseValues.insert(elseValues.end(), cond.false_values.begin(), cond.false_values.end());
if (isConditionKnown(tok, true)) {
insertImpossible(thenValues, cond.true_values);
if (Token::Match(tok, "(|.|%var%") && astIsBool(tok))
insertNegateKnown(thenValues, cond.true_values);
}
2021-01-10 14:46:19 +01:00
}
2021-01-10 14:46:19 +01:00
if (cond.inverted)
std::swap(thenValues, elseValues);
2020-12-25 08:58:12 +01:00
2021-01-10 14:46:19 +01:00
if (Token::Match(tok->astParent(), "%oror%|&&")) {
Token* parent = tok->astParent();
if (astIsRHS(tok) && parent->astParent() && parent->str() == parent->astParent()->str())
parent = parent->astParent();
else if (!astIsLHS(tok)) {
parent = nullptr;
}
if (parent) {
const std::string& op(parent->str());
std::list<ValueFlow::Value> values;
if (op == "&&")
values = thenValues;
else if (op == "||")
values = elseValues;
if (Token::Match(tok, "==|!="))
changePossibleToKnown(values);
if (!values.empty()) {
bool assign = false;
visitAstNodes(parent->astOperand2(), [&](Token* tok2) {
if (tok2 == tok)
return ChildrenToVisit::done;
if (isSameExpression(
tokenlist->isCPP(), false, cond.vartok, tok2, settings->library, true, false))
setTokenValue(tok2, values.front(), settings);
else if (Token::Match(tok2, "++|--|=") && isSameExpression(tokenlist->isCPP(),
false,
cond.vartok,
tok2->astOperand1(),
settings->library,
true,
false)) {
assign = true;
return ChildrenToVisit::done;
}
return ChildrenToVisit::op1_and_op2;
});
if (assign)
return;
}
2021-01-10 14:46:19 +01:00
}
}
{
const Token* tok2 = tok;
std::string op;
bool mixedOperators = false;
while (tok2->astParent()) {
const Token* parent = tok2->astParent();
if (Token::Match(parent, "%oror%|&&")) {
if (op.empty()) {
op = parent->str();
} else if (op != parent->str()) {
mixedOperators = true;
break;
}
}
2021-01-10 14:46:19 +01:00
if (parent->str() == "!") {
op = (op == "&&" ? "||" : "&&");
}
tok2 = parent;
}
if (mixedOperators) {
return;
}
}
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
// does condition reassign variable?
if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") &&
isVariablesChanged(top, top->link(), 0, vars, settings, tokenlist->isCPP())) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok, "assignment in condition");
return;
}
2020-12-25 08:58:12 +01:00
2021-01-10 14:46:19 +01:00
// start token of conditional code
Token* startTokens[] = {nullptr, nullptr};
// if astParent is "!" we need to invert codeblock
{
const Token* tok2 = tok;
while (tok2->astParent()) {
const Token* parent = tok2->astParent();
2021-01-10 14:46:19 +01:00
while (parent && parent->str() == "&&")
parent = parent->astParent();
if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) {
std::swap(thenValues, elseValues);
}
tok2 = parent;
}
}
2021-01-10 14:46:19 +01:00
// determine startToken(s)
if (Token::simpleMatch(top->link(), ") {"))
startTokens[0] = top->link()->next();
if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
startTokens[1] = top->link()->linkAt(1)->tokAt(2);
2021-01-10 14:46:19 +01:00
int changeBlock = -1;
2021-01-10 14:46:19 +01:00
for (int i = 0; i < 2; i++) {
const Token* const startToken = startTokens[i];
if (!startToken)
continue;
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
valueFlowSetConditionToKnown(tok, values, i == 0);
2021-01-10 14:46:19 +01:00
// TODO: The endToken should not be startTokens[i]->link() in the valueFlowForwardVariable call
if (forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings))
changeBlock = i;
changeKnownToPossible(values);
}
// TODO: Values changed in noreturn blocks should not bail
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
if (settings->debugwarnings)
bailout(tokenlist,
errorLogger,
startTokens[changeBlock]->link(),
"valueFlowAfterCondition: " + cond.vartok->expressionString() +
" is changed in conditional block");
return;
}
2021-01-10 14:46:19 +01:00
// After conditional code..
if (Token::simpleMatch(top->link(), ") {")) {
Token* after = top->link()->linkAt(1);
const Token* unknownFunction = nullptr;
const bool isWhile =
tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (");
bool dead_if = (!isBreakScope(after) && isWhile) ||
(isReturnScope(after, &settings->library, &unknownFunction) && !isWhile);
bool dead_else = false;
if (!dead_if && unknownFunction) {
if (settings->debugwarnings)
2021-01-10 14:46:19 +01:00
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
return;
}
2021-01-10 14:46:19 +01:00
if (Token::simpleMatch(after, "} else {")) {
after = after->linkAt(2);
unknownFunction = nullptr;
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
if (!dead_else && unknownFunction) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
return;
}
2021-01-10 14:46:19 +01:00
}
2021-01-10 14:46:19 +01:00
if (dead_if && dead_else)
return;
2021-01-10 14:46:19 +01:00
std::list<ValueFlow::Value> values;
if (dead_if) {
values = elseValues;
} else if (dead_else) {
values = thenValues;
} else {
std::copy_if(thenValues.begin(),
thenValues.end(),
std::back_inserter(values),
std::mem_fn(&ValueFlow::Value::isPossible));
std::copy_if(elseValues.begin(),
elseValues.end(),
std::back_inserter(values),
std::mem_fn(&ValueFlow::Value::isPossible));
}
if (dead_if || dead_else) {
if (Token::Match(tok->astParent(), "&&|&")) {
values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible));
changeKnownToPossible(values);
} else {
2021-01-10 14:46:19 +01:00
valueFlowSetConditionToKnown(tok, values, true);
valueFlowSetConditionToKnown(tok, values, false);
}
}
if (!values.empty())
forward(after, scope->bodyEnd, cond.vartok, values, tokenlist, settings);
}
2021-01-10 14:46:19 +01:00
}
});
}
virtual ~ConditionHandler() {}
};
static void valueFlowCondition(const ValuePtr<ConditionHandler>& handler,
TokenList* tokenlist,
SymbolDatabase* symboldatabase,
ErrorLogger* errorLogger,
const Settings* settings)
{
handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings);
handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings);
}
struct SimpleConditionHandler : ConditionHandler {
virtual bool forward(Token* start,
const Token* stop,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
2020-12-25 08:58:12 +01:00
const Settings* settings) const OVERRIDE {
return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, settings).isModified();
}
virtual void reverse(Token* start,
const Token* endToken,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
2021-01-25 22:51:50 +01:00
const Settings* settings) const OVERRIDE {
return valueFlowReverse(start, endToken, exprTok, values, tokenlist, settings);
}
2020-12-25 08:58:12 +01:00
virtual Condition parse(const Token* tok, const Settings*) const OVERRIDE {
Condition cond;
ValueFlow::Value true_value;
ValueFlow::Value false_value;
const Token *vartok = parseCompareInt(tok, true_value, false_value);
if (vartok) {
if (vartok->hasKnownValue())
return cond;
if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2())
vartok = vartok->astOperand1();
cond.true_values.push_back(true_value);
cond.false_values.push_back(false_value);
cond.vartok = vartok;
return cond;
}
if (tok->str() == "!") {
vartok = tok->astOperand1();
} else if (tok->astParent() && (Token::Match(tok->astParent(), "%oror%|&&|?") ||
Token::Match(tok->astParent()->previous(), "if|while ("))) {
if (Token::simpleMatch(tok, "="))
vartok = tok->astOperand1();
else if (!Token::Match(tok, "%comp%|%assign%"))
vartok = tok;
}
if (!vartok)
return cond;
cond.true_values.emplace_back(tok, 0LL);
cond.false_values.emplace_back(tok, 0LL);
cond.vartok = vartok;
return cond;
}
};
static bool isInBounds(const ValueFlow::Value& value, MathLib::bigint x)
{
if (value.intvalue == x)
return true;
if (value.bound == ValueFlow::Value::Bound::Lower && value.intvalue > x)
return false;
if (value.bound == ValueFlow::Value::Bound::Upper && value.intvalue < x)
return false;
// Checking for equality is not necessary since we already know the value is not equal
if (value.bound == ValueFlow::Value::Bound::Point)
return false;
return true;
}
2020-02-01 08:24:31 +01:00
static const ValueFlow::Value* getCompareIntValue(const std::list<ValueFlow::Value>& values, std::function<bool(MathLib::bigint, MathLib::bigint)> compare)
{
const ValueFlow::Value* result = nullptr;
for (const ValueFlow::Value& value : values) {
if (!value.isIntValue())
continue;
if (result)
2020-02-01 08:28:18 +01:00
result = &std::min(value, *result, [compare](const ValueFlow::Value& x, const ValueFlow::Value& y) {
2019-12-26 15:48:17 +01:00
return compare(x.intvalue, y.intvalue);
});
else
result = &value;
}
return result;
}
static const ValueFlow::Value* proveLessThan(const std::list<ValueFlow::Value>& values, MathLib::bigint x)
{
const ValueFlow::Value* result = nullptr;
2019-12-26 15:48:17 +01:00
const ValueFlow::Value* maxValue = getCompareIntValue(values, std::greater<MathLib::bigint> {});
if (maxValue && maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower) {
if (maxValue->intvalue <= x)
result = maxValue;
}
return result;
}
static const ValueFlow::Value* proveGreaterThan(const std::list<ValueFlow::Value>& values, MathLib::bigint x)
{
const ValueFlow::Value* result = nullptr;
2019-12-26 15:48:17 +01:00
const ValueFlow::Value* minValue = getCompareIntValue(values, std::less<MathLib::bigint> {});
if (minValue && minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper) {
if (minValue->intvalue >= x)
result = minValue;
}
return result;
}
static const ValueFlow::Value* proveNotEqual(const std::list<ValueFlow::Value>& values, MathLib::bigint x)
{
const ValueFlow::Value* result = nullptr;
for (const ValueFlow::Value& value : values) {
if (value.valueType != ValueFlow::Value::ValueType::INT)
continue;
if (result && !isInBounds(value, result->intvalue))
continue;
if (value.isImpossible()) {
if (value.intvalue == x)
return &value;
if (!isInBounds(value, x))
continue;
result = &value;
} else {
if (value.intvalue == x)
return nullptr;
if (!isInBounds(value, x))
continue;
result = nullptr;
}
}
return result;
}
ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val)
{
if (!varTok)
return ValueFlow::Value{};
if (varTok->hasKnownIntValue())
return ValueFlow::Value{};
if (std::none_of(varTok->values().begin(), varTok->values().end(), [](const ValueFlow::Value& v) {
return v.isImpossible() && v.valueType == ValueFlow::Value::ValueType::INT;
})) {
return ValueFlow::Value{};
}
const ValueFlow::Value* result = nullptr;
bool known = false;
if (op == "==" || op == "!=") {
result = proveNotEqual(varTok->values(), val);
known = op == "!=";
} else if (op == "<" || op == ">=") {
result = proveLessThan(varTok->values(), val);
known = op == "<";
if (!result && !isSaturated(val)) {
result = proveGreaterThan(varTok->values(), val - 1);
known = op == ">=";
}
} else if (op == ">" || op == "<=") {
result = proveGreaterThan(varTok->values(), val);
known = op == ">";
if (!result && !isSaturated(val)) {
result = proveLessThan(varTok->values(), val + 1);
known = op == "<=";
}
}
if (!result)
return ValueFlow::Value{};
ValueFlow::Value value = *result;
value.intvalue = known;
value.bound = ValueFlow::Value::Bound::Point;
value.setKnown();
return value;
}
ValueFlow::Value inferCondition(std::string op, MathLib::bigint val, const Token* varTok)
{
// Flip the operator
if (op == ">")
op = "<";
else if (op == "<")
op = ">";
else if (op == ">=")
op = "<=";
else if (op == "<=")
op = ">=";
return inferCondition(op, varTok, val);
}
static void valueFlowInferCondition(TokenList* tokenlist,
const Settings* settings)
{
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->astParent())
continue;
if (tok->hasKnownValue())
continue;
if (tok->variable() && (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
Token::Match(tok->astParent()->previous(), "if|while ("))) {
const ValueFlow::Value* result = proveNotEqual(tok->values(), 0);
if (!result)
continue;
ValueFlow::Value value = *result;
value.intvalue = 1;
value.bound = ValueFlow::Value::Bound::Point;
value.setKnown();
setTokenValue(tok, value, settings);
} else if (tok->isComparisonOp()) {
ValueFlow::Value value{};
std::string op = tok->str();
if (tok->astOperand1()->hasKnownIntValue()) {
MathLib::bigint val = tok->astOperand1()->values().front().intvalue;
const Token* varTok = tok->astOperand2();
value = inferCondition(tok->str(), val, varTok);
} else if (tok->astOperand2()->hasKnownIntValue()) {
MathLib::bigint val = tok->astOperand2()->values().front().intvalue;
const Token* varTok = tok->astOperand1();
value = inferCondition(tok->str(), varTok, val);
}
if (!value.isKnown())
continue;
setTokenValue(tok, value, settings);
}
}
}
static bool valueFlowForLoop2(const Token *tok,
ProgramMemory *memory1,
ProgramMemory *memory2,
ProgramMemory *memoryAfter)
{
// for ( firstExpression ; secondExpression ; thirdExpression )
const Token *firstExpression = tok->next()->astOperand2()->astOperand1();
const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1();
const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2();
ProgramMemory programMemory;
2014-03-30 10:22:06 +02:00
MathLib::bigint result(0);
bool error = false;
execute(firstExpression, &programMemory, &result, &error);
if (error)
return false;
execute(secondExpression, &programMemory, &result, &error);
if (result == 0) // 2nd expression is false => no looping
return false;
if (error) {
// If a variable is reassigned in second expression, return false
2018-11-23 19:16:19 +01:00
bool reassign = false;
visitAstNodes(secondExpression,
[&](const Token *t) {
if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId()))
// TODO: investigate what variable is assigned.
2018-11-23 19:16:19 +01:00
reassign = true;
return reassign ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2;
});
if (reassign)
return false;
}
ProgramMemory startMemory(programMemory);
ProgramMemory endMemory;
int maxcount = 10000;
while (result != 0 && !error && --maxcount > 0) {
endMemory = programMemory;
execute(thirdExpression, &programMemory, &result, &error);
if (!error)
execute(secondExpression, &programMemory, &result, &error);
}
memory1->swap(startMemory);
if (!error) {
memory2->swap(endMemory);
memoryAfter->swap(programMemory);
}
return true;
}
static void valueFlowForLoopSimplify(Token * const bodyStart, const nonneg int varid, bool globalvar, const MathLib::bigint value, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
const Token * const bodyEnd = bodyStart->link();
// Is variable modified inside for loop
if (isVariableChanged(bodyStart, bodyEnd, varid, globalvar, settings, tokenlist->isCPP()))
return;
for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) {
if (tok2->varId() == varid) {
const Token * parent = tok2->astParent();
while (parent) {
const Token * const p = parent;
parent = parent->astParent();
2014-09-03 12:17:34 +02:00
if (!parent || parent->str() == ":")
break;
2014-09-03 12:17:34 +02:00
if (parent->str() == "?") {
if (parent->astOperand2() != p)
parent = nullptr;
break;
}
}
if (parent) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on ?");
continue;
}
ValueFlow::Value value1(value);
value1.varId = tok2->varId();
2016-11-05 09:59:48 +01:00
setTokenValue(tok2, value1, settings);
}
if (Token::Match(tok2, "%oror%|&&")) {
const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value)));
if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory)) ||
(tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory))) {
// Skip second expression..
const Token *parent = tok2;
while (parent && parent->str() == tok2->str())
parent = parent->astParent();
// Jump to end of condition
if (parent && parent->str() == "(") {
tok2 = parent->link();
// cast
if (Token::simpleMatch(tok2, ") ("))
tok2 = tok2->linkAt(1);
}
}
}
if ((tok2->str() == "&&" && conditionIsFalse(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value)))) ||
(tok2->str() == "||" && conditionIsTrue(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value)))))
break;
else if (Token::simpleMatch(tok2, ") {") && Token::findmatch(tok2->link(), "%varid%", tok2, varid)) {
if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(1), varid)) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return");
break;
}
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable skipping conditional scope");
tok2 = tok2->next()->link();
if (Token::simpleMatch(tok2, "} else {")) {
if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(2), varid)) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return");
break;
}
tok2 = tok2->linkAt(2);
}
}
else if (Token::simpleMatch(tok2, ") {")) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "For loop skipping {} code");
tok2 = tok2->linkAt(1);
if (Token::simpleMatch(tok2, "} else {"))
tok2 = tok2->linkAt(2);
}
}
}
static void valueFlowForLoopSimplifyAfter(Token *fortok, nonneg int varid, const MathLib::bigint num, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
const Token *vartok = nullptr;
for (const Token *tok = fortok; tok; tok = tok->next()) {
if (tok->varId() == varid) {
vartok = tok;
break;
}
}
if (!vartok || !vartok->variable())
return;
const Variable *var = vartok->variable();
const Token *endToken = nullptr;
if (var->isLocal())
2020-01-11 09:17:32 +01:00
endToken = var->scope()->bodyEnd;
else
endToken = fortok->scope()->bodyEnd;
Token* blockTok = fortok->linkAt(1)->linkAt(1);
std::list<ValueFlow::Value> values;
values.emplace_back(num);
values.back().errorPath.emplace_back(fortok,"After for loop, " + var->name() + " has value " + values.back().infoString());
if (blockTok != endToken) {
valueFlowForwardVariable(
blockTok->next(), endToken, var, varid, values, false, false, tokenlist, errorLogger, settings);
}
}
static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
2018-07-14 22:26:22 +02:00
for (const Scope &scope : symboldatabase->scopeList) {
if (scope.type != Scope::eFor)
continue;
2018-07-14 22:26:22 +02:00
Token* tok = const_cast<Token*>(scope.classDef);
Token* const bodyStart = const_cast<Token*>(scope.bodyStart);
if (!Token::simpleMatch(tok->next()->astOperand2(), ";") ||
!Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";"))
continue;
nonneg int varid;
2020-06-29 21:53:14 +02:00
bool knownInitValue, partialCond;
MathLib::bigint initValue, stepValue, lastValue;
2020-06-29 21:53:14 +02:00
if (extractForLoopValues(tok, &varid, &knownInitValue, &initValue, &partialCond, &stepValue, &lastValue)) {
const bool executeBody = !knownInitValue || initValue <= lastValue;
if (executeBody) {
valueFlowForLoopSimplify(bodyStart, varid, false, initValue, tokenlist, errorLogger, settings);
if (stepValue == 1)
valueFlowForLoopSimplify(bodyStart, varid, false, lastValue, tokenlist, errorLogger, settings);
}
const MathLib::bigint afterValue = executeBody ? lastValue + stepValue : initValue;
valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist, errorLogger, settings);
} else {
ProgramMemory mem1, mem2, memAfter;
if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) {
ProgramMemory::Map::const_iterator it;
for (it = mem1.values.begin(); it != mem1.values.end(); ++it) {
if (!it->second.isIntValue())
continue;
valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings);
}
for (it = mem2.values.begin(); it != mem2.values.end(); ++it) {
if (!it->second.isIntValue())
continue;
valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings);
}
for (it = memAfter.values.begin(); it != memAfter.values.end(); ++it) {
if (!it->second.isIntValue())
continue;
valueFlowForLoopSimplifyAfter(tok, it->first, it->second.intvalue, tokenlist, errorLogger, settings);
}
}
}
}
}
2020-11-10 16:00:55 +01:00
struct MultiValueFlowAnalyzer : ValueFlowAnalyzer {
std::unordered_map<nonneg int, ValueFlow::Value> values;
std::unordered_map<nonneg int, const Variable*> vars;
SymbolDatabase* symboldatabase;
MultiValueFlowAnalyzer() : ValueFlowAnalyzer(), values(), vars(), symboldatabase(nullptr) {}
MultiValueFlowAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const TokenList* t, SymbolDatabase* s)
: ValueFlowAnalyzer(t), values(), vars(), symboldatabase(s) {
2020-03-01 20:39:00 +01:00
for (const auto& p:args) {
values[p.first->declarationId()] = p.second;
vars[p.first->declarationId()] = p.first;
}
}
virtual const std::unordered_map<nonneg int, const Variable*>& getVars() const {
return vars;
}
virtual const ValueFlow::Value* getValue(const Token* tok) const OVERRIDE {
if (tok->varId() == 0)
return nullptr;
auto it = values.find(tok->varId());
if (it == values.end())
return nullptr;
return &it->second;
}
virtual ValueFlow::Value* getValue(const Token* tok) OVERRIDE {
if (tok->varId() == 0)
return nullptr;
auto it = values.find(tok->varId());
if (it == values.end())
return nullptr;
return &it->second;
}
virtual void makeConditional() OVERRIDE {
2020-03-01 20:39:00 +01:00
for (auto&& p:values) {
p.second.conditional = true;
}
}
virtual void addErrorPath(const Token* tok, const std::string& s) OVERRIDE {
2020-03-01 20:39:00 +01:00
for (auto&& p:values) {
p.second.errorPath.emplace_back(tok, "Assuming condition is " + s);
}
}
virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE {
const auto range = SelectValueFromVarIdMapRange(&values);
for (const auto& p:getVars()) {
nonneg int varid = p.first;
const Variable* var = p.second;
if (tok->varId() == varid)
return true;
if (isAliasOf(var, tok, varid, range, &inconclusive))
return true;
}
return false;
}
virtual bool isGlobal() const OVERRIDE {
return false;
}
virtual bool lowerToPossible() OVERRIDE {
2020-03-01 20:39:00 +01:00
for (auto&& p:values) {
if (p.second.isImpossible())
return false;
p.second.changeKnownToPossible();
}
return true;
}
virtual bool lowerToInconclusive() OVERRIDE {
2020-03-01 20:39:00 +01:00
for (auto&& p:values) {
if (p.second.isImpossible())
return false;
p.second.setInconclusive();
}
return true;
}
virtual bool isConditional() const OVERRIDE {
2020-03-01 20:39:00 +01:00
for (auto&& p:values) {
if (p.second.conditional)
return true;
if (p.second.condition)
return !p.second.isImpossible();
}
return false;
}
virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE {
const Scope* scope = endBlock->scope();
if (!scope)
return false;
if (scope->type == Scope::eLambda) {
2020-03-01 20:39:00 +01:00
for (const auto& p:values) {
if (!p.second.isLifetimeValue())
return false;
}
return true;
} else if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile ||
scope->type == Scope::eFor) {
auto pred = [](const ValueFlow::Value& value) {
if (value.isKnown())
return true;
if (value.isImpossible())
return true;
if (value.isLifetimeValue())
return true;
return false;
};
if (std::all_of(values.begin(), values.end(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1))))
return true;
if (isConditional())
return false;
const Token* condTok = getCondTokFromEnd(endBlock);
std::set<nonneg int> varids;
std::transform(getVars().begin(), getVars().end(), std::inserter(varids, varids.begin()), SelectMapKeys{});
return bifurcate(condTok, varids, getSettings());
}
return false;
}
virtual bool match(const Token* tok) const OVERRIDE {
return values.count(tok->varId()) > 0;
}
virtual ProgramState getProgramState() const OVERRIDE {
ProgramState ps;
2020-03-01 20:39:00 +01:00
for (const auto& p:values)
ps[p.first] = p.second;
return ps;
}
virtual void forkScope(const Token* endBlock) OVERRIDE {
ProgramMemory pm = {getProgramState()};
const Scope* scope = endBlock->scope();
const Token* condTok = getCondTokFromEnd(endBlock);
if (scope && condTok)
programMemoryParseCondition(pm, condTok, nullptr, getSettings(), scope->type != Scope::eElse);
// ProgramMemory pm = pms.get(endBlock->link()->next(), getProgramState());
2021-01-12 21:28:56 +01:00
for (const auto& p:pm.values) {
nonneg int varid = p.first;
if (!symboldatabase->isVarId(varid))
continue;
ValueFlow::Value value = p.second;
if (vars.count(varid) != 0)
continue;
if (value.isImpossible())
continue;
value.setPossible();
values[varid] = value;
if (symboldatabase)
vars[varid] = symboldatabase->getVariableFromVarId(varid);
}
}
};
static void valueFlowInjectParameter(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings, const Scope* functionScope, const std::unordered_map<const Variable*, std::list<ValueFlow::Value>>& vars)
{
using Args = std::vector<std::unordered_map<const Variable*, ValueFlow::Value>>;
Args args(1);
// Compute cartesian product of all arguments
2020-03-01 20:39:00 +01:00
for (const auto& p:vars) {
if (p.second.empty())
continue;
args.back()[p.first] = p.second.front();
}
2020-03-01 20:39:00 +01:00
for (const auto& p:vars) {
if (args.size() > 256) {
std::string fname = "<unknown>";
Function* f = functionScope->function;
if (f)
fname = f->name();
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, functionScope->bodyStart, "Too many argument passed to " + fname);
break;
}
std::for_each(std::next(p.second.begin()), p.second.end(), [&](const ValueFlow::Value& value) {
Args new_args;
2020-03-01 20:39:00 +01:00
for (auto arg:args) {
if (value.path != 0) {
2020-04-04 10:31:38 +02:00
for (const auto& q:arg) {
if (q.second.path == 0)
continue;
if (q.second.path != value.path)
return;
}
}
arg[p.first] = value;
new_args.push_back(arg);
}
std::copy(new_args.begin(), new_args.end(), std::back_inserter(args));
});
}
2020-03-01 20:39:00 +01:00
for (const auto& arg:args) {
if (arg.empty())
continue;
bool skip = false;
// Make sure all arguments are the same path
MathLib::bigint path = arg.begin()->second.path;
2020-03-01 20:39:00 +01:00
for (const auto& p:arg) {
if (p.second.path != path) {
skip = true;
break;
}
}
if (skip)
continue;
MultiValueFlowAnalyzer a(arg, tokenlist, symboldatabase);
valueFlowGenericForward(const_cast<Token*>(functionScope->bodyStart), functionScope->bodyEnd, a, settings);
}
}
static void valueFlowInjectParameter(TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, const Variable* arg, const Scope* functionScope, const std::list<ValueFlow::Value>& argvalues)
{
// Is argument passed by value or const reference, and is it a known non-class type?
if (arg->isReference() && !arg->isConst() && !arg->isClass())
return;
// Set value in function scope..
const nonneg int varid2 = arg->declarationId();
if (!varid2)
return;
valueFlowForwardVariable(const_cast<Token*>(functionScope->bodyStart->next()),
functionScope->bodyEnd,
arg,
varid2,
argvalues,
false,
true,
tokenlist,
errorLogger,
settings);
}
static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
2018-07-14 22:26:22 +02:00
for (const Scope &scope : symboldatabase->scopeList) {
if (scope.type != Scope::ScopeType::eSwitch)
continue;
2018-07-14 22:26:22 +02:00
if (!Token::Match(scope.classDef, "switch ( %var% ) {"))
continue;
2018-07-14 22:26:22 +02:00
const Token *vartok = scope.classDef->tokAt(2);
2015-08-12 09:57:36 +02:00
const Variable *var = vartok->variable();
if (!var)
continue;
// bailout: global non-const variables
if (!(var->isLocal() || var->isArgument()) && !var->isConst()) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, vartok, "switch variable " + var->name() + " is global");
continue;
}
2018-07-14 22:26:22 +02:00
for (Token *tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) {
if (tok->str() == "{") {
tok = tok->link();
continue;
}
if (Token::Match(tok, "case %num% :")) {
std::list<ValueFlow::Value> values;
values.emplace_back(MathLib::toLongNumber(tok->next()->str()));
values.back().condition = tok;
const std::string info("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here.");
values.back().errorPath.emplace_back(tok, info);
bool known = false;
if ((Token::simpleMatch(tok->previous(), "{") || Token::simpleMatch(tok->tokAt(-2), "break ;")) && !Token::Match(tok->tokAt(3), ";| case"))
known = true;
while (Token::Match(tok->tokAt(3), ";| case %num% :")) {
2017-10-20 22:11:12 +02:00
known = false;
tok = tok->tokAt(3);
if (!tok->isName())
tok = tok->next();
values.emplace_back(MathLib::toLongNumber(tok->next()->str()));
values.back().condition = tok;
const std::string info2("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here.");
values.back().errorPath.emplace_back(tok, info2);
}
for (std::list<ValueFlow::Value>::const_iterator val = values.begin(); val != values.end(); ++val) {
valueFlowReverse(tokenlist,
2018-07-14 22:26:22 +02:00
const_cast<Token*>(scope.classDef),
vartok,
*val,
ValueFlow::Value(),
errorLogger,
settings);
}
if (vartok->variable()->scope()) {
2017-10-20 22:11:12 +02:00
if (known)
values.back().setKnown();
// FIXME We must check if there is a return. See #9276
/*
valueFlowForwardVariable(tok->tokAt(3),
vartok->variable()->scope()->bodyEnd,
vartok->variable(),
vartok->varId(),
values,
values.back().isKnown(),
false,
tokenlist,
errorLogger,
settings);
*/
2017-10-20 22:11:12 +02:00
}
}
}
}
}
2016-11-05 09:59:48 +01:00
static void setTokenValues(Token *tok, const std::list<ValueFlow::Value> &values, const Settings *settings)
{
for (const ValueFlow::Value &value : values) {
2016-11-13 22:33:39 +01:00
if (value.isIntValue())
2016-11-05 09:59:48 +01:00
setTokenValue(tok, value, settings);
}
}
static bool evaluate(const Token *expr, const std::vector<std::list<ValueFlow::Value>> &values, std::list<ValueFlow::Value> *result)
{
if (!expr)
return false;
// strlen(arg)..
if (expr->str() == "(" && Token::Match(expr->previous(), "strlen ( %name% )")) {
const Token *arg = expr->next();
if (arg->str().compare(0,3,"arg") != 0 || arg->str().size() != 4)
return false;
const char n = arg->str()[3];
if (n < '1' || n - '1' >= values.size())
return false;
for (const ValueFlow::Value &argvalue : values[n - '1']) {
if (argvalue.isTokValue() && argvalue.tokvalue->tokType() == Token::eString) {
ValueFlow::Value res(argvalue); // copy all "inconclusive", "condition", etc attributes
// set return value..
res.valueType = ValueFlow::Value::ValueType::INT;
res.tokvalue = nullptr;
res.intvalue = Token::getStrLength(argvalue.tokvalue);
result->emplace_back(std::move(res));
}
}
return !result->empty();
}
// unary operands
if (expr->astOperand1() && !expr->astOperand2()) {
std::list<ValueFlow::Value> opvalues;
if (!evaluate(expr->astOperand1(), values, &opvalues))
return false;
if (expr->str() == "+") {
result->swap(opvalues);
return true;
}
if (expr->str() == "-") {
for (ValueFlow::Value v: opvalues) {
if (v.isIntValue()) {
v.intvalue = -v.intvalue;
result->emplace_back(std::move(v));
}
}
return true;
}
return false;
}
// binary/ternary operands
if (expr->astOperand1() && expr->astOperand2()) {
std::list<ValueFlow::Value> lhsValues, rhsValues;
if (!evaluate(expr->astOperand1(), values, &lhsValues))
return false;
if (expr->str() != "?" && !evaluate(expr->astOperand2(), values, &rhsValues))
return false;
for (const ValueFlow::Value &val1 : lhsValues) {
if (!val1.isIntValue())
continue;
if (expr->str() == "?") {
rhsValues.clear();
const Token *expr2 = val1.intvalue ? expr->astOperand2()->astOperand1() : expr->astOperand2()->astOperand2();
if (!evaluate(expr2, values, &rhsValues))
continue;
result->insert(result->end(), rhsValues.begin(), rhsValues.end());
continue;
}
for (const ValueFlow::Value &val2 : rhsValues) {
if (!val2.isIntValue())
continue;
if (val1.varId != 0 && val2.varId != 0) {
if (val1.varId != val2.varId || val1.varvalue != val2.varvalue)
continue;
}
if (expr->str() == "+")
result->emplace_back(ValueFlow::Value(val1.intvalue + val2.intvalue));
else if (expr->str() == "-")
result->emplace_back(ValueFlow::Value(val1.intvalue - val2.intvalue));
else if (expr->str() == "*")
result->emplace_back(ValueFlow::Value(val1.intvalue * val2.intvalue));
else if (expr->str() == "/" && val2.intvalue != 0)
result->emplace_back(ValueFlow::Value(val1.intvalue / val2.intvalue));
else if (expr->str() == "%" && val2.intvalue != 0)
result->emplace_back(ValueFlow::Value(val1.intvalue % val2.intvalue));
else if (expr->str() == "&")
result->emplace_back(ValueFlow::Value(val1.intvalue & val2.intvalue));
else if (expr->str() == "|")
result->emplace_back(ValueFlow::Value(val1.intvalue | val2.intvalue));
else if (expr->str() == "^")
result->emplace_back(ValueFlow::Value(val1.intvalue ^ val2.intvalue));
else if (expr->str() == "==")
result->emplace_back(ValueFlow::Value(val1.intvalue == val2.intvalue));
else if (expr->str() == "!=")
result->emplace_back(ValueFlow::Value(val1.intvalue != val2.intvalue));
else if (expr->str() == "<")
result->emplace_back(ValueFlow::Value(val1.intvalue < val2.intvalue));
else if (expr->str() == ">")
result->emplace_back(ValueFlow::Value(val1.intvalue > val2.intvalue));
else if (expr->str() == ">=")
result->emplace_back(ValueFlow::Value(val1.intvalue >= val2.intvalue));
else if (expr->str() == "<=")
result->emplace_back(ValueFlow::Value(val1.intvalue <= val2.intvalue));
else if (expr->str() == "&&")
result->emplace_back(ValueFlow::Value(val1.intvalue && val2.intvalue));
else if (expr->str() == "||")
result->emplace_back(ValueFlow::Value(val1.intvalue || val2.intvalue));
else if (expr->str() == "<<")
result->emplace_back(ValueFlow::Value(val1.intvalue << val2.intvalue));
else if (expr->str() == ">>")
result->emplace_back(ValueFlow::Value(val1.intvalue >> val2.intvalue));
else
return false;
combineValueProperties(val1, val2, &result->back());
}
}
return !result->empty();
}
if (expr->str().compare(0,3,"arg")==0) {
*result = values[expr->str()[3] - '1'];
return true;
}
if (expr->isNumber()) {
result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str())));
result->back().setKnown();
return true;
} else if (expr->tokType() == Token::eChar) {
result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str())));
result->back().setKnown();
return true;
}
return false;
}
static std::list<ValueFlow::Value> getFunctionArgumentValues(const Token *argtok)
{
std::list<ValueFlow::Value> argvalues(argtok->values());
removeImpossible(argvalues);
if (argvalues.empty() && Token::Match(argtok, "%comp%|%oror%|&&|!")) {
argvalues.emplace_back(0);
argvalues.emplace_back(1);
}
return argvalues;
}
static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings)
{
std::vector<std::list<ValueFlow::Value>> argValues;
for (const Token *argtok : getArguments(tok->previous())) {
argValues.emplace_back(getFunctionArgumentValues(argtok));
if (argValues.back().empty())
return;
}
if (returnValue.find("arg") != std::string::npos && argValues.empty())
return;
TokenList tokenList(settings);
{
const std::string code = "return " + returnValue + ";";
std::istringstream istr(code);
if (!tokenList.createTokens(istr))
return;
}
// combine operators, set links, etc..
std::stack<Token *> lpar;
for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
if (Token::Match(tok2, "[!<>=] =")) {
tok2->str(tok2->str() + "=");
tok2->deleteNext();
} else if (tok2->str() == "(")
lpar.push(tok2);
else if (tok2->str() == ")") {
if (lpar.empty())
return;
Token::createMutualLinks(lpar.top(), tok2);
lpar.pop();
}
}
if (!lpar.empty())
return;
// Evaluate expression
tokenList.createAst();
std::list<ValueFlow::Value> results;
if (evaluate(tokenList.front()->astOperand1(), argValues, &results))
setTokenValues(tok, results, settings);
}
static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings)
{
for (const Scope* scope : symboldatabase->functionScopes) {
const Function* function = scope->function;
if (!function)
continue;
int id = 0;
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
if (!Token::Match(tok, "%name% ("))
continue;
const Function * const calledFunction = tok->function();
if (!calledFunction) {
// library function?
const std::string& returnValue(settings->library.returnValue(tok));
if (!returnValue.empty())
valueFlowLibraryFunction(tok->next(), returnValue, settings);
continue;
}
const Scope * const calledFunctionScope = calledFunction->functionScope;
if (!calledFunctionScope)
continue;
id++;
std::unordered_map<const Variable*, std::list<ValueFlow::Value>> argvars;
// TODO: Rewrite this. It does not work well to inject 1 argument at a time.
const std::vector<const Token *> &callArguments = getArguments(tok);
for (int argnr = 0U; argnr < callArguments.size(); ++argnr) {
const Token *argtok = callArguments[argnr];
// Get function argument
const Variable * const argvar = calledFunction->getArgumentVar(argnr);
if (!argvar)
break;
2014-08-06 06:33:06 +02:00
// passing value(s) to function
std::list<ValueFlow::Value> argvalues(getFunctionArgumentValues(argtok));
// Remove non-local lifetimes
argvalues.remove_if([](const ValueFlow::Value& v) {
if (v.isLifetimeValue())
return !v.isLocalLifetimeValue() && !v.isSubFunctionLifetimeValue();
return false;
});
// Don't forward container sizes for now since programmemory can't evaluate conditions
argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue));
if (argvalues.empty())
continue;
2017-05-19 14:34:59 +02:00
// Error path..
for (ValueFlow::Value &v : argvalues) {
const std::string nr = MathLib::toString(argnr + 1) + getOrdinalText(argnr + 1);
v.errorPath.emplace_back(argtok,
"Calling function '" +
calledFunction->name() +
"', " +
nr +
" argument '" +
argtok->expressionString() +
"' value is " +
v.infoString());
v.path = 256 * v.path + id;
2020-09-04 18:56:34 +02:00
// Change scope of lifetime values
if (v.isLifetimeValue())
v.lifetimeScope = ValueFlow::Value::LifetimeScope::SubFunction;
}
// passed values are not "known"..
lowerToPossible(argvalues);
argvars[argvar] = argvalues;
}
valueFlowInjectParameter(tokenlist, symboldatabase, errorLogger, settings, calledFunctionScope, argvars);
}
}
}
2014-08-06 06:33:06 +02:00
static void valueFlowFunctionDefaultParameter(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
if (!tokenlist->isCPP())
return;
2014-08-06 06:33:06 +02:00
2018-07-14 22:26:22 +02:00
for (const Scope* scope : symboldatabase->functionScopes) {
const Function* function = scope->function;
if (!function)
continue;
for (std::size_t arg = function->minArgCount(); arg < function->argCount(); arg++) {
const Variable* var = function->getArgumentVar(arg);
if (var && var->hasDefault() && Token::Match(var->nameToken(), "%var% = %num%|%str% [,)]")) {
const std::list<ValueFlow::Value> &values = var->nameToken()->tokAt(2)->values();
std::list<ValueFlow::Value> argvalues;
2018-07-14 22:26:22 +02:00
for (const ValueFlow::Value &value : values) {
ValueFlow::Value v(value);
v.defaultArg = true;
v.changeKnownToPossible();
if (v.isPossible())
argvalues.push_back(v);
}
if (!argvalues.empty())
valueFlowInjectParameter(tokenlist, errorLogger, settings, var, scope, argvalues);
}
}
}
}
static bool isKnown(const Token * tok)
{
return tok && tok->hasKnownIntValue();
}
2016-11-05 09:59:48 +01:00
static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogger)
{
for (Token *tok = tokenlist->back(); tok; tok = tok->previous()) {
if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand1()->function())
continue;
2018-11-03 15:53:24 +01:00
if (tok->hasKnownValue())
continue;
// Arguments..
std::vector<MathLib::bigint> parvalues;
if (tok->astOperand2()) {
const Token *partok = tok->astOperand2();
while (partok && partok->str() == "," && isKnown(partok->astOperand2()))
partok = partok->astOperand1();
if (!isKnown(partok))
continue;
parvalues.push_back(partok->values().front().intvalue);
partok = partok->astParent();
while (partok && partok->str() == ",") {
parvalues.push_back(partok->astOperand2()->values().front().intvalue);
partok = partok->astParent();
}
if (partok != tok)
continue;
}
// Get scope and args of function
const Function * const function = tok->astOperand1()->function();
const Scope * const functionScope = function->functionScope;
if (!functionScope || !Token::simpleMatch(functionScope->bodyStart, "{ return")) {
if (functionScope && tokenlist->getSettings()->debugwarnings && Token::findsimplematch(functionScope->bodyStart, "return", functionScope->bodyEnd))
bailout(tokenlist, errorLogger, tok, "function return; nontrivial function body");
continue;
}
ProgramMemory programMemory;
for (std::size_t i = 0; i < parvalues.size(); ++i) {
const Variable * const arg = function->getArgumentVar(i);
if (!arg || !Token::Match(arg->typeStartToken(), "%type% %name% ,|)")) {
2016-11-05 09:59:48 +01:00
if (tokenlist->getSettings()->debugwarnings)
bailout(tokenlist, errorLogger, tok, "function return; unhandled argument type");
programMemory.clear();
break;
}
programMemory.setIntValue(arg->declarationId(), parvalues[i]);
}
if (programMemory.empty() && !parvalues.empty())
continue;
// Determine return value of subfunction..
MathLib::bigint result = 0;
bool error = false;
execute(functionScope->bodyStart->next()->astOperand1(),
&programMemory,
&result,
&error);
if (!error) {
ValueFlow::Value v(result);
if (function->hasVirtualSpecifier())
v.setPossible();
else
v.setKnown();
2016-11-05 09:59:48 +01:00
setTokenValue(tok, v, tokenlist->getSettings());
}
}
}
static void valueFlowUninit(TokenList *tokenlist, SymbolDatabase * /*symbolDatabase*/, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!Token::Match(tok,"[;{}] %type%"))
continue;
if (!tok->scope()->isExecutable())
continue;
const Token *vardecl = tok->next();
bool stdtype = false;
bool pointer = false;
while (Token::Match(vardecl, "%name%|::|*") && vardecl->varId() == 0) {
stdtype |= vardecl->isStandardType();
pointer |= vardecl->str() == "*";
vardecl = vardecl->next();
}
// if (!stdtype && !pointer)
2019-08-15 10:46:16 +02:00
// continue;
if (!Token::Match(vardecl, "%var% ;"))
continue;
const Variable *var = vardecl->variable();
if (!var || var->nameToken() != vardecl || var->isInit())
continue;
if ((!var->isPointer() && var->type() && var->type()->needInitialization != Type::NeedInitialization::True) ||
2017-04-28 13:29:09 +02:00
!var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow())
continue;
if (!var->type() && !stdtype && !pointer)
continue;
ValueFlow::Value uninitValue;
uninitValue.setKnown();
uninitValue.valueType = ValueFlow::Value::ValueType::UNINIT;
uninitValue.tokvalue = vardecl;
std::list<ValueFlow::Value> values;
values.push_back(uninitValue);
const bool constValue = true;
const bool subFunction = false;
valueFlowForwardVariable(vardecl->next(),
vardecl->scope()->bodyEnd,
var,
vardecl->varId(),
values,
constValue,
subFunction,
tokenlist,
errorLogger,
settings);
}
}
static bool isContainerSize(const Token* tok)
{
if (!Token::Match(tok, "%var% . %name% ("))
return false;
if (!astIsContainer(tok))
return false;
if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE)
return true;
if (Token::Match(tok->tokAt(2), "size|length ( )"))
return true;
return false;
}
static bool isContainerEmpty(const Token* tok)
{
if (!Token::Match(tok, "%var% . %name% ("))
return false;
if (!astIsContainer(tok))
return false;
if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY)
return true;
if (Token::simpleMatch(tok->tokAt(2), "empty ( )"))
return true;
return false;
}
static bool isContainerSizeChanged(const Token *tok, int depth=20);
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20);
2018-11-02 20:10:40 +01:00
static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20)
{
if (!tok->valueType() || !tok->valueType()->container)
return false;
// If we are accessing an element then we are not changing the container size
if (Token::Match(tok, "%name% . %name% (")) {
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2));
if (yield != Library::Container::Yield::NO_YIELD)
return false;
}
if (Token::simpleMatch(tok->astParent(), "["))
return false;
// address of variable
const bool addressOf = tok->valueType()->pointer || (tok->astParent() && tok->astParent()->isUnaryOp("&"));
int narg;
const Token * ftok = getTokenArgumentFunction(tok, narg);
if (!ftok)
return false; // not a function => variable not changed
const Function * fun = ftok->function();
if (fun && !fun->hasVirtualSpecifier()) {
const Variable *arg = fun->getArgumentVar(narg);
if (arg) {
if (!arg->isReference() && !addressOf)
return false;
if (!addressOf && arg->isConst())
return false;
if (arg->valueType() && arg->valueType()->constness == 1)
return false;
const Scope * scope = fun->functionScope;
if (scope) {
// Argument not used
if (!arg->nameToken())
return false;
if (depth > 0)
return isContainerSizeChanged(arg->declarationId(), scope->bodyStart, scope->bodyEnd, depth - 1);
}
// Don't know => Safe guess
return true;
}
}
bool inconclusive = false;
const bool isChanged = isVariableChangedByFunctionCall(tok, 0, nullptr, &inconclusive);
return (isChanged || inconclusive);
}
2020-11-10 16:00:55 +01:00
struct ContainerVariableAnalyzer : VariableAnalyzer {
ContainerVariableAnalyzer() : VariableAnalyzer() {}
2020-11-10 16:00:55 +01:00
ContainerVariableAnalyzer(const Variable* v,
const ValueFlow::Value& val,
std::vector<const Variable*> paliases,
const TokenList* t)
: VariableAnalyzer(v, val, std::move(paliases), t)
{}
virtual bool match(const Token* tok) const OVERRIDE {
return tok->varId() == var->declarationId() || (astIsIterator(tok) && isAliasOf(tok, var->declarationId()));
}
2020-11-11 09:15:36 +01:00
virtual Action isWritable(const Token* tok, Direction d) const OVERRIDE {
if (astIsIterator(tok))
return Action::None;
2020-11-10 16:00:55 +01:00
if (d == Direction::Reverse)
return Action::None;
if (!getValue(tok))
return Action::None;
if (!tok->valueType() || !tok->valueType()->container)
return Action::None;
const Token* parent = tok->astParent();
if (tok->valueType()->container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) {
const Token* rhs = parent->astOperand2();
if (rhs->tokType() == Token::eString)
return Action::Read | Action::Write | Action::Incremental;
if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) {
2020-08-23 17:17:33 +02:00
if (std::any_of(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value &rhsval) {
return rhsval.isKnown() && rhsval.isContainerSizeValue();
}))
return Action::Read | Action::Write | Action::Incremental;
}
2020-08-10 05:52:03 +02:00
} else if (Token::Match(tok, "%name% . %name% (")) {
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) {
std::vector<const Token*> args = getArguments(tok->tokAt(3));
if (args.size() < 2)
return Action::Read | Action::Write | Action::Incremental;
}
}
return Action::None;
}
virtual void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const OVERRIDE {
2020-11-10 16:00:55 +01:00
if (d == Direction::Reverse)
return;
if (!val)
return;
if (!tok->astParent())
return;
const Token* parent = tok->astParent();
if (!tok->valueType() || !tok->valueType()->container)
return;
if (tok->valueType()->container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) {
const Token* rhs = parent->astOperand2();
if (rhs->tokType() == Token::eString)
val->intvalue += Token::getStrLength(rhs);
else if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) {
for (const ValueFlow::Value &rhsval : rhs->values()) {
if (rhsval.isKnown() && rhsval.isContainerSizeValue()) {
val->intvalue += rhsval.intvalue;
}
}
}
2020-08-10 05:52:03 +02:00
} else if (Token::Match(tok, "%name% . %name% (")) {
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
if (action == Library::Container::Action::PUSH)
val->intvalue++;
2020-08-10 05:52:03 +02:00
if (action == Library::Container::Action::POP)
val->intvalue--;
}
}
virtual Action isModified(const Token* tok) const OVERRIDE {
Action read = Action::Read;
// An iterator won't change the container size
if (astIsIterator(tok))
return read;
if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok))
return Action::Invalid;
if (isLikelyStreamRead(isCPP(), tok->astParent()))
return Action::Invalid;
if (astIsContainer(tok) && isContainerSizeChanged(tok))
return Action::Invalid;
return read;
}
};
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowContainerForward(Token* tok,
2020-11-11 09:15:36 +01:00
const Token* endToken,
const Variable* var,
ValueFlow::Value value,
TokenList* tokenlist)
{
2020-11-10 16:00:55 +01:00
ContainerVariableAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
}
2020-11-10 16:00:55 +01:00
static Analyzer::Action valueFlowContainerForward(Token* tok,
2020-11-11 09:15:36 +01:00
const Variable* var,
ValueFlow::Value value,
TokenList* tokenlist)
{
const Token * endOfVarScope = nullptr;
if (var->isLocal() || var->isArgument())
endOfVarScope = var->scope()->bodyEnd;
if (!endOfVarScope)
endOfVarScope = tok->scope()->bodyEnd;
return valueFlowContainerForward(tok, endOfVarScope, var, std::move(value), tokenlist);
}
static void valueFlowContainerReverse(Token* tok,
const Token* const endToken,
const Token* const varToken,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
const Settings* settings)
{
const Variable* var = varToken->variable();
auto aliases = getAliasesFromValues(values);
for (const ValueFlow::Value& value : values) {
ContainerVariableAnalyzer a(var, value, aliases, tokenlist);
valueFlowGenericReverse(tok, endToken, a, settings);
}
}
static void valueFlowContainerReverse(Token* tok,
const Token* const varToken,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
const Settings* settings)
{
valueFlowContainerReverse(tok, nullptr, varToken, values, tokenlist, settings);
}
static bool isContainerSizeChanged(const Token *tok, int depth)
{
if (!tok)
return false;
if (!tok->valueType() || !tok->valueType()->container)
return true;
if (Token::Match(tok, "%name% %assign%|<<"))
return true;
if (Token::Match(tok, "%var% [") && tok->valueType()->container->stdAssociativeLike)
return true;
if (Token::Match(tok, "%name% . %name% (")) {
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2));
switch (action) {
case Library::Container::Action::RESIZE:
case Library::Container::Action::CLEAR:
case Library::Container::Action::PUSH:
case Library::Container::Action::POP:
case Library::Container::Action::CHANGE:
case Library::Container::Action::INSERT:
case Library::Container::Action::ERASE:
case Library::Container::Action::CHANGE_INTERNAL:
return true;
case Library::Container::Action::NO_ACTION: // might be unknown action
return yield == Library::Container::Yield::NO_YIELD;
case Library::Container::Action::FIND:
case Library::Container::Action::CHANGE_CONTENT:
break;
}
}
if (isContainerSizeChangedByFunction(tok, depth))
return true;
return false;
}
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
{
for (const Token *tok = start; tok != end; tok = tok->next()) {
if (tok->varId() != varId)
continue;
if (isContainerSizeChanged(tok, depth))
return true;
}
return false;
}
static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->scope())
continue;
if (!tok->scope()->isExecutable())
continue;
if (tok->variable()) {
2020-09-09 04:55:53 +02:00
const Variable* var = tok->variable();
if (!var->isSmartPointer())
continue;
if (var->nameToken() == tok) {
2020-09-09 04:55:53 +02:00
if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() &&
tok->next()->astOperand2()->str() != ",") {
Token* inTok = tok->next()->astOperand2();
std::list<ValueFlow::Value> values = inTok->values();
const bool constValue = inTok->isNumber();
valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings);
} else if (Token::Match(tok, "%var% ;")) {
std::list<ValueFlow::Value> values;
ValueFlow::Value v(0);
v.setKnown();
values.push_back(v);
valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings);
}
} else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") {
if (Token::simpleMatch(tok->tokAt(3), "( )")) {
std::list<ValueFlow::Value> values;
ValueFlow::Value v(0);
v.setKnown();
values.push_back(v);
valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings);
} else {
tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue));
2020-09-09 04:55:53 +02:00
Token* inTok = tok->tokAt(3)->astOperand2();
if (!inTok)
continue;
std::list<ValueFlow::Value> values = inTok->values();
const bool constValue = inTok->isNumber();
valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings);
}
} else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") {
std::list<ValueFlow::Value> values;
ValueFlow::Value v(0);
v.setKnown();
values.push_back(v);
valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings);
}
2020-09-09 04:55:53 +02:00
} else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) &&
astIsSmartPointer(tok->astOperand1())) {
std::vector<const Token*> args = getArguments(tok);
if (args.empty())
continue;
2020-09-09 04:55:53 +02:00
for (const ValueFlow::Value& v : args.front()->values())
setTokenValue(tok, v, settings);
}
2019-04-26 11:30:35 +02:00
}
}
2020-08-21 17:23:55 +02:00
static void valueFlowIterators(TokenList *tokenlist, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->scope())
continue;
if (!tok->scope()->isExecutable())
continue;
if (!astIsContainer(tok))
continue;
if (Token::Match(tok->astParent(), ". %name% (")) {
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->astParent()->strAt(1));
ValueFlow::Value v(0);
v.setKnown();
if (yield == Library::Container::Yield::START_ITERATOR) {
v.valueType = ValueFlow::Value::ValueType::ITERATOR_START;
setTokenValue(tok->astParent()->tokAt(2), v, settings);
} else if (yield == Library::Container::Yield::END_ITERATOR) {
v.valueType = ValueFlow::Value::ValueType::ITERATOR_END;
setTokenValue(tok->astParent()->tokAt(2), v, settings);
}
}
}
}
static std::list<ValueFlow::Value> getIteratorValues(std::list<ValueFlow::Value> values, ValueFlow::Value::ValueKind* kind = nullptr)
{
values.remove_if([&](const ValueFlow::Value& v) {
if (kind && v.valueKind != *kind)
return true;
return !v.isIteratorValue();
});
return values;
}
struct IteratorConditionHandler : SimpleConditionHandler {
2020-12-25 08:58:12 +01:00
virtual Condition parse(const Token* tok, const Settings*) const OVERRIDE {
Condition cond;
ValueFlow::Value true_value;
ValueFlow::Value false_value;
if (Token::Match(tok, "==|!=")) {
if (!tok->astOperand1() || !tok->astOperand2())
return cond;
ValueFlow::Value::ValueKind kind = ValueFlow::Value::ValueKind::Known;
std::list<ValueFlow::Value> values = getIteratorValues(tok->astOperand1()->values(), &kind);
if (!values.empty()) {
cond.vartok = tok->astOperand2();
} else {
values = getIteratorValues(tok->astOperand2()->values());
if (!values.empty())
cond.vartok = tok->astOperand1();
}
2020-08-28 19:29:33 +02:00
for (ValueFlow::Value& v:values) {
v.setPossible();
v.assumeCondition(tok);
}
cond.true_values = values;
cond.false_values = values;
}
return cond;
}
};
static void valueFlowIteratorInfer(TokenList *tokenlist, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->scope())
continue;
if (!tok->scope()->isExecutable())
continue;
std::list<ValueFlow::Value> values = getIteratorValues(tok->values());
values.remove_if([&](const ValueFlow::Value& v) {
if (!v.isImpossible())
return true;
if (v.isIteratorEndValue() && v.intvalue <= 0)
return true;
if (v.isIteratorStartValue() && v.intvalue >= 0)
return true;
return false;
});
2020-09-01 20:00:04 +02:00
for (ValueFlow::Value& v:values) {
v.setPossible();
if (v.isIteratorStartValue())
v.intvalue++;
if (v.isIteratorEndValue())
v.intvalue--;
setTokenValue(tok, v, settings);
}
}
}
2020-12-19 12:23:19 +01:00
static std::vector<ValueFlow::Value> getInitListSize(const Token* tok, const Library::Container *container)
{
std::vector<const Token*> args = getArguments(tok);
2020-12-19 12:23:19 +01:00
if ((args.size() == 1 && astIsContainer(args[0]) && args[0]->valueType()->container == container) ||
(args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1]))) {
std::vector<ValueFlow::Value> values;
std::copy_if(args[0]->values().begin(),
args[0]->values().end(),
std::back_inserter(values),
std::mem_fn(&ValueFlow::Value::isContainerSizeValue));
return values;
}
ValueFlow::Value value(args.size());
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
return {value};
}
static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger * /*errorLogger*/, const Settings *settings)
{
std::map<int, std::size_t> static_sizes;
// declaration
for (const Variable *var : symboldatabase->variableList()) {
if (!var || !var->isLocal() || var->isPointer() || var->isReference() || var->isStatic())
continue;
if (!var->valueType() || !var->valueType()->container)
continue;
if (!astIsContainer(var->nameToken()))
continue;
2018-11-03 15:53:24 +01:00
if (var->nameToken()->hasKnownValue())
continue;
if (!Token::Match(var->nameToken(), "%name% ;") &&
!(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;")))
continue;
if (var->valueType()->container->size_templateArgNo >= 0) {
if (var->dimensions().size() == 1 && var->dimensions().front().known)
static_sizes[var->declarationId()] = var->dimensions().front().num;
continue;
}
std::vector<ValueFlow::Value> values{ValueFlow::Value{0}};
values.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
values.back().setKnown();
if (Token::simpleMatch(var->nameToken()->next(), "{")) {
const Token* initList = var->nameToken()->next();
2020-12-19 12:23:19 +01:00
values = getInitListSize(initList, var->valueType()->container);
}
for (const ValueFlow::Value& value : values)
valueFlowContainerForward(var->nameToken()->next(), var, value, tokenlist);
}
// after assignment
for (const Scope *functionScope : symboldatabase->functionScopes) {
for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
if (static_sizes.count(tok->varId()) > 0) {
ValueFlow::Value value(static_sizes.at(tok->varId()));
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
setTokenValue(const_cast<Token*>(tok), value, settings);
} else if (Token::Match(tok, "%name%|;|{|} %var% = %str% ;")) {
const Token *containerTok = tok->next();
if (containerTok && containerTok->valueType() && containerTok->valueType()->container && containerTok->valueType()->container->stdStringLike) {
ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2)));
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
valueFlowContainerForward(containerTok->next(), containerTok->variable(), value, tokenlist);
}
} else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) {
const Token* containerTok = tok->next();
if (astIsContainer(containerTok) && containerTok->valueType()->container->size_templateArgNo < 0) {
2020-12-19 12:23:19 +01:00
std::vector<ValueFlow::Value> values = getInitListSize(tok->tokAt(3), containerTok->valueType()->container);
for (const ValueFlow::Value& value : values)
valueFlowContainerForward(containerTok->next(), containerTok->variable(), value, tokenlist);
}
2020-08-10 05:52:03 +02:00
} else if (Token::Match(tok, "%var% . %name% (") && tok->valueType() && tok->valueType()->container) {
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
if (action == Library::Container::Action::CLEAR) {
ValueFlow::Value value(0);
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
valueFlowContainerForward(tok->next(), tok->variable(), value, tokenlist);
} else if (action == Library::Container::Action::RESIZE && tok->tokAt(4)->hasKnownIntValue()) {
ValueFlow::Value value(tok->tokAt(4)->values().front());
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
valueFlowContainerForward(tok->next(), tok->variable(), value, tokenlist);
}
}
}
}
// conditional conditionSize
for (const Scope &scope : symboldatabase->scopeList) {
if (scope.type != Scope::ScopeType::eIf) // TODO: while
continue;
for (const Token *tok = scope.classDef; tok && tok->str() != "{"; tok = tok->next()) {
if (!tok->isName() || !tok->valueType() || tok->valueType()->type != ValueType::CONTAINER || !tok->valueType()->container)
continue;
const Token *conditionToken;
MathLib::bigint intval;
if (Token::Match(tok, "%name% . %name% (")) {
if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) {
const Token *parent = tok->tokAt(3)->astParent();
if (!parent || !parent->isComparisonOp() || !parent->astOperand2())
continue;
if (parent->astOperand1()->hasKnownIntValue())
intval = parent->astOperand1()->values().front().intvalue;
else if (parent->astOperand2()->hasKnownIntValue())
intval = parent->astOperand2()->values().front().intvalue;
else
continue;
conditionToken = parent;
} else if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) {
conditionToken = tok->tokAt(3);
intval = 0;
} else {
continue;
}
} else if (tok->valueType()->container->stdStringLike && Token::Match(tok, "%name% ==|!= %str%") && tok->next()->astOperand2() == tok->tokAt(2)) {
intval = Token::getStrLength(tok->tokAt(2));
conditionToken = tok->next();
} else {
continue;
}
ValueFlow::Value value(conditionToken, intval);
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
// possible value before condition
valueFlowContainerReverse(const_cast<Token*>(scope.classDef), tok, {value}, tokenlist, settings);
}
}
}
struct ContainerConditionHandler : ConditionHandler {
virtual bool forward(Token* start,
const Token* stop,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
2020-12-25 08:58:12 +01:00
const Settings*) const OVERRIDE {
2019-09-26 10:32:49 +02:00
// TODO: Forward multiple values
if (values.empty())
return false;
const Variable* var = exprTok->variable();
2019-09-26 10:32:49 +02:00
if (!var)
return false;
return valueFlowContainerForward(start->next(), stop, var, values.front(), tokenlist).isModified();
}
virtual void reverse(Token* start,
const Token* endTok,
const Token* exprTok,
const std::list<ValueFlow::Value>& values,
TokenList* tokenlist,
2021-01-25 22:51:50 +01:00
const Settings* settings) const OVERRIDE {
if (values.empty())
return;
if (!exprTok->variable())
return;
return valueFlowContainerReverse(start, endTok, exprTok, values, tokenlist, settings);
}
2020-12-25 08:58:12 +01:00
virtual Condition parse(const Token* tok, const Settings*) const OVERRIDE {
Condition cond;
ValueFlow::Value true_value;
ValueFlow::Value false_value;
const Token *vartok = parseCompareInt(tok, true_value, false_value);
if (vartok) {
vartok = vartok->tokAt(-3);
if (!isContainerSize(vartok))
return cond;
true_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
false_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
cond.true_values.push_back(true_value);
cond.false_values.push_back(false_value);
cond.vartok = vartok;
return cond;
}
// Empty check
if (tok->str() == "(") {
vartok = tok->tokAt(-3);
// TODO: Handle .size()
if (!isContainerEmpty(vartok))
return cond;
const Token *parent = tok->astParent();
while (parent) {
if (Token::Match(parent, "%comp%"))
return cond;
parent = parent->astParent();
}
ValueFlow::Value value(tok, 0LL);
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
cond.true_values.emplace_back(value);
cond.false_values.emplace_back(std::move(value));
cond.vartok = vartok;
cond.inverted = true;
return cond;
}
// String compare
if (Token::Match(tok, "==|!=")) {
const Token *strtok = nullptr;
if (Token::Match(tok->astOperand1(), "%str%")) {
strtok = tok->astOperand1();
vartok = tok->astOperand2();
} else if (Token::Match(tok->astOperand2(), "%str%")) {
strtok = tok->astOperand2();
vartok = tok->astOperand1();
}
if (!strtok)
return cond;
if (!astIsContainer(vartok))
return cond;
ValueFlow::Value value(tok, Token::getStrLength(strtok));
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
cond.false_values.emplace_back(value);
cond.true_values.emplace_back(std::move(value));
cond.vartok = vartok;
return cond;
}
return cond;
}
};
static void valueFlowDynamicBufferSize(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
for (const Scope *functionScope : symboldatabase->functionScopes) {
for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
if (!Token::Match(tok, "[;{}] %var% ="))
continue;
if (!tok->next()->variable())
continue;
const Token *rhs = tok->tokAt(2)->astOperand2();
while (rhs && rhs->isCast())
rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1();
if (!rhs)
continue;
if (!Token::Match(rhs->previous(), "%name% ("))
continue;
const Library::AllocFunc *allocFunc = settings->library.getAllocFuncInfo(rhs->previous());
if (!allocFunc)
allocFunc = settings->library.getReallocFuncInfo(rhs->previous());
if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none)
continue;
const std::vector<const Token *> args = getArguments(rhs->previous());
2019-03-20 19:26:57 +01:00
const Token * const arg1 = (args.size() >= allocFunc->bufferSizeArg1) ? args[allocFunc->bufferSizeArg1 - 1] : nullptr;
const Token * const arg2 = (args.size() >= allocFunc->bufferSizeArg2) ? args[allocFunc->bufferSizeArg2 - 1] : nullptr;
MathLib::bigint sizeValue = -1;
switch (allocFunc->bufferSize) {
case Library::AllocFunc::BufferSize::none:
break;
case Library::AllocFunc::BufferSize::malloc:
2019-03-20 19:26:57 +01:00
if (arg1 && arg1->hasKnownIntValue())
sizeValue = arg1->getKnownIntValue();
break;
case Library::AllocFunc::BufferSize::calloc:
2019-03-20 19:26:57 +01:00
if (arg1 && arg2 && arg1->hasKnownIntValue() && arg2->hasKnownIntValue())
sizeValue = arg1->getKnownIntValue() * arg2->getKnownIntValue();
break;
case Library::AllocFunc::BufferSize::strdup:
2019-03-20 19:26:57 +01:00
if (arg1 && arg1->hasKnownValue()) {
const ValueFlow::Value &value = arg1->values().back();
if (value.isTokValue() && value.tokvalue->tokType() == Token::eString)
sizeValue = Token::getStrLength(value.tokvalue) + 1; // Add one for the null terminator
}
break;
}
if (sizeValue < 0)
continue;
ValueFlow::Value value(sizeValue);
value.errorPath.emplace_back(tok->tokAt(2), "Assign " + tok->strAt(1) + ", buffer with size " + MathLib::toString(sizeValue));
value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE;
value.setKnown();
const std::list<ValueFlow::Value> values{value};
valueFlowForwardVariable(const_cast<Token*>(rhs),
functionScope->bodyEnd,
tok->next()->variable(),
tok->next()->varId(),
values,
true,
false,
tokenlist,
errorLogger,
settings);
}
}
}
static bool getMinMaxValues(const ValueType *vt, const cppcheck::Platform &platform, MathLib::bigint *minValue, MathLib::bigint *maxValue)
{
if (!vt || !vt->isIntegral() || vt->pointer)
return false;
int bits;
switch (vt->type) {
case ValueType::Type::BOOL:
bits = 1;
break;
case ValueType::Type::CHAR:
bits = platform.char_bit;
break;
case ValueType::Type::SHORT:
bits = platform.short_bit;
break;
case ValueType::Type::INT:
bits = platform.int_bit;
break;
case ValueType::Type::LONG:
bits = platform.long_bit;
break;
case ValueType::Type::LONGLONG:
bits = platform.long_long_bit;
break;
default:
return false;
}
if (bits == 1) {
*minValue = 0;
*maxValue = 1;
} else if (bits < 62) {
if (vt->sign == ValueType::Sign::UNSIGNED) {
*minValue = 0;
*maxValue = (1LL << bits) - 1;
} else {
*minValue = -(1LL << (bits - 1));
*maxValue = (1LL << (bits - 1)) - 1;
}
} else if (bits == 64) {
if (vt->sign == ValueType::Sign::UNSIGNED) {
*minValue = 0;
*maxValue = LLONG_MAX; // todo max unsigned value
} else {
*minValue = LLONG_MIN;
*maxValue = LLONG_MAX;
}
} else {
return false;
}
return true;
}
static bool getMinMaxValues(const std::string &typestr, const Settings *settings, MathLib::bigint *minvalue, MathLib::bigint *maxvalue)
{
TokenList typeTokens(settings);
std::istringstream istr(typestr+";");
if (!typeTokens.createTokens(istr))
return false;
typeTokens.simplifyPlatformTypes();
typeTokens.simplifyStdType();
const ValueType &vt = ValueType::parseDecl(typeTokens.front(), settings);
return getMinMaxValues(&vt, *settings, minvalue, maxvalue);
}
static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
for (const Scope *functionScope : symboldatabase->functionScopes) {
if (!functionScope->bodyStart)
continue;
const Function *function = functionScope->function;
if (!function)
continue;
2019-07-24 11:39:18 +02:00
const bool safe = function->isSafe(settings);
const bool all = safe && settings->platformType != cppcheck::Platform::PlatformType::Unspecified;
for (const Variable &arg : function->argumentList) {
2019-07-24 12:08:56 +02:00
if (!arg.nameToken() || !arg.valueType())
continue;
2019-07-24 12:08:56 +02:00
if (arg.valueType()->type == ValueType::Type::CONTAINER) {
2019-07-24 11:39:18 +02:00
if (!safe)
continue;
std::list<ValueFlow::Value> argValues;
argValues.emplace_back(0);
argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " is empty");
2019-07-25 17:19:51 +02:00
argValues.back().safe = true;
2019-07-24 11:39:18 +02:00
argValues.emplace_back(1000000);
argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000");
2019-07-25 17:19:51 +02:00
argValues.back().safe = true;
2019-07-24 11:39:18 +02:00
for (const ValueFlow::Value &value : argValues)
valueFlowContainerForward(const_cast<Token*>(functionScope->bodyStart), &arg, value, tokenlist);
2019-07-24 11:39:18 +02:00
continue;
}
MathLib::bigint low, high;
bool isLow = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low);
bool isHigh = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high);
if (!isLow && !isHigh && !all)
continue;
2019-07-25 17:19:51 +02:00
const bool safeLow = !isLow;
const bool safeHigh = !isHigh;
if ((!isLow || !isHigh) && all) {
MathLib::bigint minValue, maxValue;
if (getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue)) {
if (!isLow)
low = minValue;
if (!isHigh)
high = maxValue;
isLow = isHigh = true;
2019-07-24 12:08:56 +02:00
} else if (arg.valueType()->type == ValueType::Type::FLOAT || arg.valueType()->type == ValueType::Type::DOUBLE || arg.valueType()->type == ValueType::Type::LONGDOUBLE) {
std::list<ValueFlow::Value> argValues;
argValues.emplace_back(0);
argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT;
argValues.back().floatValue = isLow ? low : -1E25f;
2019-07-25 17:19:51 +02:00
argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue));
argValues.back().safe = true;
argValues.emplace_back(0);
2019-07-24 12:08:56 +02:00
argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT;
argValues.back().floatValue = isHigh ? high : 1E25f;
2019-07-25 17:19:51 +02:00
argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue));
argValues.back().safe = true;
valueFlowForwardVariable(const_cast<Token*>(functionScope->bodyStart->next()),
functionScope->bodyEnd,
&arg,
arg.declarationId(),
argValues,
false,
false,
tokenlist,
errorLogger,
settings);
2019-07-24 12:08:56 +02:00
continue;
}
}
std::list<ValueFlow::Value> argValues;
if (isLow) {
argValues.emplace_back(low);
2019-07-25 17:19:51 +02:00
argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeLow ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(low));
argValues.back().safe = safeLow;
}
if (isHigh) {
argValues.emplace_back(high);
2019-07-25 17:19:51 +02:00
argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeHigh ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(high));
argValues.back().safe = safeHigh;
}
if (!argValues.empty())
valueFlowForwardVariable(const_cast<Token*>(functionScope->bodyStart->next()),
functionScope->bodyEnd,
&arg,
arg.declarationId(),
argValues,
false,
false,
tokenlist,
errorLogger,
settings);
}
}
}
2019-07-11 16:05:34 +02:00
static void valueFlowUnknownFunctionReturn(TokenList *tokenlist, const Settings *settings)
{
2019-07-11 16:05:34 +02:00
if (settings->checkUnknownFunctionReturn.empty())
return;
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (!tok->astParent() || tok->str() != "(" || !tok->previous()->isName())
continue;
2019-07-11 16:05:34 +02:00
if (settings->checkUnknownFunctionReturn.find(tok->previous()->str()) == settings->checkUnknownFunctionReturn.end())
continue;
std::vector<MathLib::bigint> unknownValues = settings->library.unknownReturnValues(tok->astOperand1());
if (unknownValues.empty())
continue;
// Get min/max values for return type
2019-07-10 22:08:27 +02:00
const std::string &typestr = settings->library.returnValueType(tok->previous());
MathLib::bigint minvalue, maxvalue;
if (!getMinMaxValues(typestr, settings, &minvalue, &maxvalue))
continue;
2019-07-11 16:05:34 +02:00
for (MathLib::bigint value : unknownValues) {
if (value < minvalue)
value = minvalue;
else if (value > maxvalue)
value = maxvalue;
setTokenValue(const_cast<Token *>(tok), ValueFlow::Value(value), settings);
}
}
}
ValueFlow::Value::Value(const Token* c, long long val)
: valueType(ValueType::INT),
bound(Bound::Point),
intvalue(val),
tokvalue(nullptr),
floatValue(0.0),
2019-07-10 15:27:07 +02:00
moveKind(MoveKind::NonMovedVariable),
varvalue(val),
condition(c),
varId(0),
2019-07-25 17:19:51 +02:00
safe(false),
conditional(false),
defaultArg(false),
indirect(0),
path(0),
2019-07-16 10:51:07 +02:00
lifetimeKind(LifetimeKind::Object),
2019-07-16 11:12:35 +02:00
lifetimeScope(LifetimeScope::Local),
valueKind(ValueKind::Possible)
{
errorPath.emplace_back(c, "Assuming that condition '" + c->expressionString() + "' is not redundant");
}
void ValueFlow::Value::assumeCondition(const Token* tok)
{
condition = tok;
errorPath.emplace_back(tok, "Assuming that condition '" + tok->expressionString() + "' is not redundant");
}
std::string ValueFlow::Value::infoString() const
{
switch (valueType) {
case ValueType::INT:
2017-05-20 08:47:35 +02:00
return MathLib::toString(intvalue);
case ValueType::TOK:
2017-05-20 08:47:35 +02:00
return tokvalue->str();
case ValueType::FLOAT:
2017-05-20 08:47:35 +02:00
return MathLib::toString(floatValue);
case ValueType::MOVED:
2017-05-20 08:47:35 +02:00
return "<Moved>";
case ValueType::UNINIT:
2017-05-20 08:47:35 +02:00
return "<Uninit>";
case ValueType::BUFFER_SIZE:
case ValueType::CONTAINER_SIZE:
return "size=" + MathLib::toString(intvalue);
case ValueType::ITERATOR_START:
return "start=" + MathLib::toString(intvalue);
case ValueType::ITERATOR_END:
return "end=" + MathLib::toString(intvalue);
case ValueType::LIFETIME:
return "lifetime=" + tokvalue->str();
}
throw InternalError(nullptr, "Invalid ValueFlow Value type");
}
const char* ValueFlow::Value::toString(MoveKind moveKind)
{
switch (moveKind) {
case MoveKind::NonMovedVariable:
return "NonMovedVariable";
case MoveKind::MovedVariable:
return "MovedVariable";
case MoveKind::ForwardedVariable:
return "ForwardedVariable";
}
return "";
}
const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(Token *expr, const Settings *settings)
2016-05-07 20:18:07 +02:00
{
if (expr && expr->values().empty()) {
valueFlowConstantFoldAST(expr->astOperand1(), settings);
valueFlowConstantFoldAST(expr->astOperand2(), settings);
2016-10-18 21:44:02 +02:00
valueFlowSetConstantValue(expr, settings, true /* TODO: this is a guess */);
}
return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr;
2016-05-07 20:18:07 +02:00
}
2018-11-03 15:53:24 +01:00
static std::size_t getTotalValues(TokenList *tokenlist)
{
std::size_t n = 1;
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
n += tok->values().size();
return n;
}
2016-05-07 20:18:07 +02:00
void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
tok->clearValueFlow();
Fix #9647: Set correct enum value (#2856) * Tokenize: Set varId for variables in enum Set varIds in enum values. It was previously disabled in 5119ae84b879fad to avoid issues with enums named the same as global variables. Take care to only set varids to variables used to set the value of an enumerator, not the enumerator itself. This is somewhat complicated by the fact that at the time this happens, astOperand1(), astOperand2(), astParent() etc are not set. The current implementation is not perfect, for example in the code below, y will not have a varid set, but x and z will. This is deemed sufficient for now. int x, y, z; enum E { a = f(x, y, z); }; * Fix #9647: Value of enums with variables as init values C++ allows enum values to be set using constexprs, which cppcheck did not handle before. To solve this, add a new pass to valueflow to update enum values after global consts have been processed. In order to do so, I moved all settings of enum values to valueflow. After setting the enum values, we need another call to valueFlowNumber() to actually set users of the enums. There is still room for improvements, since each pass of valueFlowGlobalConstVar() and valueFlowEnumValue() only sets variables that are possible to set directly, and not if setting the value of a variable allows us to set the value of another. For example constexpr int a = 5; constexpr int b = a + 5; enum E { X = a }; constexpr E e = X; Here both b and e will not have their values set, even though cppcheck should be possible to figure out their values. That's for another PR though. This was tested by running test-my-pr.py with 500 packages. The only difference was one error message in fairy-stockfish_11.1, where cppcheck now printed the correct size of an array instead of 2147483648 which I assume is some kind of default value. In that package, using a constexpr when setting enum values is common, but as mentioned, there was no change in the number of warnings.
2020-10-22 07:45:04 +02:00
valueFlowEnumValue(symboldatabase, settings);
valueFlowNumber(tokenlist);
valueFlowString(tokenlist);
valueFlowArray(tokenlist);
2019-07-11 16:05:34 +02:00
valueFlowUnknownFunctionReturn(tokenlist, settings);
valueFlowGlobalConstVar(tokenlist, settings);
Fix #9647: Set correct enum value (#2856) * Tokenize: Set varId for variables in enum Set varIds in enum values. It was previously disabled in 5119ae84b879fad to avoid issues with enums named the same as global variables. Take care to only set varids to variables used to set the value of an enumerator, not the enumerator itself. This is somewhat complicated by the fact that at the time this happens, astOperand1(), astOperand2(), astParent() etc are not set. The current implementation is not perfect, for example in the code below, y will not have a varid set, but x and z will. This is deemed sufficient for now. int x, y, z; enum E { a = f(x, y, z); }; * Fix #9647: Value of enums with variables as init values C++ allows enum values to be set using constexprs, which cppcheck did not handle before. To solve this, add a new pass to valueflow to update enum values after global consts have been processed. In order to do so, I moved all settings of enum values to valueflow. After setting the enum values, we need another call to valueFlowNumber() to actually set users of the enums. There is still room for improvements, since each pass of valueFlowGlobalConstVar() and valueFlowEnumValue() only sets variables that are possible to set directly, and not if setting the value of a variable allows us to set the value of another. For example constexpr int a = 5; constexpr int b = a + 5; enum E { X = a }; constexpr E e = X; Here both b and e will not have their values set, even though cppcheck should be possible to figure out their values. That's for another PR though. This was tested by running test-my-pr.py with 500 packages. The only difference was one error message in fairy-stockfish_11.1, where cppcheck now printed the correct size of an array instead of 2147483648 which I assume is some kind of default value. In that package, using a constexpr when setting enum values is common, but as mentioned, there was no change in the number of warnings.
2020-10-22 07:45:04 +02:00
valueFlowEnumValue(symboldatabase, settings);
valueFlowNumber(tokenlist);
valueFlowGlobalStaticVar(tokenlist, settings);
valueFlowPointerAlias(tokenlist);
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
valueFlowBitAnd(tokenlist);
valueFlowSameExpressions(tokenlist);
valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, settings);
2018-11-03 15:53:24 +01:00
std::size_t values = 0;
std::size_t n = 4;
while (n > 0 && values < getTotalValues(tokenlist)) {
2018-11-03 15:53:24 +01:00
values = getTotalValues(tokenlist);
valueFlowImpossibleValues(tokenlist, settings);
valueFlowPointerAliasDeref(tokenlist);
valueFlowArrayBool(tokenlist);
valueFlowRightShift(tokenlist, settings);
2018-11-03 15:53:24 +01:00
valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings);
valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
2019-09-28 20:50:56 +02:00
valueFlowInferCondition(tokenlist, settings);
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
2018-11-03 15:53:24 +01:00
valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings);
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings);
valueFlowFunctionReturn(tokenlist, errorLogger);
2020-09-04 18:56:34 +02:00
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
2018-11-03 15:53:24 +01:00
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
if (tokenlist->isCPP()) {
valueFlowSmartPointer(tokenlist, errorLogger, settings);
2020-08-21 17:23:55 +02:00
valueFlowIterators(tokenlist, settings);
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
valueFlowIteratorInfer(tokenlist, settings);
2018-11-03 15:53:24 +01:00
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
}
valueFlowSafeFunctions(tokenlist, symboldatabase, errorLogger, settings);
n--;
2018-11-03 15:53:24 +01:00
}
valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings);
}
std::string ValueFlow::eitherTheConditionIsRedundant(const Token *condition)
{
if (!condition)
return "Either the condition is redundant";
if (condition->str() == "case") {
std::string expr;
for (const Token *tok = condition; tok && tok->str() != ":"; tok = tok->next()) {
expr += tok->str();
if (Token::Match(tok, "%name%|%num% %name%|%num%"))
expr += ' ';
}
return "Either the switch case '" + expr + "' is redundant";
}
return "Either the condition '" + condition->expressionString() + "' is redundant";
2020-05-19 21:55:28 +02:00
}