Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -227,7 +227,9 @@ const IteratorPosition &Pos, const SVal &Distance) const; void reportOutOfRangeBug(const StringRef &Message, const SVal &Val, - CheckerContext &C, ExplodedNode *ErrNode) const; + CheckerContext &C, ExplodedNode *ErrNode, + const IteratorPosition *Pos, + bool PastTheEnd = true) const; void reportMismatchedBug(const StringRef &Message, const SVal &Val1, const SVal &Val2, CheckerContext &C, ExplodedNode *ErrNode) const; @@ -235,7 +237,8 @@ const MemRegion *Reg, CheckerContext &C, ExplodedNode *ErrNode) const; void reportInvalidatedBug(const StringRef &Message, const SVal &Val, - CheckerContext &C, ExplodedNode *ErrNode) const; + CheckerContext &C, ExplodedNode *ErrNode, + const IteratorPosition *Pos) const; public: IteratorChecker(); @@ -262,6 +265,52 @@ }; } // namespace +// This visitor helps the user to better understand the out-of-range and the +// invalidated iterator bug types. It adds a note to the statement which +// results in the past-the-end, beginning of the range or the invalideted +// state of the iterator. Furthermore it also markes the block edges where +// we assume that the container of the iterator is empty or non-empty. +class IteratorBRVisitor final : public BugReporterVisitor { +public: + enum ErrorTypeT { AheadOfRange, PastTheEnd, Invalidated }; + +private: + SVal Iter; + const MemRegion *Cont; + ErrorTypeT ErrorType; + + // `FoundFirst` becomes true when we find the first assignment to the + // iterator. + // `FoundWrong` becomes true when we find the statement that results in the + // current wrong state of the iterator. + // `FoundEmptyness` becomes true when we find the block edge assuming + // emptiness or non-emptiness of the container. + bool FoundFirst = false, FoundWrong = false, FoundEmptiness; + +public: + IteratorBRVisitor(SVal It, const MemRegion *C, ErrorTypeT ET) + : Iter(It), Cont(C), ErrorType(ET) { + // Emptyness does not matter for invalidated iterator access + FoundEmptiness = ErrorType == Invalidated; + } + IteratorBRVisitor(const IteratorBRVisitor &LHS, SVal NewIt) + : Iter(NewIt), Cont(LHS.Cont), ErrorType(LHS.ErrorType), + FoundWrong(true), FoundEmptiness(LHS.FoundEmptiness) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(Iter); + ID.Add(Cont); + ID.AddInteger(ErrorType); + } + + std::shared_ptr VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; +private: + SVal getPredecessor(const ExplodedNode *Node, const Stmt *Stmt, + BugReport &BR); +}; + REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition) REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, IteratorPosition) @@ -926,7 +975,7 @@ auto *N = C.generateNonFatalErrorNode(State); if (!N) return; - reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N); + reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N, Pos); return; } } @@ -939,7 +988,7 @@ if (!N) { return; } - reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N); + reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N, Pos); } } @@ -1047,14 +1096,14 @@ if (!N) return; reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS, - C, N); + C, N, Pos, false); } if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) { auto *N = C.generateNonFatalErrorNode(State); if (!N) return; reportOutOfRangeBug("Iterator incremented behind the past-the-end " - "iterator.", LHS, C, N); + "iterator.", LHS, C, N, Pos); } } @@ -1224,21 +1273,23 @@ OldEndSym, BO_GE); auto &SymMgr = C.getSymbolManager(); auto &SVB = C.getSValBuilder(); - // Then generate and assign a new "end" symbol for the new container. - auto NewEndSym = - SymMgr.conjureSymbol(CE, C.getLocationContext(), - C.getASTContext().LongTy, C.blockCount()); - State = assumeNoOverflow(State, NewEndSym, 4); if (CData) { - State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); + State = setContainerData(State, ContReg, CData->newEnd(OldEndSym)); } else { State = setContainerData(State, ContReg, - ContainerData::fromEnd(NewEndSym)); + ContainerData::fromEnd(OldEndSym)); } + // Then generate and assign a new "end" symbol for the old container. + auto NewEndSym = + SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, NewEndSym, 4); + State = setContainerData(State, OldContReg, + OldCData->newEnd(NewEndSym)); // Finally, replace the old "end" symbol in the already reassigned // iterator positions with the new "end" symbol. State = rebaseSymbolInIteratorPositionsIf( - State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); + State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_GE); } else { // There was no "end" symbol assigned yet to the old container, // so reassign all iterator positions to the new container. @@ -1586,9 +1637,15 @@ void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, const SVal &Val, CheckerContext &C, - ExplodedNode *ErrNode) const { + ExplodedNode *ErrNode, + const IteratorPosition *Pos, + bool PastTheEnd) const { auto R = llvm::make_unique(*OutOfRangeBugType, Message, ErrNode); R->markInteresting(Val); + R->addVisitor(llvm::make_unique(Val, Pos->getContainer(), + PastTheEnd ? + IteratorBRVisitor::PastTheEnd : + IteratorBRVisitor::AheadOfRange)); C.emitReport(std::move(R)); } @@ -1614,12 +1671,281 @@ void IteratorChecker::reportInvalidatedBug(const StringRef &Message, const SVal &Val, CheckerContext &C, - ExplodedNode *ErrNode) const { + ExplodedNode *ErrNode, + const IteratorPosition *Pos) const { auto R = llvm::make_unique(*InvalidatedBugType, Message, ErrNode); R->markInteresting(Val); + R->addVisitor(llvm::make_unique(Val, Pos->getContainer(), + IteratorBRVisitor::Invalidated)); C.emitReport(std::move(R)); } +std::shared_ptr +IteratorBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, + BugReport &BR) { + if (FoundWrong && FoundEmptiness && FoundFirst) + return nullptr; + + const ExplodedNode *Pred = Succ->getFirstPred(); + const Stmt *S = nullptr; + const ProgramPointTag *Tag = nullptr; + const auto SP = Succ->getLocation().getAs(); + if (SP.hasValue()) { + S = SP->getStmt(); + Tag = SP->getTag(); + } else { + const auto E = Succ->getLocation().getAs(); + if (E.hasValue()) { + S = E->getSrc()->getTerminator().getStmt(); + Tag = E->getTag(); + } + } + + if (!S) + return nullptr; + + const auto &StateBefore = Pred->getState(); + const auto &StateAfter = Succ->getState(); + const auto *CDBefore = getContainerData(StateBefore, Cont); + const auto *CDAfter = getContainerData(StateAfter, Cont); + const auto *PosBefore = getIteratorPosition(StateBefore, Iter); + const auto *PosAfter = getIteratorPosition(StateAfter, Iter); + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + if (!FoundWrong) { + + // If the bug type is decremention of the iterator ahead of its range, + // then we must find a program state pair where in the predecessor we + // either do not store anything about the iterator or the container or + // the iterator does not reference to the first position of the container, + // while in the successor we store both the position of the iterator and + // the boundaries of its container and the iterator references to the + // first position of the container. + if (ErrorType == AheadOfRange && + ((!PosBefore || !CDBefore || + PosBefore->getOffset() != CDBefore->getBegin()) && + (PosAfter && CDAfter && + PosAfter->getOffset() == CDAfter->getBegin()))) { + Out << "Iterator reached the first position of the container."; + FoundWrong = true; + + // If the bug type is dereference or incremention of the past-the-end + // iterator, then we must find a program state pair where in the + // predecessor we either do not store anything about the iterator or the + // container or the iterator does not reference to the past-the-end + // of the container, while in the successor we store both the position + // of the iterator and the boundaries of its container and the iterator + // references to the past-the-end iterator of the container. + } else if (ErrorType == PastTheEnd && + ((!PosBefore || !CDBefore || + PosBefore->getOffset() != CDBefore->getEnd()) && + (PosAfter && CDAfter && + PosAfter->getOffset() == CDAfter->getEnd()))) { + Out << "Iterator reached the past-the-end position of the container."; + FoundWrong = true; + + // If the bug type is access of an invalidated iterator, then we must find + // a program state pair where in the predecessor we either do not store + // anything about the iterator or the iterator is valid, while in the + // successor we store the position of the iterator and it is invalidated. + } else if (ErrorType == Invalidated && + ((!PosBefore || PosBefore->isValid()) && + (PosAfter && !PosAfter->isValid()))) { + Out << "Iterator invalidated."; + FoundWrong = true; + } + } + + if ((FoundWrong || FoundEmptiness) && !FoundFirst && + (ErrorType == AheadOfRange || ErrorType == PastTheEnd) && PosAfter) { + SmallString<64> Buf2; + llvm::raw_svector_ostream Out2(Buf2); + + llvm::Optional DiffInt = llvm::None; + auto &SVB = StateAfter->getStateManager().getSValBuilder(); + auto &SymMgr = SVB.getSymbolManager(); + + // Try to calculate the difference between the new and the old position. + // If it is a concrete number then its is most probably an increment + // or decrement operation. + if (PosBefore) { + DiffInt = + SVB.evalBinOpNN(StateAfter, BO_Sub, + nonloc::SymbolVal(PosAfter->getOffset()), + nonloc::SymbolVal(PosBefore->getOffset()), + SymMgr.getType(PosAfter->getOffset())) + .getAs(); + } + + // If we did not get a concrete integer then try to find out whether the + // position of the iterator was just transferred from another iterator. + // If this is true then track that iterator as well and retry calculation + // of the difference using that iterator. + if (!DiffInt && Tag && + Tag->getTagDescription() == "alpha.cplusplus.IteratorModeling") { + SVal PredIter = getPredecessor(Succ, S, BR); + if (!PredIter.isUndef()) { + PosBefore = getIteratorPosition(StateBefore, PredIter); + if (PosBefore) { + DiffInt = + SVB.evalBinOpNN(StateAfter, BO_Sub, + nonloc::SymbolVal(PosAfter->getOffset()), + nonloc::SymbolVal(PosBefore->getOffset()), + SymMgr.getType(PosAfter->getOffset())) + .getAs(); + } + if (DiffInt && !DiffInt->getValue().getExtValue()) + Buf.clear(); + BR.addVisitor(llvm::make_unique(*this, PredIter)); + } + } + + if (DiffInt) { + int64_t D = DiffInt->getValue().getExtValue(); + if (D < - 1) { + Out2 << "Iterator decremented by " << -D; + } else if (D < 0) { + Out2 << "Iterator decremented"; + } else if (D > 1) { + Out2 << "Iterator incremented by " << D; + } else if (D > 0) { + Out2 << "Iterator incremented"; + } + } else { + Out2 << "Iterator received its initial value"; + FoundFirst = true; + } + + // If we just reached the "wrong" position and we did an increment/decrement + // at the same time then connect the two sentences by an "and" and finish + // the second one otherwise. + if (!Buf2.empty()) { + if (!Buf.empty()) { + Out2 << " and" << Buf.substr(8); + Buf.clear(); + } else { + Out2 << "."; + } + } + + // If we just initialized the iterator to the first or the past-the-end + // position of the container then refine the note. + if (Buf2.substr(0, 17) == "Iterator received") { + if (const auto *CData = getContainerData(StateAfter, + PosAfter->getContainer())) { + if (PosAfter->getOffset() == CData->getBegin()) { + Buf2.clear(); + Out2 << "Iterator set to the first position of the container"; + Buf.clear(); + } else if (PosAfter->getOffset() == CData->getEnd()) { + Buf2.clear(); + Out2 << "Iterator set to the past-the-end position of the container"; + Buf.clear(); + } + } + } + + Out << Buf2; + } + + if (!FoundEmptiness) { + ProgramStateRef NotEmptyBefore = + (CDBefore && CDBefore->getBegin() && CDBefore->getEnd()) ? + relateSymbols(StateBefore, CDBefore->getBegin(), CDBefore->getEnd(), + false) : StateBefore; + ProgramStateRef NotEmptyAfter = + (CDAfter && CDAfter->getBegin() && CDAfter->getEnd()) ? + relateSymbols(StateAfter, CDAfter->getBegin(), CDAfter->getEnd(), false) : + StateAfter; + ProgramStateRef EmptyBefore = + (CDBefore && CDBefore->getBegin() && CDBefore->getEnd()) ? + relateSymbols(StateBefore, CDBefore->getBegin(), CDBefore->getEnd(), + true) : StateBefore; + ProgramStateRef EmptyAfter = + (CDAfter && CDAfter->getBegin() && CDAfter->getEnd()) ? + relateSymbols(StateAfter, CDAfter->getBegin(), CDAfter->getEnd(), true) : + StateAfter; + + // If the container could be empty in the predecessor state and in the + // successor state it can be non-empty and cannot be empty, then we found + // the first program point where we assume it is non-empty. + if (EmptyBefore && (NotEmptyAfter && !EmptyAfter)) { + Out << "Assuming the container/range is non-empty."; + FoundEmptiness = true; + + // If the container could be non-empty in the predecessor state and in the + // successor state it can be empty and cannot be non-empty, then we found + // the first program point where we assume it is empty. + } else if (NotEmptyBefore && (EmptyAfter && !NotEmptyAfter)) { + Out << "Assuming the container/range is empty."; + FoundEmptiness = true; + } + } + + if (Buf.empty()) + return nullptr; + + auto L = PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), + Succ->getLocationContext()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + auto Piece = std::make_shared(L, Out.str()); + Piece->addRange(S->getSourceRange()); + + return std::move(Piece); +} + +SVal IteratorBRVisitor::getPredecessor(const ExplodedNode *Node, + const Stmt *S, BugReport &BR) { + static SVal Undef = UndefinedVal(); + + auto State = Node->getState(); + const auto *LCtx = Node->getLocationContext(); + + const auto IterSym = Iter.getAsSymbol(); + const auto *IterReg = Iter.getAsRegion(); + if (const auto IterLC = Iter.getAs()) { + IterReg = IterLC->getRegion(); + } + const auto *IterPos = getIteratorPosition(State, Iter); + assert(IterPos && + "This function is only for iterators with existing positions."); + + const auto *E = dyn_cast(S); + if (!E) + return Undef; + + const auto &RetVal = State->getSVal(E, LCtx); + const auto RetSym = RetVal.getAsSymbol(); + const auto *RetReg = RetVal.getAsRegion(); + if (const auto RetLC = RetVal.getAs()) { + RetReg = RetLC->getRegion(); + } + + // If the current iterator is the return value of an operator then it may be + // an copy or move assignment or an increment or decrement operator. In this + // case its parameter is the predecessor. + if (const auto *OpCall = dyn_cast(E)) { + if (IterSym == RetSym || IterReg == RetReg) { + return State->getSVal(OpCall->getArg(0), LCtx); + } + // If the current iterator is the result of a C++ construct expression + // and the constructor is a copy or move constructor then its parameter is + // the predecessor. + } else if (const auto *ConstrExpr = dyn_cast(E)) { + if (ConstrExpr->getConstructor()->isCopyOrMoveConstructor()) { + if (IterSym == RetSym || IterReg == RetReg) { + return State->getSVal(ConstrExpr->getArg(0), LCtx); + } + } + } + return Undef; +} + namespace { bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); 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 @@ -147,7 +147,7 @@ typedef std::bidirectional_iterator_tag iterator_category; __list_iterator(T* it = 0) : item(it) {} - __list_iterator(const iterator &rhs): item(rhs.base()) {} + __list_iterator(const iterator &rhs): item(rhs.item) {} __list_iterator operator++() { item = item->next; return *this; } __list_iterator operator++(int) { auto tmp = *this; @@ -172,6 +172,8 @@ const T* &base() const { return item; } + template friend struct __list_iterator; + private: T* item; }; @@ -187,7 +189,7 @@ typedef std::forward_iterator_tag iterator_category; __fwdl_iterator(T* it = 0) : item(it) {} - __fwdl_iterator(const iterator &rhs): item(rhs.base()) {} + __fwdl_iterator(const iterator &rhs): item(rhs.item) {} __fwdl_iterator operator++() { item = item->next; return *this; } __fwdl_iterator operator++(int) { auto tmp = *this; @@ -205,6 +207,8 @@ const T* &base() const { return item; } + template friend struct __fwdl_iterator; + private: T* item; }; 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:677 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:681 {{Called C++ object pointer is null}} #endif } Index: test/Analysis/invalidated-iterator.cpp =================================================================== --- test/Analysis/invalidated-iterator.cpp +++ test/Analysis/invalidated-iterator.cpp @@ -1,399 +1,828 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.InvalidatedIterator -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.InvalidatedIterator -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.InvalidatedIterator -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -analyzer-output=text -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.InvalidatedIterator -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -analyzer-output=text -verify #include "Inputs/system-header-simulator-cxx.h" -void bad_copy_assign_operator_list1(std::list &L1, +void bad_copy_assign_operator1_list(std::list &L1, const std::list &L2) { auto i0 = L1.cbegin(); - L1 = L2; + L1 = L2; // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_copy_assign_operator_vector1(std::vector &V1, +void bad_copy_assign_operator1_vector(std::vector &V1, const std::vector &V2) { auto i0 = V1.cbegin(); - V1 = V2; + V1 = V2; // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_copy_assign_operator_deque1(std::deque &D1, +void bad_copy_assign_operator1_deque(std::deque &D1, const std::deque &D2) { auto i0 = D1.cbegin(); - D1 = D2; + D1 = D2; // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_copy_assign_operator_forward_list1(std::forward_list &FL1, +void bad_copy_assign_operator1_forward_list(std::forward_list &FL1, const std::forward_list &FL2) { auto i0 = FL1.cbegin(); - FL1 = FL2; + FL1 = FL2; // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_assign_list1(std::list &L, int n) { +void bad_assign1_list(std::list &L, int n) { auto i0 = L.cbegin(); - L.assign(10, n); + L.assign(10, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_assign_vector1(std::vector &V, int n) { +void bad_assign1_vector(std::vector &V, int n) { auto i0 = V.cbegin(); - V.assign(10, n); + V.assign(10, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_assign_deque1(std::deque &D, int n) { +void bad_assign1_deque(std::deque &D, int n) { auto i0 = D.cbegin(); - D.assign(10, n); + D.assign(10, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_assign_forward_list1(std::forward_list &FL, int n) { +void bad_assign1_forward_list(std::forward_list &FL, int n) { auto i0 = FL.cbegin(); - FL.assign(10, n); + FL.assign(10, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_clear_list1(std::list &L) { +void good_clear1_list(std::list &L) { auto i0 = L.cend(); L.clear(); --i0; // no-warning } -void bad_clear_list1(std::list &L) { +void bad_clear1_list(std::list &L) { auto i0 = L.cbegin(), i1 = L.cend(); - L.clear(); + L.clear(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_clear_vector1(std::vector &V) { +void bad_clear1_vector(std::vector &V) { auto i0 = V.cbegin(), i1 = V.cend(); - V.clear(); + V.clear(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_clear1_vector_decr(std::vector &V) { + auto i0 = V.cbegin(), i1 = V.cend(); + V.clear(); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_clear_deque1(std::deque &D) { +void bad_clear1_deque(std::deque &D) { auto i0 = D.cbegin(), i1 = D.cend(); - D.clear(); + D.clear(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_clear1_deque_decr(std::deque &D) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.clear(); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_push_back_list1(std::list &L, int n) { +void good_push_back1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(); L.push_back(n); *i0; // no-warning --i1; // no-warning } -void good_push_back_vector1(std::vector &V, int n) { +void good_push_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(); V.push_back(n); *i0; // no-warning } -void bad_push_back_vector1(std::vector &V, int n) { +void bad_push_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(); - V.push_back(n); + V.push_back(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_push_back_deque1(std::deque &D, int n) { +void bad_push_back1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(); - D.push_back(n); + D.push_back(n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_push_back1_deque_decr(std::deque &D, int n) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.push_back(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_emplace_back_list1(std::list &L, int n) { +void good_emplace_back1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(); L.emplace_back(n); *i0; // no-warning --i1; // no-warning } -void good_emplace_back_vector1(std::vector &V, int n) { +void good_emplace_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(); V.emplace_back(n); *i0; // no-warning } -void bad_emplace_back_vector1(std::vector &V, int n) { +void bad_emplace_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(); - V.emplace_back(n); + V.emplace_back(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_emplace_back_deque1(std::deque &D, int n) { +void bad_emplace_back1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(); - D.emplace_back(n); + D.emplace_back(n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_emplace_back1_deque_decr(std::deque &D, int n) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.emplace_back(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_pop_back_list1(std::list &L, int n) { +void good_pop_back1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(), i2 = i1--; L.pop_back(); *i0; // no-warning *i2; // no-warning } -void bad_pop_back_list1(std::list &L, int n) { +void bad_pop_back1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(), i2 = i1--; - L.pop_back(); + L.pop_back(); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_pop_back_vector1(std::vector &V, int n) { +void good_pop_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--; V.pop_back(); *i0; // no-warning } -void bad_pop_back_vector1(std::vector &V, int n) { +void bad_pop_back1_vector(std::vector &V, int n) { auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--; - V.pop_back(); + V.pop_back(); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_pop_back1_vector_decr(std::vector &V, int n) { + auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--; + V.pop_back(); // expected-note{{Iterator invalidated}} --i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_pop_back_deque1(std::deque &D, int n) { +void good_pop_back1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--; D.pop_back(); *i0; // no-warning } -void bad_pop_back_deque1(std::deque &D, int n) { +void bad_pop_back1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--; - D.pop_back(); + D.pop_back(); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_pop_back1_deque_decr(std::deque &D, int n) { + auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--; + D.pop_back(); // expected-note{{Iterator invalidated}} --i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_push_front_list1(std::list &L, int n) { +void good_push_front1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(); L.push_front(n); *i0; // no-warning --i1; // no-warning } -void bad_push_front_deque1(std::deque &D, int n) { +void bad_push_front1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(); - D.push_front(n); + D.push_front(n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_push_front1_deque_decr(std::deque &D, int n) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.push_front(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_push_front_forward_list1(std::forward_list &FL, int n) { +void good_push_front1_forward_list(std::forward_list &FL, int n) { auto i0 = FL.cbegin(), i1 = FL.cend(); FL.push_front(n); *i0; // no-warning } -void good_emplace_front_list1(std::list &L, int n) { +void good_emplace_front1_list(std::list &L, int n) { auto i0 = L.cbegin(), i1 = L.cend(); L.emplace_front(n); *i0; // no-warning --i1; // no-warning } -void bad_emplace_front_deque1(std::deque &D, int n) { +void bad_emplace_front1_deque(std::deque &D, int n) { auto i0 = D.cbegin(), i1 = D.cend(); - D.emplace_front(n); + D.emplace_front(n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_emplace_front1_deque_decr(std::deque &D, int n) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.emplace_front(n); // expected-note{{Iterator invalidated}} --i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_emplace_front_forward_list1(std::forward_list &FL, int n) { +void good_emplace_front1_forward_list(std::forward_list &FL, int n) { auto i0 = FL.cbegin(), i1 = FL.cend(); FL.emplace_front(n); *i0; // no-warning } -void good_pop_front_list1(std::list &L, int n) { +void good_pop_front1_list(std::list &L, int n) { auto i1 = L.cbegin(), i0 = i1++; L.pop_front(); *i1; // no-warning } -void bad_pop_front_list1(std::list &L, int n) { +void bad_pop_front1_list(std::list &L, int n) { auto i1 = L.cbegin(), i0 = i1++; - L.pop_front(); + L.pop_front(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_pop_front_deque1(std::deque &D, int n) { +void good_pop_front1_deque(std::deque &D, int n) { auto i1 = D.cbegin(), i0 = i1++; D.pop_front(); *i1; // no-warning } -void bad_pop_front_deque1(std::deque &D, int n) { +void bad_pop_front1_deque(std::deque &D, int n) { auto i1 = D.cbegin(), i0 = i1++; - D.pop_front(); + D.pop_front(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_pop_front_forward_list1(std::forward_list &FL, int n) { +void good_pop_front1_forward_list(std::forward_list &FL, int n) { auto i1 = FL.cbegin(), i0 = i1++; FL.pop_front(); *i1; // no-warning } -void bad_pop_front_forward_list1(std::forward_list &FL, int n) { +void bad_pop_front1_forward_list(std::forward_list &FL, int n) { auto i1 = FL.cbegin(), i0 = i1++; - FL.pop_front(); + FL.pop_front(); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_insert_list1(std::list &L, int n) { +void good_insert1_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) { +void good_insert1_list2(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + i1 = L.insert(i1, n); + *i1; // no-warning +} + +void good_insert1_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) { +void good_insert1_vector2(std::vector &V, int n) { auto i1 = V.cbegin(), i0 = i1++; - V.insert(i1, n); + i1 = V.insert(i1, n); + *i1; // no-warning +} + +void bad_insert1_vector(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, n); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert1_deque(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + i0 = D.insert(i1, n); + *i0; // no-warning } -void bad_insert_deque1(std::deque &D, int n) { +void bad_insert1_deque1(std::deque &D, int n) { auto i1 = D.cbegin(), i0 = i1++; - D.insert(i1, n); + D.insert(i1, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_insert1_deque2(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, n); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert2_list1(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + L.insert(i1, std::move(n)); + *i0; // no-warning + *i1; // no-warning } -void good_emplace_list1(std::list &L, int n) { +void good_insert2_list2(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + i1 = L.insert(i1, std::move(n)); + *i1; // no-warning +} + +void good_insert2_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, std::move(n)); + *i0; // no-warning +} + +void good_insert2_vector2(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + i1 = V.insert(i1, std::move(n)); + *i1; // no-warning +} + +void bad_insert2_vector(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert2_deque(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + i1 = D.insert(i1, std::move(n)); + *i1; // no-warning +} + +void bad_insert2_deque1(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}} + *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_insert2_deque2(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert3_list1(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + L.insert(i1, 10, n); + *i0; // no-warning + *i1; // no-warning +} + +void good_insert3_list2(std::list &L, int n) { + auto i1 = L.cbegin(), i0 = i1++; + i1 = L.insert(i1, 10, n); + *i1; // no-warning +} + +void good_insert3_vector1(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, 10, n); + *i0; // no-warning +} + +void good_insert3_vector2(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + i1 = V.insert(i1, 10, n); + *i1; // no-warning +} + +void bad_insert3_vector(std::vector &V, int n) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, 10, n); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert3_deque(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + i1 = D.insert(i1, 10, std::move(n)); + *i1; // no-warning +} + +void bad_insert3_deque1(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, 10, std::move(n)); // expected-note{{Iterator invalidated}} + *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_insert3_deque2(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, 10, std::move(n)); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert4_list1(std::list &L1, std::list &L2, int n) { + auto i1 = L1.cbegin(), i0 = i1++; + L1.insert(i1, L2.cbegin(), L2.cend()); + *i0; // no-warning + *i1; // no-warning +} + +void good_insert4_list2(std::list &L1, std::list &L2, int n) { + auto i1 = L1.cbegin(), i0 = i1++; + i1 = L1.insert(i1, L2.cbegin(), L2.cend()); + *i1; // no-warning +} + +void good_insert4_vector1(std::vector &V1, std::vector &V2, int n) { + auto i1 = V1.cbegin(), i0 = i1++; + V1.insert(i1, V2.cbegin(), V2.cend()); + *i0; // no-warning +} + +void good_insert4_vector2(std::vector &V1, std::vector &V2, int n) { + auto i1 = V1.cbegin(), i0 = i1++; + i1 = V1.insert(i1, V2.cbegin(), V2.cend()); + *i1; // no-warning +} + +void bad_insert4_vector(std::vector &V1, std::vector &V2, int n) { + auto i1 = V1.cbegin(), i0 = i1++; + V1.insert(i1, V2.cbegin(), V2.cend()); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert4_deque(std::deque &D1, std::deque &D2, int n) { + auto i1 = D1.cbegin(), i0 = i1++; + i1 = D1.insert(i1, D2.cbegin(), D2.cend()); + *i1; // no-warning +} + +void bad_insert4_deque1(std::deque &D1, std::deque &D2, int n) { + auto i1 = D1.cbegin(), i0 = i1++; + D1.insert(i1, D2.cbegin(), D2.cend()); // expected-note{{Iterator invalidated}} + *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_insert4_deque2(std::deque &D1, std::deque &D2, int n) { + auto i1 = D1.cbegin(), i0 = i1++; + D1.insert(i1, D2.cbegin(), D2.cend()); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert5_list1(std::list &L) { + auto i1 = L.cbegin(), i0 = i1++; + L.insert(i1, {1, 2, 3, 4}); + *i0; // no-warning + *i1; // no-warning +} + +void good_insert5_list2(std::list &L) { + auto i1 = L.cbegin(), i0 = i1++; + i1 = L.insert(i1, {1, 2, 3, 4}); + *i1; // no-warning +} + +void good_insert5_vector1(std::vector &V) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, {1, 2, 3, 4}); + *i0; // no-warning +} + +void good_insert5_vector2(std::vector &V) { + auto i1 = V.cbegin(), i0 = i1++; + i1 = V.insert(i1, {1, 2, 3, 4}); + *i1; // no-warning +} + +void bad_insert5_vector(std::vector &V) { + auto i1 = V.cbegin(), i0 = i1++; + V.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_insert5_deque(std::deque &D) { + auto i1 = D.cbegin(), i0 = i1++; + i1 = D.insert(i1, {1, 2, 3, 4}); + *i1; // no-warning +} + +void bad_insert5_deque1(std::deque &D) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}} + *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_insert5_deque2(std::deque &D) { + auto i1 = D.cbegin(), i0 = i1++; + D.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}} + *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_emplace1_list(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) { +void good_emplace1_vector(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) { +void bad_emplace1_vector(std::vector &V, int n) { auto i1 = V.cbegin(), i0 = i1++; - V.emplace(i1, n); + V.emplace(i1, n); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_emplace_deque1(std::deque &D, int n) { +void bad_emplace1_deque1(std::deque &D, int n) { auto i1 = D.cbegin(), i0 = i1++; - D.emplace(i1, n); + D.emplace(i1, n); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_emplace1_deque2(std::deque &D, int n) { + auto i1 = D.cbegin(), i0 = i1++; + D.emplace(i1, n); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_list1(std::list &L) { +void good_erase1_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) { +void good_erase1_list2(std::list &L) { + auto i0 = L.cbegin(); + i0 = L.erase(i0); + *i0; // no-warning +} + +void bad_erase1_list(std::list &L) { auto i0 = L.cbegin(); - L.erase(i0); + L.erase(i0); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_vector1(std::vector &V) { +void good_erase1_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) { +void good_erase1_vector2(std::vector &V) { + auto i0 = V.cbegin(); + i0 = V.erase(i0); + *i0; // no-warning +} + +void bad_erase1_vector1(std::vector &V) { auto i1 = V.cbegin(), i0 = i1++; - V.erase(i0); + V.erase(i0); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase1_vector2(std::vector &V) { + auto i1 = V.cbegin(), i0 = i1++; + V.erase(i0); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void bad_erase_deque1(std::deque &D) { +void good_erase1_deque(std::deque &D) { + auto i0 = D.cbegin(); + i0 = D.erase(i0); + *i0; // no-warning +} + +void bad_erase1_deque1(std::deque &D) { auto i2 = D.cbegin(), i0 = i2++, i1 = i2++; - D.erase(i1); + D.erase(i1); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase1_deque2(std::deque &D) { + auto i2 = D.cbegin(), i0 = i2++, i1 = i2++; + D.erase(i1); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase1_deque3(std::deque &D) { + auto i2 = D.cbegin(), i0 = i2++, i1 = i2++; + D.erase(i1); // expected-note{{Iterator invalidated}} *i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_list2(std::list &L) { +void good_erase2_list1(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) { +void good_erase2_list2(std::list &L) { + auto i2 = L.cbegin(), i0 = i2++, i1 = i2++; + i0 = L.erase(i0, i2); + *i0; // no-warning +} + +void bad_erase2_list1(std::list &L) { auto i2 = L.cbegin(), i0 = i2++, i1 = i2++; - L.erase(i0, i2); + L.erase(i0, i2); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_list2(std::list &L) { + auto i2 = L.cbegin(), i0 = i2++, i1 = i2++; + L.erase(i0, i2); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_vector2(std::vector &V) { +void good_erase2_vector1(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) { +void good_erase2_vector2(std::vector &V) { auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; - V.erase(i0, i2); + i0 = V.erase(i0, i2); + *i0; // no-warning +} + +void bad_erase2_vector1(std::vector &V) { + auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; + V.erase(i0, i2); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_vector2(std::vector &V) { + auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; + V.erase(i0, i2); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_vector3(std::vector &V) { + auto i2 = V.cbegin(), i0 = i2++, i1 = i2++; + V.erase(i0, i2); // expected-note{{Iterator invalidated}} *i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void good_erase2_deque(std::deque &D) { + auto i2 = D.cbegin(), i0 = i2++, i1 = i2++; + i0 = D.erase(i0, i2); + *i0; // no-warning } -void bad_erase_deque2(std::deque &D) { +void bad_erase2_deque1(std::deque &D) { auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; - D.erase(i1, i3); + D.erase(i1, i3); // expected-note{{Iterator invalidated}} *i0; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_deque2(std::deque &D) { + auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + D.erase(i1, i3); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_deque3(std::deque &D) { + auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + D.erase(i1, i3); // expected-note{{Iterator invalidated}} *i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase2_deque4(std::deque &D) { + auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + D.erase(i1, i3); // expected-note{{Iterator invalidated}} *i3; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_after_forward_list1(std::forward_list &FL) { +void good_erase_after1_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) { +void good_erase_after1_forward_lis2(std::forward_list &FL) { auto i1 = FL.cbegin(), i0 = i1++; - FL.erase_after(i0); + i1 = FL.erase_after(i0); + *i1; // no-warning +} + +void bad_erase_after1_forward_list(std::forward_list &FL) { + auto i1 = FL.cbegin(), i0 = i1++; + FL.erase_after(i0); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } -void good_erase_after_forward_list2(std::forward_list &FL) { +void good_erase_after2_forward_list1(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) { +void good_erase_after2_forward_list2(std::forward_list &FL) { auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; - FL.erase_after(i0, i3); + i2 = FL.erase_after(i0, i3); + *i2; // no-warning +} + +void bad_erase_after2_forward_list1(std::forward_list &FL) { + auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + FL.erase_after(i0, i3); // expected-note{{Iterator invalidated}} *i1; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} +} + +void bad_erase_after2_forward_list2(std::forward_list &FL) { + auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++; + FL.erase_after(i0, i3); // expected-note{{Iterator invalidated}} *i2; // expected-warning{{Invalidated iterator accessed}} + // expected-note@-1{{Invalidated iterator accessed}} } Index: test/Analysis/iterator-range.cpp =================================================================== --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -analyzer-output=text -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -analyzer-output=text -verify #include "Inputs/system-header-simulator-cxx.h" @@ -22,37 +22,34 @@ } void simple_bad_end(const std::vector &v) { - auto i = v.end(); + auto i = v.end(); // expected-note{{Iterator set to the past-the-end position of the container}} *i; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } void copy(const std::vector &v) { - auto i1 = v.end(); + auto i1 = v.end(); // expected-note{{Iterator set to the past-the-end position of the container}} auto i2 = i1; *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } -void decrease(const std::vector &v) { - auto i = v.end(); - --i; - *i; // no-warning -} - -void copy_and_decrease1(const std::vector &v) { +void copy_and_decrement1(const std::vector &v) { auto i1 = v.end(); auto i2 = i1; --i1; *i1; // no-warning } -void copy_and_decrease2(const std::vector &v) { - auto i1 = v.end(); +void copy_and_decrement2(const std::vector &v) { + auto i1 = v.end(); // expected-note{{Iterator set to the past-the-end position of the container}} auto i2 = i1; --i1; *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } -void copy_and_increase1(const std::vector &v) { +void copy_and_increment1(const std::vector &v) { auto i1 = v.begin(); auto i2 = i1; ++i1; @@ -60,20 +57,24 @@ *i2; // no-warning } -void copy_and_increase2(const std::vector &v) { - auto i1 = v.begin(); +void copy_and_increment2(const std::vector &v) { + auto i1 = v.begin(); // expected-note{{Iterator set to the first position of the container}} auto i2 = i1; ++i1; - if (i2 == v.end()) + if (i2 == v.end()) // expected-note{{Assuming the container/range is empty}} + // expected-note@-1{{Taking true branch}} *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } -void copy_and_increase3(const std::vector &v) { - auto i1 = v.begin(); +void copy_and_increment3(const std::vector &v) { + auto i1 = v.begin(); // expected-note{{Iterator set to the first position of the container}} auto i2 = i1; ++i1; - if (v.end() == i2) + if (v.end() == i2) // expected-note{{Assuming the container/range is empty}} + // expected-note@-1{{Taking true branch}} *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } template @@ -94,8 +95,13 @@ } void bad_non_std_find(std::vector &V, int e) { - auto first = nonStdFind(V.begin(), V.end(), e); + auto first = nonStdFind(V.begin(), V.end(), e); // expected-note{{Calling 'nonStdFind<__vector_iterator, int>'}} + // expected-note@-1{{Iterator set to the past-the-end position of the container}} + // expected-note@-17{{Assuming the container/range is empty}} + // expected-note@-18{{Loop condition is false. Execution continues on line}} + // expected-note@-4{{Returning from 'nonStdFind<__vector_iterator, int>'}} *first; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } void tricky(std::vector &V, int e) { @@ -123,10 +129,13 @@ } void bad_push_back(std::list &L, int n) { - auto i0 = --L.cend(); + auto i0 = --L.cend(); // expected-note{{Iterator set to the past-the-end position of the container}} + // expected-note@-1{{Iterator decremented}} L.push_back(n); - ++i0; + ++i0; // expected-note{{Iterator incremented}} *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator incremented and reached the past-the-end position of the container}} + // expected-note@-2{{Past-the-end iterator dereferenced}} } void good_pop_back(std::list &L, int n) { @@ -136,9 +145,14 @@ } void bad_pop_back(std::list &L, int n) { - auto i0 = --L.cend(); --i0; + auto i0 = --L.cend(); // expected-note{{Iterator set to the past-the-end position of the container}} + // expected-note@-1{{Iterator decremented}} + --i0; // expected-note{{Iterator decremented}} + L.pop_back(); *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator incremented and reached the past-the-end position of the container}} + // expected-note@-2{{Past-the-end iterator dereferenced}} } void good_push_front(std::list &L, int n) { @@ -148,10 +162,11 @@ } void bad_push_front(std::list &L, int n) { - auto i0 = L.cbegin(); + auto i0 = L.cbegin(); // expected-note{{Iterator set to the first position of the container}} L.push_front(n); - --i0; + --i0; // expected-note{{Iterator decremented and reached the first position of the container}} --i0; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void good_pop_front(std::list &L, int n) { @@ -161,23 +176,31 @@ } void bad_pop_front(std::list &L, int n) { - auto i0 = ++L.cbegin(); - L.pop_front(); + auto i0 = ++L.cbegin(); // expected-note{{Iterator set to the first position of the container}} + // expected-note@-1{{Iterator incremented}} + L.pop_front(); // expected-note{{Iterator reached the first position of the container}} --i0; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void bad_move(std::list &L1, std::list &L2) { - auto i0 = --L2.cend(); + auto i0 = --L2.cend(); // expected-note{{Iterator set to the past-the-end position of the container}} + // expected-note@-1{{Iterator decremented}} L1 = std::move(L2); *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator incremented and reached the past-the-end position of the container}} + // expected-note@-2{{Past-the-end iterator dereferenced}} } void bad_move_push_back(std::list &L1, std::list &L2, int n) { - auto i0 = --L2.cend(); + auto i0 = --L2.cend(); // expected-note{{Iterator set to the past-the-end position of the container}} + // expected-note@-1{{Iterator decremented}} L2.push_back(n); L1 = std::move(L2); - ++i0; + ++i0; // expected-note{{Iterator incremented}} *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator incremented and reached the past-the-end position of the container}} + // expected-note@-2{{Past-the-end iterator dereferenced}} } void good_incr_begin(const std::list &L) { @@ -185,9 +208,18 @@ ++i0; // no-warning } -void bad_decr_begin(const std::list &L) { - auto i0 = L.begin(); +void bad_decr_begin1(const std::list &L) { + auto i0 = L.begin(); // expected-note{{Iterator set to the first position of the container}} --i0; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} +} + +void bad_decr_begin2(const std::list &L) { + auto i0 = L.begin(); // expected-note{{Iterator set to the first position of the container}} + ++i0; // expected-note{{Iterator incremented}} + --i0; // expected-note{{Iterator decremented and reached the first position of the container}} + --i0; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void good_decr_end(const std::list &L) { @@ -195,9 +227,24 @@ --i0; // no-warning } -void bad_incr_end(const std::list &L) { +void good_decr_end2(const std::list &L) { auto i0 = L.end(); + --i0; // no-warning + *i0; // no-warning +} + +void bad_incr_end1(const std::list &L) { + auto i0 = L.end(); // expected-note{{Iterator set to the past-the-end position of the container}} + ++i0; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} +} + +void bad_incr_end2(const std::list &L) { + auto i0 = L.end(); // expected-note{{Iterator set to the past-the-end position of the container}} + --i0; // expected-note{{Iterator decremented}} + ++i0; // expected-note{{Iterator incremented and reached the past-the-end position of the container}} ++i0; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } struct simple_iterator_base {