baloghadamsoftware created this revision.
baloghadamsoftware added reviewers: NoQ, Szelethus.
baloghadamsoftware added a project: clang.
Herald added subscribers: Charusso, gamesh411, donat.nagy, mikhail.ramalho, 
a.sidorin, rnkovacs, szepet, xazax.hun, whisperity.

Iterator bugs such as dereference of out-of-range or any access of invalidated 
iterators are difficult to understand. A `BugReporterVisitor` that marks the 
place where the iterator reached to past-the-end or the first position, became 
invalidated and marks the execution path with a message about the emptiness of 
the container helps much in understanding the actual bug. It also helps 
deciding whether the finding is a true or a false positive. This patch adds 
such a visitor to the iterator checkers.


Repository:
  rC Clang

https://reviews.llvm.org/D62525

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/iterator-range.cpp

Index: test/Analysis/iterator-range.cpp
===================================================================
--- test/Analysis/iterator-range.cpp
+++ 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 %s -analyzer-output=text -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 -analyzer-output=text -verify
 
 #include "Inputs/system-header-simulator-cxx.h"
 
@@ -22,14 +22,16 @@
 }
 
 void simple_bad_end(const std::vector<int> &v) {
-  auto i = v.end();
+  auto i = v.end(); // expected-note{{Iterator reached the past-the-end position of the container}}
   *i; // expected-warning{{Past-the-end iterator dereferenced}}
+      // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 void copy(const std::vector<int> &v) {
   auto i1 = v.end();
-  auto i2 = i1;
+  auto i2 = i1; // expected-note{{Iterator reached the past-the-end position of the container}}
   *i2; // expected-warning{{Past-the-end iterator dereferenced}}
+       // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 void decrease(const std::vector<int> &v) {
@@ -47,9 +49,10 @@
 
 void copy_and_decrease2(const std::vector<int> &v) {
   auto i1 = v.end();
-  auto i2 = i1;
+  auto i2 = i1; // expected-note{{Iterator reached the past-the-end position of the container}}
   --i1;
   *i2; // expected-warning{{Past-the-end iterator dereferenced}}
+       // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 void copy_and_increase1(const std::vector<int> &v) {
@@ -64,16 +67,22 @@
   auto i1 = v.begin();
   auto i2 = i1;
   ++i1;
-  if (i2 == v.end())
+  if (i2 == v.end()) // expected-note 0-1{{Assuming the condition is true}}
+                     // expected-note@-1{{Assuming the container/range is empty}}
+                     // expected-note@-2{{Taking true branch}}
     *i2; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 void copy_and_increase3(const std::vector<int> &v) {
   auto i1 = v.begin();
   auto i2 = i1;
   ++i1;
-  if (v.end() == i2)
+  if (v.end() == i2) // expected-note 0-1{{Assuming the condition is true}}
+                     // expected-note@-1{{Assuming the container/range is empty}}
+                     // expected-note@-2{{Taking true branch}}
     *i2; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 template <class InputIterator, class T>
@@ -94,8 +103,13 @@
 }
 
 void bad_non_std_find(std::vector<int> &V, int e) {
-  auto first = nonStdFind(V.begin(), V.end(), e);
+  auto first = nonStdFind(V.begin(), V.end(), e); // expected-note{{Calling 'nonStdFind<__vector_iterator<int, int *, int &>, int>'}}
+                                                  // expected-note@-16{{Assuming the container/range is empty}}
+                                                  // expected-note@-17{{Loop condition is false. Execution continues on line}}
+                                                  // expected-note@-13{{Iterator reached the past-the-end position of the container}}
+                                                  // expected-note@-4{{Returning from 'nonStdFind<__vector_iterator<int, int *, int &>, int>'}}
   *first; // expected-warning{{Past-the-end iterator dereferenced}}
+          // expected-note@-1{{Past-the-end iterator dereferenced}}
 }
 
 void tricky(std::vector<int> &V, int e) {
@@ -127,6 +141,8 @@
   L.push_back(n);
   ++i0;
   *++i0; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Iterator reached the past-the-end position of the container}}
+         // expected-note@-2{{Past-the-end iterator dereferenced}}
 }
 
 void good_pop_back(std::list<int> &L, int n) {
@@ -139,6 +155,8 @@
   auto i0 = --L.cend(); --i0;
   L.pop_back();
   *++i0; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Iterator reached the past-the-end position of the container}}
