[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
baloghadamsoftware updated this revision to Diff 176256. baloghadamsoftware added a comment. Thank you for the review! You are absolutely right, these error messages were not accurate and even misleading. Now I updated them. To achieve this I also had to separate the function `isOutOfRange()` into three different functions. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D53812/new/ https://reviews.llvm.org/D53812 Files: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp test/Analysis/iterator-range.cpp Index: test/Analysis/iterator-range.cpp === --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -23,34 +23,13 @@ void simple_bad_end(const std::vector ) { auto i = v.end(); - *i; // expected-warning{{Iterator accessed outside of its range}} -} - -void simple_good_begin(const std::vector ) { - auto i = v.begin(); - if (i != v.begin()) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_good_begin_negated(const std::vector ) { - auto i = v.begin(); - if (!(i == v.begin())) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_bad_begin(const std::vector ) { - auto i = v.begin(); - *--i; // expected-warning{{Iterator accessed outside of its range}} + *i; // expected-warning{{The past-the-end iterator dereferenced}} } void copy(const std::vector ) { auto i1 = v.end(); auto i2 = i1; - *i2; // expected-warning{{Iterator accessed outside of its range}} + *i2; // expected-warning{{The past-the-end iterator dereferenced}} } void decrease(const std::vector ) { @@ -70,7 +49,7 @@ auto i1 = v.end(); auto i2 = i1; --i1; - *i2; // expected-warning{{Iterator accessed outside of its range}} + *i2; // expected-warning{{The past-the-end iterator dereferenced}} } void copy_and_increase1(const std::vector ) { @@ -86,7 +65,7 @@ auto i2 = i1; ++i1; if (i2 == v.end()) -*i2; // expected-warning{{Iterator accessed outside of its range}} +*i2; // expected-warning{{The past-the-end iterator dereferenced}} } void copy_and_increase3(const std::vector ) { @@ -94,7 +73,7 @@ auto i2 = i1; ++i1; if (v.end() == i2) -*i2; // expected-warning{{Iterator accessed outside of its range}} +*i2; // expected-warning{{The past-the-end iterator dereferenced}} } template @@ -116,14 +95,14 @@ void bad_non_std_find(std::vector , int e) { auto first = nonStdFind(V.begin(), V.end(), e); - *first; // expected-warning{{Iterator accessed outside of its range}} + *first; // expected-warning{{The past-the-end iterator dereferenced}} } void tricky(std::vector , int e) { const auto first = V.begin(); const auto comp1 = (first != V.end()), comp2 = (first == V.end()); if (comp1) -*first; +*first; // no-warning } void loop(std::vector , int e) { @@ -147,7 +126,7 @@ auto i0 = --L.cend(); L.push_back(n); ++i0; - *++i0; // expected-warning{{Iterator accessed outside of its range}} + *++i0; // expected-warning{{The past-the-end iterator dereferenced}} } void good_pop_back(std::list , int n) { @@ -159,7 +138,7 @@ void bad_pop_back(std::list , int n) { auto i0 = --L.cend(); --i0; L.pop_back(); - *++i0; // expected-warning{{Iterator accessed outside of its range}} + *++i0; // expected-warning{{The past-the-end iterator dereferenced}} } void good_push_front(std::list , int n) { @@ -172,7 +151,7 @@ auto i0 = L.cbegin(); L.push_front(n); --i0; - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator decremented ahead of its valid range}} } void good_pop_front(std::list , int n) { @@ -184,13 +163,13 @@ void bad_pop_front(std::list , int n) { auto i0 = ++L.cbegin(); L.pop_front(); - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator decremented ahead of its valid range}} } void bad_move(std::list , std::list ) { auto i0 = --L2.cend(); L1 = std::move(L2); - *++i0; // expected-warning{{Iterator accessed outside of its range}} + *++i0; // expected-warning{{The past-the-end iterator dereferenced}} } void bad_move_push_back(std::list , std::list , int n) { @@ -198,5 +177,25 @@ L2.push_back(n); L1 = std::move(L2); ++i0; - *++i0; // expected-warning{{Iterator accessed outside of its range}} + *++i0; // expected-warning{{The past-the-end iterator dereferenced}} +} + +void good_incr_begin(const std::list ) { + auto i0 = L.begin(); + ++i0; // no-warning +} + +void bad_decr_begin(const std::list ) { + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator decremented ahead of its valid range}} +} + +void good_decr_end(const std::list ) { + auto i0 = L.end(); + --i0; // no-warning +} + +void bad_incr_end(const std::list ) { + auto i0 = L.end(); + ++i0; // expected-warning{{Iterator incremented behind the
Re: [PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
I suspect that the point is that previously we believed that decrementing below begin and than *incrementing* it back to begin() would yield begin(), but now we disallow this "incrementing back" behavior and warn immediately on decrement because in fact it's already UB. Though, yeah, the title is wonky. On 11/30/18 2:19 PM, Roman Lebedev wrote: ""Re: [PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers" Is it be or that reads backwards? Why can't you increment past the begin()? Can you decrement past the begin() instead? And the opposite for the end(). On Sat, Dec 1, 2018 at 1:17 AM Artem Dergachev via Phabricator via cfe-commits wrote: NoQ accepted this revision. NoQ added a comment. This revision is now accepted and ready to land. Makes perfect sense to me! Comment at: test/Analysis/iterator-range.cpp:190 + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator accessed outside of its range}} +} I guess we'll have to come up with a separate warning text for this case, as there's no access happening through the iterator. Regardless of how the final message would look, i suggest adding logic for having a different message now, so that we didn't forget about this case later. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D53812/new/ https://reviews.llvm.org/D53812 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
""Re: [PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers" Is it be or that reads backwards? Why can't you increment past the begin()? Can you decrement past the begin() instead? And the opposite for the end(). On Sat, Dec 1, 2018 at 1:17 AM Artem Dergachev via Phabricator via cfe-commits wrote: > > NoQ accepted this revision. > NoQ added a comment. > This revision is now accepted and ready to land. > > Makes perfect sense to me! > > > > > Comment at: test/Analysis/iterator-range.cpp:190 > + auto i0 = L.begin(); > + --i0; // expected-warning{{Iterator accessed outside of its range}} > +} > > I guess we'll have to come up with a separate warning text for this case, as > there's no access happening through the iterator. > > Regardless of how the final message would look, i suggest adding logic for > having a different message now, so that we didn't forget about this case > later. > > > CHANGES SINCE LAST ACTION > https://reviews.llvm.org/D53812/new/ > > https://reviews.llvm.org/D53812 > > > > ___ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
NoQ accepted this revision. NoQ added a comment. This revision is now accepted and ready to land. Makes perfect sense to me! Comment at: test/Analysis/iterator-range.cpp:190 + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator accessed outside of its range}} +} I guess we'll have to come up with a separate warning text for this case, as there's no access happening through the iterator. Regardless of how the final message would look, i suggest adding logic for having a different message now, so that we didn't forget about this case later. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D53812/new/ https://reviews.llvm.org/D53812 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
baloghadamsoftware updated this revision to Diff 175134. baloghadamsoftware added a comment. Updated according to the comments. https://reviews.llvm.org/D53812 Files: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp test/Analysis/iterator-range.cpp Index: test/Analysis/iterator-range.cpp === --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -26,27 +26,6 @@ *i; // expected-warning{{Iterator accessed outside of its range}} } -void simple_good_begin(const std::vector ) { - auto i = v.begin(); - if (i != v.begin()) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_good_begin_negated(const std::vector ) { - auto i = v.begin(); - if (!(i == v.begin())) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_bad_begin(const std::vector ) { - auto i = v.begin(); - *--i; // expected-warning{{Iterator accessed outside of its range}} -} - void copy(const std::vector ) { auto i1 = v.end(); auto i2 = i1; @@ -172,7 +151,7 @@ auto i0 = L.cbegin(); L.push_front(n); --i0; - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void good_pop_front(std::list , int n) { @@ -184,7 +163,7 @@ void bad_pop_front(std::list , int n) { auto i0 = ++L.cbegin(); L.pop_front(); - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void bad_move(std::list , std::list ) { @@ -200,3 +179,23 @@ ++i0; *++i0; // expected-warning{{Iterator accessed outside of its range}} } + +void good_incr_begin(const std::list ) { + auto i0 = L.begin(); + ++i0; // no-warning +} + +void bad_decr_begin(const std::list ) { + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_decr_end(const std::list ) { + auto i0 = L.end(); + --i0; // no-warning +} + +void bad_incr_end(const std::list ) { + auto i0 = L.end(); + ++i0; // expected-warning{{Iterator accessed outside of its range}} +} Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp === --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -238,14 +238,17 @@ void handleEraseAfter(CheckerContext , const SVal ) const; void handleEraseAfter(CheckerContext , const SVal , const SVal ) const; + void verifyIncrement(CheckerContext , const SVal ) const; + void verifyDecrement(CheckerContext , const SVal ) const; void verifyRandomIncrOrDecr(CheckerContext , OverloadedOperatorKind Op, - const SVal , const SVal , - const SVal ) const; + const SVal , const SVal ) const; void verifyMatch(CheckerContext , const SVal , const MemRegion *Cont) const; void verifyMatch(CheckerContext , const SVal , const SVal ) const; - + IteratorPosition advancePosition(CheckerContext , OverloadedOperatorKind Op, + const IteratorPosition , + const SVal ) const; void reportOutOfRangeBug(const StringRef , const SVal , CheckerContext , ExplodedNode *ErrNode) const; void reportMismatchedBug(const StringRef , const SVal , @@ -388,7 +391,8 @@ bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); bool isBoundThroughLazyCompoundVal(const Environment , const MemRegion *Reg); -bool isOutOfRange(ProgramStateRef State, const IteratorPosition ); +bool isOutOfRange(ProgramStateRef State, const IteratorPosition , + bool IncludeEnd = false); bool isZero(ProgramStateRef State, const NonLoc ); } // namespace @@ -422,29 +426,46 @@ verifyAccess(C, Call.getArgSVal(0)); } } -if (ChecksEnabled[CK_IteratorRangeChecker] && -isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { - if (const auto *InstCall = dyn_cast()) { -// Check for out-of-range incrementions and decrementions -if (Call.getNumArgs() >= 1) { - verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), - Call.getReturnValue(), - InstCall->getCXXThisVal(), Call.getArgSVal(0)); +if (ChecksEnabled[CK_IteratorRangeChecker]) { + if (isIncrementOperator(Func->getOverloadedOperator())) { +// Check for out-of-range incrementions +if (const auto *InstCall = dyn_cast()) { + verifyIncrement(C, InstCall->getCXXThisVal()); +} else { + if (Call.getNumArgs() >= 1) { +verifyIncrement(C, Call.getArgSVal(0)); +
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
baloghadamsoftware updated this revision to Diff 175124. baloghadamsoftware added a comment. Restored corrected patch after uploading an incorrect one. https://reviews.llvm.org/D53812 Files: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp test/Analysis/iterator-range.cpp Index: test/Analysis/iterator-range.cpp === --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -26,27 +26,6 @@ *i; // expected-warning{{Iterator accessed outside of its range}} } -void simple_good_begin(const std::vector ) { - auto i = v.begin(); - if (i != v.begin()) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_good_begin_negated(const std::vector ) { - auto i = v.begin(); - if (!(i == v.begin())) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_bad_begin(const std::vector ) { - auto i = v.begin(); - *--i; // expected-warning{{Iterator accessed outside of its range}} -} - void copy(const std::vector ) { auto i1 = v.end(); auto i2 = i1; @@ -172,7 +151,7 @@ auto i0 = L.cbegin(); L.push_front(n); --i0; - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void good_pop_front(std::list , int n) { @@ -184,7 +163,7 @@ void bad_pop_front(std::list , int n) { auto i0 = ++L.cbegin(); L.pop_front(); - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void bad_move(std::list , std::list ) { @@ -200,3 +179,23 @@ ++i0; *++i0; // expected-warning{{Iterator accessed outside of its range}} } + +void good_incr_begin(const std::list ) { + auto i0 = L.begin(); + ++i0; // no-warning +} + +void bad_decr_begin(const std::list ) { + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_decr_end(const std::list ) { + auto i0 = L.end(); + --i0; // no-warning +} + +void bad_incr_end(const std::list ) { + auto i0 = L.end(); + ++i0; // expected-warning{{Iterator accessed outside of its range}} +} Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp === --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -238,14 +238,17 @@ void handleEraseAfter(CheckerContext , const SVal ) const; void handleEraseAfter(CheckerContext , const SVal , const SVal ) const; + void verifyIncrement(CheckerContext , const SVal ) const; + void verifyDecrement(CheckerContext , const SVal ) const; void verifyRandomIncrOrDecr(CheckerContext , OverloadedOperatorKind Op, - const SVal , const SVal , - const SVal ) const; + const SVal , const SVal ) const; void verifyMatch(CheckerContext , const SVal , const MemRegion *Cont) const; void verifyMatch(CheckerContext , const SVal , const SVal ) const; - + IteratorPosition shiftPosition(CheckerContext , OverloadedOperatorKind Op, + const IteratorPosition , + const SVal ) const; void reportOutOfRangeBug(const StringRef , const SVal , CheckerContext , ExplodedNode *ErrNode) const; void reportMismatchedBug(const StringRef , const SVal , @@ -388,7 +391,8 @@ bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); bool isBoundThroughLazyCompoundVal(const Environment , const MemRegion *Reg); -bool isOutOfRange(ProgramStateRef State, const IteratorPosition ); +bool isOutOfRange(ProgramStateRef State, const IteratorPosition , + bool IncludeEnd = false); bool isZero(ProgramStateRef State, const NonLoc ); } // namespace @@ -422,29 +426,46 @@ verifyAccess(C, Call.getArgSVal(0)); } } -if (ChecksEnabled[CK_IteratorRangeChecker] && -isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { - if (const auto *InstCall = dyn_cast()) { -// Check for out-of-range incrementions and decrementions -if (Call.getNumArgs() >= 1) { - verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), - Call.getReturnValue(), - InstCall->getCXXThisVal(), Call.getArgSVal(0)); +if (ChecksEnabled[CK_IteratorRangeChecker]) { + if (isIncrementOperator(Func->getOverloadedOperator())) { +// Check for out-of-range incrementions +if (const auto *InstCall = dyn_cast()) { + verifyIncrement(C, InstCall->getCXXThisVal()); +} else { + if (Call.getNumArgs() >= 1) { +verifyIncrement(C,
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
baloghadamsoftware updated this revision to Diff 175113. baloghadamsoftware added a comment. Herald added a subscriber: gamesh411. More standard-like tests. https://reviews.llvm.org/D53812 Files: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h lib/StaticAnalyzer/Checkers/IteratorChecker.cpp lib/StaticAnalyzer/Core/MemRegion.cpp test/Analysis/iterator-range.cpp Index: test/Analysis/iterator-range.cpp === --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -200,3 +200,40 @@ ++i0; *++i0; // expected-warning{{Iterator accessed outside of its range}} } + +struct simple_iterator_base { + simple_iterator_base(); + simple_iterator_base(const simple_iterator_base& rhs); + simple_iterator_base =(const simple_iterator_base& rhs); + virtual ~simple_iterator_base(); + bool friend operator==(const simple_iterator_base , + const simple_iterator_base ); + bool friend operator!=(const simple_iterator_base , + const simple_iterator_base ); +private: + int *ptr; +}; + +struct simple_derived_iterator: public simple_iterator_base { + int& operator*(); + int* operator->(); + simple_iterator_base ++(); + simple_iterator_base operator++(int); + simple_iterator_base (); + simple_iterator_base operator--(int); +}; + +struct simple_container { + typedef simple_derived_iterator iterator; + + iterator begin(); + iterator end(); +}; + +void good_derived(simple_container c) { + auto i0 = c.end(); + if (i0 != c.end()) { +clang_analyzer_warnIfReached(); +*i0; // no-warning + } +} Index: lib/StaticAnalyzer/Core/MemRegion.cpp === --- lib/StaticAnalyzer/Core/MemRegion.cpp +++ lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1175,6 +1175,20 @@ return R; } +// getgetMostDerivedObjectRegion gets the region of the root class of a C++ +// class hierarchy. +const MemRegion *MemRegion::getMostDerivedObjectRegion() const { + const MemRegion *R = this; + while (true) { +if (R->getKind() == MemRegion::CXXBaseObjectRegionKind) { +R = cast(R)->getSuperRegion(); +continue; +} +break; + } + return R; +} + bool MemRegion::isSubRegionOf(const MemRegion *) const { return false; } Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp === --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -1089,9 +1089,7 @@ void IteratorChecker::verifyMatch(CheckerContext , const SVal , const MemRegion *Cont) const { // Verify match between a container and the container of an iterator - while (const auto *CBOR = Cont->getAs()) { -Cont = CBOR->getSuperRegion(); - } + Cont = Cont->getMostDerivedObjectRegion(); auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Iter); @@ -1125,9 +1123,7 @@ if (!ContReg) return; - while (const auto *CBOR = ContReg->getAs()) { -ContReg = CBOR->getSuperRegion(); - } + ContReg = ContReg->getMostDerivedObjectRegion(); // If the container already has a begin symbol then use it. Otherwise first // create a new one. @@ -1151,9 +1147,7 @@ if (!ContReg) return; - while (const auto *CBOR = ContReg->getAs()) { -ContReg = CBOR->getSuperRegion(); - } + ContReg = ContReg->getMostDerivedObjectRegion(); // If the container already has an end symbol then use it. Otherwise first // create a new one. @@ -1174,9 +1168,7 @@ void IteratorChecker::assignToContainer(CheckerContext , const Expr *CE, const SVal , const MemRegion *Cont) const { - while (const auto *CBOR = Cont->getAs()) { -Cont = CBOR->getSuperRegion(); - } + Cont = Cont->getMostDerivedObjectRegion(); auto State = C.getState(); auto = C.getSymbolManager(); @@ -1194,9 +1186,7 @@ if (!ContReg) return; - while (const auto *CBOR = ContReg->getAs()) { -ContReg = CBOR->getSuperRegion(); - } + ContReg = ContReg->getMostDerivedObjectRegion(); // Assignment of a new value to a container always invalidates all its // iterators @@ -1211,9 +1201,7 @@ if (!OldCont.isUndef()) { const auto *OldContReg = OldCont.getAsRegion(); if (OldContReg) { - while (const auto *CBOR = OldContReg->getAs()) { -OldContReg = CBOR->getSuperRegion(); - } + OldContReg = OldContReg->getMostDerivedObjectRegion(); const auto OldCData = getContainerData(State, OldContReg); if (OldCData) { if (const auto OldEndSym = OldCData->getEnd()) { @@ -1273,9 +1261,7 @@ if (!ContReg) return; - while (const auto *CBOR = ContReg->getAs()) { -ContReg = CBOR->getSuperRegion(); - } + ContReg = ContReg->getMostDerivedObjectRegion();
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
whisperity added inline comments. Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:420-465 if (ChecksEnabled[CK_InvalidatedIteratorChecker] && isAccessOperator(Func->getOverloadedOperator())) { // Check for any kind of access of invalidated iterator positions if (const auto *InstCall = dyn_cast()) { verifyAccess(C, InstCall->getCXXThisVal()); } else { verifyAccess(C, Call.getArgSVal(0)); This is a bit of an organisational comment (and the compiler of course hopefully optimises this out), but could you, for the sake of code readability, break the if-elseif-elseif-elseif chain's common check out into an outer if, and only check for the inner parts? Thinking of something like this: ``` if (ChecksEnabled[CK_InvalidatedIteratorChecker]) { /* yadda */ } if (ChecksEnabled[CK_IteratorRangeChecker]) { X* OOperator = Func->getOverloadedOperator(); if (isIncrementOperator(OOperator)) { /* yadda */ } else if (isDecrementOperator(OOperator)) { /* yadda */ } } /* etc. */ ``` Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:1074 - auto = C.getSymbolManager(); - auto = C.getSValBuilder(); - auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub; - const auto OldOffset = Pos->getOffset(); - const auto intValue = value.getAs(); - if (!intValue) + // Incremention or decremention by 0 is never bug + if (isZero(State, Value.castAs())) is never a, also `.` at the end Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:1568 +IteratorPosition IteratorChecker::shiftPosition(CheckerContext , +OverloadedOperatorKind Op, (Minor: I don't like the name of this function, `advancePosition` (akin to `std::advance`) sounds cleaner to my ears.) Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:1575 + auto = C.getSValBuilder(); + auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub; + if (const auto IntDist = Distance.getAs()) { For the sake of clarity, I think an assert should be introduced her, lest someone ends up shifting the position with `<=`. Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:2329-2330 // Out of range means less than the begin symbol or greater or equal to the // end symbol. How does the introduction of `IncludeEnd` change this behaviour? What does the parameter refer to? Repository: rC Clang https://reviews.llvm.org/D53812 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D53812: [Analyzer] Iterator Checker - Forbid increments past the begin() and decrements past the end() of containers
baloghadamsoftware created this revision. baloghadamsoftware added a reviewer: NoQ. baloghadamsoftware added a project: clang. Herald added subscribers: donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, whisperity. Herald added a reviewer: george.karpenkov. Previously, the iterator range checker only warned upon dereference of iterators outside their valid range as well as increments and decrements of out-of range iterators where the result remains out-of range. However, the `C++` standard is more strict than this: decrementing `begin()` or inrementing `end()` results in undefined behavior even if the iterator is not dereferenced afterward. Coming back to the range once out-of-range is also undefined. This patch corrects the behavior of the iterator range checker: warnings are given for any operation whose result is ahead of `begin()` or past the `end()` (which is the past-end iterator itself, thus now we are speaking of past past-end). Repository: rC Clang https://reviews.llvm.org/D53812 Files: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp test/Analysis/iterator-range.cpp Index: test/Analysis/iterator-range.cpp === --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -26,27 +26,6 @@ *i; // expected-warning{{Iterator accessed outside of its range}} } -void simple_good_begin(const std::vector ) { - auto i = v.begin(); - if (i != v.begin()) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_good_begin_negated(const std::vector ) { - auto i = v.begin(); - if (!(i == v.begin())) { -clang_analyzer_warnIfReached(); -*--i; // no-warning - } -} - -void simple_bad_begin(const std::vector ) { - auto i = v.begin(); - *--i; // expected-warning{{Iterator accessed outside of its range}} -} - void copy(const std::vector ) { auto i1 = v.end(); auto i2 = i1; @@ -172,7 +151,7 @@ auto i0 = L.cbegin(); L.push_front(n); --i0; - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void good_pop_front(std::list , int n) { @@ -184,7 +163,7 @@ void bad_pop_front(std::list , int n) { auto i0 = ++L.cbegin(); L.pop_front(); - *--i0; // expected-warning{{Iterator accessed outside of its range}} + --i0; // expected-warning{{Iterator accessed outside of its range}} } void bad_move(std::list , std::list ) { @@ -200,3 +179,23 @@ ++i0; *++i0; // expected-warning{{Iterator accessed outside of its range}} } + +void good_incr_begin(const std::list ) { + auto i0 = L.begin(); + ++i0; // no-warning +} + +void bad_decr_begin(const std::list ) { + auto i0 = L.begin(); + --i0; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_decr_end(const std::list ) { + auto i0 = L.end(); + --i0; // no-warning +} + +void bad_incr_end(const std::list ) { + auto i0 = L.end(); + ++i0; // expected-warning{{Iterator accessed outside of its range}} +} Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp === --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -238,14 +238,17 @@ void handleEraseAfter(CheckerContext , const SVal ) const; void handleEraseAfter(CheckerContext , const SVal , const SVal ) const; + void verifyIncrement(CheckerContext , const SVal ) const; + void verifyDecrement(CheckerContext , const SVal ) const; void verifyRandomIncrOrDecr(CheckerContext , OverloadedOperatorKind Op, - const SVal , const SVal , - const SVal ) const; + const SVal , const SVal ) const; void verifyMatch(CheckerContext , const SVal , const MemRegion *Cont) const; void verifyMatch(CheckerContext , const SVal , const SVal ) const; - + IteratorPosition shiftPosition(CheckerContext , OverloadedOperatorKind Op, + const IteratorPosition , + const SVal ) const; void reportOutOfRangeBug(const StringRef , const SVal , CheckerContext , ExplodedNode *ErrNode) const; void reportMismatchedBug(const StringRef , const SVal , @@ -388,7 +391,8 @@ bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); bool isBoundThroughLazyCompoundVal(const Environment , const MemRegion *Reg); -bool isOutOfRange(ProgramStateRef State, const IteratorPosition ); +bool isOutOfRange(ProgramStateRef State, const IteratorPosition , + bool IncludeEnd = false); bool isZero(ProgramStateRef State, const NonLoc ); } // namespace @@ -423,19 +427,37 @@ } } if (ChecksEnabled[CK_IteratorRangeChecker] && +