namespace {
class InvalidatedIteratorChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
+ check::PreStmt<BinaryOperator>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PreStmt<MemberExpr>> {
std::unique_ptr<BugType> InvalidatedBugType;
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;
};
}
}
+void InvalidatedIteratorChecker::checkPreStmt(const UnaryOperator *UO,
+ CheckerContext &C) const {
+ if (isa<CXXThisExpr>(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);
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<ContainerMap>(Cont);
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
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,
namespace {
class IteratorModeling
- : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
+ : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
+ check::PostStmt<BinaryOperator>,
+ check::PostStmt<MaterializeTemporaryExpr>,
check::Bind, check::LiveSymbols, check::DeadSymbols> {
using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
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,
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,
};
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);
}
// 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());
}
}
+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 */
}
}
+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<nonloc::ConcreteInt>().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 {
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();
namespace {
class IteratorRangeChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
+ check::PreStmt<BinaryOperator>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PreStmt<MemberExpr>> {
std::unique_ptr<BugType> OutOfRangeBugType;
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;
}
}
+void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
+ CheckerContext &C) const {
+ if (isa<CXXThisExpr>(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();
namespace {
class MismatchedIteratorChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
std::unique_ptr<BugType> MismatchedBugType;
MismatchedIteratorChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
};
// Example:
// template<typename I1, typename I2>
// 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.
}
}
+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
V.erase(i);
auto j = V.cbegin(); // no-warning
}
+
+template<typename T>
+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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &C) {
+ auto i = C.begin();
+ C.erase(i);
+ (void) i[1]; // expected-warning{{Invalidated iterator accessed}}
+}
long clang_analyzer_container_end(const Container&);
template <typename Iterator>
long clang_analyzer_iterator_position(const Iterator&);
+long clang_analyzer_iterator_position(int*);
template <typename Iterator>
void* clang_analyzer_iterator_container(const Iterator&);
template <typename Iterator>
}
}
+template<typename T>
+struct cont_with_ptr_iterator {
+ typedef T* iterator;
+ T* begin() const;
+ T* end() const;
+};
+
+void begin_ptr_iterator(const cont_with_ptr_iterator<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &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<int> &V) {
*i; // expected-warning{{Past-the-end iterator dereferenced}}
// expected-note@-1{{Past-the-end iterator dereferenced}}
}
+
+template<typename T>
+struct cont_with_ptr_iterator {
+ T* begin() const;
+ T* end() const;
+};
+
+void deref_end_ptr_iterator(const cont_with_ptr_iterator<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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<S> &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}}
+}
+
if (V1.cbegin() == V2.cbegin()) {} //no-warning
}
+
+template<typename T>
+struct cont_with_ptr_iterator {
+ T *begin() const;
+ T *end() const;
+};
+
+void comparison_ptr_iterator(cont_with_ptr_iterator<int> &C1,
+ cont_with_ptr_iterator<int> &C2) {
+ if (C1.begin() != C2.end()) {} // expected-warning{{Iterators of different containers used where the same container is expected}}
+}
+