Use lifetime analysis for checking mismatching containers (#2456)
* Use lifetimes to check for mismatching containers * Fix error messages * Format * Remove unused variables * Fix configuration and track iterators through algorithms * Fix iterator value types in qt config * Fix library issue with QStringList * Remove unused functions * Fix cppcheck errors
This commit is contained in:
parent
0cd2935dc7
commit
42d44f02a2
11
cfg/qt.cfg
11
cfg/qt.cfg
|
@ -4839,7 +4839,7 @@
|
||||||
</arg>
|
</arg>
|
||||||
</function>
|
</function>
|
||||||
<!-- ##### Container ##### -->
|
<!-- ##### Container ##### -->
|
||||||
<container id="qtContainer" opLessAllowed="false" itEndPattern="> :: iterator|const_iterator">
|
<container id="qtContainer" endPattern="> !!::" opLessAllowed="false" itEndPattern="> :: iterator|const_iterator">
|
||||||
<type templateParameter="0"/>
|
<type templateParameter="0"/>
|
||||||
<size>
|
<size>
|
||||||
<function name="append" action="push"/>
|
<function name="append" action="push"/>
|
||||||
|
@ -4873,6 +4873,7 @@
|
||||||
<function name="pop_front" action="pop"/>
|
<function name="pop_front" action="pop"/>
|
||||||
<function name="removeAll" action="change"/>
|
<function name="removeAll" action="change"/>
|
||||||
<function name="removeAt" action="pop"/>
|
<function name="removeAt" action="pop"/>
|
||||||
|
<function name="removeDuplicates" action="change"/>
|
||||||
<function name="removeFirst" action="pop"/>
|
<function name="removeFirst" action="pop"/>
|
||||||
<function name="removeLast" action="pop"/>
|
<function name="removeLast" action="pop"/>
|
||||||
<function name="takeAt" action="pop"/>
|
<function name="takeAt" action="pop"/>
|
||||||
|
@ -5000,12 +5001,8 @@
|
||||||
<function name="crend" yields="end-iterator"/>
|
<function name="crend" yields="end-iterator"/>
|
||||||
</access>
|
</access>
|
||||||
</container>
|
</container>
|
||||||
<!-- TODO: Inheriting from qtList also inherits from qtContainer which sets "<type templateParameter="0"/>". This is not correct, but seems to do no harm. Currently this can not be unset. -->
|
<!-- Treat QStringList as QList<QString> since we can't remove the template parameter when we inherit. -->
|
||||||
<container id="qtStringList" startPattern="QStringList" inherits="qtList" opLessAllowed="true" itEndPattern=":: iterator|const_iterator|reverse_iterator|const_reverse_iterator">
|
<define name="QStringList" value="QList<QString>" />
|
||||||
<size>
|
|
||||||
<function name="removeDuplicates" action="change"/>
|
|
||||||
</size>
|
|
||||||
</container>
|
|
||||||
<define name="Q_ARG(type, data)" value="QArgument<type >(#type, data)"/>
|
<define name="Q_ARG(type, data)" value="QArgument<type >(#type, data)"/>
|
||||||
<!-- TODO: Enable when ticket 8479 got fixed
|
<!-- TODO: Enable when ticket 8479 got fixed
|
||||||
<define name="Q_D(Class)" value="Class##Private * const d = d_func()"/>
|
<define name="Q_D(Class)" value="Class##Private * const d = d_func()"/>
|
||||||
|
|
224
lib/checkstl.cpp
224
lib/checkstl.cpp
|
@ -274,23 +274,6 @@ void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const
|
||||||
"Same iterator is used with containers '" + containerName + "' that are defined in different scopes.", CWE664, false);
|
"Same iterator is used with containers '" + containerName + "' that are defined in different scopes.", CWE664, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2)
|
|
||||||
{
|
|
||||||
std::list<const Token*> callstack = { cmpOperatorTok, containerTok1, containerTok2 };
|
|
||||||
reportError(callstack, Severity::error, "iteratorsCmp1",
|
|
||||||
"$symbol:" + containerName1 + "\n"
|
|
||||||
"$symbol:" + containerName2 + "\n"
|
|
||||||
"Comparison of iterators from containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName)
|
|
||||||
{
|
|
||||||
std::list<const Token*> callstack = { cmpOperatorTok, containerTok1, containerTok2 };
|
|
||||||
reportError(callstack, Severity::error, "iteratorsCmp2",
|
|
||||||
"$symbol:" + containerName + "\n"
|
|
||||||
"Comparison of iterators from containers '" + containerName + "' that are defined in different scopes.", CWE664, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error message used when dereferencing an iterator that has been erased..
|
// Error message used when dereferencing an iterator that has been erased..
|
||||||
void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive)
|
void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive)
|
||||||
{
|
{
|
||||||
|
@ -359,24 +342,6 @@ enum OperandPosition {
|
||||||
Right
|
Right
|
||||||
};
|
};
|
||||||
|
|
||||||
static const Token* findIteratorContainer(const Token* start, const Token* end, nonneg int id)
|
|
||||||
{
|
|
||||||
const Token* containerToken = nullptr;
|
|
||||||
for (const Token* tok = start; tok != end; tok = tok->next()) {
|
|
||||||
if (Token::Match(tok, "%varid% = %name% . %name% (", id)) {
|
|
||||||
// Iterator is assigned to value
|
|
||||||
if (tok->tokAt(5)->valueType() && tok->tokAt(5)->valueType()->type == ValueType::Type::ITERATOR) {
|
|
||||||
containerToken = tok->tokAt(2);
|
|
||||||
}
|
|
||||||
} else if (Token::Match(tok, "%varid% = %name% (", id)) {
|
|
||||||
// Prevent FP: iterator is assigned to something
|
|
||||||
// TODO: Fix it in future
|
|
||||||
containerToken = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return containerToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isVector(const Token* tok)
|
static bool isVector(const Token* tok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
@ -400,8 +365,6 @@ void CheckStl::iterators()
|
||||||
if (iteratorId != 0)
|
if (iteratorId != 0)
|
||||||
iteratorScopeBeginInfo[iteratorId] = var->nameToken();
|
iteratorScopeBeginInfo[iteratorId] = var->nameToken();
|
||||||
}
|
}
|
||||||
// Storage to save found comparison problems to avoid duplicate error messages
|
|
||||||
std::set<const Token*> foundOperatorErrors;
|
|
||||||
|
|
||||||
for (const Variable* var : symbolDatabase->variableList()) {
|
for (const Variable* var : symbolDatabase->variableList()) {
|
||||||
bool inconclusiveType=false;
|
bool inconclusiveType=false;
|
||||||
|
@ -439,16 +402,8 @@ void CheckStl::iterators()
|
||||||
invalidationScope = nullptr;
|
invalidationScope = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is comparison expression?
|
|
||||||
// Check whether iterator compared against different container or iterator of different container?
|
|
||||||
if (tok2->isComparisonOp() && tok2->astOperand1() && tok2->astOperand2() &&
|
|
||||||
(foundOperatorErrors.find(tok2) == foundOperatorErrors.end()) &&
|
|
||||||
compareIteratorAgainstDifferentContainer(tok2, containerToken, iteratorId, iteratorScopeBeginInfo)) {
|
|
||||||
foundOperatorErrors.insert(tok2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the iterator used in a insert/erase operation?
|
// Is the iterator used in a insert/erase operation?
|
||||||
else if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId) && !isVector(tok2)) {
|
if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId) && !isVector(tok2)) {
|
||||||
const Token* itTok = tok2->tokAt(4);
|
const Token* itTok = tok2->tokAt(4);
|
||||||
if (itTok->str() == "*") {
|
if (itTok->str() == "*") {
|
||||||
if (tok2->strAt(2) == "insert")
|
if (tok2->strAt(2) == "insert")
|
||||||
|
@ -579,65 +534,17 @@ void CheckStl::iterators()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckStl::compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const nonneg int iteratorId, const std::map<int, const Token*>& iteratorScopeBeginInfo)
|
|
||||||
{
|
|
||||||
if (!containerTok)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const Token *otherOperand = nullptr;
|
|
||||||
OperandPosition operandPosition;
|
|
||||||
if (operatorTok->astOperand1()->varId() == iteratorId) {
|
|
||||||
otherOperand = operatorTok->astOperand2();
|
|
||||||
operandPosition = OperandPosition::Right;
|
|
||||||
} else if (operatorTok->astOperand2()->varId() == iteratorId) {
|
|
||||||
otherOperand = operatorTok->astOperand1();
|
|
||||||
operandPosition = OperandPosition::Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!otherOperand)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const Token * const otherExprPart = otherOperand->tokAt(-3);
|
|
||||||
if (Token::Match(otherExprPart, "%name% . end|rend|cend|crend ( )") && otherExprPart->varId() != containerTok->varId()) {
|
|
||||||
const std::string& firstContainerName = getContainerName(containerTok);
|
|
||||||
const std::string& secondContainerName = getContainerName(otherExprPart);
|
|
||||||
if (firstContainerName != secondContainerName) {
|
|
||||||
if (operandPosition == OperandPosition::Right)
|
|
||||||
iteratorsError(operatorTok, containerTok, firstContainerName, secondContainerName);
|
|
||||||
else
|
|
||||||
iteratorsError(operatorTok, containerTok, secondContainerName, firstContainerName);
|
|
||||||
} else {
|
|
||||||
iteratorsError(operatorTok, containerTok, firstContainerName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
const int otherId = otherOperand->varId();
|
|
||||||
auto it = iteratorScopeBeginInfo.find(otherId);
|
|
||||||
if (it != iteratorScopeBeginInfo.end()) {
|
|
||||||
const Token* otherContainerToken = findIteratorContainer(it->second, operatorTok->astOperand1(), otherId);
|
|
||||||
if (otherContainerToken && otherContainerToken->varId() != containerTok->varId()) {
|
|
||||||
const std::string& firstContainerName = getContainerName(containerTok);
|
|
||||||
const std::string& secondContainerName = getContainerName(otherContainerToken);
|
|
||||||
if (firstContainerName != secondContainerName) {
|
|
||||||
if (operandPosition == OperandPosition::Right)
|
|
||||||
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName, secondContainerName);
|
|
||||||
else
|
|
||||||
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, secondContainerName, firstContainerName);
|
|
||||||
} else {
|
|
||||||
iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error message for bad iterator usage..
|
// Error message for bad iterator usage..
|
||||||
void CheckStl::mismatchingContainersError(const Token *tok)
|
void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::error, "mismatchingContainers", "Iterators of different containers are used together.", CWE664, false);
|
const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1"));
|
||||||
|
const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2"));
|
||||||
|
reportError(tok1,
|
||||||
|
Severity::error,
|
||||||
|
"mismatchingContainers",
|
||||||
|
"Iterators of different containers '" + expr1 + "' and '" + expr2 + "' are used together.",
|
||||||
|
CWE664,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2)
|
void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2)
|
||||||
|
@ -679,18 +586,6 @@ static const std::string pattern1x1_1 = "%name% . " + iteratorBeginFuncPattern +
|
||||||
static const std::string pattern1x1_2 = "%name% . " + iteratorEndFuncPattern + " ( ) ,|)";
|
static const std::string pattern1x1_2 = "%name% . " + iteratorEndFuncPattern + " ( ) ,|)";
|
||||||
static const std::string pattern2 = pattern1x1_1 + pattern1x1_2;
|
static const std::string pattern2 = pattern1x1_1 + pattern1x1_2;
|
||||||
|
|
||||||
static const Variable *getContainer(const Token *argtok)
|
|
||||||
{
|
|
||||||
while (argtok && argtok->astOperand1())
|
|
||||||
argtok = argtok->astOperand1();
|
|
||||||
if (!Token::Match(argtok, "%var% . begin|end|rbegin|rend ( )")) // TODO: use Library yield
|
|
||||||
return nullptr;
|
|
||||||
const Variable *var = argtok->variable();
|
|
||||||
if (var && Token::simpleMatch(var->typeStartToken(), "std ::"))
|
|
||||||
return var;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const Token * getIteratorExpression(const Token * tok)
|
static const Token * getIteratorExpression(const Token * tok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
@ -715,6 +610,40 @@ static const Token * getIteratorExpression(const Token * tok)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2)
|
||||||
|
{
|
||||||
|
if (!tok1)
|
||||||
|
return false;
|
||||||
|
if (!tok2)
|
||||||
|
return false;
|
||||||
|
ValueFlow::Value val1 = getLifetimeObjValue(tok1);
|
||||||
|
ValueFlow::Value val2 = getLifetimeObjValue(tok2);
|
||||||
|
if (val1.tokvalue && val2.tokvalue && val1.lifetimeKind == val2.lifetimeKind) {
|
||||||
|
if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Lambda)
|
||||||
|
return false;
|
||||||
|
if (isSameExpression(true, false, val1.tokvalue, val2.tokvalue, mSettings->library, false, false))
|
||||||
|
return false;
|
||||||
|
if (val1.tokvalue->expressionString() == val2.tokvalue->expressionString())
|
||||||
|
iteratorsError(tok1, val1.tokvalue, val1.tokvalue->expressionString());
|
||||||
|
else
|
||||||
|
mismatchingContainersError(val1.tokvalue, val2.tokvalue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* iter1 = getIteratorExpression(tok1);
|
||||||
|
const Token* iter2 = getIteratorExpression(tok2);
|
||||||
|
if (iter1 && iter2 && !isSameExpression(true, false, iter1, iter2, mSettings->library, false, false)) {
|
||||||
|
mismatchingContainerExpressionError(iter1, iter2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArgIteratorInfo {
|
||||||
|
const Token* tok;
|
||||||
|
const Library::ArgumentChecks::IteratorInfo* info;
|
||||||
|
};
|
||||||
|
|
||||||
void CheckStl::mismatchingContainers()
|
void CheckStl::mismatchingContainers()
|
||||||
{
|
{
|
||||||
// Check if different containers are used in various calls of standard functions
|
// Check if different containers are used in various calls of standard functions
|
||||||
|
@ -722,75 +651,50 @@ void CheckStl::mismatchingContainers()
|
||||||
for (const Scope * scope : symbolDatabase->functionScopes) {
|
for (const Scope * scope : symbolDatabase->functionScopes) {
|
||||||
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
if (Token::Match(tok, "%comp%|-")) {
|
if (Token::Match(tok, "%comp%|-")) {
|
||||||
const Token * iter1 = getIteratorExpression(tok->astOperand1());
|
if (checkIteratorPair(tok->astOperand1(), tok->astOperand2()))
|
||||||
const Token * iter2 = getIteratorExpression(tok->astOperand2());
|
|
||||||
if (iter1 && iter2 && !isSameExpression(true, false, iter1, iter2, mSettings->library, false, false)) {
|
|
||||||
mismatchingContainerExpressionError(iter1, iter2);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!Token::Match(tok, "%name% ( !!)"))
|
if (!Token::Match(tok, "%name% ( !!)"))
|
||||||
continue;
|
continue;
|
||||||
const Token * const ftok = tok;
|
const Token * const ftok = tok;
|
||||||
const Token * firstArg = nullptr;
|
|
||||||
|
|
||||||
const std::vector<const Token *> args = getArguments(ftok);
|
const std::vector<const Token *> args = getArguments(ftok);
|
||||||
if (args.size() < 2)
|
if (args.size() < 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::map<const Variable *, int> containerNr;
|
// Group args together by container
|
||||||
|
std::map<int, std::vector<ArgIteratorInfo>> containers;
|
||||||
for (int argnr = 1; argnr <= args.size(); ++argnr) {
|
for (int argnr = 1; argnr <= args.size(); ++argnr) {
|
||||||
const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(ftok, argnr);
|
const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(ftok, argnr);
|
||||||
if (!i)
|
if (!i)
|
||||||
continue;
|
continue;
|
||||||
const Token * const argTok = args[argnr - 1];
|
const Token * const argTok = args[argnr - 1];
|
||||||
if (i->first) {
|
containers[i->container].push_back({argTok, i});
|
||||||
firstArg = argTok;
|
|
||||||
}
|
}
|
||||||
if (i->last && firstArg && argTok && isSameExpression(true, false, firstArg, argTok, mSettings->library, false, false)) {
|
|
||||||
sameIteratorExpressionError(firstArg);
|
// Lambda is used to escape the nested loops
|
||||||
}
|
[&] {
|
||||||
const Variable *c = getContainer(argTok);
|
for (const auto& p : containers) {
|
||||||
if (c) {
|
const std::vector<ArgIteratorInfo>& cargs = p.second;
|
||||||
std::map<const Variable *, int>::const_iterator it = containerNr.find(c);
|
for (ArgIteratorInfo iter1 : cargs) {
|
||||||
if (it == containerNr.end()) {
|
for (ArgIteratorInfo iter2 : cargs) {
|
||||||
for (it = containerNr.begin(); it != containerNr.end(); ++it) {
|
if (iter1.tok == iter2.tok)
|
||||||
if (it->second == i->container) {
|
continue;
|
||||||
mismatchingContainersError(argTok);
|
if (iter1.info->first && iter2.info->last &&
|
||||||
break;
|
isSameExpression(true, false, iter1.tok, iter2.tok, mSettings->library, false, false))
|
||||||
}
|
sameIteratorExpressionError(iter1.tok);
|
||||||
}
|
if (checkIteratorPair(iter1.tok, iter2.tok))
|
||||||
containerNr[c] = i->container;
|
return;
|
||||||
} else if (it->second != i->container) {
|
|
||||||
mismatchingContainersError(argTok);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (i->last && firstArg && argTok) {
|
|
||||||
const Token * iter1 = getIteratorExpression(firstArg);
|
|
||||||
const Token * iter2 = getIteratorExpression(argTok);
|
|
||||||
if (iter1 && iter2 && !isSameExpression(true, false, iter1, iter2, mSettings->library, false, false)) {
|
|
||||||
mismatchingContainerExpressionError(iter1, iter2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}();
|
||||||
const int ret = mSettings->library.returnValueContainer(ftok);
|
|
||||||
if (ret != -1 && Token::Match(ftok->next()->astParent(), "==|!=")) {
|
|
||||||
const Token *parent = ftok->next()->astParent();
|
|
||||||
const Token *other = (parent->astOperand1() == ftok->next()) ? parent->astOperand2() : parent->astOperand1();
|
|
||||||
const Variable *c = getContainer(other);
|
|
||||||
if (c) {
|
|
||||||
const std::map<const Variable *, int>::const_iterator it = containerNr.find(c);
|
|
||||||
if (it == containerNr.end() || it->second != ret)
|
|
||||||
mismatchingContainersError(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const Variable *var : symbolDatabase->variableList()) {
|
for (const Variable *var : symbolDatabase->variableList()) {
|
||||||
if (var && var->isStlStringType() && Token::Match(var->nameToken(), "%var% (") && Token::Match(var->nameToken()->tokAt(2), pattern2.c_str())) {
|
if (var && var->isStlStringType() && Token::Match(var->nameToken(), "%var% (") && Token::Match(var->nameToken()->tokAt(2), pattern2.c_str())) {
|
||||||
if (var->nameToken()->strAt(2) != var->nameToken()->strAt(8)) {
|
if (var->nameToken()->strAt(2) != var->nameToken()->strAt(8)) {
|
||||||
mismatchingContainersError(var->nameToken());
|
mismatchingContainersError(var->nameToken(), var->nameToken()->tokAt(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,8 @@ public:
|
||||||
|
|
||||||
void invalidContainer();
|
void invalidContainer();
|
||||||
|
|
||||||
|
bool checkIteratorPair(const Token* tok1, const Token* tok2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mismatching containers:
|
* Mismatching containers:
|
||||||
* std::find(foo.begin(), bar.end(), x)
|
* std::find(foo.begin(), bar.end(), x)
|
||||||
|
@ -195,9 +197,7 @@ private:
|
||||||
void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2);
|
void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2);
|
||||||
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2);
|
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2);
|
||||||
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName);
|
void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName);
|
||||||
void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2);
|
void mismatchingContainersError(const Token* tok1, const Token* tok2);
|
||||||
void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName);
|
|
||||||
void mismatchingContainersError(const Token* tok);
|
|
||||||
void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2);
|
void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2);
|
||||||
void sameIteratorExpressionError(const Token *tok);
|
void sameIteratorExpressionError(const Token *tok);
|
||||||
void stlBoundariesError(const Token* tok);
|
void stlBoundariesError(const Token* tok);
|
||||||
|
@ -220,8 +220,6 @@ private:
|
||||||
|
|
||||||
void useStlAlgorithmError(const Token *tok, const std::string &algoName);
|
void useStlAlgorithmError(const Token *tok, const std::string &algoName);
|
||||||
|
|
||||||
bool compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const nonneg int iteratorId, const std::map<int, const Token*>& iteratorScopeBeginInfo);
|
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE {
|
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE {
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
CheckStl c(nullptr, settings, errorLogger);
|
CheckStl c(nullptr, settings, errorLogger);
|
||||||
|
@ -230,10 +228,8 @@ private:
|
||||||
c.iteratorsError(nullptr, "container1", "container2");
|
c.iteratorsError(nullptr, "container1", "container2");
|
||||||
c.iteratorsError(nullptr, nullptr, "container0", "container1");
|
c.iteratorsError(nullptr, nullptr, "container0", "container1");
|
||||||
c.iteratorsError(nullptr, nullptr, "container");
|
c.iteratorsError(nullptr, nullptr, "container");
|
||||||
c.iteratorsCmpError(nullptr, nullptr, nullptr, "container1", "container2");
|
|
||||||
c.iteratorsCmpError(nullptr, nullptr, nullptr, "container");
|
|
||||||
c.invalidContainerError(nullptr, nullptr, nullptr, errorPath);
|
c.invalidContainerError(nullptr, nullptr, nullptr, errorPath);
|
||||||
c.mismatchingContainersError(nullptr);
|
c.mismatchingContainersError(nullptr, nullptr);
|
||||||
c.mismatchingContainerExpressionError(nullptr, nullptr);
|
c.mismatchingContainerExpressionError(nullptr, nullptr);
|
||||||
c.sameIteratorExpressionError(nullptr);
|
c.sameIteratorExpressionError(nullptr);
|
||||||
c.dereferenceErasedError(nullptr, nullptr, "iter", false);
|
c.dereferenceErasedError(nullptr, nullptr, "iter", false);
|
||||||
|
|
|
@ -3612,7 +3612,20 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
|
||||||
{
|
{
|
||||||
if (!Token::Match(tok, "%name% ("))
|
if (!Token::Match(tok, "%name% ("))
|
||||||
return;
|
return;
|
||||||
if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) {
|
int returnContainer = settings->library.returnValueContainer(tok);
|
||||||
|
if (returnContainer >= 0) {
|
||||||
|
std::vector<const Token *> args = getArguments(tok);
|
||||||
|
for (int argnr = 1; argnr <= args.size(); ++argnr) {
|
||||||
|
const Library::ArgumentChecks::IteratorInfo *i = settings->library.getArgIteratorInfo(tok, argnr);
|
||||||
|
if (!i)
|
||||||
|
continue;
|
||||||
|
if (i->container != returnContainer)
|
||||||
|
continue;
|
||||||
|
const Token * const argTok = args[argnr - 1];
|
||||||
|
LifetimeStore{argTok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Iterator} .byVal(
|
||||||
|
tok->next(), tokenlist, errorLogger, settings);
|
||||||
|
}
|
||||||
|
} else if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) {
|
||||||
for (const Token *argtok : getArguments(tok)) {
|
for (const Token *argtok : getArguments(tok)) {
|
||||||
LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byRef(
|
LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byRef(
|
||||||
tok->next(), tokenlist, errorLogger, settings);
|
tok->next(), tokenlist, errorLogger, settings);
|
||||||
|
|
|
@ -40,7 +40,7 @@ QString::iterator QString3()
|
||||||
{
|
{
|
||||||
QString qstring1;
|
QString qstring1;
|
||||||
QString qstring2;
|
QString qstring2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QString::iterator it = qstring1.begin(); it != qstring2.end(); ++it)
|
for (QString::iterator it = qstring1.begin(); it != qstring2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -126,12 +126,12 @@ QList<int>::iterator QList3()
|
||||||
{
|
{
|
||||||
QList<int> qlist1;
|
QList<int> qlist1;
|
||||||
QList<int> qlist2;
|
QList<int> qlist2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QList<int>::iterator it = qlist1.begin(); it != qlist2.end(); ++it)
|
for (QList<int>::iterator it = qlist1.begin(); it != qlist2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QList<int>::iterator it = qlist1.begin();
|
QList<int>::iterator it = qlist1.begin();
|
||||||
// TODO: cppcheck-suppress returnDanglingLifetime
|
// cppcheck-suppress returnDanglingLifetime
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,12 +162,12 @@ QLinkedList<int>::iterator QLinkedList3()
|
||||||
{
|
{
|
||||||
QLinkedList<int> intQLinkedList1;
|
QLinkedList<int> intQLinkedList1;
|
||||||
QLinkedList<int> intQLinkedList2;
|
QLinkedList<int> intQLinkedList2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QLinkedList<int>::iterator it = intQLinkedList1.begin(); it != intQLinkedList2.end(); ++it)
|
for (QLinkedList<int>::iterator it = intQLinkedList1.begin(); it != intQLinkedList2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QLinkedList<int>::iterator it = intQLinkedList1.begin();
|
QLinkedList<int>::iterator it = intQLinkedList1.begin();
|
||||||
// TODO: cppcheck-suppress returnDanglingLifetime
|
// cppcheck-suppress returnDanglingLifetime
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ QStringList::iterator QStringList2()
|
||||||
{
|
{
|
||||||
QStringList qstringlist1;
|
QStringList qstringlist1;
|
||||||
QStringList qstringlist2;
|
QStringList qstringlist2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QStringList::iterator it = qstringlist1.begin(); it != qstringlist2.end(); ++it)
|
for (QStringList::iterator it = qstringlist1.begin(); it != qstringlist2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -273,12 +273,12 @@ QVector<int>::iterator QVector2()
|
||||||
{
|
{
|
||||||
QVector<int> qvector1;
|
QVector<int> qvector1;
|
||||||
QVector<int> qvector2;
|
QVector<int> qvector2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QVector<int>::iterator it = qvector1.begin(); it != qvector2.end(); ++it)
|
for (QVector<int>::iterator it = qvector1.begin(); it != qvector2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QVector<int>::iterator it = qvector1.begin();
|
QVector<int>::iterator it = qvector1.begin();
|
||||||
// TODO cppcheck-suppress returnDanglingLifetime
|
// cppcheck-suppress returnDanglingLifetime
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,12 +326,12 @@ QStack<int>::iterator QStack2()
|
||||||
{
|
{
|
||||||
QStack<int> qstack1;
|
QStack<int> qstack1;
|
||||||
QStack<int> qstack2;
|
QStack<int> qstack2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (QStack<int>::iterator it = qstack1.begin(); it != qstack2.end(); ++it)
|
for (QStack<int>::iterator it = qstack1.begin(); it != qstack2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QStack<int>::iterator it = qstack1.begin();
|
QStack<int>::iterator it = qstack1.begin();
|
||||||
// TODO cppcheck-suppress returnDanglingLifetime
|
// cppcheck-suppress returnDanglingLifetime
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,7 @@ wxString::iterator wxString_test3()
|
||||||
{
|
{
|
||||||
wxString wxString1;
|
wxString wxString1;
|
||||||
wxString wxString2;
|
wxString wxString2;
|
||||||
// cppcheck-suppress iterators2
|
// cppcheck-suppress mismatchingContainers
|
||||||
for (wxString::iterator it = wxString1.begin(); it != wxString2.end(); ++it)
|
for (wxString::iterator it = wxString1.begin(); it != wxString2.end(); ++it)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
171
test/teststl.cpp
171
test/teststl.cpp
|
@ -357,21 +357,23 @@ private:
|
||||||
void iterator1() {
|
void iterator1() {
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" for (list<int>::iterator it = l1.begin(); it != l2.end(); ++it)\n"
|
" for (std::list<int>::iterator it = l1.begin(); it != l2.end(); ++it)\n"
|
||||||
" { }\n"
|
" { }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" for (list<int>::iterator it = l1.begin(); l2.end() != it; ++it)\n"
|
" for (std::list<int>::iterator it = l1.begin(); l2.end() != it; ++it)\n"
|
||||||
" { }\n"
|
" { }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l2' and 'l1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("struct C { std::list<int> l1; void func(); };\n"
|
check("struct C { std::list<int> l1; void func(); };\n"
|
||||||
"void C::func() {\n"
|
"void C::func() {\n"
|
||||||
|
@ -385,79 +387,82 @@ private:
|
||||||
// Same check with reverse iterator
|
// Same check with reverse iterator
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" for (list<int>::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n"
|
" for (std::list<int>::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n"
|
||||||
" { }\n"
|
" { }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator2() {
|
void iterator2() {
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" while (it != l2.end())\n"
|
" while (it != l2.end())\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" ++it;\n"
|
" ++it;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" while (l2.end() != it)\n"
|
" while (l2.end() != it)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" ++it;\n"
|
" ++it;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Iterators of different containers 'l2' and 'l1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator3() {
|
void iterator3() {
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" l2.insert(it, 0);\n"
|
" l2.insert(it, 0);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str());
|
||||||
|
|
||||||
check("void foo() {\n" // #5803
|
check("void foo() {\n" // #5803
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" l2.insert(it, l1.end());\n"
|
" l2.insert(it, l1.end());\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
check("void foo() {\n" // #7658
|
check("void foo() {\n" // #7658
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" list<int>::iterator end = l1.end();\n"
|
" std::list<int>::iterator end = l1.end();\n"
|
||||||
" l2.insert(it, end);\n"
|
" l2.insert(it, end);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// only warn for insert when there are preciself 2 arguments.
|
// only warn for insert when there are preciself 2 arguments.
|
||||||
check("void foo() {\n"
|
check("void foo() {\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" l2.insert(it);\n"
|
" l2.insert(it);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
check("void foo() {\n"
|
check("void foo() {\n"
|
||||||
" list<int> l1;\n"
|
" std::list<int> l1;\n"
|
||||||
" list<int> l2;\n"
|
" std::list<int> l2;\n"
|
||||||
" list<int>::iterator it = l1.begin();\n"
|
" std::list<int>::iterator it = l1.begin();\n"
|
||||||
" l2.insert(it,0,1);\n"
|
" l2.insert(it,0,1);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
@ -485,7 +490,8 @@ private:
|
||||||
" std::vector<int> ints2;\n"
|
" std::vector<int> ints2;\n"
|
||||||
" std::vector<int>::iterator it = std::find(ints1.begin(), ints2.end(), 22);\n"
|
" std::vector<int>::iterator it = std::find(ints1.begin(), ints2.end(), 22);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'ints1' and 'ints2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator6() {
|
void iterator6() {
|
||||||
|
@ -516,7 +522,8 @@ private:
|
||||||
" std::vector<int> ints2;\n"
|
" std::vector<int> ints2;\n"
|
||||||
" std::vector<int>::iterator it = std::inplace_merge(ints1.begin(), std::advance(ints1.rbegin(), 5), ints2.end());\n"
|
" std::vector<int>::iterator it = std::inplace_merge(ints1.begin(), std::advance(ints1.rbegin(), 5), ints2.end());\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'ints1' and 'ints2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -534,7 +541,8 @@ private:
|
||||||
" std::vector<int> ints2;\n"
|
" std::vector<int> ints2;\n"
|
||||||
" std::vector<int>::iterator it = std::find_first_of(ints1.begin(), ints2.end(), ints1.begin(), ints1.end());\n"
|
" std::vector<int>::iterator it = std::find_first_of(ints1.begin(), ints2.end(), ints1.begin(), ints1.end());\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'ints1' and 'ints2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -542,7 +550,8 @@ private:
|
||||||
" std::vector<int> ints2;\n"
|
" std::vector<int> ints2;\n"
|
||||||
" std::vector<int>::iterator it = std::find_first_of(ints1.begin(), ints1.end(), ints2.begin(), ints1.end());\n"
|
" std::vector<int>::iterator it = std::find_first_of(ints1.begin(), ints1.end(), ints2.begin(), ints1.end());\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'ints2' and 'ints1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -550,7 +559,8 @@ private:
|
||||||
" std::vector<int> ints2;\n"
|
" std::vector<int> ints2;\n"
|
||||||
" std::vector<int>::iterator it = std::find_first_of(foo.bar.begin(), foo.bar.end()-6, ints2.begin(), ints1.end());\n"
|
" std::vector<int>::iterator it = std::find_first_of(foo.bar.begin(), foo.bar.end()-6, ints2.begin(), ints1.end());\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'ints2' and 'ints1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -571,8 +581,9 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS( // TODO "[test.cpp:2]: (error) Iterators of different containers are used together.\n"
|
ASSERT_EQUALS( // TODO "[test.cpp:2]: (error) Iterators of different containers are used together.\n"
|
||||||
// TODO "[test.cpp:3]: (error) Iterators of different containers are used together.\n"
|
// TODO "[test.cpp:3]: (error) Iterators of different containers are used together.\n"
|
||||||
"[test.cpp:4]: (error) Iterators of different containers are used together.\n"
|
"[test.cpp:4]: (error) Iterators of different containers 'tp3' and 'a' are used together.\n"
|
||||||
"[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str());
|
"[test.cpp:5]: (error) Iterators of different containers 'tp4' and 'b' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator9() {
|
void iterator9() {
|
||||||
|
@ -640,7 +651,8 @@ private:
|
||||||
" if (it != s2.end()) continue;\n"
|
" if (it != s2.end()) continue;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 's1' and 's2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator11() {
|
void iterator11() {
|
||||||
|
@ -662,7 +674,8 @@ private:
|
||||||
" std::map<int, int>::const_iterator it = map1.find(123);\n"
|
" std::map<int, int>::const_iterator it = map1.find(123);\n"
|
||||||
" if (it == map2.end()) { }"
|
" if (it == map2.end()) { }"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Iterators of different containers 'map1' and 'map2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" std::map<int, int> map1;\n"
|
" std::map<int, int> map1;\n"
|
||||||
|
@ -670,7 +683,8 @@ private:
|
||||||
" std::map<int, int>::const_iterator it = map1.find(123);\n"
|
" std::map<int, int>::const_iterator it = map1.find(123);\n"
|
||||||
" if (map2.end() == it) { }"
|
" if (map2.end() == it) { }"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (error) Same iterator is used with different containers 'map2' and 'map1'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'map2' and 'map1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void f(std::string &s) {\n"
|
check("void f(std::string &s) {\n"
|
||||||
" int pos = s.find(x);\n"
|
" int pos = s.find(x);\n"
|
||||||
|
@ -692,7 +706,8 @@ private:
|
||||||
" while (it!=a.end())\n"
|
" while (it!=a.end())\n"
|
||||||
" ++it;\n"
|
" ++it;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:8]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:8]: (error) Iterators of different containers 't' and 'a' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
// #4062
|
// #4062
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
|
@ -753,7 +768,8 @@ private:
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -766,7 +782,8 @@ private:
|
||||||
" ++it2;\n"
|
" ++it2;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Iterators of different containers 'l2' and 'l1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -779,21 +796,22 @@ private:
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" std::list<int> l1;\n"
|
" std::set<int> l1;\n"
|
||||||
" std::list<int> l2(10, 4);\n"
|
" std::set<int> l2(10, 4);\n"
|
||||||
" std::list<int>::iterator it1 = l1.begin();\n"
|
" std::set<int>::iterator it1 = l1.begin();\n"
|
||||||
" std::list<int>::iterator it2 = l2.find(4);\n"
|
" std::set<int>::iterator it2 = l2.find(4);\n"
|
||||||
" while (it1 != it2)\n"
|
" while (it1 != it2)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator17() {
|
void iterator17() {
|
||||||
|
@ -809,7 +827,8 @@ private:
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:5] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -854,7 +873,8 @@ private:
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:9]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator18() {
|
void iterator18() {
|
||||||
|
@ -920,8 +940,9 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n",
|
TODO_ASSERT_EQUALS(
|
||||||
"[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n",
|
"[test.cpp:7] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n",
|
||||||
|
"[test.cpp:7] -> [test.cpp:7]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n[test.cpp:7]: (error) Dangerous comparison using operator< on iterator.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
|
@ -936,7 +957,9 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str());
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:8] -> [test.cpp:4]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -950,8 +973,9 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:4] -> [test.cpp:7]: (error) Comparison of iterators from containers 'l1' that are defined in different scopes.\n", errout.str());
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:8] -> [test.cpp:7]: (error) Same iterator is used with containers 'l1' that are defined in different scopes.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator20() {
|
void iterator20() {
|
||||||
|
@ -967,7 +991,8 @@ private:
|
||||||
" ++it1;\n"
|
" ++it1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (error) Same iterator is used with different containers 'l2' and 'l1'.\n", "", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (error) Iterators of different containers 'l2' and 'l1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("std::list<int> l3;\n"
|
check("std::list<int> l3;\n"
|
||||||
"std::list<int>::iterator bar()\n"
|
"std::list<int>::iterator bar()\n"
|
||||||
|
@ -1004,8 +1029,9 @@ private:
|
||||||
" {\n"
|
" {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n"
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n"
|
||||||
"[test.cpp:10] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l2' and 'l1'.\n", errout.str());
|
"[test.cpp:6]: (error) Iterators of different containers 'l2' and 'l1' are used together.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@ -1017,7 +1043,8 @@ private:
|
||||||
" {\n"
|
" {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6] -> [test.cpp:5]: (error) Comparison of iterators from containers 'l1' and 'l2'.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers 'l1' and 'l2' are used together.\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iterator22() { // #7107
|
void iterator22() { // #7107
|
||||||
|
@ -1453,9 +1480,9 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// #2101
|
// #2101
|
||||||
check("void f(vector< list<int> > &ints, unsigned int i)\n"
|
check("void f(vector< std::list<int> > &ints, unsigned int i)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" list<int>::iterator it;\n"
|
" std::list<int>::iterator it;\n"
|
||||||
" for(it = ints[i].begin(); it != ints[i].end(); it++) {\n"
|
" for(it = ints[i].begin(); it != ints[i].end(); it++) {\n"
|
||||||
" if (*it % 2)\n"
|
" if (*it % 2)\n"
|
||||||
" it = ints[i].erase(it);\n"
|
" it = ints[i].erase(it);\n"
|
||||||
|
@ -1717,11 +1744,11 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void eraseAssign2() {
|
void eraseAssign2() {
|
||||||
check("void f(list<int> &ints)\n"
|
check("void f(std::list<int> &ints)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" for (list<int>::iterator it = ints.begin(); it != ints.end();) {\n"
|
" for (std::list<int>::iterator it = ints.begin(); it != ints.end();) {\n"
|
||||||
" if (*it == 123) {\n"
|
" if (*it == 123) {\n"
|
||||||
" list<int>::iterator copy = it;\n"
|
" std::list<int>::iterator copy = it;\n"
|
||||||
" ++copy;\n"
|
" ++copy;\n"
|
||||||
" ints.erase(it);\n"
|
" ints.erase(it);\n"
|
||||||
" it = copy;\n"
|
" it = copy;\n"
|
||||||
|
|
Loading…
Reference in New Issue