Merge pull request #2735 from pfultz2/container-forward

Use ForwardAnalyzer for container forward
This commit is contained in:
Daniel Marjamäki 2020-08-13 21:58:37 +02:00 committed by GitHub
commit 7ff5a208a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 321 additions and 172 deletions

View File

@ -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()

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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,16 +2329,15 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
return Action::None; return Action::None;
} }
virtual Action analyze(const Token* tok) const OVERRIDE { virtual Action isWritable(const Token* tok) const {
if (invalid()) const ValueFlow::Value* value = getValue(tok);
return Action::Invalid; if (!value)
if (match(tok)) { return Action::None;
if (!(value->isIntValue() || value->isFloatValue()))
return Action::None;
const Token* parent = tok->astParent(); const Token* parent = tok->astParent();
if ((Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
return Action::Read;
Action read = Action::Read; if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
if (parent && isWritableValue(tok) && parent->isAssignmentOp() && astIsLHS(tok) &&
parent->astOperand2()->hasKnownValue()) { parent->astOperand2()->hasKnownValue()) {
const Token* rhs = parent->astOperand2(); const Token* rhs = parent->astOperand2();
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT); const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
@ -2360,9 +2352,53 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
} }
// increment/decrement // increment/decrement
if (isWritableValue(tok) && (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--"))) { if (Token::Match(tok->previous(), "++|-- %name%") || Token::Match(tok, "%name% ++|--")) {
return read | Action::Write; 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 {
if (invalid())
return Action::Invalid;
if (match(tok)) {
const Token* parent = tok->astParent();
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
return Action::Read;
// Action read = Action::Read;
Action w = isWritable(tok);
if (w != Action::None)
return w;
// 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,77 +5616,108 @@ 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
break;
} }
if (isLikelyStreamRead(cpp, tok->astParent())) } else if (Token::Match(tok, "%name% . %name% (")) {
break; Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
if (isContainerSizeChangedByFunction(tok)) if (action == Library::Container::Action::PUSH)
break; value->intvalue++;
if (!tok->valueType() || !tok->valueType()->container) if (action == Library::Container::Action::POP)
break; value->intvalue--;
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(nonneg int varId, const Token *start, const Token *end, int depth) static bool isContainerSizeChanged(const Token *tok, int depth)
{ {
for (const Token *tok = start; tok != end; tok = tok->next()) { if (!tok)
if (tok->varId() != varId) return false;
continue;
if (!tok->valueType() || !tok->valueType()->container) if (!tok->valueType() || !tok->valueType()->container)
return true; return true;
if (Token::Match(tok, "%name% %assign%|<<")) if (Token::Match(tok, "%name% %assign%|<<"))
return true; return true;
if (Token::Match(tok, "%var% [") && tok->valueType()->container->stdAssociativeLike)
return true;
if (Token::Match(tok, "%name% . %name% (")) { if (Token::Match(tok, "%name% . %name% (")) {
Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2)); Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2));
Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2));
switch (action) { switch (action) {
case Library::Container::Action::RESIZE: case Library::Container::Action::RESIZE:
case Library::Container::Action::CLEAR: case Library::Container::Action::CLEAR:
@ -5674,7 +5729,7 @@ static bool isContainerSizeChanged(nonneg int varId, const Token *start, const T
case Library::Container::Action::CHANGE_INTERNAL: case Library::Container::Action::CHANGE_INTERNAL:
return true; return true;
case Library::Container::Action::NO_ACTION: // might be unknown action case Library::Container::Action::NO_ACTION: // might be unknown action
return true; return yield == Library::Container::Yield::NO_YIELD;
case Library::Container::Action::FIND: case Library::Container::Action::FIND:
case Library::Container::Action::CHANGE_CONTENT: case Library::Container::Action::CHANGE_CONTENT:
break; break;
@ -5682,6 +5737,16 @@ static bool isContainerSizeChanged(nonneg int varId, const Token *start, const T
} }
if (isContainerSizeChangedByFunction(tok, depth)) if (isContainerSizeChangedByFunction(tok, depth))
return true; return true;
return false;
}
static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth)
{
for (const Token *tok = start; tok != end; tok = tok->next()) {
if (tok->varId() != varId)
continue;
if (isContainerSizeChanged(tok, depth))
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;
} }

View File

@ -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

View File

@ -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];
} }

View File

@ -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());
} }
{ {

View File

@ -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() {