diff --git a/lib/astutils.cpp b/lib/astutils.cpp index f0aa7b625..fac0557b6 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -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; } diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 765a7d74c..36fada30a 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -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); diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 347da8134..28069fe7c 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -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); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index bba098520..1926f3cf0 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -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() == "||") diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 8d360c379..2a62588bf 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -225,7 +225,7 @@ static void conditionAlwaysTrueOrFalse(const Token *tok, const std::mapisComparisonOp()) { - if (tok->values().size() == 1U && tok->values().front().isKnown()) { + if (tok->hasKnownIntValue()) { if (tok->values().front().intvalue) *alwaysTrue = true; else diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 28763ad40..115e13ad2 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -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; } diff --git a/lib/token.cpp b/lib/token.cpp index 0b958af42..0a4b91add 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -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); diff --git a/lib/token.h b/lib/token.h index 094597c3e..a4d1ac48a 100644 --- a/lib/token.h +++ b/lib/token.h @@ -25,7 +25,9 @@ #include "mathlib.h" #include "valueflow.h" +#include #include +#include #include #include #include @@ -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 { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index a3fa9957b..dacef00c7 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -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 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(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 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 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::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 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(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 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, diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 28902fc12..0334cc4f9 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -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());