================
@@ -0,0 +1,320 @@
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-invalidation -Wno-dangling 
-verify %s
+
+#include "Inputs/lifetime-analysis.h"
+
+namespace logging {
+class VoidifyStream {
+ public:
+  VoidifyStream() = default;
+  template <typename T>
+  void operator&(const T&) {}
+};
+class CheckError {
+ public:
+  static CheckError Check(const char* file, int line, const char* condition);
+};
+}  // namespace logging
+#define LAZY_CHECK_STREAM(stream, condition) \
+  !(condition) ? (void)0 : ::logging::VoidifyStream() & (stream)
+#define CHECK(condition)                                                     \
+  LAZY_CHECK_STREAM(                                                         \
+      ::logging::CheckError::Check(__FILE__, __LINE__, #condition).stream(), \
+      !(condition))
+
+bool Bool();
+void PassBool(bool);
+
+// --- Test Cases ---
+
+namespace SimpleResize {
+void IteratorInvalidAfterResize(int new_size) {
+  std::vector<int> v;
+  auto it = std::begin(v);  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  v.resize(new_size);       // expected-note {{invalidated here}}
+  *it;                      // expected-note {{later used here}}
+}
+
+void IteratorValidAfterResize(int new_size) {
+  std::vector<int> v;
+  auto it = std::begin(v);
+  v.resize(new_size);
+  it = std::begin(v);
+  if (it != std::end(v)) {
+    *it;  // ok
+  }
+}
+}  // namespace SimpleResize
+
+namespace CheckModel {
+void IteratorValidAfterCheck() {
+  std::vector<int> v;
+  auto it = v.begin();
+  *it;  // ok
+}
+}  // namespace CheckModel
+
+namespace PointerToContainer {
+std::vector<int>* GetContainerPointer();
+void PointerToContainerTest() {
+  // FIXME: Use opaque loans.
+  std::vector<int>* v = GetContainerPointer();
+  auto it = v->begin();
+  *it = 0;  // not-ok
+}
+void PointerToContainerTest(std::vector<int>* v) {
+  // FIXME: Handle placeholder loans.
+  auto it = v->begin();
+  *it = 0;  // not-ok
+}
+}  // namespace PointerToContainer
+
+namespace InvalidateBeforeSwap {
+void InvalidateBeforeSwapIterators(std::vector<int> v1, std::vector<int> v2) {
+  auto it1 = std::begin(v1); // expected-warning {{object whose reference is 
captured is later invalidated}}
+  auto it2 = std::begin(v2);
+  if (it1 == std::end(v1) || it2 == std::end(v2)) return;
+  *it1 = 0;     // ok
+  *it2 = 0;     // ok
+  v1.clear();   // expected-note {{invalidated here}}
+  *it1 = 0;     // expected-note {{later used here}}
+  // FIXME: Handle invalidating functions like std::swap.
+  std::swap(it1, it2);
+  *it1 = 0;  // ok
+  *it2 = 0;  // not-ok
+}
+
+void InvalidateBeforeSwapContainers(std::vector<int> v1, std::vector<int> v2) {
+  auto it1 = std::begin(v1);  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  auto it2 = std::begin(v2);
+  if (it1 == std::end(v1) || it2 == std::end(v2)) return;
+  *it1 = 0;     // ok
+  *it2 = 0;     // ok
+  v1.clear();   // expected-note {{invalidated here}}
+  *it1 = 0;     // expected-note {{later used here}}
+}
+}  // namespace InvalidateBeforeSwap
+
+namespace MergeConditionBasic {
+bool A();
+bool B();
+void SameConditionInvalidatesThenValidatesIterator() {
+  std::vector<int> container;
+  auto it = container.begin(); // expected-warning {{object whose reference is 
captured is later invalidated}}
+  if (it == container.end()) return;
+  const bool a = A();
+  if (a) {
+    container.clear();  // expected-note {{invalidated here}}
+  }
+  if (a) {
+    it = container.begin();
+    if (it == std::end(container)) return;
+  }
+  *it = 10;  // expected-note {{later used here}}
+}
+}  // namespace MergeConditionBasic
+
+namespace IteratorWithMultipleContainers {
+void MergeWithDifferentContainerValuesIteratorNotInvalidated() {
+  std::vector<int> v1, v2, v3;
+  auto it = std::find(v1.begin(), v1.end(), 10);
+  if (Bool()) {
+    it = std::find(v2.begin(), v2.end(), 10);
+  } else {
+    it = std::find(v3.begin(), v3.end(), 10);
+  }
+  v1.clear();
+  *it = 20;
+}
+
+void MergeWithDifferentContainerValuesInvalidated() {
+  std::vector<int> v1, v2, v3;
+  auto it = std::find(v1.begin(), v1.end(), 10);
+  if (Bool()) {
+    it = std::find(v2.begin(), v2.end(), 10);  // expected-warning {{object 
whose reference is captured is later invalidated}}
+  } else {
+    it = std::find(v3.begin(), v3.end(), 10);
+  }
+  v2.clear();   // expected-note {{invalidated here}}
+  *it = 20;     // expected-note {{later used here}}
+}
+}  // namespace IteratorWithMultipleContainers
+
+namespace InvalidationInLoops {
+void IteratorInvalidationInAForLoop(std::vector<int> v) {
+  for (auto it = std::begin(v);  // expected-warning {{object whose reference 
is captured is later invalidated}}
+       it != std::end(v);
+       ++it) {  // expected-note {{later used here}}
+    if (Bool()) {
+      v.erase(it);  // expected-note {{invalidated here}}
+    }
+  }
+}
+
+void IteratorInvalidationInAWhileLoop(std::vector<int> v) {
+  auto it = std::begin(v);  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  while (it != std::end(v)) {
+    if (Bool()) {
+      v.erase(it);  // expected-note {{invalidated here}}
+    }
+    ++it; // expected-note {{later used here}}
+  }
+}
+
+void IteratorInvalidationInAForeachLoop(std::vector<int> v) {
+  for (int& x : v) { // expected-warning {{object whose reference is captured 
is later invalidated}} \
+                     // expected-note {{later used here}}
+    if (x % 2 == 0) {
+      v.erase(std::find(v.begin(), v.end(), 1)); // expected-note 
{{invalidated here}}
+    }
+  }
+}
+}  // namespace InvalidationInLoops
+
+namespace StdVectorPopBack {
+void StdVectorPopBackInvalid(std::vector<int> v) {
+  auto it = v.begin();  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  if (it == v.end()) return;
+  *it;  // ok
+  v.pop_back(); // expected-note {{invalidated here}}
+  *it;          // expected-note {{later used here}}
+}
+}  // namespace StdVectorPopBack
+
+
+namespace SimpleStdFind {
+void IteratorCheckedAfterFind(std::vector<int> v) {
+  auto it = std::find(std::begin(v), std::end(v), 3);
+  if (it != std::end(v)) {
+    *it;  // ok
+  }
+}
+
+void IteratorCheckedAfterFindThenErased(std::vector<int> v) {
+  auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning 
{{object whose reference is captured is later invalidated}}
+  if (it != std::end(v)) {
+    v.erase(it); // expected-note {{invalidated here}}
+  }
+  *it;  // expected-note {{later used here}}
+}
+}  // namespace SimpleStdFind
+
+namespace SimpleInsert {
+void UseReturnedIteratorAfterInsert(std::vector<int> v) {
+  auto it = std::begin(v);
+  it = v.insert(it, 10);
+  if (it != std::end(v)) {
+    *it;  // ok
+  }
+}
+
+void UseInvalidIteratorAfterInsert(std::vector<int> v) {
+  auto it = std::begin(v);  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  v.insert(it, 10);         // expected-note {{invalidated here}}
+  if (it != std::end(v)) {  // not-ok // expected-note {{later used here}}
+    *it;
+  }
+}
+}  // namespace SimpleInsert
+
+namespace SimpleStdInsert {
+void IteratorValidAfterInsert(std::vector<int> v) {
+  auto it = std::begin(v);
+  v.insert(it, 0);
+  it = std::begin(v);
+  if (it != std::end(v)) {
+    *it;  // ok
+  }
+}
+
+void IteratorInvalidAfterInsert(std::vector<int> v, int value) {
+  auto it = std::begin(v);  // expected-warning {{object whose reference is 
captured is later invalidated}}
+  v.insert(it, 0);          // expected-note {{invalidated here}}
+  *it;                      // expected-note {{later used here}}
+}
+}  // namespace SimpleStdInsert
+
+namespace SimpleInvalidIterators {
+void IteratorUsedAfterErase(std::vector<int> v) {
+  auto it = std::begin(v);          // expected-warning {{object whose 
reference is captured is later invalidated}}
+  for (; it != std::end(v); ++it) { // expected-note {{later used here}}
+    if (*it > 3) {
+      v.erase(it);                  // expected-note {{invalidated here}}
+    }
+  }
+}
+
+void IteratorUsedAfterPushBack(std::vector<int> v) {
+  auto it = std::begin(v); // expected-warning {{object whose reference is 
captured is later invalidated}}
+  if (it != std::end(v) && *it == 3) {
+    v.push_back(4); // expected-note {{invalidated here}}
+  }
+  ++it;             // expected-note {{later used here}}
+}
+}  // namespace SimpleInvalidIterators
+
+namespace ElementReferences {
+// Testing raw pointers and references to elements, not just iterators.
+
+void ReferenceToVectorElement() {
+  std::vector<int> v = {1, 2, 3};
+  int& ref = v[0];
+  v.push_back(4);
+  // FIXME: Detect this as a use of 'ref. (<file bug>)
+  ref = 10;
+  (void)ref;
+}
+
+void PointerToVectorElement() {
+  std::vector<int> v = {1, 2, 3};
+  int* ptr = &v[0];  // expected-warning {{object whose reference is captured 
is later invalidated}}
+  v.resize(100);     // expected-note {{invalidated here}}
+  *ptr = 10;         // expected-note {{later used here}}
+}
+
+void SelfInvalidatingMap() {
+  std::unordered_map<int, int> mp;
+  mp[1] = 1;
+  mp[2] = mp[1];  // FIXME: Detect this. We are mising a UseFact for the 
assignment params.
+}
+} // namespace ElementReferences
+
+namespace Strings {
+
+void append(std::string str) {
+  std::string_view view = str;  // expected-warning {{object whose reference 
is captured is later invalidated}}
+  str += "456";                 // expected-note {{invalidated here}}
+  (void)view;                   // expected-note {{later used here}}
+}
+void reassign(std::string str, std::string str2) {
+  std::string_view view = str;  // expected-warning {{object whose reference 
is captured is later invalidated}}
+  str = str2;                   // expected-note {{invalidated here}}
+  (void)view;                   // expected-note {{later used here}}
+}
+} // namespace Strings
+
+namespace ContainersAsFields {
+struct S {
+  std::vector<std::string> strings1;
+  std::vector<std::string> strings2;
+};
+// FIXME: Make Paths more precise to reason at field granularity.
+//        Here, we invalidate paths `s.strings2` and deeper but sibling paths
+void Invalidate1Use2() {
+    S s;
+    auto it = s.strings1.begin();  // expected-warning {{object whose 
reference is captured is later invalidated}}
+    s.strings2.push_back("1");     // expected-note {{invalidated here}}
+    *it;                           // expected-note {{later used here}}
+}
+void Invalidate1UseS() {
+    S s;
+    S* p = &s;                 // expected-warning {{object whose reference is 
captured is later invalidated}}
+    s.strings2.push_back("1"); // expected-note {{invalidated here}}
+    (void)*p;                  // expected-note {{later used here}}
+}
+void ContainerAsPointer() {
+    std::vector<std::string> s;                
+    std::vector<std::string>* p = &s; // expected-warning {{object whose 
reference is captured is later invalidated}}
----------------
Xazax-hun wrote:

Could we just not generate invalidation facts for now when the invalidation is 
done indirectly? 

https://github.com/llvm/llvm-project/pull/179093
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to