+         // expected-note@-2{{Past-the-end iterator dereferenced}}
 }
 
 void good_push_front(std::list<int> &L, int n) {
@@ -150,8 +168,9 @@
 void bad_push_front(std::list<int> &L, int n) {
   auto i0 = L.cbegin();
   L.push_front(n);
-  --i0;
+  --i0; // expected-note{{Iterator reached the first position of the container}}
   --i0; // expected-warning{{Iterator decremented ahead of its valid range}}
+        // expected-note@-1{{Iterator decremented ahead of its valid range}}
 }
 
 void good_pop_front(std::list<int> &L, int n) {
@@ -162,14 +181,17 @@
 
 void bad_pop_front(std::list<int> &L, int n) {
   auto i0 = ++L.cbegin();
-  L.pop_front();
+  L.pop_front(); // expected-note{{Iterator reached the first position of the container}}
   --i0; // expected-warning{{Iterator decremented ahead of its valid range}}
+        // expected-note@-1{{Iterator decremented ahead of its valid range}}
 }
 
 void bad_move(std::list<int> &L1, std::list<int> &L2) {
   auto i0 = --L2.cend();
   L1 = std::move(L2);
   *++i0; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Iterator reached the past-the-end position of the container}}
+         // expected-note@-2{{Past-the-end iterator dereferenced}}
 }
 
 void bad_move_push_back(std::list<int> &L1, std::list<int> &L2, int n) {
@@ -178,6 +200,8 @@
   L1 = std::move(L2);
   ++i0;
   *++i0; // expected-warning{{Past-the-end iterator dereferenced}}
+         // expected-note@-1{{Iterator reached the past-the-end position of the container}}
+         // expected-note@-2{{Past-the-end iterator dereferenced}}
 }
 
 void good_incr_begin(const std::list<int> &L) {
@@ -186,8 +210,9 @@
 }
 
 void bad_decr_begin(const std::list<int> &L) {
-  auto i0 = L.begin();
+  auto i0 = L.begin(); // expected-note{{Iterator reached the first position of the container}}
   --i0;  // expected-warning{{Iterator decremented ahead of its valid range}}
+         // expected-note@-1{{Iterator decremented ahead of its valid range}}
 }
 
 void good_decr_end(const std::list<int> &L) {
@@ -196,8 +221,9 @@
 }
 
 void bad_incr_end(const std::list<int> &L) {
-  auto i0 = L.end();
+  auto i0 = L.end(); // expected-note{{Iterator reached the past-the-end position of the container}}
   ++i0;  // expected-warning{{Iterator incremented behind the past-the-end iterator}}
+         // expected-note@-1{{Iterator incremented behind the past-the-end iterator}}
 }
 
 struct simple_iterator_base {
Index: test/Analysis/invalidated-iterator.cpp
===================================================================
--- test/Analysis/invalidated-iterator.cpp
+++ test/Analysis/invalidated-iterator.cpp
@@ -1,399 +1,828 @@
-// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.InvalidatedIterator -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.InvalidatedIterator -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.InvalidatedIterator -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,alpha.cplusplus.InvalidatedIterator -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -analyzer-output=text -verify
 
 #include "Inputs/system-header-simulator-cxx.h"
 
-void bad_copy_assign_operator_list1(std::list<int> &L1,
+void bad_copy_assign_operator1_list(std::list<int> &L1,
                                     const std::list<int> &L2) {
   auto i0 = L1.cbegin();
-  L1 = L2;
+  L1 = L2; // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_copy_assign_operator_vector1(std::vector<int> &V1,
+void bad_copy_assign_operator1_vector(std::vector<int> &V1,
                                       const std::vector<int> &V2) {
   auto i0 = V1.cbegin();
-  V1 = V2;
+  V1 = V2; // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_copy_assign_operator_deque1(std::deque<int> &D1,
+void bad_copy_assign_operator1_deque(std::deque<int> &D1,
                                      const std::deque<int> &D2) {
   auto i0 = D1.cbegin();
-  D1 = D2;
+  D1 = D2; // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_copy_assign_operator_forward_list1(std::forward_list<int> &FL1,
+void bad_copy_assign_operator1_forward_list(std::forward_list<int> &FL1,
                                             const std::forward_list<int> &FL2) {
   auto i0 = FL1.cbegin();
-  FL1 = FL2;
+  FL1 = FL2; // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_assign_list1(std::list<int> &L, int n) {
+void bad_assign1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin();
-  L.assign(10, n);
+  L.assign(10, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_assign_vector1(std::vector<int> &V, int n) {
+void bad_assign1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin();
-  V.assign(10, n);
+  V.assign(10, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_assign_deque1(std::deque<int> &D, int n) {
+void bad_assign1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin();
-  D.assign(10, n);
+  D.assign(10, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_assign_forward_list1(std::forward_list<int> &FL, int n) {
+void bad_assign1_forward_list(std::forward_list<int> &FL, int n) {
   auto i0 = FL.cbegin();
-  FL.assign(10, n);
+  FL.assign(10, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_clear_list1(std::list<int> &L) {
+void good_clear1_list(std::list<int> &L) {
   auto i0 = L.cend();
   L.clear();
   --i0; // no-warning
 }
 
-void bad_clear_list1(std::list<int> &L) {
+void bad_clear1_list(std::list<int> &L) {
   auto i0 = L.cbegin(), i1 = L.cend();
-  L.clear();
+  L.clear(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_clear_vector1(std::vector<int> &V) {
+void bad_clear1_vector(std::vector<int> &V) {
   auto i0 = V.cbegin(), i1 = V.cend();
-  V.clear();
+  V.clear(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_clear1_vector_decr(std::vector<int> &V) {
+  auto i0 = V.cbegin(), i1 = V.cend();
+  V.clear(); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_clear_deque1(std::deque<int> &D) {
+void bad_clear1_deque(std::deque<int> &D) {
   auto i0 = D.cbegin(), i1 = D.cend();
-  D.clear();
+  D.clear(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_clear1_deque_decr(std::deque<int> &D) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.clear(); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_push_back_list1(std::list<int> &L, int n) {
+void good_push_back1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend();
   L.push_back(n);
   *i0; // no-warning
   --i1; // no-warning
 }
 
-void good_push_back_vector1(std::vector<int> &V, int n) {
+void good_push_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend();
   V.push_back(n);
   *i0; // no-warning
 }
 
-void bad_push_back_vector1(std::vector<int> &V, int n) {
+void bad_push_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend();
-  V.push_back(n);
+  V.push_back(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_push_back_deque1(std::deque<int> &D, int n) {
+void bad_push_back1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend();
-  D.push_back(n);
+  D.push_back(n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_push_back1_deque_decr(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.push_back(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_emplace_back_list1(std::list<int> &L, int n) {
+void good_emplace_back1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend();
   L.emplace_back(n);
   *i0; // no-warning
   --i1; // no-warning
 }
 
-void good_emplace_back_vector1(std::vector<int> &V, int n) {
+void good_emplace_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend();
   V.emplace_back(n);
   *i0; // no-warning
 }
 
-void bad_emplace_back_vector1(std::vector<int> &V, int n) {
+void bad_emplace_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend();
-  V.emplace_back(n);
+  V.emplace_back(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_emplace_back_deque1(std::deque<int> &D, int n) {
+void bad_emplace_back1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend();
-  D.emplace_back(n);
+  D.emplace_back(n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_emplace_back1_deque_decr(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.emplace_back(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_pop_back_list1(std::list<int> &L, int n) {
+void good_pop_back1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend(), i2 = i1--;
   L.pop_back();
   *i0; // no-warning
   *i2; // no-warning
 }
 
-void bad_pop_back_list1(std::list<int> &L, int n) {
+void bad_pop_back1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend(), i2 = i1--;
-  L.pop_back();
+  L.pop_back(); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_pop_back_vector1(std::vector<int> &V, int n) {
+void good_pop_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--;
   V.pop_back();
   *i0; // no-warning
 }
 
-void bad_pop_back_vector1(std::vector<int> &V, int n) {
+void bad_pop_back1_vector(std::vector<int> &V, int n) {
   auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--;
-  V.pop_back();
+  V.pop_back(); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_pop_back1_vector_decr(std::vector<int> &V, int n) {
+  auto i0 = V.cbegin(), i1 = V.cend(), i2 = i1--;
+  V.pop_back(); // expected-note{{Iterator invalidated}}
   --i2; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_pop_back_deque1(std::deque<int> &D, int n) {
+void good_pop_back1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--;
   D.pop_back();
   *i0; // no-warning
 }
 
-void bad_pop_back_deque1(std::deque<int> &D, int n) {
+void bad_pop_back1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--;
-  D.pop_back();
+  D.pop_back(); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_pop_back1_deque_decr(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin(), i1 = D.cend(), i2 = i1--;
+  D.pop_back(); // expected-note{{Iterator invalidated}}
   --i2; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_push_front_list1(std::list<int> &L, int n) {
+void good_push_front1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend();
   L.push_front(n);
   *i0; // no-warning
   --i1; // no-warning
 }
 
-void bad_push_front_deque1(std::deque<int> &D, int n) {
+void bad_push_front1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend();
-  D.push_front(n);
+  D.push_front(n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_push_front1_deque_decr(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.push_front(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_push_front_forward_list1(std::forward_list<int> &FL, int n) {
+void good_push_front1_forward_list(std::forward_list<int> &FL, int n) {
   auto i0 = FL.cbegin(), i1 = FL.cend();
   FL.push_front(n);
   *i0; // no-warning
 }
 
-void good_emplace_front_list1(std::list<int> &L, int n) {
+void good_emplace_front1_list(std::list<int> &L, int n) {
   auto i0 = L.cbegin(), i1 = L.cend();
   L.emplace_front(n);
   *i0; // no-warning
   --i1; // no-warning
 }
 
-void bad_emplace_front_deque1(std::deque<int> &D, int n) {
+void bad_emplace_front1_deque(std::deque<int> &D, int n) {
   auto i0 = D.cbegin(), i1 = D.cend();
-  D.emplace_front(n);
+  D.emplace_front(n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_emplace_front1_deque_decr(std::deque<int> &D, int n) {
+  auto i0 = D.cbegin(), i1 = D.cend();
+  D.emplace_front(n); // expected-note{{Iterator invalidated}}
   --i1; // expected-warning{{Invalidated iterator accessed}}
+        // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_emplace_front_forward_list1(std::forward_list<int> &FL, int n) {
+void good_emplace_front1_forward_list(std::forward_list<int> &FL, int n) {
   auto i0 = FL.cbegin(), i1 = FL.cend();
   FL.emplace_front(n);
   *i0; // no-warning
 }
 
-void good_pop_front_list1(std::list<int> &L, int n) {
+void good_pop_front1_list(std::list<int> &L, int n) {
   auto i1 = L.cbegin(), i0 = i1++;
   L.pop_front();
   *i1; // no-warning
 }
 
-void bad_pop_front_list1(std::list<int> &L, int n) {
+void bad_pop_front1_list(std::list<int> &L, int n) {
   auto i1 = L.cbegin(), i0 = i1++;
-  L.pop_front();
+  L.pop_front(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_pop_front_deque1(std::deque<int> &D, int n) {
+void good_pop_front1_deque(std::deque<int> &D, int n) {
   auto i1 = D.cbegin(), i0 = i1++;
   D.pop_front();
   *i1; // no-warning
 }
 
-void bad_pop_front_deque1(std::deque<int> &D, int n) {
+void bad_pop_front1_deque(std::deque<int> &D, int n) {
   auto i1 = D.cbegin(), i0 = i1++;
-  D.pop_front();
+  D.pop_front(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_pop_front_forward_list1(std::forward_list<int> &FL, int n) {
+void good_pop_front1_forward_list(std::forward_list<int> &FL, int n) {
   auto i1 = FL.cbegin(), i0 = i1++;
   FL.pop_front();
   *i1; // no-warning
 }
 
-void bad_pop_front_forward_list1(std::forward_list<int> &FL, int n) {
+void bad_pop_front1_forward_list(std::forward_list<int> &FL, int n) {
   auto i1 = FL.cbegin(), i0 = i1++;
-  FL.pop_front();
+  FL.pop_front(); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_insert_list1(std::list<int> &L, int n) {
+void good_insert1_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) {
+void good_insert1_list2(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  i1 = L.insert(i1, n);
+  *i1; // no-warning
+}
+
+void good_insert1_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) {
+void good_insert1_vector2(std::vector<int> &V, int n) {
   auto i1 = V.cbegin(), i0 = i1++;
-  V.insert(i1, n);
+  i1 = V.insert(i1, n);
+  *i1; // no-warning
+}
+
+void bad_insert1_vector(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, n); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert1_deque(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  i0 = D.insert(i1, n);
+  *i0; // no-warning
 }
 
-void bad_insert_deque1(std::deque<int> &D, int n) {
+void bad_insert1_deque1(std::deque<int> &D, int n) {
   auto i1 = D.cbegin(), i0 = i1++;
-  D.insert(i1, n);
+  D.insert(i1, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_insert1_deque2(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, n); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert2_list1(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  L.insert(i1, std::move(n));
+  *i0; // no-warning
+  *i1; // no-warning
 }
 
-void good_emplace_list1(std::list<int> &L, int n) {
+void good_insert2_list2(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  i1 = L.insert(i1, std::move(n));
+  *i1; // no-warning
+}
+
+void good_insert2_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, std::move(n));
+  *i0; // no-warning
+}
+
+void good_insert2_vector2(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  i1 = V.insert(i1, std::move(n));
+  *i1; // no-warning
+}
+
+void bad_insert2_vector(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert2_deque(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  i1 = D.insert(i1, std::move(n));
+  *i1; // no-warning
+}
+
+void bad_insert2_deque1(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}}
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_insert2_deque2(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, std::move(n)); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert3_list1(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  L.insert(i1, 10, n);
+  *i0; // no-warning
+  *i1; // no-warning
+}
+
+void good_insert3_list2(std::list<int> &L, int n) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  i1 = L.insert(i1, 10, n);
+  *i1; // no-warning
+}
+
+void good_insert3_vector1(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, 10, n);
+  *i0; // no-warning
+}
+
+void good_insert3_vector2(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  i1 = V.insert(i1, 10, n);
+  *i1; // no-warning
+}
+
+void bad_insert3_vector(std::vector<int> &V, int n) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, 10, n); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert3_deque(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  i1 = D.insert(i1, 10, std::move(n));
+  *i1; // no-warning
+}
+
+void bad_insert3_deque1(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, 10, std::move(n)); // expected-note{{Iterator invalidated}}
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_insert3_deque2(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, 10, std::move(n)); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert4_list1(std::list<int> &L1, std::list<int> &L2, int n) {
+  auto i1 = L1.cbegin(), i0 = i1++;
+  L1.insert(i1, L2.cbegin(), L2.cend());
+  *i0; // no-warning
+  *i1; // no-warning
+}
+
+void good_insert4_list2(std::list<int> &L1, std::list<int> &L2, int n) {
+  auto i1 = L1.cbegin(), i0 = i1++;
+  i1 = L1.insert(i1, L2.cbegin(), L2.cend());
+  *i1; // no-warning
+}
+
+void good_insert4_vector1(std::vector<int> &V1, std::vector<int> &V2, int n) {
+  auto i1 = V1.cbegin(), i0 = i1++;
+  V1.insert(i1, V2.cbegin(), V2.cend());
+  *i0; // no-warning
+}
+
+void good_insert4_vector2(std::vector<int> &V1, std::vector<int> &V2, int n) {
+  auto i1 = V1.cbegin(), i0 = i1++;
+  i1 = V1.insert(i1, V2.cbegin(), V2.cend());
+  *i1; // no-warning
+}
+
+void bad_insert4_vector(std::vector<int> &V1, std::vector<int> &V2, int n) {
+  auto i1 = V1.cbegin(), i0 = i1++;
+  V1.insert(i1, V2.cbegin(), V2.cend()); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert4_deque(std::deque<int> &D1, std::deque<int> &D2, int n) {
+  auto i1 = D1.cbegin(), i0 = i1++;
+  i1 = D1.insert(i1, D2.cbegin(), D2.cend());
+  *i1; // no-warning
+}
+
+void bad_insert4_deque1(std::deque<int> &D1, std::deque<int> &D2, int n) {
+  auto i1 = D1.cbegin(), i0 = i1++;
+  D1.insert(i1, D2.cbegin(), D2.cend()); // expected-note{{Iterator invalidated}}
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_insert4_deque2(std::deque<int> &D1, std::deque<int> &D2, int n) {
+  auto i1 = D1.cbegin(), i0 = i1++;
+  D1.insert(i1, D2.cbegin(), D2.cend()); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert5_list1(std::list<int> &L) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  L.insert(i1, {1, 2, 3, 4});
+  *i0; // no-warning
+  *i1; // no-warning
+}
+
+void good_insert5_list2(std::list<int> &L) {
+  auto i1 = L.cbegin(), i0 = i1++;
+  i1 = L.insert(i1, {1, 2, 3, 4});
+  *i1; // no-warning
+}
+
+void good_insert5_vector1(std::vector<int> &V) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, {1, 2, 3, 4});
+  *i0; // no-warning
+}
+
+void good_insert5_vector2(std::vector<int> &V) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  i1 = V.insert(i1, {1, 2, 3, 4});
+  *i1; // no-warning
+}
+
+void bad_insert5_vector(std::vector<int> &V) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_insert5_deque(std::deque<int> &D) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  i1 = D.insert(i1, {1, 2, 3, 4});
+  *i1; // no-warning
+}
+
+void bad_insert5_deque1(std::deque<int> &D) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}}
+  *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_insert5_deque2(std::deque<int> &D) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.insert(i1, {1, 2, 3, 4}); // expected-note{{Iterator invalidated}}
+  *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_emplace1_list(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) {
+void good_emplace1_vector(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) {
+void bad_emplace1_vector(std::vector<int> &V, int n) {
   auto i1 = V.cbegin(), i0 = i1++;
-  V.emplace(i1, n);
+  V.emplace(i1, n); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_emplace_deque1(std::deque<int> &D, int n) {
+void bad_emplace1_deque1(std::deque<int> &D, int n) {
   auto i1 = D.cbegin(), i0 = i1++;
-  D.emplace(i1, n);
+  D.emplace(i1, n); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_emplace1_deque2(std::deque<int> &D, int n) {
+  auto i1 = D.cbegin(), i0 = i1++;
+  D.emplace(i1, n); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_list1(std::list<int> &L) {
+void good_erase1_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) {
+void good_erase1_list2(std::list<int> &L) {
+  auto i0 = L.cbegin();
+  i0 = L.erase(i0);
+  *i0; // no-warning
+}
+
+void bad_erase1_list(std::list<int> &L) {
   auto i0 = L.cbegin();
-  L.erase(i0);
+  L.erase(i0); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_vector1(std::vector<int> &V) {
+void good_erase1_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) {
+void good_erase1_vector2(std::vector<int> &V) {
+  auto i0 = V.cbegin();
+  i0 = V.erase(i0);
+  *i0; // no-warning
+}
+
+void bad_erase1_vector1(std::vector<int> &V) {
   auto i1 = V.cbegin(), i0 = i1++;
-  V.erase(i0);
+  V.erase(i0); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase1_vector2(std::vector<int> &V) {
+  auto i1 = V.cbegin(), i0 = i1++;
+  V.erase(i0); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void bad_erase_deque1(std::deque<int> &D) {
+void good_erase1_deque(std::deque<int> &D) {
+  auto i0 = D.cbegin();
+  i0 = D.erase(i0);
+  *i0; // no-warning
+}
+
+void bad_erase1_deque1(std::deque<int> &D) {
   auto i2 = D.cbegin(), i0 = i2++, i1 = i2++;
-  D.erase(i1);
+  D.erase(i1); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase1_deque2(std::deque<int> &D) {
+  auto i2 = D.cbegin(), i0 = i2++, i1 = i2++;
+  D.erase(i1); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase1_deque3(std::deque<int> &D) {
+  auto i2 = D.cbegin(), i0 = i2++, i1 = i2++;
+  D.erase(i1); // expected-note{{Iterator invalidated}}
   *i2; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_list2(std::list<int> &L) {
+void good_erase2_list1(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) {
+void good_erase2_list2(std::list<int> &L) {
+  auto i2 = L.cbegin(), i0 = i2++, i1 = i2++;
+  i0 = L.erase(i0, i2);
+  *i0; // no-warning
+}
+
+void bad_erase2_list1(std::list<int> &L) {
   auto i2 = L.cbegin(), i0 = i2++, i1 = i2++;
-  L.erase(i0, i2);
+  L.erase(i0, i2); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_list2(std::list<int> &L) {
+  auto i2 = L.cbegin(), i0 = i2++, i1 = i2++;
+  L.erase(i0, i2); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_vector2(std::vector<int> &V) {
+void good_erase2_vector1(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) {
+void good_erase2_vector2(std::vector<int> &V) {
   auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
-  V.erase(i0, i2);
+  i0 = V.erase(i0, i2);
+  *i0; // no-warning
+}
+
+void bad_erase2_vector1(std::vector<int> &V) {
+  auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
+  V.erase(i0, i2); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_vector2(std::vector<int> &V) {
+  auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
+  V.erase(i0, i2); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_vector3(std::vector<int> &V) {
+  auto i2 = V.cbegin(), i0 = i2++, i1 = i2++;
+  V.erase(i0, i2); // expected-note{{Iterator invalidated}}
   *i2; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void good_erase2_deque(std::deque<int> &D) {
+  auto i2 = D.cbegin(), i0 = i2++, i1 = i2++;
+  i0 = D.erase(i0, i2);
+  *i0; // no-warning
 }
 
-void bad_erase_deque2(std::deque<int> &D) {
+void bad_erase2_deque1(std::deque<int> &D) {
   auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
-  D.erase(i1, i3);
+  D.erase(i1, i3); // expected-note{{Iterator invalidated}}
   *i0; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_deque2(std::deque<int> &D) {
+  auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  D.erase(i1, i3); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_deque3(std::deque<int> &D) {
+  auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  D.erase(i1, i3); // expected-note{{Iterator invalidated}}
   *i2; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase2_deque4(std::deque<int> &D) {
+  auto i3 = D.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  D.erase(i1, i3); // expected-note{{Iterator invalidated}}
   *i3; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_after_forward_list1(std::forward_list<int> &FL) {
+void good_erase_after1_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) {
+void good_erase_after1_forward_lis2(std::forward_list<int> &FL) {
   auto i1 = FL.cbegin(), i0 = i1++;
-  FL.erase_after(i0);
+  i1 = FL.erase_after(i0);
+  *i1; // no-warning
+}
+
+void bad_erase_after1_forward_list(std::forward_list<int> &FL) {
+  auto i1 = FL.cbegin(), i0 = i1++;
+  FL.erase_after(i0); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
 }
 
-void good_erase_after_forward_list2(std::forward_list<int> &FL) {
+void good_erase_after2_forward_list1(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) {
+void good_erase_after2_forward_list2(std::forward_list<int> &FL) {
   auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
-  FL.erase_after(i0, i3);
+  i2 = FL.erase_after(i0, i3);
+  *i2; // no-warning
+}
+
+void bad_erase_after2_forward_list1(std::forward_list<int> &FL) {
+  auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  FL.erase_after(i0, i3); // expected-note{{Iterator invalidated}}
   *i1; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{Invalidated iterator accessed}}
+}
+
+void bad_erase_after2_forward_list2(std::forward_list<int> &FL) {
+  auto i3 = FL.cbegin(), i0 = i3++, i1 = i3++, i2 = i3++;
+  FL.erase_after(i0, i3); // expected-note{{Iterator invalidated}}
   *i2; // expected-warning{{Invalidated iterator accessed}}
+       // expected-note@-1{{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:677 {{Called C++ object pointer is null}}
+  // expected-warning@../Inputs/system-header-simulator-cxx.h:681 {{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
@@ -147,7 +147,7 @@
   typedef std::bidirectional_iterator_tag iterator_category;
 
   __list_iterator(T* it = 0) : item(it) {}
-  __list_iterator(const iterator &rhs): item(rhs.base()) {}
+  __list_iterator(const iterator &rhs): item(rhs.item) {}
   __list_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; }
   __list_iterator<T, Ptr, Ref> operator++(int) {
     auto tmp = *this;
@@ -172,6 +172,8 @@
 
   const T* &base() const { return item; }
 
+  template <typename UT, typename UPtr, typename URef> friend struct __list_iterator;
+
 private:
   T* item;
 };
@@ -187,7 +189,7 @@
   typedef std::forward_iterator_tag iterator_category;
 
   __fwdl_iterator(T* it = 0) : item(it) {}
-  __fwdl_iterator(const iterator &rhs): item(rhs.base()) {}
+  __fwdl_iterator(const iterator &rhs): item(rhs.item) {}
   __fwdl_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; }
   __fwdl_iterator<T, Ptr, Ref> operator++(int) {
     auto tmp = *this;
@@ -205,6 +207,8 @@
 
   const T* &base() const { return item; }
 
+  template <typename UT, typename UPtr, typename URef> friend struct __fwdl_iterator;
+
 private:
   T* item;
 };
Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -227,7 +227,9 @@
                                    const IteratorPosition &Pos,
                                    const SVal &Distance) const;
   void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
-                           CheckerContext &C, ExplodedNode *ErrNode) const;
+                           CheckerContext &C, ExplodedNode *ErrNode,
+                           const IteratorPosition *Pos,
+                           bool PastTheEnd = true) const;
   void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
                            const SVal &Val2, CheckerContext &C,
                            ExplodedNode *ErrNode) const;
@@ -235,7 +237,8 @@
                            const MemRegion *Reg, CheckerContext &C,
                            ExplodedNode *ErrNode) const;
   void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
-                            CheckerContext &C, ExplodedNode *ErrNode) const;
+                            CheckerContext &C, ExplodedNode *ErrNode,
+                            const IteratorPosition *Pos) const;
 
 public:
   IteratorChecker();
@@ -262,6 +265,44 @@
 };
 } // namespace
 
+// This visitor helps the user to better understand the out-of-range and the
+// invalidated iterator bug types. It adds a note to the statement which
+// results in the past-the-end, beginning of the range or the invalideted
+// state of the iterator. Furthermore it also markes the block edges where
+// we assume that the container of the iterator is empty or non-empty.
+class IteratorBRVisitor final : public BugReporterVisitor {
+public:
+    enum ErrorTypeT { AheadOfRange, PastTheEnd, Invalidated };
+
+private:
+  SVal Iter;
+  const MemRegion *Cont;
+
+  // `FoundChange` becomes true when we find the statement the results in the
+  // current state of the iterator.
+  // `FoundEmptyness` becomes true when we find the block edge assuming
+  // emptiness or non-emptiness of the container.
+  bool FoundChange = false, FoundEmptiness;
+  ErrorTypeT ErrorType;
+
+public:
+  IteratorBRVisitor(SVal It, const MemRegion *C, ErrorTypeT ET)
+    : Iter(It), Cont(C), ErrorType(ET) {
+    // Emptyness does not matter for invalidated iterator access
+    FoundEmptiness = ErrorType == Invalidated;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const override {
+    ID.Add(Iter);
+    ID.Add(Cont);
+    ID.AddInteger(ErrorType);
+  }
+
+  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
+                                                 BugReporterContext &BRC,
+                                                 BugReport &BR) override;
+};
+
 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
                                IteratorPosition)
@@ -926,7 +967,7 @@
     auto *N = C.generateNonFatalErrorNode(State);
     if (!N)
       return;
-    reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
+    reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N, Pos);
     return;
   }
 }
@@ -939,7 +980,7 @@
     if (!N) {
       return;
     }
-    reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
+    reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N, Pos);
   }
 }
 
@@ -1047,14 +1088,14 @@
     if (!N)
       return;
     reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
-                        C, N);
+                        C, N, Pos, false);
   }
   if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
     auto *N = C.generateNonFatalErrorNode(State);
     if (!N)
       return;
     reportOutOfRangeBug("Iterator incremented behind the past-the-end "
-                        "iterator.", LHS, C, N);
+                        "iterator.", LHS, C, N, Pos);
   }
 }
 
@@ -1586,9 +1627,15 @@
 
 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
                                           const SVal &Val, CheckerContext &C,
-                                          ExplodedNode *ErrNode) const {
+                                          ExplodedNode *ErrNode,
+                                          const IteratorPosition *Pos,
+                                          bool PastTheEnd) const {
   auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
   R->markInteresting(Val);
+  R->addVisitor(llvm::make_unique<IteratorBRVisitor>(Val, Pos->getContainer(),
+                                                     PastTheEnd ?
+                                              IteratorBRVisitor::PastTheEnd :
+                                              IteratorBRVisitor::AheadOfRange));
   C.emitReport(std::move(R));
 }
 
@@ -1614,12 +1661,140 @@
 
 void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
                                            const SVal &Val, CheckerContext &C,
-                                           ExplodedNode *ErrNode) const {
+                                           ExplodedNode *ErrNode,
+                                          const IteratorPosition *Pos) const {
   auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
   R->markInteresting(Val);
+  R->addVisitor(llvm::make_unique<IteratorBRVisitor>(Val, Pos->getContainer(),
+                                               IteratorBRVisitor::Invalidated));
   C.emitReport(std::move(R));
 }
 
+std::shared_ptr<PathDiagnosticPiece>
+IteratorBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
+                             BugReport &BR) {
+  if (FoundChange && FoundEmptiness)
+    return nullptr;
+
+  const ExplodedNode *Pred = Succ->getFirstPred();
+  const Stmt *S = nullptr;
+  const auto SP = Succ->getLocation().getAs<StmtPoint>();
+  if (SP.hasValue()) {
+    S = SP->getStmt();
+  } else {
+    const auto E = Succ->getLocation().getAs<BlockEdge>();
+    if (E.hasValue()) {
+      S = E->getSrc()->getTerminator().getStmt();
+    }
+  }
+
+  if (!S)
+    return nullptr;
+
+  const auto &StateBefore = Pred->getState();
+  const auto &StateAfter = Succ->getState();
+  const auto *CDBefore = getContainerData(StateBefore, Cont);
+  const auto *CDAfter = getContainerData(StateAfter, Cont);
+
+  SmallString<256> Buf;
+  llvm::raw_svector_ostream Out(Buf);
+
+  if (!FoundChange) {
+    const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
+    const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
+
+    // If the bug type is decremention of the iterator ahead of its range,
+    // then we must find a program state pair where in the predecessor we
+    // either do not store anything about the iterator or the container or
+    // the iterator does not reference to the first position of the container,
+    // while in the successor we store both the position of the iterator and
+    // the boundaries of its container and the iterator references to the
+    // first position of the container.
+    if (ErrorType == AheadOfRange &&
+        ((!PosBefore || !CDBefore ||
+          PosBefore->getOffset() != CDBefore->getBegin()) &&
+         (PosAfter && CDAfter &&
+          PosAfter->getOffset() == CDAfter->getBegin()))) {
+      Out << "Iterator reached the first position of the container.";
+      FoundChange = true;
+
+    // If the bug type is dereference or incremention of the past-the-end
+    // iterator, then we must find a program state pair where in the
+    // predecessor we either do not store anything about the iterator or the
+    // container or the iterator does not reference to the past-the-end
+    // of the container, while in the successor we store both the position
+    // of the iterator and the boundaries of its container and the iterator
+    // references to the past-the-end iterator of the container.
+    } else if (ErrorType == PastTheEnd &&
+               ((!PosBefore || !CDBefore ||
+                 PosBefore->getOffset() != CDBefore->getEnd()) &&
+                (PosAfter && CDAfter &&
+                 PosAfter->getOffset() == CDAfter->getEnd()))) {
+      Out << "Iterator reached the past-the-end position of the "
+        "container.";
+      FoundChange = true;
+
+    // If the bug type is access of an invalidated iterator, then we must find
+    // a program state pair where in the predecessor we either do not store
+    // anything about the iterator or the iterator is valid, while in the
+    // successor we store the position of the iterator and it is invalidated.
+    } else if (ErrorType == Invalidated &&
+               ((!PosBefore || PosBefore->isValid()) &&
+                (PosAfter && !PosAfter->isValid()))) {
+      Out << "Iterator invalidated.";
+      FoundChange = true;
+    }
+  }
+
+  if (!FoundEmptiness) {
+    ProgramStateRef NotEmptyBefore =
+      (CDBefore && CDBefore->getBegin() && CDBefore->getEnd()) ?
+      relateSymbols(StateBefore, CDBefore->getBegin(), CDBefore->getEnd(),
+                    false) : StateBefore;
+    ProgramStateRef NotEmptyAfter =
+      (CDAfter && CDAfter->getBegin() && CDAfter->getEnd()) ?
+      relateSymbols(StateAfter, CDAfter->getBegin(), CDAfter->getEnd(), false) :
+      StateAfter;
+    ProgramStateRef EmptyBefore =
+      (CDBefore && CDBefore->getBegin() && CDBefore->getEnd()) ?
+      relateSymbols(StateBefore, CDBefore->getBegin(), CDBefore->getEnd(),
+                    true) : StateBefore;
+    ProgramStateRef EmptyAfter =
+      (CDAfter && CDAfter->getBegin() && CDAfter->getEnd()) ?
+      relateSymbols(StateAfter, CDAfter->getBegin(), CDAfter->getEnd(), true) :
+      StateAfter;
+
+    // If the container could be empty in the predecessor state and in the
+    // successor state it can be non-empty and cannot be empty, then we found
+    // the first program point where we assume it is non-empty.
+    if (EmptyBefore && (NotEmptyAfter && !EmptyAfter)) {
+      Out << "Assuming the container/range is non-empty.";
+      FoundEmptiness = true;
+
+    // If the container could be non-empty in the predecessor state and in the
+    // successor state it can be empty and cannot be non-empty, then we found
+    // the first program point where we assume it is empty.
+    } else if (NotEmptyBefore && (EmptyAfter && !NotEmptyAfter)) {
+      Out << "Assuming the container/range is empty.";
+      FoundEmptiness = true;
+    }
+  }
+
+  if (Buf.empty())
+    return nullptr;
+
+  auto L = PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(),
+                                               Succ->getLocationContext());
+
+  if (!L.isValid() || !L.asLocation().isValid())
+    return nullptr;
+
+  auto Piece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
+  Piece->addRange(S->getSourceRange());
+
+  return std::move(Piece);
+}
+
 namespace {
 
 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to