baloghadamsoftware updated this revision to Diff 144878.
baloghadamsoftware added a comment.
Herald added a reviewer: george.karpenkov.

Rebased.


https://reviews.llvm.org/D32904

Files:
  lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
  test/Analysis/Inputs/system-header-simulator-cxx.h
  test/Analysis/diagnostics/explicit-suppression.cpp
  test/Analysis/invalidated-iterator.cpp
  test/Analysis/mismatched-iterator.cpp

Index: test/Analysis/mismatched-iterator.cpp
===================================================================
--- test/Analysis/mismatched-iterator.cpp
+++ test/Analysis/mismatched-iterator.cpp
@@ -3,6 +3,40 @@
 
 #include "Inputs/system-header-simulator-cxx.h"
 
+void good_insert1(std::vector<int> &v, int n) {
+  v.insert(v.cbegin(), n); // no-warning
+}
+
+
+void good_insert2(std::vector<int> &v, int len, int n) {
+  v.insert(v.cbegin(), len, n); // no-warning
+}
+
+void good_insert3(std::vector<int> &v1, std::vector<int> &v2) {
+  v1.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // no-warning
+}
+
+void good_insert4(std::vector<int> &v, int len, int n) {
+  v.insert(v.cbegin(), {n-1, n, n+1}); // no-warning
+}
+
+void good_insert_find(std::vector<int> &v, int n, int m) {
+  auto i = std::find(v.cbegin(), v.cend(), n);
+  v.insert(i, m); // no-warning
+}
+
+void good_erase1(std::vector<int> &v) {
+  v.erase(v.cbegin()); // no-warning
+}
+
+void good_erase2(std::vector<int> &v) {
+  v.erase(v.cbegin(), v.cend()); // no-warning
+}
+
+void good_emplace(std::vector<int> &v, int n) {
+  v.emplace(v.cbegin(), n); // no-warning
+}
+
 void good_ctor(std::vector<int> &v) {
   std::vector<int> new_v(v.cbegin(), v.cend()); // no-warning
 }
@@ -25,6 +59,38 @@
   std::find(i0, v1.cend(), n); // no-warning
 }
 
