mgehre updated this revision to Diff 53097.
mgehre added a comment.

Update for renaming in http://reviews.llvm.org/D15031


http://reviews.llvm.org/D15032

Files:
  clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  clang-tidy/cppcoreguidelines/ProLifetimeCheck.cpp
  clang-tidy/cppcoreguidelines/ProLifetimeCheck.h
  clang-tidy/cppcoreguidelines/ProLifetimeVisitor.cpp
  clang-tidy/cppcoreguidelines/ProLifetimeVisitor.h
  docs/clang-tidy/checks/cppcoreguidelines-pro-lifetime.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/cppcoreguidelines-pro-lifetime-psets.cpp
  test/clang-tidy/cppcoreguidelines-pro-lifetime.cpp

Index: test/clang-tidy/cppcoreguidelines-pro-lifetime.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/cppcoreguidelines-pro-lifetime.cpp
@@ -0,0 +1,87 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-lifetime %t -- -config="{CheckOptions: [{key: cppcoreguidelines-pro-lifetime.Debug, value: 1}]}" -- -std=c++11
+
+struct S {
+  ~S();
+  int m;
+  int f();
+};
+
+void deref_uninitialized() {
+  int *p;
+  *p = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dereferencing a dangling pointer [cppcoreguidelines-pro-lifetime]
+  // CHECK-MESSAGES: :[[@LINE-3]]:8: note: it became invalid because was never initialized here
+}
+
+void deref_nullptr() {
+  int *q = nullptr;
+  *q = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dereferencing a null pointer
+}
+
+void ref_leaves_scope() {
+  int *p;
+  {
+    int i = 0;
+    p = &i;
+    *p = 2; // OK
+  }
+  *p = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dereferencing a dangling pointer
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: note: it became invalid because the pointee i left the scope here
+}
+
+void ref_to_member_leaves_scope_call() {
+  S *p;
+  {
+    S s;
+    p = &s;
+    p->f(); // OK
+  }
+  p->f();
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: dereferencing a dangling pointer
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: note: it became invalid because the pointee s left the scope here
+  int i = p->m;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dereferencing a dangling pointer
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: note: it became invalid because the pointee s left the scope here
+  p->m = 4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: dereferencing a dangling pointer
+  // CHECK-MESSAGES: :[[@LINE-9]]:3: note: it became invalid because the pointee s left the scope here
+}
+
+// No Pointer involved, thus not checked
+void ignore_access_on_non_ref_ptr() {
+  S s;
+  s.m = 3;
+  s.f();
+}
+
+// Note: the messages below are for the template instantiation in instantiate_ref_leaves_scope_template
+// The checker only checks instantiations
+template<typename T>
+void ref_leaves_scope_template() {
+  T p;
+  {
+    int i = 0;
+    p = &i;
+    *p = 2; // OK
+  }
+  *p = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: dereferencing a dangling pointer
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: note: it became invalid because the pointee i left the scope here
+}
+
+void instantiate_ref_leaves_scope_template() {
+  ref_leaves_scope_template<int*>();
+}
+
+int global_i = 4;
+int *global_init_p = &global_i; // OK
+int *global_uninit_p;
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: the pset of global_uninit_p must be a subset of {(static), (null)}, but is {(invalid)}
+int *global_null_p = nullptr; // OK
+
+void uninitialized_static() {
+  static int* p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: the pset of p must be a subset of {(static), (null)}, but is {(invalid)}
+}
Index: test/clang-tidy/cppcoreguidelines-pro-lifetime-psets.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/cppcoreguidelines-pro-lifetime-psets.cpp
@@ -0,0 +1,512 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-lifetime %t -- -config="{CheckOptions: [{key: cppcoreguidelines-pro-lifetime.Debug, value: 1}]}" -- -std=c++11
+
+// TODO:
+// lifetime annotations
+// lambda
+// function calls
+
+template<typename T>
+void clang_analyzer_pset(const T&);
+
+int rand();
+
+struct S {
+  ~S();
+  int m;
+  void f() {
+    int* p = &m; // pset becomes m, not *this
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {m}
+  }
+};
+
+struct D : public S {
+  ~D();
+};
+
+void pointer_exprs() {
+  int *p;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(invalid)} [cppcoreguidelines-pro-lifetime]
+  int *q = nullptr;
+  clang_analyzer_pset(q);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(q) = {(null)}
+  int *q2 = 0;
+  clang_analyzer_pset(q2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(q2) = {(null)}
+  S s;
+  p = &s.m;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {s}
+  int a[2];
+  p = &a[0];
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {a}
+
+  D d;
+  S* ps = &d; // Ignore implicit cast
+  clang_analyzer_pset(ps);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(ps) = {d}
+
+  D* pd = (D*)&s; // Ignore explicit cast
+  clang_analyzer_pset(pd);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(pd) = {s}
+
+  int i;
+  p = q = &i;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+  clang_analyzer_pset(q);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(q) = {i}
+
+  p = rand()%2 ? &i : nullptr;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null), i}
+}
+
+void ref_exprs() {
+  bool b;
+  int i, j;
+  int& ref1 = i;
+  clang_analyzer_pset(ref1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(ref1) = {i}
+
+  int *p = &ref1;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+
+  int& ref2 = b ? i : j;
+  clang_analyzer_pset(ref2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(ref2) = {i, j}
+
+  // Lifetime extension
+  const int& ref3 = 3;
+  clang_analyzer_pset(ref3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(ref3) = {ref3'}
+
+  // Lifetime extension of pointer; FIXME is that correct?
+  int *const &refp = &i;
+  clang_analyzer_pset(refp);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(refp) = {refp'}
+
+}
+
+void addr_and_dref() {
+  int i;
+  int *p = &i;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+
+  int **p2 = &p;
+  clang_analyzer_pset(p2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p2) = {p}
+
+  int ***p3 = &p2;
+  clang_analyzer_pset(p3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p3) = {p2}
+
+  int **d2 = *p3;
+  clang_analyzer_pset(d2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(d2) = {p}
+
+  int *d1 = **p3;
+  clang_analyzer_pset(d1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(d1) = {i}
+
+  int **a = &**p3;
+  clang_analyzer_pset(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(a) = {p}
+
+  int *b = **&*p3;
+  clang_analyzer_pset(b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(b) = {i}
+
+  int *c = ***&p3;
+  clang_analyzer_pset(c);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(c) = {i}
+
+}
+
+void forbidden() {
+
+  int i;
+  int *p = &i;
+  p++;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+
+  p = &i;
+  p--;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+
+  p = &i;
+  ++p;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+
+  p = &i;
+  --p;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+
+  p = &i + 3;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+
+  int *q = &p[3];
+  clang_analyzer_pset(q);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(q) = {(unknown)}
+}
+
+void ref_leaves_scope() {
+  int *p;
+  {
+    int i = 0;
+    p = &i;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(invalid)}
+}
+
+void pset_scope_if(bool bb) {
+  int* p;
+  int i, j;
+  if (bb) {
+    p = &i;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+  }
+  else
+  {
+    p = &j;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {j}
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i, j}
+}
+
+
+void ref_leaves_scope_if(bool bb) {
+  int* p;
+  int j = 0;
+  if (bb) {
+    int i = 0;
+    p = &i;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+  }
+  else
+  {
+    p = &j;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {j}
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(invalid)}
+}
+
+void ref_to_declarator_leaves_scope_if() {
+  int* p;
+  int j = 0;
+  if (int i = 0) {
+    p = &i;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+  }
+  else {
+    p = &j;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {j}
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(invalid)}
+}
+
+void ignore_pointer_to_member() {
+  int S::* mp = &S::m; // pointer to member object; has no pset
+  void (S::*mpf)() = &S::f; // pointer to member function; has no pset
+}
+
+void if_stmt(int* p) {
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null), p'}
+
+  if(p) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {p'}
+  } else {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {(null), p'}
+  }
+
+  if(!p) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {(null), p'}
+  } else {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {p'}
+  }
+
+  char* q;
+  if(p && q) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {p'}
+  } else {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {(null), p'}
+  }
+
+  if(!p || !q) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {(null), p'}
+  } else {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {p'}
+  }
+}
+
+void implicit_else() {
+  int i = 0;
+  int j = 0;
+  int* p = rand()%2 ? &j : nullptr;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null), j}
+
+  if(!p) {
+    p = &i;
+  }
+
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i, j}
+}
+
+void condition_short_circuit(S *p) {
+  // FIXME: should not warn
+  if(p && p->m)
+  //CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dereferencing a (possible) null pointer [cppcoreguidelines-pro-lifetime]
+    ;
+}
+
+void switch_stmt() {
+  int initial;
+  int* p = &initial;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {initial}
+  int i;
+  int j;
+  switch(i)
+  {
+  case 0:
+    p = &i;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+    break;
+  case 1:
+    int k;
+    p = &k;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {k}
+  case 2:
+    p = &j;
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {j}
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {initial, i, j}
+}
+
+void for_stmt() {
+  int initial;
+  int* p = &initial;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {initial}
+  int j;
+  // There are different psets on the first and further iterations.
+  for(int i = 0; i < 1024; ++i) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {initial, j}
+    p = &j;
+  }
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {initial, j}
+}
+
+void for_stmt_ptr_decl() {
+  int i;
+  for (int *p = &i; ; ) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {i}
+  }
+}
+
+void goto_stmt(bool b) {
+  int *p = nullptr;
+  int i;
+l1:
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null), i}
+  p = &i;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+  if(b)
+    goto l1;
+
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+}
+
+void goto_skipping_decl(bool b) {
+  // When entering function start, there is no p;
+  // when entering from the goto, there was a p
+int i;
+l1:
+  int* p = nullptr;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null)}
+  if (b)
+    p = &i;
+    goto l1;
+}
+
+int goto_forward_over_decl() {
+  // When jumping over the declaration of p, we will never see that
+  // DeclStmt in the CFG
+  int j;
+  goto e;
+  int *p;
+e:
+  // FIXME: should be pset(p) = {(invalid)}
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: variable 'p' has no pset
+}
+
+
+void for_local_variable() {
+  int i;
+  int* p = &i;
+  while(true) {
+    clang_analyzer_pset(p);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(p) = {(invalid)}
+    int j;
+    p = &j; // p will become invalid on the next iteration, because j leaves scope
+  }
+}
+
+//Simplified from assert.h
+void __assert_fail () __attribute__ ((__noreturn__));
+# define assert(expr)                   \
+  ((expr)                               \
+   ? static_cast<void>(0)               \
+   : __assert_fail ())
+
+void asserting(int *p) {
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(null), p'}
+  assert(p);
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {p'}
+}
+
+int* global_p1 = nullptr;
+int* global_p2 = nullptr;
+int global_i;
+
+void namespace_scoped_vars(int param_i, int* param_p) {
+  clang_analyzer_pset(param_p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(param_p) = {(null), param_p'}
+
+  if(global_p1)
+  {
+    clang_analyzer_pset(global_p1);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pset(global_p1) = {(static)}
+    *global_p1 = 3;
+  }
+
+  int local_i;
+  global_p1 = &local_i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the pset of global_p1 must be a subset of {(static), (null)}, but is {local_i} [cppcoreguidelines-pro-lifetime]
+  global_p1 = &param_i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the pset of global_p1 must be a subset of {(static), (null)}, but is {param_i} [cppcoreguidelines-pro-lifetime]
+  global_p1 = param_p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the pset of global_p1 must be a subset of {(static), (null)}, but is {(null), param_p'} [cppcoreguidelines-pro-lifetime]
+
+  global_p1 = nullptr; // OK
+  clang_analyzer_pset(global_p1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(global_p1) = {(null)}
+  global_p1 = &global_i; // OK
+  clang_analyzer_pset(global_p1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(global_p1) = {(static)}
+  global_p1 = global_p2; // OK
+  clang_analyzer_pset(global_p1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(global_p1) = {(null), (static)}
+}
+
+void function_call() {
+  void f(int *p);
+
+  int i;
+  int *p = &i;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+  f(p);
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+}
+
+void function_call2() {
+  void f(int **p);
+  void const_f(int *const* p); //cannot change *p
+
+  int i;
+  int *p = &i;
+  int **pp = &p;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+  clang_analyzer_pset(pp);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(pp) = {p}
+
+  const_f(pp);
+  clang_analyzer_pset(pp);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(pp) = {p}
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+
+  // TODO: fix me
+  f(pp);
+  clang_analyzer_pset(pp);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(pp) = {p}
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+}
+
+void function_call3() {
+  void f(int *&p);
+
+  int i;
+  int *p = &i;
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {i}
+  f(p);
+  // TODO: fix me
+  clang_analyzer_pset(p);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(p) = {(unknown)}
+}
+
+void argument_ref_to_temporary() {
+  //like std::min
+  const int& min(const int& a, const int& b);
+  int x=10, y=2;
+  const int& good = min(x,y); // ok, pset(good) == {x,y}
+  //FIXME: pset should be {x, y}
+  clang_analyzer_pset(good);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(good) = {(unknown)}
+
+  const int& bad = min(x,y+1);
+  //FIXME: pset should be {(invalid)}
+  clang_analyzer_pset(bad);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pset(bad) = {(unknown)}
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -20,6 +20,7 @@
    cppcoreguidelines-pro-bounds-array-to-pointer-decay
    cppcoreguidelines-pro-bounds-constant-array-index
    cppcoreguidelines-pro-bounds-pointer-arithmetic
+   cppcoreguidelines-pro-lifetime
    cppcoreguidelines-pro-type-const-cast
    cppcoreguidelines-pro-type-cstyle-cast
    cppcoreguidelines-pro-type-member-init
Index: docs/clang-tidy/checks/cppcoreguidelines-pro-lifetime.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/cppcoreguidelines-pro-lifetime.rst
@@ -0,0 +1,12 @@
+cppcoreguidelines-pro-lifetime
+==============================
+
+This checker implements the lifetime rules presented in the paper
+by Herb Sutter and Neil MacIntosh paper [1].
+Basically, for each pointer we track to which owner it directly or
+transitivly points to. If the owner are invalidated, all their pointers
+will become invalid, too.
+This should eliminate all possibilites for dangling pointers or null pointer
+dereferences.
+
+[1] https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetimes%20I%20and%20II%20-%20v0.9.1.pdf
Index: clang-tidy/cppcoreguidelines/ProLifetimeVisitor.h
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/ProLifetimeVisitor.h
@@ -0,0 +1,24 @@
+/*
+ * ProLifetimeVisitor.h
+ *
+ *  Created on: Oct 23, 2015
+ *      Author: mgehre
+ */
+
+#ifndef CLANG_TIDY_CPPCOREGUIDELINES_PROLIFETIMEVISITOR_H_
+#define CLANG_TIDY_CPPCOREGUIDELINES_PROLIFETIMEVISITOR_H_
+#include "../ClangTidyDiagnosticConsumer.h"
+#include "clang/AST/ASTContext.h"
+
+namespace clang {
+namespace tidy {
+void VisitFunction(const FunctionDecl *Func, ASTContext *ASTCtxt,
+                   SourceManager *SourceMgr, bool DebugPSets,
+                   ClangTidyContext *CTContext);
+void VisitGlobalVar(const VarDecl *V, ASTContext *ASTCtxt,
+                    SourceManager *SourceMgr, bool DebugPSets,
+                    ClangTidyContext *CTContext);
+}
+}
+
+#endif /* CLANG_TIDY_CPPCOREGUIDELINES_PROLIFETIMEVISITOR_H_ */
Index: clang-tidy/cppcoreguidelines/ProLifetimeVisitor.cpp
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/ProLifetimeVisitor.cpp
@@ -0,0 +1,1477 @@
+//===--- ProLifetimeVisitor.cpp - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// The pset of a (pointer/reference) variable can be modified by
+//   1) Initialization
+//   2) Assignment
+// It will be set to the pset of the expression on the right-hand-side.
+// Such expressions can contain:
+//   1) Casts: Ignored, pset(expr) == pset((cast)expr)
+//   2) Address-Of operator:
+//        It can only be applied to lvalues, i.e.
+//        VarDecl, pset(&a) = {a}
+//        a function returning a ref,
+//        result of an (compound) assignment, pset(&(a = b)) == {b}
+//        pre-in/decrement, pset(&(--a)) = {a}
+//        deref,
+//        array subscript, pset(&a[3]) = {a}
+//        a.b, a.*b: pset(&a.b) = {a}
+//        a->b, a->*b,
+//        comma expr, pset(&(a,b)) = {b}
+//        string literal, pset(&"string") = {static}
+//        static_cast<int&>(x)
+//
+//   3) Dereference operator
+//   4) Function calls and CXXMemberCallExpr
+//   5) MemberExpr: pset(this->a) = {a}; pset_ref(o->a) = {o}; pset_ptr(o->a) =
+//   {o'}
+//   6) Ternary: pset(cond ? a : b) == pset(a) union pset(b)
+//   7) Assignment operator: pset(a = b) == {b}
+// Rules:
+//  1) T& p1 = expr; T* p2 = &expr; -> pset(p1) == pset(p2) == pset_ref(expr)
+//  2) T& p1 = *expr; T* p2 = expr; -> pset(p1) == pset(p2) == pset_ptr(expr)
+//  3) Casts are ignored: pset(expr) == pset((cast)expr)
+//  4) T* p1 = &C.m; -> pset(p1) == {C} (per ex. 1.3)
+//  5) T* p2 = C.get(); -> pset(p2) == {C'} (per ex. 8.1)
+//
+// Assumptions:
+// - The 'this' pointer cannot be invalidated inside a member method (enforced
+// here: no delete on *this)
+// - Global variable's pset is (static) and/or (null) (enforced here)
+// - Arithmetic on pointer types is forbidden (enforced by
+// cppcoreguidelines-pro-type-pointer-arithmetic)
+// - A function does not modify const arguments (enforced by
+// cppcoreguidelines-pro-type-pointer-arithmetic)
+// - An access to an array through array subscription always points inside the
+// array (enforced by cppcoreguidelines-pro-bounds)
+//
+// TODO:
+//  track psets for objects containing Pointers (e.g. iterators)
+//  handle function/method call in PSetFromExpr
+//  check correct use of gsl::owner<> (delete only on 'valid' owner, delete must
+//  happen before owner goes out of scope)
+//  handle try-catch blocks (AC.getCFGBuildOptions().AddEHEdges = true)
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProLifetimeVisitor.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/CFG.h"
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <unordered_map>
+
+namespace clang {
+namespace tidy {
+namespace {
+
+/// A Pointer is a
+/// 1) VarDecl or
+/// 2) FieldDecl (on 'this')
+/// and
+/// a) with pointer type, e.g. p in 'int* p' or
+/// b) with reference type, e.g. p in 'int& p' or
+/// c) of an object that contains a Pointer, e.g. p in 'struct { int* q; } p;'
+/// or
+/// TODO: implement case b) and c)
+/// Invariant: VD != FD && (!VD || !FD)
+class Pointer {
+  const VarDecl *VD = nullptr;
+  const FieldDecl *FD = nullptr;
+
+public:
+  Pointer(const VarDecl *VD) : VD(VD) { assert(VD); }
+  Pointer(const FieldDecl *FD) : FD(FD) { assert(FD); }
+  Pointer(Pointer &&) = default;
+  Pointer &operator=(Pointer &&) = default;
+  Pointer(const Pointer &) = default;
+  Pointer &operator=(const Pointer &) = default;
+
+  bool operator==(const Pointer &o) const { return VD == o.VD && FD == o.FD; }
+  bool operator!=(const Pointer &o) const { return !(*this == o); }
+  bool operator<(const Pointer &o) const {
+    if (VD != o.VD)
+      return VD < o.VD;
+    return FD < o.FD;
+  }
+
+  QualType getCanonicalType() const {
+    if (VD)
+      return VD->getType().getCanonicalType();
+    return FD->getType().getCanonicalType();
+  }
+
+  StringRef getName() const {
+    if (VD)
+      return VD->getName();
+    return FD->getName();
+  }
+
+  bool hasGlobalStorage() const { return VD && VD->hasGlobalStorage(); }
+  /// Returns true if this pointer is a member variable of the class of the
+  /// current method
+  bool isMemberVariable() const { return FD; }
+
+  bool mayBeNull() const {
+    // TODO: check if the type is gsl::not_null
+    return isa<PointerType>(getCanonicalType());
+  }
+
+  bool isRealPointer() const { return getCanonicalType()->isPointerType(); }
+  bool isReference() const { return getCanonicalType()->isReferenceType(); }
+
+  std::size_t hash() const noexcept {
+    return VD ? std::hash<const VarDecl *>()(VD)
+              : std::hash<const FieldDecl *>()(FD);
+  }
+
+  // Returns either a Pointer or None if ValD is not a Pointer
+  static Optional<Pointer> get(const ValueDecl *ValD) {
+    if (!ValD)
+      return Optional<Pointer>();
+
+    if (!Pointer::is(ValD->getType().getCanonicalType()))
+      return Optional<Pointer>();
+
+    if (auto *VD = dyn_cast<VarDecl>(ValD))
+      return {VD};
+    if (auto *FD = dyn_cast<FieldDecl>(ValD))
+      return {FD};
+
+    return Optional<Pointer>(); // TODO: can this happen?
+  }
+
+  // static bool is(const ValueDecl *VD) { return get(VD).hasValue(); }
+
+  /// Returns true if the given type has a pset
+  static bool is(QualType QT) {
+    return QT->isReferenceType() || QT->isPointerType();
+  }
+};
+}
+}
+}
+
+namespace std {
+template <> struct hash<clang::tidy::Pointer> {
+  std::size_t operator()(const clang::tidy::Pointer &P) const noexcept {
+    return P.hash();
+  }
+};
+}
+
+namespace clang {
+namespace tidy {
+namespace {
+/// An owner is anything that a Pointer can point to
+/// Invariant: VD != nullptr
+class Owner {
+  enum SpecialType { VARDECL = 0, NULLPTR = 1, STATIC = 2, TEMPORARY = 3 };
+
+  llvm::PointerIntPair<const ValueDecl *, 2> VD;
+
+  Owner(SpecialType ST) : VD(nullptr, ST) {}
+
+public:
+  Owner(const ValueDecl *VD) : VD(VD, VARDECL) {
+    assert(VD);
+    if (const auto *VarD = dyn_cast<VarDecl>(VD))
+      if (VarD->hasGlobalStorage())
+        *this = Static();
+  }
+  Owner(Owner &&) = default;
+  Owner &operator=(Owner &&) = default;
+  Owner(const Owner &) = default;
+  Owner &operator=(const Owner &) = default;
+  bool operator==(const Owner &o) const { return VD == o.VD; }
+  bool operator!=(const Owner &o) const { return !(*this == o); }
+  bool operator<(const Owner &o) const { return VD < o.VD; }
+
+  std::string getName() const {
+
+    switch (VD.getInt()) {
+    case VARDECL:
+      return VD.getPointer()->getNameAsString();
+    case NULLPTR:
+      return "(null)";
+    case STATIC:
+      return "(static)";
+    case TEMPORARY:
+      return "(temporary)";
+    }
+    llvm_unreachable("Unexpected type");
+  }
+
+  /// Special Owner to refer to a nullptr
+  static Owner Null() { return Owner(NULLPTR); }
+
+  /// An owner that can not be invalidated.
+  /// Used for variables with static duration
+  static Owner Static() { return Owner(STATIC); }
+
+  /// An owner that will vanish at the end of the full expression,
+  /// e.g. a temporary bound to const reference parameter.
+  static Owner Temporary() { return Owner(TEMPORARY); }
+
+  /// Returns either a Pointer or None if Owner is not a Pointer
+  Optional<Pointer> getPointer() const {
+    if (VD.getInt() == VARDECL)
+      return Pointer::get(VD.getPointer());
+    return Optional<Pointer>();
+  }
+};
+
+/// The reason why a pset became invalid
+/// Invariant: (Reason != POINTEE_LEFT_SCOPE || Pointee) && Loc.isValid()
+class InvalidationReason {
+  enum { NOT_INITIALIZED, POINTEE_LEFT_SCOPE, TEMPORARY_LEFT_SCOPE } Reason;
+  const VarDecl *Pointee = nullptr;
+  SourceLocation Loc;
+  InvalidationReason() = default;
+
+public:
+  SourceLocation getLoc() const { return Loc; }
+
+  std::string str() const {
+    switch (Reason) {
+    case NOT_INITIALIZED:
+      return "was never initialized";
+    case POINTEE_LEFT_SCOPE:
+      assert(Pointee);
+      return std::string("the pointee ") + Pointee->getNameAsString() +
+             " left the scope";
+    case TEMPORARY_LEFT_SCOPE:
+      return "temporary was destroyed at the end of the full expression";
+    }
+    return "";
+  }
+
+  static InvalidationReason NotInitialized(SourceLocation Loc) {
+    assert(Loc.isValid());
+    InvalidationReason R;
+    R.Loc = Loc;
+    R.Reason = NOT_INITIALIZED;
+    return R;
+  }
+
+  static InvalidationReason PointeeLeftScope(SourceLocation Loc,
+                                             const VarDecl *Pointee) {
+    assert(Loc.isValid());
+    assert(Pointee);
+    InvalidationReason R;
+    R.Loc = Loc;
+    R.Reason = POINTEE_LEFT_SCOPE;
+    R.Pointee = Pointee;
+    return R;
+  }
+
+  static InvalidationReason TemporaryLeftScope(SourceLocation Loc) {
+    assert(Loc.isValid());
+    InvalidationReason R;
+    R.Loc = Loc;
+    R.Reason = TEMPORARY_LEFT_SCOPE;
+    return R;
+  }
+};
+
+/// A pset (points-to set) can be unknown, invalid or valid.
+/// If it is valid, it can contain (static), (null) and a set of (Owner,order)
+/// Invariant: (!isInvalid() || Reason.hasValue())
+///         && (!isValid() || p.size() > 0)
+class PSet {
+public:
+  enum State {
+    Valid,           // the pointer points to one element of p
+    Unknown,         // we lost track of what it could point to
+    PossiblyInvalid, // the pointer had a pset containing multiple objects, and
+                     // one of them was killed
+    Invalid, // the pointer was never initialized or the single member of its
+             // pset was killed
+  };
+
+  PSet() : state(Unknown) {}
+
+  PSet(PSet &&) = default;
+  PSet &operator=(PSet &&) = default;
+  PSet(const PSet &) = default;
+  PSet &operator=(const PSet &) = default;
+
+  bool operator==(const PSet &o) const {
+    if (state == Valid)
+      return o.p == p;
+    return state == o.state;
+  }
+
+  bool operator!=(const PSet &o) const { return !(*this == o); }
+
+  State getState() const { return state; }
+
+  InvalidationReason getReason() const {
+    assert(isInvalid());
+    assert(Reason.hasValue());
+    return Reason.getValue();
+  }
+
+  bool isValid() const { return state == Valid; }
+
+  bool isInvalid() const {
+    return state == Invalid || state == PossiblyInvalid;
+  }
+
+  bool isUnknown() const { return state == Unknown; }
+
+  /// Returns true if this pset contains Owner with the same or a lower order
+  /// i.e. if invalidating (Owner, order) would invalidate this pset
+  bool contains(Owner Owner, unsigned order) const {
+    if (state != Valid)
+      return false;
+
+    for (const auto &i : p)
+      if (i.first == Owner && i.second >= order)
+        return true;
+
+    return false;
+  }
+
+  bool isSingular() const { return p.size() == 1; }
+
+  bool containsNull() const { return contains(Owner::Null(), 0); }
+
+  void removeNull() {
+    assert(isValid());
+    p.erase(Owner::Null());
+  }
+
+  bool isSubsetOf(const PSet &PS) {
+    assert(getState() != Unknown);
+    if (isInvalid())
+      return false;
+
+    return std::includes(PS.p.begin(), PS.p.end(), p.begin(), p.end());
+  }
+
+  std::string str() const {
+    std::stringstream ss;
+    switch (state) {
+    case Unknown:
+      ss << "(unknown)";
+      break;
+    case Invalid:
+      ss << "(invalid)";
+      break;
+    case PossiblyInvalid:
+      ss << "(possibly invalid)";
+      break;
+    case Valid:
+      bool first = true;
+      for (const auto &i : p) {
+        if (!first)
+          ss << ", ";
+        else
+          first = false;
+        ss << i.first.getName();
+        for (size_t j = 0; j < i.second; ++j)
+          ss << "'";
+      }
+      break;
+    }
+    return ss.str();
+  }
+
+  void print(raw_ostream &Out) const { Out << str() << "\n"; }
+
+  /// Merge contents of other pset into this
+  void merge(const PSet &otherPset) {
+    if (state == Unknown || otherPset.state == Unknown) {
+      state = Unknown;
+      return;
+    }
+    if (state == Invalid)
+      return;
+    if (otherPset.state == Invalid) {
+      state = Invalid;
+      assert(otherPset.Reason.hasValue());
+      Reason = otherPset.Reason;
+      return;
+    }
+    if (state == PossiblyInvalid)
+      return;
+    if (otherPset.state == PossiblyInvalid) {
+      state = PossiblyInvalid;
+      assert(otherPset.Reason.hasValue());
+      Reason = otherPset.Reason;
+      return;
+    }
+    assert(state == Valid && otherPset.state == Valid);
+
+    for (const auto &PO : otherPset.p) {
+      auto P = p.find(PO.first);
+      if (P == p.end()) {
+        p.insert(PO);
+      } else {
+        // if p contains obj' and otherPset.p contains obj''
+        // then the union shall be invalidated whenever obj' or obj'' is
+        // invalidated
+        // which is the same as whenever obj'' is invalidated
+        P->second = std::max(P->second, PO.second);
+      }
+    }
+  }
+
+  /// The pointer is dangling
+  static PSet invalid(InvalidationReason Reason) {
+    PSet ret;
+    ret.state = Invalid;
+    ret.Reason = Reason;
+    return ret;
+  }
+
+  /// We don't know the state of the pointer
+  static PSet unknown() {
+    PSet ret;
+    ret.state = Unknown;
+    return ret;
+  }
+
+  /// The pset contains obj, obj' or obj''
+  static PSet valid() {
+    PSet ret;
+    ret.state = Valid;
+    return ret;
+  }
+
+  /// The pset contains obj, obj' or obj''
+  static PSet validOwner(Owner Owner, unsigned order = 0) {
+    PSet ret;
+    ret.state = Valid;
+    ret.p.emplace(Owner, order);
+    return ret;
+  }
+
+  /// The pset contains obj, obj' or obj''
+  static PSet validOwnerOrNull(Owner Owner, unsigned order = 0) {
+    PSet ret = validOwner(Owner, order);
+    ret.p.emplace(Owner::Null(), 0);
+    return ret;
+  }
+
+  std::map<Owner, unsigned>::const_iterator begin() const { return p.begin(); }
+
+  std::map<Owner, unsigned>::const_iterator end() const { return p.end(); }
+
+  void insert(Owner Owner, unsigned order = 0) {
+    p.insert(std::make_pair(Owner, order));
+  }
+
+private:
+  State state = Unknown;
+  /// Maps owner obj to order.
+  /// (obj,0) == obj: points to obj
+  /// (obj,1) == obj': points to object owned directly by obj
+  /// (obj,2) == obj'': points an object kept alive indirectly (transitively)
+  /// via owner obj
+  std::map<Owner, unsigned> p;
+
+  Optional<InvalidationReason> Reason;
+};
+
+using PSetsMap = std::unordered_map<Pointer, PSet>;
+
+void dump(const PSetsMap &PSets) {
+  for (auto &i : PSets)
+    llvm::errs() << "pset(" << i.first.getName() << ") = " << i.second.str()
+                 << "\n";
+}
+
+class LifetimeReporter {
+  bool Debug; // TODO: remove me?
+  ClangTidyContext *CTContext;
+
+public:
+  LifetimeReporter(bool Debug, ClangTidyContext *CTContext)
+      : Debug(Debug), CTContext(CTContext) {}
+
+  /// This may either forward to a contained DiagnosticBuilder or no-op
+  class CondDiagnosticBuilder {
+    Optional<DiagnosticBuilder> DB;
+
+  public:
+    CondDiagnosticBuilder() = default;
+    CondDiagnosticBuilder(DiagnosticBuilder DB) : DB(std::move(DB)) {}
+    template <typename T>
+    const CondDiagnosticBuilder &operator<<(const T &v) const {
+      if (DB.hasValue())
+        DB.getValue() << v;
+      return *this;
+    }
+  };
+
+  CondDiagnosticBuilder DebugMsg(SourceLocation Loc, StringRef Message) const {
+    if (Debug)
+      return reportBug(Loc, Message);
+    return {};
+  }
+
+  void PsetDebug(const PSet &PS, SourceLocation Loc,
+                 const Pointer &Pointer) const {
+    DebugMsg(Loc, "pset(%0) = {%1}") << Pointer.getName() << PS.str();
+  }
+
+  DiagnosticBuilder reportBug(SourceLocation Loc, StringRef Message) const {
+    return CTContext->diag("cppcoreguidelines-pro-lifetime", Loc, Message,
+                           DiagnosticIDs::Warning);
+  }
+
+  DiagnosticBuilder reportNote(SourceLocation Loc, StringRef Message) const {
+    return CTContext->diag("cppcoreguidelines-pro-lifetime", Loc, Message,
+                           DiagnosticIDs::Note);
+  }
+};
+
+// Collection of methods to update/check PSets from statements/expressions
+class PSetsBuilder {
+
+  const LifetimeReporter *Reporter;
+  ASTContext *ASTCtxt;
+  PSetsMap &PSets;
+
+  /// IgnoreParenImpCasts - Ignore parentheses and implicit casts.  Strip off
+  /// any ParenExpr
+  /// or ImplicitCastExprs, returning their operand.
+  /// Does not ignore MaterializeTemporaryExpr as Expr::IgnoreParenImpCasts
+  /// would.
+  const Expr *IgnoreParenImpCasts(const Expr *E) {
+    while (true) {
+      E = E->IgnoreParens();
+      if (const auto *P = dyn_cast<ImplicitCastExpr>(E)) {
+        E = P->getSubExpr();
+        continue;
+      }
+      return E;
+    }
+  }
+
+  void EvalExpr(const Expr *E) {
+    E = E->IgnoreParenCasts();
+
+    switch (E->getStmtClass()) {
+    case Expr::BinaryOperatorClass: {
+      const auto *BinOp = cast<BinaryOperator>(E);
+      Optional<Pointer> P = PointerFromExpr(BinOp->getLHS());
+      if (P.hasValue() && P->isRealPointer()) {
+        switch (BinOp->getOpcode()) {
+        case BO_Assign: {
+          // This assignment updates a Pointer
+          EvalExpr(BinOp->getLHS()); // Eval for side-effects
+          PSet PS = EvalExprForPSet(BinOp->getRHS(), false);
+          SetPSet(P.getValue(), PS, BinOp->getExprLoc());
+          return;
+        }
+        case BO_AddAssign:
+        case BO_SubAssign:
+          // Affects pset; forbidden by the bounds profile.
+          SetPSet(P.getValue(), PSet::unknown(), BinOp->getExprLoc());
+        default:
+          break; // Traversing is done below
+        }
+      }
+      break;
+    }
+    case Expr::UnaryOperatorClass: {
+      const auto *UnOp = cast<UnaryOperator>(E);
+      Optional<Pointer> P = PointerFromExpr(UnOp->getSubExpr());
+      if (P.hasValue() && P->isRealPointer()) {
+        switch (UnOp->getOpcode()) {
+        case UO_Deref: {
+          // Check if dereferencing this pointer is valid
+          PSet PS = EvalExprForPSet(UnOp->getSubExpr(), false);
+          CheckPSetValidity(PS, UnOp->getExprLoc());
+          return;
+        }
+        case UO_PostInc:
+        case UO_PostDec:
+        case UO_PreInc:
+        case UO_PreDec:
+          // Affects pset; forbidden by the bounds profile.
+          SetPSet(P.getValue(), PSet::unknown(), UnOp->getExprLoc());
+        default:
+          break; // Traversing is done below
+        }
+      }
+      break;
+    }
+    case Expr::MemberExprClass: {
+      const auto *MemberE = cast<MemberExpr>(E);
+      const Expr *Base = MemberE->getBase();
+      // 'this' can never dangle
+      // TODO: check pset for !isArrow if Base is a reference
+      if (!isa<CXXThisExpr>(Base) && MemberE->isArrow()) {
+        PSet PS = EvalExprForPSet(Base, false);
+        CheckPSetValidity(PS, MemberE->getExprLoc());
+        return;
+      }
+      break;
+    }
+    case Expr::CallExprClass: {
+      if (EvalCallExpr(cast<CallExpr>(E)))
+        return;
+      break;
+    }
+    default:;
+    }
+    // Other Expr, just recurse
+    for (const Stmt *SubStmt : E->children())
+      EvalStmt(SubStmt);
+  }
+
+  /// Evaluates the CallExpr for effects on psets
+  /// When a non-const pointer to pointer or reference to pointer is passed
+  /// into
+  /// a function,
+  /// it's pointee's are invalidated.
+  /// Returns true if CallExpr was handeld
+  bool EvalCallExpr(const CallExpr *CallE) {
+    const auto *D = dyn_cast_or_null<FunctionDecl>(CallE->getCalleeDecl());
+    if (!D)
+      return false; // happens for CXXPseudoDestructorExpr
+
+    if (D->isVariadic())
+      return false; // Forbidden by type profile
+
+    if (D->getBuiltinID())
+      return false;
+
+    EvalExpr(CallE->getCallee());
+    const auto *CXXD = dyn_cast<CXXMethodDecl>(D);
+
+    if (const auto *MemberCallE = dyn_cast<CXXMemberCallExpr>(CallE)) {
+      const auto *ThisE = MemberCallE->getImplicitObjectArgument();
+      EvalExpr(ThisE);
+      // TODO: something like
+      // if(!MemberCallE->getMethodDecl()->isConst())
+      //   invalidateOwner(ThisE, 1, E->getExprLoc());
+    }
+
+    for (unsigned i = 0; i < CallE->getNumArgs(); ++i) {
+      const Expr *Arg = CallE->getArg(i);
+
+      QualType ParamType;
+      if (isa<CXXOperatorCallExpr>(CallE) && CXXD) {
+        // For CXXOperatorCallExpr, getArg(0) is the 'this' pointer
+        if (i == 0)
+          ParamType = CXXD->getThisType(*ASTCtxt).getCanonicalType();
+        else
+          ParamType = D->getParamDecl(i - 1)->getType().getCanonicalType();
+        //} else if (CXXD && !CXXD->isStatic()) {
+      } else
+        ParamType = D->getParamDecl(i)->getType().getCanonicalType();
+
+      QualType ArgType = Arg->getType().getCanonicalType();
+      // Check if the function can affect other psets through this argument
+      // (i.e. pointer to pointer or reference to pointer)
+      bool hasPSet = Pointer::is(ArgType);
+      bool canNotModifyPSet =
+          (ParamType->isPointerType() || ParamType->isReferenceType()) &&
+          ParamType->getPointeeType().isConstQualified();
+      if (!hasPSet || canNotModifyPSet) {
+        // Is not a Pointer, or its Pointee's are const and thus cannot be
+        // invalidated
+        EvalExpr(Arg);
+        continue;
+      }
+
+      PSet PS = EvalExprForPSet(Arg, !ParamType->isPointerType());
+      if (PS.isUnknown() || PS.isInvalid())
+        continue;
+
+      // Everything in this pset may be invalidated
+      for (auto &Entry : PS) {
+        if (Entry.first == Owner::Null() || Entry.first == Owner::Static())
+          continue;
+
+        // Check if this Owner is also a Pointer
+        Optional<Pointer> IndirectP = Entry.first.getPointer();
+        if (IndirectP.hasValue())
+          // TODO: add possible values; for now just stop tracking
+          SetPSet(IndirectP.getValue(), PSet::unknown(), CallE->getExprLoc());
+      }
+    }
+    return true;
+  }
+  /// Evaluates E for effects that change psets.
+  /// If ExprPset is not null and
+  /// 1) referenceCtx is true, set ExprPset to the pset of 'p' in 'auto &p =
+  /// E';
+  /// 2) referenceCtx is false, set ExprPset to the pset of 'p' in 'auto *p =
+  /// E';
+  ///
+  /// We use pset_ptr(E) to denote ExprPset when referenceCtx is true
+  /// and pset_ref(E) to denote ExprPset when referenceTxt is false.
+  /// We use pset(v) when v is a VarDecl to refer to the entry of v in the
+  /// PSets
+  /// map.
+  PSet EvalExprForPSet(const Expr *E, bool referenceCtx) {
+    E = IgnoreParenImpCasts(E);
+
+    switch (E->getStmtClass()) {
+    case Expr::MaterializeTemporaryExprClass: {
+      const auto *MaterializeTemporaryE = cast<MaterializeTemporaryExpr>(E);
+      assert(referenceCtx);
+      EvalExpr(MaterializeTemporaryE->GetTemporaryExpr());
+
+      if (MaterializeTemporaryE->getExtendingDecl())
+        return PSet::validOwner(MaterializeTemporaryE->getExtendingDecl(), 1);
+      else
+        return PSet::validOwner(Owner::Temporary(), 0);
+      break;
+    }
+    case Expr::DeclRefExprClass: {
+      const auto *DeclRef = cast<DeclRefExpr>(E);
+      const auto *VD = dyn_cast<VarDecl>(DeclRef->getDecl());
+      if (VD) {
+        if (referenceCtx) {
+          // T i; T &j = i; auto &p = j; -> pset_ref(p) = {i}
+          if (VD->getType()->isReferenceType())
+            return GetPSet(VD);
+          else
+            // T i; auto &p = i; -> pset_ref(p) = {i}
+            return PSet::validOwner(VD, 0);
+        } else {
+          if (VD->getType()->isArrayType())
+            // Forbidden by bounds profile
+            // Alternative would be: T a[]; auto *p = a; -> pset_ptr(p) = {a}
+            return PSet::unknown();
+          else
+            // T i; auto *p = i; -> pset_ptr(p) = pset(i)
+            return GetPSet(VD);
+        }
+      }
+      break;
+    }
+    case Expr::ArraySubscriptExprClass: {
+      const auto *ArraySubscriptE = cast<ArraySubscriptExpr>(E);
+      EvalExpr(ArraySubscriptE->getBase());
+      EvalExpr(ArraySubscriptE->getIdx());
+
+      // By the bounds profile, ArraySubscriptExpr is only allowed on arrays
+      // (not on pointers),
+      // thus the base needs to be a DeclRefExpr.
+      const auto *DeclRef = dyn_cast<DeclRefExpr>(
+          ArraySubscriptE->getBase()->IgnoreParenImpCasts());
+      if (DeclRef) {
+        const VarDecl *VD = dyn_cast<VarDecl>(DeclRef->getDecl());
+        if (VD && VD->getType().getCanonicalType()->isArrayType() &&
+            referenceCtx)
+          // T a[3]; -> pset_ref(a[i]) = {a}
+          return PSet::validOwner(VD, 0);
+      }
+      break;
+    }
+    case Expr::ConditionalOperatorClass: {
+      const auto *CO = cast<ConditionalOperator>(E);
+      // pset_ref(b ? a : b) = pset_ref(a) union pset_ref(b)
+      // pset_ptr(b ? a : b) = pset_ptr(a) union pset_ptr(b)
+      EvalExpr(CO->getCond());
+
+      PSet PSLHS = EvalExprForPSet(CO->getLHS(), referenceCtx);
+      PSet PSRHS = EvalExprForPSet(CO->getRHS(), referenceCtx);
+      PSRHS.merge(PSLHS);
+      return PSRHS;
+    }
+    case Expr::MemberExprClass: {
+      const auto *MemberE = cast<MemberExpr>(E);
+      const Expr *Base = MemberE->getBase();
+      if (isa<CXXThisExpr>(Base)) {
+        // We are inside the class, so track the members separately
+        const auto *FD = dyn_cast<FieldDecl>(MemberE->getMemberDecl());
+        if (FD) {
+          return PSet::validOwner(FD, 0);
+        }
+      } else {
+        return EvalExprForPSet(
+            Base, !Base->getType().getCanonicalType()->isPointerType());
+      }
+      break;
+    }
+    case Expr::BinaryOperatorClass: {
+      const auto *BinOp = cast<BinaryOperator>(E);
+      if (BinOp->getOpcode() == BO_Assign) {
+        EvalExpr(BinOp);
+        return EvalExprForPSet(BinOp->getLHS(), referenceCtx);
+      }
+      break;
+    }
+    case Expr::UnaryOperatorClass: {
+      const auto *UnOp = cast<UnaryOperator>(E);
+      if (UnOp->getOpcode() == UO_Deref) {
+        PSet PS = EvalExprForPSet(UnOp->getSubExpr(), false);
+        CheckPSetValidity(PS, UnOp->getOperatorLoc());
+        if (referenceCtx) {
+          // pset_ref(*p) = pset_ptr(p)
+          return PS;
+        } else {
+          if (PS.isInvalid() || PS.isUnknown())
+            return PS;
+
+          assert(PS.isValid());
+          // pset_ptr(*p) = replace each pset entry of pset_ptr(p) by its own
+          // pset
+          PSet RetPS = PSet::valid();
+          for (auto &Entry : PS) {
+            if (Entry.first == Owner::Null())
+              continue; // will be flagged by checkPSetValidity above
+            if (Entry.first == Owner::Static())
+              RetPS.merge(PSet::validOwner(Owner::Static()));
+            else {
+              Optional<Pointer> P = Entry.first.getPointer();
+              if (!P.hasValue())
+                // This can happen if P has array type; dereferencing an array
+                // is forbidden by the bounds profile
+                return PSet::unknown();
+              RetPS.merge(GetPSet(P.getValue()));
+            }
+          }
+          return RetPS;
+        }
+      } else if (UnOp->getOpcode() == UO_AddrOf) {
+        assert(!referenceCtx);
+        return EvalExprForPSet(UnOp->getSubExpr(), true);
+      }
+      break;
+    }
+    case Expr::CXXReinterpretCastExprClass:
+    case Expr::CStyleCastExprClass: {
+      const auto *CastE = cast<CastExpr>(E);
+      switch (CastE->getCastKind()) {
+      case CK_BitCast:
+      case CK_LValueBitCast:
+      case CK_IntegralToPointer:
+        // Those casts are forbidden by the type profile
+        return PSet::unknown();
+      default:
+        return EvalExprForPSet(CastE->getSubExpr(), referenceCtx);
+      }
+    }
+    default:;
+    }
+
+    if (E->isNullPointerConstant(*ASTCtxt, Expr::NPC_ValueDependentIsNotNull)) {
+      if (referenceCtx)
+        // It is illegal to bind a reference to null
+        return PSet::invalid(
+            InvalidationReason::NotInitialized(E->getExprLoc()));
+      else
+        return PSet::validOwner(Owner::Null());
+    }
+
+    // Unhandled case
+    EvalExpr(E);
+    return PSet::unknown();
+  }
+
+  /// Returns a Pointer if E is a DeclRefExpr of a Pointer
+  Optional<Pointer> PointerFromExpr(const Expr *E) {
+    E = E->IgnoreParenCasts();
+    const auto *DeclRef = dyn_cast<DeclRefExpr>(E);
+    if (!DeclRef)
+      return Optional<Pointer>();
+
+    return Pointer::get(DeclRef->getDecl());
+  }
+
+  void CheckPSetValidity(const PSet &PS, SourceLocation Loc);
+
+  // Traverse S in depth-first post-order and evaluate all effects on psets
+  void EvalStmt(const Stmt *S) {
+    if (const auto *DS = dyn_cast<DeclStmt>(S)) {
+      assert(DS->isSingleDecl());
+      const Decl *D = DS->getSingleDecl();
+      if (const auto *VD = dyn_cast<VarDecl>(D))
+        EvalVarDecl(VD);
+    } else if (const auto *E = dyn_cast<Expr>(S))
+      EvalExpr(E);
+  }
+
+  /// Invalidates all psets that point to V or something owned by V
+  void invalidateOwner(Owner O, unsigned order, InvalidationReason Reason) {
+    for (auto &i : PSets) {
+      const auto &Pointer = i.first;
+      PSet &PS = i.second;
+      if (!PS.isValid())
+        continue; // Nothing to invalidate
+
+      if (PS.contains(O, order))
+        SetPSet(Pointer, PSet::invalid(Reason), Reason.getLoc());
+    }
+  }
+  void erasePointer(Pointer P) { PSets.erase(P); }
+  PSet GetPSet(Pointer P);
+  void SetPSet(Pointer P, PSet PS, SourceLocation Loc);
+  void diagPSet(Pointer P, SourceLocation Loc) {
+    auto i = PSets.find(P);
+    if (i != PSets.end())
+      Reporter->PsetDebug(i->second, Loc, P);
+    else
+      Reporter->reportBug(Loc, "variable '%0' has no pset") << P.getName();
+  }
+
+  bool HandleClangAnalyzerPset(const Stmt *S, const LifetimeReporter *Reporter);
+
+public:
+  PSetsBuilder(const LifetimeReporter *Reporter, ASTContext *ASTCtxt,
+               PSetsMap &PSets)
+      : Reporter(Reporter), ASTCtxt(ASTCtxt), PSets(PSets) {}
+
+  void EvalVarDecl(const VarDecl *VD) {
+    const Expr *Initializer = VD->getInit();
+    auto P = Pointer::get(VD);
+
+    if (!P.hasValue()) {
+      // Not a Pointer, but initializer can have side-effects
+      if (Initializer)
+        EvalExpr(Initializer);
+      return;
+    }
+
+    SourceLocation Loc = VD->getLocEnd();
+
+    PSet PS; //== unknown
+
+    if (Initializer)
+      PS = EvalExprForPSet(Initializer, !P->isRealPointer());
+    else if (Loc.isValid())
+      PS = PSet::invalid(InvalidationReason::NotInitialized(Loc));
+
+    // We don't track the PSets of variables with global storage; just make
+    // sure that its pset is always {static} and/or {null}
+    if (P->hasGlobalStorage()) {
+      if (PS.isUnknown()) {
+        // Reporter.PsetDebug(PS, Loc, P.getValue());
+        return;
+      }
+
+      if (!PS.isSubsetOf(PSet::validOwnerOrNull(Owner::Static())) && Reporter)
+        Reporter->reportBug(Loc, "the pset of %0 must be a subset of "
+                                 "{(static), (null)}, but is {%1}")
+            << P->getName() << PS.str();
+      return;
+    }
+
+    SetPSet(VD, PS, Loc);
+    return;
+  }
+
+  void VisitBlock(const CFGBlock &B, const LifetimeReporter *Reporter);
+
+  void UpdatePSetsFromCondition(const Expr *E, bool positive,
+                                SourceLocation Loc);
+};
+
+// Manages lifetime information for the CFG of a FunctionDecl
+
+PSet PSetsBuilder::GetPSet(Pointer P) {
+  auto i = PSets.find(P);
+  if (i != PSets.end())
+    return i->second;
+
+  // Assumption: global Pointers have a pset of {static, null}
+  if (P.hasGlobalStorage() || P.isMemberVariable()) {
+    if (P.mayBeNull())
+      return PSet::validOwnerOrNull(Owner::Static());
+    return PSet::validOwner(Owner::Static());
+  }
+
+  llvm_unreachable("Missing pset for Pointer");
+}
+
+void PSetsBuilder::SetPSet(Pointer P, PSet PS, SourceLocation Loc) {
+  assert(P.getName() != "");
+
+  // Assumption: global Pointers have a pset that is a subset of {static,
+  // null}
+  if (P.hasGlobalStorage() && !PS.isUnknown() &&
+      !PS.isSubsetOf(PSet::validOwnerOrNull(Owner::Static())) && Reporter)
+    Reporter->reportBug(Loc, "the pset of %0 must be a subset of "
+                             "{(static), (null)}, but is {%1}")
+        << P.getName() << PS.str();
+
+  auto i = PSets.find(P);
+  if (i != PSets.end())
+    i->second = std::move(PS);
+  else
+    PSets.emplace(P, PS);
+}
+
+void PSetsBuilder::CheckPSetValidity(const PSet &PS, SourceLocation Loc) {
+  if (!Reporter)
+    return;
+
+  if (PS.getState() == PSet::Unknown) {
+    Reporter->DebugMsg(Loc, "dereferencing a unknown pointer");
+    return;
+  }
+
+  if (PS.getState() == PSet::Invalid) {
+    Reporter->reportBug(Loc, "dereferencing a dangling pointer");
+    Reporter->reportNote(PS.getReason().getLoc(),
+                         "it became invalid because %0 here")
+        << PS.getReason().str();
+    return;
+  }
+
+  if (PS.getState() == PSet::PossiblyInvalid) {
+    Reporter->reportBug(Loc, "dereferencing a possibly dangling pointer");
+    Reporter->reportNote(PS.getReason().getLoc(),
+                         "it became possibly invalid because %0 here")
+        << PS.getReason().str();
+    return;
+  }
+
+  if (PS.isValid() && PS.containsNull()) {
+    if (PS.isSingular())
+      Reporter->reportBug(Loc, "dereferencing a null pointer");
+    else
+      Reporter->reportBug(Loc, "dereferencing a (possible) null pointer");
+    return;
+  }
+}
+
+/// Updates psets to remove 'null' when entering conditional statements. If
+/// 'positive' is
+/// false,
+/// handles expression as-if it was negated.
+/// Examples:
+///   int* p = f();
+/// if(p)
+///  ... // pset of p does not contain 'null'
+/// else
+///  ... // pset of p still contains 'null'
+/// if(!p)
+///  ... // pset of p still contains 'null'
+/// else
+///  ... // pset of p does not contain 'null'
+/// TODO: Add condition like 'p == nullptr', 'p != nullptr', '!(p ==
+/// nullptr)',
+/// etc
+void PSetsBuilder::UpdatePSetsFromCondition(const Expr *E, bool positive,
+                                            SourceLocation Loc) {
+  E = E->IgnoreParenImpCasts();
+
+  if (positive) {
+    if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+      if (BO->getOpcode() != BO_LAnd)
+        return;
+      UpdatePSetsFromCondition(BO->getLHS(), true, Loc);
+      UpdatePSetsFromCondition(BO->getRHS(), true, Loc);
+      return;
+    }
+
+    if (auto *DeclRef = dyn_cast<DeclRefExpr>(E)) {
+      Optional<Pointer> P = Pointer::get(DeclRef->getDecl());
+      if (!P.hasValue())
+        return;
+      auto PS = GetPSet(P.getValue());
+      if (!PS.isValid())
+        return;
+      if (PS.containsNull()) {
+        PS.removeNull();
+        SetPSet(P.getValue(), PS, Loc);
+      }
+    }
+  } else {
+    if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+      if (BO->getOpcode() != BO_LOr)
+        return;
+      UpdatePSetsFromCondition(BO->getLHS(), false, Loc);
+      UpdatePSetsFromCondition(BO->getRHS(), false, Loc);
+      return;
+    }
+
+    if (auto *UO = dyn_cast<UnaryOperator>(E)) {
+      if (UO->getOpcode() != UO_LNot)
+        return;
+      UpdatePSetsFromCondition(UO->getSubExpr(), true, Loc);
+    }
+  }
+}
+
+/// Checks if the statement S is a call to clang_analyzer_pset and, if yes,
+/// diags the pset of its argument
+bool PSetsBuilder::HandleClangAnalyzerPset(const Stmt *S,
+                                           const LifetimeReporter *Reporter) {
+  assert(Reporter);
+  const auto *CallE = dyn_cast<CallExpr>(S);
+  if (!CallE)
+    return false;
+
+  const FunctionDecl *Callee = CallE->getDirectCallee();
+  if (!Callee)
+    return false;
+
+  const auto *I = Callee->getIdentifier();
+  if (!I)
+    return false;
+
+  if (I->getName() != "clang_analyzer_pset")
+    return false;
+
+  auto Loc = CallE->getLocStart();
+
+  if (CallE->getNumArgs() != 1) {
+    Reporter->reportBug(Loc, "clang_analyzer_pset takes one argument");
+    return true;
+  }
+
+  const auto *DeclRef =
+      dyn_cast<DeclRefExpr>(CallE->getArg(0)->IgnoreImpCasts());
+  if (!DeclRef) {
+    Reporter->reportBug(
+        Loc, "Argument to clang_analyzer_pset must be a DeclRefExpr");
+    return true;
+  }
+
+  const auto *VD = dyn_cast<VarDecl>(DeclRef->getDecl());
+  if (!VD) {
+    Reporter->reportBug(Loc,
+                        "Argument to clang_analyzer_pset must be a VarDecl");
+    return true;
+  }
+
+  diagPSet(VD, Loc);
+  return true;
+}
+
+// Update PSets in Builder through all CFGElements of this block
+void PSetsBuilder::VisitBlock(const CFGBlock &B,
+                              const LifetimeReporter *Reporter) {
+  for (auto i = B.begin(); i != B.end(); ++i) {
+    const CFGElement &E = *i;
+    switch (E.getKind()) {
+    case CFGElement::Statement: {
+      const Stmt *S = E.castAs<CFGStmt>().getStmt();
+
+      // Handle call to clang_analyzer_pset, which will print the pset of its
+      // argument
+      if (Reporter && HandleClangAnalyzerPset(S, Reporter))
+        break;
+
+      EvalStmt(S);
+
+      // Kill all temporaries that vanish at the end of the full expression
+      invalidateOwner(Owner::Temporary(), 0,
+                      InvalidationReason::TemporaryLeftScope(S->getLocEnd()));
+      break;
+    }
+    case CFGElement::LifetimeEnds: {
+      auto Leaver = E.castAs<CFGLifetimeEnds>();
+
+      // Stop tracking Pointers that leave scope
+      erasePointer(Leaver.getVarDecl());
+
+      // Invalidate all pointers that track leaving Owners
+      invalidateOwner(
+          Leaver.getVarDecl(), 0,
+          InvalidationReason::PointeeLeftScope(
+              Leaver.getTriggerStmt()->getLocEnd(), Leaver.getVarDecl()));
+      break;
+    }
+    case CFGElement::NewAllocator:
+    case CFGElement::AutomaticObjectDtor:
+    case CFGElement::DeleteDtor:
+    case CFGElement::BaseDtor:
+    case CFGElement::MemberDtor:
+    case CFGElement::TemporaryDtor:
+    case CFGElement::Initializer:
+      break;
+    }
+  }
+}
+
+class LifetimeContext {
+  /// Each CFGBlock has one (lazily allocated) BlockContext in the
+  /// BlockContextMap
+  struct BlockContext {
+    bool visited = false;
+    /// Merged PSets of all predecessors of this CFGBlock
+    PSetsMap EntryPSets;
+    /// Computed PSets after updating EntryPSets through all CFGElements of
+    /// this block
+    PSetsMap ExitPSets;
+  };
+
+  ASTContext *ASTCtxt;
+  LangOptions LangOptions;
+  SourceManager *SourceMgr;
+  CFG *ControlFlowGraph;
+  const FunctionDecl *FuncDecl;
+  std::vector<BlockContext> BlockContexts;
+  AnalysisDeclContextManager AnalysisDCMgr;
+  AnalysisDeclContext AC;
+  LifetimeReporter Reporter;
+
+  bool computeEntryPSets(const clang::CFGBlock &B, PSetsMap &EntryPSets);
+
+  BlockContext &getBlockContext(const CFGBlock *B) {
+    return BlockContexts[B->getBlockID()];
+  }
+
+  void dumpBlock(const CFGBlock &B) const {
+    auto Loc = getStartLocOfBlock(B);
+    llvm::errs() << "Block at " << SourceMgr->getBufferName(Loc) << ":"
+                 << SourceMgr->getSpellingLineNumber(Loc) << "\n";
+    B.dump(ControlFlowGraph, LangOptions, true);
+  }
+
+  void dumpCFG() const { ControlFlowGraph->dump(LangOptions, true); }
+
+  PSetsBuilder createPSetsBuilder(PSetsMap &PSets,
+                                  const LifetimeReporter *Reporter = nullptr) {
+    return PSetsBuilder(Reporter, ASTCtxt, PSets);
+  }
+
+  /// Approximate the SourceLocation of a Block for attaching pset debug
+  /// diagnostics
+  SourceLocation getStartLocOfBlock(const CFGBlock &B) const {
+    if (&B == &ControlFlowGraph->getEntry())
+      return FuncDecl->getLocStart();
+
+    if (&B == &ControlFlowGraph->getExit())
+      return FuncDecl->getLocEnd();
+
+    for (const CFGElement &E : B) {
+      switch (E.getKind()) {
+      case CFGElement::Statement:
+        return E.castAs<CFGStmt>().getStmt()->getLocStart();
+      case CFGElement::LifetimeEnds:
+        return E.castAs<CFGLifetimeEnds>()
+            .getTriggerStmt()
+            ->getLocEnd();
+      default:;
+      }
+    }
+    if (B.succ_empty())
+      return SourceLocation();
+    // for(auto i = B.succ_begin(); i != B.succ_end(); ++i)
+    //{
+    // TODO: this may lead to infinite recursion
+    return getStartLocOfBlock(**B.succ_begin());
+    //}
+    llvm_unreachable("Could not determine start loc of CFGBlock");
+  }
+
+public:
+  LifetimeContext(ASTContext *ASTCtxt, bool Debug, ClangTidyContext *CTContext,
+                  SourceManager *SourceMgr, const FunctionDecl *FuncDecl)
+      : ASTCtxt(ASTCtxt), LangOptions(CTContext->getLangOpts()),
+        SourceMgr(SourceMgr), FuncDecl(FuncDecl), AC(&AnalysisDCMgr, FuncDecl),
+        Reporter(Debug, CTContext) {
+    AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
+    AC.getCFGBuildOptions().AddInitializers = true;
+    AC.getCFGBuildOptions().AddLifetime = true;
+    AC.getCFGBuildOptions().AddStaticInitBranches = true;
+    AC.getCFGBuildOptions().AddCXXNewAllocator = true;
+    ControlFlowGraph = AC.getCFG();
+    BlockContexts.resize(ControlFlowGraph->getNumBlockIDs());
+  }
+
+  void TraverseBlocks();
+};
+
+/// Computes entry psets of this block by merging exit psets
+/// of all reachable predecessors.
+/// Returns true if this block is reachable, i.e. one of it predecessors has
+/// been visited.
+bool LifetimeContext::computeEntryPSets(const clang::CFGBlock &B,
+                                        PSetsMap &EntryPSets) {
+  // If no predecessors have been visited by now, this block is not
+  // reachable
+  bool isReachable = false;
+  for (auto i = B.pred_begin(); i != B.pred_end(); ++i) {
+    CFGBlock *PredBlock = i->getReachableBlock();
+    if (!PredBlock)
+      continue;
+
+    auto &PredBC = getBlockContext(PredBlock);
+    if (!PredBC.visited)
+      continue; // Skip this back edge.
+
+    isReachable = true;
+    auto PredPSets = PredBC.ExitPSets;
+    // If this block is only reachable from an IfStmt, modify pset according
+    // to condition.
+    if (auto TermStmt = PredBlock->getTerminator().getStmt()) {
+      // first successor is the then-branch, second sucessor is the
+      // else-branch.
+      bool thenBranch = (PredBlock->succ_begin()->getReachableBlock() == &B);
+      // TODO: also use for, do-while and while conditions
+      if (auto If = dyn_cast<IfStmt>(TermStmt)) {
+        assert(PredBlock->succ_size() == 2);
+        auto Builder = createPSetsBuilder(PredPSets);
+        Builder.UpdatePSetsFromCondition(If->getCond(), thenBranch,
+                                         If->getCond()->getLocStart());
+      } else if (const auto *CO = dyn_cast<ConditionalOperator>(TermStmt)) {
+        auto Builder = createPSetsBuilder(PredPSets);
+        Builder.UpdatePSetsFromCondition(CO->getCond(), thenBranch,
+                                         CO->getCond()->getLocStart());
+      }
+    }
+    if (EntryPSets.empty())
+      EntryPSets = PredPSets;
+    else {
+      // Merge PSets with pred's PSets; TODO: make this efficient
+      for (auto &i : EntryPSets) {
+        auto &Pointer = i.first;
+        auto &PS = i.second;
+        auto j = PredPSets.find(Pointer);
+        if (j == PredPSets.end()) {
+          // The only reason that predecessors have PSets for different
+          // variables is that some of them lazily added global variables
+          // or member variables.
+          // If a global pointer is not mentioned, its pset is implicitly
+          // {(null), (static)}
+
+          // OR there was a goto that stayed in the same scope but skipped
+          // back over the initialization of this Pointer.
+          // Then we don't care, because the variable will not be referenced in the C++
+          // code before it is declared.
+
+          PS = Pointer.mayBeNull() ? PSet::validOwnerOrNull(Owner::Static())
+                                   : PSet::validOwner(Owner::Static());
+          continue;
+        }
+        if (PS == j->second)
+          continue;
+
+        PS.merge(j->second);
+      }
+    }
+  }
+  return isReachable;
+}
+
+/// Traverse all blocks of the CFG.
+/// The traversal is repeated until the psets come to a steady state.
+void LifetimeContext::TraverseBlocks() {
+  const PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
+
+  bool updated;
+  do {
+    updated = false;
+    for (const auto *B : *SortedGraph) {
+      auto &BC = getBlockContext(B);
+
+      // The entry block introduces the function parameters into the psets
+      if (B == &ControlFlowGraph->getEntry()) {
+        if (BC.visited)
+          continue;
+
+        // ExitPSets are the function parameters.
+        for (const ParmVarDecl *PVD : FuncDecl->params()) {
+          auto P = Pointer::get(PVD);
+          if (!P.hasValue())
+            continue; // not a Pointer
+          // Parameters cannot be invalid (checked at call site).
+          auto PS = P->mayBeNull() ? PSet::validOwnerOrNull(PVD, 1)
+                                   : PSet::validOwner(PVD, 1);
+          // Reporter.PsetDebug(PS, PVD->getLocEnd(), P.getValue());
+          // PVD->dump();
+          BC.ExitPSets.emplace(P.getValue(), std::move(PS));
+        }
+        BC.visited = true;
+        continue;
+      }
+
+      if (B == &ControlFlowGraph->getExit())
+        continue;
+
+      // compute entry psets of this block by merging exit psets
+      // of all reachable predecessors.
+      PSetsMap EntryPSets;
+      bool isReachable = computeEntryPSets(*B, EntryPSets);
+      if (!isReachable)
+        continue;
+
+      if (BC.visited && EntryPSets == BC.EntryPSets) {
+        // Has been computed at least once and nothing changed; no need to
+        // recompute.
+        continue;
+      }
+
+      BC.EntryPSets = EntryPSets;
+      BC.ExitPSets = BC.EntryPSets;
+      auto Builder = createPSetsBuilder(BC.ExitPSets);
+      Builder.VisitBlock(*B, nullptr);
+      BC.visited = true;
+      updated = true;
+    }
+  } while (updated);
+
+  // Once more to emit diagnostics with final psets
+  for (const auto *B : *SortedGraph) {
+    auto &BC = getBlockContext(B);
+    if (!BC.visited)
+      continue;
+
+    BC.ExitPSets = BC.EntryPSets;
+    auto Builder = createPSetsBuilder(BC.ExitPSets, &Reporter);
+    Builder.VisitBlock(*B, &Reporter);
+  }
+}
+
+} // end unnamed namespace
+
+/// Check that the function adheres to the lifetime profile
+void VisitFunction(const FunctionDecl *Func, ASTContext *ASTCtxt,
+                   SourceManager *SourceMgr, bool Debug,
+                   ClangTidyContext *CTContext) {
+  if (!Func->doesThisDeclarationHaveABody())
+    return;
+
+  if (Func->getLocStart().isInvalid())
+    return;
+
+  if (SourceMgr->isInSystemHeader(Func->getLocStart()))
+    return;
+
+  if (!Func->getIdentifier())
+    return; // unnamed function
+
+  // We only check instantiations
+  if (cast<DeclContext>(Func)->isDependentContext())
+    return;
+
+  // llvm::errs() << "=== Entering " << Func->getName() << "\n";
+  // llvm::errs() << "at " << SourceMgr->getBufferName(Func->getLocStart()) <<
+  // ":" << SourceMgr->getSpellingLineNumber(Func->getLocStart()) << "\n";
+
+  LifetimeContext LC(ASTCtxt, Debug, CTContext, SourceMgr, Func);
+  LC.TraverseBlocks();
+}
+
+/// Check that each global variable is initialized to a pset of {static} and/or
+/// {null}
+void VisitGlobalVar(const VarDecl *VD, ASTContext *ASTCtxt,
+                    SourceManager *SourceMgr, bool Debug,
+                    ClangTidyContext *CTContext) {
+
+  auto P = Pointer::get(VD);
+  if (!P.hasValue())
+    return;
+
+  LifetimeReporter Reporter(Debug, CTContext);
+  PSetsMap PSets; // remove me
+  PSetsBuilder Builder(&Reporter, ASTCtxt, PSets);
+  Builder.EvalVarDecl(VD);
+}
+
+} // end namespace tidy
+} // end namespace clang
Index: clang-tidy/cppcoreguidelines/ProLifetimeCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/ProLifetimeCheck.h
@@ -0,0 +1,36 @@
+//===--- ProLifetimeCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_LIFETIME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_LIFETIME_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-lifetime.html
+class ProLifetimeCheck : public ClangTidyCheck {
+  ClangTidyContext *CTContext;
+  bool Debug;
+
+public:
+  ProLifetimeCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_LIFETIME_H
Index: clang-tidy/cppcoreguidelines/ProLifetimeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/cppcoreguidelines/ProLifetimeCheck.cpp
@@ -0,0 +1,48 @@
+//===--- ProLifetimeCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProLifetimeCheck.h"
+#include "ProLifetimeVisitor.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <set>
+#include <unordered_map>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+
+ProLifetimeCheck::ProLifetimeCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), CTContext(Context),
+      Debug(Options.get("Debug", false)) {}
+
+void ProLifetimeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Debug", Debug);
+}
+
+void ProLifetimeCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(functionDecl().bind("fun"), this);
+  Finder->addMatcher(varDecl(hasGlobalStorage()).bind("globalVar"), this);
+}
+
+void ProLifetimeCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("fun"))
+    VisitFunction(Func, Result.Context, Result.SourceManager, Debug, CTContext);
+
+  if (const auto *GlobalVar = Result.Nodes.getNodeAs<VarDecl>("globalVar"))
+    VisitGlobalVar(GlobalVar, Result.Context, Result.SourceManager, Debug,
+                   CTContext);
+}
+
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -15,6 +15,7 @@
 #include "ProBoundsArrayToPointerDecayCheck.h"
 #include "ProBoundsConstantArrayIndexCheck.h"
 #include "ProBoundsPointerArithmeticCheck.h"
+#include "ProLifetimeCheck.h"
 #include "ProTypeConstCastCheck.h"
 #include "ProTypeCstyleCastCheck.h"
 #include "ProTypeMemberInitCheck.h"
@@ -39,6 +40,8 @@
         "cppcoreguidelines-pro-bounds-constant-array-index");
     CheckFactories.registerCheck<ProBoundsPointerArithmeticCheck>(
         "cppcoreguidelines-pro-bounds-pointer-arithmetic");
+    CheckFactories.registerCheck<ProLifetimeCheck>(
+        "cppcoreguidelines-pro-lifetime");
     CheckFactories.registerCheck<ProTypeConstCastCheck>(
         "cppcoreguidelines-pro-type-const-cast");
     CheckFactories.registerCheck<ProTypeCstyleCastCheck>(
Index: clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -6,6 +6,8 @@
   ProBoundsArrayToPointerDecayCheck.cpp
   ProBoundsConstantArrayIndexCheck.cpp
   ProBoundsPointerArithmeticCheck.cpp
+  ProLifetimeCheck.cpp
+  ProLifetimeVisitor.cpp
   ProTypeConstCastCheck.cpp
   ProTypeCstyleCastCheck.cpp
   ProTypeMemberInitCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to