Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -145,19 +145,30 @@ // necessary to express what memory is being initialized by // the construction. class ConstructionContext { +public: + typedef llvm::PointerUnion TriggerTy; + +private: // The construction site - the statement that triggered the construction // for one of its parts. For instance, stack variable declaration statement // triggers construction of itself or its elements if it's an array, // new-expression triggers construction of the newly allocated object(s). - Stmt *Trigger = nullptr; + TriggerTy Trigger; public: ConstructionContext() = default; - ConstructionContext(Stmt *Trigger) : Trigger(Trigger) {} + ConstructionContext(TriggerTy Trigger) + : Trigger(Trigger) {} - bool isNull() const { return Trigger == nullptr; } + bool isNull() const { return Trigger.isNull(); } - const Stmt *getTriggerStmt() const { return Trigger; } + const Stmt *getTriggerStmt() const { + return Trigger.dyn_cast(); + } + + const CXXCtorInitializer *getTriggerInit() const { + return Trigger.dyn_cast(); + } const ConstructionContext *getPersistentCopy(BumpVectorContext &C) const { ConstructionContext *CC = C.getAllocator().Allocate(); @@ -185,6 +196,10 @@ return getConstructionContext()->getTriggerStmt(); } + const CXXCtorInitializer *getTriggerInit() const { + return getConstructionContext()->getTriggerInit(); + } + private: friend class CFGElement; Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -654,7 +654,8 @@ // to the trigger statement. The construction context will be unset once // it is consumed when the CFG building procedure processes the // construct-expression and adds the respective CFGConstructor element. - void EnterConstructionContextIfNecessary(Stmt *Trigger, Stmt *Child); + void EnterConstructionContextIfNecessary( + ConstructionContext::TriggerTy Trigger, Stmt *Child); // Unset the construction context after consuming it. This is done immediately // after adding the CFGConstructor element, so there's no need to // do this manually in every Visit... function. @@ -1147,8 +1148,8 @@ return nullptr; } -void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger, - Stmt *Child) { +void CFGBuilder::EnterConstructionContextIfNecessary( + ConstructionContext::TriggerTy Trigger, Stmt *Child) { if (!BuildOpts.AddRichCXXConstructors) return; if (!Child) @@ -1294,6 +1295,8 @@ appendInitializer(Block, I); if (Init) { + EnterConstructionContextIfNecessary(I, Init); + if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit // generating destructors for the second time. @@ -4605,6 +4608,27 @@ } // namespace +static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper, + const CXXCtorInitializer *I) { + if (I->isBaseInitializer()) + OS << I->getBaseClass()->getAsCXXRecordDecl()->getName(); + else if (I->isDelegatingInitializer()) + OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName(); + else + OS << I->getAnyMember()->getName(); + OS << "("; + if (Expr *IE = I->getInit()) + IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); + OS << ")"; + + if (I->isBaseInitializer()) + OS << " (Base initializer)"; + else if (I->isDelegatingInitializer()) + OS << " (Delegating initializer)"; + else + OS << " (Member initializer)"; +} + static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E) { if (Optional CS = E.getAs()) { @@ -4643,6 +4667,8 @@ if (Optional CE = E.getAs()) { if (const Stmt *S = CE->getTriggerStmt()) Helper.handledStmt((const_cast(S)), OS); + else if (const CXXCtorInitializer *I = CE->getTriggerInit()) + print_initializer(OS, Helper, I); else llvm_unreachable("Unexpected trigger kind!"); OS << ", "; @@ -4659,23 +4685,8 @@ if (isa(S)) OS << '\n'; } else if (Optional IE = E.getAs()) { - const CXXCtorInitializer *I = IE->getInitializer(); - if (I->isBaseInitializer()) - OS << I->getBaseClass()->getAsCXXRecordDecl()->getName(); - else if (I->isDelegatingInitializer()) - OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName(); - else OS << I->getAnyMember()->getName(); - - OS << "("; - if (Expr *IE = I->getInit()) - IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); - OS << ")"; - - if (I->isBaseInitializer()) - OS << " (Base initializer)\n"; - else if (I->isDelegatingInitializer()) - OS << " (Delegating initializer)\n"; - else OS << " (Member initializer)\n"; + print_initializer(OS, Helper, IE->getInitializer()); + OS << '\n'; } else if (Optional DE = E.getAs()) { const VarDecl *VD = DE->getVarDecl(); Index: test/Analysis/cfg-rich-constructors.cpp =================================================================== --- test/Analysis/cfg-rich-constructors.cpp +++ test/Analysis/cfg-rich-constructors.cpp @@ -126,3 +126,28 @@ } } // end namespace decl_stmt + +namespace ctor_initializers { + +class D: public C { + C c1; + +public: + +// CHECK: D() +// CHECK: 1: (CXXConstructExpr, C() (Base initializer), class C) +// CHECK-NEXT: 2: C([B1.1]) (Base initializer) +// CHECK-NEXT: 3: CFGNewAllocator(C *) +// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], class C) +// CHECK-NEXT: 5: new C([B1.4]) +// CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, c1([B1.5]) (Member initializer), class C) +// CHECK-NEXT: 7: c1([B1.6]) (Member initializer) + D(): C(), c1(new C()) {} + +// CHECK: D(int) +// CHECK: 1: (CXXConstructExpr, D() (Delegating initializer), class ctor_initializers::D) +// CHECK-NEXT: 2: D([B1.1]) (Delegating initializer) + D(int): D() {} +}; + +} // end namespace ctor_initializers Index: test/Analysis/initializers-cfg-output.cpp =================================================================== --- test/Analysis/initializers-cfg-output.cpp +++ test/Analysis/initializers-cfg-output.cpp @@ -63,13 +63,17 @@ // CHECK: [B2 (ENTRY)] // CHECK: Succs (1): B1 // CHECK: [B1] -// CHECK: 1: (CXXConstructExpr, class A) +// WARNINGS: 1: (CXXConstructExpr, class A) +// ANALYZER: 1: (CXXConstructExpr, A() (Base initializer), class A) // CHECK: 2: A([B1.1]) (Base initializer) -// CHECK: 3: (CXXConstructExpr, class C) +// WARNINGS: 3: (CXXConstructExpr, class C) +// ANALYZER: 3: (CXXConstructExpr, C() (Base initializer), class C) // CHECK: 4: C([B1.3]) (Base initializer) -// CHECK: 5: (CXXConstructExpr, class B) +// WARNINGS: 5: (CXXConstructExpr, class B) +// ANALYZER: 5: (CXXConstructExpr, B() (Base initializer), class B) // CHECK: 6: B([B1.5]) (Base initializer) -// CHECK: 7: (CXXConstructExpr, class A) +// WARNINGS: 7: (CXXConstructExpr, class A) +// ANALYZER: 7: (CXXConstructExpr, A() (Base initializer), class A) // CHECK: 8: A([B1.7]) (Base initializer) // CHECK: 9: /*implicit*/(int)0 // CHECK: 10: i([B1.9]) (Member initializer) @@ -118,7 +122,8 @@ // CHECK: [B1] // CHECK: 1: 2 // CHECK: 2: 3 -// CHECK: 3: [B1.1], [B1.2] (CXXConstructExpr, class TestDelegating) +// WARNINGS: 3: [B1.1], [B1.2] (CXXConstructExpr, class TestDelegating) +// ANALYZER: 3: [B1.1], [B1.2] (CXXConstructExpr, TestDelegating([B1.1], [B1.2]) (Delegating initializer), class TestDelegating) // CHECK: 4: TestDelegating([B1.3]) (Delegating initializer) // CHECK: Preds (1): B2 // CHECK: Succs (1): B0