Fix issue 9711: FP knownConditionTrueFalse for variable modified via pointer (#2813)
This commit is contained in:
parent
9f690fb478
commit
857722f859
|
@ -523,17 +523,22 @@ bool precedes(const Token * tok1, const Token * tok2)
|
||||||
return tok1->index() < tok2->index();
|
return tok1->index() < tok2->index();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAliasOf(const Token *tok, nonneg int varid)
|
bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive)
|
||||||
{
|
{
|
||||||
if (tok->varId() == varid)
|
if (tok->varId() == varid)
|
||||||
return false;
|
return false;
|
||||||
for (const ValueFlow::Value &val : tok->values()) {
|
for (const ValueFlow::Value &val : tok->values()) {
|
||||||
if (!val.isLocalLifetimeValue())
|
if (!val.isLocalLifetimeValue())
|
||||||
continue;
|
continue;
|
||||||
if (val.isInconclusive())
|
if (val.tokvalue->varId() == varid) {
|
||||||
continue;
|
if (val.isInconclusive()) {
|
||||||
if (val.tokvalue->varId() == varid)
|
if (inconclusive)
|
||||||
|
*inconclusive = true;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ const Token* findVariableChanged(const Token *start, const Token *end, int indir
|
||||||
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
|
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
|
||||||
|
|
||||||
/// If token is an alias if another variable
|
/// If token is an alias if another variable
|
||||||
bool isAliasOf(const Token *tok, nonneg int varid);
|
bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr);
|
||||||
|
|
||||||
bool isAliased(const Variable *var);
|
bool isAliased(const Variable *var);
|
||||||
|
|
||||||
|
|
|
@ -494,6 +494,7 @@ static bool isDanglingSubFunction(const Token* tokvalue, const Token* tok)
|
||||||
|
|
||||||
void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end)
|
void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end)
|
||||||
{
|
{
|
||||||
|
const bool printInconclusive = (mSettings->inconclusive);
|
||||||
if (!start)
|
if (!start)
|
||||||
return;
|
return;
|
||||||
const Scope * scope = start->scope();
|
const Scope * scope = start->scope();
|
||||||
|
@ -507,6 +508,8 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
// 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 LifetimeToken& lt : getLifetimeTokens(tok, true)) {
|
||||||
|
if (!printInconclusive && lt.inconclusive)
|
||||||
|
continue;
|
||||||
const Variable* var = lt.token->variable();
|
const Variable* var = lt.token->variable();
|
||||||
if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() &&
|
if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() &&
|
||||||
isInScope(var->nameToken(), tok->scope())) {
|
isInScope(var->nameToken(), tok->scope())) {
|
||||||
|
@ -531,6 +534,8 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
// 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 LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) {
|
||||||
|
if (!printInconclusive && lt.inconclusive)
|
||||||
|
continue;
|
||||||
const Token * tokvalue = lt.token;
|
const Token * tokvalue = lt.token;
|
||||||
if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
|
if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
|
||||||
errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive);
|
errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive);
|
||||||
|
@ -542,6 +547,8 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
||||||
for (const ValueFlow::Value& val:tok->values()) {
|
for (const ValueFlow::Value& val:tok->values()) {
|
||||||
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
|
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
|
||||||
continue;
|
continue;
|
||||||
|
if (!printInconclusive && val.isInconclusive())
|
||||||
|
continue;
|
||||||
const bool escape = Token::Match(tok->astParent(), "return|throw");
|
const bool escape = Token::Match(tok->astParent(), "return|throw");
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(val.tokvalue), escape)) {
|
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(val.tokvalue), escape)) {
|
||||||
const Token * tokvalue = lt.token;
|
const Token * tokvalue = lt.token;
|
||||||
|
|
|
@ -2193,42 +2193,13 @@ static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if its an alias of the variable or is being aliased to this variable
|
// Check if its an alias of the variable or is being aliased to this variable
|
||||||
static bool isAliasOf(const Variable* var, const Token* tok, nonneg int varid, const ValueFlow::Value& val)
|
static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const std::list<ValueFlow::Value>& values, bool* inconclusive = nullptr)
|
||||||
{
|
{
|
||||||
if (tok->varId() == varid)
|
if (tok->varId() == varid)
|
||||||
return false;
|
return false;
|
||||||
if (tok->varId() == 0)
|
if (tok->varId() == 0)
|
||||||
return false;
|
return false;
|
||||||
if (isAliasOf(tok, varid))
|
if (isAliasOf(tok, varid, inconclusive))
|
||||||
return true;
|
|
||||||
if (var && !var->isPointer())
|
|
||||||
return false;
|
|
||||||
// Search through non value aliases
|
|
||||||
|
|
||||||
if (!val.isNonValue())
|
|
||||||
return false;
|
|
||||||
if (val.isInconclusive())
|
|
||||||
return false;
|
|
||||||
if (val.isLifetimeValue() && !val.isLocalLifetimeValue())
|
|
||||||
return false;
|
|
||||||
if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
|
|
||||||
return false;
|
|
||||||
if (!Token::Match(val.tokvalue, ".|&|*|%var%"))
|
|
||||||
return false;
|
|
||||||
if (astHasVar(val.tokvalue, tok->varId()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if its an alias of the variable or is being aliased to this variable
|
|
||||||
static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const std::list<ValueFlow::Value>& values)
|
|
||||||
{
|
|
||||||
if (tok->varId() == varid)
|
|
||||||
return false;
|
|
||||||
if (tok->varId() == 0)
|
|
||||||
return false;
|
|
||||||
if (isAliasOf(tok, varid))
|
|
||||||
return true;
|
return true;
|
||||||
if (var && !var->isPointer())
|
if (var && !var->isPointer())
|
||||||
return false;
|
return false;
|
||||||
|
@ -2328,7 +2299,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
|
|
||||||
virtual bool match(const Token* tok) const = 0;
|
virtual bool match(const Token* tok) const = 0;
|
||||||
|
|
||||||
virtual bool isAlias(const Token* tok) const = 0;
|
virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0;
|
||||||
|
|
||||||
using ProgramState = std::unordered_map<nonneg int, ValueFlow::Value>;
|
using ProgramState = std::unordered_map<nonneg int, ValueFlow::Value>;
|
||||||
|
|
||||||
|
@ -2448,6 +2419,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
virtual Action analyze(const Token* tok) const OVERRIDE {
|
virtual Action analyze(const Token* tok) const OVERRIDE {
|
||||||
if (invalid())
|
if (invalid())
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
|
bool inconclusive = false;
|
||||||
if (match(tok)) {
|
if (match(tok)) {
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
||||||
|
@ -2479,8 +2451,12 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
}
|
}
|
||||||
return Action::None;
|
return Action::None;
|
||||||
|
|
||||||
} else if (isAlias(tok)) {
|
} else if (isAlias(tok, inconclusive)) {
|
||||||
return isAliasModified(tok);
|
Action a = isAliasModified(tok);
|
||||||
|
if (inconclusive && a.isModified())
|
||||||
|
return Action::Inconclusive;
|
||||||
|
else
|
||||||
|
return a;
|
||||||
} else if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
|
} else if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
|
||||||
// bailout: global non-const variables
|
// bailout: global non-const variables
|
||||||
if (isGlobal()) {
|
if (isGlobal()) {
|
||||||
|
@ -2568,7 +2544,7 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
value.errorPath.emplace_back(tok, s);
|
value.errorPath.emplace_back(tok, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isAlias(const Token* tok) const OVERRIDE {
|
virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE {
|
||||||
if (value.isLifetimeValue())
|
if (value.isLifetimeValue())
|
||||||
return false;
|
return false;
|
||||||
for (const auto& m: {
|
for (const auto& m: {
|
||||||
|
@ -2579,7 +2555,7 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
const Variable* var = p.second;
|
const Variable* var = p.second;
|
||||||
if (tok->varId() == varid)
|
if (tok->varId() == varid)
|
||||||
return true;
|
return true;
|
||||||
if (isAliasOf(var, tok, varid, value))
|
if (isAliasOf(var, tok, varid, {value}, &inconclusive))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3293,8 +3269,32 @@ static void valueFlowLifetimeConstructor(Token *tok,
|
||||||
ErrorLogger *errorLogger,
|
ErrorLogger *errorLogger,
|
||||||
const Settings *settings);
|
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())
|
||||||
|
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)
|
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();
|
Token *parent = tok->astParent();
|
||||||
while (parent && (parent->isArithmeticalOp() || parent->str() == ","))
|
while (parent && (parent->isArithmeticalOp() || parent->str() == ","))
|
||||||
parent = parent->astParent();
|
parent = parent->astParent();
|
||||||
|
@ -3311,13 +3311,7 @@ static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLog
|
||||||
|
|
||||||
std::vector<const Variable*> vars = getLHSVariables(parent);
|
std::vector<const Variable*> vars = getLHSVariables(parent);
|
||||||
|
|
||||||
const Token* endOfVarScope = nullptr;
|
const Token* endOfVarScope = getEndOfVarScope(tok, vars);
|
||||||
for (const Variable* var : vars) {
|
|
||||||
if (var && var->isLocal())
|
|
||||||
endOfVarScope = var->typeStartToken()->scope()->bodyEnd;
|
|
||||||
else if (!endOfVarScope)
|
|
||||||
endOfVarScope = tok->scope()->bodyEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only forward lifetime values
|
// Only forward lifetime values
|
||||||
std::list<ValueFlow::Value> values = parent->astOperand2()->values();
|
std::list<ValueFlow::Value> values = parent->astOperand2()->values();
|
||||||
|
@ -3427,8 +3421,6 @@ struct LifetimeStore {
|
||||||
|
|
||||||
template <class Predicate>
|
template <class Predicate>
|
||||||
void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const {
|
void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const {
|
||||||
if (!settings->inconclusive && inconclusive)
|
|
||||||
return;
|
|
||||||
if (!argtok)
|
if (!argtok)
|
||||||
return;
|
return;
|
||||||
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
|
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
|
||||||
|
@ -3465,8 +3457,6 @@ struct LifetimeStore {
|
||||||
|
|
||||||
template <class Predicate>
|
template <class Predicate>
|
||||||
void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const {
|
void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const {
|
||||||
if (!settings->inconclusive && inconclusive)
|
|
||||||
return;
|
|
||||||
if (!argtok)
|
if (!argtok)
|
||||||
return;
|
return;
|
||||||
if (argtok->values().empty()) {
|
if (argtok->values().empty()) {
|
||||||
|
@ -3647,10 +3637,21 @@ static void valueFlowLifetimeConstructor(Token* tok,
|
||||||
ErrorLogger* errorLogger,
|
ErrorLogger* errorLogger,
|
||||||
const Settings* settings)
|
const Settings* settings)
|
||||||
{
|
{
|
||||||
if (!t)
|
|
||||||
return;
|
|
||||||
if (!Token::Match(tok, "(|{"))
|
if (!Token::Match(tok, "(|{"))
|
||||||
return;
|
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);
|
||||||
|
for (const Token *argtok : args) {
|
||||||
|
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object};
|
||||||
|
ls.inconclusive = true;
|
||||||
|
ls.byVal(tok, tokenlist, errorLogger, settings);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const Scope* scope = t->classScope;
|
const Scope* scope = t->classScope;
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return;
|
return;
|
||||||
|
@ -3710,8 +3711,8 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
|
||||||
ls.byVal(tok, tokenlist, errorLogger, settings);
|
ls.byVal(tok, tokenlist, errorLogger, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (const Type* t = Token::typeOf(tok->previous())) {
|
} else {
|
||||||
valueFlowLifetimeConstructor(tok, t, tokenlist, errorLogger, settings);
|
valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5037,7 +5038,7 @@ struct MultiValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isAlias(const Token* tok) const OVERRIDE {
|
virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE {
|
||||||
std::list<ValueFlow::Value> vals;
|
std::list<ValueFlow::Value> vals;
|
||||||
std::transform(values.begin(), values.end(), std::back_inserter(vals), SelectMapValues{});
|
std::transform(values.begin(), values.end(), std::back_inserter(vals), SelectMapValues{});
|
||||||
|
|
||||||
|
@ -5046,7 +5047,7 @@ struct MultiValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
const Variable* var = p.second;
|
const Variable* var = p.second;
|
||||||
if (tok->varId() == varid)
|
if (tok->varId() == varid)
|
||||||
return true;
|
return true;
|
||||||
if (isAliasOf(var, tok, varid, vals))
|
if (isAliasOf(var, tok, varid, vals, &inconclusive))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2611,7 +2611,6 @@ private:
|
||||||
"[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
|
"[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
// TODO: Ast is missing for this case
|
|
||||||
check("struct A {\n"
|
check("struct A {\n"
|
||||||
" const int& x;\n"
|
" const int& x;\n"
|
||||||
" int y;\n"
|
" int y;\n"
|
||||||
|
@ -2621,9 +2620,8 @@ private:
|
||||||
" A r{i, i};\n"
|
" A r{i, i};\n"
|
||||||
" return r;\n"
|
" return r;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
TODO_ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
|
"[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n",
|
||||||
"",
|
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("struct A {\n"
|
check("struct A {\n"
|
||||||
|
|
|
@ -3441,6 +3441,18 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// #9711
|
||||||
|
check("int main(int argc, char* argv[]) {\n"
|
||||||
|
" int foo = 0;\n"
|
||||||
|
" struct option options[] = {\n"
|
||||||
|
" {\"foo\", no_argument, &foo, \'f\'},\n"
|
||||||
|
" {NULL, 0, NULL, 0},\n"
|
||||||
|
" };\n"
|
||||||
|
" getopt_long(argc, argv, \"f\", options, NULL);\n"
|
||||||
|
" if (foo) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void alwaysTrueInfer() {
|
void alwaysTrueInfer() {
|
||||||
|
|
|
@ -182,6 +182,24 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool testValueOfXInconclusive(const char code[], unsigned int linenr, int value) {
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
|
for (const ValueFlow::Value& val:tok->values()) {
|
||||||
|
if (val.isInconclusive() && val.intvalue == value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool testValueOfX(const char code[], unsigned int linenr, int value) {
|
bool testValueOfX(const char code[], unsigned int linenr, int value) {
|
||||||
// Tokenize..
|
// Tokenize..
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
@ -2106,6 +2124,28 @@ private:
|
||||||
"Fred::Fred(std::unique_ptr<Wilma> wilma)\n"
|
"Fred::Fred(std::unique_ptr<Wilma> wilma)\n"
|
||||||
" : mWilma(std::move(wilma)) {}\n";
|
" : mWilma(std::move(wilma)) {}\n";
|
||||||
ASSERT_EQUALS(0, tokenValues(code, "mWilma (").size());
|
ASSERT_EQUALS(0, tokenValues(code, "mWilma (").size());
|
||||||
|
|
||||||
|
code = "void g(unknown*);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" int a = 1;\n"
|
||||||
|
" unknown c[] = {{&a}};\n"
|
||||||
|
" g(c);\n"
|
||||||
|
" int x = a;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 7U, 1));
|
||||||
|
ASSERT_EQUALS(true, testValueOfXInconclusive(code, 7U, 1));
|
||||||
|
|
||||||
|
code = "void g(unknown&);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" int a = 1;\n"
|
||||||
|
" unknown c{&a};\n"
|
||||||
|
" g(c);\n"
|
||||||
|
" int x = a;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 7U, 1));
|
||||||
|
ASSERT_EQUALS(true, testValueOfXInconclusive(code, 7U, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowAfterCondition() {
|
void valueFlowAfterCondition() {
|
||||||
|
|
Loading…
Reference in New Issue