This revision was automatically updated to reflect the committed changes.
Closed by commit rC327343: [CFG] [analyzer] Add construction context to C++ 
return-by-value call elements. (authored by dergachev, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D44120?vs=137116&id=138103#toc

Repository:
  rC Clang

https://reviews.llvm.org/D44120

Files:
  include/clang/Analysis/CFG.h
  lib/Analysis/CFG.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/PathDiagnostic.cpp
  test/Analysis/cfg-rich-constructors.cpp
  test/Analysis/temp-obj-dtors-cfg-output.cpp

Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -39,6 +39,7 @@
 class BinaryOperator;
 class CFG;
 class ConstructionContext;
+class TemporaryObjectConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -65,8 +66,9 @@
     // stmt kind
     Statement,
     Constructor,
+    CXXRecordTypedCall,
     STMT_BEGIN = Statement,
-    STMT_END = Constructor,
+    STMT_END = CXXRecordTypedCall,
     // dtor kind
     AutomaticObjectDtor,
     DeleteDtor,
@@ -158,10 +160,6 @@
     return static_cast<ConstructionContext *>(Data2.getPointer());
   }
 
-  QualType getType() const {
-    return cast<CXXConstructExpr>(getStmt())->getType();
-  }
-
 private:
   friend class CFGElement;
 
@@ -172,6 +170,46 @@
   }
 };
 
+/// CFGCXXRecordTypedCall - Represents a function call that returns a C++ object
+/// by value. This, like constructor, requires a construction context, which
+/// will always be that of a temporary object - usually consumed by an elidable
+/// constructor. For such value-typed calls the ReturnedValueConstructionContext
+/// of their return value is naturally complemented by the
+/// TemporaryObjectConstructionContext at the call site (here). In C such
+/// tracking is not necessary because no additional effort is required for
+/// destroying the object or modeling copy elision. Like CFGConstructor, this is
+/// for now only used by the analyzer's CFG.
+class CFGCXXRecordTypedCall : public CFGStmt {
+public:
+  /// Returns true when call expression \p CE needs to be represented
+  /// by CFGCXXRecordTypedCall, as opposed to a regular CFGStmt.
+  static bool isCXXRecordTypedCall(CallExpr *CE) {
+    return CE->getType().getCanonicalType()->getAsCXXRecordDecl();
+  }
+
+  explicit CFGCXXRecordTypedCall(CallExpr *CE,
+                             const TemporaryObjectConstructionContext *C)
+      : CFGStmt(CE, CXXRecordTypedCall) {
+    assert(isCXXRecordTypedCall(CE));
+    assert(C);
+    Data2.setPointer(const_cast<TemporaryObjectConstructionContext *>(C));
+  }
+
+  const TemporaryObjectConstructionContext *getConstructionContext() const {
+    return static_cast<TemporaryObjectConstructionContext *>(
+        Data2.getPointer());
+  }
+
+private:
+  friend class CFGElement;
+
+  CFGCXXRecordTypedCall() = default;
+
+  static bool isKind(const CFGElement &E) {
+    return E.getKind() == CXXRecordTypedCall;
+  }
+};
+
 /// CFGInitializer - Represents C++ base or member initializer from
 /// constructor's initialization list.
 class CFGInitializer : public CFGElement {
@@ -840,6 +878,12 @@
     Elements.push_back(CFGConstructor(CE, CC), C);
   }
 
