[analyzer] Invalidate regions indirectly accessible through const pointers.

In this case, the value of 'x' may be changed after the call to indirectAccess:

  struct Wrapper {
    int *ptr;
  };

  void indirectAccess(const Wrapper &w);

  void test() {
    int x = 42;
    Wrapper w = { x };

    clang_analyzer_eval(x == 42); // TRUE
    indirectAccess(w);
    clang_analyzer_eval(x == 42); // UNKNOWN
  }

This is important for modelling return-by-value objects in C++, to show
that the contents of the struct are escaping in the return copy-constructor.

<rdar://problem/13239826>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177570 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/Analysis/call-invalidation.cpp b/test/Analysis/call-invalidation.cpp
new file mode 100644
index 0000000..54281cc
--- /dev/null
+++ b/test/Analysis/call-invalidation.cpp
@@ -0,0 +1,91 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(bool);
+
+void usePointer(int * const *);
+void useReference(int * const &);
+
+void testPointer() {
+  int x;
+  int *p;
+
+  p = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  usePointer(&p);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+
+  p = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useReference(p);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+
+  int * const cp1 = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  usePointer(&cp1);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+
+  int * const cp2 = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useReference(cp2);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+}
+
+
+struct Wrapper {
+  int *ptr;
+};
+
+void useStruct(Wrapper &w);
+void useConstStruct(const Wrapper &w);
+
+void testPointerStruct() {
+  int x;
+  Wrapper w;
+
+  w.ptr = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useStruct(w);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+
+  w.ptr = &x;
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useConstStruct(w);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+}
+
+
+struct RefWrapper {
+  int &ref;
+};
+
+void useStruct(RefWrapper &w);
+void useConstStruct(const RefWrapper &w);
+
+void testReferenceStruct() {
+  int x;
+  RefWrapper w = { x };
+
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useStruct(w);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+}
+
+// FIXME: This test is split into two functions because region invalidation
+// does not preserve reference bindings. <rdar://problem/13320347>
+void testConstReferenceStruct() {
+  int x;
+  RefWrapper w = { x };
+
+  x = 42;
+  clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+  useConstStruct(w);
+  clang_analyzer_eval(x == 42); // expected-warning{{UNKNOWN}}
+}
+