Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -287,6 +287,9 @@ bool isEraseCall(const FunctionDecl *Func); bool isEraseAfterCall(const FunctionDecl *Func); bool isEmplaceCall(const FunctionDecl *Func); +bool isStdAdvanceCall(const FunctionDecl *Func); +bool isStdPrevCall(const FunctionDecl *Func); +bool isStdNextCall(const FunctionDecl *Func); bool isAssignmentOperator(OverloadedOperatorKind OK); bool isSimpleComparisonOperator(OverloadedOperatorKind OK); bool isAccessOperator(OverloadedOperatorKind OK); @@ -473,6 +476,12 @@ verifyMatch(C, Call.getArgSVal(0), InstCall->getCXXThisVal().getAsRegion()); } + } else if (ChecksEnabled[CK_IteratorRangeChecker] && + (isStdAdvanceCall(Func) || isStdNextCall(Func))) { + verifyRandomIncrOrDecr(C, OO_Plus, Call.getArgSVal(0), Call.getArgSVal(1)); + } else if (ChecksEnabled[CK_IteratorRangeChecker] && + isStdPrevCall(Func)) { + verifyRandomIncrOrDecr(C, OO_Minus, Call.getArgSVal(0), Call.getArgSVal(1)); } else if (isa(&Call)) { // Check match of first-last iterator pair in a constructor of a container if (Call.getNumArgs() < 2) @@ -685,6 +694,24 @@ } } + if (!C.wasInlined) { + if (isStdAdvanceCall(Func)) { + handleRandomIncrOrDecr(C, OO_PlusEqual, UndefinedVal(), + Call.getArgSVal(0), Call.getArgSVal(1)); + return; + } + if (isStdPrevCall(Func)) { + handleRandomIncrOrDecr(C, OO_Minus, Call.getReturnValue(), + Call.getArgSVal(0), Call.getArgSVal(1)); + return; + } + if (isStdNextCall(Func)) { + handleRandomIncrOrDecr(C, OO_Plus, Call.getReturnValue(), + Call.getArgSVal(0), Call.getArgSVal(1)); + return; + } + } + const auto *OrigExpr = Call.getOriginExpr(); if (!OrigExpr) return; @@ -1825,6 +1852,38 @@ return IdInfo->getName() == "erase_after"; } +bool isStdAdvanceCall(const FunctionDecl *Func) { + if (!Func->isInStdNamespace()) + return false; + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + return (Func->getNumParams() == 2 && IdInfo->getName() == "advance") || + (Func->getNumParams() == 3 && IdInfo->getName() == "__advance"); +} + +bool isStdPrevCall(const FunctionDecl *Func) { + if (!Func->isInStdNamespace()) + return false; + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() != 2) + return false; + return IdInfo->getName() == "prev"; +} + +bool isStdNextCall(const FunctionDecl *Func) { + if (!Func->isInStdNamespace()) + return false; + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() != 2) + return false; + return IdInfo->getName() == "next"; +} + bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; } bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { 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 @@ -768,6 +768,15 @@ return it; } + template + ForwardIterator + next (ForwardIterator it, + typename iterator_traits::difference_type n = + 1) { + advance(it, n); + return it; + } + template InputIterator find(InputIterator first, InputIterator last, const T &val); Index: test/Analysis/iterator-range.cpp =================================================================== --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -116,6 +116,17 @@ } } +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 = vec.end(); + *std::prev(i); // no-warning +} + void good_push_back(std::list &L, int n) { auto i0 = --L.cend(); L.push_back(n); @@ -195,11 +206,31 @@ --i0; // no-warning } +void good_decr_end_advance(const std::list &L) { + auto i0 = L.end(); + std::advance(i0, -1); // no-warning +} + +void good_decr_end_prev(const std::list &L) { + auto i0 = L.end(); + auto i1 = std::prev(i0); // no-warning +} + void bad_incr_end(const std::list &L) { auto i0 = L.end(); ++i0; // expected-warning{{Iterator incremented behind the past-the-end iterator}} } +void bad_decr_end_advance(const std::list &L) { + auto i0 = L.end(); + std::advance(i0, 1); // expected-warning{{Iterator incremented behind the past-the-end }} +} + +void bad_decr_end_next(const std::list &L) { + auto i0 = L.end(); + auto i1 = std::next(i0); // expected-warning{{Iterator incremented behind the past-the-end }} +} + struct simple_iterator_base { simple_iterator_base(); simple_iterator_base(const simple_iterator_base& rhs);