]> granicus.if.org Git - clang/commitdiff
[CFG] [analyzer] Add construction contexts that explain pre-C++17 copy elision.
authorArtem Dergachev <artem.dergachev@gmail.com>
Thu, 28 Jun 2018 00:04:54 +0000 (00:04 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Thu, 28 Jun 2018 00:04:54 +0000 (00:04 +0000)
Before C++17 copy elision was optional, even if the elidable copy/move
constructor had arbitrary side effects. The elidable constructor is present
in the AST, but marked as elidable.

In these cases CFG now contains additional information that allows its clients
to figure out if a temporary object is only being constructed so that to pass
it to an elidable constructor. If so, it includes a reference to the elidable
constructor's construction context, so that the client could elide the
elidable constructor and construct the object directly at its final destination.

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

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

14 files changed:
include/clang/Analysis/AnalysisDeclContext.h
include/clang/Analysis/CFG.h
include/clang/Analysis/ConstructionContext.h
include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
lib/Analysis/AnalysisDeclContext.cpp
lib/Analysis/CFG.cpp
lib/Analysis/ConstructionContext.cpp
lib/StaticAnalyzer/Core/AnalysisManager.cpp
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
test/Analysis/analyzer-config.c
test/Analysis/analyzer-config.cpp
test/Analysis/cfg-rich-constructors.cpp
test/Analysis/temp-obj-dtors-cfg-output.cpp

index d6eb5e4d5f0d0bd68297a96fa2e0cb3e160e6394..8c391b5ee1e5d75b923a3c6edda9e8228149f2db 100644 (file)
@@ -451,6 +451,7 @@ public:
                              bool addStaticInitBranches = false,
                              bool addCXXNewAllocator = true,
                              bool addRichCXXConstructors = true,
+                             bool markElidedCXXConstructors = true,
                              CodeInjector *injector = nullptr);
 
   AnalysisDeclContext *getContext(const Decl *D);
index 580cbd6ee3e25113b213987acf7dbcd5b696dd6f..f25789822d1629f2c7bca362d3eae9abd1c62064 100644 (file)
@@ -1025,6 +1025,7 @@ public:
     bool AddCXXNewAllocator = false;
     bool AddCXXDefaultInitExprInCtors = false;
     bool AddRichCXXConstructors = false;
+    bool MarkElidedCXXConstructors = false;
 
     BuildOptions() = default;
 
index 8df95a8ada20a6e0cfff6ba91ae6159327cef144..40cb0e7e5ddaaa5f397c0fcfcf57197cf68943c3 100644 (file)
@@ -107,7 +107,10 @@ public:
     INITIALIZER_BEGIN = SimpleConstructorInitializerKind,
     INITIALIZER_END = CXX17ElidedCopyConstructorInitializerKind,
     NewAllocatedObjectKind,
-    TemporaryObjectKind,
+    SimpleTemporaryObjectKind,
+    ElidedTemporaryObjectKind,
+    TEMPORARY_BEGIN = SimpleTemporaryObjectKind,
+    TEMPORARY_END = ElidedTemporaryObjectKind,
     SimpleReturnedValueKind,
     CXX17ElidedCopyReturnedValueKind,
     RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
@@ -305,16 +308,15 @@ class TemporaryObjectConstructionContext : public ConstructionContext {
   const CXXBindTemporaryExpr *BTE;
   const MaterializeTemporaryExpr *MTE;
 
-  friend class ConstructionContext; // Allows to create<>() itself.
-
+protected:
   explicit TemporaryObjectConstructionContext(
-      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
-      : ConstructionContext(ConstructionContext::TemporaryObjectKind),
-        BTE(BTE), MTE(MTE) {
+      ConstructionContext::Kind K, const CXXBindTemporaryExpr *BTE,
+      const MaterializeTemporaryExpr *MTE)
+      : ConstructionContext(K), BTE(BTE), MTE(MTE) {
     // Both BTE and MTE can be null here, all combinations possible.
     // Even though for now at least one should be non-null, we simply haven't
-    // implemented this case yet (this would be a temporary in the middle of
-    // nowhere that doesn't have a non-trivial destructor).
+    // implemented the other case yet (this would be a temporary in the middle
+    // of nowhere that doesn't have a non-trivial destructor).
   }
 
 public:
@@ -334,7 +336,67 @@ public:
   }
 
   static bool classof(const ConstructionContext *CC) {
-    return CC->getKind() == TemporaryObjectKind;
+    return CC->getKind() >= TEMPORARY_BEGIN && CC->getKind() <= TEMPORARY_END;
+  }
+};
+
+/// Represents a temporary object that is not constructed for the purpose of
+/// being immediately copied/moved by an elidable copy/move-constructor.
+/// This includes temporary objects "in the middle of nowhere" like T(123) and
+/// lifetime-extended temporaries.
+class SimpleTemporaryObjectConstructionContext
+    : public TemporaryObjectConstructionContext {
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit SimpleTemporaryObjectConstructionContext(
+      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
+      : TemporaryObjectConstructionContext(
+            ConstructionContext::SimpleTemporaryObjectKind, BTE, MTE) {}
+
+public:
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == SimpleTemporaryObjectKind;
+  }
+};
+
+/// Represents a temporary object that is constructed for the sole purpose
+/// of being immediately copied by an elidable copy/move constructor.
+/// For example, T t = T(123); includes a temporary T(123) that is immediately
+/// copied to variable t. In such cases the elidable copy can (but not
+/// necessarily should) be omitted ("elided") accodring to the rules of the
+/// language; the constructor would then construct variable t directly.
+/// This construction context contains information of the elidable constructor
+/// and its respective construction context.
+class ElidedTemporaryObjectConstructionContext
+    : public TemporaryObjectConstructionContext {
+  const CXXConstructExpr *ElidedCE;
+  const ConstructionContext *ElidedCC;
+
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit ElidedTemporaryObjectConstructionContext(
+      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE,
+      const CXXConstructExpr *ElidedCE, const ConstructionContext *ElidedCC)
+      : TemporaryObjectConstructionContext(
+            ConstructionContext::ElidedTemporaryObjectKind, BTE, MTE),
+        ElidedCE(ElidedCE), ElidedCC(ElidedCC) {
+    // Elided constructor and its context should be either both specified
+    // or both unspecified. In the former case, the constructor must be
+    // elidable.
+    assert(ElidedCE && ElidedCE->isElidable() && ElidedCC);
+  }
+
+public:
+  const CXXConstructExpr *getConstructorAfterElision() const {
+    return ElidedCE;
+  }
+
+  const ConstructionContext *getConstructionContextAfterElision() const {
+    return ElidedCC;
+  }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == ElidedTemporaryObjectKind;
   }
 };
 
