[clang] a3f4d17 - [Analyzer] Use note tags to track container begin and and changes

Adam Balogh via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 26 00:39:50 PDT 2020


Author: Adam Balogh
Date: 2020-03-26T07:56:28+01:00
New Revision: a3f4d17a1a53c4144a5bb7c14620a5d2790f36ea

URL: https://github.com/llvm/llvm-project/commit/a3f4d17a1a53c4144a5bb7c14620a5d2790f36ea
DIFF: https://github.com/llvm/llvm-project/commit/a3f4d17a1a53c4144a5bb7c14620a5d2790f36ea.diff

LOG: [Analyzer] Use note tags to track container begin and and changes

Container operations such as `push_back()`, `pop_front()`
etc. increment and decrement the abstract begin and end
symbols of containers. This patch introduces note tags
to `ContainerModeling` to track these changes. This helps
the user to better identify the source of errors related
to containers and iterators.

Differential Revision: https://reviews.llvm.org/D73720

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
    clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
    clang/test/Analysis/container-modeling.cpp
    clang/test/Analysis/iterator-range.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
index 7d8dc8b8a0ab..b225a61db439 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -31,47 +31,43 @@ namespace {
 class ContainerModeling
   : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
 
-  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<NoItParamFn> NoIterParamFunctions = {
     {{0, "clear", 0},
@@ -184,7 +180,8 @@ void ContainerModeling::checkPostCall(const CallEvent &Call,
     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&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::checkDeadSymbols(SymbolReaper &SR,
 }
 
 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::handleBegin(CheckerContext &C, const Expr *CE,
 }
 
 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 @@ void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
   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 @@ void ContainerModeling::handleAssignment(CheckerContext &C, const SVal &Cont,
   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 @@ void ContainerModeling::handleAssign(CheckerContext &C,
   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 @@ void ContainerModeling::handleClear(CheckerContext &C, const SVal &Cont) const {
       }
     }
   }
+  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 @@ void ContainerModeling::handlePushBack(CheckerContext &C,
                     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 @@ void ContainerModeling::handlePopBack(CheckerContext &C,
                     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 @@ void ContainerModeling::handlePopBack(CheckerContext &C,
     }
     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 @@ void ContainerModeling::handlePushFront(CheckerContext &C,
                       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 @@ void ContainerModeling::handlePopFront(CheckerContext &C,
                     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::handleInsert(CheckerContext &C, const SVal &Cont,
   }
 }
 
-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 @@ void ContainerModeling::handleErase(CheckerContext &C, const SVal &Cont,
   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 @@ void ContainerModeling::handleErase(CheckerContext &C, const SVal &Cont,
   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 @@ void ContainerModeling::handleEraseAfter(CheckerContext &C, const SVal &Cont,
   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 @@ void ContainerModeling::handleEraseAfter(CheckerContext &C, const SVal &Cont,
   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<DeclRegion>(ContReg)) {
+    Name = DR->getDecl()->getName();
+  // If the region is not a `DeclRegion` then use the expression instead
+  } else if (const auto *DRE =
+             dyn_cast<DeclRefExpr>(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<ContainerMap>();

diff  --git a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
index f9b493bf9bb0..1c0cf0a97bb1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -221,7 +221,12 @@ void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
                                      ExplodedNode *ErrNode) const {
   auto R = std::make_unique<PathSensitiveBugReport>(*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
index 8162ba9039e8..9d63cc2fce69 100644
--- 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 @@ void begin(const std::vector<int> &V) {
   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 at -1{{$V.begin()}}
 }
 
 void end(const std::vector<int> &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 at -1{{$V.end()}}
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -48,8 +50,10 @@ void move_assignment(std::vector<int> &V1, std::vector<int> &V2) {
   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 at -1{{TRUE}}
+  clang_analyzer_eval(clang_analyzer_container_end(V2) == E2); // expected-warning{{TRUE}}
+                                                               // expected-note at -1{{TRUE}}
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -60,9 +64,11 @@ void move_assignment(std::vector<int> &V1, std::vector<int> &V2) {
 
 /// 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<int> &V, int n) {
   V.cbegin();
   V.cend();
@@ -70,15 +76,18 @@ void push_back(std::vector<int> &V, int n) {
   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 at -1{{Container 'V' extended to the back by 1 position}}
 
   clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
+                                                             // expected-note at -1{{$V.begin()}}
   clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}}
+                                                           // expected-note at -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<int> &V, int n) {
@@ -88,15 +97,18 @@ void emplace_back(std::vector<int> &V, int n) {
   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 at -1{{$V.begin()}}
   clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}}
+                                                           // expected-note at -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<int> &V, int n) {
@@ -106,66 +118,133 @@ void pop_back(std::vector<int> &V, int n) {
   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 at -1{{$V.begin()}}
   clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() - 1}}
+                                                           // expected-note at -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<int> &D, int n) {
-  D.cbegin();
-  D.cend();
+void push_front(std::list<int> &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 at -1{{$L.begin() - 1}}
+  clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}}
+                                                           // expected-note at -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<int> &D, int n) {
-  D.cbegin();
-  D.cend();
+void emplace_front(std::list<int> &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 at -1{{$L.begin() - 1}}
+  clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}}
+                                                           // expected-note at -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<int> &D, int n) {
-  D.cbegin();
-  D.cend();
+void pop_front(std::list<int> &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 at -1{{$L.begin() + 1}}
+  clang_analyzer_express(clang_analyzer_container_end(L)); // expected-warning{{$L.end()}}
+                                                           // expected-note at -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<int> 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 at -1{{$V.end() + 1}}
 }
 
+/// Track the right container only
+
+void push_back1(std::vector<int> &V1, std::vector<int> &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 at -1{{$V1.begin()}}
+}
+
+void push_back2(std::vector<int> &V1, std::vector<int> &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 at -1{{$V1.begin()}}
+
+  clang_analyzer_express(clang_analyzer_container_begin(V2)); // expected-warning{{$V2.begin()}}
+                                                             // expected-note at -1{{$V2.begin()}}
+}
+
+/// Print Container Data as Part of the Program State
+
 void clang_analyzer_printState();
 
 void print_state(std::vector<int> &V) {

diff  --git a/clang/test/Analysis/iterator-range.cpp b/clang/test/Analysis/iterator-range.cpp
index babcfdec99d6..bdfb04ba3a8c 100644
--- 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_ahead_of_end(const std::vector<int> &V) {
 void deref_end(const std::vector<int> &V) {
   auto i = V.end();
   *i; // expected-warning{{Past-the-end iterator dereferenced}}
+      // expected-note at -1{{Past-the-end iterator dereferenced}}
 }
 
 // Prefix increment - operator++()
@@ -59,6 +60,7 @@ void incr_ahead_of_end(const std::vector<int> &V) {
 void incr_end(const std::vector<int> &V) {
   auto i = V.end();
   ++i; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+       // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // Postfix increment - operator++(int)
@@ -86,6 +88,7 @@ void ahead_of_end_incr(const std::vector<int> &V) {
 void end_incr(const std::vector<int> &V) {
   auto i = V.end();
   i++; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+       // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // Prefix decrement - operator--()
@@ -93,6 +96,7 @@ void end_incr(const std::vector<int> &V) {
 void decr_begin(const std::vector<int> &V) {
   auto i = V.begin();
   --i; // expected-warning{{Iterator decremented ahead of its valid range}}
+       // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void decr_behind_begin(const std::vector<int> &V) {
@@ -120,6 +124,7 @@ void decr_end(const std::vector<int> &V) {
 void begin_decr(const std::vector<int> &V) {
   auto i = V.begin();
   i--; // expected-warning{{Iterator decremented ahead of its valid range}}
+       // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void behind_begin_decr(const std::vector<int> &V) {
@@ -168,11 +173,13 @@ void incr_by_2_ahead_by_2_of_end(const std::vector<int> &V) {
 void incr_by_2_ahead_of_end(const std::vector<int> &V) {
   auto i = --V.end();
   i += 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+          // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 void incr_by_2_end(const std::vector<int> &V) {
   auto i = V.end();
   i += 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+          // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // Addition - operator+(int)
@@ -201,11 +208,13 @@ void incr_by_2_copy_ahead_by_2_of_end(const std::vector<int> &V) {
 void incr_by_2_copy_ahead_of_end(const std::vector<int> &V) {
   auto i = --V.end();
   auto j = i + 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                  // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 void incr_by_2_copy_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = i + 2; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                  // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // Subtraction assignment - operator-=(int)
@@ -213,11 +222,13 @@ void incr_by_2_copy_end(const std::vector<int> &V) {
 void decr_by_2_begin(const std::vector<int> &V) {
   auto i = V.begin();
   i -= 2; // expected-warning{{Iterator decremented ahead of its valid range}}
+          // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void decr_by_2_behind_begin(const std::vector<int> &V) {
   auto i = ++V.begin();
   i -= 2; // expected-warning{{Iterator decremented ahead of its valid range}}
+          // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void decr_by_2_behind_begin_by_2(const std::vector<int> &V) {
@@ -246,11 +257,13 @@ void decr_by_2_end(const std::vector<int> &V) {
 void decr_by_2_copy_begin(const std::vector<int> &V) {
   auto i = V.begin();
   auto j = i - 2; // expected-warning{{Iterator decremented ahead of its valid range}}
+                  // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void decr_by_2_copy_behind_begin(const std::vector<int> &V) {
   auto i = ++V.begin();
   auto j = i - 2; // expected-warning{{Iterator decremented ahead of its valid range}}
+                  // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void decr_by_2_copy_behind_begin_by_2(const std::vector<int> &V) {
@@ -303,6 +316,7 @@ void subscript_zero_ahead_of_end(const std::vector<int> &V) {
 void subscript_zero_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = i[0]; // expected-warning{{Past-the-end iterator dereferenced}}
+                 // expected-note at -1{{Past-the-end iterator dereferenced}}
 }
 
 // By negative number
@@ -329,7 +343,8 @@ void subscript_negative_ahead_of_end(const std::vector<int> &V) {
 
 void subscript_negative_end(const std::vector<int> &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 at -1{{Past-the-end iterator dereferenced}}
 }
 
 // By positive number
@@ -357,6 +372,7 @@ void subscript_positive_ahead_of_end(const std::vector<int> &V) {
 void subscript_positive_end(const std::vector<int> &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 at -1{{Past-the-end iterator dereferenced}} FIXME: expect note at -1 Iterator incremented behind the past-the-end iterator
 }
 
 //
@@ -388,6 +404,7 @@ void advance_plus_1_ahead_of_end(const std::vector<int> &V) {
 void advance_plus_1_end(const std::vector<int> &V) {
   auto i = V.end();
   std::advance(i, 1); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                      // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::advance() by -1
@@ -395,6 +412,7 @@ void advance_plus_1_end(const std::vector<int> &V) {
 void advance_minus_1_begin(const std::vector<int> &V) {
   auto i = V.begin();
   std::advance(i, -1); // expected-warning{{Iterator decremented ahead of its valid range}}
+                       // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void advance_minus_1_behind_begin(const std::vector<int> &V) {
@@ -437,11 +455,13 @@ void advance_plus_2_unknown(const std::vector<int> &V) {
 void advance_plus_2_ahead_of_end(const std::vector<int> &V) {
   auto i = --V.end();
   std::advance(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                      // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 void advance_plus_2_end(const std::vector<int> &V) {
   auto i = V.end();
   std::advance(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                      // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::advance() by -2
@@ -449,11 +469,13 @@ void advance_plus_2_end(const std::vector<int> &V) {
 void advance_minus_2_begin(const std::vector<int> &V) {
   auto i = V.begin();
   std::advance(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                       // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void advance_minus_2_behind_begin(const std::vector<int> &V) {
   auto i = ++V.begin();
   std::advance(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                       // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void advance_minus_2_unknown(const std::vector<int> &V) {
@@ -527,6 +549,7 @@ void next_plus_1_ahead_of_end(const std::vector<int> &V) {
 void next_plus_1_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = std::next(i); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                         // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::next() by -1
@@ -534,6 +557,7 @@ void next_plus_1_end(const std::vector<int> &V) {
 void next_minus_1_begin(const std::vector<int> &V) {
   auto i = V.begin();
   auto j = std::next(i, -1); // expected-warning{{Iterator decremented ahead of its valid range}}
+                             // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void next_minus_1_behind_begin(const std::vector<int> &V) {
@@ -576,11 +600,13 @@ void next_plus_2_unknown(const std::vector<int> &V) {
 void next_plus_2_ahead_of_end(const std::vector<int> &V) {
   auto i = --V.end();
   auto j = std::next(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                            // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 void next_plus_2_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = std::next(i, 2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                            // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::next() by -2
@@ -588,11 +614,13 @@ void next_plus_2_end(const std::vector<int> &V) {
 void next_minus_2_begin(const std::vector<int> &V) {
   auto i = V.begin();
   auto j = std::next(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                             // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void next_minus_2_behind_begin(const std::vector<int> &V) {
   auto i = ++V.begin();
   auto j = std::next(i, -2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                             // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void next_minus_2_unknown(const std::vector<int> &V) {
@@ -646,6 +674,7 @@ void next_0_end(const std::vector<int> &V) {
 void prev_plus_1_begin(const std::vector<int> &V) {
   auto i = V.begin();
   auto j = std::prev(i); // expected-warning{{Iterator decremented ahead of its valid range}}
+                         // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void prev_plus_1_behind_begin(const std::vector<int> &V) {
@@ -693,6 +722,7 @@ void prev_minus_1_ahead_of_end(const std::vector<int> &V) {
 void prev_minus_1_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = std::prev(i, -1); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                             // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::prev() by +2
@@ -700,11 +730,13 @@ void prev_minus_1_end(const std::vector<int> &V) {
 void prev_plus_2_begin(const std::vector<int> &V) {
   auto i = V.begin();
   auto j = std::prev(i, 2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                            // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void prev_plus_2_behind_begin(const std::vector<int> &V) {
   auto i = ++V.begin();
   auto j = std::prev(i, 2); // expected-warning{{Iterator decremented ahead of its valid range}}
+                            // expected-note at -1{{Iterator decremented ahead of its valid range}}
 }
 
 void prev_plus_2_unknown(const std::vector<int> &V) {
@@ -742,11 +774,13 @@ void prev_minus_2_unknown(const std::vector<int> &V) {
 void prev_minus_2_ahead_of_end(const std::vector<int> &V) {
   auto i = --V.end();
   auto j = std::prev(i, -2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                             // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 void prev_minus_2_end(const std::vector<int> &V) {
   auto i = V.end();
   auto j = std::prev(i, -2); // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+                             // expected-note at -1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 // std::prev() by 0
@@ -793,5 +827,17 @@ void arrow_deref_begin(const std::vector<S> &V) {
 
 void arrow_deref_end(const std::vector<S> &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 at -1{{Past-the-end iterator dereferenced}}
+}
+
+// Container modification - test path notes
+
+void deref_end_after_pop_back(std::vector<int> &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 at -1{{Past-the-end iterator dereferenced}}
 }


        


More information about the cfe-commits mailing list