+  void appendCXXRecordTypedCall(CallExpr *CE,
+                                const TemporaryObjectConstructionContext *CC,
+                                BumpVectorContext &C) {
+    Elements.push_back(CFGCXXRecordTypedCall(CE, CC), C);
+  }
+
   void appendInitializer(CXXCtorInitializer *initializer,
                         BumpVectorContext &C) {
     Elements.push_back(CFGInitializer(initializer), C);
Index: test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -1111,7 +1111,8 @@
 // CHECK:   [B1]
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:     3: [B1.2]()
+// WARNINGS:     3: [B1.2]()
+// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]
@@ -1130,16 +1131,18 @@
 // CHECK:   [B1]
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:     3: [B1.2]()
+// WARNINGS:     3: [B1.2]()
+// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]
 // CHECK:     7: const A &a = A::make();
 // CHECK:     8: foo
 // CHECK:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
 // CHECK:    10: A::make
 // CHECK:    11: [B1.10] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
-// CHECK:    12: [B1.11]()
+// WARNINGS:    12: [B1.11]()
+// ANALYZER:    12: [B1.11]() (CXXRecordTypedCall, [B1.13], [B1.15])
 // CHECK:    13: [B1.12] (BindTemporary)
 // CHECK:    14: [B1.13] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:    15: [B1.14]
Index: test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -97,7 +97,7 @@
 // CHECK: void simpleVariableInitializedByValue()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CHECK-NEXT:     6: C c = C::get();
@@ -114,7 +114,7 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]()
+// CHECK-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CHECK-NEXT:     4: [B2.3]
 // CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
 // CHECK:        [B3]
@@ -172,7 +172,7 @@
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B2.2]()
+// CHECK-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CHECK-NEXT:     4: [B2.3]
 // CHECK-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.3], class C)
 // CHECK:        [B3]
@@ -217,14 +217,14 @@
 // CHECK: D(double)
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, C([B1.4]) (Base initializer), class C)
 // CHECK-NEXT:     6: C([B1.5]) (Base initializer)
 // CHECK-NEXT:     7: CFGNewAllocator(C *)
 // CHECK-NEXT:     8: C::get
 // CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:    10: [B1.9]()
+// CHECK-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11])
 // CHECK-NEXT:    11: [B1.10]
 // CHECK-NEXT:    12: [B1.11] (CXXConstructExpr, [B1.13], class C)
 // CHECK-NEXT:    13: new C([B1.12])
@@ -299,7 +299,7 @@
 // CHECK: C returnTemporaryConstructedByFunction()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CHECK-NEXT:     6: return [B1.5];
@@ -310,7 +310,7 @@
 // CHECK: C returnChainOfCopies()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CHECK-NEXT:     4: [B1.3]
 // CHECK-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
 // CHECK-NEXT:     6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
@@ -435,7 +435,7 @@
 // CHECK:        [B5]
 // CHECK-NEXT:     1: D::get
 // CHECK-NEXT:     2: [B5.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
-// CHECK-NEXT:     3: [B5.2]()
+// CHECK-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6])
 // CHECK-NEXT:     4: [B5.3] (BindTemporary)
 // CHECK-NEXT:     5: [B5.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
 // CHECK-NEXT:     6: [B5.5]
@@ -518,7 +518,7 @@
 // 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:     3: [B1.2]() (CXXRecordTypedCall, [B1.5])
 // 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)
@@ -552,7 +552,7 @@
 // CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension()
 // CHECK:          1: get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver
-// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.5])
 // 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.9], class implicit_constructor_conversion::B)
Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp
===================================================================
--- lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -579,6 +579,7 @@
   switch (Source.getKind()) {
   case CFGElement::Statement:
   case CFGElement::Constructor:
+  case CFGElement::CXXRecordTypedCall:
     return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
                                   SM, CallerCtx);
   case CFGElement::Initializer: {
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -612,6 +612,7 @@
   switch (E.getKind()) {
     case CFGElement::Statement:
     case CFGElement::Constructor:
+    case CFGElement::CXXRecordTypedCall:
       ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred);
       return;
     case CFGElement::Initializer:
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -483,8 +483,8 @@
 
   // 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.
-  llvm::DenseMap<CXXConstructExpr *, const ConstructionContextLayer *>
+  // or a function that returns an object by value is being visited.
+  llvm::DenseMap<Expr *, const ConstructionContextLayer *>
       ConstructionContextMap;
 
   using DeclsWithEndedScopeSetTy = llvm::SmallSetVector<VarDecl *, 16>;
@@ -673,7 +673,7 @@
   // Remember to apply the construction context based on the current \p Layer
   // when constructing the CFG element for \p CE.
   void consumeConstructionContext(const ConstructionContextLayer *Layer,
-                                  CXXConstructExpr *CE);
+                                  Expr *E);
 
   // Scan \p Child statement to find constructors in it, while keeping in mind
   // that its parent statement is providing a partial construction context
