assert and fixed some flawed known value usage#7394
assert and fixed some flawed known value usage#7394firewave wants to merge 3 commits intocppcheck-opensource:mainfrom
Conversation
| // is condition always true/false? | ||
| if (parent->astOperand1()->hasKnownValue()) { | ||
| const Value &condvalue = parent->astOperand1()->values().front(); | ||
| assert(condvalue.isKnown()); |
There was a problem hiding this comment.
This fails in at least TestLeakAutoVar::assign26. It has two values - the first is INT and Possible, and the other is BUFFER_SIZE and Known.
There was a problem hiding this comment.
@pfultz2 Should this be actually checking for a known INT value?
There was a problem hiding this comment.
Well its checking the condition based on a known int value or known tok value. I dont know if the known tok value is needed anymore.
There was a problem hiding this comment.
Oh - I didn't look at the subsequent code. Sorry about that.
Based on that I think it should be simply calling getKnownValue(). Will give it a spin tomorrow.
There was a problem hiding this comment.
Tests still pass with the change.
| { | ||
| if (!mImpl->mValues) | ||
| return nullptr; | ||
| if (mImpl->mValues->empty()) |
There was a problem hiding this comment.
this code is technically redundant.
There was a problem hiding this comment.
this redundant code is still here.
if (mImpl->mValues->empty())
return nullptr;
was it kept by intention?
There was a problem hiding this comment.
How is it redundant? It might not be set or it might be empty. That is not the same. Or do you mean that the function as a whole is mostly redundant to the other one.
But it is still WIP. I forgot to mark it as draft again.
There was a problem hiding this comment.
This:
if (mImpl->mValues->empty())
return nullptr;
auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) {
return value.isKnown();
});
return it == mImpl->mValues->end() ? nullptr : &*it;
}
Is logically the same if the first 2 lines are removed:
auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) {
return value.isKnown();
});
return it == mImpl->mValues->end() ? nullptr : &*it;
if mImpl->mValues is empty then it will be mImpl->mValues->end() and that means nullptr is returned.
So I suggest to remove the extra code.
There was a problem hiding this comment.
Now I get it.
That would not be the same since the lamdba might still be created even though it is never used. I think I saw a similar optimization somewhere recently (but it is possible that was copying data in the capture). Need to have a look at the generated code and/or profile it.
| case Library::AllocFunc::BufferSize::strdup: | ||
| if (arg1 && arg1->hasKnownValue()) { | ||
| const ValueFlow::Value& value = arg1->values().back(); | ||
| assert(value.isKnown()); |
There was a problem hiding this comment.
The code contradicts itself. It checks if there is a known value from the front but then always uses the last value which might not be known. The assert is to detect such a case.
Based on the check it should also be calling getKnownValue() but I do not want to change that blindly without a case hitting it.
There was a problem hiding this comment.
If I comment out these lines :
//const ValueFlow::Value& value = arg1->values().back();
//if (value.isTokValue() && value.tokvalue->tokType() == Token::eString)
// sizeValue = Token::getStrLength(value.tokvalue) + 1; // Add one for the null terminator
Then this test fails: TestValueFlow::valueFlowDynamicBufferSize
So this code is hit by a test case.
There was a problem hiding this comment.
This is about some input hitting the wrong assumption and not the code in general.
There was a problem hiding this comment.
It is not enforced to be the last element here, so this is definitely a bug. It should call getKnownValue(Value::ValueType::TOK).
There was a problem hiding this comment.
Thanks for the clarification.
There was a problem hiding this comment.
Done. All tests still pass.
| if (parent->astOperand1()->hasKnownValue()) { | ||
| const Value &condvalue = parent->astOperand1()->values().front(); | ||
| const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0)); | ||
| if (const Value* condvalue = parent->astOperand1()->getKnownValue()) { |
There was a problem hiding this comment.
This should use getKnownValue(Value::ValueType::TOK) and getKnownValue(Value::ValueType::INT) as getKnownValue can return a different type so it wont use the tokvalue if when its available.
In this case, since we prefer intvalue over tokvalue, we could make a getKnownValue that takes an initializer list and returns the first type found in the list getKnownValue({Value::ValueType::INT, Value::ValueType::TOK}):
const ValueFlow::Value* Token::getKnownValue(std::initializer_list<Value::ValueType> types) const
{
for(auto t:types) {
const ValueFlow::Value* value = getKnownValue(t);
if(value)
return value;
}
return nullptr;
}There was a problem hiding this comment.
Thanks for the clarification.
This might cause multiple lookups which would be bad in a hot path - see #6701 for such a case. But correctness is obviously first priority. So we could adjust that later on in the other PR.
No description provided.