index a3f4c38944129283a4522f82d6fe9e03693fe850..367591ae1f609dbbeae08816f23fd12dccb63af3 100644 (file)
@@ -327,6 +327,9 @@ private:
   /// \sa naiveCTUEnabled
   Optional<bool> NaiveCTU;
 
+  /// \sa shouldElideConstructors
+  Optional<bool> ElideConstructors;
+
 
   /// A helper function that retrieves option for a given full-qualified
   /// checker name.
@@ -703,6 +706,13 @@ public:
   /// This is an experimental feature to inline functions from another
   /// translation units.
   bool naiveCTUEnabled();
+
+  /// Returns true if elidable C++ copy-constructors and move-constructors
+  /// should be actually elided during analysis. Both behaviors are allowed
+  /// by the C++ standard, and the analyzer, like CodeGen, defaults to eliding.
+  /// Starting with C++17 some elisions become mandatory, and in these cases
+  /// the option will be ignored.
+  bool shouldElideConstructors();
 };
   
 using AnalyzerOptionsRef = IntrusiveRefCntPtr<AnalyzerOptions>;
index e746e8dfa5382b71b538a98c37628072dbbae544..486fffbe1299628cd68910cd02131e51f6a04b87 100644 (file)
@@ -71,7 +71,8 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
     bool addInitializers, bool addTemporaryDtors, bool addLifetime,
     bool addLoopExit, bool addScopes, bool synthesizeBodies,
     bool addStaticInitBranch, bool addCXXNewAllocator,
-    bool addRichCXXConstructors, CodeInjector *injector)
+    bool addRichCXXConstructors, bool markElidedCXXConstructors,
+    CodeInjector *injector)
     : Injector(injector), FunctionBodyFarm(ASTCtx, injector),
       SynthesizeBodies(synthesizeBodies) {
   cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
@@ -84,6 +85,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
   cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
   cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
   cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors;
+  cfgBuildOptions.MarkElidedCXXConstructors = markElidedCXXConstructors;
 }
 
 void AnalysisDeclContextManager::clear() { Contexts.clear(); }