@@ -684,9 +684,9 @@
                                 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.
-  void cleanupConstructionContext(CXXConstructExpr *CE);
+  // after adding the CFGConstructor or CFGCXXRecordTypedCall element, so
+  // there's no need to do this manually in every Visit... function.
+  void cleanupConstructionContext(Expr *E);
 
   void autoCreateBlock() { if (!Block) Block = createBlock(); }
   CFGBlock *createBlock(bool add_successor = true);
@@ -749,6 +749,27 @@
     B->appendStmt(CE, cfg->getBumpVectorContext());
   }
 
+  void appendCall(CFGBlock *B, CallExpr *CE) {
+    if (BuildOpts.AddRichCXXConstructors) {
+      if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE)) {
+        if (const ConstructionContextLayer *Layer =
+                ConstructionContextMap.lookup(CE)) {
+          const ConstructionContext *CC =
+              ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
+                                                    Layer);
+          B->appendCXXRecordTypedCall(
+              CE, cast<TemporaryObjectConstructionContext>(CC),
+              cfg->getBumpVectorContext());
+          cleanupConstructionContext(CE);
+          return;
+        }
+      }
+    }
+
+    // No valid construction context found. Fall back to statement.
+    B->appendStmt(CE, cfg->getBumpVectorContext());
+  }
+
   void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) {
     B->appendInitializer(I, cfg->getBumpVectorContext());
   }
@@ -1209,16 +1230,16 @@
 }
 
 void CFGBuilder::consumeConstructionContext(
-    const ConstructionContextLayer *Layer, CXXConstructExpr *CE) {
+    const ConstructionContextLayer *Layer, Expr *E) {
   if (const ConstructionContextLayer *PreviouslyStoredLayer =
-          ConstructionContextMap.lookup(CE)) {
+          ConstructionContextMap.lookup(E)) {
     (void)PreviouslyStoredLayer;
     // We might have visited this child when we were finding construction
     // contexts within its parents.
     assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) &&
            "Already within a different construction context!");
   } else {
-    ConstructionContextMap[CE] = Layer;
+    ConstructionContextMap[E] = Layer;
   }
 }
 
@@ -1236,6 +1257,18 @@
     consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
     break;
   }
+  // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr.
+  // FIXME: An isa<> would look much better but this whole switch is a
+  // workaround for an internal compiler error in MSVC 2015 (see r326021).
+  case Stmt::CallExprClass:
+  case Stmt::CXXMemberCallExprClass:
+  case Stmt::CXXOperatorCallExprClass:
+  case Stmt::UserDefinedLiteralClass: {
+    auto *CE = cast<CallExpr>(Child);
+    if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE))
+      consumeConstructionContext(Layer, CE);
+    break;
+  }
   case Stmt::ExprWithCleanupsClass: {
     auto *Cleanups = cast<ExprWithCleanups>(Child);
     findConstructionContexts(Layer, Cleanups->getSubExpr());
@@ -1277,12 +1310,12 @@
   }
 }
 
