Fix FP when copying pointer to string (#1479)
This commit is contained in:
parent
ac242b69d6
commit
54453c5802
|
@ -98,7 +98,7 @@ static bool match(const Token *tok, const std::string &rhs)
|
|||
{
|
||||
if (tok->str() == rhs)
|
||||
return true;
|
||||
if (tok->isName() && !tok->varId() && tok->values().size() == 1U && tok->values().front().isKnown() && MathLib::toString(tok->values().front().intvalue) == rhs)
|
||||
if (tok->isName() && !tok->varId() && tok->hasKnownIntValue() && MathLib::toString(tok->values().front().intvalue) == rhs)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -593,6 +593,9 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
for (const ValueFlow::Value& val:tok->values()) {
|
||||
if (!val.isLifetimeValue())
|
||||
continue;
|
||||
// Skip temporaries for now
|
||||
if (val.tokvalue == tok)
|
||||
continue;
|
||||
if (Token::Match(tok->astParent(), "return|throw")) {
|
||||
if (isInScope(val.tokvalue, scope)) {
|
||||
errorReturnDanglingLifetime(tok, &val);
|
||||
|
|
|
@ -285,8 +285,8 @@ void CheckCondition::checkBadBitmaskCheck()
|
|||
(parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) ||
|
||||
(parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok));
|
||||
|
||||
const bool isTrue = (tok->astOperand1()->values().size() == 1 && tok->astOperand1()->values().front().intvalue != 0 && tok->astOperand1()->values().front().isKnown()) ||
|
||||
(tok->astOperand2()->values().size() == 1 && tok->astOperand2()->values().front().intvalue != 0 && tok->astOperand2()->values().front().isKnown());
|
||||
const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) ||
|
||||
(tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0);
|
||||
|
||||
if (isBoolean && isTrue)
|
||||
badBitmaskCheckError(tok);
|
||||
|
@ -441,8 +441,8 @@ void CheckCondition::multiCondition()
|
|||
|
||||
if (cond1 &&
|
||||
tok2->astOperand2() &&
|
||||
!cond1->hasKnownValue() &&
|
||||
!tok2->astOperand2()->hasKnownValue() &&
|
||||
!cond1->hasKnownIntValue() &&
|
||||
!tok2->astOperand2()->hasKnownIntValue() &&
|
||||
isOverlappingCond(cond1, tok2->astOperand2(), true))
|
||||
multiConditionError(tok2, cond1->linenr());
|
||||
}
|
||||
|
@ -601,7 +601,7 @@ void CheckCondition::multiCondition2()
|
|||
if (firstCondition->str() == "&&") {
|
||||
tokens1.push(firstCondition->astOperand1());
|
||||
tokens1.push(firstCondition->astOperand2());
|
||||
} else if (!firstCondition->hasKnownValue()) {
|
||||
} else if (!firstCondition->hasKnownIntValue()) {
|
||||
if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) {
|
||||
if (!isAliased(vars))
|
||||
oppositeInnerConditionError(firstCondition, cond2, errorPath);
|
||||
|
@ -621,7 +621,7 @@ void CheckCondition::multiCondition2()
|
|||
if (secondCondition->str() == "||" || secondCondition->str() == "&&") {
|
||||
tokens2.push(secondCondition->astOperand1());
|
||||
tokens2.push(secondCondition->astOperand2());
|
||||
} else if ((!cond1->hasKnownValue() || !secondCondition->hasKnownValue()) &&
|
||||
} else if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) &&
|
||||
isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) {
|
||||
if (!isAliased(vars))
|
||||
identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath);
|
||||
|
|
|
@ -492,8 +492,7 @@ static bool alwaysTrue(const Token *tok)
|
|||
{
|
||||
if (!tok)
|
||||
return false;
|
||||
if (tok->values().size() == 1U &&
|
||||
tok->values().front().isKnown() &&
|
||||
if (tok->hasKnownIntValue() &&
|
||||
tok->values().front().intvalue != 0)
|
||||
return true;
|
||||
if (tok->str() == "||")
|
||||
|
|
|
@ -225,7 +225,7 @@ static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map<unsigned
|
|||
}
|
||||
|
||||
else if (tok->isComparisonOp()) {
|
||||
if (tok->values().size() == 1U && tok->values().front().isKnown()) {
|
||||
if (tok->hasKnownIntValue()) {
|
||||
if (tok->values().front().intvalue)
|
||||
*alwaysTrue = true;
|
||||
else
|
||||
|
|
|
@ -1231,7 +1231,7 @@ void SymbolDatabase::createSymbolDatabaseEnums()
|
|||
ValueFlow::valueFlowConstantFoldAST(rhs, mSettings);
|
||||
|
||||
// get constant folded value:
|
||||
if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) {
|
||||
if (rhs && rhs->hasKnownIntValue()) {
|
||||
enumerator.value = rhs->values().front().intvalue;
|
||||
enumerator.value_known = true;
|
||||
value = enumerator.value + 1;
|
||||
|
@ -1334,7 +1334,7 @@ void SymbolDatabase::createSymbolDatabaseUnknownArrayDimensions()
|
|||
ValueFlow::valueFlowConstantFoldAST(rhs, mSettings);
|
||||
|
||||
// get constant folded value:
|
||||
if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) {
|
||||
if (rhs && rhs->hasKnownIntValue()) {
|
||||
dimension.num = rhs->values().front().intvalue;
|
||||
dimension.known = true;
|
||||
}
|
||||
|
|
|
@ -1640,8 +1640,10 @@ const Token *Token::getValueTokenDeadPointer() const
|
|||
bool Token::addValue(const ValueFlow::Value &value)
|
||||
{
|
||||
if (value.isKnown() && mValues) {
|
||||
// Clear all other values since value is known
|
||||
mValues->clear();
|
||||
// Clear all other values of the same type since value is known
|
||||
mValues->remove_if([&](const ValueFlow::Value & x) {
|
||||
return x.valueType == value.valueType;
|
||||
});
|
||||
}
|
||||
|
||||
if (mValues) {
|
||||
|
@ -1660,7 +1662,7 @@ bool Token::addValue(const ValueFlow::Value &value)
|
|||
// different types => continue
|
||||
if (it->valueType != value.valueType)
|
||||
continue;
|
||||
if (value.isTokValue() && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
||||
if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
||||
continue;
|
||||
|
||||
// same value, but old value is inconclusive so replace it
|
||||
|
@ -1680,7 +1682,10 @@ bool Token::addValue(const ValueFlow::Value &value)
|
|||
ValueFlow::Value v(value);
|
||||
if (v.varId == 0)
|
||||
v.varId = mVarId;
|
||||
mValues->push_back(v);
|
||||
if(v.isKnown() && v.isIntValue())
|
||||
mValues->push_front(v);
|
||||
else
|
||||
mValues->push_back(v);
|
||||
}
|
||||
} else {
|
||||
ValueFlow::Value v(value);
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#include "mathlib.h"
|
||||
#include "valueflow.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
@ -838,11 +840,11 @@ public:
|
|||
}
|
||||
|
||||
bool hasKnownIntValue() const {
|
||||
return mValues && mValues->size() == 1U && mValues->front().isKnown() && mValues->front().isIntValue();
|
||||
return hasKnownValue() && std::any_of(mValues->begin(), mValues->end(), std::mem_fn(&ValueFlow::Value::isIntValue));
|
||||
}
|
||||
|
||||
bool hasKnownValue() const {
|
||||
return mValues && mValues->size() == 1U && mValues->front().isKnown();
|
||||
return mValues && std::any_of(mValues->begin(), mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown));
|
||||
}
|
||||
|
||||
const ValueFlow::Value * getValue(const MathLib::bigint val) const {
|
||||
|
|
|
@ -2468,6 +2468,240 @@ static bool valueFlowForward(Token * const startToken,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool isNotLifetimeValue(const ValueFlow::Value& val)
|
||||
{
|
||||
return !val.isLifetimeValue();
|
||||
}
|
||||
|
||||
static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
||||
{
|
||||
const Token * assignTok = tok->astParent();
|
||||
// Assignment
|
||||
if (!assignTok || (assignTok->str() != "=") || assignTok->astParent())
|
||||
return;
|
||||
|
||||
// Lhs should be a variable
|
||||
if (!assignTok->astOperand1() || !assignTok->astOperand1()->varId())
|
||||
return;
|
||||
const Variable *var = assignTok->astOperand1()->variable();
|
||||
if (!var || (!var->isLocal() && !var->isGlobal() && !var->isArgument()))
|
||||
return;
|
||||
|
||||
const Token * const endOfVarScope = var->typeStartToken()->scope()->bodyEnd;
|
||||
|
||||
// Rhs values..
|
||||
if (!assignTok->astOperand2() || assignTok->astOperand2()->values().empty())
|
||||
return;
|
||||
|
||||
if(astIsPointer(assignTok->astOperand2()) && !var->isPointer() && !(var->valueType() && var->valueType()->isIntegral()))
|
||||
return;
|
||||
|
||||
std::list<ValueFlow::Value> values = assignTok->astOperand2()->values();
|
||||
|
||||
// Static variable initialisation?
|
||||
if (var->isStatic() && var->nameToken() == assignTok->astOperand1())
|
||||
changeKnownToPossible(values);
|
||||
|
||||
// Skip RHS
|
||||
const Token * nextExpression = nextAfterAstRightmostLeaf(assignTok);
|
||||
|
||||
// Only forward lifetime values
|
||||
values.remove_if(&isNotLifetimeValue);
|
||||
valueFlowForward(const_cast<Token *>(nextExpression), endOfVarScope, var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings);
|
||||
}
|
||||
|
||||
static const Variable * getLifetimeVariable(const Token * tok, ErrorPath& errorPath)
|
||||
{
|
||||
const Variable * var = tok->variable();
|
||||
if (!var)
|
||||
return nullptr;
|
||||
if (var->isReference() || var->isRValueReference()) {
|
||||
for (const ValueFlow::Value& v:tok->values()) {
|
||||
if (!v.isLifetimeValue())
|
||||
continue;
|
||||
if (v.tokvalue == tok)
|
||||
continue;
|
||||
errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end());
|
||||
const Variable * var2 = getLifetimeVariable(v.tokvalue, errorPath);
|
||||
if (var2)
|
||||
return var2;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
struct Lambda {
|
||||
explicit Lambda(const Token * tok)
|
||||
: capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const Token * capture;
|
||||
const Token * arguments;
|
||||
const Token * returnTok;
|
||||
const Token * bodyTok;
|
||||
|
||||
bool isLambda() const {
|
||||
return capture && bodyTok;
|
||||
}
|
||||
};
|
||||
|
||||
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
|
||||
{
|
||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||
Lambda lam(tok);
|
||||
// Lamdas
|
||||
if (lam.isLambda()) {
|
||||
const Scope * bodyScope = lam.bodyTok->scope();
|
||||
|
||||
std::set<const Scope *> scopes;
|
||||
|
||||
// TODO: Handle explicit capture
|
||||
bool captureByRef = Token::Match(lam.capture, "[ & ]");
|
||||
bool captureByValue = Token::Match(lam.capture, "[ = ]");
|
||||
|
||||
for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) {
|
||||
ErrorPath errorPath;
|
||||
if (captureByRef) {
|
||||
const Variable * var = getLifetimeVariable(tok2, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
const Scope * scope = var->scope();
|
||||
if (scopes.count(scope) > 0)
|
||||
continue;
|
||||
if (scope->isNestedIn(bodyScope))
|
||||
continue;
|
||||
scopes.insert(scope);
|
||||
errorPath.emplace_back(tok2, "Lambda captures variable by reference here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = ValueFlow::Value::Lambda;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
} else if (captureByValue) {
|
||||
for (const ValueFlow::Value& v:tok2->values()) {
|
||||
if (!v.isLifetimeValue() && !v.tokvalue)
|
||||
continue;
|
||||
const Token * tok3 = v.tokvalue;
|
||||
errorPath = v.errorPath;
|
||||
const Variable * var = getLifetimeVariable(tok3, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
const Scope * scope = var->scope();
|
||||
if (scopes.count(scope) > 0)
|
||||
continue;
|
||||
if (scope->isNestedIn(bodyScope))
|
||||
continue;
|
||||
scopes.insert(scope);
|
||||
errorPath.emplace_back(tok2, "Lambda captures variable by value here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = ValueFlow::Value::Lambda;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// address of
|
||||
else if (tok->isUnaryOp("&")) {
|
||||
ErrorPath errorPath;
|
||||
// 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)
|
||||
continue;
|
||||
const Variable * var = getLifetimeVariable(vartok, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isPointer() && Token::Match(vartok->astParent(), "[|*"))
|
||||
continue;
|
||||
|
||||
errorPath.emplace_back(tok, "Address of variable taken here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
// container lifetimes
|
||||
else if (tok->variable() && Token::Match(tok, "%var% . begin|cbegin|rbegin|crbegin|end|cend|rend|crend|data|c_str (")) {
|
||||
ErrorPath errorPath;
|
||||
const Library::Container * container = settings->library.detectContainer(tok->variable()->typeStartToken());
|
||||
if (!container)
|
||||
continue;
|
||||
const Variable * var = tok->variable();
|
||||
|
||||
bool isIterator = !Token::Match(tok->tokAt(2), "data|c_str");
|
||||
if (isIterator)
|
||||
errorPath.emplace_back(tok, "Iterator to container is created here.");
|
||||
else
|
||||
errorPath.emplace_back(tok, "Pointer to container is created here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = isIterator ? ValueFlow::Value::Iterator : ValueFlow::Value::Object;
|
||||
setTokenValue(tok->tokAt(3), value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok->tokAt(3), tokenlist, errorLogger, settings);
|
||||
|
||||
}
|
||||
// Check variables
|
||||
else if (tok->variable()) {
|
||||
ErrorPath errorPath;
|
||||
const Variable * var = getLifetimeVariable(tok, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isArray() && tok->astParent() && (astIsPointer(tok->astParent()) || Token::Match(tok->astParent(), "%assign%|return"))) {
|
||||
errorPath.emplace_back(tok, "Array decayed to pointer here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr)
|
||||
{
|
||||
if (tok->str() != "std")
|
||||
|
@ -2629,6 +2863,12 @@ static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldat
|
|||
continue;
|
||||
|
||||
std::list<ValueFlow::Value> values = tok->astOperand2()->values();
|
||||
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(!var->isPointer())
|
||||
values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue));
|
||||
for (std::list<ValueFlow::Value>::iterator it = values.begin(); it != values.end(); ++it) {
|
||||
const std::string info = "Assignment '" + tok->expressionString() + "', assigned value is " + it->infoString();
|
||||
it->errorPath.emplace_back(tok->astOperand2(), info);
|
||||
|
@ -2913,237 +3153,6 @@ static void valueFlowAfterCondition(TokenList *tokenlist, SymbolDatabase* symbol
|
|||
}
|
||||
}
|
||||
|
||||
static bool isNotLifetimeValue(const ValueFlow::Value& val)
|
||||
{
|
||||
return !val.isLifetimeValue();
|
||||
}
|
||||
|
||||
static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
||||
{
|
||||
const Token * assignTok = tok->astParent();
|
||||
// Assignment
|
||||
if (!assignTok || (assignTok->str() != "=") || assignTok->astParent())
|
||||
return;
|
||||
|
||||
// Lhs should be a variable
|
||||
if (!assignTok->astOperand1() || !assignTok->astOperand1()->varId())
|
||||
return;
|
||||
const Variable *var = assignTok->astOperand1()->variable();
|
||||
if (!var || (!var->isLocal() && !var->isGlobal() && !var->isArgument()))
|
||||
return;
|
||||
|
||||
const Token * const endOfVarScope = var->typeStartToken()->scope()->bodyEnd;
|
||||
|
||||
// Rhs values..
|
||||
if (!assignTok->astOperand2() || assignTok->astOperand2()->values().empty())
|
||||
return;
|
||||
|
||||
std::list<ValueFlow::Value> values = assignTok->astOperand2()->values();
|
||||
|
||||
// Static variable initialisation?
|
||||
if (var->isStatic() && var->nameToken() == assignTok->astOperand1())
|
||||
changeKnownToPossible(values);
|
||||
|
||||
// Skip RHS
|
||||
const Token * nextExpression = nextAfterAstRightmostLeaf(assignTok);
|
||||
|
||||
// Only forward lifetime values
|
||||
values.remove_if(&isNotLifetimeValue);
|
||||
valueFlowForward(const_cast<Token *>(nextExpression), endOfVarScope, var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings);
|
||||
}
|
||||
|
||||
static const Variable * getLifetimeVariable(const Token * tok, ErrorPath& errorPath)
|
||||
{
|
||||
const Variable * var = tok->variable();
|
||||
if (!var)
|
||||
return nullptr;
|
||||
if (var->isReference() || var->isRValueReference()) {
|
||||
for (const ValueFlow::Value& v:tok->values()) {
|
||||
if (!v.isLifetimeValue() && !v.tokvalue)
|
||||
continue;
|
||||
if (v.tokvalue == tok)
|
||||
continue;
|
||||
errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end());
|
||||
const Variable * var2 = getLifetimeVariable(v.tokvalue, errorPath);
|
||||
if (var2)
|
||||
return var2;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
struct Lambda {
|
||||
explicit Lambda(const Token * tok)
|
||||
: capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const Token * capture;
|
||||
const Token * arguments;
|
||||
const Token * returnTok;
|
||||
const Token * bodyTok;
|
||||
|
||||
bool isLambda() const {
|
||||
return capture && bodyTok;
|
||||
}
|
||||
};
|
||||
|
||||
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
|
||||
{
|
||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||
Lambda lam(tok);
|
||||
// Lamdas
|
||||
if (lam.isLambda()) {
|
||||
const Scope * bodyScope = lam.bodyTok->scope();
|
||||
|
||||
std::set<const Scope *> scopes;
|
||||
|
||||
// TODO: Handle explicit capture
|
||||
bool captureByRef = Token::Match(lam.capture, "[ & ]");
|
||||
bool captureByValue = Token::Match(lam.capture, "[ = ]");
|
||||
|
||||
for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) {
|
||||
ErrorPath errorPath;
|
||||
if (captureByRef) {
|
||||
const Variable * var = getLifetimeVariable(tok2, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
const Scope * scope = var->scope();
|
||||
if (scopes.count(scope) > 0)
|
||||
continue;
|
||||
if (scope->isNestedIn(bodyScope))
|
||||
continue;
|
||||
scopes.insert(scope);
|
||||
errorPath.emplace_back(tok2, "Lambda captures variable by reference here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = ValueFlow::Value::Lambda;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
} else if (captureByValue) {
|
||||
for (const ValueFlow::Value& v:tok2->values()) {
|
||||
if (!v.isLifetimeValue() && !v.tokvalue)
|
||||
continue;
|
||||
const Token * tok3 = v.tokvalue;
|
||||
errorPath = v.errorPath;
|
||||
const Variable * var = getLifetimeVariable(tok3, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
const Scope * scope = var->scope();
|
||||
if (scopes.count(scope) > 0)
|
||||
continue;
|
||||
if (scope->isNestedIn(bodyScope))
|
||||
continue;
|
||||
scopes.insert(scope);
|
||||
errorPath.emplace_back(tok2, "Lambda captures variable by value here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = ValueFlow::Value::Lambda;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// address of
|
||||
else if (tok->isUnaryOp("&")) {
|
||||
ErrorPath errorPath;
|
||||
// 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)
|
||||
continue;
|
||||
const Variable * var = getLifetimeVariable(vartok, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isPointer() && Token::Match(vartok->astParent(), "[|*"))
|
||||
continue;
|
||||
|
||||
errorPath.emplace_back(tok, "Address of variable taken here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
// container lifetimes
|
||||
else if (tok->variable() && Token::Match(tok, "%var% . begin|cbegin|rbegin|crbegin|end|cend|rend|crend|data|c_str (")) {
|
||||
ErrorPath errorPath;
|
||||
const Library::Container * container = settings->library.detectContainer(tok->variable()->typeStartToken());
|
||||
if (!container)
|
||||
continue;
|
||||
const Variable * var = tok->variable();
|
||||
|
||||
bool isIterator = !Token::Match(tok->tokAt(2), "data|c_str");
|
||||
if (isIterator)
|
||||
errorPath.emplace_back(tok, "Iterator to container is created here.");
|
||||
else
|
||||
errorPath.emplace_back(tok, "Pointer to container is created here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
value.lifetimeKind = isIterator ? ValueFlow::Value::Iterator : ValueFlow::Value::Object;
|
||||
setTokenValue(tok->tokAt(3), value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok->tokAt(3), tokenlist, errorLogger, settings);
|
||||
|
||||
}
|
||||
// Check variables
|
||||
else if (tok->variable()) {
|
||||
ErrorPath errorPath;
|
||||
const Variable * var = getLifetimeVariable(tok, errorPath);
|
||||
if (!var)
|
||||
continue;
|
||||
if (var->isArray() && tok->astParent() && (astIsPointer(tok->astParent()) || Token::Match(tok->astParent(), "%assign%|return"))) {
|
||||
errorPath.emplace_back(tok, "Array decayed to pointer here.");
|
||||
|
||||
ValueFlow::Value value;
|
||||
value.valueType = ValueFlow::Value::LIFETIME;
|
||||
value.tokvalue = var->nameToken();
|
||||
value.errorPath = errorPath;
|
||||
setTokenValue(tok, value, tokenlist->getSettings());
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void execute(const Token *expr,
|
||||
ProgramMemory * const programMemory,
|
||||
MathLib::bigint *result,
|
||||
|
|
|
@ -714,7 +714,7 @@ private:
|
|||
" char *p = str;\n"
|
||||
" return p;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str());
|
||||
|
||||
check("class Fred {\n"
|
||||
" char *foo();\n"
|
||||
|
@ -1431,6 +1431,16 @@ private:
|
|||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f(bool b) {\n"
|
||||
" std::string s;\n"
|
||||
" if(b) {\n"
|
||||
" char buf[3];\n"
|
||||
" s = buf;\n"
|
||||
" }\n"
|
||||
" std::cout << s;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("int &a[];\n"
|
||||
"void b(){int *c = a};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
|
Loading…
Reference in New Issue