Use valueFlowGeneric for valueFlowForwardExpression (#2537)
This commit is contained in:
parent
95a48eac67
commit
921887a281
135
lib/astutils.cpp
135
lib/astutils.cpp
|
@ -1321,7 +1321,7 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings *settings,
|
||||||
if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) {
|
if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) {
|
||||||
const Variable * var = tok->variable();
|
const Variable * var = tok->variable();
|
||||||
bool isConst = var && var->isConst();
|
bool isConst = var && var->isConst();
|
||||||
if (!isConst && var) {
|
if (!isConst) {
|
||||||
const ValueType * valueType = var->valueType();
|
const ValueType * valueType = var->valueType();
|
||||||
isConst = (valueType && valueType->pointer == 1 && valueType->constness == 1);
|
isConst = (valueType && valueType->pointer == 1 && valueType->constness == 1);
|
||||||
}
|
}
|
||||||
|
@ -1710,6 +1710,74 @@ bool isNullOperand(const Token *expr)
|
||||||
return Token::Match(castOp, "NULL|nullptr") || (MathLib::isInt(castOp->str()) && MathLib::isNullValue(castOp->str()));
|
return Token::Match(castOp, "NULL|nullptr") || (MathLib::isInt(castOp->str()) && MathLib::isNullValue(castOp->str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isGlobalData(const Token *expr, bool cpp)
|
||||||
|
{
|
||||||
|
bool globalData = false;
|
||||||
|
visitAstNodes(expr,
|
||||||
|
[&](const Token *tok) {
|
||||||
|
if (tok->varId() && !tok->variable()) {
|
||||||
|
// Bailout, this is probably global
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->originalName() == "->") {
|
||||||
|
// TODO check if pointer points at local data
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
} else if (Token::Match(tok, "[*[]") && tok->astOperand1() && tok->astOperand1()->variable()) {
|
||||||
|
// TODO check if pointer points at local data
|
||||||
|
const Variable *lhsvar = tok->astOperand1()->variable();
|
||||||
|
const ValueType *lhstype = tok->astOperand1()->valueType();
|
||||||
|
if (lhsvar->isPointer()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
} else if (lhsvar->isArgument() && lhsvar->isArray()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
} else if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->variable()) {
|
||||||
|
// TODO : Check references
|
||||||
|
if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->variable()->isExtern()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->previous()->str() != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->variable()->isPointerArray()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unknown argument type => it might be some reference type..
|
||||||
|
if (cpp && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) {
|
||||||
|
globalData = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok, ".|["))
|
||||||
|
return ChildrenToVisit::op1;
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
|
return globalData;
|
||||||
|
}
|
||||||
|
|
||||||
struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set<int> &exprVarIds, bool local, bool inInnerClass, int depth)
|
struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set<int> &exprVarIds, bool local, bool inInnerClass, int depth)
|
||||||
{
|
{
|
||||||
// Parse the given tokens
|
// Parse the given tokens
|
||||||
|
@ -1984,70 +2052,7 @@ static bool hasVolatileCast(const Token *expr)
|
||||||
|
|
||||||
bool FwdAnalysis::isGlobalData(const Token *expr) const
|
bool FwdAnalysis::isGlobalData(const Token *expr) const
|
||||||
{
|
{
|
||||||
bool globalData = false;
|
return ::isGlobalData(expr, mCpp);
|
||||||
visitAstNodes(expr,
|
|
||||||
[&](const Token *tok) {
|
|
||||||
if (tok->varId() && !tok->variable()) {
|
|
||||||
// Bailout, this is probably global
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->originalName() == "->") {
|
|
||||||
// TODO check if pointer points at local data
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
} else if (Token::Match(tok, "[*[]") && tok->astOperand1() && tok->astOperand1()->variable()) {
|
|
||||||
// TODO check if pointer points at local data
|
|
||||||
const Variable *lhsvar = tok->astOperand1()->variable();
|
|
||||||
const ValueType *lhstype = tok->astOperand1()->valueType();
|
|
||||||
if (lhsvar->isPointer()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
} else if (lhsvar->isArgument() && lhsvar->isArray()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
} else if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->variable()) {
|
|
||||||
// TODO : Check references
|
|
||||||
if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->variable()->isExtern()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->previous()->str() != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (tok->variable()->isPointerArray()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Unknown argument type => it might be some reference type..
|
|
||||||
if (mCpp && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) {
|
|
||||||
globalData = true;
|
|
||||||
return ChildrenToVisit::none;
|
|
||||||
}
|
|
||||||
if (Token::Match(tok, ".|["))
|
|
||||||
return ChildrenToVisit::op1;
|
|
||||||
return ChildrenToVisit::op1_and_op2;
|
|
||||||
});
|
|
||||||
return globalData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<int> FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const
|
std::set<int> FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const
|
||||||
|
|
|
@ -230,6 +230,8 @@ std::vector<const Variable*> getLHSVariables(const Token* tok);
|
||||||
bool isScopeBracket(const Token* tok);
|
bool isScopeBracket(const Token* tok);
|
||||||
|
|
||||||
bool isNullOperand(const Token *expr);
|
bool isNullOperand(const Token *expr);
|
||||||
|
|
||||||
|
bool isGlobalData(const Token *expr, bool cpp);
|
||||||
/**
|
/**
|
||||||
* Forward data flow analysis for checks
|
* Forward data flow analysis for checks
|
||||||
* - unused value
|
* - unused value
|
||||||
|
|
|
@ -1573,7 +1573,7 @@ static void execute(const Token *start, const Token *end, Data &data)
|
||||||
if (Token::Match(tok2, "%assign%")) {
|
if (Token::Match(tok2, "%assign%")) {
|
||||||
if (Token::Match(tok2->astOperand1(), ". %name% =") && tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->valueType()) {
|
if (Token::Match(tok2->astOperand1(), ". %name% =") && tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->valueType()) {
|
||||||
const Token *structToken = tok2->astOperand1()->astOperand1();
|
const Token *structToken = tok2->astOperand1()->astOperand1();
|
||||||
if (!structToken || !structToken->valueType() || !structToken->varId())
|
if (!structToken->valueType() || !structToken->varId())
|
||||||
throw VerifyException(tok2, "Unhandled assignment in loop");
|
throw VerifyException(tok2, "Unhandled assignment in loop");
|
||||||
const Scope *structScope = structToken->valueType()->typeScope;
|
const Scope *structScope = structToken->valueType()->typeScope;
|
||||||
if (!structScope)
|
if (!structScope)
|
||||||
|
|
|
@ -293,7 +293,7 @@ struct ForwardTraversal {
|
||||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||||
ForwardAnalyzer::Action thenAction = ForwardAnalyzer::Action::None;
|
ForwardAnalyzer::Action thenAction = ForwardAnalyzer::Action::None;
|
||||||
ForwardAnalyzer::Action elseAction = ForwardAnalyzer::Action::None;
|
ForwardAnalyzer::Action elseAction = ForwardAnalyzer::Action::None;
|
||||||
bool hasElse = false;
|
bool hasElse = Token::simpleMatch(endBlock, "} else {");
|
||||||
bool bail = false;
|
bool bail = false;
|
||||||
|
|
||||||
// Traverse then block
|
// Traverse then block
|
||||||
|
@ -308,8 +308,7 @@ struct ForwardTraversal {
|
||||||
bail = true;
|
bail = true;
|
||||||
}
|
}
|
||||||
// Traverse else block
|
// Traverse else block
|
||||||
if (Token::simpleMatch(endBlock, "} else {")) {
|
if (hasElse) {
|
||||||
hasElse = true;
|
|
||||||
returnElse = isEscapeScope(endBlock->linkAt(2), true);
|
returnElse = isEscapeScope(endBlock->linkAt(2), true);
|
||||||
if (checkElse) {
|
if (checkElse) {
|
||||||
Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2));
|
Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2));
|
||||||
|
@ -346,6 +345,8 @@ struct ForwardTraversal {
|
||||||
if (!analyzer->lowerToInconclusive())
|
if (!analyzer->lowerToInconclusive())
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
} else if (thenAction.isModified() || elseAction.isModified()) {
|
} else if (thenAction.isModified() || elseAction.isModified()) {
|
||||||
|
if (!hasElse && analyzer->isConditional())
|
||||||
|
return Progress::Break;
|
||||||
if (!analyzer->lowerToPossible())
|
if (!analyzer->lowerToPossible())
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
analyzer->assume(condTok, elseAction.isModified());
|
analyzer->assume(condTok, elseAction.isModified());
|
||||||
|
|
|
@ -246,6 +246,28 @@ static ProgramMemory getInitialProgramState(const Token* tok,
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgramMemory getProgramMemory(const Token *tok, const std::unordered_map<nonneg int, ValueFlow::Value>& vars)
|
||||||
|
{
|
||||||
|
ProgramMemory programMemory;
|
||||||
|
for(const auto& p:vars) {
|
||||||
|
const ValueFlow::Value &value = p.second;
|
||||||
|
programMemory.replace(getInitialProgramState(tok, value.tokvalue));
|
||||||
|
programMemory.replace(getInitialProgramState(tok, value.condition));
|
||||||
|
}
|
||||||
|
fillProgramMemoryFromConditions(programMemory, tok, nullptr);
|
||||||
|
ProgramMemory state;
|
||||||
|
for(const auto& p:vars) {
|
||||||
|
nonneg int varid = p.first;
|
||||||
|
const ValueFlow::Value &value = p.second;
|
||||||
|
programMemory.setValue(varid, value);
|
||||||
|
if (value.varId)
|
||||||
|
programMemory.setIntValue(value.varId, value.varvalue);
|
||||||
|
state = programMemory;
|
||||||
|
}
|
||||||
|
fillProgramMemoryFromAssignments(programMemory, tok, state, vars);
|
||||||
|
return programMemory;
|
||||||
|
}
|
||||||
|
|
||||||
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value)
|
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value)
|
||||||
{
|
{
|
||||||
ProgramMemory programMemory;
|
ProgramMemory programMemory;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "valueflow.h"
|
#include "valueflow.h"
|
||||||
#include "mathlib.h"
|
#include "mathlib.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
struct ProgramMemory {
|
struct ProgramMemory {
|
||||||
std::map<int, ValueFlow::Value> values;
|
std::map<int, ValueFlow::Value> values;
|
||||||
|
@ -55,6 +56,8 @@ bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory)
|
||||||
*/
|
*/
|
||||||
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value);
|
ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value);
|
||||||
|
|
||||||
|
ProgramMemory getProgramMemory(const Token *tok, const std::unordered_map<nonneg int, ValueFlow::Value>& vars);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1184,14 +1184,14 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers()
|
||||||
|
|
||||||
if (membertok) {
|
if (membertok) {
|
||||||
const Variable *var = tok->variable();
|
const Variable *var = tok->variable();
|
||||||
if (var && var->typeScope()) {
|
if (var->typeScope()) {
|
||||||
const Variable *membervar = var->typeScope()->getVariable(membertok->str());
|
const Variable *membervar = var->typeScope()->getVariable(membertok->str());
|
||||||
if (membervar) {
|
if (membervar) {
|
||||||
membertok->variable(membervar);
|
membertok->variable(membervar);
|
||||||
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
||||||
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
||||||
}
|
}
|
||||||
} else if (const ::Type *type = var ? var->smartPointerType() : nullptr) {
|
} else if (const ::Type *type = var->smartPointerType()) {
|
||||||
const Scope *classScope = type->classScope;
|
const Scope *classScope = type->classScope;
|
||||||
const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr;
|
const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr;
|
||||||
if (membervar) {
|
if (membervar) {
|
||||||
|
@ -1199,7 +1199,7 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers()
|
||||||
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
||||||
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
||||||
}
|
}
|
||||||
} else if (var && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) {
|
} else if (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) {
|
||||||
if (Token::Match(var->typeStartToken(), "std :: %type% < %type% *| *| >")) {
|
if (Token::Match(var->typeStartToken(), "std :: %type% < %type% *| *| >")) {
|
||||||
const Type * type = var->typeStartToken()->tokAt(4)->type();
|
const Type * type = var->typeStartToken()->tokAt(4)->type();
|
||||||
if (type && type->classScope && type->classScope->definedType) {
|
if (type && type->classScope && type->classScope->definedType) {
|
||||||
|
|
|
@ -1067,7 +1067,7 @@ void Tokenizer::simplifyTypedef()
|
||||||
const Token *func = temp->link()->previous();
|
const Token *func = temp->link()->previous();
|
||||||
if (temp->str() != ")")
|
if (temp->str() != ")")
|
||||||
continue;
|
continue;
|
||||||
if (!func || !func->previous()) // Ticket #4239
|
if (!func->previous()) // Ticket #4239
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/** @todo add support for multi-token operators */
|
/** @todo add support for multi-token operators */
|
||||||
|
|
|
@ -1226,7 +1226,7 @@ static bool isLambdaCaptureList(const Token * tok)
|
||||||
if (!tok->astOperand1() || tok->astOperand1()->str() != "(")
|
if (!tok->astOperand1() || tok->astOperand1()->str() != "(")
|
||||||
return false;
|
return false;
|
||||||
const Token * params = tok->astOperand1();
|
const Token * params = tok->astOperand1();
|
||||||
if (!params || !params->astOperand1() || params->astOperand1()->str() != "{")
|
if (!params->astOperand1() || params->astOperand1()->str() != "{")
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1324,8 +1324,6 @@ static Token * createAstAtToken(Token *tok, bool cpp)
|
||||||
while (tok2 && tok2 != endPar && tok2->str() != ";") {
|
while (tok2 && tok2 != endPar && tok2->str() != ";") {
|
||||||
if (tok2->str() == "<" && tok2->link()) {
|
if (tok2->str() == "<" && tok2->link()) {
|
||||||
tok2 = tok2->link();
|
tok2 = tok2->link();
|
||||||
if (!tok2)
|
|
||||||
break;
|
|
||||||
} else if (Token::Match(tok2, "%name% %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) {
|
} else if (Token::Match(tok2, "%name% %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) {
|
||||||
init1 = tok2;
|
init1 = tok2;
|
||||||
AST_state state1(cpp);
|
AST_state state1(cpp);
|
||||||
|
|
|
@ -2081,7 +2081,7 @@ static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid,
|
||||||
return false;
|
return false;
|
||||||
if (isAliasOf(tok, varid))
|
if (isAliasOf(tok, varid))
|
||||||
return true;
|
return true;
|
||||||
if (!var->isPointer())
|
if (var && !var->isPointer())
|
||||||
return false;
|
return false;
|
||||||
// Search through non value aliases
|
// Search through non value aliases
|
||||||
for (const ValueFlow::Value &val : values) {
|
for (const ValueFlow::Value &val : values) {
|
||||||
|
@ -2101,31 +2101,6 @@ static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowForwardExpression(Token* startToken,
|
|
||||||
const Token* endToken,
|
|
||||||
const Token* exprTok,
|
|
||||||
const std::list<ValueFlow::Value>& values,
|
|
||||||
const TokenList* const tokenlist,
|
|
||||||
const Settings* settings)
|
|
||||||
{
|
|
||||||
FwdAnalysis fwdAnalysis(tokenlist->isCPP(), settings->library);
|
|
||||||
for (const FwdAnalysis::KnownAndToken read : fwdAnalysis.valueFlow(exprTok, startToken, endToken)) {
|
|
||||||
for (const ValueFlow::Value& value : values) {
|
|
||||||
// Don't set inconclusive values
|
|
||||||
if (value.isInconclusive())
|
|
||||||
continue;
|
|
||||||
ValueFlow::Value v = value;
|
|
||||||
if (v.isImpossible()) {
|
|
||||||
if (read.known)
|
|
||||||
continue;
|
|
||||||
} else if (!read.known) {
|
|
||||||
v.valueKind = ValueFlow::Value::ValueKind::Possible;
|
|
||||||
}
|
|
||||||
setTokenValue(const_cast<Token*>(read.token), v, settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const ValueFlow::Value* getKnownValue(const Token* tok, ValueFlow::Value::ValueType type)
|
static const ValueFlow::Value* getKnownValue(const Token* tok, ValueFlow::Value::ValueType type)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
@ -2169,24 +2144,86 @@ static bool bifurcate(const Token* tok, const std::set<nonneg int>& varids, cons
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VariableForwardAnalyzer : ForwardAnalyzer {
|
struct SelectMapKeys
|
||||||
Token* start;
|
{
|
||||||
const Variable* var;
|
template<class Pair>
|
||||||
nonneg int varid;
|
typename Pair::first_type operator()(const Pair& p) const
|
||||||
ValueFlow::Value value;
|
{
|
||||||
const Settings* settings;
|
return p.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
VariableForwardAnalyzer() = default;
|
struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
|
const TokenList* tokenlist;
|
||||||
|
|
||||||
bool isWritableValue() const {
|
ValueFlowForwardAnalyzer()
|
||||||
return value.isIntValue() || value.isFloatValue();
|
: tokenlist(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ValueFlowForwardAnalyzer(const TokenList* t)
|
||||||
|
: tokenlist(t)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual int getIndirect() const = 0;
|
||||||
|
|
||||||
|
virtual bool match(const Token* tok) const = 0;
|
||||||
|
|
||||||
|
virtual bool isAlias(const Token* tok) const = 0;
|
||||||
|
|
||||||
|
using ProgramState = std::unordered_map<nonneg int, ValueFlow::Value>;
|
||||||
|
|
||||||
|
virtual ProgramState getProgramState() const = 0;
|
||||||
|
|
||||||
|
virtual bool isWritableValue() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isGlobal() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool invalid() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCPP() const {
|
||||||
|
return tokenlist->isCPP();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Settings* getSettings() const {
|
||||||
|
return tokenlist->getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Action isModified(const Token* tok) const {
|
||||||
|
Action read = Action::Read;
|
||||||
|
bool inconclusive = false;
|
||||||
|
if (isVariableChangedByFunctionCall(tok, getIndirect(), getSettings(), &inconclusive))
|
||||||
|
return read | Action::Invalid;
|
||||||
|
if (inconclusive)
|
||||||
|
return read | Action::Inconclusive;
|
||||||
|
if (isVariableChanged(tok, getIndirect(), getSettings(), isCPP())) {
|
||||||
|
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
|
||||||
|
return read | Action::Invalid;
|
||||||
|
return Action::Invalid;
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Action isAliasModified(const Token* tok) const {
|
||||||
|
int indirect = 0;
|
||||||
|
if (tok->valueType())
|
||||||
|
indirect = tok->valueType()->pointer;
|
||||||
|
if (isVariableChanged(tok, indirect, getSettings(), isCPP()))
|
||||||
|
return Action::Invalid;
|
||||||
|
return Action::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Action analyze(const Token* tok) const OVERRIDE {
|
virtual Action analyze(const Token* tok) const OVERRIDE {
|
||||||
bool cpp = true;
|
if (invalid())
|
||||||
if (tok->varId() == varid) {
|
return Action::Invalid;
|
||||||
|
if (match(tok)) {
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
if ((Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && value.indirect <= 0)
|
if ((Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect() <= 0)
|
||||||
return Action::Read;
|
return Action::Read;
|
||||||
|
|
||||||
Action read = Action::Read;
|
Action read = Action::Read;
|
||||||
|
@ -2205,74 +2242,80 @@ struct VariableForwardAnalyzer : ForwardAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment/decrement
|
// increment/decrement
|
||||||
if (value.valueType == ValueFlow::Value::ValueType::INT && (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--"))) {
|
if (isWritableValue() && (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--"))) {
|
||||||
return read | Action::Write;
|
return read | Action::Write;
|
||||||
}
|
}
|
||||||
// Check for modifications by function calls
|
// Check for modifications by function calls
|
||||||
bool inconclusive = false;
|
return isModified(tok);
|
||||||
if (isVariableChangedByFunctionCall(tok, value.indirect, settings, &inconclusive))
|
} else if (isAlias(tok)) {
|
||||||
return read | Action::Invalid;
|
return isAliasModified(tok);
|
||||||
if (inconclusive)
|
|
||||||
return read | Action::Inconclusive;
|
|
||||||
if (isVariableChanged(tok, value.indirect, settings, cpp)) {
|
|
||||||
if (Token::Match(parent, "*|[|.|++|--"))
|
|
||||||
return read | Action::Invalid;
|
|
||||||
return Action::Invalid;
|
|
||||||
}
|
|
||||||
return read;
|
|
||||||
} else if (!value.isLifetimeValue() && isAliasOf(var, tok, varid, {value})) {
|
|
||||||
int indirect = 0;
|
|
||||||
if (tok->valueType())
|
|
||||||
indirect = tok->valueType()->pointer;
|
|
||||||
if (isVariableChanged(tok, indirect, settings, cpp))
|
|
||||||
return Action::Invalid;
|
|
||||||
} 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 (var->isGlobal() && !var->isConst()) {
|
if (isGlobal()) {
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Action::None;
|
return Action::None;
|
||||||
}
|
}
|
||||||
virtual void update(Token* tok, Action a) OVERRIDE {
|
|
||||||
if (a.isRead())
|
|
||||||
setTokenValue(tok, value, settings);
|
|
||||||
if (a.isInconclusive())
|
|
||||||
lowerToInconclusive();
|
|
||||||
if (a.isWrite()) {
|
|
||||||
if (Token::Match(tok->astParent(), "%assign%")) {
|
|
||||||
// TODO: Check result
|
|
||||||
if (evalAssignment(value,
|
|
||||||
tok->astParent()->str(),
|
|
||||||
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
|
|
||||||
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
|
|
||||||
value.infoString());
|
|
||||||
value.errorPath.emplace_back(tok, info);
|
|
||||||
} else {
|
|
||||||
// TODO: Dont set to zero
|
|
||||||
value.intvalue = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Token::Match(tok->astParent(), "++|--")) {
|
|
||||||
const bool inc = Token::simpleMatch(tok->astParent(), "++");
|
|
||||||
value.intvalue += (inc ? 1 : -1);
|
|
||||||
const std::string info(tok->str() + " is " + std::string(inc ? "incremented" : "decremented") +
|
|
||||||
"', new value is " + value.infoString());
|
|
||||||
value.errorPath.emplace_back(tok, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual std::vector<int> evaluate(const Token* tok) const OVERRIDE {
|
virtual std::vector<int> evaluate(const Token* tok) const OVERRIDE {
|
||||||
if (tok->hasKnownIntValue())
|
if (tok->hasKnownIntValue())
|
||||||
return {static_cast<int>(tok->values().front().intvalue)};
|
return {static_cast<int>(tok->values().front().intvalue)};
|
||||||
std::vector<int> result;
|
std::vector<int> result;
|
||||||
ProgramMemory programMemory = getProgramMemory(tok, varid, value);
|
ProgramState ps = getProgramState();
|
||||||
|
ProgramMemory programMemory = getProgramMemory(tok, ps);
|
||||||
if (conditionIsTrue(tok, programMemory))
|
if (conditionIsTrue(tok, programMemory))
|
||||||
result.push_back(1);
|
result.push_back(1);
|
||||||
if (conditionIsFalse(tok, programMemory))
|
if (conditionIsFalse(tok, programMemory))
|
||||||
result.push_back(0);
|
result.push_back(0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
|
ValueFlow::Value value;
|
||||||
|
|
||||||
|
SingleValueFlowForwardAnalyzer()
|
||||||
|
: ValueFlowForwardAnalyzer()
|
||||||
|
{}
|
||||||
|
|
||||||
|
SingleValueFlowForwardAnalyzer(const ValueFlow::Value& v, const TokenList* t)
|
||||||
|
: ValueFlowForwardAnalyzer(t), value(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual const std::unordered_map<nonneg int, const Variable*>& getVars() const = 0;
|
||||||
|
|
||||||
|
virtual int getIndirect() const OVERRIDE {
|
||||||
|
return value.indirect;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isAlias(const Token* tok) const OVERRIDE {
|
||||||
|
if (value.isLifetimeValue())
|
||||||
|
return false;
|
||||||
|
for(const auto& p:getVars()) {
|
||||||
|
nonneg int varid = p.first;
|
||||||
|
const Variable* var = p.second;
|
||||||
|
if (tok->varId() == varid)
|
||||||
|
return true;
|
||||||
|
if (isAliasOf(var, tok, varid, {value}))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isWritableValue() const OVERRIDE {
|
||||||
|
return value.isIntValue() || value.isFloatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isGlobal() const OVERRIDE {
|
||||||
|
for(const auto&p:getVars()) {
|
||||||
|
const Variable* var = p.second;
|
||||||
|
if (var->isGlobal() && !var->isConst())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool lowerToPossible() OVERRIDE {
|
virtual bool lowerToPossible() OVERRIDE {
|
||||||
if (value.isImpossible())
|
if (value.isImpossible())
|
||||||
return false;
|
return false;
|
||||||
|
@ -2299,6 +2342,35 @@ struct VariableForwardAnalyzer : ForwardAnalyzer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void update(Token* tok, Action a) OVERRIDE {
|
||||||
|
if (a.isRead())
|
||||||
|
setTokenValue(tok, value, getSettings());
|
||||||
|
if (a.isInconclusive())
|
||||||
|
lowerToInconclusive();
|
||||||
|
if (a.isWrite()) {
|
||||||
|
if (Token::Match(tok->astParent(), "%assign%")) {
|
||||||
|
// TODO: Check result
|
||||||
|
if (evalAssignment(value,
|
||||||
|
tok->astParent()->str(),
|
||||||
|
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
|
||||||
|
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
|
||||||
|
value.infoString());
|
||||||
|
value.errorPath.emplace_back(tok, info);
|
||||||
|
} else {
|
||||||
|
// TODO: Dont set to zero
|
||||||
|
value.intvalue = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Token::Match(tok->astParent(), "++|--")) {
|
||||||
|
const bool inc = Token::simpleMatch(tok->astParent(), "++");
|
||||||
|
value.intvalue += (inc ? 1 : -1);
|
||||||
|
const std::string info(tok->str() + " is " + std::string(inc ? "incremented" : "decremented") +
|
||||||
|
"', new value is " + value.infoString());
|
||||||
|
value.errorPath.emplace_back(tok, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE {
|
virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE {
|
||||||
const Scope* scope = endBlock->scope();
|
const Scope* scope = endBlock->scope();
|
||||||
if (!scope)
|
if (!scope)
|
||||||
|
@ -2314,36 +2386,145 @@ struct VariableForwardAnalyzer : ForwardAnalyzer {
|
||||||
if (isConditional())
|
if (isConditional())
|
||||||
return false;
|
return false;
|
||||||
const Token* condTok = getCondTokFromEnd(endBlock);
|
const Token* condTok = getCondTokFromEnd(endBlock);
|
||||||
return bifurcate(condTok, {varid}, settings);
|
std::set<nonneg int> varids;
|
||||||
|
std::transform(getVars().begin(), getVars().end(), std::inserter(varids, varids.begin()), SelectMapKeys{});
|
||||||
|
return bifurcate(condTok, varids, getSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VariableForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
||||||
|
const Variable* var;
|
||||||
|
std::unordered_map<nonneg int, const Variable*> varids;
|
||||||
|
|
||||||
|
VariableForwardAnalyzer()
|
||||||
|
: SingleValueFlowForwardAnalyzer(), var(nullptr), varids()
|
||||||
|
{}
|
||||||
|
|
||||||
|
VariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, const TokenList* t)
|
||||||
|
: SingleValueFlowForwardAnalyzer(val, t), var(v), varids() {
|
||||||
|
varids[var->declarationId()] = var;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const std::unordered_map<nonneg int, const Variable*>& getVars() const OVERRIDE {
|
||||||
|
return varids;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool match(const Token* tok) const OVERRIDE {
|
||||||
|
return tok->varId() == var->declarationId();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ProgramState getProgramState() const OVERRIDE {
|
||||||
|
ProgramState ps;
|
||||||
|
ps[var->declarationId()] = value;
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static bool valueFlowForwardVariable(Token* const startToken,
|
static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
const Token* const endToken,
|
const Token* const endToken,
|
||||||
const Variable* const var,
|
const Variable* const var,
|
||||||
const nonneg int varid,
|
const nonneg int,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
const bool,
|
const bool,
|
||||||
const bool,
|
const bool,
|
||||||
TokenList* const,
|
TokenList* const tokenlist,
|
||||||
ErrorLogger* const,
|
ErrorLogger* const,
|
||||||
const Settings* const settings)
|
const Settings* const settings)
|
||||||
{
|
{
|
||||||
VariableForwardAnalyzer a;
|
|
||||||
a.start = startToken;
|
|
||||||
a.var = var;
|
|
||||||
a.varid = varid;
|
|
||||||
a.settings = settings;
|
|
||||||
for (ValueFlow::Value& v : values) {
|
for (ValueFlow::Value& v : values) {
|
||||||
a.value = v;
|
VariableForwardAnalyzer a(var, v, tokenlist);
|
||||||
valueFlowGenericForward(startToken, endToken, a, settings);
|
valueFlowGenericForward(startToken, endToken, a, settings);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ExpressionForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
||||||
|
const Token* expr;
|
||||||
|
std::unordered_map<nonneg int, const Variable*> varids;
|
||||||
|
bool local;
|
||||||
|
bool unknown;
|
||||||
|
|
||||||
|
ExpressionForwardAnalyzer()
|
||||||
|
: SingleValueFlowForwardAnalyzer(), expr(nullptr), varids(), local(true), unknown(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ExpressionForwardAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
||||||
|
: SingleValueFlowForwardAnalyzer(val, t), expr(e), varids(), local(true), unknown(false) {
|
||||||
|
|
||||||
|
setupExprVarIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nonLocal(const Variable* var, bool deref)
|
||||||
|
{
|
||||||
|
return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupExprVarIds()
|
||||||
|
{
|
||||||
|
visitAstNodes(expr,
|
||||||
|
[&](const Token *tok) {
|
||||||
|
if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") {
|
||||||
|
// unknown variable
|
||||||
|
unknown = true;
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
}
|
||||||
|
if (tok->varId() > 0) {
|
||||||
|
varids[tok->varId()] = tok->variable();
|
||||||
|
if (!Token::simpleMatch(tok->previous(), ".")) {
|
||||||
|
const Variable *var = tok->variable();
|
||||||
|
if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2(), isCPP()))
|
||||||
|
return ChildrenToVisit::none;
|
||||||
|
const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1()));
|
||||||
|
local &= !nonLocal(tok->variable(), deref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool invalid() const OVERRIDE {
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::vector<int> evaluate(const Token* tok) const OVERRIDE {
|
||||||
|
if (tok->hasKnownIntValue())
|
||||||
|
return {static_cast<int>(tok->values().front().intvalue)};
|
||||||
|
return std::vector<int>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const std::unordered_map<nonneg int, const Variable*>& getVars() const OVERRIDE {
|
||||||
|
return varids;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool match(const Token* tok) const OVERRIDE {
|
||||||
|
return isSameExpression(isCPP(), true, expr, tok, getSettings()->library, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ProgramState getProgramState() const OVERRIDE {
|
||||||
|
return ProgramState{};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isGlobal() const OVERRIDE {
|
||||||
|
return !local;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void valueFlowForwardExpression(Token* startToken,
|
||||||
|
const Token* endToken,
|
||||||
|
const Token* exprTok,
|
||||||
|
const std::list<ValueFlow::Value>& values,
|
||||||
|
const TokenList* const tokenlist,
|
||||||
|
const Settings* settings)
|
||||||
|
{
|
||||||
|
for (const ValueFlow::Value& v : values) {
|
||||||
|
ExpressionForwardAnalyzer a(exprTok, v, tokenlist);
|
||||||
|
valueFlowGenericForward(startToken, endToken, a, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known)
|
static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known)
|
||||||
{
|
{
|
||||||
if (!expr)
|
if (!expr)
|
||||||
|
@ -5269,6 +5450,9 @@ static void valueFlowFwdAnalysis(const TokenList *tokenlist, const Settings *set
|
||||||
tok = tok->linkAt(1);
|
tok = tok->linkAt(1);
|
||||||
if (tok->str() != "=" || !tok->astOperand1() || !tok->astOperand2())
|
if (tok->str() != "=" || !tok->astOperand1() || !tok->astOperand2())
|
||||||
continue;
|
continue;
|
||||||
|
// Skip variables
|
||||||
|
if (tok->astOperand1()->variable())
|
||||||
|
continue;
|
||||||
if (!tok->scope()->isExecutable())
|
if (!tok->scope()->isExecutable())
|
||||||
continue;
|
continue;
|
||||||
if (!tok->astOperand2()->hasKnownIntValue())
|
if (!tok->astOperand2()->hasKnownIntValue())
|
||||||
|
|
|
@ -91,6 +91,7 @@ private:
|
||||||
TEST_CASE(nullpointer49); // #7804
|
TEST_CASE(nullpointer49); // #7804
|
||||||
TEST_CASE(nullpointer50); // #6462
|
TEST_CASE(nullpointer50); // #6462
|
||||||
TEST_CASE(nullpointer51);
|
TEST_CASE(nullpointer51);
|
||||||
|
TEST_CASE(nullpointer52);
|
||||||
TEST_CASE(nullpointer_addressOf); // address of
|
TEST_CASE(nullpointer_addressOf); // address of
|
||||||
TEST_CASE(nullpointerSwitch); // #2626
|
TEST_CASE(nullpointerSwitch); // #2626
|
||||||
TEST_CASE(nullpointer_cast); // #4692
|
TEST_CASE(nullpointer_cast); // #4692
|
||||||
|
@ -1707,6 +1708,46 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nullpointer52() {
|
||||||
|
check("int f(int a, int* b) {\n"
|
||||||
|
" int* c = nullptr;\n"
|
||||||
|
" if(b) c = b;\n"
|
||||||
|
" if (!c) c = &a;\n"
|
||||||
|
" return *c;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("int f(int a, int* b) {\n"
|
||||||
|
" int* c = nullptr;\n"
|
||||||
|
" if(b) c = b;\n"
|
||||||
|
" bool d = !c;\n"
|
||||||
|
" if (d) c = &a;\n"
|
||||||
|
" return *c;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A { int* x; };\n"
|
||||||
|
"int f(int a, int* b) {\n"
|
||||||
|
" A c;\n"
|
||||||
|
" c.x = nullptr;\n"
|
||||||
|
" if(b) c.x = b;\n"
|
||||||
|
" if (!c.x) c.x = &a;\n"
|
||||||
|
" return *c.x;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A { int* x; };\n"
|
||||||
|
"int f(int a, int* b) {\n"
|
||||||
|
" A c;\n"
|
||||||
|
" c.x = nullptr;\n"
|
||||||
|
" if(b) c.x = b;\n"
|
||||||
|
" bool d = !c.x;\n"
|
||||||
|
" if (!d) c.x = &a;\n"
|
||||||
|
" return *c.x;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void nullpointer_addressOf() { // address of
|
void nullpointer_addressOf() { // address of
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" struct X *x = 0;\n"
|
" struct X *x = 0;\n"
|
||||||
|
|
|
@ -4346,7 +4346,7 @@ private:
|
||||||
" if (b == 'x') {}\n"
|
" if (b == 'x') {}\n"
|
||||||
" if (a) {}\n"
|
" if (a) {}\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8]: (error) Uninitialized variable: a\n", errout.str());
|
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Uninitialized variable: a\n", "", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("void h() {\n"
|
valueFlowUninit("void h() {\n"
|
||||||
" int i;\n"
|
" int i;\n"
|
||||||
|
|
|
@ -1665,7 +1665,7 @@ private:
|
||||||
" if (condition2) x = 789;\n"
|
" if (condition2) x = 789;\n"
|
||||||
" a = 2 + x;\n" // <- either assignment "x=123" is redundant or x can be 123 here.
|
" a = 2 + x;\n" // <- either assignment "x=123" is redundant or x can be 123 here.
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 5U, 123));
|
TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 123));
|
||||||
|
|
||||||
code = "void f(int a) {\n"
|
code = "void f(int a) {\n"
|
||||||
" int x = 123;\n"
|
" int x = 123;\n"
|
||||||
|
@ -2717,7 +2717,9 @@ private:
|
||||||
" s.x++;\n"
|
" s.x++;\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "<");
|
values = tokenValues(code, "<");
|
||||||
ASSERT_EQUALS(true, values.empty());
|
ASSERT_EQUALS(1, values.size());
|
||||||
|
ASSERT(values.front().isPossible());
|
||||||
|
ASSERT_EQUALS(true, values.front().intvalue);
|
||||||
|
|
||||||
code = "void f() {\n"
|
code = "void f() {\n"
|
||||||
" S s;\n"
|
" S s;\n"
|
||||||
|
@ -2755,7 +2757,9 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, ">");
|
values = tokenValues(code, ">");
|
||||||
ASSERT_EQUALS(true, values.empty());
|
ASSERT_EQUALS(1, values.size());
|
||||||
|
ASSERT(values.front().isPossible());
|
||||||
|
ASSERT_EQUALS(true, values.front().intvalue);
|
||||||
|
|
||||||
code = "void foo() {\n"
|
code = "void foo() {\n"
|
||||||
" struct ISO_PVD_s pvd;\n"
|
" struct ISO_PVD_s pvd;\n"
|
||||||
|
@ -3647,7 +3651,11 @@ private:
|
||||||
" int x;\n"
|
" int x;\n"
|
||||||
" f(x=3), return x+3;\n"
|
" f(x=3), return x+3;\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(0U, tokenValues(code, "x +").size());
|
values = tokenValues(code, "x +");
|
||||||
|
ASSERT_EQUALS(true, values.empty());
|
||||||
|
// ASSERT_EQUALS(1U, values.size());
|
||||||
|
// ASSERT(values.front().isIntValue());
|
||||||
|
// ASSERT_EQUALS(3, values.front().intvalue);
|
||||||
|
|
||||||
// #8195
|
// #8195
|
||||||
code = "void foo(std::istream &is) {\n"
|
code = "void foo(std::istream &is) {\n"
|
||||||
|
@ -3677,8 +3685,7 @@ private:
|
||||||
" x = 1;\n"
|
" x = 1;\n"
|
||||||
" if (x>1) {}\n"
|
" if (x>1) {}\n"
|
||||||
"}";
|
"}";
|
||||||
values = tokenValues(code, "x >");
|
ASSERT_EQUALS(true, testValueOfXKnown(code, 8U, 1));
|
||||||
ASSERT_EQUALS(true, values.size() == 1U && values.front().isIntValue());
|
|
||||||
|
|
||||||
// #8348 - noreturn else
|
// #8348 - noreturn else
|
||||||
code = "int test_input_int(int a, int b) {\n"
|
code = "int test_input_int(int a, int b) {\n"
|
||||||
|
|
Loading…
Reference in New Issue