parent
bc3f5554a4
commit
f493ce16b3
|
@ -281,8 +281,7 @@ QString CppcheckLibraryData::open(QIODevice &file)
|
||||||
}
|
}
|
||||||
if (xmlReader.hasError()) {
|
if (xmlReader.hasError()) {
|
||||||
return xmlReader.errorString();
|
return xmlReader.errorString();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -839,11 +839,11 @@ bool CheckBufferOverrun::analyseWholeProgram1(const std::map<std::string, std::l
|
||||||
|
|
||||||
const std::list<ErrorMessage::FileLocation> &locationList =
|
const std::list<ErrorMessage::FileLocation> &locationList =
|
||||||
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow,
|
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow,
|
||||||
unsafeUsage,
|
unsafeUsage,
|
||||||
callsMap,
|
callsMap,
|
||||||
"Using argument ARG",
|
"Using argument ARG",
|
||||||
&functionCall,
|
&functionCall,
|
||||||
false);
|
false);
|
||||||
if (locationList.empty())
|
if (locationList.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -592,11 +592,11 @@ bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std::
|
||||||
|
|
||||||
const std::list<ErrorMessage::FileLocation> &locationList =
|
const std::list<ErrorMessage::FileLocation> &locationList =
|
||||||
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::null,
|
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::null,
|
||||||
unsafeUsage,
|
unsafeUsage,
|
||||||
callsMap,
|
callsMap,
|
||||||
"Dereferencing argument ARG that is null",
|
"Dereferencing argument ARG that is null",
|
||||||
nullptr,
|
nullptr,
|
||||||
warning);
|
warning);
|
||||||
if (locationList.empty())
|
if (locationList.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -1439,11 +1439,11 @@ bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::li
|
||||||
|
|
||||||
const std::list<ErrorMessage::FileLocation> &locationList =
|
const std::list<ErrorMessage::FileLocation> &locationList =
|
||||||
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::uninit,
|
CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::uninit,
|
||||||
unsafeUsage,
|
unsafeUsage,
|
||||||
callsMap,
|
callsMap,
|
||||||
"Using argument ARG",
|
"Using argument ARG",
|
||||||
&functionCall,
|
&functionCall,
|
||||||
false);
|
false);
|
||||||
if (locationList.empty())
|
if (locationList.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -349,9 +349,15 @@ T asInt(T x)
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
MathLib::bigint asInt(float x) { return x; }
|
MathLib::bigint asInt(float x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
MathLib::bigint asInt(double x) { return x; }
|
MathLib::bigint asInt(double x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T, class U>
|
template <class T, class U>
|
||||||
static T calculate(const std::string& s, T x, U y)
|
static T calculate(const std::string& s, T x, U y)
|
||||||
|
@ -4111,98 +4117,97 @@ struct ConditionHandler {
|
||||||
void beforeCondition(TokenList* tokenlist,
|
void beforeCondition(TokenList* tokenlist,
|
||||||
SymbolDatabase* symboldatabase,
|
SymbolDatabase* symboldatabase,
|
||||||
ErrorLogger* errorLogger,
|
ErrorLogger* errorLogger,
|
||||||
const Settings* settings) const
|
const Settings* settings) const {
|
||||||
{
|
|
||||||
traverseCondition(
|
traverseCondition(
|
||||||
tokenlist,
|
tokenlist,
|
||||||
symboldatabase,
|
symboldatabase,
|
||||||
errorLogger,
|
errorLogger,
|
||||||
settings,
|
settings,
|
||||||
[&](const Condition& cond, Token* tok, const Scope*, const std::vector<const Variable*>&) {
|
[&](const Condition& cond, Token* tok, const Scope*, const std::vector<const Variable*>&) {
|
||||||
if (cond.vartok->exprId() == 0)
|
if (cond.vartok->exprId() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If condition is known then dont propogate value
|
// If condition is known then dont propogate value
|
||||||
if (tok->hasKnownIntValue())
|
if (tok->hasKnownIntValue())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Token* top = tok->astTop();
|
const Token* top = tok->astTop();
|
||||||
|
|
||||||
if (Token::Match(top, "%assign%"))
|
if (Token::Match(top, "%assign%"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) {
|
if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist,
|
bailout(tokenlist,
|
||||||
errorLogger,
|
errorLogger,
|
||||||
tok,
|
tok,
|
||||||
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
|
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if,macro => bailout
|
// if,macro => bailout
|
||||||
if (Token::simpleMatch(top->previous(), "if (") && top->previous()->isExpandedMacro()) {
|
if (Token::simpleMatch(top->previous(), "if (") && top->previous()->isExpandedMacro()) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist,
|
bailout(tokenlist,
|
||||||
errorLogger,
|
errorLogger,
|
||||||
tok,
|
tok,
|
||||||
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
|
"variable '" + cond.vartok->expressionString() + "', condition is defined in macro");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bailout: for/while-condition, variable is changed in while loop
|
// bailout: for/while-condition, variable is changed in while loop
|
||||||
if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) {
|
if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) {
|
||||||
|
|
||||||
// Variable changed in 3rd for-expression
|
// Variable changed in 3rd for-expression
|
||||||
if (Token::simpleMatch(top->previous(), "for (")) {
|
if (Token::simpleMatch(top->previous(), "for (")) {
|
||||||
if (top->astOperand2() && top->astOperand2()->astOperand2() &&
|
if (top->astOperand2() && top->astOperand2()->astOperand2() &&
|
||||||
isExpressionChanged(
|
isExpressionChanged(
|
||||||
cond.vartok, top->astOperand2()->astOperand2(), top->link(), settings, tokenlist->isCPP())) {
|
cond.vartok, top->astOperand2()->astOperand2(), top->link(), settings, tokenlist->isCPP())) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist,
|
bailout(tokenlist,
|
||||||
errorLogger,
|
errorLogger,
|
||||||
tok,
|
tok,
|
||||||
"variable '" + cond.vartok->expressionString() + "' used in loop");
|
"variable '" + cond.vartok->expressionString() + "' used in loop");
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable changed in loop code
|
|
||||||
if (Token::Match(top->previous(), "for|while (")) {
|
|
||||||
const Token* const start = top;
|
|
||||||
const Token* const block = top->link()->next();
|
|
||||||
const Token* const end = block->link();
|
|
||||||
|
|
||||||
if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
tok,
|
|
||||||
"variable '" + cond.vartok->expressionString() + "' used in loop");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ValueFlow::Value> values = cond.true_values;
|
|
||||||
if (cond.true_values != cond.false_values)
|
|
||||||
values.insert(values.end(), cond.false_values.begin(), cond.false_values.end());
|
|
||||||
|
|
||||||
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
|
|
||||||
if (Token::Match(tok, "<|>")) {
|
|
||||||
values.remove_if([](const ValueFlow::Value& v) {
|
|
||||||
if (v.isIntValue())
|
|
||||||
return v.intvalue != 0;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (values.empty())
|
|
||||||
|
// Variable changed in loop code
|
||||||
|
if (Token::Match(top->previous(), "for|while (")) {
|
||||||
|
const Token* const start = top;
|
||||||
|
const Token* const block = top->link()->next();
|
||||||
|
const Token* const end = block->link();
|
||||||
|
|
||||||
|
if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist,
|
||||||
|
errorLogger,
|
||||||
|
tok,
|
||||||
|
"variable '" + cond.vartok->expressionString() + "' used in loop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ValueFlow::Value> values = cond.true_values;
|
||||||
|
if (cond.true_values != cond.false_values)
|
||||||
|
values.insert(values.end(), cond.false_values.begin(), cond.false_values.end());
|
||||||
|
|
||||||
|
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
|
||||||
|
if (Token::Match(tok, "<|>")) {
|
||||||
|
values.remove_if([](const ValueFlow::Value& v) {
|
||||||
|
if (v.isIntValue())
|
||||||
|
return v.intvalue != 0;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED)
|
||||||
return;
|
return;
|
||||||
Token* startTok = tok->astParent() ? tok->astParent() : tok->previous();
|
}
|
||||||
reverse(startTok, cond.vartok, values, tokenlist, settings);
|
if (values.empty())
|
||||||
});
|
return;
|
||||||
|
Token* startTok = tok->astParent() ? tok->astParent() : tok->previous();
|
||||||
|
reverse(startTok, cond.vartok, values, tokenlist, settings);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void afterCondition(TokenList* tokenlist,
|
void afterCondition(TokenList* tokenlist,
|
||||||
|
@ -4214,211 +4219,211 @@ struct ConditionHandler {
|
||||||
symboldatabase,
|
symboldatabase,
|
||||||
errorLogger,
|
errorLogger,
|
||||||
settings,
|
settings,
|
||||||
[&](const Condition& cond, Token* tok, const Scope* scope, const std::vector<const Variable*>& vars) {
|
[&](const Condition& cond, Token* tok, const Scope* scope, const std::vector<const Variable*>& vars) {
|
||||||
if (Token::simpleMatch(tok->astParent(), "?"))
|
if (Token::simpleMatch(tok->astParent(), "?"))
|
||||||
return;
|
return;
|
||||||
const Token* top = tok->astTop();
|
const Token* top = tok->astTop();
|
||||||
|
|
||||||
std::list<ValueFlow::Value> thenValues;
|
std::list<ValueFlow::Value> thenValues;
|
||||||
std::list<ValueFlow::Value> elseValues;
|
std::list<ValueFlow::Value> elseValues;
|
||||||
|
|
||||||
if (!Token::Match(tok, "!=|=|(|.") && tok != cond.vartok) {
|
if (!Token::Match(tok, "!=|=|(|.") && tok != cond.vartok) {
|
||||||
thenValues.insert(thenValues.end(), cond.true_values.begin(), cond.true_values.end());
|
thenValues.insert(thenValues.end(), cond.true_values.begin(), cond.true_values.end());
|
||||||
if (isConditionKnown(tok, false))
|
if (isConditionKnown(tok, false))
|
||||||
insertImpossible(elseValues, cond.false_values);
|
insertImpossible(elseValues, cond.false_values);
|
||||||
|
}
|
||||||
|
if (!Token::Match(tok, "==|!")) {
|
||||||
|
elseValues.insert(elseValues.end(), cond.false_values.begin(), cond.false_values.end());
|
||||||
|
if (isConditionKnown(tok, true)) {
|
||||||
|
insertImpossible(thenValues, cond.true_values);
|
||||||
|
if (Token::Match(tok, "(|.|%var%") && astIsBool(tok))
|
||||||
|
insertNegateKnown(thenValues, cond.true_values);
|
||||||
}
|
}
|
||||||
if (!Token::Match(tok, "==|!")) {
|
}
|
||||||
elseValues.insert(elseValues.end(), cond.false_values.begin(), cond.false_values.end());
|
|
||||||
if (isConditionKnown(tok, true)) {
|
if (cond.inverted)
|
||||||
insertImpossible(thenValues, cond.true_values);
|
std::swap(thenValues, elseValues);
|
||||||
if (Token::Match(tok, "(|.|%var%") && astIsBool(tok))
|
|
||||||
insertNegateKnown(thenValues, cond.true_values);
|
if (Token::Match(tok->astParent(), "%oror%|&&")) {
|
||||||
|
Token* parent = tok->astParent();
|
||||||
|
if (astIsRHS(tok) && parent->astParent() && parent->str() == parent->astParent()->str())
|
||||||
|
parent = parent->astParent();
|
||||||
|
else if (!astIsLHS(tok)) {
|
||||||
|
parent = nullptr;
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
const std::string& op(parent->str());
|
||||||
|
std::list<ValueFlow::Value> values;
|
||||||
|
if (op == "&&")
|
||||||
|
values = thenValues;
|
||||||
|
else if (op == "||")
|
||||||
|
values = elseValues;
|
||||||
|
if (Token::Match(tok, "==|!="))
|
||||||
|
changePossibleToKnown(values);
|
||||||
|
if (!values.empty()) {
|
||||||
|
bool assign = false;
|
||||||
|
visitAstNodes(parent->astOperand2(), [&](Token* tok2) {
|
||||||
|
if (tok2 == tok)
|
||||||
|
return ChildrenToVisit::done;
|
||||||
|
if (isSameExpression(
|
||||||
|
tokenlist->isCPP(), false, cond.vartok, tok2, settings->library, true, false))
|
||||||
|
setTokenValue(tok2, values.front(), settings);
|
||||||
|
else if (Token::Match(tok2, "++|--|=") && isSameExpression(tokenlist->isCPP(),
|
||||||
|
false,
|
||||||
|
cond.vartok,
|
||||||
|
tok2->astOperand1(),
|
||||||
|
settings->library,
|
||||||
|
true,
|
||||||
|
false)) {
|
||||||
|
assign = true;
|
||||||
|
return ChildrenToVisit::done;
|
||||||
|
}
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
|
if (assign)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cond.inverted)
|
{
|
||||||
std::swap(thenValues, elseValues);
|
const Token* tok2 = tok;
|
||||||
|
std::string op;
|
||||||
if (Token::Match(tok->astParent(), "%oror%|&&")) {
|
bool mixedOperators = false;
|
||||||
Token* parent = tok->astParent();
|
while (tok2->astParent()) {
|
||||||
if (astIsRHS(tok) && parent->astParent() && parent->str() == parent->astParent()->str())
|
const Token* parent = tok2->astParent();
|
||||||
parent = parent->astParent();
|
if (Token::Match(parent, "%oror%|&&")) {
|
||||||
else if (!astIsLHS(tok)) {
|
if (op.empty()) {
|
||||||
parent = nullptr;
|
op = parent->str();
|
||||||
}
|
} else if (op != parent->str()) {
|
||||||
if (parent) {
|
mixedOperators = true;
|
||||||
const std::string& op(parent->str());
|
break;
|
||||||
std::list<ValueFlow::Value> values;
|
|
||||||
if (op == "&&")
|
|
||||||
values = thenValues;
|
|
||||||
else if (op == "||")
|
|
||||||
values = elseValues;
|
|
||||||
if (Token::Match(tok, "==|!="))
|
|
||||||
changePossibleToKnown(values);
|
|
||||||
if (!values.empty()) {
|
|
||||||
bool assign = false;
|
|
||||||
visitAstNodes(parent->astOperand2(), [&](Token* tok2) {
|
|
||||||
if (tok2 == tok)
|
|
||||||
return ChildrenToVisit::done;
|
|
||||||
if (isSameExpression(
|
|
||||||
tokenlist->isCPP(), false, cond.vartok, tok2, settings->library, true, false))
|
|
||||||
setTokenValue(tok2, values.front(), settings);
|
|
||||||
else if (Token::Match(tok2, "++|--|=") && isSameExpression(tokenlist->isCPP(),
|
|
||||||
false,
|
|
||||||
cond.vartok,
|
|
||||||
tok2->astOperand1(),
|
|
||||||
settings->library,
|
|
||||||
true,
|
|
||||||
false)) {
|
|
||||||
assign = true;
|
|
||||||
return ChildrenToVisit::done;
|
|
||||||
}
|
|
||||||
return ChildrenToVisit::op1_and_op2;
|
|
||||||
});
|
|
||||||
if (assign)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (parent->str() == "!") {
|
||||||
|
op = (op == "&&" ? "||" : "&&");
|
||||||
|
}
|
||||||
|
tok2 = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mixedOperators) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
|
||||||
|
// does condition reassign variable?
|
||||||
|
if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") &&
|
||||||
|
isVariablesChanged(top, top->link(), 0, vars, settings, tokenlist->isCPP())) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok, "assignment in condition");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start token of conditional code
|
||||||
|
Token* startTokens[] = {nullptr, nullptr};
|
||||||
|
|
||||||
|
// if astParent is "!" we need to invert codeblock
|
||||||
{
|
{
|
||||||
const Token* tok2 = tok;
|
const Token* tok2 = tok;
|
||||||
std::string op;
|
|
||||||
bool mixedOperators = false;
|
|
||||||
while (tok2->astParent()) {
|
while (tok2->astParent()) {
|
||||||
const Token* parent = tok2->astParent();
|
const Token* parent = tok2->astParent();
|
||||||
if (Token::Match(parent, "%oror%|&&")) {
|
while (parent && parent->str() == "&&")
|
||||||
if (op.empty()) {
|
parent = parent->astParent();
|
||||||
op = parent->str();
|
if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) {
|
||||||
} else if (op != parent->str()) {
|
std::swap(thenValues, elseValues);
|
||||||
mixedOperators = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (parent->str() == "!") {
|
|
||||||
op = (op == "&&" ? "||" : "&&");
|
|
||||||
}
|
}
|
||||||
tok2 = parent;
|
tok2 = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mixedOperators) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
|
// determine startToken(s)
|
||||||
// does condition reassign variable?
|
if (Token::simpleMatch(top->link(), ") {"))
|
||||||
if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") &&
|
startTokens[0] = top->link()->next();
|
||||||
isVariablesChanged(top, top->link(), 0, vars, settings, tokenlist->isCPP())) {
|
if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
||||||
|
startTokens[1] = top->link()->linkAt(1)->tokAt(2);
|
||||||
|
|
||||||
|
int changeBlock = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
const Token* const startToken = startTokens[i];
|
||||||
|
if (!startToken)
|
||||||
|
continue;
|
||||||
|
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
|
||||||
|
valueFlowSetConditionToKnown(tok, values, i == 0);
|
||||||
|
|
||||||
|
// TODO: The endToken should not be startTokens[i]->link() in the valueFlowForwardVariable call
|
||||||
|
if (forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings))
|
||||||
|
changeBlock = i;
|
||||||
|
changeKnownToPossible(values);
|
||||||
|
}
|
||||||
|
// TODO: Values changed in noreturn blocks should not bail
|
||||||
|
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist,
|
||||||
|
errorLogger,
|
||||||
|
startTokens[changeBlock]->link(),
|
||||||
|
"valueFlowAfterCondition: " + cond.vartok->expressionString() +
|
||||||
|
" is changed in conditional block");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After conditional code..
|
||||||
|
if (Token::simpleMatch(top->link(), ") {")) {
|
||||||
|
Token* after = top->link()->linkAt(1);
|
||||||
|
const Token* unknownFunction = nullptr;
|
||||||
|
const bool isWhile =
|
||||||
|
tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (");
|
||||||
|
bool dead_if = (!isBreakScope(after) && isWhile) ||
|
||||||
|
(isReturnScope(after, &settings->library, &unknownFunction) && !isWhile);
|
||||||
|
bool dead_else = false;
|
||||||
|
|
||||||
|
if (!dead_if && unknownFunction) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, tok, "assignment in condition");
|
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start token of conditional code
|
if (Token::simpleMatch(after, "} else {")) {
|
||||||
Token* startTokens[] = {nullptr, nullptr};
|
after = after->linkAt(2);
|
||||||
|
unknownFunction = nullptr;
|
||||||
// if astParent is "!" we need to invert codeblock
|
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
||||||
{
|
if (!dead_else && unknownFunction) {
|
||||||
const Token* tok2 = tok;
|
|
||||||
while (tok2->astParent()) {
|
|
||||||
const Token* parent = tok2->astParent();
|
|
||||||
while (parent && parent->str() == "&&")
|
|
||||||
parent = parent->astParent();
|
|
||||||
if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) {
|
|
||||||
std::swap(thenValues, elseValues);
|
|
||||||
}
|
|
||||||
tok2 = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine startToken(s)
|
|
||||||
if (Token::simpleMatch(top->link(), ") {"))
|
|
||||||
startTokens[0] = top->link()->next();
|
|
||||||
if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
|
||||||
startTokens[1] = top->link()->linkAt(1)->tokAt(2);
|
|
||||||
|
|
||||||
int changeBlock = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
const Token* const startToken = startTokens[i];
|
|
||||||
if (!startToken)
|
|
||||||
continue;
|
|
||||||
std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues);
|
|
||||||
valueFlowSetConditionToKnown(tok, values, i == 0);
|
|
||||||
|
|
||||||
// TODO: The endToken should not be startTokens[i]->link() in the valueFlowForwardVariable call
|
|
||||||
if (forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings))
|
|
||||||
changeBlock = i;
|
|
||||||
changeKnownToPossible(values);
|
|
||||||
}
|
|
||||||
// TODO: Values changed in noreturn blocks should not bail
|
|
||||||
if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
startTokens[changeBlock]->link(),
|
|
||||||
"valueFlowAfterCondition: " + cond.vartok->expressionString() +
|
|
||||||
" is changed in conditional block");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After conditional code..
|
|
||||||
if (Token::simpleMatch(top->link(), ") {")) {
|
|
||||||
Token* after = top->link()->linkAt(1);
|
|
||||||
const Token* unknownFunction = nullptr;
|
|
||||||
const bool isWhile =
|
|
||||||
tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (");
|
|
||||||
bool dead_if = (!isBreakScope(after) && isWhile) ||
|
|
||||||
(isReturnScope(after, &settings->library, &unknownFunction) && !isWhile);
|
|
||||||
bool dead_else = false;
|
|
||||||
|
|
||||||
if (!dead_if && unknownFunction) {
|
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(after, "} else {")) {
|
if (dead_if && dead_else)
|
||||||
after = after->linkAt(2);
|
return;
|
||||||
unknownFunction = nullptr;
|
|
||||||
dead_else = isReturnScope(after, &settings->library, &unknownFunction);
|
std::list<ValueFlow::Value> values;
|
||||||
if (!dead_else && unknownFunction) {
|
if (dead_if) {
|
||||||
if (settings->debugwarnings)
|
values = elseValues;
|
||||||
bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope");
|
} else if (dead_else) {
|
||||||
return;
|
values = thenValues;
|
||||||
}
|
} else {
|
||||||
}
|
std::copy_if(thenValues.begin(),
|
||||||
|
thenValues.end(),
|
||||||
if (dead_if && dead_else)
|
std::back_inserter(values),
|
||||||
return;
|
std::mem_fn(&ValueFlow::Value::isPossible));
|
||||||
|
std::copy_if(elseValues.begin(),
|
||||||
std::list<ValueFlow::Value> values;
|
elseValues.end(),
|
||||||
if (dead_if) {
|
std::back_inserter(values),
|
||||||
values = elseValues;
|
std::mem_fn(&ValueFlow::Value::isPossible));
|
||||||
} else if (dead_else) {
|
}
|
||||||
values = thenValues;
|
|
||||||
} else {
|
if (!values.empty()) {
|
||||||
std::copy_if(thenValues.begin(),
|
if ((dead_if || dead_else) && !Token::Match(tok->astParent(), "&&|&")) {
|
||||||
thenValues.end(),
|
valueFlowSetConditionToKnown(tok, values, true);
|
||||||
std::back_inserter(values),
|
valueFlowSetConditionToKnown(tok, values, false);
|
||||||
std::mem_fn(&ValueFlow::Value::isPossible));
|
|
||||||
std::copy_if(elseValues.begin(),
|
|
||||||
elseValues.end(),
|
|
||||||
std::back_inserter(values),
|
|
||||||
std::mem_fn(&ValueFlow::Value::isPossible));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!values.empty()) {
|
|
||||||
if ((dead_if || dead_else) && !Token::Match(tok->astParent(), "&&|&")) {
|
|
||||||
valueFlowSetConditionToKnown(tok, values, true);
|
|
||||||
valueFlowSetConditionToKnown(tok, values, false);
|
|
||||||
}
|
|
||||||
forward(after, scope->bodyEnd, cond.vartok, values, tokenlist, settings);
|
|
||||||
}
|
}
|
||||||
|
forward(after, scope->bodyEnd, cond.vartok, values, tokenlist, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
virtual ~ConditionHandler() {}
|
virtual ~ConditionHandler() {}
|
||||||
};
|
};
|
||||||
|
@ -4447,8 +4452,7 @@ struct SimpleConditionHandler : ConditionHandler {
|
||||||
const Token* exprTok,
|
const Token* exprTok,
|
||||||
const std::list<ValueFlow::Value>& values,
|
const std::list<ValueFlow::Value>& values,
|
||||||
TokenList* tokenlist,
|
TokenList* tokenlist,
|
||||||
const Settings* settings) const OVERRIDE
|
const Settings* settings) const OVERRIDE {
|
||||||
{
|
|
||||||
return valueFlowReverse(start, exprTok, values, tokenlist, settings);
|
return valueFlowReverse(start, exprTok, values, tokenlist, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6155,8 +6159,7 @@ struct ContainerConditionHandler : ConditionHandler {
|
||||||
const Token* exprTok,
|
const Token* exprTok,
|
||||||
const std::list<ValueFlow::Value>& values,
|
const std::list<ValueFlow::Value>& values,
|
||||||
TokenList* tokenlist,
|
TokenList* tokenlist,
|
||||||
const Settings* settings) const OVERRIDE
|
const Settings* settings) const OVERRIDE {
|
||||||
{
|
|
||||||
if (values.empty())
|
if (values.empty())
|
||||||
return;
|
return;
|
||||||
if (!exprTok->variable())
|
if (!exprTok->variable())
|
||||||
|
|
|
@ -2102,7 +2102,7 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void nullpointer68() {
|
void nullpointer68() {
|
||||||
check("struct A {\n"
|
check("struct A {\n"
|
||||||
" A* b;\n"
|
" A* b;\n"
|
||||||
|
|
Loading…
Reference in New Issue