+void bad_insert1(std::vector<int> &v1, std::vector<int> &v2, int n) {
+  v2.insert(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_insert2(std::vector<int> &v1, std::vector<int> &v2, int len, int n) {
+  v2.insert(v1.cbegin(), len, n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_insert3(std::vector<int> &v1, std::vector<int> &v2) {
+  v2.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}}
+  v1.insert(v1.cbegin(), v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}}
+  v1.insert(v1.cbegin(), v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_insert4(std::vector<int> &v1, std::vector<int> &v2, int len, int n) {
+  v2.insert(v1.cbegin(), {n-1, n, n+1}); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_erase1(std::vector<int> &v1, std::vector<int> &v2) {
+  v2.erase(v1.cbegin()); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_erase2(std::vector<int> &v1, std::vector<int> &v2) {
+  v2.erase(v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}}
+  v2.erase(v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}}
+  v2.erase(v1.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_emplace(std::vector<int> &v1, std::vector<int> &v2, int n) {
+  v2.emplace(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}}
+}
+
 void good_move_find2(std::vector<int> &v1, std::vector<int> &v2, int n) {
   auto i0 = --v2.cend();
   v1 = std::move(v2);
@@ -61,6 +127,35 @@
   std::find(i0, v2.cend(), n); // expected-warning{{Iterator access mismatched}}
 }
 
+void bad_insert_find(std::vector<int> &v1, std::vector<int> &v2, int n, int m) {
+  auto i = std::find(v1.cbegin(), v1.cend(), n);
+  v2.insert(i, m); // expected-warning{{Iterator access mismatched}}
+}
+
+void good_overwrite(std::vector<int> &v1, std::vector<int> &v2, int n) {
+  auto i = v1.cbegin();
+  i = v2.cbegin();
+  v2.insert(i, n); // no-warning
+}
+
+void bad_overwrite(std::vector<int> &v1, std::vector<int> &v2, int n) {
+  auto i = v1.cbegin();
+  i = v2.cbegin();
+  v1.insert(i, n); // expected-warning{{Iterator access mismatched}}
+}
+
+void good_move(std::vector<int> &v1, std::vector<int> &v2) {
+  const auto i0 = ++v2.cbegin();
+  v1 = std::move(v2);
+  v1.erase(i0); // no-warning
+}
+
+void bad_move(std::vector<int> &v1, std::vector<int> &v2) {
+  const auto i0 = ++v2.cbegin();
+  v1 = std::move(v2);
+  v2.erase(i0); // expected-warning{{Iterator access mismatched}}
+}
+
 void bad_move_find2(std::vector<int> &v1, std::vector<int> &v2, int n) {
   auto i0 = --v2.cend();
   v1 = std::move(v2);
Index: test/Analysis/invalidated-iterator.cpp
===================================================================
--- test/Analysis/invalidated-iterator.cpp
+++ test/Analysis/invalidated-iterator.cpp
@@ -31,6 +31,56 @@
   *i0; // expected-warning{{Invalidated iterator accessed}}
 }
 
+void bad_assign_list1(std::list<int> &L, int n) {
+  auto i0 = L.cbegin();
+  L.assign(10, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_assign_vector1(std::vector<int> &V, int n) {
+  auto i0 = V.cbegin();
+  V.assign(10, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_assign_deque1(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin();
+  D.assign(10, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_assign_forward_list1(std::forward_list<int> &FL, int n) {
+  auto i0 = FL.cbegin();
+  FL.assign(10, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_clear_list1(std::list<int> &L) {
+  auto i0 = L.cend();
+  L.clear();
+  --i0; // no-warning
+}
+
+void bad_clear_list1(std::list<int> &L) {
+  auto i0 = L.cbegin(), i1 = L.cend();
+  L.clear();
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_clear_vector1(std::vector<int> &V) {
+  auto i0 = V.cbegin(), i1 = V.cend();
+  V.clear();
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  --i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_clear_deque1(std::deque<int> &D) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.clear();
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  --i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
 void good_push_back_list1(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend();
   L.push_back(n);
@@ -197,3 +247,153 @@
   FL.pop_front();
   *i0; // expected-warning{{Invalidated iterator accessed}}
 }
+
+void good_insert_list1(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  L.insert(i1, n);
+  *i0; // no-warning
+  *i1; // no-warning
+}
+
+void good_insert_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, n);
+  *i0; // no-warning
+}
+
+void bad_insert_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, n);
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_insert_deque1(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_emplace_list1(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  L.emplace(i1, n);
+  *i0; // no-warning
+  *i1; // no-warning
+}
+
+void good_emplace_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.emplace(i1, n);
+  *i0; // no-warning
+}
+
+void bad_emplace_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.emplace(i1, n);
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_emplace_deque1(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.emplace(i1, n);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_list1(std::list<int> &L) {
+  auto i2 = L.cbegin(), i0 = i2++, i1 = i2++;
+  L.erase(i1);
+  *i0; // no-warning
+  *i2; // no-warning
+}
+
+void bad_erase_list1(std::list<int> &L) {
+  auto i0 = L.cbegin();
+  L.erase(i0);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_vector1(std::vector<int> &V) {
+  auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
+  V.erase(i1);
+  *i0; // no-warning
+}
+
+void bad_erase_vector1(std::vector<int> &V) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.erase(i0);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_erase_deque1(std::deque<int> &D) {
+  auto i2 = D.cbegin(), i0 = i2++, i1 = i2++;
+  D.erase(i1);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+  *i2; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_list2(std::list<int> &L) {
+  auto i3 = L.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  L.erase(i1, i3);
+  *i0; // no-warning
+  *i3; // no-warning
+}
+
+void bad_erase_list2(std::list<int> &L) {
+  auto i2 = L.cbegin(), i0 = i2++, i1 = i2++;
+  L.erase(i0, i2);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_vector2(std::vector<int> &V) {
+  auto i3 = V.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;;
+  V.erase(i1, i3);
+  *i0; // no-warning
+}
+
+void bad_erase_vector2(std::vector<int> &V) {
+  auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
+  V.erase(i0, i2);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+  *i2; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void bad_erase_deque2(std::deque<int> &D) {
+  auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  D.erase(i1, i3);
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+  *i2; // expected-warning{{Invalidated iterator accessed}}
+  *i3; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_after_forward_list1(std::forward_list<int> &FL) {
+  auto i2 = FL.cbegin(), i0 = i2++, i1 = i2++;
+  FL.erase_after(i0);
+  *i0; // no-warning
+  *i2; // no-warning
+}
+
+void bad_erase_after_forward_list1(std::forward_list<int> &FL) {
+  auto i1 = FL.cbegin(), i0 = i1++;
+  FL.erase_after(i0);
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+}
+
+void good_erase_after_forward_list2(std::forward_list<int> &FL) {
+  auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  FL.erase_after(i0, i3);
+  *i0; // no-warning
+  *i3; // no-warning
+}
+
+void bad_erase_after_forward_list2(std::forward_list<int> &FL) {
+  auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  FL.erase_after(i0, i3);
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+  *i2; // expected-warning{{Invalidated iterator accessed}}
+}
Index: test/Analysis/diagnostics/explicit-suppression.cpp
===================================================================
--- test/Analysis/diagnostics/explicit-suppression.cpp
+++ test/Analysis/diagnostics/explicit-suppression.cpp
@@ -19,6 +19,6 @@
 void testCopyNull(C *I, C *E) {
   std::copy(I, E, (C *)0);
 #ifndef SUPPRESSED
-  // expected-warning@../Inputs/system-header-simulator-cxx.h:566 {{Called C++ object pointer is null}}
+  // expected-warning@../Inputs/system-header-simulator-cxx.h:627 {{Called C++ object pointer is null}}
 #endif
 }
Index: test/Analysis/Inputs/system-header-simulator-cxx.h
===================================================================
--- test/Analysis/Inputs/system-header-simulator-cxx.h
+++ test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -269,6 +269,21 @@
     void emplace_back(Args&&... args);
     void pop_back();
 
+    iterator insert(const_iterator position, const value_type &val);
+    iterator insert(const_iterator position, size_type n,
+                    const value_type &val);
+    template <typename InputIterator>
+    iterator insert(const_iterator position, InputIterator first,
+                    InputIterator last);
+    iterator insert(const_iterator position, value_type &&val);
+    iterator insert(const_iterator position, initializer_list<value_type> il);
+
+    template <class... Args>
+    iterator emplace(const_iterator position, Args&&... args);
+
+    iterator erase(const_iterator position);
+    iterator erase(const_iterator first, const_iterator last);
+
     T &operator[](size_t n) {
       return _start[n];
     }
@@ -331,6 +346,21 @@
     void emplace_front(Args&&... args);
     void pop_front();
 
+    iterator insert(const_iterator position, const value_type &val);
+    iterator insert(const_iterator position, size_type n,
+                    const value_type &val);
+    template <typename InputIterator>
+    iterator insert(const_iterator position, InputIterator first,
+                    InputIterator last);
+    iterator insert(const_iterator position, value_type &&val);
+    iterator insert(const_iterator position, initializer_list<value_type> il);
+
+    template <class... Args>
+    iterator emplace(const_iterator position, Args&&... args);
+
+    iterator erase(const_iterator position);
+    iterator erase(const_iterator first, const_iterator last);
+
     iterator begin() { return iterator(_start); }
     const_iterator begin() const { return const_iterator(_start); }
     const_iterator cbegin() const { return const_iterator(_start); }
@@ -389,6 +419,21 @@
     void emplace_front(Args&&... args);
     void pop_front();
 
+    iterator insert(const_iterator position, const value_type &val);
+    iterator insert(const_iterator position, size_type n,
+                    const value_type &val);
+    template <typename InputIterator>
+    iterator insert(const_iterator position, InputIterator first,
+                    InputIterator last);
+    iterator insert(const_iterator position, value_type &&val);
+    iterator insert(const_iterator position, initializer_list<value_type> il);
+
+    template <class... Args>
+    iterator emplace(const_iterator position, Args&&... args);
+
+    iterator erase(const_iterator position);
+    iterator erase(const_iterator first, const_iterator last);
+
     T &operator[](size_t n) {
       return _start[n];
     }
@@ -445,6 +490,22 @@
     void emplace_front(Args&&... args);
     void pop_front();
 
+    iterator insert_after(const_iterator position, const value_type &val);
+    iterator insert_after(const_iterator position, value_type &&val);
+    iterator insert_after(const_iterator position, size_type n,
+                          const value_type &val);
+    template <typename InputIterator>
+    iterator insert_after(const_iterator position, InputIterator first,
+                          InputIterator last);
+    iterator insert_after(const_iterator position,
+                          initializer_list<value_type> il);
+
+    template <class... Args>
+    iterator emplace_after(const_iterator position, Args&&... args);
+
+    iterator erase_after(const_iterator position);
+    iterator erase_after(const_iterator first, const_iterator last);
+
     iterator begin() { return iterator(_start); }
     const_iterator begin() const { return const_iterator(_start); }
     const_iterator cbegin() const { return const_iterator(_start); }
Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -199,7 +199,7 @@
     : public Checker<check::PreCall, check::PostCall,
                      check::PreStmt<CXXConstructExpr>,
                      check::PreStmt<CXXOperatorCallExpr>,
-                     check::PostStmt<MaterializeTemporaryExpr>,
+                     check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
                      check::LiveSymbols, check::DeadSymbols,
                      eval::Assume> {
 
@@ -227,10 +227,18 @@
   void handleAssign(CheckerContext &C, const SVal &Cont,
                     const Expr *CE = nullptr,
                     const SVal &OldCont = UndefinedVal()) 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 &Iter) const;
+  void handleErase(CheckerContext &C, const SVal &Iter) const;
+  void handleErase(CheckerContext &C, const SVal &Iter1,
+                   const SVal &Iter2) const;
+  void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
+  void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
+                        const SVal &Iter2) const;
   void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
                               const SVal &RetVal, const SVal &LHS,
                               const SVal &RHS) const;
@@ -267,6 +275,9 @@
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
   void checkPreStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
   void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
+  void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
+  void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
+  void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
                      CheckerContext &C) const;
   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
@@ -292,12 +303,18 @@
 bool isComparisonOperator(OverloadedOperatorKind OK);
 bool isBeginCall(const FunctionDecl *Func);
 bool isEndCall(const FunctionDecl *Func);
+bool isAssignCall(const FunctionDecl *Func);
+bool isClearCall(const FunctionDecl *Func);
 bool isPushBackCall(const FunctionDecl *Func);
 bool isEmplaceBackCall(const FunctionDecl *Func);
 bool isPopBackCall(const FunctionDecl *Func);
 bool isPushFrontCall(const FunctionDecl *Func);
 bool isEmplaceFrontCall(const FunctionDecl *Func);
 bool isPopFrontCall(const FunctionDecl *Func);
+bool isInsertCall(const FunctionDecl *Func);
+bool isEraseCall(const FunctionDecl *Func);
+bool isEraseAfterCall(const FunctionDecl *Func);
+bool isEmplaceCall(const FunctionDecl *Func);
 bool isAssignmentOperator(OverloadedOperatorKind OK);
 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
 bool isAccessOperator(OverloadedOperatorKind OK);
@@ -344,9 +361,18 @@
                                         bool Equal);
 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
                                                const MemRegion *Cont);
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+                                     const MemRegion *Cont, SymbolRef Offset,
+                                     BinaryOperator::Opcode Opc);
 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
                                             SymbolRef Offset,
                                             BinaryOperator::Opcode Opc);
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+                                            SymbolRef Offset1,
+                                            BinaryOperator::Opcode Opc1,
+                                            SymbolRef Offset2,
+                                            BinaryOperator::Opcode Opc2);
 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
                                              const MemRegion *Cont,
                                              const MemRegion *NewCont);
@@ -443,6 +469,33 @@
         verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
       }
     }
+  } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+    if (!ChecksEnabled[CK_MismatchedIteratorChecker])
+      return;
+
+    const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
+    if (!ContReg)
+      return;
+    // Check for erase, insert and emplace using iterator of another container
+    if (isEraseCall(Func) || isEraseAfterCall(Func)) {
+      verifyMatch(C, Call.getArgSVal(0),
+                  InstCall->getCXXThisVal().getAsRegion());
+      if (Call.getNumArgs() == 2) {
+        verifyMatch(C, Call.getArgSVal(1),
+                    InstCall->getCXXThisVal().getAsRegion());
+      }
+    } else if (isInsertCall(Func)) {
+      verifyMatch(C, Call.getArgSVal(0),
+                  InstCall->getCXXThisVal().getAsRegion());
+      if (Call.getNumArgs() == 3 &&
+          isIteratorType(Call.getArgExpr(1)->getType()) &&
+          isIteratorType(Call.getArgExpr(2)->getType())) {
+        verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
+      }
+    } else if (isEmplaceCall(Func)) {
+      verifyMatch(C, Call.getArgSVal(0),
+                  InstCall->getCXXThisVal().getAsRegion());
+    }
   } else if (!isa<CXXConstructorCall>(&Call)) {
     // The main purpose of iterators is to abstract away from different
     // containers and provide a (maybe limited) uniform access to them.
@@ -563,14 +616,32 @@
     }
   } else {
     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
-      if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
+      if (isAssignCall(Func)) {
+        handleAssign(C, InstCall->getCXXThisVal());
+      } else if (isClearCall(Func)) {
+        handleClear(C, InstCall->getCXXThisVal());
+      } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
         handlePushBack(C, InstCall->getCXXThisVal());
       } else if (isPopBackCall(Func)) {
         handlePopBack(C, InstCall->getCXXThisVal());
       } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
         handlePushFront(C, InstCall->getCXXThisVal());
       } else if (isPopFrontCall(Func)) {
         handlePopFront(C, InstCall->getCXXThisVal());
+      } else if (isInsertCall(Func) || isEmplaceCall(Func)) {
+        handleInsert(C, Call.getArgSVal(0));
+      } else if (isEraseCall(Func)) {
+        if (Call.getNumArgs() == 1) {
+          handleErase(C, Call.getArgSVal(0));
+        } else if (Call.getNumArgs() == 2) {
+          handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
+        }
+      } else if (isEraseAfterCall(Func)) {
+        if (Call.getNumArgs() == 1) {
+          handleEraseAfter(C, Call.getArgSVal(0));
+        } else if (Call.getNumArgs() == 2) {
+          handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
+        }
       }
     }
 
@@ -675,6 +746,22 @@
   }
 }
 
