Merge pull request #2735 from pfultz2/container-forward
Use ForwardAnalyzer for container forward
This commit is contained in:
commit
7ff5a208a5
|
@ -139,8 +139,6 @@ void CheckThread::run()
|
||||||
|
|
||||||
void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName)
|
void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName)
|
||||||
{
|
{
|
||||||
QString dumpFile;
|
|
||||||
|
|
||||||
foreach (const QString addon, mAddonsAndTools) {
|
foreach (const QString addon, mAddonsAndTools) {
|
||||||
if (addon == CLANG_ANALYZER || addon == CLANG_TIDY) {
|
if (addon == CLANG_ANALYZER || addon == CLANG_TIDY) {
|
||||||
if (!fileSettings)
|
if (!fileSettings)
|
||||||
|
@ -298,10 +296,6 @@ void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSetti
|
||||||
parseClangErrors(addon, fileName, errout);
|
parseClangErrors(addon, fileName, errout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dumpFile.isEmpty()) {
|
|
||||||
QFile::remove(dumpFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckThread::stop()
|
void CheckThread::stop()
|
||||||
|
|
|
@ -70,7 +70,7 @@ struct ForwardTraversal {
|
||||||
Progress p = traverseTok(tok, f, traverseUnknown);
|
Progress p = traverseTok(tok, f, traverseUnknown);
|
||||||
if (p == Progress::Break)
|
if (p == Progress::Break)
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
if (p == Progress::Continue && traverseRecursive(tok->astOperand2(), f, traverseUnknown, recursion+1) == Progress::Break)
|
if (p == Progress::Continue && tok->astOperand2() && traverseRecursive(tok->astOperand2(), f, traverseUnknown, recursion+1) == Progress::Break)
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
return Progress::Continue;
|
return Progress::Continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,15 @@ bool ProgramMemory::getTokValue(nonneg int varid, const Token** result) const
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProgramMemory::getContainerSizeValue(nonneg int varid, MathLib::bigint* result) const
|
||||||
|
{
|
||||||
|
const ProgramMemory::Map::const_iterator it = values.find(varid);
|
||||||
|
const bool found = it != values.end() && it->second.isContainerSizeValue();
|
||||||
|
if (found)
|
||||||
|
*result = it->second.intvalue;
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
void ProgramMemory::setUnknown(nonneg int varid)
|
void ProgramMemory::setUnknown(nonneg int varid)
|
||||||
{
|
{
|
||||||
values[varid].valueType = ValueFlow::Value::ValueType::UNINIT;
|
values[varid].valueType = ValueFlow::Value::ValueType::UNINIT;
|
||||||
|
@ -534,6 +543,25 @@ void execute(const Token *expr,
|
||||||
else
|
else
|
||||||
*error = true;
|
*error = true;
|
||||||
}
|
}
|
||||||
|
else if (Token::Match(expr->tokAt(-3), "%var% . %name% (")) {
|
||||||
|
const Token* containerTok = expr->tokAt(-3);
|
||||||
|
if (astIsContainer(containerTok)) {
|
||||||
|
Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
|
||||||
|
if (yield == Library::Container::Yield::SIZE) {
|
||||||
|
if (!programMemory->getContainerSizeValue(containerTok->varId(), result))
|
||||||
|
*error = true;
|
||||||
|
} else if (yield == Library::Container::Yield::EMPTY) {
|
||||||
|
MathLib::bigint size = 0;
|
||||||
|
if (!programMemory->getContainerSizeValue(containerTok->varId(), &size))
|
||||||
|
*error = true;
|
||||||
|
*result = (size == 0);
|
||||||
|
} else {
|
||||||
|
*error = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
*error = true;
|
*error = true;
|
||||||
|
|
|
@ -19,6 +19,8 @@ struct ProgramMemory {
|
||||||
bool getIntValue(nonneg int varid, MathLib::bigint* result) const;
|
bool getIntValue(nonneg int varid, MathLib::bigint* result) const;
|
||||||
void setIntValue(nonneg int varid, MathLib::bigint value);
|
void setIntValue(nonneg int varid, MathLib::bigint value);
|
||||||
|
|
||||||
|
bool getContainerSizeValue(nonneg int varid, MathLib::bigint* result) const;
|
||||||
|
|
||||||
void setUnknown(nonneg int varid);
|
void setUnknown(nonneg int varid);
|
||||||
|
|
||||||
bool getTokValue(nonneg int varid, const Token** result) const;
|
bool getTokValue(nonneg int varid, const Token** result) const;
|
||||||
|
|
|
@ -1053,8 +1053,6 @@ void Token::insertToken(const std::string &tokenStr, const std::string &original
|
||||||
scope = tok1->strAt(-3) + " :: " + scope;
|
scope = tok1->strAt(-3) + " :: " + scope;
|
||||||
tok1 = tok1->tokAt(-2);
|
tok1 = tok1->tokAt(-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nextScopeNameAddition.empty() && !scope.empty()) nextScopeNameAddition += " :: ";
|
|
||||||
nextScopeNameAddition += scope;
|
nextScopeNameAddition += scope;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2285,13 +2285,6 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isWritableValue(const Token* tok) const {
|
|
||||||
const ValueFlow::Value* value = getValue(tok);
|
|
||||||
if (value)
|
|
||||||
return value->isIntValue() || value->isFloatValue();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool isGlobal() const {
|
virtual bool isGlobal() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2336,33 +2329,76 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
return Action::None;
|
return Action::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual Action isWritable(const Token* tok) const {
|
||||||
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
|
if (!value)
|
||||||
|
return Action::None;
|
||||||
|
if (!(value->isIntValue() || value->isFloatValue()))
|
||||||
|
return Action::None;
|
||||||
|
const Token* parent = tok->astParent();
|
||||||
|
|
||||||
|
if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
|
||||||
|
parent->astOperand2()->hasKnownValue()) {
|
||||||
|
const Token* rhs = parent->astOperand2();
|
||||||
|
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
|
||||||
|
Action a;
|
||||||
|
if (!rhsValue)
|
||||||
|
a = Action::Invalid;
|
||||||
|
else
|
||||||
|
a = Action::Write;
|
||||||
|
if (parent->str() != "=")
|
||||||
|
a |= Action::Read;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment/decrement
|
||||||
|
if (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--")) {
|
||||||
|
return Action::Read | Action::Write;
|
||||||
|
}
|
||||||
|
return Action::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void writeValue(ValueFlow::Value* value, const Token* tok) const {
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
if (!tok->astParent())
|
||||||
|
return;
|
||||||
|
if (tok->astParent()->isAssignmentOp()) {
|
||||||
|
// 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());
|
||||||
|
if (tok->astParent()->str() == "=")
|
||||||
|
value->errorPath.clear();
|
||||||
|
value->errorPath.emplace_back(tok, info);
|
||||||
|
} else {
|
||||||
|
// TODO: Don't set to zero
|
||||||
|
value->intvalue = 0;
|
||||||
|
}
|
||||||
|
} else if (tok->astParent()->tokType() == Token::eIncDecOp) {
|
||||||
|
const bool inc = tok->astParent()->str() == "++";
|
||||||
|
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 Action analyze(const Token* tok) const OVERRIDE {
|
virtual Action analyze(const Token* tok) const OVERRIDE {
|
||||||
if (invalid())
|
if (invalid())
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
if (match(tok)) {
|
if (match(tok)) {
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
if ((Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
||||||
return Action::Read;
|
return Action::Read;
|
||||||
|
|
||||||
Action read = Action::Read;
|
// Action read = Action::Read;
|
||||||
if (parent && isWritableValue(tok) && parent->isAssignmentOp() && astIsLHS(tok) &&
|
Action w = isWritable(tok);
|
||||||
parent->astOperand2()->hasKnownValue()) {
|
if (w != Action::None)
|
||||||
const Token* rhs = parent->astOperand2();
|
return w;
|
||||||
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
|
|
||||||
Action a;
|
|
||||||
if (!rhsValue)
|
|
||||||
a = Action::Invalid;
|
|
||||||
else
|
|
||||||
a = Action::Write;
|
|
||||||
if (parent->str() != "=")
|
|
||||||
a |= Action::Read;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment/decrement
|
|
||||||
if (isWritableValue(tok) && (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--"))) {
|
|
||||||
return read | Action::Write;
|
|
||||||
}
|
|
||||||
// Check for modifications by function calls
|
// Check for modifications by function calls
|
||||||
return isModified(tok);
|
return isModified(tok);
|
||||||
} else if (tok->isUnaryOp("*")) {
|
} else if (tok->isUnaryOp("*")) {
|
||||||
|
@ -2432,27 +2468,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
if (a.isInconclusive())
|
if (a.isInconclusive())
|
||||||
lowerToInconclusive();
|
lowerToInconclusive();
|
||||||
if (a.isWrite() && tok->astParent()) {
|
if (a.isWrite() && tok->astParent()) {
|
||||||
if (tok->astParent()->isAssignmentOp()) {
|
writeValue(value, tok);
|
||||||
// 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());
|
|
||||||
if (tok->astParent()->str() == "=")
|
|
||||||
value->errorPath.clear();
|
|
||||||
value->errorPath.emplace_back(tok, info);
|
|
||||||
} else {
|
|
||||||
// TODO: Don't set to zero
|
|
||||||
value->intvalue = 0;
|
|
||||||
}
|
|
||||||
} else if (tok->astParent()->tokType() == Token::eIncDecOp) {
|
|
||||||
const bool inc = tok->astParent()->str() == "++";
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2597,6 +2613,31 @@ struct VariableForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::vector<const Variable*> getAliasesFromValues(std::list<ValueFlow::Value> values, bool address=false)
|
||||||
|
{
|
||||||
|
std::vector<const Variable*> aliases;
|
||||||
|
for (const ValueFlow::Value& v : values) {
|
||||||
|
if (!v.tokvalue)
|
||||||
|
continue;
|
||||||
|
const Token* lifeTok = nullptr;
|
||||||
|
for (const ValueFlow::Value& lv:v.tokvalue->values()) {
|
||||||
|
if (!lv.isLocalLifetimeValue())
|
||||||
|
continue;
|
||||||
|
if (address && lv.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
|
||||||
|
continue;
|
||||||
|
if (lifeTok) {
|
||||||
|
lifeTok = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lifeTok = lv.tokvalue;
|
||||||
|
}
|
||||||
|
if (lifeTok && lifeTok->variable()) {
|
||||||
|
aliases.push_back(lifeTok->variable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowForwardVariable(Token* const startToken,
|
static void valueFlowForwardVariable(Token* const startToken,
|
||||||
const Token* const endToken,
|
const Token* const endToken,
|
||||||
const Variable* const var,
|
const Variable* const var,
|
||||||
|
@ -2622,25 +2663,7 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
ErrorLogger* const,
|
ErrorLogger* const,
|
||||||
const Settings* const settings)
|
const Settings* const settings)
|
||||||
{
|
{
|
||||||
std::vector<const Variable*> aliases;
|
valueFlowForwardVariable(startToken, endToken, var, std::move(values), getAliasesFromValues(values), tokenlist, settings);
|
||||||
for (const ValueFlow::Value& v : values) {
|
|
||||||
if (!v.tokvalue)
|
|
||||||
continue;
|
|
||||||
const Token* lifeTok = nullptr;
|
|
||||||
for (const ValueFlow::Value& lv:v.tokvalue->values()) {
|
|
||||||
if (!lv.isLocalLifetimeValue())
|
|
||||||
continue;
|
|
||||||
if (lifeTok) {
|
|
||||||
lifeTok = nullptr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lifeTok = lv.tokvalue;
|
|
||||||
}
|
|
||||||
if (lifeTok && lifeTok->variable()) {
|
|
||||||
aliases.push_back(lifeTok->variable());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valueFlowForwardVariable(startToken, endToken, var, std::move(values), aliases, tokenlist, settings);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5519,6 +5542,7 @@ static bool isContainerEmpty(const Token* tok)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
static bool isContainerSizeChanged(const Token *tok, int depth=20);
|
||||||
|
|
||||||
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20);
|
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20);
|
||||||
|
|
||||||
|
@ -5592,64 +5616,128 @@ static void valueFlowContainerReverse(Token *tok, nonneg int containerId, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowContainerForward(Token *tok, nonneg int containerId, ValueFlow::Value value, const Settings *settings, bool cpp)
|
struct ContainerVariableForwardAnalyzer : VariableForwardAnalyzer {
|
||||||
{
|
ContainerVariableForwardAnalyzer()
|
||||||
while (nullptr != (tok = tok->next())) {
|
: VariableForwardAnalyzer()
|
||||||
if (Token::Match(tok, "[{}]"))
|
{}
|
||||||
break;
|
|
||||||
if (Token::Match(tok, "while|for (")) {
|
ContainerVariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
|
||||||
const Token *start = tok->linkAt(1)->next();
|
: VariableForwardAnalyzer(v, val, std::move(paliases), t) {}
|
||||||
if (!Token::simpleMatch(start->link(), "{"))
|
|
||||||
break;
|
virtual Action isWritable(const Token* tok) const OVERRIDE {
|
||||||
if (isContainerSizeChanged(containerId, start, start->link()))
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
break;
|
if (!value)
|
||||||
}
|
return Action::None;
|
||||||
if (Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->previous(), "while|for|if (")) {
|
if (!tok->valueType() || !tok->valueType()->container)
|
||||||
const Token *start = tok->next();
|
return Action::None;
|
||||||
if (isContainerSizeChanged(containerId, start, start->link()) || isEscapeScope(start, nullptr))
|
const Token* parent = tok->astParent();
|
||||||
break;
|
|
||||||
tok = start->link();
|
if (tok->valueType()->container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) {
|
||||||
if (Token::simpleMatch(tok, "} else {")) {
|
const Token* rhs = parent->astOperand2();
|
||||||
start = tok->tokAt(2);
|
|
||||||
if (isContainerSizeChanged(containerId, start, start->link()))
|
|
||||||
break;
|
|
||||||
tok = start->link();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tok->varId() != containerId)
|
|
||||||
continue;
|
|
||||||
if (Token::Match(tok, "%name% ="))
|
|
||||||
break;
|
|
||||||
if (Token::Match(tok, "%name% +=")) {
|
|
||||||
if (!tok->valueType() || !tok->valueType()->container || !tok->valueType()->container->stdStringLike)
|
|
||||||
break;
|
|
||||||
const Token *rhs = tok->next()->astOperand2();
|
|
||||||
if (rhs->tokType() == Token::eString)
|
if (rhs->tokType() == Token::eString)
|
||||||
value.intvalue += Token::getStrLength(rhs);
|
return Action::Read | Action::Write;
|
||||||
|
if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) {
|
||||||
|
if (std::any_of(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value &rhsval) { return rhsval.isKnown() && rhsval.isContainerSizeValue(); }))
|
||||||
|
return Action::Read | Action::Write;
|
||||||
|
}
|
||||||
|
} else if (Token::Match(tok, "%name% . %name% (")) {
|
||||||
|
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
|
||||||
|
if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP)
|
||||||
|
return Action::Read | Action::Write;
|
||||||
|
const Token* arg = tok->tokAt(4);
|
||||||
|
}
|
||||||
|
return Action::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void writeValue(ValueFlow::Value* value, const Token* tok) const OVERRIDE {
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
if (!tok->astParent())
|
||||||
|
return;
|
||||||
|
const Token* parent = tok->astParent();
|
||||||
|
if (!tok->valueType() || !tok->valueType()->container)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tok->valueType()->container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) {
|
||||||
|
const Token* rhs = parent->astOperand2();
|
||||||
|
if (rhs->tokType() == Token::eString)
|
||||||
|
value->intvalue += Token::getStrLength(rhs);
|
||||||
else if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) {
|
else if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) {
|
||||||
bool found = false;
|
|
||||||
for (const ValueFlow::Value &rhsval : rhs->values()) {
|
for (const ValueFlow::Value &rhsval : rhs->values()) {
|
||||||
if (rhsval.isKnown() && rhsval.isContainerSizeValue()) {
|
if (rhsval.isKnown() && rhsval.isContainerSizeValue()) {
|
||||||
value.intvalue += rhsval.intvalue;
|
value->intvalue += rhsval.intvalue;
|
||||||
found = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
}
|
||||||
break;
|
} else if (Token::Match(tok, "%name% . %name% (")) {
|
||||||
} else
|
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
|
||||||
break;
|
if (action == Library::Container::Action::PUSH)
|
||||||
|
value->intvalue++;
|
||||||
|
if (action == Library::Container::Action::POP)
|
||||||
|
value->intvalue--;
|
||||||
}
|
}
|
||||||
if (isLikelyStreamRead(cpp, tok->astParent()))
|
|
||||||
break;
|
|
||||||
if (isContainerSizeChangedByFunction(tok))
|
|
||||||
break;
|
|
||||||
if (!tok->valueType() || !tok->valueType()->container)
|
|
||||||
break;
|
|
||||||
if (Token::Match(tok, "%name% . %name% (") && tok->valueType()->container->getAction(tok->strAt(2)) != Library::Container::Action::NO_ACTION)
|
|
||||||
break;
|
|
||||||
if (!hasContainerSizeGuard(tok, containerId))
|
|
||||||
setTokenValue(tok, value, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual Action isModified(const Token* tok) const OVERRIDE {
|
||||||
|
Action read = Action::Read;
|
||||||
|
if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok))
|
||||||
|
return Action::Invalid;
|
||||||
|
if (isLikelyStreamRead(isCPP(), tok->astParent()))
|
||||||
|
return Action::Invalid;
|
||||||
|
if (isContainerSizeChanged(tok))
|
||||||
|
return Action::Invalid;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void valueFlowContainerForward(Token *tok, const Token* endToken, const Variable* var, ValueFlow::Value value, TokenList *tokenlist)
|
||||||
|
{
|
||||||
|
ContainerVariableForwardAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
|
||||||
|
valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
|
||||||
|
}
|
||||||
|
static void valueFlowContainerForward(Token *tok, const Variable* var, ValueFlow::Value value, TokenList *tokenlist)
|
||||||
|
{
|
||||||
|
const Token * endOfVarScope = nullptr;
|
||||||
|
if (var->isLocal() || var->isArgument())
|
||||||
|
endOfVarScope = var->scope()->bodyEnd;
|
||||||
|
if (!endOfVarScope)
|
||||||
|
endOfVarScope = tok->scope()->bodyEnd;
|
||||||
|
valueFlowContainerForward(tok, endOfVarScope, var, std::move(value), tokenlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isContainerSizeChanged(const Token *tok, int depth)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
if (!tok->valueType() || !tok->valueType()->container)
|
||||||
|
return true;
|
||||||
|
if (Token::Match(tok, "%name% %assign%|<<"))
|
||||||
|
return true;
|
||||||
|
if (Token::Match(tok, "%var% [") && tok->valueType()->container->stdAssociativeLike)
|
||||||
|
return true;
|
||||||
|
if (Token::Match(tok, "%name% . %name% (")) {
|
||||||
|
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
|
||||||
|
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2));
|
||||||
|
switch (action) {
|
||||||
|
case Library::Container::Action::RESIZE:
|
||||||
|
case Library::Container::Action::CLEAR:
|
||||||
|
case Library::Container::Action::PUSH:
|
||||||
|
case Library::Container::Action::POP:
|
||||||
|
case Library::Container::Action::CHANGE:
|
||||||
|
case Library::Container::Action::INSERT:
|
||||||
|
case Library::Container::Action::ERASE:
|
||||||
|
case Library::Container::Action::CHANGE_INTERNAL:
|
||||||
|
return true;
|
||||||
|
case Library::Container::Action::NO_ACTION: // might be unknown action
|
||||||
|
return yield == Library::Container::Yield::NO_YIELD;
|
||||||
|
case Library::Container::Action::FIND:
|
||||||
|
case Library::Container::Action::CHANGE_CONTENT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isContainerSizeChangedByFunction(tok, depth))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
|
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
|
||||||
|
@ -5657,30 +5745,7 @@ static bool isContainerSizeChanged(nonneg int varId, const Token *start, const T
|
||||||
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
for (const Token *tok = start; tok != end; tok = tok->next()) {
|
||||||
if (tok->varId() != varId)
|
if (tok->varId() != varId)
|
||||||
continue;
|
continue;
|
||||||
if (!tok->valueType() || !tok->valueType()->container)
|
if (isContainerSizeChanged(tok, depth))
|
||||||
return true;
|
|
||||||
if (Token::Match(tok, "%name% %assign%|<<"))
|
|
||||||
return true;
|
|
||||||
if (Token::Match(tok, "%name% . %name% (")) {
|
|
||||||
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
|
|
||||||
switch (action) {
|
|
||||||
case Library::Container::Action::RESIZE:
|
|
||||||
case Library::Container::Action::CLEAR:
|
|
||||||
case Library::Container::Action::PUSH:
|
|
||||||
case Library::Container::Action::POP:
|
|
||||||
case Library::Container::Action::CHANGE:
|
|
||||||
case Library::Container::Action::INSERT:
|
|
||||||
case Library::Container::Action::ERASE:
|
|
||||||
case Library::Container::Action::CHANGE_INTERNAL:
|
|
||||||
return true;
|
|
||||||
case Library::Container::Action::NO_ACTION: // might be unknown action
|
|
||||||
return true;
|
|
||||||
case Library::Container::Action::FIND:
|
|
||||||
case Library::Container::Action::CHANGE_CONTENT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isContainerSizeChangedByFunction(tok, depth))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -5759,7 +5824,7 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
|
||||||
}
|
}
|
||||||
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
value.setKnown();
|
value.setKnown();
|
||||||
valueFlowContainerForward(var->nameToken()->next(), var->declarationId(), value, settings, tokenlist->isCPP());
|
valueFlowContainerForward(var->nameToken()->next(), var, value, tokenlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
// after assignment
|
// after assignment
|
||||||
|
@ -5771,7 +5836,20 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
|
||||||
ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2)));
|
ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2)));
|
||||||
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
value.setKnown();
|
value.setKnown();
|
||||||
valueFlowContainerForward(containerTok->next(), containerTok->varId(), value, settings, tokenlist->isCPP());
|
valueFlowContainerForward(containerTok->next(), containerTok->variable(), value, tokenlist);
|
||||||
|
}
|
||||||
|
} else if (Token::Match(tok, "%var% . %name% (") && tok->valueType() && tok->valueType()->container) {
|
||||||
|
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
|
||||||
|
if (action == Library::Container::Action::CLEAR) {
|
||||||
|
ValueFlow::Value value(0);
|
||||||
|
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
|
value.setKnown();
|
||||||
|
valueFlowContainerForward(tok->next(), tok->variable(), value, tokenlist);
|
||||||
|
} else if (action == Library::Container::Action::RESIZE && tok->tokAt(4)->hasKnownIntValue()) {
|
||||||
|
ValueFlow::Value value(tok->tokAt(4)->values().front());
|
||||||
|
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
|
value.setKnown();
|
||||||
|
valueFlowContainerForward(tok->next(), tok->variable(), value, tokenlist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5836,7 +5914,7 @@ static void valueFlowContainerAfterCondition(TokenList *tokenlist,
|
||||||
const Variable* var = vartok->variable();
|
const Variable* var = vartok->variable();
|
||||||
if (!var)
|
if (!var)
|
||||||
return false;
|
return false;
|
||||||
valueFlowContainerForward(start, var->declarationId(), values.front(), settings, tokenlist->isCPP());
|
valueFlowContainerForward(start->next(), stop, var, values.front(), tokenlist);
|
||||||
return isContainerSizeChanged(var->declarationId(), start, stop);
|
return isContainerSizeChanged(var->declarationId(), start, stop);
|
||||||
};
|
};
|
||||||
handler.parse = [&](const Token *tok) {
|
handler.parse = [&](const Token *tok) {
|
||||||
|
@ -6094,7 +6172,7 @@ static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symbold
|
||||||
argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000");
|
argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000");
|
||||||
argValues.back().safe = true;
|
argValues.back().safe = true;
|
||||||
for (const ValueFlow::Value &value : argValues)
|
for (const ValueFlow::Value &value : argValues)
|
||||||
valueFlowContainerForward(const_cast<Token*>(functionScope->bodyStart), arg.declarationId(), value, settings, tokenlist->isCPP());
|
valueFlowContainerForward(const_cast<Token*>(functionScope->bodyStart), &arg, value, tokenlist);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,15 +88,14 @@ void QList1(QList<int> intListArg)
|
||||||
QList<QString> qstringList2 = {"one", "two"};
|
QList<QString> qstringList2 = {"one", "two"};
|
||||||
(void)qstringList2[1];
|
(void)qstringList2[1];
|
||||||
qstringList2.clear();
|
qstringList2.clear();
|
||||||
// TODO: cppcheck-suppress containerOutOfBounds #9243
|
// cppcheck-suppress containerOutOfBounds
|
||||||
(void)qstringList2[1];
|
(void)qstringList2[1];
|
||||||
|
|
||||||
QList<QString> qstringList3;
|
QList<QString> qstringList3;
|
||||||
qstringList3 << "one" << "two";
|
qstringList3 << "one" << "two";
|
||||||
// FIXME: The following containerOutOfBounds suppression is wrong #9242
|
|
||||||
// Please remove the suppression as soon as this is fixed
|
|
||||||
// cppcheck-suppress containerOutOfBounds
|
|
||||||
(void)qstringList3[1];
|
(void)qstringList3[1];
|
||||||
|
// FIXME: The following should have a containerOutOfBounds suppression #9242
|
||||||
|
(void)qstringList3[3];
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
qstringList3.startsWith("one");
|
qstringList3.startsWith("one");
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
|
@ -118,7 +117,7 @@ void QList1(QList<int> intListArg)
|
||||||
qstringList4.append("a");
|
qstringList4.append("a");
|
||||||
(void)qstringList4[0];
|
(void)qstringList4[0];
|
||||||
qstringList4.clear();
|
qstringList4.clear();
|
||||||
// TODO: cppcheck-suppress containerOutOfBounds #9243
|
// cppcheck-suppress containerOutOfBounds
|
||||||
(void)qstringList4[0];
|
(void)qstringList4[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,10 +194,9 @@ void QStringList1(QStringList stringlistArg)
|
||||||
|
|
||||||
QStringList qstringlist3;
|
QStringList qstringlist3;
|
||||||
qstringlist3 << "one" << "two";
|
qstringlist3 << "one" << "two";
|
||||||
// FIXME: The following containerOutOfBounds suppression is wrong #9242
|
|
||||||
// Please remove the suppression as soon as this is fixed
|
|
||||||
// cppcheck-suppress containerOutOfBounds
|
|
||||||
(void)qstringlist3[1];
|
(void)qstringlist3[1];
|
||||||
|
// FIXME: The following should have a containerOutOfBounds suppression #9242
|
||||||
|
(void)qstringlist3[3];
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
qstringlist3.startsWith("one");
|
qstringlist3.startsWith("one");
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
|
@ -249,10 +247,9 @@ void QVector1(QVector<int> intVectorArg)
|
||||||
|
|
||||||
QVector<QString> qstringVector3;
|
QVector<QString> qstringVector3;
|
||||||
qstringVector3 << "one" << "two";
|
qstringVector3 << "one" << "two";
|
||||||
// FIXME: The following containerOutOfBounds suppression is wrong #9242
|
|
||||||
// Please remove the suppression as soon as this is fixed
|
|
||||||
// cppcheck-suppress containerOutOfBounds
|
|
||||||
(void)qstringVector3[1];
|
(void)qstringVector3[1];
|
||||||
|
// FIXME: The following should have a containerOutOfBounds suppression #9242
|
||||||
|
(void)qstringVector3[3];
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
qstringVector3.startsWith("one");
|
qstringVector3.startsWith("one");
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
|
@ -302,10 +299,9 @@ void QStack1(QStack<int> intStackArg)
|
||||||
|
|
||||||
QStack<QString> qstringStack2;
|
QStack<QString> qstringStack2;
|
||||||
qstringStack2 << "one" << "two";
|
qstringStack2 << "one" << "two";
|
||||||
// FIXME: The following containerOutOfBounds suppression is wrong #9242
|
|
||||||
// Please remove the suppression as soon as this is fixed
|
|
||||||
// cppcheck-suppress containerOutOfBounds
|
|
||||||
(void)qstringStack2[1];
|
(void)qstringStack2[1];
|
||||||
|
// FIXME: The following should have a containerOutOfBounds suppression #9242
|
||||||
|
(void)qstringStack2[3];
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
qstringStack2.startsWith("one");
|
qstringStack2.startsWith("one");
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
|
|
|
@ -33,7 +33,8 @@ wxString containerOutOfBounds_wxArrayString(void)
|
||||||
wxArrayString a;
|
wxArrayString a;
|
||||||
a.Add("42");
|
a.Add("42");
|
||||||
a.Clear();
|
a.Clear();
|
||||||
// cppcheck-suppress containerOutOfBounds
|
// TODO: wxArrayString is defined to be a vector
|
||||||
|
// TODO: cppcheck-suppress containerOutOfBounds
|
||||||
return a[0];
|
return a[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +43,8 @@ int containerOutOfBounds_wxArrayInt(void)
|
||||||
wxArrayInt a;
|
wxArrayInt a;
|
||||||
a.Add(42);
|
a.Add(42);
|
||||||
a.Clear();
|
a.Clear();
|
||||||
// cppcheck-suppress containerOutOfBounds
|
// TODO: wxArrayString is defined to be a vector
|
||||||
|
// TODO: cppcheck-suppress containerOutOfBounds
|
||||||
return a[0];
|
return a[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,6 +335,16 @@ private:
|
||||||
" x[0] = 0;\n"
|
" x[0] = 0;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkNormal("void f(bool b) {\n"
|
||||||
|
" std::vector<int> x;\n"
|
||||||
|
" if (b)\n"
|
||||||
|
" x.push_back(1);\n"
|
||||||
|
" if (x.size() < 2)\n"
|
||||||
|
" return;\n"
|
||||||
|
" x[0] = 2;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void outOfBoundsIndexExpression() {
|
void outOfBoundsIndexExpression() {
|
||||||
|
@ -1403,7 +1413,8 @@ private:
|
||||||
" foo[ii] = 0;\n"
|
" foo[ii] = 0;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Out of bounds access in expression 'foo[ii]' because 'foo' is empty.\n"
|
||||||
|
"[test.cpp:6]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str());
|
||||||
|
|
||||||
check("void foo(std::vector<int> foo) {\n"
|
check("void foo(std::vector<int> foo) {\n"
|
||||||
" for (unsigned int ii = 0; ii <= foo.size(); ++ii) {\n"
|
" for (unsigned int ii = 0; ii <= foo.size(); ++ii) {\n"
|
||||||
|
@ -1506,7 +1517,7 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("[test.cpp:11]: (error) Out of bounds access in expression 'foo[ii]' because 'foo' is empty.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -324,6 +324,12 @@ private:
|
||||||
return tok ? tok->values() : std::list<ValueFlow::Value>();
|
return tok ? tok->values() : std::list<ValueFlow::Value>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<ValueFlow::Value> tokenValues(const char code[], const char tokstr[], ValueFlow::Value::ValueType vt, const Settings *s = nullptr) {
|
||||||
|
std::list<ValueFlow::Value> values = tokenValues(code, tokstr, s);
|
||||||
|
values.remove_if([&](const ValueFlow::Value& v) { return v.valueType != vt; });
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
|
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
|
||||||
std::list<ValueFlow::Value> values = tokenValues(code, tokstr);
|
std::list<ValueFlow::Value> values = tokenValues(code, tokstr);
|
||||||
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
|
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
|
||||||
|
@ -4253,7 +4259,7 @@ private:
|
||||||
" while (!links.empty() || indentlevel)\n"
|
" while (!links.empty() || indentlevel)\n"
|
||||||
" links.push(tok);\n"
|
" links.push(tok);\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT(tokenValues(code, "links . empty").empty());
|
ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "links . empty"), 0));
|
||||||
|
|
||||||
// valueFlowContainerForward, function call
|
// valueFlowContainerForward, function call
|
||||||
code = "void f() {\n"
|
code = "void f() {\n"
|
||||||
|
@ -4439,6 +4445,40 @@ private:
|
||||||
" x = s + s;\n"
|
" x = s + s;\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8));
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8));
|
||||||
|
|
||||||
|
code = "void f(const std::vector<int> &ints) {\n"
|
||||||
|
" ints.clear();\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 0));
|
||||||
|
|
||||||
|
code = "void f(const std::vector<int> &ints) {\n"
|
||||||
|
" ints.resize(3);\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 3));
|
||||||
|
|
||||||
|
code = "void f(const std::vector<int> &ints) {\n"
|
||||||
|
" ints.resize(3);\n"
|
||||||
|
" ints.push_back(3);\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 4));
|
||||||
|
|
||||||
|
code = "void f(const std::vector<int> &ints) {\n"
|
||||||
|
" ints.resize(3);\n"
|
||||||
|
" ints.pop_back();\n"
|
||||||
|
" ints.front();\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 2));
|
||||||
|
|
||||||
|
code = "int f(bool b) {\n"
|
||||||
|
" std::map<int, int> m;\n"
|
||||||
|
" if (b)\n"
|
||||||
|
" m[0] = 1;\n"
|
||||||
|
" return m.at(0);\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "m . at", ValueFlow::Value::CONTAINER_SIZE), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowDynamicBufferSize() {
|
void valueFlowDynamicBufferSize() {
|
||||||
|
|
Loading…
Reference in New Issue