forked from OSchip/llvm-project
[CFG] [analyzer] Add construction context for implicit constructor conversions.
Implicit constructor conversions such as A a = B() are represented by surrounding the constructor for B() with an ImplicitCastExpr of CK_ConstructorConversion kind, similarly to how explicit constructor conversions are surrounded by a CXXFunctionalCastExpr. Support this syntax pattern when extracting the construction context for the implicit constructor that performs the conversion. Differential Revision: https://reviews.llvm.org/D44051 llvm-svn: 327096
This commit is contained in:
parent
4b7ea1f33c
commit
13f9664d2b
|
@ -1202,8 +1202,13 @@ void CFGBuilder::findConstructionContexts(
|
|||
case Stmt::ImplicitCastExprClass: {
|
||||
auto *Cast = cast<ImplicitCastExpr>(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: {
|
||||
|
|
|
@ -70,8 +70,11 @@ const ConstructionContext *ConstructionContext::createFromLayers(
|
|||
C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
|
||||
return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
|
||||
} else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(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<TemporaryObjectConstructionContext>();
|
||||
|
|
|
@ -498,20 +498,70 @@ public:
|
|||
~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
|
||||
}
|
||||
|
||||
|
|
|
@ -940,3 +940,48 @@ void test() {
|
|||
}
|
||||
} // 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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue