diff --git a/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp @@ -26,7 +26,10 @@ namespace { class InvalidatedIteratorChecker - : public Checker { + : public Checker, + check::PreStmt, + check::PreStmt, + check::PreStmt> { std::unique_ptr InvalidatedBugType; @@ -37,6 +40,10 @@ InvalidatedIteratorChecker(); void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; + void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const; + void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const; }; @@ -65,6 +72,48 @@ } } +void InvalidatedIteratorChecker::checkPreStmt(const UnaryOperator *UO, + CheckerContext &C) const { + if (isa(UO->getSubExpr())) + return; + + ProgramStateRef State = C.getState(); + UnaryOperatorKind OK = UO->getOpcode(); + SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext()); + + if (isAccessOperator(OK)) { + verifyAccess(C, SubVal); + } +} + +void InvalidatedIteratorChecker::checkPreStmt(const BinaryOperator *BO, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + BinaryOperatorKind OK = BO->getOpcode(); + SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); + + if (isAccessOperator(OK)) { + verifyAccess(C, LVal); + } +} + +void InvalidatedIteratorChecker::checkPreStmt(const ArraySubscriptExpr *ASE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext()); + verifyAccess(C, LVal); +} + +void InvalidatedIteratorChecker::checkPreStmt(const MemberExpr *ME, + CheckerContext &C) const { + if (!ME->isArrow() || ME->isImplicitAccess()) + return; + + ProgramStateRef State = C.getState(); + SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext()); + verifyAccess(C, BaseVal); +} + void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Val); diff --git a/clang/lib/StaticAnalyzer/Checkers/Iterator.h b/clang/lib/StaticAnalyzer/Checkers/Iterator.h --- a/clang/lib/StaticAnalyzer/Checkers/Iterator.h +++ b/clang/lib/StaticAnalyzer/Checkers/Iterator.h @@ -115,9 +115,12 @@ class IteratorRegionMap {}; class ContainerMap {}; -using IteratorSymbolMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, IteratorPosition); -using IteratorRegionMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, IteratorPosition); -using ContainerMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, ContainerData); +using IteratorSymbolMapTy = + CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, IteratorPosition); +using IteratorRegionMapTy = + CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, IteratorPosition); +using ContainerMapTy = + CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, ContainerData); } // namespace iterator @@ -149,10 +152,17 @@ bool isEraseAfterCall(const FunctionDecl *Func); bool isEmplaceCall(const FunctionDecl *Func); bool isAccessOperator(OverloadedOperatorKind OK); +bool isAccessOperator(UnaryOperatorKind OK); +bool isAccessOperator(BinaryOperatorKind OK); bool isDereferenceOperator(OverloadedOperatorKind OK); +bool isDereferenceOperator(UnaryOperatorKind OK); +bool isDereferenceOperator(BinaryOperatorKind OK); bool isIncrementOperator(OverloadedOperatorKind OK); +bool isIncrementOperator(UnaryOperatorKind OK); bool isDecrementOperator(OverloadedOperatorKind OK); +bool isDecrementOperator(UnaryOperatorKind OK); bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); +bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK); const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont); const IteratorPosition *getIteratorPosition(ProgramStateRef State, diff --git a/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp b/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp --- a/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp @@ -128,24 +128,54 @@ isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK); } +bool isAccessOperator(UnaryOperatorKind OK) { + return isDereferenceOperator(OK) || isIncrementOperator(OK) || + isDecrementOperator(OK); +} + +bool isAccessOperator(BinaryOperatorKind OK) { + return isDereferenceOperator(OK) || isRandomIncrOrDecrOperator(OK); +} + bool isDereferenceOperator(OverloadedOperatorKind OK) { return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar || OK == OO_Subscript; } +bool isDereferenceOperator(UnaryOperatorKind OK) { + return OK == UO_Deref; +} + +bool isDereferenceOperator(BinaryOperatorKind OK) { + return OK == BO_PtrMemI; +} + bool isIncrementOperator(OverloadedOperatorKind OK) { return OK == OO_PlusPlus; } +bool isIncrementOperator(UnaryOperatorKind OK) { + return OK == UO_PreInc || OK == UO_PostInc; +} + bool isDecrementOperator(OverloadedOperatorKind OK) { return OK == OO_MinusMinus; } +bool isDecrementOperator(UnaryOperatorKind OK) { + return OK == UO_PreDec || OK == UO_PostDec; +} + bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus || OK == OO_MinusEqual; } +bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK) { + return OK == BO_Add || OK == BO_AddAssign || + OK == BO_Sub || OK == BO_SubAssign; +} + const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont) { return State->get(Cont); diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp @@ -83,7 +83,9 @@ namespace { class IteratorModeling - : public Checker, + : public Checker, + check::PostStmt, + check::PostStmt, check::Bind, check::LiveSymbols, check::DeadSymbols> { using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, @@ -108,6 +110,8 @@ void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, OverloadedOperatorKind Op, const SVal &RetVal, const SVal &LHS, const SVal &RHS) const; + void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, + OverloadedOperatorKind OK, SVal Offset) const; void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, SVal Amount) const; void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, @@ -144,6 +148,8 @@ void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; + void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; void checkPostStmt(const MaterializeTemporaryExpr *MTE, @@ -153,6 +159,7 @@ }; bool isSimpleComparisonOperator(OverloadedOperatorKind OK); +bool isSimpleComparisonOperator(BinaryOperatorKind OK); ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, bool Equal); @@ -207,12 +214,15 @@ } // Assumption: if return value is an iterator which is not yet bound to a - // container, then look for the first iterator argument, and - // bind the return value to the same container. This approach - // works for STL algorithms. + // container, then look for the first iterator argument of the + // same type as the return value and bind the return value to + // the same container. This approach works for STL algorithms. // FIXME: Add a more conservative mode for (unsigned i = 0; i < Call.getNumArgs(); ++i) { - if (isIteratorType(Call.getArgExpr(i)->getType())) { + if (isIteratorType(Call.getArgExpr(i)->getType()) && + Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType( + C.getASTContext()).getTypePtr() == + Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) { if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { assignToContainer(C, OrigExpr, Call.getReturnValue(), Pos->getContainer()); @@ -238,6 +248,35 @@ } } +void IteratorModeling::checkPostStmt(const UnaryOperator *UO, + CheckerContext &C) const { + UnaryOperatorKind OK = UO->getOpcode(); + if (!isIncrementOperator(OK) && !isDecrementOperator(OK)) + return; + + auto &SVB = C.getSValBuilder(); + handlePtrIncrOrDecr(C, UO->getSubExpr(), + isIncrementOperator(OK) ? OO_Plus : OO_Minus, + SVB.makeArrayIndex(1)); +} + +void IteratorModeling::checkPostStmt(const BinaryOperator *BO, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + BinaryOperatorKind OK = BO->getOpcode(); + SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); + + if (isSimpleComparisonOperator(BO->getOpcode())) { + SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); + SVal Result = State->getSVal(BO, C.getLocationContext()); + handleComparison(C, BO, Result, LVal, RVal, + BinaryOperator::getOverloadedOperator(OK)); + } else if (isRandomIncrOrDecrOperator(OK)) { + handlePtrIncrOrDecr(C, BO->getLHS(), + BinaryOperator::getOverloadedOperator(OK), RVal); + } +} + void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const { /* Transfer iterator state to temporary objects */ @@ -556,6 +595,49 @@ } } +void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, + const Expr *Iterator, + OverloadedOperatorKind OK, + SVal Offset) const { + QualType PtrType = Iterator->getType(); + if (!PtrType->isPointerType()) + return; + QualType ElementType = PtrType->getPointeeType(); + + ProgramStateRef State = C.getState(); + SVal OldVal = State->getSVal(Iterator, C.getLocationContext()); + + const IteratorPosition *OldPos = getIteratorPosition(State, OldVal); + if (!OldPos) + return; + + SVal NewVal; + if (OK == OO_Plus || OK == OO_PlusEqual) + NewVal = State->getLValue(ElementType, Offset, OldVal); + else { + const llvm::APSInt &OffsetInt = + Offset.castAs().getValue(); + auto &BVF = C.getSymbolManager().getBasicVals(); + SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt)); + NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); + } + + // `AdvancedState` is a state where the position of `Old` is advanced. We + // only need this state to retrieve the new position, but we do not want + // ever to change the position of `OldVal`. + auto AdvancedState = advancePosition(State, OldVal, OK, Offset); + if (AdvancedState) { + const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal); + assert(NewPos && + "Iterator should have position after successful advancement"); + + ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos); + C.addTransition(NewState); + } else { + assignToContainer(C, Iterator, NewVal, OldPos->getContainer()); + } +} + void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, SVal Amount) const { @@ -652,6 +734,10 @@ return OK == OO_EqualEqual || OK == OO_ExclaimEqual; } +bool isSimpleComparisonOperator(BinaryOperatorKind OK) { + return OK == BO_EQ || OK == BO_NE; +} + ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { if (auto Reg = Val.getAsRegion()) { Reg = Reg->getMostDerivedObjectRegion(); diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp @@ -27,7 +27,10 @@ namespace { class IteratorRangeChecker - : public Checker { + : public Checker, + check::PreStmt, + check::PreStmt, + check::PreStmt> { std::unique_ptr OutOfRangeBugType; @@ -46,6 +49,10 @@ IteratorRangeChecker(); void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; + void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const; + void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const; using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal, SVal) const; @@ -134,6 +141,56 @@ } } +void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO, + CheckerContext &C) const { + if (isa(UO->getSubExpr())) + return; + + ProgramStateRef State = C.getState(); + UnaryOperatorKind OK = UO->getOpcode(); + SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext()); + + if (isDereferenceOperator(OK)) { + verifyDereference(C, SubVal); + } else if (isIncrementOperator(OK)) { + verifyIncrement(C, SubVal); + } else if (isDecrementOperator(OK)) { + verifyDecrement(C, SubVal); + } +} + +void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + BinaryOperatorKind OK = BO->getOpcode(); + SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); + + if (isDereferenceOperator(OK)) { + verifyDereference(C, LVal); + } else if (isRandomIncrOrDecrOperator(OK)) { + SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); + verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal, + RVal); + } +} + +void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext()); + verifyDereference(C, LVal); +} + +void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME, + CheckerContext &C) const { + if (!ME->isArrow() || ME->isImplicitAccess()) + return; + + ProgramStateRef State = C.getState(); + SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext()); + verifyDereference(C, BaseVal); +} + void IteratorRangeChecker::verifyDereference(CheckerContext &C, SVal Val) const { auto State = C.getState(); diff --git a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -28,7 +28,7 @@ namespace { class MismatchedIteratorChecker - : public Checker { + : public Checker> { std::unique_ptr MismatchedBugType; @@ -47,6 +47,7 @@ MismatchedIteratorChecker(); void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; }; @@ -141,7 +142,7 @@ // Example: // template // void f(I1 first1, I1 last1, I2 first2, I2 last2); - // + // // In this case the first two arguments to f() must be iterators must belong // to the same container and the last to also to the same container but // not necessarily to the same as the first two. @@ -188,6 +189,17 @@ } } +void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO, + CheckerContext &C) const { + if (!BO->isComparisonOp()) + return; + + ProgramStateRef State = C.getState(); + SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); + SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); + verifyMatch(C, LVal, RVal); +} + void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, const MemRegion *Cont) const { // Verify match between a container and the container of an iterator diff --git a/clang/test/Analysis/invalidated-iterator.cpp b/clang/test/Analysis/invalidated-iterator.cpp --- a/clang/test/Analysis/invalidated-iterator.cpp +++ b/clang/test/Analysis/invalidated-iterator.cpp @@ -120,3 +120,80 @@ V.erase(i); auto j = V.cbegin(); // no-warning } + +template +struct cont_with_ptr_iterator { + T *begin() const; + T *end() const; + T &operator[](size_t); + void push_back(const T&); + T* erase(T*); +}; + +void invalidated_dereference_end_ptr_iterator(cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + (void) *i; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_prefix_increment_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + ++i; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_prefix_decrement_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin() + 1; + C.erase(i); + --i; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_postfix_increment_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + i++; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_postfix_decrement_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin() + 1; + C.erase(i); + i--; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_increment_by_2_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + i += 2; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_increment_by_2_copy_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + auto j = i + 2; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_decrement_by_2_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + i -= 2; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_decrement_by_2_copy_end_ptr_iterator( + cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + auto j = i - 2; // expected-warning{{Invalidated iterator accessed}} +} + +void invalidated_subscript_end_ptr_iterator(cont_with_ptr_iterator &C) { + auto i = C.begin(); + C.erase(i); + (void) i[1]; // expected-warning{{Invalidated iterator accessed}} +} diff --git a/clang/test/Analysis/iterator-modeling.cpp b/clang/test/Analysis/iterator-modeling.cpp --- a/clang/test/Analysis/iterator-modeling.cpp +++ b/clang/test/Analysis/iterator-modeling.cpp @@ -18,6 +18,7 @@ long clang_analyzer_container_end(const Container&); template long clang_analyzer_iterator_position(const Iterator&); +long clang_analyzer_iterator_position(int*); template void* clang_analyzer_iterator_container(const Iterator&); template @@ -1864,6 +1865,113 @@ } } +template +struct cont_with_ptr_iterator { + typedef T* iterator; + T* begin() const; + T* end() const; +}; + +void begin_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + + clang_analyzer_eval(clang_analyzer_iterator_container(i) == &c); // expected-warning{{TRUE}} + clang_analyzer_denote(clang_analyzer_container_begin(c), "$c.begin()"); + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.begin()}} + + if (i != c.begin()) { + clang_analyzer_warnIfReached(); + } + } + +void prefix_increment_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + + clang_analyzer_denote(clang_analyzer_container_begin(c), "$c.begin()"); + + auto j = ++i; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.begin() + 1}} + clang_analyzer_express(clang_analyzer_iterator_position(j)); // expected-warning{{$c.begin() + 1}} +} + +void prefix_decrement_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + + clang_analyzer_denote(clang_analyzer_container_end(c), "$c.end()"); + + auto j = --i; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.end() - 1}} + clang_analyzer_express(clang_analyzer_iterator_position(j)); // expected-warning{{$c.end() - 1}} +} + +void postfix_increment_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + + clang_analyzer_denote(clang_analyzer_container_begin(c), "$c.begin()"); + + auto j = i++; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.begin() + 1}} + clang_analyzer_express(clang_analyzer_iterator_position(j)); // expected-warning{{$c.begin()}} +} + +void postfix_decrement_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + + clang_analyzer_denote(clang_analyzer_container_end(c), "$c.end()"); + + auto j = i--; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.end() - 1}} + clang_analyzer_express(clang_analyzer_iterator_position(j)); // expected-warning{{$c.end()}} +} + +void plus_equal_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + + clang_analyzer_denote(clang_analyzer_container_begin(c), "$c.begin()"); + + i += 2; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.begin() + 2}} +} + +void minus_equal_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + + clang_analyzer_denote(clang_analyzer_container_end(c), "$c.end()"); + + i -= 2; + + clang_analyzer_express(clang_analyzer_iterator_position(i)); // expected-warning{{$c.end() - 2}} +} + +void plus_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i1 = c.begin(); + + clang_analyzer_denote(clang_analyzer_container_begin(c), "$c.begin()"); + + auto i2 = i1 + 2; + + clang_analyzer_eval(clang_analyzer_iterator_container(i2) == &c); // expected-warning{{TRUE}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning{{$c.begin()}} + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning{{$c.begin() + 2}} +} + +void minus_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i1 = c.end(); + + clang_analyzer_denote(clang_analyzer_container_end(c), "$c.end()"); + + auto i2 = i1 - 2; + + clang_analyzer_eval(clang_analyzer_iterator_container(i2) == &c); // expected-warning{{TRUE}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning{{$c.end()}} + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning{{$c.end() - 2}} +} + void clang_analyzer_printState(); void print_state(std::vector &V) { diff --git a/clang/test/Analysis/iterator-range.cpp b/clang/test/Analysis/iterator-range.cpp --- a/clang/test/Analysis/iterator-range.cpp +++ b/clang/test/Analysis/iterator-range.cpp @@ -854,3 +854,84 @@ *i; // expected-warning{{Past-the-end iterator dereferenced}} // expected-note@-1{{Past-the-end iterator dereferenced}} } + +template +struct cont_with_ptr_iterator { + T* begin() const; + T* end() const; +}; + +void deref_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + (void) *i; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} +} + +void array_deref_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + (void) i[0]; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} +} + +void arrow_deref_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + (void) i->n; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} +} + +void arrow_star_deref_end_ptr_iterator(const cont_with_ptr_iterator &c, + int S::*p) { + auto i = c.end(); + (void)(i->*p); // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} +} + +void prefix_incr_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + ++i; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} +} + +void postfix_incr_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + i++; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} +} + +void prefix_decr_begin_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + --i; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} +} + +void postfix_decr_begin_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + i--; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} +} + +void prefix_add_2_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + (void)(i + 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} +} + +void postfix_add_assign_2_end_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.end(); + i += 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} +} + +void prefix_minus_2_begin_ptr_iterator(const cont_with_ptr_iterator &c) { + auto i = c.begin(); + (void)(i - 2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} +} + +void postfix_minus_assign_2_begin_ptr_iterator( + const cont_with_ptr_iterator &c) { + auto i = c.begin(); + i -= 2; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} +} + diff --git a/clang/test/Analysis/mismatched-iterator.cpp b/clang/test/Analysis/mismatched-iterator.cpp --- a/clang/test/Analysis/mismatched-iterator.cpp +++ b/clang/test/Analysis/mismatched-iterator.cpp @@ -118,3 +118,15 @@ if (V1.cbegin() == V2.cbegin()) {} //no-warning } + +template +struct cont_with_ptr_iterator { + T *begin() const; + T *end() const; +}; + +void comparison_ptr_iterator(cont_with_ptr_iterator &C1, + cont_with_ptr_iterator &C2) { + if (C1.begin() != C2.end()) {} // expected-warning{{Iterators of different containers used where the same container is expected}} +} +