+void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
+                                CheckerContext &C) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Val);
+  if (Pos) {
+    State = setIteratorPosition(State, Loc, *Pos);
+    C.addTransition(State);
+  } else {
+    const auto *OldPos = getIteratorPosition(State, Loc);
+    if (OldPos) {
+      State = removeIteratorPosition(State, Loc);
+      C.addTransition(State);
+    }
+  }
+}
+
 void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
                                     CheckerContext &C) const {
   /* Transfer iterator state to temporary objects */
@@ -1206,6 +1293,33 @@
   C.addTransition(State);
 }
 
+void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
+  const auto *ContReg = Cont.getAsRegion();
+  if (!ContReg)
+    return;
+
+  while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
+    ContReg = CBOR->getSuperRegion();
+  }
+
+  // The clear() operation invalidates all the iterators, except the past-end
+  // iterators of list-like containers
+  auto State = C.getState();
+  if (!hasSubscriptOperator(ContReg) || !backModifiable(ContReg)) {
+    const auto CData = getContainerData(State, ContReg);
+    if (CData) {
+      if (const auto EndSym = CData->getEnd()) {
+        State =
+            invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
+        C.addTransition(State);
+        return;
+      }
+    }
+  }
+  State = invalidateAllIteratorPositions(State, ContReg);
+  C.addTransition(State);
+}
+
 void IteratorChecker::handlePushBack(CheckerContext &C,
                                      const SVal &Cont) const {
   const auto *ContReg = Cont.getAsRegion();
@@ -1355,6 +1469,126 @@
   }
 }
 
