diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -31,47 +31,43 @@ class ContainerModeling : public Checker { - void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal, - const SVal &Cont) const; - void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal, - const SVal &Cont) const; - void handleAssignment(CheckerContext &C, const SVal &Cont, - const Expr *CE = nullptr, - const SVal &OldCont = UndefinedVal()) const; - void handleAssign(CheckerContext &C, const SVal &Cont) const; - void handleClear(CheckerContext &C, const SVal &Cont) const; - void handlePushBack(CheckerContext &C, const SVal &Cont) const; - void handlePopBack(CheckerContext &C, const SVal &Cont) const; - void handlePushFront(CheckerContext &C, const SVal &Cont) const; - void handlePopFront(CheckerContext &C, const SVal &Cont) const; - void handleInsert(CheckerContext &C, const SVal &Cont, - const SVal &Iter) const; - void handleErase(CheckerContext &C, const SVal &Cont, const SVal &Iter) const; - void handleErase(CheckerContext &C, const SVal &Cont, const SVal &Iter1, - const SVal &Iter2) const; - void handleEraseAfter(CheckerContext &C, const SVal &Cont, - const SVal &Iter) const; - void handleEraseAfter(CheckerContext &C, const SVal &Cont, const SVal &Iter1, - const SVal &Iter2) const; + void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal, + SVal Cont) const; + void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal, + SVal Cont) const; + void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr, + SVal OldCont = UndefinedVal()) const; + void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; + void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const; + void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const; + void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const; + void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const; + void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1, + SVal Iter2) const; + const NoteTag *getChangeTag(CheckerContext &C, StringRef Text, + const MemRegion *ContReg, + const Expr *ContE) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; public: - ContainerModeling() {} + ContainerModeling() = default; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; - typedef void (ContainerModeling::*NoItParamFn)(CheckerContext &, - const SVal &) const; - typedef void (ContainerModeling::*OneItParamFn)(CheckerContext &, - const SVal &, - const SVal &) const; - typedef void (ContainerModeling::*TwoItParamFn)(CheckerContext &, - const SVal &, - const SVal &, - const SVal &) const; + using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, + const Expr *) const; + using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, + SVal) const; + using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal, + SVal) const; CallDescriptionMap NoIterParamFunctions = { {{0, "clear", 0}, @@ -184,7 +180,8 @@ if (const auto *InstCall = dyn_cast(&Call)) { const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call); if (Handler0) { - (this->**Handler0)(C, InstCall->getCXXThisVal()); + (this->**Handler0)(C, InstCall->getCXXThisVal(), + InstCall->getCXXThisExpr()); return; } @@ -259,7 +256,7 @@ } void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, - const SVal &RetVal, const SVal &Cont) const { + SVal RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -281,7 +278,7 @@ } void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE, - const SVal &RetVal, const SVal &Cont) const { + SVal RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -302,9 +299,8 @@ C.addTransition(State); } -void ContainerModeling::handleAssignment(CheckerContext &C, const SVal &Cont, - const Expr *CE, - const SVal &OldCont) const { +void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont, + const Expr *CE, SVal OldCont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -379,8 +375,8 @@ C.addTransition(State); } -void ContainerModeling::handleAssign(CheckerContext &C, - const SVal &Cont) const { +void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -393,7 +389,8 @@ C.addTransition(State); } -void ContainerModeling::handleClear(CheckerContext &C, const SVal &Cont) const { +void ContainerModeling::handleClear(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -415,12 +412,14 @@ } } } + const NoteTag *ChangeTag = + getChangeTag(C, "became empty", ContReg, ContE); State = invalidateAllIteratorPositions(State, ContReg); - C.addTransition(State); + C.addTransition(State, ChangeTag); } -void ContainerModeling::handlePushBack(CheckerContext &C, - const SVal &Cont) const { +void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -452,13 +451,15 @@ nonloc::SymbolVal(EndSym), nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), SymMgr.getType(EndSym)).getAsSymbol(); + const NoteTag *ChangeTag = + getChangeTag(C, "extended to the back by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); + C.addTransition(State, ChangeTag); } - C.addTransition(State); } -void ContainerModeling::handlePopBack(CheckerContext &C, - const SVal &Cont) const { +void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -479,6 +480,8 @@ nonloc::SymbolVal(EndSym), nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), SymMgr.getType(EndSym)).getAsSymbol(); + const NoteTag *ChangeTag = + getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE); // For vector-like and deque-like containers invalidate the last and the // past-end iterator positions. For list-like containers only invalidate // the last position @@ -491,12 +494,12 @@ } auto newEndSym = BackSym; State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); - C.addTransition(State); + C.addTransition(State, ChangeTag); } } -void ContainerModeling::handlePushFront(CheckerContext &C, - const SVal &Cont) const { +void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -522,14 +525,16 @@ nonloc::SymbolVal(BeginSym), nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), SymMgr.getType(BeginSym)).getAsSymbol(); + const NoteTag *ChangeTag = + getChangeTag(C, "extended to the front by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); - C.addTransition(State); + C.addTransition(State, ChangeTag); } } } -void ContainerModeling::handlePopFront(CheckerContext &C, - const SVal &Cont) const { +void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont, + const Expr *ContE) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -557,13 +562,15 @@ nonloc::SymbolVal(BeginSym), nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), SymMgr.getType(BeginSym)).getAsSymbol(); + const NoteTag *ChangeTag = + getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); - C.addTransition(State); + C.addTransition(State, ChangeTag); } } -void ContainerModeling::handleInsert(CheckerContext &C, const SVal &Cont, - const SVal &Iter) const { +void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont, + SVal Iter) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -593,8 +600,8 @@ } } -void ContainerModeling::handleErase(CheckerContext &C, const SVal &Cont, - const SVal &Iter) const { +void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, + SVal Iter) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -627,9 +634,8 @@ C.addTransition(State); } -void ContainerModeling::handleErase(CheckerContext &C, const SVal &Cont, - const SVal &Iter1, - const SVal &Iter2) const { +void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1, + SVal Iter2) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -664,8 +670,8 @@ C.addTransition(State); } -void ContainerModeling::handleEraseAfter(CheckerContext &C, const SVal &Cont, - const SVal &Iter) const { +void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, + SVal Iter) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Iter); if (!Pos) @@ -685,9 +691,8 @@ C.addTransition(State); } -void ContainerModeling::handleEraseAfter(CheckerContext &C, const SVal &Cont, - const SVal &Iter1, - const SVal &Iter2) const { +void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, + SVal Iter1, SVal Iter2) const { auto State = C.getState(); const auto *Pos1 = getIteratorPosition(State, Iter1); const auto *Pos2 = getIteratorPosition(State, Iter2); @@ -700,6 +705,30 @@ C.addTransition(State); } +const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C, + StringRef Text, + const MemRegion *ContReg, + const Expr *ContE) const { + StringRef Name; + // First try to get the name of the variable from the region + if (const auto *DR = dyn_cast(ContReg)) { + Name = DR->getDecl()->getName(); + // If the region is not a `DeclRegion` then use the expression instead + } else if (const auto *DRE = + dyn_cast(ContE->IgnoreParenCasts())) { + Name = DRE->getDecl()->getName(); + } + + return C.getNoteTag( + [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string { + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" ) + << Text; + return std::string(Out.str()); + }); +} + void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { auto ContMap = State->get(); 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 @@ -221,7 +221,12 @@ ExplodedNode *ErrNode) const { auto R = std::make_unique(*OutOfRangeBugType, Message, ErrNode); + + const auto *Pos = getIteratorPosition(C.getState(), Val); + assert(Pos && "Iterator without known position cannot be out-of-range."); + R->markInteresting(Val); + R->markInteresting(Pos->getContainer()); C.emitReport(std::move(R)); } diff --git a/clang/test/Analysis/container-modeling.cpp b/clang/test/Analysis/container-modeling.cpp --- a/clang/test/Analysis/container-modeling.cpp +++ b/clang/test/Analysis/container-modeling.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -analyzer-output=text -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -analyzer-output=text -verify // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true %s 2>&1 | FileCheck %s @@ -20,14 +20,16 @@ V.begin(); clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); - clang_analyzer_express(clang_analyzer_container_begin(V)); //expected-warning{{$V.begin()}} + clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}} + // expected-note@-1{{$V.begin()}} } void end(const std::vector &V) { V.end(); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); - clang_analyzer_express(clang_analyzer_container_end(V)); //expected-warning{{$V.end()}} + clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end()}} + // expected-note@-1{{$V.end()}} } //////////////////////////////////////////////////////////////////////////////// @@ -48,8 +50,10 @@ long B2 = clang_analyzer_container_begin(V2); long E2 = clang_analyzer_container_end(V2); V1 = std::move(V2); - clang_analyzer_eval(clang_analyzer_container_begin(V1) == B2); //expected-warning{{TRUE}} - clang_analyzer_eval(clang_analyzer_container_end(V2) == E2); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_container_begin(V1) == B2); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} + clang_analyzer_eval(clang_analyzer_container_end(V2) == E2); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} } //////////////////////////////////////////////////////////////////////////////// @@ -60,9 +64,11 @@ /// push_back() /// -/// Design decision: extends containers to the ->RIGHT-> (i.e. the +/// Design decision: extends containers to the ->BACK-> (i.e. the /// past-the-end position of the container is incremented). +void clang_analyzer_dump(void*); + void push_back(std::vector &V, int n) { V.cbegin(); V.cend(); @@ -70,15 +76,18 @@ clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); - V.push_back(n); + V.push_back(n); // expected-note{{Container 'V' extended to the back by 1 position}} + // expected-note@-1{{Container 'V' extended to the back by 1 position}} clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}} + // expected-note@-1{{$V.begin()}} clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}} + // expected-note@-1{{$V.end() + 1}} } /// emplace_back() /// -/// Design decision: extends containers to the ->RIGHT-> (i.e. the +/// Design decision: extends containers to the ->BACK-> (i.e. the /// past-the-end position of the container is incremented). void emplace_back(std::vector &V, int n) { @@ -88,15 +97,18 @@ clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); - V.emplace_back(n); + V.emplace_back(n); // expected-note 2{{Container 'V' extended to the back by 1 position}} + clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}} + // expected-note@-1{{$V.begin()}} clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}} + // expected-note@-1{{$V.end() + 1}} } /// pop_back() /// -/// Design decision: shrinks containers to the <-LEFT<- (i.e. the +/// Design decision: shrinks containers to the <-FRONT<- (i.e. the /// past-the-end position of the container is decremented). void pop_back(std::vector &V, int n) { @@ -106,66 +118,133 @@ clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); - V.pop_back(); + V.pop_back(); // expected-note 2{{Container 'V' shrank from the back by 1 position}} + clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}} + // expected-note@-1{{$V.begin()}} clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() - 1}} + // expected-note@-1{{$V.end() - 1}} } /// push_front() /// -/// Design decision: extends containers to the <-LEFT<- (i.e. the first +/// Design decision: extends containers to the <-FRONT<- (i.e. the first /// position of the container is decremented). -void push_front(std::deque &D, int n) { - D.cbegin(); - D.cend(); +void push_front(std::list &L, int n) { + L.cbegin(); + L.cend(); - clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); - clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); - D.push_front(n); + L.push_front(n); // expected-note 2{{Container 'L' extended to the front by 1 position}} - clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin()}} FIXME: Should be $D.begin() - 1 (to correctly track the container's size) - clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}} + clang_analyzer_express(clang_analyzer_container_begin(L)); // expected-warning{{$L.begin() - 1}} + // expected-note@-1{{$L.begin() - 1}} + clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}} + // expected-note@-1{{$L.end()}} } /// emplace_front() /// -/// Design decision: extends containers to the <-LEFT<- (i.e. the first +/// Design decision: extends containers to the <-FRONT<- (i.e. the first /// position of the container is decremented). -void deque_emplace_front(std::deque &D, int n) { - D.cbegin(); - D.cend(); +void emplace_front(std::list &L, int n) { + L.cbegin(); + L.cend(); - clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); - clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); - D.emplace_front(n); + L.emplace_front(n); // expected-note 2{{Container 'L' extended to the front by 1 position}} - clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin()}} FIXME: Should be $D.begin - 1 (to correctly track the container's size) - clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}} + clang_analyzer_express(clang_analyzer_container_begin(L)); // expected-warning{{$L.begin() - 1}} + // expected-note@-1{{$L.begin() - 1}} + clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}} + // expected-note@-1{{$L.end()}} } /// pop_front() /// -/// Design decision: shrinks containers to the ->RIGHT-> (i.e. the first +/// Design decision: shrinks containers to the ->BACK-> (i.e. the first /// position of the container is incremented). -void deque_pop_front(std::deque &D, int n) { - D.cbegin(); - D.cend(); +void pop_front(std::list &L, int n) { + L.cbegin(); + L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + L.pop_front(); // expected-note 2{{Container 'L' shrank from the front by 1 position}} + + clang_analyzer_express(clang_analyzer_container_begin(L)); // expected-warning{{$L.begin() + 1}} + // expected-note@-1{{$L.begin() + 1}} + clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}} + // expected-note@-1{{$L.end()}} +} + +//////////////////////////////////////////////////////////////////////////////// +/// +/// O T H E R T E S T S +/// +//////////////////////////////////////////////////////////////////////////////// + +/// Track local variable - clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); - clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); +void push_back() { + std::vector V; + V.end(); + + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); - D.pop_front(); + V.push_back(1); // expected-note{{Container 'V' extended to the back by 1 position}} - clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin() + 1}} - clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}} + clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}} + // expected-note@-1{{$V.end() + 1}} } +/// Track the right container only + +void push_back1(std::vector &V1, std::vector &V2, int n) { + V1.cbegin(); + V1.cend(); + V2.cbegin(); + V2.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()"); + + V2.push_back(n); // expected-note{{Container 'V2' extended to the back by 1 position}} FIXME: This note should not appear since `V2` is not affected in the "bug" + + clang_analyzer_express(clang_analyzer_container_begin(V1)); // expected-warning{{$V1.begin()}} + // expected-note@-1{{$V1.begin()}} +} + +void push_back2(std::vector &V1, std::vector &V2, int n) { + V1.cbegin(); + V1.cend(); + V2.cbegin(); + V2.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()"); + clang_analyzer_denote(clang_analyzer_container_begin(V2), "$V2.begin()"); + + V1.push_back(n); // expected-note 2{{Container 'V1' extended to the back by 1 position}} + // FIXME: This should appear only once since there is only + // one "bug" where `V1` is affected + + clang_analyzer_express(clang_analyzer_container_begin(V1)); // expected-warning{{$V1.begin()}} + // expected-note@-1{{$V1.begin()}} + + clang_analyzer_express(clang_analyzer_container_begin(V2)); // expected-warning{{$V2.begin()}} + // expected-note@-1{{$V2.begin()}} +} + +/// Print Container Data as Part of the Program State + 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 @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false -analyzer-output=text %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 -analyzer-output=text %s -verify #include "Inputs/system-header-simulator-cxx.h" @@ -32,6 +32,7 @@ void deref_end(const std::vector &V) { auto i = V.end(); *i; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } // Prefix increment - operator++() @@ -59,6 +60,7 @@ void incr_end(const std::vector &V) { auto i = V.end(); ++i; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // Postfix increment - operator++(int) @@ -86,6 +88,7 @@ void end_incr(const std::vector &V) { auto i = V.end(); i++; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // Prefix decrement - operator--() @@ -93,6 +96,7 @@ void decr_begin(const std::vector &V) { auto i = V.begin(); --i; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void decr_behind_begin(const std::vector &V) { @@ -120,6 +124,7 @@ void begin_decr(const std::vector &V) { auto i = V.begin(); i--; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void behind_begin_decr(const std::vector &V) { @@ -168,11 +173,13 @@ void incr_by_2_ahead_of_end(const std::vector &V) { auto i = --V.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 incr_by_2_end(const std::vector &V) { auto i = V.end(); i += 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // Addition - operator+(int) @@ -201,11 +208,13 @@ void incr_by_2_copy_ahead_of_end(const std::vector &V) { auto i = --V.end(); auto j = i + 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } void incr_by_2_copy_end(const std::vector &V) { auto i = V.end(); auto j = i + 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // Subtraction assignment - operator-=(int) @@ -213,11 +222,13 @@ void decr_by_2_begin(const std::vector &V) { auto i = V.begin(); i -= 2; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void decr_by_2_behind_begin(const std::vector &V) { auto i = ++V.begin(); i -= 2; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void decr_by_2_behind_begin_by_2(const std::vector &V) { @@ -246,11 +257,13 @@ void decr_by_2_copy_begin(const std::vector &V) { auto i = V.begin(); auto j = i - 2; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void decr_by_2_copy_behind_begin(const std::vector &V) { auto i = ++V.begin(); auto j = i - 2; // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void decr_by_2_copy_behind_begin_by_2(const std::vector &V) { @@ -303,6 +316,7 @@ void subscript_zero_end(const std::vector &V) { auto i = V.end(); auto j = i[0]; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} } // By negative number @@ -329,7 +343,8 @@ void subscript_negative_end(const std::vector &V) { auto i = V.end(); - auto j = i[-1]; // // expected-warning{{Past-the-end iterator dereferenced}} FIXME: expect no warning + auto j = i[-1]; // expected-warning{{Past-the-end iterator dereferenced}} FIXME: expect no warning + // expected-note@-1{{Past-the-end iterator dereferenced}} } // By positive number @@ -357,6 +372,7 @@ void subscript_positive_end(const std::vector &V) { auto i = V.end(); auto j = i[1]; // expected-warning{{Past-the-end iterator dereferenced}} FIXME: expect warning Iterator incremented behind the past-the-end iterator + // expected-note@-1{{Past-the-end iterator dereferenced}} FIXME: expect note@-1 Iterator incremented behind the past-the-end iterator } // @@ -388,6 +404,7 @@ void advance_plus_1_end(const std::vector &V) { auto i = V.end(); std::advance(i, 1); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::advance() by -1 @@ -395,6 +412,7 @@ void advance_minus_1_begin(const std::vector &V) { auto i = V.begin(); std::advance(i, -1); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void advance_minus_1_behind_begin(const std::vector &V) { @@ -437,11 +455,13 @@ void advance_plus_2_ahead_of_end(const std::vector &V) { auto i = --V.end(); std::advance(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } void advance_plus_2_end(const std::vector &V) { auto i = V.end(); std::advance(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::advance() by -2 @@ -449,11 +469,13 @@ void advance_minus_2_begin(const std::vector &V) { auto i = V.begin(); std::advance(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void advance_minus_2_behind_begin(const std::vector &V) { auto i = ++V.begin(); std::advance(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void advance_minus_2_unknown(const std::vector &V) { @@ -527,6 +549,7 @@ void next_plus_1_end(const std::vector &V) { auto i = V.end(); auto j = std::next(i); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::next() by -1 @@ -534,6 +557,7 @@ void next_minus_1_begin(const std::vector &V) { auto i = V.begin(); auto j = std::next(i, -1); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void next_minus_1_behind_begin(const std::vector &V) { @@ -576,11 +600,13 @@ void next_plus_2_ahead_of_end(const std::vector &V) { auto i = --V.end(); auto j = std::next(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } void next_plus_2_end(const std::vector &V) { auto i = V.end(); auto j = std::next(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::next() by -2 @@ -588,11 +614,13 @@ void next_minus_2_begin(const std::vector &V) { auto i = V.begin(); auto j = std::next(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void next_minus_2_behind_begin(const std::vector &V) { auto i = ++V.begin(); auto j = std::next(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void next_minus_2_unknown(const std::vector &V) { @@ -646,6 +674,7 @@ void prev_plus_1_begin(const std::vector &V) { auto i = V.begin(); auto j = std::prev(i); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void prev_plus_1_behind_begin(const std::vector &V) { @@ -693,6 +722,7 @@ void prev_minus_1_end(const std::vector &V) { auto i = V.end(); auto j = std::prev(i, -1); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::prev() by +2 @@ -700,11 +730,13 @@ void prev_plus_2_begin(const std::vector &V) { auto i = V.begin(); auto j = std::prev(i, 2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void prev_plus_2_behind_begin(const std::vector &V) { auto i = ++V.begin(); auto j = std::prev(i, 2); // expected-warning{{Iterator decremented ahead of its valid range}} + // expected-note@-1{{Iterator decremented ahead of its valid range}} } void prev_plus_2_unknown(const std::vector &V) { @@ -742,11 +774,13 @@ void prev_minus_2_ahead_of_end(const std::vector &V) { auto i = --V.end(); auto j = std::prev(i, -2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } void prev_minus_2_end(const std::vector &V) { auto i = V.end(); auto j = std::prev(i, -2); // expected-warning{{Iterator incremented behind the past-the-end iterator}} + // expected-note@-1{{Iterator incremented behind the past-the-end iterator}} } // std::prev() by 0 @@ -793,5 +827,17 @@ void arrow_deref_end(const std::vector &V) { auto i = V.end(); - int n = i->n; // expected-warning{{Past-the-end iterator dereferenced}} + int n = i->n; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} +} + +// Container modification - test path notes + +void deref_end_after_pop_back(std::vector &V) { + const auto i = --V.end(); + + V.pop_back(); // expected-note{{Container 'V' shrank from the back by 1 position}} + + *i; // expected-warning{{Past-the-end iterator dereferenced}} + // expected-note@-1{{Past-the-end iterator dereferenced}} }