Index: cfe/trunk/lib/Analysis/CFG.cpp =================================================================== --- cfe/trunk/lib/Analysis/CFG.cpp +++ cfe/trunk/lib/Analysis/CFG.cpp @@ -1202,8 +1202,13 @@ case Stmt::ImplicitCastExprClass: { auto *Cast = cast(Child); // TODO: We need to support CK_ConstructorConversion, maybe other kinds? - if (Cast->getCastKind() == CK_NoOp) + switch (Cast->getCastKind()) { + case CK_NoOp: + case CK_ConstructorConversion: findConstructionContexts(Layer, Cast->getSubExpr()); + default: + break; + } break; } case Stmt::CXXBindTemporaryExprClass: { Index: cfe/trunk/lib/Analysis/ConstructionContext.cpp =================================================================== --- cfe/trunk/lib/Analysis/ConstructionContext.cpp +++ cfe/trunk/lib/Analysis/ConstructionContext.cpp @@ -70,8 +70,11 @@ C.getAllocator().Allocate(); return new (CC) TemporaryObjectConstructionContext(BTE, MTE); } else if (const auto *MTE = dyn_cast(S)) { + // If the object requires destruction and is not lifetime-extended, + // then it must have a BTE within its MTE. assert(MTE->getType().getCanonicalType() - ->getAsCXXRecordDecl()->hasTrivialDestructor()); + ->getAsCXXRecordDecl()->hasTrivialDestructor() || + MTE->getStorageDuration() != SD_FullExpression); assert(TopLayer->isLast()); auto *CC = C.getAllocator().Allocate(); Index: cfe/trunk/test/Analysis/cfg-rich-constructors.cpp =================================================================== --- cfe/trunk/test/Analysis/cfg-rich-constructors.cpp +++ cfe/trunk/test/Analysis/cfg-rich-constructors.cpp @@ -498,20 +498,70 @@ ~B() {} }; -// FIXME: Find construction context for the implicit constructor conversion. +// CHECK: void implicitConstructionConversionFromTemporary() +// CHECK: 1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A) +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) +// CHECK-NEXT: 3: [B1.2] +// CHECK-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B) +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B) +// CHECK-NEXT: 6: [B1.5] (BindTemporary) +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) +// CHECK-NEXT: 8: [B1.7] +// CHECK-NEXT: 9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B) +// CHECK-NEXT: 10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A(); +// CHECK-NEXT: 11: ~implicit_constructor_conversion::B() (Temporary object destructor) +// CHECK-NEXT: 12: [B1.10].~B() (Implicit destructor) +void implicitConstructionConversionFromTemporary() { + B b = A(); +} + // CHECK: void implicitConstructionConversionFromFunctionValue() // CHECK: 1: get +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conversion::A (*)(void)) +// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) +// CHECK-NEXT: 5: [B1.4] +// CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B) +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B) +// CHECK-NEXT: 8: [B1.7] (BindTemporary) +// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B) +// CHECK-NEXT: 10: [B1.9] +// CHECK-NEXT: 11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B) +// CHECK-NEXT: 12: implicit_constructor_conversion::B b = get(); +// CHECK-NEXT: 13: ~implicit_constructor_conversion::B() (Temporary object destructor) +// CHECK-NEXT: 14: [B1.12].~B() (Implicit destructor) +void implicitConstructionConversionFromFunctionValue() { + B b = get(); +} + +// CHECK: void implicitConstructionConversionFromTemporaryWithLifetimeExtension() +// CHECK: 1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A) +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) +// CHECK-NEXT: 3: [B1.2] +// CHECK-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.7], class implicit_constructor_conversion::B) +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B) +// 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) +void implicitConstructionConversionFromTemporaryWithLifetimeExtension() { + const B &b = A(); +} + +// FIXME: Find construction context for the implicit constructor conversion. +// CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() +// CHECK: 1: get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver // CHECK-NEXT: 3: [B1.2]() // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) // CHECK-NEXT: 5: [B1.4] -// CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, class implicit_constructor_conversion::B) +// CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.9], class implicit_constructor_conversion::B) // CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_convers // 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) -void implicitConstructionConversionFromFunctionValue() { +void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() { const B &b = get(); // no-crash } Index: cfe/trunk/test/Analysis/temporaries.cpp =================================================================== --- cfe/trunk/test/Analysis/temporaries.cpp +++ cfe/trunk/test/Analysis/temporaries.cpp @@ -940,3 +940,48 @@ } } // 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); +#ifdef TEMPORARY_DTORS + // expected-warning@-2{{TRUE}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif + + C c2 = s; + clang_analyzer_eval(c2.getX() == 20); +#ifdef TEMPORARY_DTORS + // expected-warning@-2{{TRUE}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif +} +} // end namespace implicit_constructor_conversion + +