+void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Iter);
+  if (!Pos)
+    return;
+
+  // For deque-like containers invalidate all iterator positions. For
+  // vector-like containers invalidate iterator positions after the insertion.
+  const auto *Cont = Pos->getContainer();
+  if (hasSubscriptOperator(Cont) && backModifiable(Cont)) {
+    if (frontModifiable(Cont)) {
+      State = invalidateAllIteratorPositions(State, Cont);
+    } else {
+      State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+    }
+    if (const auto *CData = getContainerData(State, Cont)) {
+      if (const auto EndSym = CData->getEnd()) {
+        State = invalidateIteratorPositions(State, EndSym, BO_GE);
+        State = setContainerData(State, Cont, CData->newEnd(nullptr));
+      }
+    }
+    C.addTransition(State);
+  }
+}
+
+void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Iter);
+  if (!Pos)
+    return;
+
+  // For deque-like containers invalidate all iterator positions. For
+  // vector-like containers invalidate iterator positions at and after the
+  // deletion. For list-like containers only invalidate the deleted position.
+  const auto *Cont = Pos->getContainer();
+  if (hasSubscriptOperator(Cont) && backModifiable(Cont)) {
+    if (frontModifiable(Cont)) {
+      State = invalidateAllIteratorPositions(State, Cont);
+    } else {
+      State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+    }
+    if (const auto *CData = getContainerData(State, Cont)) {
+      if (const auto EndSym = CData->getEnd()) {
+        State = invalidateIteratorPositions(State, EndSym, BO_GE);
+        State = setContainerData(State, Cont, CData->newEnd(nullptr));
+      }
+    }
+  } else {
+    State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
+  }
+  C.addTransition(State);
+}
+
+void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
+                                  const SVal &Iter2) const {
+  auto State = C.getState();
+  const auto *Pos1 = getIteratorPosition(State, Iter1);
+  const auto *Pos2 = getIteratorPosition(State, Iter2);
+  if (!Pos1 || !Pos2)
+    return;
+
+  // For deque-like containers invalidate all iterator positions. For
+  // vector-like containers invalidate iterator positions at and after the
+  // deletion range. For list-like containers only invalidate the deleted
+  // position range [first..last].
+  const auto *Cont = Pos1->getContainer();
+  if (hasSubscriptOperator(Cont) && backModifiable(Cont)) {
+    if (frontModifiable(Cont)) {
+      State = invalidateAllIteratorPositions(State, Cont);
+    } else {
+      State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
+    }
+    if (const auto *CData = getContainerData(State, Cont)) {
+      if (const auto EndSym = CData->getEnd()) {
+        State = invalidateIteratorPositions(State, EndSym, BO_GE);
+        State = setContainerData(State, Cont, CData->newEnd(nullptr));
+      }
+    }
+  } else {
+    State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
+                                        Pos2->getOffset(), BO_LT);
+  }
+  C.addTransition(State);
+}
+
+void IteratorChecker::handleEraseAfter(CheckerContext &C,
+                                       const SVal &Iter) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Iter);
+  if (!Pos)
+    return;
+
+  // Invalidate the deleted iterator position, which is the position of the
+  // parameter plus one.
+  auto &SymMgr = C.getSymbolManager();
+  auto &BVF = SymMgr.getBasicVals();
+  auto &SVB = C.getSValBuilder();
+  const auto NextSym =
+    SVB.evalBinOp(State, BO_Add,
+                  nonloc::SymbolVal(Pos->getOffset()),
+                  nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                  SymMgr.getType(Pos->getOffset())).getAsSymbol();
+  State = invalidateIteratorPositions(State, NextSym, BO_EQ);
+  C.addTransition(State);
+}
+
+void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
+                                       const SVal &Iter2) const {
+  auto State = C.getState();
+  const auto *Pos1 = getIteratorPosition(State, Iter1);
+  const auto *Pos2 = getIteratorPosition(State, Iter2);
+  if (!Pos1 || !Pos2)
+    return;
+
+  // Invalidate the deleted iterator position range (first..last)
+  State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
+                                      Pos2->getOffset(), BO_LT);
+  C.addTransition(State);
+}
+
 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
                                           const SVal &Val, CheckerContext &C,
                                           ExplodedNode *ErrNode) const {
@@ -1474,6 +1708,24 @@
   return IdInfo->getName().endswith_lower("end");
 }
 
