[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:
Artem Dergachev 2018-03-09 01:39:59 +00:00
parent 4b7ea1f33c
commit 13f9664d2b
4 changed files with 108 additions and 5 deletions

View File

@ -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: {

View File

@ -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>();

View File

@ -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
}

View File

@ -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