summaryrefslogtreecommitdiff
path: root/test/Analysis/temporaries.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/Analysis/temporaries.cpp')
-rw-r--r--test/Analysis/temporaries.cpp423
1 files changed, 419 insertions, 4 deletions
diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp
index 99851dd68539c..de3420e70891c 100644
--- a/test/Analysis/temporaries.cpp
+++ b/test/Analysis/temporaries.cpp
@@ -1,9 +1,15 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++11
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++17
+
+// Note: The C++17 run-line doesn't -verify yet - it is a no-crash test.
extern bool clang_analyzer_eval(bool);
extern bool clang_analyzer_warnIfReached();
+void clang_analyzer_checkInlined(bool);
+
+#include "Inputs/system-header-simulator-cxx.h";
struct Trivial {
Trivial(int x) : value(x) {}
@@ -422,6 +428,10 @@ namespace destructors {
struct CtorWithNoReturnDtor {
CtorWithNoReturnDtor() = default;
+ CtorWithNoReturnDtor(int x) {
+ clang_analyzer_checkInlined(false); // no-warning
+ }
+
~CtorWithNoReturnDtor() __attribute__((noreturn));
};
@@ -434,11 +444,17 @@ namespace destructors {
const CtorWithNoReturnDtor &c = CtorWithNoReturnDtor();
// This represents an (expected) loss of coverage, since the destructor
- // of the lifetime-exended temporary is executed at at the end of
+ // of the lifetime-exended temporary is executed at the end of
// scope.
clang_analyzer_warnIfReached(); // no-warning
}
+#if __cplusplus >= 201103L
+ CtorWithNoReturnDtor returnNoReturnDtor() {
+ return {1}; // no-crash
+ }
+#endif
+
#endif // TEMPORARY_DTORS
}
@@ -530,3 +546,402 @@ void run() {
Sub(i).m();
}
}
+
+namespace test_return_temporary {
+class C {
+ int x, y;
+
+public:
+ C(int x, int y) : x(x), y(y) {}
+ int getX() const { return x; }
+ int getY() const { return y; }
+ ~C() {}
+};
+
+class D: public C {
+public:
+ D() : C(1, 2) {}
+ D(const D &d): C(d.getX(), d.getY()) {}
+};
+
+C returnTemporaryWithVariable() { C c(1, 2); return c; }
+C returnTemporaryWithAnotherFunctionWithVariable() {
+ return returnTemporaryWithVariable();
+}
+C returnTemporaryWithCopyConstructionWithVariable() {
+ return C(returnTemporaryWithVariable());
+}
+
+C returnTemporaryWithConstruction() { return C(1, 2); }
+C returnTemporaryWithAnotherFunctionWithConstruction() {
+ return returnTemporaryWithConstruction();
+}
+C returnTemporaryWithCopyConstructionWithConstruction() {
+ return C(returnTemporaryWithConstruction());
+}
+
+D returnTemporaryWithVariableAndNonTrivialCopy() { D d; return d; }
+D returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy() {
+ return returnTemporaryWithVariableAndNonTrivialCopy();
+}
+D returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy() {
+ return D(returnTemporaryWithVariableAndNonTrivialCopy());
+}
+
+#if __cplusplus >= 201103L
+C returnTemporaryWithBraces() { return {1, 2}; }
+C returnTemporaryWithAnotherFunctionWithBraces() {
+ return returnTemporaryWithBraces();
+}
+C returnTemporaryWithCopyConstructionWithBraces() {
+ return C(returnTemporaryWithBraces());
+}
+#endif // C++11
+
+void test() {
+ C c1 = returnTemporaryWithVariable();
+ clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c1.getY() == 2); // expected-warning{{TRUE}}
+
+ C c2 = returnTemporaryWithAnotherFunctionWithVariable();
+ clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c2.getY() == 2); // expected-warning{{TRUE}}
+
+ C c3 = returnTemporaryWithCopyConstructionWithVariable();
+ clang_analyzer_eval(c3.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}}
+
+ C c4 = returnTemporaryWithConstruction();
+ clang_analyzer_eval(c4.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c4.getY() == 2); // expected-warning{{TRUE}}
+
+ C c5 = returnTemporaryWithAnotherFunctionWithConstruction();
+ clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}
+
+ C c6 = returnTemporaryWithCopyConstructionWithConstruction();
+ clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}
+
+#if __cplusplus >= 201103L
+
+ C c7 = returnTemporaryWithBraces();
+ clang_analyzer_eval(c7.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c7.getY() == 2); // expected-warning{{TRUE}}
+
+ C c8 = returnTemporaryWithAnotherFunctionWithBraces();
+ clang_analyzer_eval(c8.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c8.getY() == 2); // expected-warning{{TRUE}}
+
+ C c9 = returnTemporaryWithCopyConstructionWithBraces();
+ clang_analyzer_eval(c9.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c9.getY() == 2); // expected-warning{{TRUE}}
+
+#endif // C++11
+
+ D d1 = returnTemporaryWithVariableAndNonTrivialCopy();
+ clang_analyzer_eval(d1.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d1.getY() == 2); // expected-warning{{TRUE}}
+
+ D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy();
+ clang_analyzer_eval(d2.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d2.getY() == 2); // expected-warning{{TRUE}}
+
+ D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy();
+ clang_analyzer_eval(d3.getX() == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(d3.getY() == 2); // expected-warning{{TRUE}}
+}
+} // namespace test_return_temporary
+
+
+namespace test_temporary_object_expr_without_dtor {
+class C {
+ int x;
+public:
+ C(int x) : x(x) {}
+ int getX() const { return x; }
+};
+
+void test() {
+ clang_analyzer_eval(C(3).getX() == 3); // expected-warning{{TRUE}}
+};
+}
+
+namespace test_temporary_object_expr_with_dtor {
+class C {
+ int x;
+
+public:
+ C(int x) : x(x) {}
+ ~C() {}
+ int getX() const { return x; }
+};
+
+void test(int coin) {
+ clang_analyzer_eval(C(3).getX() == 3);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+
+ const C &c1 = coin ? C(1) : C(2);
+ if (coin) {
+ clang_analyzer_eval(c1.getX() == 1);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+ } else {
+ clang_analyzer_eval(c1.getX() == 2);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+ }
+
+ C c2 = coin ? C(1) : C(2);
+ if (coin) {
+ clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}
+ } else {
+ clang_analyzer_eval(c2.getX() == 2); // expected-warning{{TRUE}}
+ }
+}
+
+} // namespace test_temporary_object_expr
+
+namespace test_match_constructors_and_destructors {
+class C {
+public:
+ int &x, &y;
+ C(int &_x, int &_y) : x(_x), y(_y) { ++x; }
+ C(const C &c): x(c.x), y(c.y) { ++x; }
+ ~C() { ++y; }
+};
+
+void test_simple_temporary() {
+ int x = 0, y = 0;
+ {
+ const C &c = C(x, y);
+ }
+ // One constructor and one destructor.
+ clang_analyzer_eval(x == 1);
+ clang_analyzer_eval(y == 1);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-3{{TRUE}}
+ // expected-warning@-3{{TRUE}}
+#else
+ // expected-warning@-6{{UNKNOWN}}
+ // expected-warning@-6{{UNKNOWN}}
+#endif
+}
+
+void test_simple_temporary_with_copy() {
+ int x = 0, y = 0;
+ {
+ C c = C(x, y);
+ }
+ // Only one constructor directly into the variable, and one destructor.
+ clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
+}
+
+void test_ternary_temporary(int coin) {
+ int x = 0, y = 0, z = 0, w = 0;
+ {
+ const C &c = coin ? C(x, y) : C(z, w);
+ }
+ // Only one constructor on every branch, and one automatic destructor.
+ if (coin) {
+ clang_analyzer_eval(x == 1);
+ clang_analyzer_eval(y == 1);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-3{{TRUE}}
+ // expected-warning@-3{{TRUE}}
+#else
+ // expected-warning@-6{{UNKNOWN}}
+ // expected-warning@-6{{UNKNOWN}}
+#endif
+ clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}
+
+ } else {
+ clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(z == 1);
+ clang_analyzer_eval(w == 1);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-3{{TRUE}}
+ // expected-warning@-3{{TRUE}}
+#else
+ // expected-warning@-6{{UNKNOWN}}
+ // expected-warning@-6{{UNKNOWN}}
+#endif
+ }
+}
+
+void test_ternary_temporary_with_copy(int coin) {
+ int x = 0, y = 0, z = 0, w = 0;
+ {
+ C c = coin ? C(x, y) : C(z, w);
+ }
+ // On each branch the variable is constructed directly.
+ if (coin) {
+ clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}
+
+ } else {
+ clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}
+ }
+}
+} // namespace test_match_constructors_and_destructors
+
+namespace destructors_for_return_values {
+
+class C {
+public:
+ ~C() {
+ 1 / 0; // expected-warning{{Division by zero}}
+ }
+};
+
+C make();
+
+void testFloatingCall() {
+ make();
+ // Should have divided by zero in the destructor.
+ clang_analyzer_warnIfReached();
+#ifndef TEMPORARY_DTORS
+ // expected-warning@-2{{REACHABLE}}
+#endif
+}
+
+void testLifetimeExtendedCall() {
+ {
+ const C &c = make();
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+ // Should have divided by zero in the destructor.
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+void testCopiedCall() {
+ {
+ C c = make();
+ // Should have elided the constructor/destructor for the temporary
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+ // Should have divided by zero in the destructor.
+ clang_analyzer_warnIfReached(); // no-warning
+}
+} // namespace destructors_for_return_values
+
+namespace dont_forget_destructor_around_logical_op {
+int glob;
+
+class C {
+public:
+ ~C() {
+ glob = 1;
+ clang_analyzer_checkInlined(true);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#endif
+ }
+};
+
+C get();
+
+bool is(C);
+
+
+void test(int coin) {
+ // Here temporaries are being cleaned up after && is evaluated. There are two
+ // temporaries: the return value of get() and the elidable copy constructor
+ // of that return value into is(). According to the CFG, we need to cleanup
+ // both of them depending on whether the temporary corresponding to the
+ // return value of get() was initialized. However, we didn't track
+ // temporaries returned from functions, so we took the wrong branch.
+ coin && is(get()); // no-crash
+ if (coin) {
+ clang_analyzer_eval(glob);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+ } else {
+ // The destructor is not called on this branch.
+ clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
+ }
+}
+} // namespace dont_forget_destructor_around_logical_op
+
+#if __cplusplus >= 201103L
+namespace temporary_list_crash {
+class C {
+public:
+ C() {}
+ ~C() {}
+};
+
+void test() {
+ std::initializer_list<C>{C(), C()}; // no-crash
+}
+} // namespace temporary_list_crash
+#endif // C++11
+
+namespace implicit_constructor_conversion {
+struct S {
+ int x;
+ S(int x) : x(x) {}
+ ~S() {}
+};
+
+class C {
+ int x;
+
+public:
+ C(const S &s) : x(s.x) {}
+ ~C() {}
+ int getX() const { return x; }
+};
+
+void test() {
+ const C &c1 = S(10);
+ clang_analyzer_eval(c1.getX() == 10);
+#ifdef TEMPORARY_DTORS
+ // expected-warning@-2{{TRUE}}
+#else
+ // expected-warning@-4{{UNKNOWN}}
+#endif
+
+ S s = 20;
+ clang_analyzer_eval(s.x == 20); // expected-warning{{TRUE}}
+
+ C c2 = s;
+ clang_analyzer_eval(c2.getX() == 20); // expected-warning{{TRUE}}
+}
+} // end namespace implicit_constructor_conversion
+
+namespace pass_references_through {
+class C {
+public:
+ ~C() {}
+};
+
+const C &foo1();
+C &&foo2();
+
+// In these examples the foo() expression has record type, not reference type.
+// Don't try to figure out how to perform construction of the record here.
+const C &bar1() { return foo1(); } // no-crash
+C &&bar2() { return foo2(); } // no-crash
+} // end namespace pass_references_through