+bool isAssignCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() > 2)
+    return false;
+  return IdInfo->getName() == "assign";
+}
+
+bool isClearCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() > 0)
+    return false;
+  return IdInfo->getName() == "clear";
+}
+
 bool isPushBackCall(const FunctionDecl *Func) {
   const auto *IdInfo = Func->getIdentifier();
   if (!IdInfo)
@@ -1528,6 +1780,56 @@
   return IdInfo->getName() == "pop_front";
 }
 
+bool isInsertCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
+    return false;
+  if (!isIteratorType(Func->getParamDecl(0)->getType()))
+    return false;
+  return IdInfo->getName() == "insert";
+}
+
+bool isEmplaceCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() < 2)
+    return false;
+  if (!isIteratorType(Func->getParamDecl(0)->getType()))
+    return false;
+  return IdInfo->getName() == "emplace";
+}
+
+bool isEraseCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+    return false;
+  if (!isIteratorType(Func->getParamDecl(0)->getType()))
+    return false;
+  if (Func->getNumParams() == 2 &&
+      !isIteratorType(Func->getParamDecl(1)->getType()))
+    return false;
+  return IdInfo->getName() == "erase";
+}
+
+bool isEraseAfterCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+    return false;
+  if (!isIteratorType(Func->getParamDecl(0)->getType()))
+    return false;
+  if (Func->getNumParams() == 2 &&
+      !isIteratorType(Func->getParamDecl(1)->getType()))
+    return false;
+  return IdInfo->getName() == "erase_after";
+}
+
 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
 
 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
