Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -199,7 +199,7 @@ : public Checker, check::PreStmt, - check::PostStmt, + check::PostStmt, check::Bind, check::LiveSymbols, check::DeadSymbols, eval::Assume> { @@ -227,10 +227,18 @@ void handleAssign(CheckerContext &C, const SVal &Cont, const Expr *CE = nullptr, const SVal &OldCont = UndefinedVal()) const; + void handleClear(CheckerContext &C, const SVal &Cont) const; void handlePushBack(CheckerContext &C, const SVal &Cont) const; void handlePopBack(CheckerContext &C, const SVal &Cont) const; void handlePushFront(CheckerContext &C, const SVal &Cont) const; void handlePopFront(CheckerContext &C, const SVal &Cont) const; + void handleInsert(CheckerContext &C, const SVal &Iter) const; + void handleErase(CheckerContext &C, const SVal &Iter) const; + void handleErase(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; + void handleEraseAfter(CheckerContext &C, const SVal &Iter) const; + void handleEraseAfter(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, const SVal &RetVal, const SVal &LHS, const SVal &RHS) const; @@ -267,6 +275,9 @@ void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const; + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; + void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; void checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; @@ -292,12 +303,18 @@ bool isComparisonOperator(OverloadedOperatorKind OK); bool isBeginCall(const FunctionDecl *Func); bool isEndCall(const FunctionDecl *Func); +bool isAssignCall(const FunctionDecl *Func); +bool isClearCall(const FunctionDecl *Func); bool isPushBackCall(const FunctionDecl *Func); bool isEmplaceBackCall(const FunctionDecl *Func); bool isPopBackCall(const FunctionDecl *Func); bool isPushFrontCall(const FunctionDecl *Func); bool isEmplaceFrontCall(const FunctionDecl *Func); bool isPopFrontCall(const FunctionDecl *Func); +bool isInsertCall(const FunctionDecl *Func); +bool isEraseCall(const FunctionDecl *Func); +bool isEraseAfterCall(const FunctionDecl *Func); +bool isEmplaceCall(const FunctionDecl *Func); bool isAssignmentOperator(OverloadedOperatorKind OK); bool isSimpleComparisonOperator(OverloadedOperatorKind OK); bool isAccessOperator(OverloadedOperatorKind OK); @@ -344,9 +361,18 @@ bool Equal); ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, const MemRegion *Cont); +ProgramStateRef +invalidateAllIteratorPositionsExcept(ProgramStateRef State, + const MemRegion *Cont, SymbolRef Offset, + BinaryOperator::Opcode Opc); ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, SymbolRef Offset, BinaryOperator::Opcode Opc); +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset1, + BinaryOperator::Opcode Opc1, + SymbolRef Offset2, + BinaryOperator::Opcode Opc2); ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, const MemRegion *Cont, const MemRegion *NewCont); @@ -443,6 +469,33 @@ verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); } } + } else if (const auto *InstCall = dyn_cast(&Call)) { + if (!ChecksEnabled[CK_MismatchedIteratorChecker]) + return; + + const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); + if (!ContReg) + return; + // Check for erase, insert and emplace using iterator of another container + if (isEraseCall(Func) || isEraseAfterCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 2) { + verifyMatch(C, Call.getArgSVal(1), + InstCall->getCXXThisVal().getAsRegion()); + } + } else if (isInsertCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 3 && + isIteratorType(Call.getArgExpr(1)->getType()) && + isIteratorType(Call.getArgExpr(2)->getType())) { + verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); + } + } else if (isEmplaceCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + } } else if (!isa(&Call)) { // The main purpose of iterators is to abstract away from different // containers and provide a (maybe limited) uniform access to them. @@ -563,7 +616,11 @@ } } else { if (const auto *InstCall = dyn_cast(&Call)) { - if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { + if (isAssignCall(Func)) { + handleAssign(C, InstCall->getCXXThisVal()); + } else if (isClearCall(Func)) { + handleClear(C, InstCall->getCXXThisVal()); + } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { handlePushBack(C, InstCall->getCXXThisVal()); } else if (isPopBackCall(Func)) { handlePopBack(C, InstCall->getCXXThisVal()); @@ -571,6 +628,20 @@ handlePushFront(C, InstCall->getCXXThisVal()); } else if (isPopFrontCall(Func)) { handlePopFront(C, InstCall->getCXXThisVal()); + } else if (isInsertCall(Func) || isEmplaceCall(Func)) { + handleInsert(C, Call.getArgSVal(0)); + } else if (isEraseCall(Func)) { + if (Call.getNumArgs() == 1) { + handleErase(C, Call.getArgSVal(0)); + } else if (Call.getNumArgs() == 2) { + handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } + } else if (isEraseAfterCall(Func)) { + if (Call.getNumArgs() == 1) { + handleEraseAfter(C, Call.getArgSVal(0)); + } else if (Call.getNumArgs() == 2) { + handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } } } @@ -675,6 +746,22 @@ } } +void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, + CheckerContext &C) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Val); + if (Pos) { + State = setIteratorPosition(State, Loc, *Pos); + C.addTransition(State); + } else { + const auto *OldPos = getIteratorPosition(State, Loc); + if (OldPos) { + State = removeIteratorPosition(State, Loc); + C.addTransition(State); + } + } +} + void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const { /* Transfer iterator state to temporary objects */ @@ -1206,6 +1293,33 @@ C.addTransition(State); } +void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + while (const auto *CBOR = ContReg->getAs()) { + ContReg = CBOR->getSuperRegion(); + } + + // The clear() operation invalidates all the iterators, except the past-end + // iterators of list-like containers + auto State = C.getState(); + if (!hasSubscriptOperator(ContReg) || !backModifiable(ContReg)) { + const auto CData = getContainerData(State, ContReg); + if (CData) { + if (const auto EndSym = CData->getEnd()) { + State = + invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); + C.addTransition(State); + return; + } + } + } + State = invalidateAllIteratorPositions(State, ContReg); + C.addTransition(State); +} + void IteratorChecker::handlePushBack(CheckerContext &C, const SVal &Cont) const { const auto *ContReg = Cont.getAsRegion(); @@ -1355,6 +1469,126 @@ } } +void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions after the insertion. + const auto *Cont = Pos->getContainer(); + if (hasSubscriptOperator(Cont) && backModifiable(Cont)) { + if (frontModifiable(Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + C.addTransition(State); + } +} + +void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions at and after the + // deletion. For list-like containers only invalidate the deleted position. + const auto *Cont = Pos->getContainer(); + if (hasSubscriptOperator(Cont) && backModifiable(Cont)) { + if (frontModifiable(Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); + } + C.addTransition(State); +} + +void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const { + auto State = C.getState(); + const auto *Pos1 = getIteratorPosition(State, Iter1); + const auto *Pos2 = getIteratorPosition(State, Iter2); + if (!Pos1 || !Pos2) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions at and after the + // deletion range. For list-like containers only invalidate the deleted + // position range [first..last]. + const auto *Cont = Pos1->getContainer(); + if (hasSubscriptOperator(Cont) && backModifiable(Cont)) { + if (frontModifiable(Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + } else { + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, + Pos2->getOffset(), BO_LT); + } + C.addTransition(State); +} + +void IteratorChecker::handleEraseAfter(CheckerContext &C, + const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // Invalidate the deleted iterator position, which is the position of the + // parameter plus one. + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto NextSym = + SVB.evalBinOp(State, BO_Add, + nonloc::SymbolVal(Pos->getOffset()), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(Pos->getOffset())).getAsSymbol(); + State = invalidateIteratorPositions(State, NextSym, BO_EQ); + C.addTransition(State); +} + +void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const { + auto State = C.getState(); + const auto *Pos1 = getIteratorPosition(State, Iter1); + const auto *Pos2 = getIteratorPosition(State, Iter2); + if (!Pos1 || !Pos2) + return; + + // Invalidate the deleted iterator position range (first..last) + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, + Pos2->getOffset(), BO_LT); + C.addTransition(State); +} + void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, const SVal &Val, CheckerContext &C, ExplodedNode *ErrNode) const { @@ -1474,6 +1708,24 @@ return IdInfo->getName().endswith_lower("end"); } +bool isAssignCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 2) + return false; + return IdInfo->getName() == "assign"; +} + +bool isClearCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 0) + return false; + return IdInfo->getName() == "clear"; +} + bool isPushBackCall(const FunctionDecl *Func) { const auto *IdInfo = Func->getIdentifier(); if (!IdInfo) @@ -1528,6 +1780,56 @@ return IdInfo->getName() == "pop_front"; } +bool isInsertCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2 || Func->getNumParams() > 3) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "insert"; +} + +bool isEmplaceCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "emplace"; +} + +bool isEraseCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1 || Func->getNumParams() > 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + if (Func->getNumParams() == 2 && + !isIteratorType(Func->getParamDecl(1)->getType())) + return false; + return IdInfo->getName() == "erase"; +} + +bool isEraseAfterCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1 || Func->getNumParams() > 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + if (Func->getNumParams() == 2 && + !isIteratorType(Func->getParamDecl(1)->getType())) + return false; + return IdInfo->getName() == "erase_after"; +} + bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; } bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { @@ -1863,6 +2165,20 @@ return processIteratorPositions(State, MatchCont, Invalidate); } +ProgramStateRef +invalidateAllIteratorPositionsExcept(ProgramStateRef State, + const MemRegion *Cont, SymbolRef Offset, + BinaryOperator::Opcode Opc) { + auto MatchContAndCompare = [&](const IteratorPosition &Pos) { + return Pos.getContainer() == Cont && + !compare(State, Pos.getOffset(), Offset, Opc); + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, MatchContAndCompare, Invalidate); +} + ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, SymbolRef Offset, BinaryOperator::Opcode Opc) { @@ -1875,6 +2191,21 @@ return processIteratorPositions(State, Compare, Invalidate); } +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset1, + BinaryOperator::Opcode Opc1, + SymbolRef Offset2, + BinaryOperator::Opcode Opc2) { + auto Compare = [&](const IteratorPosition &Pos) { + return compare(State, Pos.getOffset(), Offset1, Opc1) && + compare(State, Pos.getOffset(), Offset2, Opc2); + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, Compare, Invalidate); +} + ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, const MemRegion *Cont, const MemRegion *NewCont) { Index: test/Analysis/Inputs/system-header-simulator-cxx.h =================================================================== --- test/Analysis/Inputs/system-header-simulator-cxx.h +++ test/Analysis/Inputs/system-header-simulator-cxx.h @@ -269,6 +269,21 @@ void emplace_back(Args&&... args); void pop_back(); + iterator insert(const_iterator position, const value_type &val); + iterator insert(const_iterator position, size_type n, + const value_type &val); + template + iterator insert(const_iterator position, InputIterator first, + InputIterator last); + iterator insert(const_iterator position, value_type &&val); + iterator insert(const_iterator position, initializer_list il); + + template + iterator emplace(const_iterator position, Args&&... args); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + T &operator[](size_t n) { return _start[n]; } @@ -331,6 +346,21 @@ void emplace_front(Args&&... args); void pop_front(); + iterator insert(const_iterator position, const value_type &val); + iterator insert(const_iterator position, size_type n, + const value_type &val); + template + iterator insert(const_iterator position, InputIterator first, + InputIterator last); + iterator insert(const_iterator position, value_type &&val); + iterator insert(const_iterator position, initializer_list il); + + template + iterator emplace(const_iterator position, Args&&... args); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } const_iterator cbegin() const { return const_iterator(_start); } @@ -389,6 +419,21 @@ void emplace_front(Args&&... args); void pop_front(); + iterator insert(const_iterator position, const value_type &val); + iterator insert(const_iterator position, size_type n, + const value_type &val); + template + iterator insert(const_iterator position, InputIterator first, + InputIterator last); + iterator insert(const_iterator position, value_type &&val); + iterator insert(const_iterator position, initializer_list il); + + template + iterator emplace(const_iterator position, Args&&... args); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + T &operator[](size_t n) { return _start[n]; } @@ -445,6 +490,22 @@ void emplace_front(Args&&... args); void pop_front(); + iterator insert_after(const_iterator position, const value_type &val); + iterator insert_after(const_iterator position, value_type &&val); + iterator insert_after(const_iterator position, size_type n, + const value_type &val); + template + iterator insert_after(const_iterator position, InputIterator first, + InputIterator last); + iterator insert_after(const_iterator position, + initializer_list il); + + template + iterator emplace_after(const_iterator position, Args&&... args); + + iterator erase_after(const_iterator position); + iterator erase_after(const_iterator first, const_iterator last); + iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } const_iterator cbegin() const { return const_iterator(_start); } Index: test/Analysis/diagnostics/explicit-suppression.cpp =================================================================== --- test/Analysis/diagnostics/explicit-suppression.cpp +++ test/Analysis/diagnostics/explicit-suppression.cpp @@ -19,6 +19,6 @@ void testCopyNull(C *I, C *E) { std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:566 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:627 {{Called C++ object pointer is null}} #endif } Index: test/Analysis/invalidated-iterator.cpp =================================================================== --- test/Analysis/invalidated-iterator.cpp +++ test/Analysis/invalidated-iterator.cpp @@ -31,6 +31,56 @@ *i0; // expected-warning{{Invalidated iterator accessed}} } +void bad_assign_list1(std::list &L, int n) { + auto i0 = L.cbegin(); + L.assign(10, n); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_assign_vector1(std::vector &V, int n) { + auto i0 = V.cbegin(); + V.assign(10, n); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_assign_deque1(std::deque &D, int n) { + auto i0 = D.cbegin(); + D.assign(10, n); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_assign_forward_list1(std::forward_list &FL, int n) { + auto i0 = FL.cbegin(); + FL.assign(10, n); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void good_clear_list1(std::list &L) { + auto i0 = L.cend(); + L.clear(); + --i0; // no-warning +} + +void bad_clear_list1(std::list &L) { + auto i0 = L.cbegin(), i1 = L.cend(); + L.clear(); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_clear_vector1(std::vector &V) { + auto i0 = V.cbegin(), i1 = V.cend(); + V.clear(); + *i0; // expected-warning{{Invalidated iterator accessed}} + --i1; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_clear_deque1(std::deque &D) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.clear(); + *i0; // expected-warning{{Invalidated iterator accessed}} + --i1; // expected-warning{{Invalidated iterator accessed}} +} + void good_push_back_list1(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(); L.push_back(n); @@ -197,3 +247,153 @@ FL.pop_front(); *i0; // expected-warning{{Invalidated iterator accessed}} } + +void good_insert_list1(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + L.insert(i1, n); + *i0; // no-warning + *i1; // no-warning +} + +void good_insert_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, n); + *i0; // no-warning +} + +void bad_insert_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, n); + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_insert_deque1(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, n); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void good_emplace_list1(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + L.emplace(i1, n); + *i0; // no-warning + *i1; // no-warning +} + +void good_emplace_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.emplace(i1, n); + *i0; // no-warning +} + +void bad_emplace_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.emplace(i1, n); + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_emplace_deque1(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.emplace(i1, n); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_list1(std::list &L) { + auto i2 = L.cbegin(), i0 = i2++, i1 = i2++; + L.erase(i1); + *i0; // no-warning + *i2; // no-warning +} + +void bad_erase_list1(std::list &L) { + auto i0 = L.cbegin(); + L.erase(i0); + *i0; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_vector1(std::vector &V) { + auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; + V.erase(i1); + *i0; // no-warning +} + +void bad_erase_vector1(std::vector &V) { + auto i1 = V.cbegin(), i0 = i1++; + V.erase(i0); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_erase_deque1(std::deque &D) { + auto i2 = D.cbegin(), i0 = i2++, i1 = i2++; + D.erase(i1); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} + *i2; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_list2(std::list &L) { + auto i3 = L.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + L.erase(i1, i3); + *i0; // no-warning + *i3; // no-warning +} + +void bad_erase_list2(std::list &L) { + auto i2 = L.cbegin(), i0 = i2++, i1 = i2++; + L.erase(i0, i2); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_vector2(std::vector &V) { + auto i3 = V.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;; + V.erase(i1, i3); + *i0; // no-warning +} + +void bad_erase_vector2(std::vector &V) { + auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; + V.erase(i0, i2); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} + *i2; // expected-warning{{Invalidated iterator accessed}} +} + +void bad_erase_deque2(std::deque &D) { + auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + D.erase(i1, i3); + *i0; // expected-warning{{Invalidated iterator accessed}} + *i1; // expected-warning{{Invalidated iterator accessed}} + *i2; // expected-warning{{Invalidated iterator accessed}} + *i3; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_after_forward_list1(std::forward_list &FL) { + auto i2 = FL.cbegin(), i0 = i2++, i1 = i2++; + FL.erase_after(i0); + *i0; // no-warning + *i2; // no-warning +} + +void bad_erase_after_forward_list1(std::forward_list &FL) { + auto i1 = FL.cbegin(), i0 = i1++; + FL.erase_after(i0); + *i1; // expected-warning{{Invalidated iterator accessed}} +} + +void good_erase_after_forward_list2(std::forward_list &FL) { + auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + FL.erase_after(i0, i3); + *i0; // no-warning + *i3; // no-warning +} + +void bad_erase_after_forward_list2(std::forward_list &FL) { + auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + FL.erase_after(i0, i3); + *i1; // expected-warning{{Invalidated iterator accessed}} + *i2; // expected-warning{{Invalidated iterator accessed}} +} Index: test/Analysis/mismatched-iterator.cpp =================================================================== --- test/Analysis/mismatched-iterator.cpp +++ test/Analysis/mismatched-iterator.cpp @@ -3,6 +3,40 @@ #include "Inputs/system-header-simulator-cxx.h" +void good_insert1(std::vector &v, int n) { + v.insert(v.cbegin(), n); // no-warning +} + + +void good_insert2(std::vector &v, int len, int n) { + v.insert(v.cbegin(), len, n); // no-warning +} + +void good_insert3(std::vector &v1, std::vector &v2) { + v1.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // no-warning +} + +void good_insert4(std::vector &v, int len, int n) { + v.insert(v.cbegin(), {n-1, n, n+1}); // no-warning +} + +void good_insert_find(std::vector &v, int n, int m) { + auto i = std::find(v.cbegin(), v.cend(), n); + v.insert(i, m); // no-warning +} + +void good_erase1(std::vector &v) { + v.erase(v.cbegin()); // no-warning +} + +void good_erase2(std::vector &v) { + v.erase(v.cbegin(), v.cend()); // no-warning +} + +void good_emplace(std::vector &v, int n) { + v.emplace(v.cbegin(), n); // no-warning +} + void good_ctor(std::vector &v) { std::vector new_v(v.cbegin(), v.cend()); // no-warning } @@ -25,6 +59,38 @@ std::find(i0, v1.cend(), n); // no-warning } +void bad_insert1(std::vector &v1, std::vector &v2, int n) { + v2.insert(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert2(std::vector &v1, std::vector &v2, int len, int n) { + v2.insert(v1.cbegin(), len, n); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert3(std::vector &v1, std::vector &v2) { + v2.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v1.insert(v1.cbegin(), v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v1.insert(v1.cbegin(), v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert4(std::vector &v1, std::vector &v2, int len, int n) { + v2.insert(v1.cbegin(), {n-1, n, n+1}); // expected-warning{{Iterator access mismatched}} +} + +void bad_erase1(std::vector &v1, std::vector &v2) { + v2.erase(v1.cbegin()); // expected-warning{{Iterator access mismatched}} +} + +void bad_erase2(std::vector &v1, std::vector &v2) { + v2.erase(v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} + v2.erase(v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v2.erase(v1.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} +} + +void bad_emplace(std::vector &v1, std::vector &v2, int n) { + v2.emplace(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}} +} + void good_move_find2(std::vector &v1, std::vector &v2, int n) { auto i0 = --v2.cend(); v1 = std::move(v2); @@ -61,6 +127,35 @@ std::find(i0, v2.cend(), n); // expected-warning{{Iterator access mismatched}} } +void bad_insert_find(std::vector &v1, std::vector &v2, int n, int m) { + auto i = std::find(v1.cbegin(), v1.cend(), n); + v2.insert(i, m); // expected-warning{{Iterator access mismatched}} +} + +void good_overwrite(std::vector &v1, std::vector &v2, int n) { + auto i = v1.cbegin(); + i = v2.cbegin(); + v2.insert(i, n); // no-warning +} + +void bad_overwrite(std::vector &v1, std::vector &v2, int n) { + auto i = v1.cbegin(); + i = v2.cbegin(); + v1.insert(i, n); // expected-warning{{Iterator access mismatched}} +} + +void good_move(std::vector &v1, std::vector &v2) { + const auto i0 = ++v2.cbegin(); + v1 = std::move(v2); + v1.erase(i0); // no-warning +} + +void bad_move(std::vector &v1, std::vector &v2) { + const auto i0 = ++v2.cbegin(); + v1 = std::move(v2); + v2.erase(i0); // expected-warning{{Iterator access mismatched}} +} + void bad_move_find2(std::vector &v1, std::vector &v2, int n) { auto i0 = --v2.cend(); v1 = std::move(v2);