index b02fbe10712b04c59dddac6d5f74b8acfe19c9a5..62803f4ba566e3dc99a53b5aff6219918e1acf3c 100644 (file)
@@ -1252,10 +1252,22 @@ void CFGBuilder::findConstructionContexts(
   if (!Child)
     return;
 
+  auto withExtraLayer = [this, Layer](Stmt *S) {
+    return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S,
+                                            Layer);
+  };
+
   switch(Child->getStmtClass()) {
   case Stmt::CXXConstructExprClass:
   case Stmt::CXXTemporaryObjectExprClass: {
-    consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
+    // Support pre-C++17 copy elision AST.
+    auto *CE = cast<CXXConstructExpr>(Child);
+    if (BuildOpts.MarkElidedCXXConstructors && CE->isElidable()) {
+      assert(CE->getNumArgs() == 1);
+      findConstructionContexts(withExtraLayer(CE), CE->getArg(0));
+    }
+
+    consumeConstructionContext(Layer, CE);
     break;
   }
   // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr.
@@ -1294,10 +1306,21 @@ void CFGBuilder::findConstructionContexts(
   }
   case Stmt::CXXBindTemporaryExprClass: {
     auto *BTE = cast<CXXBindTemporaryExpr>(Child);
-    findConstructionContexts(
-        ConstructionContextLayer::create(cfg->getBumpVectorContext(),
-                                         BTE, Layer),
-        BTE->getSubExpr());
+    findConstructionContexts(withExtraLayer(BTE), BTE->getSubExpr());
+    break;
+  }
+  case Stmt::MaterializeTemporaryExprClass: {
+    // Normally we don't want to search in MaterializeTemporaryExpr because
+    // it indicates the beginning of a temporary object construction context,
+    // so it shouldn't be found in the middle. However, if it is the beginning
+    // of an elidable copy or move construction context, we need to include it.
+    if (const auto *CE =
+            dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) {
+      if (CE->isElidable()) {
+        auto *MTE = cast<MaterializeTemporaryExpr>(Child);
+        findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr());
+      }
+    }
     break;
   }
   case Stmt::ConditionalOperatorClass: {
@@ -4931,7 +4954,7 @@ static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper,
 static void print_construction_context(raw_ostream &OS,
                                        StmtPrinterHelper &Helper,
                                        const ConstructionContext *CC) {
-  const Stmt *S1 = nullptr, *S2 = nullptr;
+  SmallVector<const Stmt *, 3> Stmts;
   switch (CC->getKind()) {
   case ConstructionContext::SimpleConstructorInitializerKind: {
     OS << ", ";
@@ -4944,52 +4967,56 @@ static void print_construction_context(raw_ostream &OS,
     const auto *CICC =
         cast<CXX17ElidedCopyConstructorInitializerConstructionContext>(CC);
     print_initializer(OS, Helper, CICC->getCXXCtorInitializer());
-    S2 = CICC->getCXXBindTemporaryExpr();
+    Stmts.push_back(CICC->getCXXBindTemporaryExpr());
     break;
   }
   case ConstructionContext::SimpleVariableKind: {
     const auto *SDSCC = cast<SimpleVariableConstructionContext>(CC);
-    S1 = SDSCC->getDeclStmt();
+    Stmts.push_back(SDSCC->getDeclStmt());
     break;
   }
   case ConstructionContext::CXX17ElidedCopyVariableKind: {
     const auto *CDSCC = cast<CXX17ElidedCopyVariableConstructionContext>(CC);
-    S1 = CDSCC->getDeclStmt();
-    S2 = CDSCC->getCXXBindTemporaryExpr();
+    Stmts.push_back(CDSCC->getDeclStmt());
+    Stmts.push_back(CDSCC->getCXXBindTemporaryExpr());
     break;
   }
   case ConstructionContext::NewAllocatedObjectKind: {
     const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
-    S1 = NECC->getCXXNewExpr();
+    Stmts.push_back(NECC->getCXXNewExpr());
     break;
   }
   case ConstructionContext::SimpleReturnedValueKind: {
     const auto *RSCC = cast<SimpleReturnedValueConstructionContext>(CC);
-    S1 = RSCC->getReturnStmt();
+    Stmts.push_back(RSCC->getReturnStmt());
     break;
   }
   case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
     const auto *RSCC =
         cast<CXX17ElidedCopyReturnedValueConstructionContext>(CC);
-    S1 = RSCC->getReturnStmt();
-    S2 = RSCC->getCXXBindTemporaryExpr();
+    Stmts.push_back(RSCC->getReturnStmt());
+    Stmts.push_back(RSCC->getCXXBindTemporaryExpr());
     break;
   }
-  case ConstructionContext::TemporaryObjectKind: {
-    const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
-    S1 = TOCC->getCXXBindTemporaryExpr();
-    S2 = TOCC->getMaterializedTemporaryExpr();
+  case ConstructionContext::SimpleTemporaryObjectKind: {
+    const auto *TOCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+    Stmts.push_back(TOCC->getCXXBindTemporaryExpr());
+    Stmts.push_back(TOCC->getMaterializedTemporaryExpr());
     break;
   }
+  case ConstructionContext::ElidedTemporaryObjectKind: {
+    const auto *TOCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+    Stmts.push_back(TOCC->getCXXBindTemporaryExpr());
+    Stmts.push_back(TOCC->getMaterializedTemporaryExpr());
+    Stmts.push_back(TOCC->getConstructorAfterElision());
+    break;
   }
-  if (S1) {
-    OS << ", ";
-    Helper.handledStmt(const_cast<Stmt *>(S1), OS);
-  }
-  if (S2) {
-    OS << ", ";
-    Helper.handledStmt(const_cast<Stmt *>(S2), OS);
   }
+  for (auto I: Stmts)
+    if (I) {
+      OS << ", ";
+      Helper.handledStmt(const_cast<Stmt *>(I), OS);
+    }
 }
 
 static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
index 9db6f214beed6281553c6f0e82e26e4546faa42b..de00375336768dfd8a40f4ca33c557014ff81a46 100644 (file)
@@ -61,7 +61,6 @@ const ConstructionContext *ConstructionContext::createFromLayers(
       // For temporaries with destructors, there may or may not be
       // lifetime extension on the parent layer.
       if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
-        assert(ParentLayer->isLast());
         // C++17 *requires* elision of the constructor at the return site
         // and at variable/member initialization site, while previous standards
         // were allowing an optional elidable constructor.
@@ -77,8 +76,33 @@ const ConstructionContext *ConstructionContext::createFromLayers(
         // both destruction and materialization info attached to it in the AST.
         if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
                  ParentLayer->getTriggerStmt()))) {
-          return create<TemporaryObjectConstructionContext>(C, BTE, MTE);
+          // Handle pre-C++17 copy and move elision.
+          const CXXConstructExpr *ElidedCE = nullptr;
+          const ConstructionContext *ElidedCC = nullptr;
+          if (const ConstructionContextLayer *ElidedLayer =
+                  ParentLayer->getParent()) {
+            ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
+            assert(ElidedCE->isElidable());
+            // We're creating a construction context that might have already
+            // been created elsewhere. Maybe we should unique our construction
+            // contexts. That's what we often do, but in this case it's unlikely
+            // to bring any benefits.
+            ElidedCC = createFromLayers(C, ElidedLayer->getParent());
+            if (!ElidedCC) {
+              // We may fail to create the elided construction context.
+              // In this case, skip copy elision entirely.
+              return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
+                                                                      MTE);
+            } else {
+              return create<ElidedTemporaryObjectConstructionContext>(
+                  C, BTE, MTE, ElidedCE, ElidedCC);
+            }
+          }
+          assert(ParentLayer->isLast());
+          return create<SimpleTemporaryObjectConstructionContext>(C, BTE, MTE);
         }
+        assert(ParentLayer->isLast());
+
         // This is a constructor into a function argument. Not implemented yet.
         if (isa<CallExpr>(ParentLayer->getTriggerStmt()))
           return nullptr;
@@ -99,7 +123,11 @@ const ConstructionContext *ConstructionContext::createFromLayers(
         llvm_unreachable("Unexpected construction context with destructor!");
       }
       // A temporary object that doesn't require materialization.
-      return create<TemporaryObjectConstructionContext>(C, BTE, /*MTE=*/nullptr);
+      // In particular, it shouldn't require copy elision, because
+      // copy/move constructors take a reference, which requires
+      // materialization to obtain the glvalue.
+      return create<SimpleTemporaryObjectConstructionContext>(C, BTE,
+                                                              /*MTE=*/nullptr);
     }
     if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
       // If the object requires destruction and is not lifetime-extended,