@@ -1863,6 +2165,20 @@
   return processIteratorPositions(State, MatchCont, Invalidate);
 }
 
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+                                     const MemRegion *Cont, SymbolRef Offset,
+                                     BinaryOperator::Opcode Opc) {
+  auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
+    return Pos.getContainer() == Cont &&
+           !compare(State, Pos.getOffset(), Offset, Opc);
+  };
+  auto Invalidate = [&](const IteratorPosition &Pos) {
+    return Pos.invalidate();
+  };
+  return processIteratorPositions(State, MatchContAndCompare, Invalidate);
+}
+
 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
                                             SymbolRef Offset,
                                             BinaryOperator::Opcode Opc) {
@@ -1875,6 +2191,21 @@
   return processIteratorPositions(State, Compare, Invalidate);
 }
 
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+                                            SymbolRef Offset1,
+                                            BinaryOperator::Opcode Opc1,
+                                            SymbolRef Offset2,
+                                            BinaryOperator::Opcode Opc2) {
+  auto Compare = [&](const IteratorPosition &Pos) {
+    return compare(State, Pos.getOffset(), Offset1, Opc1) &&
+           compare(State, Pos.getOffset(), Offset2, Opc2);
+  };
+  auto Invalidate = [&](const IteratorPosition &Pos) {
+    return Pos.invalidate();
+  };
+  return processIteratorPositions(State, Compare, Invalidate);
+}
+
 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
                                              const MemRegion *Cont,
                                              const MemRegion *NewCont) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to