Index: lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp @@ -69,19 +69,22 @@ class IteratorPastEndChecker : public Checker< check::PreCall, check::PostCall, check::PreStmt, - check::PostStmt, check::PostStmt, - check::PostStmt, check::BeginFunction, - check::DeadSymbols, eval::Assume, eval::Call> { - mutable IdentifierInfo *II_find = nullptr, - *II_find_end = nullptr, *II_find_first_of = nullptr, - *II_find_if = nullptr, *II_find_if_not = nullptr, - *II_lower_bound = nullptr, *II_upper_bound = nullptr, - *II_search = nullptr, *II_search_n = nullptr; + check::PostStmt, check::Bind, + check::BeginFunction, check::DeadSymbols, eval::Assume, eval::Call> { + mutable IdentifierInfo *II_find = nullptr, *II_find_end = nullptr, + *II_find_first_of = nullptr, *II_find_if = nullptr, + *II_find_if_not = nullptr, *II_lower_bound = nullptr, + *II_upper_bound = nullptr, *II_search = nullptr, + *II_search_n = nullptr; std::unique_ptr PastEndBugType; - void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, - const SVal &RVal, OverloadedOperatorKind Op) const; + void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LHS, + const SVal &RHS, OverloadedOperatorKind Op) const; + void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, + const SVal &RetVal, const SVal &LHS, + const SVal &RHS, QualType RHSType, + bool PreCheck) const; void handleAccess(CheckerContext &C, const SVal &Val) const; void handleDecrement(CheckerContext &C, const SVal &Val) const; void handleEnd(CheckerContext &C, const SVal &RetVal) const; @@ -108,8 +111,7 @@ void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const; void checkBeginFunction(CheckerContext &C) const; - void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; - void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -137,15 +139,16 @@ bool isEndCall(const FunctionDecl *Func); bool isSimpleComparisonOperator(OverloadedOperatorKind OK); bool isAccessOperator(OverloadedOperatorKind OK); +bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); bool isDecrementOperator(OverloadedOperatorKind OK); BinaryOperator::Opcode getOpcode(const SymExpr *SE); const RegionOrSymbol getRegionOrSymbol(const SVal &Val); const ProgramStateRef processComparison(ProgramStateRef State, - RegionOrSymbol LVal, - RegionOrSymbol RVal, bool Equal); + RegionOrSymbol LHS, RegionOrSymbol RHS, + bool Equal); const ProgramStateRef saveComparison(ProgramStateRef State, - const SymExpr *Condition, const SVal &LVal, - const SVal &RVal, bool Eq); + const SymExpr *Condition, const SVal &LHS, + const SVal &RHS, bool Eq); const IteratorComparison *loadComparison(ProgramStateRef State, const SymExpr *Condition); const IteratorPosition *getIteratorPosition(ProgramStateRef State, @@ -157,6 +160,7 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, IteratorPosition Pos); +ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); ProgramStateRef adjustIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, IteratorPosition Pos, bool Equal); @@ -173,11 +177,27 @@ void IteratorPastEndChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Check for access past end - const auto *Func = Call.getDecl()->getAsFunction(); + const auto *Func = dyn_cast_or_null(Call.getDecl()); if (!Func) return; if (Func->isOverloadedOperator()) { - if (isAccessOperator(Func->getOverloadedOperator())) { + if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast(&Call)) { + if (Call.getNumArgs() >= 1) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), + InstCall->getCXXThisVal(), Call.getArgSVal(0), + Call.getArgExpr(0)->getType(), true); + } + } else { + if (Call.getNumArgs() >= 2) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), Call.getArgSVal(0), + Call.getArgSVal(1), + Call.getArgExpr(1)->getType(), true); + } + } + } else if (isAccessOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast(&Call)) { handleAccess(C, InstCall->getCXXThisVal()); } else { @@ -190,24 +210,38 @@ void IteratorPastEndChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // Record end() iterators, iterator decrementation and comparison - const auto *Func = Call.getDecl()->getAsFunction(); + const auto *Func = dyn_cast_or_null(Call.getDecl()); if (!Func) return; if (Func->isOverloadedOperator()) { const auto Op = Func->getOverloadedOperator(); if (isSimpleComparisonOperator(Op)) { - if (Func->isCXXInstanceMember()) { - const auto &InstCall = static_cast(Call); - handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(), - InstCall.getArgSVal(0), Op); + if (const auto *InstCall = dyn_cast(&Call)) { + handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(), + Call.getArgSVal(0), Op); } else { handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0), Call.getArgSVal(1), Op); } + } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast(&Call)) { + if (Call.getNumArgs() >= 1) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), + InstCall->getCXXThisVal(), Call.getArgSVal(0), + Call.getArgExpr(0)->getType(), false); + } + } else { + if (Call.getNumArgs() >= 2) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), Call.getArgSVal(0), + Call.getArgSVal(1), + Call.getArgExpr(1)->getType(), false); + } + } } else if (isDecrementOperator(Func->getOverloadedOperator())) { - if (Func->isCXXInstanceMember()) { - const auto &InstCall = static_cast(Call); - handleDecrement(C, InstCall.getCXXThisVal()); + if (const auto *InstCall = dyn_cast(&Call)) { + handleDecrement(C, InstCall->getCXXThisVal()); } else { handleDecrement(C, Call.getArgSVal(0)); } @@ -242,6 +276,22 @@ } } +void IteratorPastEndChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, + CheckerContext &C) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Val); + if (Pos) { + State = setIteratorPosition(State, Loc, *Pos); + C.addTransition(State); + } else { + const auto *OldPos = getIteratorPosition(State, Loc); + if (OldPos) { + State = removeIteratorPosition(State, Loc); + C.addTransition(State); + } + } +} + void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const { // Copy state of iterator arguments to iterator parameters auto State = C.getState(); @@ -275,46 +325,6 @@ } } -void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE, - CheckerContext &C) const { - // Transfer iterator state in case of copy or move by constructor - const auto *ctr = CCE->getConstructor(); - if (!ctr->isCopyOrMoveConstructor()) - return; - const auto *RHSExpr = CCE->getArg(0); - - auto State = C.getState(); - const auto *LCtx = C.getLocationContext(); - - const auto RetVal = State->getSVal(CCE, LCtx); - - const auto RHSVal = State->getSVal(RHSExpr, LCtx); - const auto *RHSPos = getIteratorPosition(State, RHSVal); - if (!RHSPos) - return; - State = setIteratorPosition(State, RetVal, *RHSPos); - C.addTransition(State); -} - -void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS, - CheckerContext &C) const { - // Transfer iterator state to new variable declaration - for (const auto *D : DS->decls()) { - const auto *VD = dyn_cast(D); - if (!VD || !VD->hasInit()) - continue; - - auto State = C.getState(); - const auto *LCtx = C.getPredecessor()->getLocationContext(); - const auto *Pos = - getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx)); - if (!Pos) - continue; - State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos); - C.addTransition(State); - } -} - void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const { /* Transfer iterator state for to temporary objects */ @@ -436,8 +446,7 @@ void IteratorPastEndChecker::handleComparison(CheckerContext &C, const SVal &RetVal, - const SVal &LVal, - const SVal &RVal, + const SVal &LHS, const SVal &RHS, OverloadedOperatorKind Op) const { // Record the operands and the operator of the comparison for the next // evalAssume, if the result is a symbolic expression. If it is a concrete @@ -445,15 +454,15 @@ // the operands according to the operator and the result auto State = C.getState(); if (const auto *Condition = RetVal.getAsSymbolicExpression()) { - const auto *LPos = getIteratorPosition(State, LVal); - const auto *RPos = getIteratorPosition(State, RVal); + const auto *LPos = getIteratorPosition(State, LHS); + const auto *RPos = getIteratorPosition(State, RHS); if (!LPos && !RPos) return; - State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual); + State = saveComparison(State, Condition, LHS, RHS, Op == OO_EqualEqual); C.addTransition(State); } else if (const auto TruthVal = RetVal.getAs()) { if ((State = processComparison( - State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal), + State, getRegionOrSymbol(LHS), getRegionOrSymbol(RHS), (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) { C.addTransition(State); } else { @@ -462,15 +471,86 @@ } } +void IteratorPastEndChecker::handleRandomIncrOrDecr( + CheckerContext &C, OverloadedOperatorKind Op, const SVal &RetVal, + const SVal &LHS, const SVal &RHS, QualType RHSType, bool PreCheck) const { + if (!RHSType->isIntegerType()) + return; + + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, LHS); + if (!Pos || Pos->isInRange()) + return; + + auto &SVB = C.getSValBuilder(); + auto &CM = C.getConstraintManager(); + auto zeroVal = SVB.makeIntVal(0, RHSType); + + // Hack - begin + auto value = RHS; + if (auto loc = value.getAs()) { + value = State->getRawSVal(*loc); + } + // Hack - end + + auto greaterThanZero = + SVB.evalBinOp(State, BO_GT, value, zeroVal, SVB.getConditionType()) + .getAs(); + + auto lessThanZero = + SVB.evalBinOp(State, BO_LT, value, zeroVal, SVB.getConditionType()) + .getAs(); + + if (!greaterThanZero || !lessThanZero) { + // Cannot properly reason so assume the best to prevent false positives + if (!PreCheck) { + auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; + State = removeIteratorPosition(State, TgtVal); + C.addTransition(State); + } + return; + } + + ProgramStateRef StatePositive, StateNonPositive; + std::tie(StatePositive, StateNonPositive) = + CM.assumeDual(State, *greaterThanZero); + + ProgramStateRef StateNegative, StateZero; + std::tie(StateNegative, StateZero) = + CM.assumeDual(StateNonPositive, *lessThanZero); + + // When increasing by positive or decreasing by negative an iterator past its + // end, then it is a bug. We check for bugs before the operator call. + if (PreCheck && + ((StatePositive && (Op == OO_Plus || Op == OO_PlusEqual)) || + (StateNegative && (Op == OO_Minus || Op == OO_MinusEqual)))) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportPastEndBug("Iterator accessed past its end.", LHS, C, N); + } + + // When increasing by negative or decreasing by positive an iterator past its + // end, then we assume that the iterator is back to its range. + if (!PreCheck && + ((StatePositive && (Op == OO_Minus || Op == OO_MinusEqual)) || + (StateNegative && (Op == OO_Plus || Op == OO_PlusEqual)))) { + State = + (Op == OO_Minus || Op == OO_MinusEqual) ? StatePositive : StateNegative; + auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; + State = setIteratorPosition(State, TgtVal, IteratorPosition::getInRange()); + C.addTransition(State); + } +} + void IteratorPastEndChecker::handleAccess(CheckerContext &C, const SVal &Val) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Val); if (Pos && Pos->isOutofRange()) { auto *N = C.generateNonFatalErrorNode(State); - if (!N) { + if (!N) return; - } reportPastEndBug("Iterator accessed past its end.", Val, C, N); } } @@ -701,12 +781,16 @@ bool isAccessOperator(OverloadedOperatorKind OK) { return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar || - OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus || - OK == OO_Subscript; + OK == OO_PlusPlus || OK == OO_Subscript; +} + +bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { + return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus || + OK == OO_MinusEqual; } bool isDecrementOperator(OverloadedOperatorKind OK) { - return OK == OO_MinusEqual || OK == OO_MinusMinus; + return OK == OO_MinusMinus; } BinaryOperator::Opcode getOpcode(const SymExpr *SE) { @@ -738,14 +822,14 @@ } const ProgramStateRef processComparison(ProgramStateRef State, - RegionOrSymbol LVal, - RegionOrSymbol RVal, bool Equal) { - const auto *LPos = getIteratorPosition(State, LVal); - const auto *RPos = getIteratorPosition(State, RVal); + RegionOrSymbol LHS, RegionOrSymbol RHS, + bool Equal) { + const auto *LPos = getIteratorPosition(State, LHS); + const auto *RPos = getIteratorPosition(State, RHS); if (LPos && !RPos) { - State = adjustIteratorPosition(State, RVal, *LPos, Equal); + State = adjustIteratorPosition(State, RHS, *LPos, Equal); } else if (!LPos && RPos) { - State = adjustIteratorPosition(State, LVal, *RPos, Equal); + State = adjustIteratorPosition(State, LHS, *RPos, Equal); } else if (LPos && RPos) { if (contradictingIteratorPositions(*LPos, *RPos, Equal)) { return nullptr; @@ -755,10 +839,10 @@ } const ProgramStateRef saveComparison(ProgramStateRef State, - const SymExpr *Condition, const SVal &LVal, - const SVal &RVal, bool Eq) { - const auto Left = getRegionOrSymbol(LVal); - const auto Right = getRegionOrSymbol(RVal); + const SymExpr *Condition, const SVal &LHS, + const SVal &RHS, bool Eq) { + const auto Left = getRegionOrSymbol(LHS); + const auto Right = getRegionOrSymbol(RHS); if (!Left || !Right) return State; return State->set(Condition, @@ -816,6 +900,17 @@ return nullptr; } +ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { + if (const auto Reg = Val.getAsRegion()) { + return State->remove(Reg); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->remove(Sym); + } else if (const auto LCVal = Val.getAs()) { + return State->remove(LCVal->getRegion()); + } + return nullptr; +} + ProgramStateRef adjustIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, IteratorPosition Pos, bool Equal) { 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 @@ -8,18 +8,60 @@ typedef unsigned char uint8_t; typedef __typeof__(sizeof(int)) size_t; +typedef __typeof__((char*)0-(char*)0) ptrdiff_t; void *memmove(void *s1, const void *s2, size_t n); -template struct __iterator { - typedef __iterator iterator; - typedef __iterator const_iterator; +namespace std { + struct input_iterator_tag { }; + struct output_iterator_tag { }; + struct forward_iterator_tag : public input_iterator_tag { }; + struct bidirectional_iterator_tag : public forward_iterator_tag { }; + struct random_access_iterator_tag : public bidirectional_iterator_tag { }; - __iterator(const Ptr p) : ptr(p) {} + template struct iterator_traits { + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + typedef typename Iterator::iterator_category iterator_category; + }; +} + +template struct __vector_iterator { + typedef __vector_iterator iterator; + typedef __vector_iterator const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::random_access_iterator_tag iterator_category; + + __vector_iterator(const Ptr p) : ptr(p) {} + __vector_iterator operator++() { ++ ptr; return *this; } + __vector_iterator operator++(int) { + auto tmp = *this; + ++ ptr; + return tmp; + } + __vector_iterator operator--() { -- ptr; return *this; } + __vector_iterator operator--(int) { + auto tmp = *this; -- ptr; + return tmp; + } + __vector_iterator operator+(difference_type n) { + return ptr + n; + } + __vector_iterator operator-(difference_type n) { + return ptr - n; + } + __vector_iterator operator+=(difference_type n) { + return ptr += n; + } + __vector_iterator operator-=(difference_type n) { + return ptr -= n; + } - __iterator operator++() { return *this; } - __iterator operator++(int) { return *this; } - __iterator operator--() { return *this; } - __iterator operator--(int) { return *this; } Ref operator*() const { return *ptr; } Ptr operator->() const { return *ptr; } @@ -33,7 +75,45 @@ Ptr ptr; }; +template struct __list_iterator { + typedef __vector_iterator iterator; + typedef __vector_iterator const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::bidirectional_iterator_tag iterator_category; + + __list_iterator(T* it) : item(it) {} + __list_iterator operator++() { item = item->next; return *this; } + __list_iterator operator++(int) { + auto tmp = *this; + item = item->next; + return tmp; + } + __list_iterator operator--() { item = item->prev; return *this; } + __list_iterator operator--(int) { + auto tmp = *this; + item = item->prev; + return tmp; + } + + Ref operator*() const { return item->data; } + Ptr operator->() const { return item->data; } + + bool operator==(const iterator &rhs) const { return item == rhs->item; } + bool operator==(const const_iterator &rhs) const { return item == rhs->item; } + + bool operator!=(const iterator &rhs) const { return item != rhs->item; } + bool operator!=(const const_iterator &rhs) const { return item != rhs->item; } + +private: + T* item; +}; + namespace std { + template struct pair { T1 first; @@ -50,13 +130,13 @@ template class vector { - typedef __iterator iterator; - typedef __iterator const_iterator; - T *_start; T *_finish; T *_end_of_storage; public: + typedef __vector_iterator iterator; + typedef __vector_iterator const_iterator; + vector() : _start(0), _finish(0), _end_of_storage(0) {} ~vector(); @@ -79,6 +159,38 @@ const_iterator begin() const { return const_iterator(_start); } iterator end() { return iterator(_finish); } const_iterator end() const { return const_iterator(_finish); } + + T& front() { return *begin(); } + const T& front() const { return *begin(); } + T& back() { return *(end() - 1); } + const T& back() const { return *(end() - 1); } + }; + + template + class list { + struct __item { + T data; + __item *prev, *next; + } *_start, *_finish; + public: + typedef __list_iterator<__item, T *, T &> iterator; + typedef __list_iterator<__item, const T *, const T &> const_iterator; + + list() : _start(0), _finish(0) {} + ~list(); + + void push_back(); + T pop_back(); + + iterator begin() { return iterator(_start); } + const_iterator begin() const { return const_iterator(_start); } + iterator end() { return iterator(_finish); } + const_iterator end() const { return const_iterator(_finish); } + + T& front() { return *begin(); } + const T& front() const { return *begin(); } + T& back() { return *--end(); } + const T& back() const { return *--end(); } }; class exception { @@ -247,6 +359,34 @@ OutputIter copy_backward(InputIter II, InputIter IE, OutputIter OI) { return __copy_backward(II, IE, OI); } +} + +template +void __advance (BidirectionalIterator& it, Distance n, + std::bidirectional_iterator_tag) { + if (n >= 0) while(n-- > 0) ++it; else while (n++<0) --it; +} + +template +void __advance (RandomAccessIterator& it, Distance n, + std::random_access_iterator_tag) { + it += n; +} + +namespace std { + template + void advance (InputIterator& it, Distance n) { + __advance(it, n, typename InputIterator::iterator_category()); + } + + template + BidirectionalIterator + prev (BidirectionalIterator it, + typename iterator_traits::difference_type n = + 1) { + advance(it, -n); + return it; + } template InputIterator find(InputIterator first, InputIterator last, const T &val); @@ -277,12 +417,6 @@ ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2); - struct input_iterator_tag { }; - struct output_iterator_tag { }; - struct forward_iterator_tag : public input_iterator_tag { }; - struct bidirectional_iterator_tag : public forward_iterator_tag { }; - struct random_access_iterator_tag : public bidirectional_iterator_tag { }; - } void* operator new(std::size_t, const std::nothrow_t&) throw(); Index: test/Analysis/diagnostics/explicit-suppression.cpp =================================================================== --- test/Analysis/diagnostics/explicit-suppression.cpp +++ test/Analysis/diagnostics/explicit-suppression.cpp @@ -18,6 +18,6 @@ void testCopyNull(C *I, C *E) { std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:303 {{Called C++ object pointer is null}} #endif } Index: test/Analysis/iterator-past-end.cpp =================================================================== --- test/Analysis/iterator-past-end.cpp +++ test/Analysis/iterator-past-end.cpp @@ -203,3 +203,50 @@ start = ++item; // no-warning } } + +void good_overwrite(std::vector &vec) { + auto i = vec.end(); + i = vec.begin(); + *i; // no-warning +} + +void good_overwrite_find(std::vector &vec, int e) { + auto i = std::find(vec.begin(), vec.end(), e); + if(i == vec.end()) { + i = vec.begin(); + } + *i; // no-warning +} + +void bad_overwrite(std::vector &vec) { + auto i = vec.begin(); + i = vec.end(); + *i; // expected-warning{{Iterator accessed past its end}} +} + +void bad_overwrite_find(std::vector &vec, int e) { + auto i = std::find(vec.begin(), vec.end(), e); + if(i != vec.end()) { + i = vec.begin(); + } + *i; // expected-warning{{Iterator accessed past its end}} +} + +void good_advance(std::vector &vec) { + auto i = vec.end(); + std::advance(i, -1); + *i; // no-warning +} + +void good_prev(std::vector &vec) { + auto i = std::prev(vec.end()); + *i; // no-warning +} + +void front(const std::vector &vec) { + vec.front(); +} + +void back(const std::vector &vec) { + vec.back(); +}