@@ -110,8 +138,28 @@ const ConstructionContext *ConstructionContext::createFromLayers(
              MTE->getStorageDuration() != SD_FullExpression))
         return nullptr;
 
+      // Handle pre-C++17 copy and move elision.
+      const CXXConstructExpr *ElidedCE = nullptr;
+      const ConstructionContext *ElidedCC = nullptr;
+      if (const ConstructionContextLayer *ElidedLayer = TopLayer->getParent()) {
+        ElidedCE = cast<CXXConstructExpr>(ElidedLayer->getTriggerStmt());
+        assert(ElidedCE->isElidable());
+        // We're creating a construction context that might have already
+        // been created elsewhere. Maybe we should unique our construction
+        // contexts. That's what we often do, but in this case it's unlikely
+        // to bring any benefits.
+        ElidedCC = createFromLayers(C, ElidedLayer->getParent());
+        if (!ElidedCC) {
+          // We may fail to create the elided construction context.
+          // In this case, skip copy elision entirely.
+          return create<SimpleTemporaryObjectConstructionContext>(C, nullptr,
+                                                                  MTE);
+        }
+        return create<ElidedTemporaryObjectConstructionContext>(
+            C, nullptr, MTE, ElidedCE, ElidedCC);
+      }
       assert(TopLayer->isLast());
-      return create<TemporaryObjectConstructionContext>(C, nullptr, MTE);
+      return create<SimpleTemporaryObjectConstructionContext>(C, nullptr, MTE);
     }
     if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
       assert(TopLayer->isLast());
index af107ab224f6beeff31c1508443f807ddc570805..dc0d3ec8493a2998e103a4927eaa64af06443c33 100644 (file)
@@ -31,6 +31,7 @@ AnalysisManager::AnalysisManager(
                 Options.shouldConditionalizeStaticInitializers(),
                 /*addCXXNewAllocator=*/true,
                 Options.includeRichConstructorsInCFG(),
+                Options.shouldElideConstructors(),
                 injector),
       Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC),
       CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
index bddd4435a753d303264393a63c9b2489f76fe173..6fa5fec52885a7a219de09b19d22218e5beb74d2 100644 (file)
@@ -321,6 +321,12 @@ bool AnalyzerOptions::shouldSerializeStats() {
                           /* Default = */ false);
 }
 
