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,44 @@ }; } // 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; + + // `FoundChange` becomes true when we find the statement the results in the + // current state of the iterator. + // `FoundEmptyness` becomes true when we find the block edge assuming + // emptiness or non-emptiness of the container. + bool FoundChange = false, FoundEmptiness; + ErrorTypeT ErrorType; + +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; + } + + 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; +}; + REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition) REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, IteratorPosition) @@ -926,7 +967,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 +980,7 @@ if (!N) { return; } - reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N); + reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N, Pos); } } @@ -1047,14 +1088,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); } } @@ -1586,9 +1627,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 +1661,140 @@ 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 (FoundChange && FoundEmptiness) + return nullptr; + + const ExplodedNode *Pred = Succ->getFirstPred(); + const Stmt *S = nullptr; + const auto SP = Succ->getLocation().getAs(); + if (SP.hasValue()) { + S = SP->getStmt(); + } else { + const auto E = Succ->getLocation().getAs(); + if (E.hasValue()) { + S = E->getSrc()->getTerminator().getStmt(); + } + } + + 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); + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + if (!FoundChange) { + const auto *PosBefore = getIteratorPosition(StateBefore, Iter); + const auto *PosAfter = getIteratorPosition(StateAfter, Iter); + + // 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."; + FoundChange = 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."; + FoundChange = 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."; + FoundChange = true; + } + } + + 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); +} + 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,14 +22,16 @@ } void simple_bad_end(const std::vector &v) { - auto i = v.end(); + auto i = v.end(); // expected-note{{Iterator reached 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 i2 = i1; + auto i2 = i1; // expected-note{{Iterator reached the past-the-end position of the container}} *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } void decrease(const std::vector &v) { @@ -47,9 +49,10 @@ void copy_and_decrease2(const std::vector &v) { auto i1 = v.end(); - auto i2 = i1; + auto i2 = i1; // expected-note{{Iterator reached the past-the-end position of the container}} --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) { @@ -64,16 +67,22 @@ auto i1 = v.begin(); auto i2 = i1; ++i1; - if (i2 == v.end()) + if (i2 == v.end()) // expected-note 0-1{{Assuming the condition is true}} + // expected-note@-1{{Assuming the container/range is empty}} + // expected-note@-2{{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(); auto i2 = i1; ++i1; - if (v.end() == i2) + if (v.end() == i2) // expected-note 0-1{{Assuming the condition is true}} + // expected-note@-1{{Assuming the container/range is empty}} + // expected-note@-2{{Taking true branch}} *i2; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } template @@ -94,8 +103,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@-16{{Assuming the container/range is empty}} + // expected-note@-17{{Loop condition is false. Execution continues on line}} + // expected-note@-13{{Iterator reached the past-the-end position of the container}} + // 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) { @@ -127,6 +141,8 @@ L.push_back(n); ++i0; *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator 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) { @@ -139,6 +155,8 @@ auto i0 = --L.cend(); --i0; L.pop_back(); *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator 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) { @@ -150,8 +168,9 @@ void bad_push_front(std::list &L, int n) { auto i0 = L.cbegin(); L.push_front(n); - --i0; + --i0; // 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 good_pop_front(std::list &L, int n) { @@ -162,14 +181,17 @@ void bad_pop_front(std::list &L, int n) { auto i0 = ++L.cbegin(); - L.pop_front(); + 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(); L1 = std::move(L2); *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator 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) { @@ -178,6 +200,8 @@ L1 = std::move(L2); ++i0; *++i0; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Iterator 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) { @@ -186,8 +210,9 @@ } void bad_decr_begin(const std::list &L) { - auto i0 = L.begin(); + auto i0 = L.begin(); // 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 good_decr_end(const std::list &L) { @@ -196,8 +221,9 @@ } void bad_incr_end(const std::list &L) { - auto i0 = L.end(); + auto i0 = L.end(); // expected-note{{Iterator 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 {