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:
Paul Fultz II 2019-12-25 02:32:50 -06:00 committed by Daniel Marjamäki
parent 0cd2935dc7
commit 42d44f02a2
7 changed files with 200 additions and 263 deletions

View File

@ -4839,7 +4839,7 @@
</arg> </arg>
</function> </function>
<!-- ##### Container ##### --> <!-- ##### Container ##### -->
<container id="qtContainer" opLessAllowed="false" itEndPattern="&gt; :: iterator|const_iterator"> <container id="qtContainer" endPattern="&gt; !!::" opLessAllowed="false" itEndPattern="&gt; :: 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&lt;QString&gt;" />
<size>
<function name="removeDuplicates" action="change"/>
</size>
</container>
<define name="Q_ARG(type, data)" value="QArgument&lt;type &gt;(#type, data)"/> <define name="Q_ARG(type, data)" value="QArgument&lt;type &gt;(#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()"/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
@ -569,10 +579,11 @@ private:
" const std::string fp1 = std::string(a.begin(), a.end());\n" " const std::string fp1 = std::string(a.begin(), a.end());\n"
" const std::string tp2(a.begin(), a.end());\n" " const std::string tp2(a.begin(), a.end());\n"
"}"); "}");
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"