+bool AnalyzerOptions::shouldElideConstructors() {
+  return getBooleanOption(ElideConstructors,
+                          "elide-constructors",
+                          /* Default = */ true);
+}
+
 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
                                         const CheckerBase *C,
                                         bool SearchInParents) {
index f85bfc6e4927d0c2674a13deda9774378a360ca1..497e1fa5560f393dd5aa3ece6c34c7e3a3ff7c4c 100644 (file)
@@ -209,7 +209,11 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
       }
       llvm_unreachable("Unhandled return value construction context!");
     }
-    case ConstructionContext::TemporaryObjectKind: {
+    case ConstructionContext::ElidedTemporaryObjectKind:
+      assert(AMgr.getAnalyzerOptions().shouldElideConstructors());
+      // FALL-THROUGH
+    case ConstructionContext::SimpleTemporaryObjectKind: {
+      // TODO: Copy elision implementation goes here.
       const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
       const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
       const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
index 40f86e1504d90b4b43b173faa8fc92d2a8c4b5ba..2105a4faf4c7ab54f2af27dfbd81e4923742cd3a 100644 (file)
@@ -18,6 +18,7 @@ void foo() {
 // CHECK-NEXT: cfg-rich-constructors = true
 // CHECK-NEXT: cfg-scopes = false
 // CHECK-NEXT: cfg-temporary-dtors = true
+// CHECK-NEXT: elide-constructors = true
 // CHECK-NEXT: exploration_strategy = unexplored_first_queue
 // CHECK-NEXT: faux-bodies = true
 // CHECK-NEXT: graph-trim-interval = 1000
@@ -35,4 +36,4 @@ void foo() {
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 23
+// CHECK-NEXT: num-entries = 24
index 888cd77eef05434b6e116f7598e22728a296e11b..0eaefe2abf2deb5f6425f160dc2734002cb0505b 100644 (file)
@@ -32,6 +32,7 @@ public:
 // CHECK-NEXT: cfg-rich-constructors = true
 // CHECK-NEXT: cfg-scopes = false
 // CHECK-NEXT: cfg-temporary-dtors = true
+// CHECK-NEXT: elide-constructors = true
 // CHECK-NEXT: experimental-enable-naive-ctu-analysis = false
 // CHECK-NEXT: exploration_strategy = unexplored_first_queue
 // CHECK-NEXT: faux-bodies = true
@@ -50,4 +51,4 @@ public:
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 30
+// CHECK-NEXT: num-entries = 31
index 42fe8539a4e55ac1e85d6a2e391d02015937b16d..a07bdc7b87f6ec4a7f0bd9797f7023fc8d144745 100644 (file)
@@ -1,7 +1,11 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11 %s
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ELIDE,CXX11-ELIDE %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17 %s
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,ELIDE,CXX17-ELIDE %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w -analyzer-config elide-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,NOELIDE,CXX11-NOELIDE %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w -analyzer-config elide-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,NOELIDE,CXX17-NOELIDE %s
 
 class C {
 public:
@@ -99,10 +103,12 @@ void simpleVariableWithOperatorNewInBraces() {
 // CHECK: void simpleVariableInitializedByValue()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
 // CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CXX11-NEXT:     6: C c = C::get();
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX17-NEXT:     4: C c = C::get();
 void simpleVariableInitializedByValue() {
   C c = C::get();
@@ -122,17 +128,21 @@ void simpleVariableInitializedByValue() {
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
+// CXX11-ELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5])
+// CXX11-NOELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CXX11-NEXT:     4: [B2.3]
-// CXX11-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     3: [B2.2]()
 // CHECK:        [B3]
 // CHECK-NEXT:     1: 0
 // CHECK-NEXT:     2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
 // CXX11-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B3.4]
-// CXX11-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     3: [B3.2] (CXXConstructExpr, class C)
 // CXX17-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CHECK:        [B4]
@@ -146,7 +156,9 @@ void simpleVariableWithTernaryOperator(bool coin) {
 // CHECK: void simpleVariableWithElidableCopy()
 // CHECK:          1: 0
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX17-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B1.4]
 // CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
@@ -185,14 +197,16 @@ void referenceVariableWithInitializer() {
 // CHECK:        [B2]
 // CHECK-NEXT:     1: C::get
 // CHECK-NEXT:     2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
+// CXX11-ELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4], [B2.5])
+// CXX11-NOELIDE-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B2.4])
 // CXX11-NEXT:     4: [B2.3]
 // CXX11-NEXT:     5: [B2.4] (CXXConstructExpr, [B1.3], class C)
 // CXX17-NEXT:     3: [B2.2]() (CXXRecordTypedCall, [B1.3])
 // CHECK:        [B3]
 // CHECK-NEXT:     1: 0
 // CHECK-NEXT:     2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
+// CXX11-ELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], [B3.6], class C)
+// CXX11-NOELIDE-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.5], class C)
 // CXX11-NEXT:     4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B3.4]
 // CXX11-NEXT:     6: [B3.5] (CXXConstructExpr, [B1.3], class C)
@@ -242,7 +256,8 @@ public:
 // CHECK-NEXT:     7: CFGNewAllocator(C *)
 // CHECK-NEXT:     8: C::get
 // CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11])
+// CXX11-ELIDE-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11], [B1.12])
+// CXX11-NOELIDE-NEXT:    10: [B1.9]() (CXXRecordTypedCall, [B1.11])
 // CXX11-NEXT:    11: [B1.10]
 // CXX11-NEXT:    12: [B1.11] (CXXConstructExpr, [B1.13], class C)
 // CXX11-NEXT:    13: new C([B1.12])
@@ -270,7 +285,8 @@ public:
 // CHECK: F()
 // CHECK:          1: E::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class ctor_initializers::E (*)(
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class ctor_initializers::E)
 // CXX11-NEXT:     6: [B1.5]
@@ -326,10 +342,12 @@ C returnBracesWithMultipleItems() {
 }
 
 // CHECK: C returnTemporary()
-// CHECK:          1: C() (CXXConstructExpr, [B1.2], class C)
+// CXX11-ELIDE:    1: C() (CXXConstructExpr, [B1.2], [B1.3], class C)
+// CXX11-NOELIDE:  1: C() (CXXConstructExpr, [B1.2], class C)
 // CXX11-NEXT:     2: [B1.1]
 // CXX11-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.4], class C)
 // CXX11-NEXT:     4: return [B1.3];
+// CXX17:          1: C() (CXXConstructExpr, [B1.2], class C)
 // CXX17-NEXT:     2: return [B1.1];
 C returnTemporary() {
   return C();
@@ -338,7 +356,9 @@ C returnTemporary() {
 // CHECK: C returnTemporaryWithArgument()
 // CHECK:          1: nullptr
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
-// CHECK-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX11-ELIDE-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], [B1.6], class C)
+// CXX11-NOELIDE-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class C)
+// CXX17-NEXT:     3: [B1.2] (CXXConstructExpr, [B1.5], class C)
 // CHECK-NEXT:     4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     5: [B1.4]
 // CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.7], class C)
@@ -352,10 +372,12 @@ C returnTemporaryWithArgument() {
 // CHECK: C returnTemporaryConstructedByFunction()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CHECK-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
 // CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.6], class C)
 // CXX11-NEXT:     6: return [B1.5];
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX17-NEXT:     4: return [B1.3];
 C returnTemporaryConstructedByFunction() {
   return C::get();
@@ -364,9 +386,11 @@ C returnTemporaryConstructedByFunction() {
 // CHECK: C returnChainOfCopies()
 // CHECK:          1: C::get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.5])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4])
 // CXX11-NEXT:     4: [B1.3]
-// CXX11-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
+// CXX11-ELIDE-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], [B1.8], class C)
+// CXX11-NOELIDE-NEXT:     5: [B1.4] (CXXConstructExpr, [B1.7], class C)
 // CXX11-NEXT:     6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
 // CXX11-NEXT:     7: [B1.6]
 // CXX11-NEXT:     8: [B1.7] (CXXConstructExpr, [B1.9], class C)
@@ -390,7 +414,8 @@ public:
 
 // FIXME: There should be no temporary destructor in C++17.
 // CHECK:  return_stmt_with_dtor::D returnTemporary()
-// CXX11:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
+// CXX11-ELIDE:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class return_stmt_with_dtor::D)
+// CXX11-NOELIDE:          1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.2], [B1.4], class return_stmt_with_dtor::D)
 // CXX11-NEXT:     2: [B1.1] (BindTemporary)
 // CXX11-NEXT:     3: [B1.2] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
 // CXX11-NEXT:     4: [B1.3]
@@ -409,7 +434,8 @@ D returnTemporary() {
 // CHECK: void returnByValueIntoVariable()
 // CHECK:          1: returnTemporary
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class return_stmt_with_dtor::D (*)(void))
-// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// CXX11-ELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
+// CXX11-NOELIDE-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
 // CXX11-NEXT:     4: [B1.3] (BindTemporary)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class return_stmt_with_dtor::D)
 // CXX11-NEXT:     6: [B1.5]
@@ -451,7 +477,8 @@ void temporaryInCondition() {
 }
 
 // CHECK: void temporaryInConditionVariable()
-// CHECK:          1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE:    1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE:  1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c = C();
@@ -461,6 +488,7 @@ void temporaryInCondition() {
 // CXX11-NEXT:     8: [B2.6]
 // CXX11-NEXT:     9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX11-NEXT:     T: if [B2.9]
+// CXX17:          1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX17-NEXT:     2: C c = C();
 // CXX17-NEXT:     3: c
 // CXX17-NEXT:     4: [B2.3] (ImplicitCastExpr, NoOp, const class C)
@@ -475,7 +503,8 @@ void temporaryInConditionVariable() {
 
 // CHECK: void temporaryInForLoopConditionVariable()
 // CHECK:        [B2]
-// CXX11-NEXT:     1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE-NEXT:     1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE-NEXT:     1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c2 = C();
@@ -494,7 +523,8 @@ void temporaryInConditionVariable() {
 // CXX17-NEXT:     7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX17-NEXT:     T: for (...; [B2.7]; )
 // CHECK:        [B3]
-// CXX11-NEXT:     1: C() (CXXConstructExpr, [B3.2], class C)
+// CXX11-ELIDE-NEXT:     1: C() (CXXConstructExpr, [B3.2], [B3.3], class C)
+// CXX11-NOELIDE-NEXT:     1: C() (CXXConstructExpr, [B3.2], class C)
 // CXX11-NEXT:     2: [B3.1]
 // CXX11-NEXT:     3: [B3.2] (CXXConstructExpr, [B3.4], class C)
 // CXX11-NEXT:     4: C c1 = C();
@@ -505,9 +535,9 @@ void temporaryInForLoopConditionVariable() {
 }
 
 
-// FIXME: Find construction context for the loop condition variable.
 // CHECK: void temporaryInWhileLoopConditionVariable()
-// CXX11:          1: C() (CXXConstructExpr, [B2.2], class C)
+// CXX11-ELIDE:          1: C() (CXXConstructExpr, [B2.2], [B2.3], class C)
+// CXX11-NOELIDE:          1: C() (CXXConstructExpr, [B2.2], class C)
 // CXX11-NEXT:     2: [B2.1]
 // CXX11-NEXT:     3: [B2.2] (CXXConstructExpr, [B2.4], class C)
 // CXX11-NEXT:     4: C c = C();
@@ -603,7 +633,8 @@ void referenceVariableWithInitializer() {
 // CXX11:        [B5]
 // CXX11-NEXT:     1: D::get
 // CXX11-NEXT:     2: [B5.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
-// CXX11-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6])
+// CXX11-ELIDE-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6], [B5.7])
+// CXX11-NOELIDE-NEXT:     3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6])
 // CXX11-NEXT:     4: [B5.3] (BindTemporary)
 // CXX11-NEXT:     5: [B5.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     6: [B5.5]
@@ -611,7 +642,8 @@ void referenceVariableWithInitializer() {
 // CXX11-NEXT:     8: [B5.7] (BindTemporary)
 // CXX11:        [B6]
 // CXX11-NEXT:     1: 0
-// CXX11-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], class temporary_object_expr_with_dtors::D)
+// CXX11-ELIDE-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], [B6.7], class temporary_object_expr_with_dtors::D)
+// CXX11-NOELIDE-NEXT:     2: [B6.1] (CXXConstructExpr, [B6.3], [B6.6], class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     3: [B6.2] (BindTemporary)
 // CXX11-NEXT:     4: temporary_object_expr_with_dtors::D([B6.3]) (CXXFunctionalCastExpr, ConstructorConversion, class temporary_object_expr_with_dtors::D)
 // CXX11-NEXT:     5: [B6.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
@@ -699,7 +731,8 @@ public:
 // CHECK:          1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A)
 // CXX11-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CXX11-NEXT:     3: [B1.2]
-// CXX11-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
+// CXX11-ELIDE-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], [B1.9], class implicit_constructor_conversion::B)
+// CXX11-NOELIDE-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
 // CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
 // CXX11-NEXT:     6: [B1.5] (BindTemporary)
 // CXX11-NEXT:     7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
@@ -724,7 +757,8 @@ void implicitConstructionConversionFromTemporary() {
 // 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]
-// CXX11-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
+// CXX11-ELIDE-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], [B1.11], class implicit_constructor_conversion::B)
+// CXX11-NOELIDE-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
 // CXX11-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
 // CXX11-NEXT:     8: [B1.7] (BindTemporary)
 // CXX11-NEXT:     9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
index 29d327e3835a3bb7c6efd287c292b63a4a12a04d..a0e501553560cd89eb8ed54081140ee294c5fe21 100644 (file)
@@ -235,7 +235,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -295,7 +295,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -550,12 +550,12 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (2): B6 B5
 // CHECK:   [B8]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B8.2], [B8.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B8.2], [B8.4], [B8.5], class A)
 // CHECK:     2: [B8.1] (BindTemporary)
 // CHECK:     3: [B8.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B8.3]
 // WARNINGS:     5: [B8.4] (CXXConstructExpr, class A)
-// ANALYZER:     5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], class A)
+// ANALYZER:     5: [B8.4] (CXXConstructExpr, [B8.6], [B7.3], [B7.4], class A)
 // CHECK:     6: [B8.5] (BindTemporary)
 // CHECK:     Preds (1): B10
 // CHECK:     Succs (1): B7
@@ -570,13 +570,13 @@ const C &bar3(bool coin) {
 // CHECK:     7: [B9.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B9.7]
 // WARNINGS:     9: [B9.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], class A)
+// ANALYZER:     9: [B9.8] (CXXConstructExpr, [B9.10], [B9.13], [B9.14], class A)
 // CHECK:    10: [B9.9] (BindTemporary)
 // CHECK:    11: A([B9.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B9.11] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:    13: [B9.12]
 // WARNINGS:    14: [B9.13] (CXXConstructExpr, class A)
-// ANALYZER:    14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], class A)
+// ANALYZER:    14: [B9.13] (CXXConstructExpr, [B9.15], [B7.3], [B7.4], class A)
 // CHECK:    15: [B9.14] (BindTemporary)
 // CHECK:     Preds (1): B10
 // CHECK:     Succs (1): B7
@@ -680,7 +680,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B4]
 // WARNINGS:     1: C() (CXXConstructExpr, struct C)
-// ANALYZER:     1: C() (CXXConstructExpr, [B4.2], [B4.4], struct C)
+// ANALYZER:     1: C() (CXXConstructExpr, [B4.2], [B4.4], [B4.5], struct C)
 // CHECK:     2: [B4.1] (BindTemporary)
 // CHECK:     3: [B4.2] (ImplicitCastExpr, NoOp, const struct C)
 // CHECK:     4: [B4.3]
@@ -733,7 +733,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B3]
 // CXX98-WARNINGS:     1: D() (CXXConstructExpr, struct D)
-// CXX98-ANALYZER:     1: D() (CXXConstructExpr, [B3.3], struct D)
+// CXX98-ANALYZER:     1: D() (CXXConstructExpr, [B3.3], [B3.4], struct D)
 // CXX98:     2: [B3.1] (ImplicitCastExpr, NoOp, const struct D)
 // CXX98:     3: [B3.2]
 // CXX98-WARNINGS:     4: [B3.3] (CXXConstructExpr, struct D)
@@ -745,7 +745,7 @@ const C &bar3(bool coin) {
 // CXX98:     9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
 // CXX98:     T: if [B3.9]
 // CXX11-WARNINGS:     1: D() (CXXConstructExpr, struct D)
-// CXX11-ANALYZER:     1: D() (CXXConstructExpr, [B3.2], struct D)
+// CXX11-ANALYZER:     1: D() (CXXConstructExpr, [B3.2], [B3.3], struct D)
 // CXX11:     2: [B3.1]
 // CXX11-WARNINGS:     3: [B3.2] (CXXConstructExpr, struct D)
 // CXX11-ANALYZER:     3: [B3.2] (CXXConstructExpr, [B3.4], struct D)
@@ -789,7 +789,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (2): B3 B2
 // CHECK:   [B5]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B5.2], [B5.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B5.2], [B5.4], [B5.5], class A)
 // CHECK:     2: [B5.1] (BindTemporary)
 // CHECK:     3: [B5.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B5.3]
@@ -809,7 +809,7 @@ const C &bar3(bool coin) {
 // CHECK:     7: [B6.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B6.7]
 // WARNINGS:     9: [B6.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], class A)
+// ANALYZER:     9: [B6.8] (CXXConstructExpr, [B6.10], [B6.13], [B6.14], class A)
 // CHECK:    10: [B6.9] (BindTemporary)
 // CHECK:    11: A([B6.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B6.11] (ImplicitCastExpr, NoOp, const class A)
@@ -852,7 +852,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (2): B9 B8
 // CHECK:   [B11]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B11.2], [B11.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B11.2], [B11.4], [B11.5], class A)
 // CHECK:     2: [B11.1] (BindTemporary)
 // CHECK:     3: [B11.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B11.3]
@@ -872,7 +872,7 @@ const C &bar3(bool coin) {
 // CHECK:     7: [B12.6] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     8: [B12.7]
 // WARNINGS:     9: [B12.8] (CXXConstructExpr, class A)
-// ANALYZER:     9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], class A)
+// ANALYZER:     9: [B12.8] (CXXConstructExpr, [B12.10], [B12.13], [B12.14], class A)
 // CHECK:    10: [B12.9] (BindTemporary)
 // CHECK:    11: A([B12.10]) (CXXFunctionalCastExpr, ConstructorConversion, class A)
 // CHECK:    12: [B12.11] (ImplicitCastExpr, NoOp, const class A)
@@ -935,7 +935,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B4
 // CHECK:   [B6]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A)
 // CHECK:     2: [B6.1] (BindTemporary)
 // CHECK:     3: [B6.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B6.3]
@@ -1001,7 +1001,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B4
 // CHECK:   [B6]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B6.2], [B6.4], [B6.5], class A)
 // CHECK:     2: [B6.1] (BindTemporary)
 // CHECK:     3: [B6.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B6.3]
@@ -1086,7 +1086,7 @@ const C &bar3(bool coin) {
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
 // WARNINGS:     1: A() (CXXConstructExpr, class A)
-// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// ANALYZER:     1: A() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class A)
 // CHECK:     2: [B1.1] (BindTemporary)
 // CHECK:     3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     4: [B1.3]
@@ -1130,7 +1130,7 @@ const C &bar3(bool coin) {
 // CHECK:     1: A::make
 // CHECK:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
 // WARNINGS:     3: [B1.2]()
-// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// ANALYZER:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6], [B1.7])
 // CHECK:     4: [B1.3] (BindTemporary)
 // CHECK:     5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     6: [B1.5]