[analyzer] Be more forgiving about calling methods on struct rvalues.
The problem is that the value of 'this' in a C++ member function call
should always be a region (or NULL). However, if the object is an rvalue,
it has no associated region (only a conjured symbol or LazyCompoundVal).
For now, we handle this in two ways:
1) Actually respect MaterializeTemporaryExpr. Before, it was relying on
CXXConstructExpr to create temporary regions for all struct values.
Now it just does the right thing: if the value is not in a temporary
region, create one.
2) Have CallEvent recognize the case where its 'this' pointer is a
non-region, and just return UnknownVal to keep from confusing clients.
The long-term problem is being tracked internally in <rdar://problem/12137950>,
but this makes many test cases pass.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163220 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/Analysis/array-struct-region.cpp b/test/Analysis/array-struct-region.cpp
new file mode 100644
index 0000000..3581566
--- /dev/null
+++ b/test/Analysis/array-struct-region.cpp
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -verify -x c %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -verify -x c++ -analyzer-config c++-inlining=constructors %s
+
+void clang_analyzer_eval(int);
+
+struct S {
+ int field;
+
+#if __cplusplus
+ const struct S *getThis() const { return this; }
+#endif
+};
+
+struct S getS();
+
+
+void testAssignment() {
+ struct S s = getS();
+
+ if (s.field != 42) return;
+ clang_analyzer_eval(s.field == 42); // expected-warning{{TRUE}}
+
+ s.field = 0;
+ clang_analyzer_eval(s.field == 0); // expected-warning{{TRUE}}
+
+#if __cplusplus
+ clang_analyzer_eval(s.getThis() == &s); // expected-warning{{TRUE}}
+#endif
+}
+
+
+void testImmediateUse() {
+ int x = getS().field;
+
+ if (x != 42) return;
+ clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+
+#if __cplusplus
+ clang_analyzer_eval((void *)getS().getThis() == (void *)&x); // expected-warning{{FALSE}}
+#endif
+}
+
+int getConstrainedField(struct S s) {
+ if (s.field != 42) return 42;
+ return s.field;
+}
+
+int getAssignedField(struct S s) {
+ s.field = 42;
+ return s.field;
+}
+
+void testArgument() {
+ clang_analyzer_eval(getConstrainedField(getS()) == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(getAssignedField(getS()) == 42); // expected-warning{{TRUE}}
+}
+
+
+//--------------------
+// C++-only tests
+//--------------------
+
+#if __cplusplus
+void testReferenceAssignment() {
+ const S &s = getS();
+
+ if (s.field != 42) return;
+ clang_analyzer_eval(s.field == 42); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(s.getThis() == &s); // expected-warning{{TRUE}}
+}
+
+
+int getConstrainedFieldRef(const S &s) {
+ if (s.field != 42) return 42;
+ return s.field;
+}
+
+bool checkThis(const S &s) {
+ return s.getThis() == &s;
+}
+
+void testReferenceArgument() {
+ clang_analyzer_eval(getConstrainedFieldRef(getS()) == 42); // expected-warning{{TRUE}}
+ clang_analyzer_eval(checkThis(getS())); // expected-warning{{TRUE}}
+}
+#endif
diff --git a/test/Analysis/reference.cpp b/test/Analysis/reference.cpp
index 4a2cbb8..374f3f7 100644
--- a/test/Analysis/reference.cpp
+++ b/test/Analysis/reference.cpp
@@ -116,8 +116,13 @@
struct S { int &x; };
- extern S *getS();
- clang_analyzer_eval(&getS()->x != 0); // expected-warning{{TRUE}}
+ // FIXME: Should be TRUE. Fields of return-by-value structs are not yet
+ // symbolicated. Tracked by <rdar://problem/12137950>.
+ extern S getS();
+ clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}}
+
+ extern S *getSP();
+ clang_analyzer_eval(&getSP()->x != 0); // expected-warning{{TRUE}}
}
@@ -150,10 +155,3 @@
return *x; // should warn here!
}
}
-
-void testReferenceFieldAddress() {
- struct S { int &x; };
-
- extern S getS();
- clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}}
-}