-void CFGBuilder::cleanupConstructionContext(CXXConstructExpr *CE) {
+void CFGBuilder::cleanupConstructionContext(Expr *E) {
   assert(BuildOpts.AddRichCXXConstructors &&
          "We should not be managing construction contexts!");
-  assert(ConstructionContextMap.count(CE) &&
+  assert(ConstructionContextMap.count(E) &&
          "Cannot exit construction context without the context!");
-  ConstructionContextMap.erase(CE);
+  ConstructionContextMap.erase(E);
 }
 
 
@@ -2360,7 +2393,10 @@
   }
 
   if (!NoReturn && !AddEHEdge) {
-    return VisitStmt(C, asc.withAlwaysAdd(true));
+    autoCreateBlock();
+    appendCall(Block, C);
+
+    return VisitChildren(C);
   }
 
   if (Block) {
@@ -2374,7 +2410,7 @@
   else
     Block = createBlock();
 
-  appendStmt(Block, C);
+  appendCall(Block, C);
 
   if (AddEHEdge) {
     // Add exceptional edges.
@@ -4516,6 +4552,7 @@
     case CFGElement::LifetimeEnds:
     case CFGElement::Statement:
     case CFGElement::Constructor:
+    case CFGElement::CXXRecordTypedCall:
     case CFGElement::ScopeBegin:
     case CFGElement::ScopeEnd:
       llvm_unreachable("getDestructorDecl should only be used with "
@@ -4868,6 +4905,49 @@
     OS << " (Member initializer)";
 }
 
+static void print_construction_context(raw_ostream &OS,
+                                       StmtPrinterHelper &Helper,
+                                       const ConstructionContext *CC) {
+  const Stmt *S1 = nullptr, *S2 = nullptr;
+  switch (CC->getKind()) {
+  case ConstructionContext::ConstructorInitializerKind: {
+    OS << ", ";
+    const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+    print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+    break;
+  }
+  case ConstructionContext::SimpleVariableKind: {
+    const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+    S1 = DSCC->getDeclStmt();
+    break;
+  }
+  case ConstructionContext::NewAllocatedObjectKind: {
+    const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+    S1 = NECC->getCXXNewExpr();
+    break;
+  }
+  case ConstructionContext::ReturnedValueKind: {
+    const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+    S1 = RSCC->getReturnStmt();
+    break;
+  }
+  case ConstructionContext::TemporaryObjectKind: {
+    const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+    S1 = TOCC->getCXXBindTemporaryExpr();
+    S2 = TOCC->getMaterializedTemporaryExpr();
+    break;
+  }
+  }
+  if (S1) {
+    OS << ", ";
+    Helper.handledStmt(const_cast<Stmt *>(S1), OS);
+  }
+  if (S2) {
+    OS << ", ";
+    Helper.handledStmt(const_cast<Stmt *>(S2), OS);
+  }
+}
+
 static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
                        const CFGElement &E) {
   if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
@@ -4897,54 +4977,22 @@
     }
     S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts()));
 
-    if (isa<CXXOperatorCallExpr>(S)) {
+    if (auto VTC = E.getAs<CFGCXXRecordTypedCall>()) {
+      if (isa<CXXOperatorCallExpr>(S))
+        OS << " (OperatorCall)";
+      OS << " (CXXRecordTypedCall";
+      print_construction_context(OS, Helper, VTC->getConstructionContext());
+      OS << ")";
+    } else if (isa<CXXOperatorCallExpr>(S)) {
       OS << " (OperatorCall)";
     } else if (isa<CXXBindTemporaryExpr>(S)) {
       OS << " (BindTemporary)";
     } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
-      OS << " (CXXConstructExpr, ";
+      OS << " (CXXConstructExpr";
       if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
-        const ConstructionContext *CC = CE->getConstructionContext();
-        const Stmt *S1 = nullptr, *S2 = nullptr;
-        switch (CC->getKind()) {
-        case ConstructionContext::ConstructorInitializerKind: {
-          const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
-          print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
-          OS << ", ";
-          break;
-        }
-        case ConstructionContext::SimpleVariableKind: {
-          const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
-          S1 = DSCC->getDeclStmt();
-          break;
-        }
-        case ConstructionContext::NewAllocatedObjectKind: {
-          const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
-          S1 = NECC->getCXXNewExpr();
-          break;
-        }
-        case ConstructionContext::ReturnedValueKind: {
-          const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
-          S1 = RSCC->getReturnStmt();
-          break;
-        }
-        case ConstructionContext::TemporaryObjectKind: {
-          const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
-          S1 = TOCC->getCXXBindTemporaryExpr();
-          S2 = TOCC->getMaterializedTemporaryExpr();
-          break;
-        }
-        }
-        if (S1) {
-          Helper.handledStmt(const_cast<Stmt *>(S1), OS);
-          OS << ", ";
-        }
-        if (S2) {
-          Helper.handledStmt(const_cast<Stmt *>(S2), OS);
-          OS << ", ";
-        }
+        print_construction_context(OS, Helper, CE->getConstructionContext());
       }
-      OS << CCE->getType().getAsString() << ")";
+      OS << ", " << CCE->getType().getAsString() << ")";
     } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) {
       OS << " (" << CE->getStmtClassName() << ", "
          << CE->getCastKindName()
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to