ValueFlow: some interface and namespace cleanups (#4746)
This commit is contained in:
parent
464fbe8d53
commit
afd13ea11d
|
@ -879,7 +879,7 @@ bool extractForLoopValues(const Token *forToken,
|
||||||
const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2();
|
const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2();
|
||||||
if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%"))
|
if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%"))
|
||||||
return false;
|
return false;
|
||||||
std::vector<MathLib::bigint> minInitValue = getMinValue(makeIntegralInferModel(), initExpr->astOperand2()->values());
|
std::vector<MathLib::bigint> minInitValue = getMinValue(ValueFlow::makeIntegralInferModel(), initExpr->astOperand2()->values());
|
||||||
*varid = initExpr->astOperand1()->varId();
|
*varid = initExpr->astOperand1()->varId();
|
||||||
*knownInitValue = initExpr->astOperand2()->hasKnownIntValue();
|
*knownInitValue = initExpr->astOperand2()->hasKnownIntValue();
|
||||||
*initValue = minInitValue.empty() ? 0 : minInitValue.front();
|
*initValue = minInitValue.empty() ? 0 : minInitValue.front();
|
||||||
|
@ -1289,10 +1289,10 @@ const Token* followReferences(const Token* tok, ErrorPath* errors)
|
||||||
|
|
||||||
static bool isSameLifetime(const Token * const tok1, const Token * const tok2)
|
static bool isSameLifetime(const Token * const tok1, const Token * const tok2)
|
||||||
{
|
{
|
||||||
ValueFlow::Value v1 = getLifetimeObjValue(tok1);
|
ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1);
|
||||||
if (!v1.isLifetimeValue())
|
if (!v1.isLifetimeValue())
|
||||||
return false;
|
return false;
|
||||||
ValueFlow::Value v2 = getLifetimeObjValue(tok2);
|
ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2);
|
||||||
if (!v2.isLifetimeValue())
|
if (!v2.isLifetimeValue())
|
||||||
return false;
|
return false;
|
||||||
return v1.tokvalue == v2.tokvalue;
|
return v1.tokvalue == v2.tokvalue;
|
||||||
|
|
|
@ -518,7 +518,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
|
for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
|
||||||
// Return reference from function
|
// Return reference from function
|
||||||
if (returnRef && Token::simpleMatch(tok->astParent(), "return")) {
|
if (returnRef && Token::simpleMatch(tok->astParent(), "return")) {
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(tok, true)) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok, true)) {
|
||||||
if (!printInconclusive && lt.inconclusive)
|
if (!printInconclusive && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
const Variable* var = lt.token->variable();
|
const Variable* var = lt.token->variable();
|
||||||
|
@ -537,14 +537,14 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() &&
|
tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() &&
|
||||||
!tok->variable()->isArgument()) {
|
!tok->variable()->isArgument()) {
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
const Variable *var = getLifetimeVariable(tok, errorPath);
|
const Variable *var = ValueFlow::getLifetimeVariable(tok, errorPath);
|
||||||
if (var && isInScope(var->nameToken(), tok->scope())) {
|
if (var && isInScope(var->nameToken(), tok->scope())) {
|
||||||
errorDanglingReference(tok, var, errorPath);
|
errorDanglingReference(tok, var, errorPath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Reference to temporary
|
// Reference to temporary
|
||||||
} else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) {
|
} else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) {
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(getParentLifetime(tok))) {
|
||||||
if (!printInconclusive && lt.inconclusive)
|
if (!printInconclusive && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
const Token * tokvalue = lt.token;
|
const Token * tokvalue = lt.token;
|
||||||
|
@ -565,13 +565,13 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
const Token* parent = getParentLifetime(mTokenizer->isCPP(), val.tokvalue, &mSettings->library);
|
const Token* parent = getParentLifetime(mTokenizer->isCPP(), val.tokvalue, &mSettings->library);
|
||||||
if (!exprs.insert(parent).second)
|
if (!exprs.insert(parent).second)
|
||||||
continue;
|
continue;
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) {
|
||||||
const Token * tokvalue = lt.token;
|
const Token * tokvalue = lt.token;
|
||||||
if (val.isLocalLifetimeValue()) {
|
if (val.isLocalLifetimeValue()) {
|
||||||
if (escape) {
|
if (escape) {
|
||||||
if (getPointerDepth(tok) < getPointerDepth(tokvalue))
|
if (getPointerDepth(tok) < getPointerDepth(tokvalue))
|
||||||
continue;
|
continue;
|
||||||
if (!isLifetimeBorrowed(tok, mSettings))
|
if (!ValueFlow::isLifetimeBorrowed(tok, mSettings))
|
||||||
continue;
|
continue;
|
||||||
if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) &&
|
if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) &&
|
||||||
!astIsContainerView(tok->astParent()))
|
!astIsContainerView(tok->astParent()))
|
||||||
|
@ -604,7 +604,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
} else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) {
|
} else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) {
|
||||||
var = tok->variable();
|
var = tok->variable();
|
||||||
}
|
}
|
||||||
if (!isLifetimeBorrowed(tok, mSettings))
|
if (!ValueFlow::isLifetimeBorrowed(tok, mSettings))
|
||||||
continue;
|
continue;
|
||||||
const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop());
|
const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop());
|
||||||
if (!nextTok)
|
if (!nextTok)
|
||||||
|
|
|
@ -1030,7 +1030,7 @@ void CheckBufferOverrun::objectIndex()
|
||||||
if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0)
|
if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::vector<ValueFlow::Value> values = getLifetimeObjValues(obj, false, -1);
|
std::vector<ValueFlow::Value> values = ValueFlow::getLifetimeObjValues(obj, false, -1);
|
||||||
for (const ValueFlow::Value& v:values) {
|
for (const ValueFlow::Value& v:values) {
|
||||||
if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
|
if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1446,7 +1446,7 @@ void CheckOther::checkConstVariable()
|
||||||
retTok = retTok->astOperand2();
|
retTok = retTok->astOperand2();
|
||||||
while (Token::simpleMatch(retTok, "."))
|
while (Token::simpleMatch(retTok, "."))
|
||||||
retTok = retTok->astOperand2();
|
retTok = retTok->astOperand2();
|
||||||
return hasLifetimeToken(getParentLifetime(retTok), var->nameToken());
|
return ValueFlow::hasLifetimeToken(getParentLifetime(retTok), var->nameToken());
|
||||||
}))
|
}))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -3671,8 +3671,8 @@ void CheckOther::checkComparePointers()
|
||||||
const Token *tok2 = tok->astOperand2();
|
const Token *tok2 = tok->astOperand2();
|
||||||
if (!astIsPointer(tok1) || !astIsPointer(tok2))
|
if (!astIsPointer(tok1) || !astIsPointer(tok2))
|
||||||
continue;
|
continue;
|
||||||
ValueFlow::Value v1 = getLifetimeObjValue(tok1);
|
ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1);
|
||||||
ValueFlow::Value v2 = getLifetimeObjValue(tok2);
|
ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2);
|
||||||
if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue())
|
if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue())
|
||||||
continue;
|
continue;
|
||||||
const Variable *var1 = v1.tokvalue->variable();
|
const Variable *var1 = v1.tokvalue->variable();
|
||||||
|
|
|
@ -727,7 +727,7 @@ static bool isSameIteratorContainerExpression(const Token* tok1,
|
||||||
|
|
||||||
static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigint path = 0)
|
static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigint path = 0)
|
||||||
{
|
{
|
||||||
std::vector<ValueFlow::Value> values = getLifetimeObjValues(tok, false, path);
|
std::vector<ValueFlow::Value> values = ValueFlow::getLifetimeObjValues(tok, false, path);
|
||||||
auto it = std::find_if(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) {
|
auto it = std::find_if(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) {
|
||||||
return v.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator;
|
return v.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator;
|
||||||
});
|
});
|
||||||
|
@ -1155,7 +1155,7 @@ void CheckStl::invalidContainer()
|
||||||
|
|
||||||
ErrorPath ep;
|
ErrorPath ep;
|
||||||
bool addressOf = false;
|
bool addressOf = false;
|
||||||
const Variable* var = getLifetimeVariable(info.tok, ep, &addressOf);
|
const Variable* var = ValueFlow::getLifetimeVariable(info.tok, ep, &addressOf);
|
||||||
// Check the reference is created before the change
|
// Check the reference is created before the change
|
||||||
if (var && var->declarationId() == r.tok->varId() && !addressOf) {
|
if (var && var->declarationId() == r.tok->varId() && !addressOf) {
|
||||||
// An argument always reaches
|
// An argument always reaches
|
||||||
|
|
|
@ -1287,7 +1287,7 @@ void CheckUnusedVar::checkFunctionVariableUsage()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library);
|
FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library);
|
||||||
const Token* scopeEnd = getEndOfExprScope(expr, scope, /*smallest*/ false);
|
const Token* scopeEnd = ValueFlow::getEndOfExprScope(expr, scope, /*smallest*/ false);
|
||||||
if (fwdAnalysis.unusedValue(expr, start, scopeEnd)) {
|
if (fwdAnalysis.unusedValue(expr, start, scopeEnd)) {
|
||||||
if (!bailoutTypeName.empty()) {
|
if (!bailoutTypeName.empty()) {
|
||||||
if (bailoutTypeName != "auto")
|
if (bailoutTypeName != "auto")
|
||||||
|
|
|
@ -1274,13 +1274,13 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const
|
||||||
if (expr->isComparisonOp()) {
|
if (expr->isComparisonOp()) {
|
||||||
if (rhs.isIntValue()) {
|
if (rhs.isIntValue()) {
|
||||||
std::vector<ValueFlow::Value> result =
|
std::vector<ValueFlow::Value> result =
|
||||||
infer(makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs});
|
infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs});
|
||||||
if (result.empty() || !result.front().isKnown())
|
if (result.empty() || !result.front().isKnown())
|
||||||
return unknown;
|
return unknown;
|
||||||
return result.front();
|
return result.front();
|
||||||
} else if (lhs.isIntValue()) {
|
} else if (lhs.isIntValue()) {
|
||||||
std::vector<ValueFlow::Value> result =
|
std::vector<ValueFlow::Value> result =
|
||||||
infer(makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values());
|
infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values());
|
||||||
if (result.empty() || !result.front().isKnown())
|
if (result.empty() || !result.front().isKnown())
|
||||||
return unknown;
|
return unknown;
|
||||||
return result.front();
|
return result.front();
|
||||||
|
@ -1353,7 +1353,7 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const
|
||||||
if (child->exprId() > 0 && pm.hasValue(child->exprId())) {
|
if (child->exprId() > 0 && pm.hasValue(child->exprId())) {
|
||||||
ValueFlow::Value& v = pm.at(child->exprId());
|
ValueFlow::Value& v = pm.at(child->exprId());
|
||||||
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
|
if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) {
|
||||||
if (isContainerSizeChanged(child, v.indirect, settings))
|
if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings))
|
||||||
v = unknown;
|
v = unknown;
|
||||||
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
|
} else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) {
|
||||||
if (isVariableChanged(child, v.indirect, settings, true))
|
if (isVariableChanged(child, v.indirect, settings, true))
|
||||||
|
|
|
@ -343,10 +343,10 @@ static void parseCompareEachInt(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token* parseCompareInt(const Token* tok,
|
const Token* ValueFlow::parseCompareInt(const Token* tok,
|
||||||
ValueFlow::Value& true_value,
|
ValueFlow::Value& true_value,
|
||||||
ValueFlow::Value& false_value,
|
ValueFlow::Value& false_value,
|
||||||
const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate)
|
const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate)
|
||||||
{
|
{
|
||||||
const Token* result = nullptr;
|
const Token* result = nullptr;
|
||||||
parseCompareEachInt(
|
parseCompareEachInt(
|
||||||
|
@ -370,7 +370,7 @@ const Token* parseCompareInt(const Token* tok,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value)
|
const Token *ValueFlow::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> {
|
return parseCompareInt(tok, true_value, false_value, [](const Token* t) -> std::vector<MathLib::bigint> {
|
||||||
if (t->hasKnownIntValue())
|
if (t->hasKnownIntValue())
|
||||||
|
@ -417,7 +417,7 @@ static bool isNumeric(const ValueFlow::Value& value) {
|
||||||
return value.isIntValue() || value.isFloatValue();
|
return value.isIntValue() || value.isFloatValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result)
|
void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result)
|
||||||
{
|
{
|
||||||
if (value1.isKnown() && value2.isKnown())
|
if (value1.isKnown() && value2.isKnown())
|
||||||
result->setKnown();
|
result->setKnown();
|
||||||
|
@ -688,7 +688,7 @@ static void setTokenValue(Token* tok,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.isLifetimeValue()) {
|
if (value.isLifetimeValue()) {
|
||||||
if (!isLifetimeBorrowed(parent, settings))
|
if (!ValueFlow::isLifetimeBorrowed(parent, settings))
|
||||||
return;
|
return;
|
||||||
if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) {
|
if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) {
|
||||||
setTokenValue(parent,value,settings);
|
setTokenValue(parent,value,settings);
|
||||||
|
@ -1288,7 +1288,6 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b
|
||||||
return tok->next();
|
return tok->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void valueFlowNumber(TokenList *tokenlist)
|
static void valueFlowNumber(TokenList *tokenlist)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok;) {
|
for (Token *tok = tokenlist->front(); tok;) {
|
||||||
|
@ -1973,8 +1972,8 @@ static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *setti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist);
|
static ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist);
|
||||||
ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist);
|
static ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist);
|
||||||
|
|
||||||
static Analyzer::Result valueFlowForward(Token* startToken,
|
static Analyzer::Result valueFlowForward(Token* startToken,
|
||||||
const Token* endToken,
|
const Token* endToken,
|
||||||
|
@ -3229,7 +3228,7 @@ struct MemberExpressionAnalyzer : SubExpressionAnalyzer {
|
||||||
|
|
||||||
enum class LifetimeCapture { Undefined, ByValue, ByReference };
|
enum class LifetimeCapture { Undefined, ByValue, ByReference };
|
||||||
|
|
||||||
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
|
static std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if (!val)
|
if (!val)
|
||||||
|
@ -3253,7 +3252,7 @@ std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath)
|
std::string ValueFlow::lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath)
|
||||||
{
|
{
|
||||||
const Token *tokvalue = val ? val->tokvalue : nullptr;
|
const Token *tokvalue = val ? val->tokvalue : nullptr;
|
||||||
const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr;
|
const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr;
|
||||||
|
@ -3291,7 +3290,7 @@ std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, Error
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ValueFlow::Value> getLifetimeObjValues(const Token* tok, bool inconclusive, MathLib::bigint path)
|
std::vector<ValueFlow::Value> ValueFlow::getLifetimeObjValues(const Token* tok, bool inconclusive, MathLib::bigint path)
|
||||||
{
|
{
|
||||||
std::vector<ValueFlow::Value> result;
|
std::vector<ValueFlow::Value> result;
|
||||||
auto pred = [&](const ValueFlow::Value& v) {
|
auto pred = [&](const ValueFlow::Value& v) {
|
||||||
|
@ -3338,9 +3337,9 @@ static bool derefShared(const Token* tok)
|
||||||
return !hasUniqueOwnership(ptrTok);
|
return !hasUniqueOwnership(ptrTok);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive)
|
ValueFlow::Value ValueFlow::getLifetimeObjValue(const Token *tok, bool inconclusive)
|
||||||
{
|
{
|
||||||
std::vector<ValueFlow::Value> values = getLifetimeObjValues(tok, inconclusive);
|
std::vector<ValueFlow::Value> values = ValueFlow::getLifetimeObjValues(tok, inconclusive);
|
||||||
// There should only be one lifetime
|
// There should only be one lifetime
|
||||||
if (values.size() != 1)
|
if (values.size() != 1)
|
||||||
return ValueFlow::Value{};
|
return ValueFlow::Value{};
|
||||||
|
@ -3348,16 +3347,16 @@ ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Predicate>
|
template<class Predicate>
|
||||||
static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
static std::vector<ValueFlow::LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
bool escape,
|
bool escape,
|
||||||
ValueFlow::Value::ErrorPath errorPath,
|
ValueFlow::Value::ErrorPath errorPath,
|
||||||
Predicate pred,
|
Predicate pred,
|
||||||
int depth = 20)
|
int depth = 20)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return std::vector<LifetimeToken> {};
|
return std::vector<ValueFlow::LifetimeToken> {};
|
||||||
if (Token::simpleMatch(tok, "..."))
|
if (Token::simpleMatch(tok, "..."))
|
||||||
return std::vector<LifetimeToken>{};
|
return std::vector<ValueFlow::LifetimeToken>{};
|
||||||
const Variable *var = tok->variable();
|
const Variable *var = tok->variable();
|
||||||
if (pred(tok))
|
if (pred(tok))
|
||||||
return {{tok, std::move(errorPath)}};
|
return {{tok, std::move(errorPath)}};
|
||||||
|
@ -3392,9 +3391,9 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
if (astIsContainer(contok))
|
if (astIsContainer(contok))
|
||||||
return getLifetimeTokens(contok, escape, std::move(errorPath), pred, depth - 1);
|
return getLifetimeTokens(contok, escape, std::move(errorPath), pred, depth - 1);
|
||||||
else
|
else
|
||||||
return std::vector<LifetimeToken>{};
|
return std::vector<ValueFlow::LifetimeToken>{};
|
||||||
} else {
|
} else {
|
||||||
return std::vector<LifetimeToken> {};
|
return std::vector<ValueFlow::LifetimeToken> {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Token::Match(tok->previous(), "%name% (")) {
|
} else if (Token::Match(tok->previous(), "%name% (")) {
|
||||||
|
@ -3402,12 +3401,12 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
if (f) {
|
if (f) {
|
||||||
if (!Function::returnsReference(f))
|
if (!Function::returnsReference(f))
|
||||||
return {{tok, std::move(errorPath)}};
|
return {{tok, std::move(errorPath)}};
|
||||||
std::vector<LifetimeToken> result;
|
std::vector<ValueFlow::LifetimeToken> result;
|
||||||
std::vector<const Token*> returns = Function::findReturns(f);
|
std::vector<const Token*> returns = Function::findReturns(f);
|
||||||
for (const Token* returnTok : returns) {
|
for (const Token* returnTok : returns) {
|
||||||
if (returnTok == tok)
|
if (returnTok == tok)
|
||||||
continue;
|
continue;
|
||||||
for (LifetimeToken& lt : getLifetimeTokens(returnTok, escape, errorPath, pred, depth - returns.size())) {
|
for (ValueFlow::LifetimeToken& lt : getLifetimeTokens(returnTok, escape, errorPath, pred, depth - returns.size())) {
|
||||||
const Token* argvarTok = lt.token;
|
const Token* argvarTok = lt.token;
|
||||||
const Variable* argvar = argvarTok->variable();
|
const Variable* argvar = argvarTok->variable();
|
||||||
if (!argvar)
|
if (!argvar)
|
||||||
|
@ -3416,11 +3415,11 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
|
if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
|
||||||
const int n = getArgumentPos(argvar, f);
|
const int n = getArgumentPos(argvar, f);
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return std::vector<LifetimeToken> {};
|
return std::vector<ValueFlow::LifetimeToken> {};
|
||||||
std::vector<const Token*> args = getArguments(tok->previous());
|
std::vector<const Token*> args = getArguments(tok->previous());
|
||||||
// TODO: Track lifetimes of default parameters
|
// TODO: Track lifetimes of default parameters
|
||||||
if (n >= args.size())
|
if (n >= args.size())
|
||||||
return std::vector<LifetimeToken> {};
|
return std::vector<ValueFlow::LifetimeToken> {};
|
||||||
argTok = args[n];
|
argTok = args[n];
|
||||||
lt.errorPath.emplace_back(returnTok, "Return reference.");
|
lt.errorPath.emplace_back(returnTok, "Return reference.");
|
||||||
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
|
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
|
||||||
|
@ -3432,7 +3431,7 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
"Calling member function on '" + argTok->expressionString() + "'.");
|
"Calling member function on '" + argTok->expressionString() + "'.");
|
||||||
}
|
}
|
||||||
if (argTok) {
|
if (argTok) {
|
||||||
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
|
std::vector<ValueFlow::LifetimeToken> arglts = ValueFlow::LifetimeToken::setInconclusive(
|
||||||
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), pred, depth - returns.size()),
|
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), pred, depth - returns.size()),
|
||||||
returns.size() > 1);
|
returns.size() > 1);
|
||||||
result.insert(result.end(), arglts.cbegin(), arglts.cend());
|
result.insert(result.end(), arglts.cbegin(), arglts.cend());
|
||||||
|
@ -3445,7 +3444,7 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
const Library::Container::Yield y = library->getYield(tok->previous()->str());
|
const Library::Container::Yield y = library->getYield(tok->previous()->str());
|
||||||
if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) {
|
if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) {
|
||||||
errorPath.emplace_back(tok->previous(), "Accessing container.");
|
errorPath.emplace_back(tok->previous(), "Accessing container.");
|
||||||
return LifetimeToken::setAddressOf(
|
return ValueFlow::LifetimeToken::setAddressOf(
|
||||||
getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), pred, depth - 1),
|
getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), pred, depth - 1),
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
@ -3474,8 +3473,8 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath), pred, depth - 1);
|
return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath), pred, depth - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1),
|
return ValueFlow::LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1),
|
||||||
!(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "[")));
|
!(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "[")));
|
||||||
}
|
}
|
||||||
} else if (Token::simpleMatch(tok, "{") && getArgumentStart(tok) &&
|
} else if (Token::simpleMatch(tok, "{") && getArgumentStart(tok) &&
|
||||||
!Token::simpleMatch(getArgumentStart(tok), ",") && getArgumentStart(tok)->valueType()) {
|
!Token::simpleMatch(getArgumentStart(tok), ",") && getArgumentStart(tok)->valueType()) {
|
||||||
|
@ -3490,14 +3489,14 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
return {{tok, std::move(errorPath)}};
|
return {{tok, std::move(errorPath)}};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath)
|
std::vector<ValueFlow::LifetimeToken> ValueFlow::getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath)
|
||||||
{
|
{
|
||||||
return getLifetimeTokens(tok, escape, std::move(errorPath), [](const Token*) {
|
return getLifetimeTokens(tok, escape, std::move(errorPath), [](const Token*) {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasLifetimeToken(const Token* tok, const Token* lifetime)
|
bool ValueFlow::hasLifetimeToken(const Token* tok, const Token* lifetime)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
getLifetimeTokens(tok, false, ValueFlow::Value::ErrorPath{}, [&](const Token* tok2) {
|
getLifetimeTokens(tok, false, ValueFlow::Value::ErrorPath{}, [&](const Token* tok2) {
|
||||||
|
@ -3509,7 +3508,7 @@ bool hasLifetimeToken(const Token* tok, const Token* lifetime)
|
||||||
|
|
||||||
static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr)
|
static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr)
|
||||||
{
|
{
|
||||||
std::vector<LifetimeToken> lts = getLifetimeTokens(tok);
|
std::vector<ValueFlow::LifetimeToken> lts = ValueFlow::getLifetimeTokens(tok);
|
||||||
if (lts.size() != 1)
|
if (lts.size() != 1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (lts.front().inconclusive)
|
if (lts.front().inconclusive)
|
||||||
|
@ -3520,7 +3519,7 @@ static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPa
|
||||||
return lts.front().token;
|
return lts.front().token;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf)
|
const Variable* ValueFlow::getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf)
|
||||||
{
|
{
|
||||||
const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf);
|
const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf);
|
||||||
if (tok2 && tok2->variable())
|
if (tok2 && tok2->variable())
|
||||||
|
@ -3528,7 +3527,7 @@ const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPat
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Variable* getLifetimeVariable(const Token* tok)
|
const Variable* ValueFlow::getLifetimeVariable(const Token* tok)
|
||||||
{
|
{
|
||||||
ValueFlow::Value::ErrorPath errorPath;
|
ValueFlow::Value::ErrorPath errorPath;
|
||||||
return getLifetimeVariable(tok, errorPath, nullptr);
|
return getLifetimeVariable(tok, errorPath, nullptr);
|
||||||
|
@ -3662,7 +3661,7 @@ static bool isDifferentType(const Token* src, const Token* dst)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isLifetimeBorrowed(const Token *tok, const Settings *settings)
|
bool ValueFlow::isLifetimeBorrowed(const Token *tok, const Settings *settings)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return true;
|
return true;
|
||||||
|
@ -3729,7 +3728,7 @@ static const Token* getEndOfVarScope(const Variable* var)
|
||||||
return innerScope->bodyEnd;
|
return innerScope->bodyEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token* getEndOfExprScope(const Token* tok, const Scope* defaultScope, bool smallest)
|
const Token* ValueFlow::getEndOfExprScope(const Token* tok, const Scope* defaultScope, bool smallest)
|
||||||
{
|
{
|
||||||
const Token* end = nullptr;
|
const Token* end = nullptr;
|
||||||
bool local = false;
|
bool local = false;
|
||||||
|
@ -3767,7 +3766,7 @@ static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLog
|
||||||
if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous())) {
|
if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous())) {
|
||||||
std::list<ValueFlow::Value> values = tok->values();
|
std::list<ValueFlow::Value> values = tok->values();
|
||||||
values.remove_if(&isNotLifetimeValue);
|
values.remove_if(&isNotLifetimeValue);
|
||||||
valueFlowForward(nextAfterAstRightmostLeaf(tok), getEndOfExprScope(tok), tok->previous(), values, tokenlist);
|
valueFlowForward(nextAfterAstRightmostLeaf(tok), ValueFlow::getEndOfExprScope(tok), tok->previous(), values, tokenlist);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Token *parent = tok->astParent();
|
Token *parent = tok->astParent();
|
||||||
|
@ -3781,7 +3780,7 @@ static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLog
|
||||||
if (!parent->astOperand2() || parent->astOperand2()->values().empty())
|
if (!parent->astOperand2() || parent->astOperand2()->values().empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!isLifetimeBorrowed(parent->astOperand2(), settings))
|
if (!ValueFlow::isLifetimeBorrowed(parent->astOperand2(), settings))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Token* expr = getLHSVariableToken(parent);
|
const Token* expr = getLHSVariableToken(parent);
|
||||||
|
@ -3791,7 +3790,7 @@ static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLog
|
||||||
if (expr->exprId() == 0)
|
if (expr->exprId() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Token* endOfVarScope = getEndOfExprScope(expr);
|
const Token* endOfVarScope = ValueFlow::getEndOfExprScope(expr);
|
||||||
|
|
||||||
// Only forward lifetime values
|
// Only forward lifetime values
|
||||||
std::list<ValueFlow::Value> values = parent->astOperand2()->values();
|
std::list<ValueFlow::Value> values = parent->astOperand2()->values();
|
||||||
|
@ -3938,7 +3937,7 @@ struct LifetimeStore {
|
||||||
if (!argtok)
|
if (!argtok)
|
||||||
return false;
|
return false;
|
||||||
bool update = false;
|
bool update = false;
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(argtok)) {
|
||||||
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
ErrorPath er = errorPath;
|
ErrorPath er = errorPath;
|
||||||
|
@ -4000,7 +3999,7 @@ struct LifetimeStore {
|
||||||
if (argtok->values().empty()) {
|
if (argtok->values().empty()) {
|
||||||
ErrorPath er;
|
ErrorPath er;
|
||||||
er.emplace_back(argtok, message);
|
er.emplace_back(argtok, message);
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(argtok)) {
|
||||||
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
ValueFlow::Value value;
|
ValueFlow::Value value;
|
||||||
|
@ -4029,7 +4028,7 @@ struct LifetimeStore {
|
||||||
if (!v.isLifetimeValue())
|
if (!v.isLifetimeValue())
|
||||||
continue;
|
continue;
|
||||||
const Token *tok3 = v.tokvalue;
|
const Token *tok3 = v.tokvalue;
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(tok3)) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok3)) {
|
||||||
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
ErrorPath er = v.errorPath;
|
ErrorPath er = v.errorPath;
|
||||||
|
@ -4101,7 +4100,7 @@ struct LifetimeStore {
|
||||||
continue;
|
continue;
|
||||||
const Token *tok2 = v.tokvalue;
|
const Token *tok2 = v.tokvalue;
|
||||||
ErrorPath er = v.errorPath;
|
ErrorPath er = v.errorPath;
|
||||||
const Variable *var = getLifetimeVariable(tok2, er);
|
const Variable *var = ValueFlow::getLifetimeVariable(tok2, er);
|
||||||
// TODO: the inserted data is never used
|
// TODO: the inserted data is never used
|
||||||
er.insert(er.end(), errorPath.cbegin(), errorPath.cend());
|
er.insert(er.end(), errorPath.cbegin(), errorPath.cend());
|
||||||
if (!var)
|
if (!var)
|
||||||
|
@ -4199,9 +4198,9 @@ static void valueFlowLifetimeUserConstructor(Token* tok,
|
||||||
const Token* expr = tok2->astOperand2();
|
const Token* expr = tok2->astOperand2();
|
||||||
if (!var)
|
if (!var)
|
||||||
continue;
|
continue;
|
||||||
if (!isLifetimeBorrowed(expr, settings))
|
if (!ValueFlow::isLifetimeBorrowed(expr, settings))
|
||||||
continue;
|
continue;
|
||||||
const Variable* argvar = getLifetimeVariable(expr);
|
const Variable* argvar = ValueFlow::getLifetimeVariable(expr);
|
||||||
if (var->isReference() || var->isRValueReference()) {
|
if (var->isReference() || var->isRValueReference()) {
|
||||||
if (argvar && argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
|
if (argvar && argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) {
|
||||||
paramCapture[argvar] = LifetimeCapture::ByReference;
|
paramCapture[argvar] = LifetimeCapture::ByReference;
|
||||||
|
@ -4312,7 +4311,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
|
||||||
LifetimeStore{
|
LifetimeStore{
|
||||||
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
|
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
|
||||||
.byDerefCopy(memtok, tokenlist, errorLogger, settings);
|
.byDerefCopy(memtok, tokenlist, errorLogger, settings);
|
||||||
} else if (!args.empty() && isLifetimeBorrowed(args.back(), settings)) {
|
} else if (!args.empty() && ValueFlow::isLifetimeBorrowed(args.back(), settings)) {
|
||||||
LifetimeStore{
|
LifetimeStore{
|
||||||
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
|
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
|
||||||
.byVal(memtok, tokenlist, errorLogger, settings);
|
.byVal(memtok, tokenlist, errorLogger, settings);
|
||||||
|
@ -4331,7 +4330,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
|
||||||
for (const Token* returnTok : returns) {
|
for (const Token* returnTok : returns) {
|
||||||
if (returnTok == tok)
|
if (returnTok == tok)
|
||||||
continue;
|
continue;
|
||||||
const Variable *returnVar = getLifetimeVariable(returnTok);
|
const Variable *returnVar = ValueFlow::getLifetimeVariable(returnTok);
|
||||||
if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) {
|
if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) {
|
||||||
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger);
|
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger);
|
||||||
ls.inconclusive = inconclusive;
|
ls.inconclusive = inconclusive;
|
||||||
|
@ -4750,7 +4749,7 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
|
||||||
}
|
}
|
||||||
// address of
|
// address of
|
||||||
else if (tok->isUnaryOp("&")) {
|
else if (tok->isUnaryOp("&")) {
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(tok->astOperand1())) {
|
for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok->astOperand1())) {
|
||||||
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive)
|
||||||
continue;
|
continue;
|
||||||
ErrorPath errorPath = lt.errorPath;
|
ErrorPath errorPath = lt.errorPath;
|
||||||
|
@ -4869,7 +4868,7 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase* /*db*/, Erro
|
||||||
// Check variables
|
// Check variables
|
||||||
else if (tok->variable()) {
|
else if (tok->variable()) {
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
const Variable * var = getLifetimeVariable(tok, errorPath);
|
const Variable * var = ValueFlow::getLifetimeVariable(tok, errorPath);
|
||||||
if (!var)
|
if (!var)
|
||||||
continue;
|
continue;
|
||||||
if (var->nameToken() == tok)
|
if (var->nameToken() == tok)
|
||||||
|
@ -5002,7 +5001,7 @@ static void valueFlowAfterMove(TokenList* tokenlist, SymbolDatabase* symboldatab
|
||||||
continue;
|
continue;
|
||||||
if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId)
|
if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId)
|
||||||
continue;
|
continue;
|
||||||
const Token* const endOfVarScope = getEndOfExprScope(varTok);
|
const Token* const endOfVarScope = ValueFlow::getEndOfExprScope(varTok);
|
||||||
|
|
||||||
const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok);
|
const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok);
|
||||||
const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove);
|
const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove);
|
||||||
|
@ -5261,7 +5260,7 @@ static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldataba
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Token* start = nextAfterAstRightmostLeaf(tok);
|
Token* start = nextAfterAstRightmostLeaf(tok);
|
||||||
const Token* end = getEndOfExprScope(tok->astOperand1(), scope);
|
const Token* end = ValueFlow::getEndOfExprScope(tok->astOperand1(), scope);
|
||||||
|
|
||||||
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
|
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
|
||||||
rhs.errorPath.emplace_back(tok,
|
rhs.errorPath.emplace_back(tok,
|
||||||
|
@ -5306,6 +5305,8 @@ static const Token* isStrlenOf(const Token* tok, const Token* expr, int depth =
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val);
|
||||||
|
|
||||||
static void valueFlowSymbolicOperators(TokenList* tokenlist, SymbolDatabase* symboldatabase)
|
static void valueFlowSymbolicOperators(TokenList* tokenlist, SymbolDatabase* symboldatabase)
|
||||||
{
|
{
|
||||||
for (const Scope* scope : symboldatabase->functionScopes) {
|
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||||
|
@ -5530,7 +5531,7 @@ static void valueFlowForwardAssign(Token* const tok,
|
||||||
{
|
{
|
||||||
if (Token::simpleMatch(tok->astParent(), "return"))
|
if (Token::simpleMatch(tok->astParent(), "return"))
|
||||||
return;
|
return;
|
||||||
const Token* endOfVarScope = getEndOfExprScope(expr);
|
const Token* endOfVarScope = ValueFlow::getEndOfExprScope(expr);
|
||||||
if (std::any_of(values.cbegin(), values.cend(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
|
if (std::any_of(values.cbegin(), values.cend(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
|
||||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||||
values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue));
|
values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue));
|
||||||
|
@ -5872,7 +5873,7 @@ static bool isBreakScope(const Token* const endToken)
|
||||||
return Token::findmatch(endToken->link(), "break|goto", endToken);
|
return Token::findmatch(endToken->link(), "break|goto", endToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueFlow::Value asImpossible(ValueFlow::Value v)
|
ValueFlow::Value ValueFlow::asImpossible(ValueFlow::Value v)
|
||||||
{
|
{
|
||||||
v.invertRange();
|
v.invertRange();
|
||||||
v.setImpossible();
|
v.setImpossible();
|
||||||
|
@ -5881,7 +5882,7 @@ ValueFlow::Value asImpossible(ValueFlow::Value v)
|
||||||
|
|
||||||
static void insertImpossible(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
|
static void insertImpossible(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
|
||||||
{
|
{
|
||||||
std::transform(input.cbegin(), input.cend(), std::back_inserter(values), &asImpossible);
|
std::transform(input.cbegin(), input.cend(), std::back_inserter(values), &ValueFlow::asImpossible);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insertNegateKnown(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
|
static void insertNegateKnown(std::list<ValueFlow::Value>& values, const std::list<ValueFlow::Value>& input)
|
||||||
|
@ -6476,7 +6477,7 @@ struct ConditionHandler {
|
||||||
changeKnownToPossible(values);
|
changeKnownToPossible(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
forward(after, getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist);
|
forward(after, ValueFlow::getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6548,7 +6549,7 @@ struct IntegralInferModel : InferModel {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ValuePtr<InferModel> makeIntegralInferModel() {
|
ValuePtr<InferModel> ValueFlow::makeIntegralInferModel() {
|
||||||
return IntegralInferModel{};
|
return IntegralInferModel{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6564,18 +6565,6 @@ ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, Math
|
||||||
return ValueFlow::Value{};
|
return ValueFlow::Value{};
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueFlow::Value inferCondition(const std::string &op, MathLib::bigint val, const Token* varTok)
|
|
||||||
{
|
|
||||||
if (!varTok)
|
|
||||||
return ValueFlow::Value{};
|
|
||||||
if (varTok->hasKnownIntValue())
|
|
||||||
return ValueFlow::Value{};
|
|
||||||
std::vector<ValueFlow::Value> r = infer(IntegralInferModel{}, op, val, varTok->values());
|
|
||||||
if (r.size() == 1 && r.front().isKnown())
|
|
||||||
return r.front();
|
|
||||||
return ValueFlow::Value{};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IteratorInferModel : InferModel {
|
struct IteratorInferModel : InferModel {
|
||||||
virtual ValueFlow::Value::ValueType getType() const = 0;
|
virtual ValueFlow::Value::ValueType getType() const = 0;
|
||||||
bool match(const ValueFlow::Value& value) const override {
|
bool match(const ValueFlow::Value& value) const override {
|
||||||
|
@ -6944,14 +6933,14 @@ static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabas
|
||||||
if (executeBody && vartok) {
|
if (executeBody && vartok) {
|
||||||
std::list<ValueFlow::Value> initValues;
|
std::list<ValueFlow::Value> initValues;
|
||||||
initValues.emplace_back(initValue, ValueFlow::Value::Bound::Lower);
|
initValues.emplace_back(initValue, ValueFlow::Value::Bound::Lower);
|
||||||
initValues.push_back(asImpossible(initValues.back()));
|
initValues.push_back(ValueFlow::asImpossible(initValues.back()));
|
||||||
Analyzer::Result result = valueFlowForward(bodyStart, bodyStart->link(), vartok, initValues, tokenlist);
|
Analyzer::Result result = valueFlowForward(bodyStart, bodyStart->link(), vartok, initValues, tokenlist);
|
||||||
|
|
||||||
if (!result.action.isModified()) {
|
if (!result.action.isModified()) {
|
||||||
std::list<ValueFlow::Value> lastValues;
|
std::list<ValueFlow::Value> lastValues;
|
||||||
lastValues.emplace_back(lastValue, ValueFlow::Value::Bound::Upper);
|
lastValues.emplace_back(lastValue, ValueFlow::Value::Bound::Upper);
|
||||||
lastValues.back().conditional = true;
|
lastValues.back().conditional = true;
|
||||||
lastValues.push_back(asImpossible(lastValues.back()));
|
lastValues.push_back(ValueFlow::asImpossible(lastValues.back()));
|
||||||
if (stepValue != 1)
|
if (stepValue != 1)
|
||||||
lastValues.pop_front();
|
lastValues.pop_front();
|
||||||
valueFlowForward(bodyStart, bodyStart->link(), vartok, lastValues, tokenlist);
|
valueFlowForward(bodyStart, bodyStart->link(), vartok, lastValues, tokenlist);
|
||||||
|
@ -7838,7 +7827,7 @@ struct ContainerExpressionAnalyzer : ExpressionAnalyzer {
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
if (isLikelyStreamRead(isCPP(), tok->astParent()))
|
if (isLikelyStreamRead(isCPP(), tok->astParent()))
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
if (astIsContainer(tok) && isContainerSizeChanged(tok, getIndirect(tok), getSettings()))
|
if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings()))
|
||||||
return read | Action::Invalid;
|
return read | Action::Invalid;
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
@ -7871,9 +7860,9 @@ static const Token* parseBinaryIntOp(const Token* expr,
|
||||||
return varTok;
|
return varTok;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token* solveExprValue(const Token* expr,
|
const Token* ValueFlow::solveExprValue(const Token* expr,
|
||||||
const std::function<std::vector<MathLib::bigint>(const Token*)>& eval,
|
const std::function<std::vector<MathLib::bigint>(const Token*)>& eval,
|
||||||
ValueFlow::Value& value)
|
ValueFlow::Value& value)
|
||||||
{
|
{
|
||||||
if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue())
|
if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue())
|
||||||
return expr;
|
return expr;
|
||||||
|
@ -7889,24 +7878,24 @@ const Token* solveExprValue(const Token* expr,
|
||||||
switch (expr->str()[0]) {
|
switch (expr->str()[0]) {
|
||||||
case '+': {
|
case '+': {
|
||||||
value.intvalue -= intval;
|
value.intvalue -= intval;
|
||||||
return solveExprValue(binaryTok, eval, value);
|
return ValueFlow::solveExprValue(binaryTok, eval, value);
|
||||||
}
|
}
|
||||||
case '-': {
|
case '-': {
|
||||||
if (rhs)
|
if (rhs)
|
||||||
value.intvalue = intval - value.intvalue;
|
value.intvalue = intval - value.intvalue;
|
||||||
else
|
else
|
||||||
value.intvalue += intval;
|
value.intvalue += intval;
|
||||||
return solveExprValue(binaryTok, eval, value);
|
return ValueFlow::solveExprValue(binaryTok, eval, value);
|
||||||
}
|
}
|
||||||
case '*': {
|
case '*': {
|
||||||
if (intval == 0)
|
if (intval == 0)
|
||||||
break;
|
break;
|
||||||
value.intvalue /= intval;
|
value.intvalue /= intval;
|
||||||
return solveExprValue(binaryTok, eval, value);
|
return ValueFlow::solveExprValue(binaryTok, eval, value);
|
||||||
}
|
}
|
||||||
case '^': {
|
case '^': {
|
||||||
value.intvalue ^= intval;
|
value.intvalue ^= intval;
|
||||||
return solveExprValue(binaryTok, eval, value);
|
return ValueFlow::solveExprValue(binaryTok, eval, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7915,7 +7904,7 @@ const Token* solveExprValue(const Token* expr,
|
||||||
|
|
||||||
static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value)
|
static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value)
|
||||||
{
|
{
|
||||||
return solveExprValue(
|
return ValueFlow::solveExprValue(
|
||||||
expr,
|
expr,
|
||||||
[](const Token* tok) -> std::vector<MathLib::bigint> {
|
[](const Token* tok) -> std::vector<MathLib::bigint> {
|
||||||
if (tok->hasKnownIntValue())
|
if (tok->hasKnownIntValue())
|
||||||
|
@ -7940,7 +7929,7 @@ ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Va
|
||||||
return ExpressionAnalyzer(exprTok, value, tokenlist);
|
return ExpressionAnalyzer(exprTok, value, tokenlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings, int depth)
|
bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings, int depth)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return false;
|
return false;
|
||||||
|
@ -7991,7 +7980,7 @@ static bool isContainerSizeChanged(nonneg int varId,
|
||||||
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
||||||
if (tok->varId() != varId)
|
if (tok->varId() != varId)
|
||||||
continue;
|
continue;
|
||||||
if (isContainerSizeChanged(tok, indirect, settings, depth))
|
if (ValueFlow::isContainerSizeChanged(tok, indirect, settings, depth))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
147
lib/valueflow.h
147
lib/valueflow.h
|
@ -47,7 +47,7 @@ class ValuePtr;
|
||||||
|
|
||||||
namespace ValueFlow {
|
namespace ValueFlow {
|
||||||
/// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues).
|
/// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues).
|
||||||
const ValueFlow::Value * valueFlowConstantFoldAST(Token *expr, const Settings *settings);
|
const Value * valueFlowConstantFoldAST(Token *expr, const Settings *settings);
|
||||||
|
|
||||||
/// Perform valueflow analysis.
|
/// Perform valueflow analysis.
|
||||||
void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings);
|
void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings);
|
||||||
|
@ -56,82 +56,77 @@ namespace ValueFlow {
|
||||||
|
|
||||||
size_t getSizeOf(const ValueType &vt, const Settings *settings);
|
size_t getSizeOf(const ValueType &vt, const Settings *settings);
|
||||||
|
|
||||||
const ValueFlow::Value* findValue(const std::list<ValueFlow::Value>& values,
|
const Value* findValue(const std::list<Value>& values,
|
||||||
const Settings* settings,
|
const Settings* settings,
|
||||||
const std::function<bool(const ValueFlow::Value&)> &pred);
|
const std::function<bool(const Value&)> &pred);
|
||||||
|
|
||||||
std::vector<ValueFlow::Value> isOutOfBounds(const Value& size, const Token* indexTok, bool possible = true);
|
std::vector<Value> isOutOfBounds(const Value& size, const Token* indexTok, bool possible = true);
|
||||||
|
|
||||||
|
Value asImpossible(Value v);
|
||||||
|
|
||||||
|
bool isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings = nullptr, int depth = 20);
|
||||||
|
|
||||||
|
struct LifetimeToken {
|
||||||
|
const Token* token;
|
||||||
|
Value::ErrorPath errorPath;
|
||||||
|
bool addressOf;
|
||||||
|
bool inconclusive;
|
||||||
|
|
||||||
|
LifetimeToken() : token(nullptr), errorPath(), addressOf(false), inconclusive(false) {}
|
||||||
|
|
||||||
|
LifetimeToken(const Token* token, Value::ErrorPath errorPath)
|
||||||
|
: token(token), errorPath(std::move(errorPath)), addressOf(false), inconclusive(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LifetimeToken(const Token* token, bool addressOf, Value::ErrorPath errorPath)
|
||||||
|
: token(token), errorPath(std::move(errorPath)), addressOf(addressOf), inconclusive(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
static std::vector<LifetimeToken> setAddressOf(std::vector<LifetimeToken> v, bool b) {
|
||||||
|
for (LifetimeToken& x : v)
|
||||||
|
x.addressOf = b;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<LifetimeToken> setInconclusive(std::vector<LifetimeToken> v, bool b) {
|
||||||
|
for (LifetimeToken& x : v)
|
||||||
|
x.inconclusive = b;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value, const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate);
|
||||||
|
const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value);
|
||||||
|
|
||||||
|
CPPCHECKLIB ValuePtr<InferModel> makeIntegralInferModel();
|
||||||
|
|
||||||
|
const Token* solveExprValue(const Token* expr,
|
||||||
|
const std::function<std::vector<MathLib::bigint>(const Token*)>& eval,
|
||||||
|
Value& value);
|
||||||
|
|
||||||
|
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||||
|
bool escape = false,
|
||||||
|
Value::ErrorPath errorPath = Value::ErrorPath{});
|
||||||
|
|
||||||
|
bool hasLifetimeToken(const Token* tok, const Token* lifetime);
|
||||||
|
|
||||||
|
const Variable* getLifetimeVariable(const Token* tok, Value::ErrorPath& errorPath, bool* addressOf = nullptr);
|
||||||
|
|
||||||
|
const Variable* getLifetimeVariable(const Token* tok);
|
||||||
|
|
||||||
|
bool isLifetimeBorrowed(const Token *tok, const Settings *settings);
|
||||||
|
|
||||||
|
std::string lifetimeMessage(const Token *tok, const Value *val, Value::ErrorPath &errorPath);
|
||||||
|
|
||||||
|
CPPCHECKLIB Value getLifetimeObjValue(const Token *tok, bool inconclusive = false);
|
||||||
|
|
||||||
|
CPPCHECKLIB std::vector<Value> getLifetimeObjValues(const Token* tok,
|
||||||
|
bool inconclusive = false,
|
||||||
|
MathLib::bigint path = 0);
|
||||||
|
|
||||||
|
const Token* getEndOfExprScope(const Token* tok, const Scope* defaultScope = nullptr, bool smallest = true);
|
||||||
|
|
||||||
|
void combineValueProperties(const Value& value1, const Value& value2, Value* result);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueFlow::Value asImpossible(ValueFlow::Value v);
|
|
||||||
|
|
||||||
bool isContainerSizeChanged(const Token* tok, int indirect, const Settings* settings = nullptr, int depth = 20);
|
|
||||||
|
|
||||||
struct LifetimeToken {
|
|
||||||
const Token* token;
|
|
||||||
ValueFlow::Value::ErrorPath errorPath;
|
|
||||||
bool addressOf;
|
|
||||||
bool inconclusive;
|
|
||||||
|
|
||||||
LifetimeToken() : token(nullptr), errorPath(), addressOf(false), inconclusive(false) {}
|
|
||||||
|
|
||||||
LifetimeToken(const Token* token, ValueFlow::Value::ErrorPath errorPath)
|
|
||||||
: token(token), errorPath(std::move(errorPath)), addressOf(false), inconclusive(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
LifetimeToken(const Token* token, bool addressOf, ValueFlow::Value::ErrorPath errorPath)
|
|
||||||
: token(token), errorPath(std::move(errorPath)), addressOf(addressOf), inconclusive(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static std::vector<LifetimeToken> setAddressOf(std::vector<LifetimeToken> v, bool b) {
|
|
||||||
for (LifetimeToken& x : v)
|
|
||||||
x.addressOf = b;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<LifetimeToken> setInconclusive(std::vector<LifetimeToken> v, bool b) {
|
|
||||||
for (LifetimeToken& x : v)
|
|
||||||
x.inconclusive = b;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value, const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate);
|
|
||||||
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value);
|
|
||||||
|
|
||||||
ValueFlow::Value inferCondition(const std::string& op, MathLib::bigint val, const Token* varTok);
|
|
||||||
ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val);
|
|
||||||
|
|
||||||
CPPCHECKLIB ValuePtr<InferModel> makeIntegralInferModel();
|
|
||||||
|
|
||||||
const Token* solveExprValue(const Token* expr,
|
|
||||||
const std::function<std::vector<MathLib::bigint>(const Token*)>& eval,
|
|
||||||
ValueFlow::Value& value);
|
|
||||||
|
|
||||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
|
||||||
bool escape = false,
|
|
||||||
ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{});
|
|
||||||
|
|
||||||
bool hasLifetimeToken(const Token* tok, const Token* lifetime);
|
|
||||||
|
|
||||||
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);
|
|
||||||
|
|
||||||
const Variable* getLifetimeVariable(const Token* tok);
|
|
||||||
|
|
||||||
bool isLifetimeBorrowed(const Token *tok, const Settings *settings);
|
|
||||||
|
|
||||||
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val);
|
|
||||||
|
|
||||||
std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ValueFlow::Value::ErrorPath &errorPath);
|
|
||||||
|
|
||||||
CPPCHECKLIB ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive = false);
|
|
||||||
|
|
||||||
CPPCHECKLIB std::vector<ValueFlow::Value> getLifetimeObjValues(const Token* tok,
|
|
||||||
bool inconclusive = false,
|
|
||||||
MathLib::bigint path = 0);
|
|
||||||
|
|
||||||
const Token* getEndOfExprScope(const Token* tok, const Scope* defaultScope = nullptr, bool smallest = true);
|
|
||||||
|
|
||||||
void combineValueProperties(const ValueFlow::Value& value1, const ValueFlow::Value& value2, ValueFlow::Value* result);
|
|
||||||
|
|
||||||
#endif // valueflowH
|
#endif // valueflowH
|
||||||
|
|
Loading…
Reference in New Issue