Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -139,6 +139,10 @@ // necessary to express what memory is being initialized by // the construction. class ConstructionContext { +public: + typedef llvm::PointerUnion TriggerTy; + +private: // The constructor that is going to be executed. We describe the context for // this constructor. It describes how are we constructing the object. CXXConstructExpr *Constructor; @@ -146,18 +150,20 @@ // 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; + TriggerTy Trigger; public: - ConstructionContext() = default; - ConstructionContext(CXXConstructExpr *Constructor, Stmt *Trigger) + ConstructionContext(CXXConstructExpr *Constructor, TriggerTy Trigger) : Constructor(Constructor), Trigger(Trigger) {} bool isNull() const { return Constructor == nullptr; } const CXXConstructExpr *getConstructor() const { return Constructor; } - 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(); @@ -188,6 +194,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 @@ -475,7 +475,7 @@ // Information about the currently visited C++ object construction site. // This is set in the construction trigger and read when the constructor // itself is being visited. - ConstructionContext CurrentConstructionContext = {nullptr, nullptr}; + ConstructionContext CurrentConstructionContext = {}; bool badCFG = false; const CFG::BuildOptions &BuildOpts; @@ -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. @@ -1148,8 +1149,8 @@ return nullptr; } -void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger, - Stmt *Child) { +void CFGBuilder::EnterConstructionContextIfNecessary( + ConstructionContext::TriggerTy Trigger, Stmt *Child) { if (!BuildOpts.AddRichCXXConstructors) return; if (!Child) @@ -1295,6 +1296,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. @@ -4608,6 +4611,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()) { @@ -4657,26 +4681,14 @@ CE->getConstructor()->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); OS << " (CXXConstructExpr, "; - Helper.handledStmt(const_cast(CE->getTriggerStmt()), OS); + if (const Stmt *S = CE->getTriggerStmt()) + Helper.handledStmt((const_cast(S)), OS); + else + print_initializer(OS, Helper, CE->getTriggerInit()); OS << ", " << CE->getType().getAsString() << ")\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,27 @@ } } // 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