summaryrefslogtreecommitdiff
path: root/test/Analysis
diff options
context:
space:
mode:
Diffstat (limited to 'test/Analysis')
-rw-r--r--test/Analysis/auto-obj-dtors-cfg-output.cpp4
-rw-r--r--test/Analysis/cfg-rich-constructors.cpp33
-rw-r--r--test/Analysis/cfg-rich-constructors.mm3
-rw-r--r--test/Analysis/cfg.cpp4
-rw-r--r--test/Analysis/missing-bind-temporary.cpp6
-rw-r--r--test/Analysis/more-dtors-cfg-output.cpp317
-rw-r--r--test/Analysis/scopes-cfg-output.cpp4
-rw-r--r--test/Analysis/temporaries.cpp19
8 files changed, 343 insertions, 47 deletions
diff --git a/test/Analysis/auto-obj-dtors-cfg-output.cpp b/test/Analysis/auto-obj-dtors-cfg-output.cpp
index 7e678a1ec7..716eaf3c0f 100644
--- a/test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ b/test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -334,7 +334,7 @@ void test_aggregate_array_lifetime_extension() {
// CHECK-NEXT: 61: ~A() (Temporary object destructor)
// CHECK-NEXT: 62: ~A() (Temporary object destructor)
// CHECK-NEXT: 63: ~A() (Temporary object destructor)
-// CHECK-NEXT: 64: [B1.57].~D() (Implicit destructor)
+// CHECK-NEXT: 64: [B1.57].~D [2]() (Implicit destructor)
// CHECK-NEXT: 65: [B1.18].~D() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
@@ -363,7 +363,7 @@ void test_aggregate_with_nontrivial_own_destructor() {
// WARNINGS-NEXT: 3: (CXXConstructExpr, class A [0])
// ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], class A [0])
// CHECK-NEXT: 4: A b[0];
-// CHECK-NEXT: 5: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B1.2].~A [2]() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
// CHECK: [B0 (EXIT)]
diff --git a/test/Analysis/cfg-rich-constructors.cpp b/test/Analysis/cfg-rich-constructors.cpp
index cb1ed8eb6b..e33e57597d 100644
--- a/test/Analysis/cfg-rich-constructors.cpp
+++ b/test/Analysis/cfg-rich-constructors.cpp
@@ -412,7 +412,6 @@ public:
~D();
};
-// FIXME: There should be no temporary destructor in C++17.
// CHECK: return_stmt_with_dtor::D returnTemporary()
// CXX11-ELIDE: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class return_stmt_with_dtor::D)
// CXX11-NOELIDE: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
@@ -422,15 +421,13 @@ public:
// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], class return_stmt_with_dtor::D)
// CXX11-NEXT: 6: ~return_stmt_with_dtor::D() (Temporary object destructor)
// CXX11-NEXT: 7: return [B1.5];
-// CXX17: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.4], [B1.2], class return_stmt_w
+// CXX17: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.3], [B1.2], class return_stmt_w
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
-// CXX17-NEXT: 3: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX17-NEXT: 4: return [B1.2];
+// CXX17-NEXT: 3: return [B1.2];
D returnTemporary() {
return D();
}
-// FIXME: There should be no temporary destructor in C++17.
// CHECK: void returnByValueIntoVariable()
// CHECK: 1: returnTemporary
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class return_stmt_with_dtor::D (*)(void))
@@ -442,12 +439,10 @@ D returnTemporary() {
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], class return_stmt_with_dtor::D)
// CXX11-NEXT: 8: return_stmt_with_dtor::D d = returnTemporary();
// CXX11-NEXT: 9: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX11-NEXT: 10: [B1.8].~D() (Implicit destructor)
+// CXX11-NEXT: 10: [B1.8].~return_stmt_with_dtor::D() (Implicit destructor)
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: return_stmt_with_dtor::D d = returnTemporary();
-// CXX17-NEXT: 6: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX17-NEXT: 7: [B1.5].~D() (Implicit destructor)
void returnByValueIntoVariable() {
D d = returnTemporary();
}
@@ -602,7 +597,7 @@ void temporaryInCondition() {
// CHECK-NEXT: 3: [B1.2] (BindTemporary)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d(0);
-// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceVariableWithConstructor() {
const D &d(0);
}
@@ -613,14 +608,14 @@ void referenceVariableWithConstructor() {
// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d = temporary_object_expr_with_dtors::D();
-// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceVariableWithInitializer() {
const D &d = D();
}
// CHECK: void referenceVariableWithTernaryOperator(bool coin)
// CXX11: [B1]
-// CXX11-NEXT: 1: [B4.4].~D() (Implicit destructor)
+// CXX11-NEXT: 1: [B4.4].~temporary_object_expr_with_dtors::D() (Implicit destructor)
// CXX11: [B2]
// CXX11-NEXT: 1: ~temporary_object_expr_with_dtors::D() (Temporary object destructor)
// CXX11: [B3]
@@ -660,7 +655,7 @@ void referenceVariableWithInitializer() {
// CXX17-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
// CXX17-NEXT: 3: [B1.2]
// CXX17-NEXT: 4: const temporary_object_expr_with_dtors::D &d = coin ? D::get() : temporary_object_expr_with_dtors::D(0);
-// CXX17-NEXT: 5: [B1.4].~D() (Implicit destructor)
+// CXX17-NEXT: 5: [B1.4].~temporary_object_expr_with_dtors::D() (Implicit destructor)
// CXX17: [B2]
// CXX17-NEXT: 1: D::get
// CXX17-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
@@ -686,7 +681,7 @@ void referenceVariableWithTernaryOperator(bool coin) {
// CHECK-NEXT: 4: temporary_object_expr_with_dtors::D([B1.3]) (CXXFunctionalCastExpr, ConstructorCon
// CHECK-NEXT: 5: [B1.4]
// CHECK-NEXT: 6: temporary_object_expr_with_dtors::D &&d = temporary_object_expr_with_dtors::D(1);
-// CHECK-NEXT: 7: [B1.6].~D() (Implicit destructor)
+// CHECK-NEXT: 7: [B1.6].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceWithFunctionalCast() {
D &&d = D(1);
}
@@ -743,13 +738,13 @@ public:
// CXX11-NEXT: 9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B)
// CXX11-NEXT: 10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
// CXX11-NEXT: 11: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CXX11-NEXT: 12: [B1.10].~B() (Implicit destructor)
+// CXX11-NEXT: 12: [B1.10].~implicit_constructor_conversion::B() (Implicit destructor)
// CXX17-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
// CXX17-NEXT: 3: [B1.2]
// CXX17-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], class implicit_constructor_conversion::B)
// CXX17-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
// CXX17-NEXT: 6: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
-// CXX17-NEXT: 7: [B1.6].~B() (Implicit destructor)
+// CXX17-NEXT: 7: [B1.6].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromTemporary() {
B b = A();
}
@@ -769,11 +764,11 @@ void implicitConstructionConversionFromTemporary() {
// CXX11-NEXT: 11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B)
// CXX11-NEXT: 12: implicit_constructor_conversion::B b = get();
// CXX11-NEXT: 13: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CXX11-NEXT: 14: [B1.12].~B() (Implicit destructor)
+// CXX11-NEXT: 14: [B1.12].~implicit_constructor_conversion::B() (Implicit destructor)
// CXX17-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], class implicit_constructor_conversion::B)
// CXX17-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
// CXX17-NEXT: 8: implicit_constructor_conversion::B b = get();
-// CXX17-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CXX17-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValue() {
B b = get();
}
@@ -787,7 +782,7 @@ void implicitConstructionConversionFromFunctionValue() {
// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
// CHECK-NEXT: 7: [B1.6]
// CHECK-NEXT: 8: const implicit_constructor_conversion::B &b = implicit_constructor_conversion::A();
-// CHECK-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CHECK-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
const B &b = A();
}
@@ -803,7 +798,7 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: const implicit_constructor_conversion::B &b = get();
-// CHECK-NEXT: 11: [B1.10].~B() (Implicit destructor)
+// CHECK-NEXT: 11: [B1.10].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() {
const B &b = get(); // no-crash
}
diff --git a/test/Analysis/cfg-rich-constructors.mm b/test/Analysis/cfg-rich-constructors.mm
index 289094293e..e55928d4c5 100644
--- a/test/Analysis/cfg-rich-constructors.mm
+++ b/test/Analysis/cfg-rich-constructors.mm
@@ -59,8 +59,7 @@ void passArgumentIntoMessage(E *e) {
// CXX17-NEXT: 3: {{\[}}[B1.2] bar] (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: D d = [e bar];
-// CXX17-NEXT: 6: ~D() (Temporary object destructor)
-// CXX17-NEXT: 7: [B1.5].~D() (Implicit destructor)
+// CXX17-NEXT: 6: [B1.5].~D() (Implicit destructor)
void returnObjectFromMessage(E *e) {
D d = [e bar];
}
diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp
index ea028e06f3..74f4d50f25 100644
--- a/test/Analysis/cfg.cpp
+++ b/test/Analysis/cfg.cpp
@@ -203,7 +203,7 @@ namespace NoReturnSingleSuccessor {
// CHECK-LABEL: int test1(int *x)
// CHECK: 1: 1
// CHECK-NEXT: 2: return
-// CHECK-NEXT: ~B() (Implicit destructor)
+// CHECK-NEXT: ~NoReturnSingleSuccessor::B() (Implicit destructor)
// CHECK-NEXT: Preds (1)
// CHECK-NEXT: Succs (1): B0
int test1(int *x) {
@@ -477,7 +477,7 @@ void test_lifetime_extended_temporaries() {
// WARNINGS-NEXT: 1: (CXXConstructExpr, struct pr37688_deleted_union_destructor::A)
// ANALYZER-NEXT: 1: (CXXConstructExpr, [B1.2], struct pr37688_deleted_union_destructor::A)
// CHECK-NEXT: 2: pr37688_deleted_union_destructor::A a;
-// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B1.2].~pr37688_deleted_union_destructor::A() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
// CHECK: [B0 (EXIT)]
diff --git a/test/Analysis/missing-bind-temporary.cpp b/test/Analysis/missing-bind-temporary.cpp
index 7be4e2ddf3..18204929bc 100644
--- a/test/Analysis/missing-bind-temporary.cpp
+++ b/test/Analysis/missing-bind-temporary.cpp
@@ -31,7 +31,7 @@ class B {
// CHECK-NEXT: 8: [B1.7]
// CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall)
// CHECK-NEXT: 10: ~variant_0::B() (Temporary object destructor)
-// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 11: [B1.2].~variant_0::B() (Implicit destructor)
void foo(int) {
B i;
i = {};
@@ -71,7 +71,7 @@ class B {
// CHECK-NEXT: 6: {} (CXXConstructExpr, class variant_1::B)
// CHECK-NEXT: 7: [B1.6]
// CHECK-NEXT: 8: [B1.5] = [B1.7] (OperatorCall)
-// CHECK-NEXT: 9: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 9: [B1.2].~variant_1::B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
@@ -114,7 +114,7 @@ public:
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: [B1.5] = [B1.9] (OperatorCall)
// CHECK-NEXT: 11: ~variant_2::B() (Temporary object destructor)
-// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 12: [B1.2].~variant_2::B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
diff --git a/test/Analysis/more-dtors-cfg-output.cpp b/test/Analysis/more-dtors-cfg-output.cpp
new file mode 100644
index 0000000000..c0df5953aa
--- /dev/null
+++ b/test/Analysis/more-dtors-cfg-output.cpp
@@ -0,0 +1,317 @@
+// RUN: rm -f %t.14 %t.2a
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1
+// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s
+// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s
+
+int puts(const char *);
+
+struct Foo {
+ Foo() = delete;
+#if CXX2A
+ // Guarantee that the elided examples are actually elided by deleting the
+ // copy constructor.
+ Foo(const Foo &) = delete;
+#else
+ // No elision support, so we need a copy constructor.
+ Foo(const Foo &);
+#endif
+ ~Foo();
+};
+
+struct TwoFoos {
+ Foo foo1, foo2;
+ ~TwoFoos();
+};
+
+Foo get_foo();
+
+struct Bar {
+ Bar();
+ Bar(const Bar &);
+ ~Bar();
+ Bar &operator=(const Bar &);
+};
+
+Bar get_bar();
+
+struct TwoBars {
+ Bar foo1, foo2;
+ ~TwoBars();
+};
+
+// Start of tests:
+
+void elided_assign() {
+ Foo x = get_foo();
+}
+// CHECK: void elided_assign()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_assign() {
+ Bar x = (const Bar &)get_bar();
+}
+// CHECK: void nonelided_assign()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_paren_init() {
+ Foo x(get_foo());
+}
+// CHECK: void elided_paren_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_paren_init() {
+ Bar x((const Bar &)get_bar());
+}
+// CHECK: void nonelided_paren_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_brace_init() {
+ Foo x{get_foo()};
+}
+// CHECK: void elided_brace_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_brace_init() {
+ Bar x{(const Bar &)get_bar()};
+}
+// CHECK: void nonelided_brace_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_lambda_capture_init() {
+ // The copy from get_foo() into the lambda should be elided. Should call
+ // the lambda's destructor, but not ~Foo() separately.
+ // (This syntax is C++14 'generalized lambda captures'.)
+ auto z = [x=get_foo()]() {};
+}
+// CHECK: void elided_lambda_capture_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
+
+void nonelided_lambda_capture_init() {
+ // Should call the lambda's destructor as well as ~Bar() for the temporary.
+ auto z = [x((const Bar &)get_bar())]() {};
+}
+// CHECK: void nonelided_lambda_capture_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
+
+Foo elided_return_stmt_expr() {
+ // Two copies, both elided in C++17.
+ return ({ get_foo(); });
+}
+// CHECK: Foo elided_return_stmt_expr()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void elided_stmt_expr() {
+ // One copy, elided in C++17.
+ ({ get_foo(); });
+}
+// CHECK: void elided_stmt_expr()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+
+void elided_stmt_expr_multiple_stmts() {
+ // Make sure that only the value returned out of a statement expression is
+ // elided.
+ ({ get_bar(); get_foo(); });
+}
+// CHECK: void elided_stmt_expr_multiple_stmts()
+// CHECK: ~Bar() (Temporary object destructor)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+
+void unelided_stmt_expr() {
+ ({ (const Bar &)get_bar(); });
+}
+// CHECK: void unelided_stmt_expr()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+
+void elided_aggregate_init() {
+ TwoFoos x{get_foo(), get_foo()};
+}
+// CHECK: void elided_aggregate_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~TwoFoos() (Implicit destructor)
+
+void nonelided_aggregate_init() {
+ TwoBars x{(const Bar &)get_bar(), (const Bar &)get_bar()};
+}
+// CHECK: void nonelided_aggregate_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~TwoBars() (Implicit destructor)
+
+TwoFoos return_aggregate_init() {
+ return TwoFoos{get_foo(), get_foo()};
+}
+// CHECK: TwoFoos return_aggregate_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~TwoFoos() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void lifetime_extended() {
+ const Foo &x = (get_foo(), get_foo());
+ puts("one destroyed before, one destroyed after");
+}
+// CHECK: void lifetime_extended()
+// CHECK: ~Foo() (Temporary object destructor)
+// CHECK: one destroyed before, one destroyed after
+// CHECK: ~Foo() (Implicit destructor)
+
+void not_lifetime_extended() {
+ Foo x = (get_foo(), get_foo());
+ puts("one destroyed before, one destroyed after");
+}
+// CHECK: void not_lifetime_extended()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CHECK: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: one destroyed before, one destroyed after
+// CHECK: ~Foo() (Implicit destructor)
+
+void compound_literal() {
+ (void)(Bar[]){{}, {}};
+}
+// CHECK: void compound_literal()
+// CHECK: (CXXConstructExpr, struct Bar)
+// CHECK: (CXXConstructExpr, struct Bar)
+// CHECK: ~Bar [2]() (Temporary object destructor)
+
+Foo elided_return() {
+ return get_foo();
+}
+// CHECK: Foo elided_return()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+
+auto elided_return_lambda() {
+ return [x=get_foo()]() {};
+}
+// CHECK: (lambda at {{.*}}) elided_return_lambda()
+// CXX14: (CXXConstructExpr{{.*}}, class (lambda at {{.*}}))
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void const_auto_obj() {
+ const Bar bar;
+}
+// CHECK: void const_auto_obj()
+// CHECK: .~Bar() (Implicit destructor)
+
+void has_default_arg(Foo foo = get_foo());
+void test_default_arg() {
+ // FIXME: This emits a destructor but no constructor. Search CFG.cpp for
+ // 'PR13385' for details.
+ has_default_arg();
+}
+// CHECK: void test_default_arg()
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+struct DefaultArgInCtor {
+ DefaultArgInCtor(Foo foo = get_foo());
+ ~DefaultArgInCtor();
+};
+
+void default_ctor_with_default_arg() {
+ // FIXME: Default arguments are mishandled in two ways:
+ // - The constructor is not emitted at all (not specific to arrays; see fixme
+ // in CFG.cpp that mentions PR13385).
+ // - The destructor is emitted once, even though the default argument will be
+ // constructed and destructed once per array element.
+ // Ideally, the CFG would expand array constructions into a loop that
+ // constructs each array element, allowing default argument
+ // constructor/destructor calls to be correctly placed inside the loop.
+ DefaultArgInCtor qux[3];
+}
+// CHECK: void default_ctor_with_default_arg()
+// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [3]
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+// CHECK: .~DefaultArgInCtor [3]() (Implicit destructor)
+
+void new_default_ctor_with_default_arg(long count) {
+ // Same problems as above.
+ new DefaultArgInCtor[count];
+}
+// CHECK: void new_default_ctor_with_default_arg(long count)
+// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor []
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+#if CXX2A
+// Boilerplate needed to test co_return:
+
+namespace std::experimental {
+ template <typename Promise>
+ struct coroutine_handle {
+ static coroutine_handle from_address(void *);
+ };
+}
+
+struct TestPromise {
+ TestPromise initial_suspend();
+ TestPromise final_suspend();
+ bool await_ready();
+ void await_suspend(const std::experimental::coroutine_handle<TestPromise> &);
+ void await_resume();
+ Foo return_value(const Bar &);
+ Bar get_return_object();
+ void unhandled_exception();
+};
+
+namespace std::experimental {
+ template <typename Ret, typename... Args>
+ struct coroutine_traits;
+ template <>
+ struct coroutine_traits<Bar> {
+ using promise_type = TestPromise;
+ };
+}
+
+Bar coreturn() {
+ co_return get_bar();
+ // This expands to something like:
+ // promise.return_value(get_bar());
+ // get_bar() is passed by reference to return_value() and is then destroyed;
+ // there is no equivalent of RVO. TestPromise::return_value also returns a
+ // Foo, which should be immediately destroyed.
+ // FIXME: The generated CFG completely ignores get_return_object().
+}
+// CXX2A: Bar coreturn()
+// CXX2A: ~Foo() (Temporary object destructor)
+// CXX2A: ~Bar() (Temporary object destructor)
+#endif
diff --git a/test/Analysis/scopes-cfg-output.cpp b/test/Analysis/scopes-cfg-output.cpp
index f8d84b60de..4b6e2a92c5 100644
--- a/test/Analysis/scopes-cfg-output.cpp
+++ b/test/Analysis/scopes-cfg-output.cpp
@@ -38,7 +38,7 @@ extern const bool UV;
// CHECK-NEXT: 3: A a[2];
// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], class A [0])
// CHECK-NEXT: 5: A b[0];
-// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.3].~A [2]() (Implicit destructor)
// CHECK-NEXT: 7: CFGScopeEnd(a)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
@@ -810,7 +810,7 @@ void test_for_compound_and_break() {
// CHECK-NEXT: 1: CFGScopeEnd(__end1)
// CHECK-NEXT: 2: CFGScopeEnd(__begin1)
// CHECK-NEXT: 3: CFGScopeEnd(__range1)
-// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B5.3].~A [10]() (Implicit destructor)
// CHECK-NEXT: 5: CFGScopeEnd(a)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp
index 6191abfb4d..012cef52f1 100644
--- a/test/Analysis/temporaries.cpp
+++ b/test/Analysis/temporaries.cpp
@@ -830,12 +830,7 @@ void test_ternary_temporary_with_copy(int coin) {
// On each branch the variable is constructed directly.
if (coin) {
clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
-#if __cplusplus < 201703L
clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
-#else
- // FIXME: Destructor called twice in C++17?
- clang_analyzer_eval(y == 2); // expected-warning{{TRUE}}
-#endif
clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}
@@ -843,12 +838,7 @@ void test_ternary_temporary_with_copy(int coin) {
clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}
-#if __cplusplus < 201703L
clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}
-#else
- // FIXME: Destructor called twice in C++17?
- clang_analyzer_eval(w == 2); // expected-warning{{TRUE}}
-#endif
}
}
} // namespace test_match_constructors_and_destructors
@@ -1055,16 +1045,11 @@ void foo(void (*bar4)(S)) {
#endif
bar2(S(2));
- // FIXME: Why are we losing information in C++17?
clang_analyzer_eval(glob == 2);
#ifdef TEMPORARY_DTORS
-#if __cplusplus < 201703L
- // expected-warning@-3{{TRUE}}
-#else
- // expected-warning@-5{{UNKNOWN}}
-#endif
+ // expected-warning@-2{{TRUE}}
#else
- // expected-warning@-8{{UNKNOWN}}
+ // expected-warning@-4{{UNKNOWN}}
#endif
C *c = new D();