Fix 11610: false negative: knownConditionTrueFalse with address of variable (#4883)
This commit is contained in:
parent
a75392307f
commit
61e8b84578
|
@ -1591,7 +1591,8 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure())
|
if (!tok1->function()->isConst() && !tok1->function()->isAttributeConst() &&
|
||||||
|
!tok1->function()->isAttributePure())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1965,7 +1965,7 @@ void CheckStl::string_c_str()
|
||||||
string_c_strError(tok);
|
string_c_strError(tok);
|
||||||
} else if (printPerformance && tok->tokAt(1)->astOperand2() && Token::Match(tok->tokAt(1)->astOperand2()->tokAt(-3), "%var% . c_str|data ( ) ;")) {
|
} else if (printPerformance && tok->tokAt(1)->astOperand2() && Token::Match(tok->tokAt(1)->astOperand2()->tokAt(-3), "%var% . c_str|data ( ) ;")) {
|
||||||
const Token* vartok = tok->tokAt(1)->astOperand2()->tokAt(-3);
|
const Token* vartok = tok->tokAt(1)->astOperand2()->tokAt(-3);
|
||||||
if (tok->variable() && tok->variable()->isStlStringType() && vartok->variable() && vartok->variable()->isStlStringType())
|
if (tok->variable()->isStlStringType() && vartok->variable() && vartok->variable()->isStlStringType())
|
||||||
string_c_strAssignment(tok);
|
string_c_strAssignment(tok);
|
||||||
}
|
}
|
||||||
} else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && tok->str() != scope.className) {
|
} else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && tok->str() != scope.className) {
|
||||||
|
|
|
@ -1612,7 +1612,7 @@ void CheckUninitVar::valueFlowUninit()
|
||||||
bool uninitderef = false;
|
bool uninitderef = false;
|
||||||
if (tok->variable()) {
|
if (tok->variable()) {
|
||||||
bool unknown;
|
bool unknown;
|
||||||
const bool isarray = !tok->variable() || tok->variable()->isArray();
|
const bool isarray = tok->variable()->isArray();
|
||||||
const bool ispointer = astIsPointer(tok) && !isarray;
|
const bool ispointer = astIsPointer(tok) && !isarray;
|
||||||
const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, mSettings);
|
const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, mSettings);
|
||||||
if (ispointer && v->indirect == 1 && !deref)
|
if (ispointer && v->indirect == 1 && !deref)
|
||||||
|
|
|
@ -269,6 +269,12 @@ static void addToErrorPath(ValueFlow::Value& value, const std::vector<const Valu
|
||||||
[&](const ErrorPathItem& e) {
|
[&](const ErrorPathItem& e) {
|
||||||
return locations.insert(e.first).second;
|
return locations.insert(e.first).second;
|
||||||
});
|
});
|
||||||
|
std::copy_if(ref->debugPath.cbegin(),
|
||||||
|
ref->debugPath.cend(),
|
||||||
|
std::back_inserter(value.debugPath),
|
||||||
|
[&](const ErrorPathItem& e) {
|
||||||
|
return locations.insert(e.first).second;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6138,7 +6138,7 @@ void SymbolDatabase::setValueType(Token* tok, const ValueType& valuetype, Source
|
||||||
if (!parent->astOperand1())
|
if (!parent->astOperand1())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ValueType *vt1 = parent->astOperand1() ? parent->astOperand1()->valueType() : nullptr;
|
const ValueType *vt1 = parent->astOperand1()->valueType();
|
||||||
const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr;
|
const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr;
|
||||||
|
|
||||||
if (vt1 && Token::Match(parent, "<<|>>")) {
|
if (vt1 && Token::Match(parent, "<<|>>")) {
|
||||||
|
|
|
@ -508,6 +508,24 @@ static bool isComputableValue(const Token* parent, const ValueFlow::Value& value
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Library::Container::Yield getContainerYield(Token* tok, const Settings* settings, Token** parent = nullptr)
|
||||||
|
{
|
||||||
|
if (Token::Match(tok, ". %name% (") && tok->astParent() == tok->tokAt(2) && tok->astOperand1() &&
|
||||||
|
tok->astOperand1()->valueType()) {
|
||||||
|
const Library::Container* c = getLibraryContainer(tok->astOperand1());
|
||||||
|
if (parent)
|
||||||
|
*parent = tok->astParent();
|
||||||
|
return c ? c->getYield(tok->strAt(1)) : Library::Container::Yield::NO_YIELD;
|
||||||
|
} else if (Token::Match(tok->previous(), "%name% (")) {
|
||||||
|
if (parent)
|
||||||
|
*parent = tok;
|
||||||
|
if (const Library::Function* f = settings->library.getFunction(tok->previous())) {
|
||||||
|
return f->containerYield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Library::Container::Yield::NO_YIELD;
|
||||||
|
}
|
||||||
|
|
||||||
/** Set token value for cast */
|
/** Set token value for cast */
|
||||||
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings);
|
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings);
|
||||||
|
|
||||||
|
@ -663,36 +681,25 @@ static void setTokenValue(Token* tok,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) &&
|
Token* next = nullptr;
|
||||||
parent->astOperand1() && parent->astOperand1()->valueType()) {
|
const Library::Container::Yield yields = getContainerYield(parent, settings, &next);
|
||||||
const Library::Container* c = getLibraryContainer(parent->astOperand1());
|
|
||||||
const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD;
|
|
||||||
if (yields == Library::Container::Yield::SIZE) {
|
if (yields == Library::Container::Yield::SIZE) {
|
||||||
ValueFlow::Value v(value);
|
ValueFlow::Value v(value);
|
||||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
v.valueType = ValueFlow::Value::ValueType::INT;
|
||||||
setTokenValue(parent->astParent(), std::move(v), settings);
|
setTokenValue(next, std::move(v), settings);
|
||||||
} else if (yields == Library::Container::Yield::EMPTY) {
|
} else if (yields == Library::Container::Yield::EMPTY) {
|
||||||
ValueFlow::Value v(value);
|
ValueFlow::Value v(value);
|
||||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
v.valueType = ValueFlow::Value::ValueType::INT;
|
||||||
if (value.isImpossible() && value.intvalue == 0)
|
v.bound = ValueFlow::Value::Bound::Point;
|
||||||
|
if (value.isImpossible()) {
|
||||||
|
if (value.intvalue == 0)
|
||||||
v.setKnown();
|
v.setKnown();
|
||||||
else
|
else
|
||||||
|
v.setPossible();
|
||||||
|
} else {
|
||||||
v.intvalue = !v.intvalue;
|
v.intvalue = !v.intvalue;
|
||||||
setTokenValue(parent->astParent(), std::move(v), settings);
|
|
||||||
}
|
|
||||||
} else if (Token::Match(parent->previous(), "%name% (")) {
|
|
||||||
if (const Library::Function* f = settings->library.getFunction(parent->previous())) {
|
|
||||||
if (f->containerYield == Library::Container::Yield::SIZE) {
|
|
||||||
ValueFlow::Value v(value);
|
|
||||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
|
||||||
setTokenValue(parent, std::move(v), settings);
|
|
||||||
} else if (f->containerYield == Library::Container::Yield::EMPTY) {
|
|
||||||
ValueFlow::Value v(value);
|
|
||||||
v.intvalue = !v.intvalue;
|
|
||||||
v.valueType = ValueFlow::Value::ValueType::INT;
|
|
||||||
setTokenValue(parent, std::move(v), settings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setTokenValue(next, std::move(v), settings);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -917,6 +924,9 @@ static void setTokenValue(Token* tok,
|
||||||
if (val.isImpossible() && val.intvalue != 0)
|
if (val.isImpossible() && val.intvalue != 0)
|
||||||
continue;
|
continue;
|
||||||
ValueFlow::Value v(val);
|
ValueFlow::Value v(val);
|
||||||
|
if (val.isImpossible())
|
||||||
|
v.setKnown();
|
||||||
|
else
|
||||||
v.intvalue = !v.intvalue;
|
v.intvalue = !v.intvalue;
|
||||||
setTokenValue(parent, std::move(v), settings);
|
setTokenValue(parent, std::move(v), settings);
|
||||||
}
|
}
|
||||||
|
@ -1859,7 +1869,7 @@ static void valueFlowImpossibleValues(TokenList* tokenList, const Settings* sett
|
||||||
ValueFlow::Value value{0};
|
ValueFlow::Value value{0};
|
||||||
value.setImpossible();
|
value.setImpossible();
|
||||||
setTokenValue(tok->linkAt(1)->next(), std::move(value), settings);
|
setTokenValue(tok->linkAt(1)->next(), std::move(value), settings);
|
||||||
} else if (tokenList->isCPP() && Token::simpleMatch(tok, "this")) {
|
} else if ((tokenList->isCPP() && Token::simpleMatch(tok, "this")) || tok->isUnaryOp("&")) {
|
||||||
ValueFlow::Value value{0};
|
ValueFlow::Value value{0};
|
||||||
value.setImpossible();
|
value.setImpossible();
|
||||||
setTokenValue(tok, std::move(value), settings);
|
setTokenValue(tok, std::move(value), settings);
|
||||||
|
@ -6666,16 +6676,7 @@ static void valueFlowInferCondition(TokenList* tokenlist,
|
||||||
continue;
|
continue;
|
||||||
if (tok->hasKnownIntValue())
|
if (tok->hasKnownIntValue())
|
||||||
continue;
|
continue;
|
||||||
if (tok->variable() && (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
|
if (Token::Match(tok, "%comp%|-") && tok->astOperand1() && tok->astOperand2()) {
|
||||||
Token::Match(tok->astParent()->previous(), "if|while ("))) {
|
|
||||||
std::vector<ValueFlow::Value> result = infer(IntegralInferModel{}, "!=", tok->values(), 0);
|
|
||||||
if (result.size() != 1)
|
|
||||||
continue;
|
|
||||||
ValueFlow::Value value = result.front();
|
|
||||||
value.intvalue = 1;
|
|
||||||
value.bound = ValueFlow::Value::Bound::Point;
|
|
||||||
setTokenValue(tok, std::move(value), settings);
|
|
||||||
} else if (Token::Match(tok, "%comp%|-") && tok->astOperand1() && tok->astOperand2()) {
|
|
||||||
if (astIsIterator(tok->astOperand1()) || astIsIterator(tok->astOperand2())) {
|
if (astIsIterator(tok->astOperand1()) || astIsIterator(tok->astOperand2())) {
|
||||||
static const std::array<ValuePtr<InferModel>, 2> iteratorModels = {EndIteratorInferModel{},
|
static const std::array<ValuePtr<InferModel>, 2> iteratorModels = {EndIteratorInferModel{},
|
||||||
StartIteratorInferModel{}};
|
StartIteratorInferModel{}};
|
||||||
|
@ -6694,6 +6695,15 @@ static void valueFlowInferCondition(TokenList* tokenlist,
|
||||||
setTokenValue(tok, std::move(value), settings);
|
setTokenValue(tok, std::move(value), settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
|
||||||
|
Token::Match(tok->astParent()->previous(), "if|while (")) {
|
||||||
|
std::vector<ValueFlow::Value> result = infer(IntegralInferModel{}, "!=", tok->values(), 0);
|
||||||
|
if (result.size() != 1)
|
||||||
|
continue;
|
||||||
|
ValueFlow::Value value = result.front();
|
||||||
|
value.intvalue = 1;
|
||||||
|
value.bound = ValueFlow::Value::Bound::Point;
|
||||||
|
setTokenValue(tok, std::move(value), settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,11 +353,14 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// no crash on unary operator& (#5643)
|
// no crash on unary operator& (#5643)
|
||||||
|
// #11610
|
||||||
check("SdrObject* ApplyGraphicToObject() {\n"
|
check("SdrObject* ApplyGraphicToObject() {\n"
|
||||||
" if (&rHitObject) {}\n"
|
" if (&rHitObject) {}\n"
|
||||||
" else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n"
|
" else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("[test.cpp:2]: (style) Condition '&rHitObject' is always true\n"
|
||||||
|
"[test.cpp:3]: (style) Condition '!&rHitObject' is always false\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
// #5695: increment
|
// #5695: increment
|
||||||
check("void f(int a0, int n) {\n"
|
check("void f(int a0, int n) {\n"
|
||||||
|
@ -3800,6 +3803,7 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// TODO: if (!v) is a known condition as well
|
||||||
check("struct a {\n"
|
check("struct a {\n"
|
||||||
" int *b();\n"
|
" int *b();\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
|
@ -3813,7 +3817,7 @@ private:
|
||||||
" if (v == nullptr && e) {}\n"
|
" if (v == nullptr && e) {}\n"
|
||||||
" return d;\n"
|
" return d;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("[test.cpp:11]: (style) Condition 'e' is always true\n", errout.str());
|
||||||
|
|
||||||
// #10037
|
// #10037
|
||||||
check("struct a {\n"
|
check("struct a {\n"
|
||||||
|
|
|
@ -1072,6 +1072,13 @@ private:
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
|
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
|
||||||
|
|
||||||
|
code = "void f(int i) {\n"
|
||||||
|
" int * p = &i;\n"
|
||||||
|
" bool x = !p || i;\n"
|
||||||
|
" bool a = x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfX(code, 4U, 1));
|
||||||
|
|
||||||
code = "bool f(const uint16_t * const p) {\n"
|
code = "bool f(const uint16_t * const p) {\n"
|
||||||
" const uint8_t x = (uint8_t)(*p & 0x01E0U) >> 5U;\n"
|
" const uint8_t x = (uint8_t)(*p & 0x01E0U) >> 5U;\n"
|
||||||
" return x != 0;\n"
|
" return x != 0;\n"
|
||||||
|
|
Loading…
Reference in New Issue