if (BuildOpts.AddRichCXXConstructors) {
if (const ConstructionContextLayer *Layer =
ConstructionContextMap.lookup(CE)) {
- const ConstructionContext *CC =
- ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
- Layer);
- B->appendConstructor(CE, CC, cfg->getBumpVectorContext());
cleanupConstructionContext(CE);
- return;
+ if (const auto *CC = ConstructionContext::createFromLayers(
+ cfg->getBumpVectorContext(), Layer)) {
+ B->appendConstructor(CE, CC, cfg->getBumpVectorContext());
+ return;
+ }
}
}
if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) {
if (const ConstructionContextLayer *Layer =
ConstructionContextMap.lookup(CE)) {
- const ConstructionContext *CC =
- ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
- Layer);
- B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext());
cleanupConstructionContext(CE);
- return;
+ if (const auto *CC = ConstructionContext::createFromLayers(
+ cfg->getBumpVectorContext(), Layer)) {
+ B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext());
+ return;
+ }
}
}
}
--- /dev/null
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++14 -verify %s
+
+void clang_analyzer_eval(bool);
+
+int global;
+
+namespace variant_0 {
+// This variant of the code works correctly. Function foo() is not a template
+// function. Note that there are two destructors within foo().
+
+class A {
+public:
+ ~A() { ++global; }
+};
+
+class B {
+ A a;
+};
+
+// CHECK: void foo(int)
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_0::B)
+// CHECK-NEXT: 2: variant_0::B i;
+// CHECK-NEXT: 3: operator=
+// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_0::B &(*)(class variant_0::B &&) noexcept)
+// CHECK-NEXT: 5: i
+// CHECK-NEXT: 6: {} (CXXConstructExpr, [B1.7], [B1.8], class variant_0::B)
+// CHECK-NEXT: 7: [B1.6] (BindTemporary)
+// 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)
+void foo(int) {
+ B i;
+ i = {};
+}
+
+void bar() {
+ global = 0;
+ foo(1);
+ clang_analyzer_eval(global == 2); // expected-warning{{TRUE}}
+}
+
+} // end namespace variant_0
+
+namespace variant_1 {
+// Suddenly, if we turn foo() into a template, we are missing a
+// CXXBindTemporaryExpr in the AST, and therefore we're missing a
+// temporary destructor in the CFG.
+
+class A {
+public:
+ ~A() { ++global; }
+};
+
+class B {
+ A a;
+};
+
+// FIXME: Find the construction context for {} and enforce the temporary
+// destructor.
+// CHECK: template<> void foo<int>(int)
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_1::B)
+// CHECK-NEXT: 2: variant_1::B i;
+// CHECK-NEXT: 3: operator=
+// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_1::B &(*)(class variant_1::B &&) noexcept)
+// CHECK-NEXT: 5: i
+// 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)
+template <typename T> void foo(T) {
+ B i;
+ i = {};
+}
+
+void bar() {
+ global = 0;
+ foo(1);
+ // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors.
+ clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}}
+}
+
+} // end namespace variant_1
+
+namespace variant_2 {
+// Making field 'a' in class 'B' public turns the class into an aggregate.
+// In this case there is no constructor at {} - only an aggregate
+// initialization. Aggregate initialization is unsupported for now.
+
+class A {
+public:
+ ~A() { ++global; }
+};
+
+class B {
+public:
+ A a;
+};
+
+// CHECK: template<> void foo<int>(int)
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], class variant_2::B)
+// CHECK-NEXT: 2: variant_2::B i;
+// CHECK-NEXT: 3: operator=
+// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, class variant_2::B &(*)(class variant_2::B &&) noexcept)
+// CHECK-NEXT: 5: i
+// CHECK-NEXT: 6: {}
+// CHECK-NEXT: 7: {}
+// CHECK-NEXT: 8: [B1.7] (BindTemporary)
+// 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)
+template <typename T> void foo(T) {
+ B i;
+ i = {};
+}
+
+void bar() {
+ global = 0;
+ foo(1);
+ // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors.
+ clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}}
+}
+
+} // end namespace variant_2