]> granicus.if.org Git - clang/commitdiff
[CFG] [analyzer] Add construction context to C++ return-by-value call elements.
authorArtem Dergachev <artem.dergachev@gmail.com>
Mon, 12 Mar 2018 23:12:40 +0000 (23:12 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Mon, 12 Mar 2018 23:12:40 +0000 (23:12 +0000)
This patch adds a new CFGStmt sub-class, CFGCXXRecordTypedCall, which replaces
the regular CFGStmt for the respective CallExpr whenever the CFG has additional
information to provide regarding the lifetime of the returned value.

This additional call site information is represented by a ConstructionContext
(which was previously used for CFGConstructor elements) that provides references
to CXXBindTemporaryExpr and MaterializeTemporaryExpr that surround the call.

This corresponds to the common C++ calling convention solution of providing
the target address for constructing the return value as an auxiliary implicit
argument during function call.

One of the use cases for such extra context at the call site would be to perform
any sort of inter-procedural analysis over the CFG that involves functions
returning objects by value. In this case the elidable constructor at the return
site would construct the object explained by the context at the call site, and
its lifetime would also be managed by the caller, not the callee.

The extra context would also be useful for properly handling the return-value
temporary at the call site, even if the callee is not being analyzed
inter-procedurally.

Differential Revision: https://reviews.llvm.org/D44120

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@327343 91177308-0d34-0410-b5e6-96231b3b80d8

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 1d6a8d40f4ecca190ce83c053b4ea0731f686015..133d207ca6124d880128ed44cb840374b84ac1c7 100644 (file)
@@ -39,6 +39,7 @@ class ASTContext;
 class BinaryOperator;
 class CFG;
 class ConstructionContext;
+class TemporaryObjectConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -65,8 +66,9 @@ public:
     // stmt kind
     Statement,
     Constructor,
+    CXXRecordTypedCall,
     STMT_BEGIN = Statement,
-    STMT_END = Constructor,
+    STMT_END = CXXRecordTypedCall,
     // dtor kind
     AutomaticObjectDtor,
     DeleteDtor,
@@ -158,10 +160,6 @@ public:
     return static_cast<ConstructionContext *>(Data2.getPointer());
   }
 
-  QualType getType() const {
-    return cast<CXXConstructExpr>(getStmt())->getType();
-  }
-
 private:
   friend class CFGElement;
 
@@ -172,6 +170,46 @@ private:
   }
 };
 
+/// 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 @@ public:
     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 7fec3f7de8caf4c64d86931e2633f43fb6e4a1ee..6aab6b690452e83dea64b9e55200b8572c7ea75b 100644 (file)
@@ -483,8 +483,8 @@ class CFGBuilder {
 
   // 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 @@ private:
   // 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 @@ private:
                                 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 @@ private:
     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 @@ static const VariableArrayType *FindVA(const Type *t) {
 }
 
 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 @@ void CFGBuilder::findConstructionContexts(
     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::findConstructionContexts(
   }
 }
 
-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 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
   }
 
   if (!NoReturn && !AddEHEdge) {
-    return VisitStmt(C, asc.withAlwaysAdd(true));
+    autoCreateBlock();
+    appendCall(Block, C);
+
+    return VisitChildren(C);
   }
 
   if (Block) {
@@ -2374,7 +2410,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
   else
     Block = createBlock();
 
-  appendStmt(Block, C);
+  appendCall(Block, C);
 
   if (AddEHEdge) {
     // Add exceptional edges.
@@ -4516,6 +4552,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
     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 @@ static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper,
     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 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
     }
     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()
index bacc99047cf85e6b71469e7234fa8fc0ad9cbf45..b25c9ac8c4c6acce2283da71e63c85ad9b73ce68 100644 (file)
@@ -612,6 +612,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
   switch (E.getKind()) {
     case CFGElement::Statement:
     case CFGElement::Constructor:
+    case CFGElement::CXXRecordTypedCall:
       ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred);
       return;
     case CFGElement::Initializer:
index db9fcfac0080b7534bffa9f554ad091dc204e375..fafedbb32b64cfe33b5d37186d0eb82c2916f501 100644 (file)
@@ -579,6 +579,7 @@ getLocationForCaller(const StackFrameContext *SFC,
   switch (Source.getKind()) {
   case CFGElement::Statement:
   case CFGElement::Constructor:
+  case CFGElement::CXXRecordTypedCall:
     return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
                                   SM, CallerCtx);
   case CFGElement::Initializer: {
index cad32f842c23e40d465eea7d5cafb935e93c2731..c22f96dd42a26a2a530d5a18fa7da0eb3c88940c 100644 (file)
@@ -97,7 +97,7 @@ void simpleVariableWithOperatorNewInBraces() {
 // 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 @@ void simpleVariableInitializedByValue() {
 // 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 @@ void referenceVariableWithInitializer() {
 // 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 @@ public:
 // 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 @@ C returnTemporaryWithArgument() {
 // 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 @@ C returnTemporaryConstructedByFunction() {
 // 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 @@ void referenceVariableWithInitializer() {
 // 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 @@ void implicitConstructionConversionFromTemporary() {
 // 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 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
 // 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 d17c5beff4a641dddd90ad9a6dba5e7139cb5bdd..eecbabe5a1cf11e1bd1843a006aa2b7b888644d5 100644 (file)
@@ -1111,7 +1111,8 @@ int testConsistencyNestedNormalReturn(bool value) {
 // 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,7 +1131,8 @@ int testConsistencyNestedNormalReturn(bool value) {
 // 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]
@@ -1139,7 +1141,8 @@ int testConsistencyNestedNormalReturn(bool value) {
 // 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]