[clang] [LifetimeSafety] Overhaul CFG and analysis to also work with trivially destructed temporary objects (PR #177985)
Abhinav Pradeep via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 06:08:36 PST 2026
https://github.com/AbhinavPradeep updated https://github.com/llvm/llvm-project/pull/177985
>From 8bae199a384392630a2189467c5be254b9abc05b Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Tue, 27 Jan 2026 01:20:55 +1000
Subject: [PATCH 1/3] Modify CFG to have a CFGFullExprCleanup marker.
---
clang/include/clang/Analysis/CFG.h | 32 ++++++++++++
clang/lib/Analysis/CFG.cpp | 71 ++++++++++++++++++++++++---
clang/lib/Analysis/PathDiagnostic.cpp | 1 +
3 files changed, 98 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h
index a4bafd4927df0..c021ffcf85785 100644
--- a/clang/include/clang/Analysis/CFG.h
+++ b/clang/include/clang/Analysis/CFG.h
@@ -62,6 +62,7 @@ class CFGElement {
NewAllocator,
LifetimeEnds,
LoopExit,
+ FullExprCleanup,
// stmt kind
Statement,
Constructor,
@@ -313,6 +314,32 @@ class CFGLifetimeEnds : public CFGElement {
}
};
+class CFGFullExprCleanup : public CFGElement {
+ using MTEVecTy = BumpVector<const MaterializeTemporaryExpr *>;
+
+public:
+ explicit CFGFullExprCleanup(const MTEVecTy *vec)
+ : CFGElement(FullExprCleanup, vec, nullptr) {}
+
+ ArrayRef<const MaterializeTemporaryExpr *> getExpiringMTEs() const {
+ const MTEVecTy *ExpiringMTEs =
+ static_cast<const MTEVecTy *>(Data1.getPointer());
+ if (!ExpiringMTEs)
+ return {};
+ return ArrayRef<const MaterializeTemporaryExpr *>(ExpiringMTEs->begin(),
+ ExpiringMTEs->end());
+ }
+
+private:
+ friend class CFGElement;
+
+ CFGFullExprCleanup() = default;
+
+ static bool isKind(const CFGElement &elem) {
+ return elem.getKind() == FullExprCleanup;
+ }
+};
+
/// Represents beginning of a scope implicitly generated
/// by the compiler on encountering a CompoundStmt
class CFGScopeBegin : public CFGElement {
@@ -1183,6 +1210,11 @@ class CFGBlock {
Elements.push_back(CFGLifetimeEnds(VD, S), C);
}
+ void appendFullExprCleanup(BumpVector<const MaterializeTemporaryExpr *> *BV,
+ BumpVectorContext &C) {
+ Elements.push_back(CFGFullExprCleanup(BV), C);
+ }
+
void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) {
Elements.push_back(CFGLoopExit(LoopStmt), C);
}
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index a9c7baa00543c..03b3fd7efe89e 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -55,6 +55,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <cstddef>
#include <memory>
#include <optional>
#include <string>
@@ -698,7 +699,9 @@ class CFGBuilder {
TempDtorContext() = default;
TempDtorContext(TryResult KnownExecuted)
: IsConditional(true), KnownExecuted(KnownExecuted) {}
-
+ TempDtorContext(TryResult KnownExecuted, bool TrackExpiringMTEs)
+ : IsConditional(true), TrackExpiringMTEs(TrackExpiringMTEs),
+ KnownExecuted(KnownExecuted) {}
/// Returns whether we need to start a new branch for a temporary destructor
/// call. This is the case when the temporary destructor is
/// conditionally executed, and it is the first one we encounter while
@@ -716,7 +719,16 @@ class CFGBuilder {
TerminatorExpr = E;
}
+ void track(const MaterializeTemporaryExpr *MTE) {
+ // Must only be invoked when TrackMTE is true
+ assert(TrackExpiringMTEs);
+ if (MTE)
+ CollectedMTEs.push_back(MTE);
+ }
+
const bool IsConditional = false;
+ bool TrackExpiringMTEs = false;
+ SmallVector<const MaterializeTemporaryExpr *, 5> CollectedMTEs;
const TryResult KnownExecuted = true;
CFGBlock *Succ = nullptr;
CXXBindTemporaryExpr *TerminatorExpr = nullptr;
@@ -803,6 +815,7 @@ class CFGBuilder {
void addScopeChangesHandling(LocalScope::const_iterator SrcPos,
LocalScope::const_iterator DstPos,
Stmt *S);
+ void addFullExprCleanupMarker(TempDtorContext &Context);
CFGBlock *createScopeChangesHandlingBlock(LocalScope::const_iterator SrcPos,
CFGBlock *SrcBlk,
LocalScope::const_iterator DstPost,
@@ -1816,8 +1829,11 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
/*ExternallyDestructed=*/false, Context);
+
+ addFullExprCleanupMarker(Context);
}
}
@@ -2065,6 +2081,23 @@ void CFGBuilder::addScopeChangesHandling(LocalScope::const_iterator SrcPos,
addAutomaticObjHandling(SrcPos, BasePos, S);
}
+void CFGBuilder::addFullExprCleanupMarker(TempDtorContext &Context) {
+ assert(Context.TrackExpiringMTEs);
+
+ using MTEVecTy = BumpVector<const MaterializeTemporaryExpr *>;
+ MTEVecTy *ExpiringMTEs = nullptr;
+ BumpVectorContext &BVC = cfg->getBumpVectorContext();
+
+ size_t NumCollected = Context.CollectedMTEs.size();
+ if (NumCollected > 0) {
+ autoCreateBlock();
+ ExpiringMTEs = new (cfg->getAllocator()) MTEVecTy(BVC, NumCollected);
+ for (const MaterializeTemporaryExpr *MTE : Context.CollectedMTEs)
+ ExpiringMTEs->push_back(MTE, BVC);
+ Block->appendFullExprCleanup(ExpiringMTEs, BVC);
+ }
+}
+
/// createScopeChangesHandlingBlock - Creates a block with cfgElements
/// corresponding to changing the scope from the source scope of the GotoStmt,
/// to destination scope. Add destructor, lifetime and cfgScopeEnd
@@ -3113,8 +3146,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
/*ExternallyDestructed=*/true, Context);
+
+ addFullExprCleanupMarker(Context);
}
}
@@ -4924,13 +4960,17 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
}
CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
- AddStmtChoice asc, bool ExternallyDestructed) {
+ AddStmtChoice asc,
+ bool ExternallyDestructed) {
if (BuildOpts.AddTemporaryDtors) {
// If adding implicit destructors visit the full expression for adding
// destructors of temporaries.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(E->getSubExpr(), ExternallyDestructed, Context);
+ addFullExprCleanupMarker(Context);
+
// Full expression has to be added as CFGStmt so it will be sequenced
// before destructors of it's temporaries.
asc = asc.withAlwaysAdd(true);
@@ -5117,6 +5157,8 @@ CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
case Stmt::MaterializeTemporaryExprClass: {
const MaterializeTemporaryExpr* MTE = cast<MaterializeTemporaryExpr>(E);
ExternallyDestructed = (MTE->getStorageDuration() != SD_FullExpression);
+ if (Context.TrackExpiringMTEs && !ExternallyDestructed)
+ Context.track(MTE);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
// Find the expression whose lifetime needs to be extended.
@@ -5208,10 +5250,14 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
// executed, thus we add a branch node that depends on the temporary
// constructor call.
TempDtorContext RHSContext(
- bothKnownTrue(Context.KnownExecuted, RHSExecuted));
+ bothKnownTrue(Context.KnownExecuted, RHSExecuted),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getRHS(), false, RHSContext);
InsertTempDtorDecisionBlock(RHSContext);
+ if (Context.TrackExpiringMTEs)
+ Context.CollectedMTEs.append(RHSContext.CollectedMTEs);
+
return Block;
}
@@ -5290,14 +5336,15 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
if (NegatedVal.isKnown()) NegatedVal.negate();
TempDtorContext TrueContext(
- bothKnownTrue(Context.KnownExecuted, ConditionVal));
+ bothKnownTrue(Context.KnownExecuted, ConditionVal),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getTrueExpr(), ExternallyDestructed, TrueContext);
CFGBlock *TrueBlock = Block;
Block = ConditionBlock;
Succ = ConditionSucc;
- TempDtorContext FalseContext(
- bothKnownTrue(Context.KnownExecuted, NegatedVal));
+ TempDtorContext FalseContext(bothKnownTrue(Context.KnownExecuted, NegatedVal),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getFalseExpr(), ExternallyDestructed, FalseContext);
if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
@@ -5308,6 +5355,11 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
} else {
InsertTempDtorDecisionBlock(FalseContext);
}
+ if (Context.TrackExpiringMTEs) {
+ Context.CollectedMTEs.append(TrueContext.CollectedMTEs);
+ Context.CollectedMTEs.append(FalseContext.CollectedMTEs);
+ }
+
return Block;
}
@@ -5989,6 +6041,13 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
OS << " (Lifetime ends)";
break;
+ case CFGElement::Kind::FullExprCleanup:
+ OS << "(FullExprCleanup collected "
+ << std::to_string(
+ E.castAs<CFGFullExprCleanup>().getExpiringMTEs().size())
+ << " MTEs)";
+ break;
+
case CFGElement::Kind::LoopExit:
OS << E.castAs<CFGLoopExit>().getLoopStmt()->getStmtClassName()
<< " (LoopExit)";
diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp
index e42731b93bfb2..5e387e086a5a7 100644
--- a/clang/lib/Analysis/PathDiagnostic.cpp
+++ b/clang/lib/Analysis/PathDiagnostic.cpp
@@ -564,6 +564,7 @@ getLocationForCaller(const StackFrameContext *SFC,
case CFGElement::CleanupFunction:
llvm_unreachable("not yet implemented!");
case CFGElement::LifetimeEnds:
+ case CFGElement::FullExprCleanup:
case CFGElement::LoopExit:
llvm_unreachable("CFGElement kind should not be on callsite!");
}
>From 5cc1973c118ea08159b197836c79af5551354b94 Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Wed, 28 Jan 2026 03:12:45 +1000
Subject: [PATCH 2/3] Fixed solver related issues by no-oping new CFGElement
---
clang/lib/Analysis/CFG.cpp | 1 +
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 6 ++++++
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 +
clang/lib/StaticAnalyzer/Core/SymbolManager.cpp | 2 ++
4 files changed, 10 insertions(+)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 03b3fd7efe89e..ca47c644228f4 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -5477,6 +5477,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
case CFGElement::CXXRecordTypedCall:
case CFGElement::ScopeBegin:
case CFGElement::ScopeEnd:
+ case CFGElement::FullExprCleanup:
case CFGElement::CleanupFunction:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 95a843ee87571..49835a0224171 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -524,6 +524,12 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
assert(B);
assert(!B->empty());
+ // We no-op by skipping any FullExprCleanup
+ while (StmtIdx < B->size() &&
+ (*B)[StmtIdx].getKind() == CFGElement::FullExprCleanup) {
+ StmtIdx++;
+ }
+
if (StmtIdx == B->size())
HandleBlockExit(B, Pred);
else {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a6a96b594fe85..da176ebd20bb4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -995,6 +995,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
return;
case CFGElement::LifetimeEnds:
case CFGElement::CleanupFunction:
+ case CFGElement::FullExprCleanup:
case CFGElement::ScopeBegin:
case CFGElement::ScopeEnd:
return;
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index d03f47fa301e6..35fbe4d60c3f2 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -115,6 +115,8 @@ const Stmt *SymbolConjured::getStmt() const {
return Elem->castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
case CFGElement::CleanupFunction:
return nullptr;
+ case CFGElement::FullExprCleanup:
+ return nullptr;
}
return nullptr;
}
>From 9782c9c0bd6d28596118e3fca7f2f8597ce93777 Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Tue, 3 Feb 2026 00:07:14 +1000
Subject: [PATCH 3/3] Fixed tests to reflect new CFG
---
.../Analysis/auto-obj-dtors-cfg-output.cpp | 172 +++++++++---------
clang/test/Analysis/cfg-rich-constructors.cpp | 155 +++++++++-------
clang/test/Analysis/cfg-rich-constructors.mm | 8 +-
clang/test/Analysis/cfg.cpp | 1 +
.../test/Analysis/missing-bind-temporary.cpp | 15 +-
.../Analysis/temp-obj-dtors-cfg-output.cpp | 166 ++++++++++-------
6 files changed, 293 insertions(+), 224 deletions(-)
diff --git a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
index 96b9a5508cc08..4790469850107 100644
--- a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -188,50 +188,52 @@ void test_aggregate_lifetime_extension() {
// CXX11-NEXT: 14: {[B1.13]}
// Double curly braces trigger regexps, escape as per FileCheck manual.
// CXX11-NEXT: 15: C c = {{[{][{]}}A(), A(){{[}][}]}};
-// CXX11-NEXT: 16: ~A() (Temporary object destructor)
+// CXX11-NEXT: 16: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: 17: ~A() (Temporary object destructor)
-// CXX11-WARNINGS-NEXT: 18: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 18: A() (CXXConstructExpr, [B1.19], [B1.21], A)
-// CXX11-NEXT: 19: [B1.18] (BindTemporary)
-// CXX11-NEXT: 20: [B1.19] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 21: [B1.20]
-// CXX11-NEXT: 22: [B1.21] (CXXConstructExpr, const A)
-// CXX11-WARNINGS-NEXT: 23: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 23: A() (CXXConstructExpr, [B1.24], [B1.26], A)
-// CXX11-NEXT: 24: [B1.23] (BindTemporary)
-// CXX11-NEXT: 25: [B1.24] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 26: [B1.25]
-// CXX11-NEXT: 27: [B1.26] (CXXConstructExpr, const A)
+// CXX11-NEXT: 18: ~A() (Temporary object destructor)
+// CXX11-WARNINGS-NEXT: 19: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 19: A() (CXXConstructExpr, [B1.20], [B1.22], A)
+// CXX11-NEXT: 20: [B1.19] (BindTemporary)
+// CXX11-NEXT: 21: [B1.20] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 22: [B1.21]
+// CXX11-NEXT: 23: [B1.22] (CXXConstructExpr, const A)
+// CXX11-WARNINGS-NEXT: 24: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 24: A() (CXXConstructExpr, [B1.25], [B1.27], A)
+// CXX11-NEXT: 25: [B1.24] (BindTemporary)
+// CXX11-NEXT: 26: [B1.25] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 27: [B1.26]
+// CXX11-NEXT: 28: [B1.27] (CXXConstructExpr, const A)
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CXX11-NEXT: 28: {[B1.19], [B1.24]}
-// CXX11-NEXT: 29: [B1.28] (BindTemporary)
-// CXX11-NEXT: 30: [B1.29]
-// CXX11-NEXT: 31: {[B1.30]}
-// CXX11-WARNINGS-NEXT: 32: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 32: A() (CXXConstructExpr, [B1.33], [B1.35], A)
-// CXX11-NEXT: 33: [B1.32] (BindTemporary)
-// CXX11-NEXT: 34: [B1.33] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 35: [B1.34]
-// CXX11-NEXT: 36: [B1.35] (CXXConstructExpr, const A)
-// CXX11-WARNINGS-NEXT: 37: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 37: A() (CXXConstructExpr, [B1.38], [B1.40], A)
-// CXX11-NEXT: 38: [B1.37] (BindTemporary)
-// CXX11-NEXT: 39: [B1.38] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 40: [B1.39]
-// CXX11-NEXT: 41: [B1.40] (CXXConstructExpr, const A)
+// CXX11-NEXT: 29: {[B1.20], [B1.25]}
+// CXX11-NEXT: 30: [B1.29] (BindTemporary)
+// CXX11-NEXT: 31: [B1.30]
+// CXX11-NEXT: 32: {[B1.31]}
+// CXX11-WARNINGS-NEXT: 33: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 33: A() (CXXConstructExpr, [B1.34], [B1.36], A)
+// CXX11-NEXT: 34: [B1.33] (BindTemporary)
+// CXX11-NEXT: 35: [B1.34] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 36: [B1.35]
+// CXX11-NEXT: 37: [B1.36] (CXXConstructExpr, const A)
+// CXX11-WARNINGS-NEXT: 38: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 38: A() (CXXConstructExpr, [B1.39], [B1.41], A)
+// CXX11-NEXT: 39: [B1.38] (BindTemporary)
+// CXX11-NEXT: 40: [B1.39] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 41: [B1.40]
+// CXX11-NEXT: 42: [B1.41] (CXXConstructExpr, const A)
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CXX11-NEXT: 42: {[B1.33], [B1.38]}
-// CXX11-NEXT: 43: [B1.42] (BindTemporary)
-// CXX11-NEXT: 44: [B1.43]
-// CXX11-NEXT: 45: {[B1.44]}
+// CXX11-NEXT: 43: {[B1.34], [B1.39]}
+// CXX11-NEXT: 44: [B1.43] (BindTemporary)
+// CXX11-NEXT: 45: [B1.44]
+// CXX11-NEXT: 46: {[B1.45]}
// Double curly braces trigger regexps, escape as per FileCheck manual.
-// CXX11-NEXT: 46: {{[{][{]}}[B1.30]}, {[B1.44]{{[}][}]}}
+// CXX11-NEXT: 47: {{[{][{]}}[B1.31]}, {[B1.45]{{[}][}]}}
// Double curly braces trigger regexps, escape as per FileCheck manual.
-// CXX11-NEXT: 47: C cc[2] = {{[{][{][{]}}A(), A(){{[}][}]}}, {{[{][{]}}A(), A(){{[}][}][}]}};
-// CXX11-NEXT: 48: ~A() (Temporary object destructor)
-// CXX11-NEXT: 49: ~A() (Temporary object destructor)
+// CXX11-NEXT: 48: C cc[2] = {{[{][{][{]}}A(), A(){{[}][}]}}, {{[{][{]}}A(), A(){{[}][}][}]}};
+// CXX11-NEXT: 49: (FullExprCleanup collected 4 MTEs)
// CXX11-NEXT: 50: ~A() (Temporary object destructor)
// CXX11-NEXT: 51: ~A() (Temporary object destructor)
+// CXX11-NEXT: 52: ~A() (Temporary object destructor)
+// CXX11-NEXT: 53: ~A() (Temporary object destructor)
// CXX11-NEXT: Preds (1): B2
// CXX11-NEXT: Succs (1): B0
// CXX11: [B0 (EXIT)]
@@ -277,65 +279,67 @@ void test_aggregate_array_lifetime_extension() {
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
// CHECK-NEXT: 17: {[B1.2], {[B1.7], [B1.12]}}
// CHECK-NEXT: 18: D d = {A(), {A(), A()}};
-// CHECK-NEXT: 19: ~A() (Temporary object destructor)
+// CHECK-NEXT: 19: (FullExprCleanup collected 3 MTEs)
// CHECK-NEXT: 20: ~A() (Temporary object destructor)
// CHECK-NEXT: 21: ~A() (Temporary object destructor)
-// WARNINGS-NEXT: 22: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 22: A() (CXXConstructExpr, [B1.23], [B1.25], A)
-// CHECK-NEXT: 23: [B1.22] (BindTemporary)
-// CHECK-NEXT: 24: [B1.23] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 25: [B1.24]
-// CHECK-NEXT: 26: [B1.25] (CXXConstructExpr, A)
-// WARNINGS-NEXT: 27: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 27: A() (CXXConstructExpr, [B1.28], [B1.30], A)
-// CHECK-NEXT: 28: [B1.27] (BindTemporary)
-// CHECK-NEXT: 29: [B1.28] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 30: [B1.29]
-// CHECK-NEXT: 31: [B1.30] (CXXConstructExpr, A)
-// WARNINGS-NEXT: 32: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 32: A() (CXXConstructExpr, [B1.33], [B1.35], A)
-// CHECK-NEXT: 33: [B1.32] (BindTemporary)
-// CHECK-NEXT: 34: [B1.33] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 35: [B1.34]
-// CHECK-NEXT: 36: [B1.35] (CXXConstructExpr, A)
+// CHECK-NEXT: 22: ~A() (Temporary object destructor)
+// WARNINGS-NEXT: 23: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 23: A() (CXXConstructExpr, [B1.24], [B1.26], A)
+// CHECK-NEXT: 24: [B1.23] (BindTemporary)
+// CHECK-NEXT: 25: [B1.24] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 26: [B1.25]
+// CHECK-NEXT: 27: [B1.26] (CXXConstructExpr, A)
+// WARNINGS-NEXT: 28: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 28: A() (CXXConstructExpr, [B1.29], [B1.31], A)
+// CHECK-NEXT: 29: [B1.28] (BindTemporary)
+// CHECK-NEXT: 30: [B1.29] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 31: [B1.30]
+// CHECK-NEXT: 32: [B1.31] (CXXConstructExpr, A)
+// WARNINGS-NEXT: 33: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 33: A() (CXXConstructExpr, [B1.34], [B1.36], A)
+// CHECK-NEXT: 34: [B1.33] (BindTemporary)
+// CHECK-NEXT: 35: [B1.34] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 36: [B1.35]
+// CHECK-NEXT: 37: [B1.36] (CXXConstructExpr, A)
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CHECK-NEXT: 37: {[B1.28], [B1.33]}
+// CHECK-NEXT: 38: {[B1.29], [B1.34]}
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CHECK-NEXT: 38: {[B1.23], {[B1.28], [B1.33]}}
-// WARNINGS-NEXT: 39: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 39: A() (CXXConstructExpr, [B1.40], [B1.42], A)
-// CHECK-NEXT: 40: [B1.39] (BindTemporary)
-// CHECK-NEXT: 41: [B1.40] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 42: [B1.41]
-// CHECK-NEXT: 43: [B1.42] (CXXConstructExpr, A)
-// WARNINGS-NEXT: 44: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 44: A() (CXXConstructExpr, [B1.45], [B1.47], A)
-// CHECK-NEXT: 45: [B1.44] (BindTemporary)
-// CHECK-NEXT: 46: [B1.45] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 47: [B1.46]
-// CHECK-NEXT: 48: [B1.47] (CXXConstructExpr, A)
-// WARNINGS-NEXT: 49: A() (CXXConstructExpr, A)
-// ANALYZER-NEXT: 49: A() (CXXConstructExpr, [B1.50], [B1.52], A)
-// CHECK-NEXT: 50: [B1.49] (BindTemporary)
-// CHECK-NEXT: 51: [B1.50] (ImplicitCastExpr, NoOp, const A)
-// CHECK-NEXT: 52: [B1.51]
-// CHECK-NEXT: 53: [B1.52] (CXXConstructExpr, A)
+// CHECK-NEXT: 39: {[B1.24], {[B1.29], [B1.34]}}
+// WARNINGS-NEXT: 40: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 40: A() (CXXConstructExpr, [B1.41], [B1.43], A)
+// CHECK-NEXT: 41: [B1.40] (BindTemporary)
+// CHECK-NEXT: 42: [B1.41] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 43: [B1.42]
+// CHECK-NEXT: 44: [B1.43] (CXXConstructExpr, A)
+// WARNINGS-NEXT: 45: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 45: A() (CXXConstructExpr, [B1.46], [B1.48], A)
+// CHECK-NEXT: 46: [B1.45] (BindTemporary)
+// CHECK-NEXT: 47: [B1.46] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 48: [B1.47]
+// CHECK-NEXT: 49: [B1.48] (CXXConstructExpr, A)
+// WARNINGS-NEXT: 50: A() (CXXConstructExpr, A)
+// ANALYZER-NEXT: 50: A() (CXXConstructExpr, [B1.51], [B1.53], A)
+// CHECK-NEXT: 51: [B1.50] (BindTemporary)
+// CHECK-NEXT: 52: [B1.51] (ImplicitCastExpr, NoOp, const A)
+// CHECK-NEXT: 53: [B1.52]
+// CHECK-NEXT: 54: [B1.53] (CXXConstructExpr, A)
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CHECK-NEXT: 54: {[B1.45], [B1.50]}
+// CHECK-NEXT: 55: {[B1.46], [B1.51]}
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CHECK-NEXT: 55: {[B1.40], {[B1.45], [B1.50]}}
+// CHECK-NEXT: 56: {[B1.41], {[B1.46], [B1.51]}}
// Double curly braces trigger regexps, escape as per FileCheck manual.
-// CHECK-NEXT: 56: {{[{][{]}}[B1.23], {[B1.28], [B1.33]{{[}][}]}}, {[B1.40], {[B1.45], [B1.50]{{[}][}][}]}}
+// CHECK-NEXT: 57: {{[{][{]}}[B1.24], {[B1.29], [B1.34]{{[}][}]}}, {[B1.41], {[B1.46], [B1.51]{{[}][}][}]}}
// Double curly braces trigger regexps, escape as per FileCheck manual.
-// CHECK-NEXT: 57: D dd[2] = {{[{][{]}}A(), {A(), A(){{[}][}]}}, {A(), {A(), A(){{[}][}][}]}};
-// CHECK-NEXT: 58: ~A() (Temporary object destructor)
-// CHECK-NEXT: 59: ~A() (Temporary object destructor)
+// CHECK-NEXT: 58: D dd[2] = {{[{][{]}}A(), {A(), A(){{[}][}]}}, {A(), {A(), A(){{[}][}][}]}};
+// CHECK-NEXT: 59: (FullExprCleanup collected 6 MTEs)
// CHECK-NEXT: 60: ~A() (Temporary object destructor)
// CHECK-NEXT: 61: ~A() (Temporary object destructor)
// CHECK-NEXT: 62: ~A() (Temporary object destructor)
// CHECK-NEXT: 63: ~A() (Temporary object destructor)
-// CHECK-NEXT: 64: [B1.57].~D[2]() (Implicit destructor)
-// CHECK-NEXT: 65: [B1.18].~D() (Implicit destructor)
+// CHECK-NEXT: 64: ~A() (Temporary object destructor)
+// CHECK-NEXT: 65: ~A() (Temporary object destructor)
+// CHECK-NEXT: 66: [B1.58].~D[2]() (Implicit destructor)
+// CHECK-NEXT: 67: [B1.18].~D() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
// CHECK: [B0 (EXIT)]
diff --git a/clang/test/Analysis/cfg-rich-constructors.cpp b/clang/test/Analysis/cfg-rich-constructors.cpp
index aea983f1f74da..c91eec9fda871 100644
--- a/clang/test/Analysis/cfg-rich-constructors.cpp
+++ b/clang/test/Analysis/cfg-rich-constructors.cpp
@@ -253,20 +253,22 @@ class D: public C {
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, C([B1.4]) (Base initializer), 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, C (*)(void))
-// 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], C)
-// CXX11-NEXT: 13: new C([B1.12])
-// CXX11-NEXT: 14: [B1.13] (CXXConstructExpr, c1([B1.13]) (Member initializer), C)
-// CXX11-NEXT: 15: c1([B1.14]) (Member initializer)
-// CXX17-NEXT: 10: [B1.9]()
-// CXX17-NEXT: 11: new C([B1.10])
-// CXX17-NEXT: 12: [B1.11] (CXXConstructExpr, c1([B1.11]) (Member initializer), C)
-// CXX17-NEXT: 13: c1([B1.12]) (Member initializer)
+// CHECK-NEXT: 7: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 8: CFGNewAllocator(C *)
+// CHECK-NEXT: 9: C::get
+// CHECK-NEXT: 10: [B1.9] (ImplicitCastExpr, FunctionToPointerDecay, C (*)(void))
+// CXX11-ELIDE-NEXT: 11: [B1.10]() (CXXRecordTypedCall, [B1.12], [B1.13])
+// CXX11-NOELIDE-NEXT: 11: [B1.10]() (CXXRecordTypedCall, [B1.12])
+// CXX11-NEXT: 12: [B1.11]
+// CXX11-NEXT: 13: [B1.12] (CXXConstructExpr, [B1.14], C)
+// CXX11-NEXT: 14: new C([B1.13])
+// CXX11-NEXT: 15: [B1.14] (CXXConstructExpr, c1([B1.14]) (Member initializer), C)
+// CXX11-NEXT: 16: c1([B1.15]) (Member initializer)
+// CXX11-NEXT: 17: (FullExprCleanup collected 1 MTEs)
+// CXX17-NEXT: 11: [B1.10]()
+// CXX17-NEXT: 12: new C([B1.11])
+// CXX17-NEXT: 13: [B1.12] (CXXConstructExpr, c1([B1.12]) (Member initializer), C)
+// CXX17-NEXT: 14: c1([B1.13]) (Member initializer)
D(double): C(C::get()), c1(new C(C::get())) {}
};
@@ -292,7 +294,8 @@ class F {
// CXX11-NEXT: 6: [B1.5]
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, e([B1.6]) (Member initializer), E)
// CXX11-NEXT: 8: e([B1.7]) (Member initializer)
-// CXX11-NEXT: 9: ~E() (Temporary object destructor)
+// CXX11-NEXT: 9: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 10: ~E() (Temporary object destructor)
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, e([B1.4]) (Member initializer), [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: e([B1.4]) (Member initializer)
@@ -345,8 +348,9 @@ C returnBracesWithMultipleItems() {
// CXX11-ELIDE: 1: C() (CXXConstructExpr, [B1.2], [B1.3], C)
// CXX11-NOELIDE: 1: C() (CXXConstructExpr, [B1.2], C)
// CXX11-NEXT: 2: [B1.1]
-// CXX11-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.4], C)
-// CXX11-NEXT: 4: return [B1.3];
+// CXX11-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], C)
+// CXX11-NEXT: 4: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 5: return [B1.3];
// CXX17: 1: C() (CXXConstructExpr, [B1.2], C)
// CXX17-NEXT: 2: return [B1.1];
C returnTemporary() {
@@ -361,8 +365,9 @@ C returnTemporary() {
// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], C)
// CHECK-NEXT: 4: C([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, C)
// CXX11-NEXT: 5: [B1.4]
-// CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.7], C)
-// CXX11-NEXT: 7: return [B1.6];
+// CXX11-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], C)
+// CXX11-NEXT: 7: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 8: return [B1.6];
// CXX17-NEXT: 5: return [B1.4];
C returnTemporaryWithArgument() {
@@ -375,8 +380,9 @@ C returnTemporaryWithArgument() {
// 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], C)
-// CXX11-NEXT: 6: return [B1.5];
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], C)
+// CXX11-NEXT: 6: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 7: return [B1.5];
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4])
// CXX17-NEXT: 4: return [B1.3];
C returnTemporaryConstructedByFunction() {
@@ -393,8 +399,9 @@ C returnTemporaryConstructedByFunction() {
// CXX11-NOELIDE-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], C)
// CXX11-NEXT: 6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, C)
// CXX11-NEXT: 7: [B1.6]
-// CXX11-NEXT: 8: [B1.7] (CXXConstructExpr, [B1.9], C)
-// CXX11-NEXT: 9: return [B1.8];
+// CXX11-NEXT: 8: [B1.7] (CXXConstructExpr, [B1.10], C)
+// CXX11-NEXT: 9: (FullExprCleanup collected 2 MTEs)
+// CXX11-NEXT: 10: return [B1.8];
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5])
// CXX17-NEXT: 4: C([B1.3]) (CXXFunctionalCastExpr, NoOp, C)
// CXX17-NEXT: 5: return [B1.4];
@@ -418,9 +425,10 @@ class D {
// CXX11-NEXT: 2: [B1.1] (BindTemporary)
// CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const D)
// CXX11-NEXT: 4: [B1.3]
-// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], D)
-// CXX11-NEXT: 6: ~D() (Temporary object destructor)
-// CXX11-NEXT: 7: return [B1.5];
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.8], D)
+// CXX11-NEXT: 6: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 7: ~D() (Temporary object destructor)
+// CXX11-NEXT: 8: return [B1.5];
// CXX17: 1: D() (CXXConstructExpr, [B1.3], [B1.2], D)
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
// CXX17-NEXT: 3: return [B1.2];
@@ -438,8 +446,9 @@ D returnTemporary() {
// CXX11-NEXT: 6: [B1.5]
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], D)
// CXX11-NEXT: 8: D d = returnTemporary();
-// CXX11-NEXT: 9: ~D() (Temporary object destructor)
-// CXX11-NEXT: 10: [B1.8].~D() (Implicit destructor)
+// CXX11-NEXT: 9: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 10: ~D() (Temporary object destructor)
+// CXX11-NEXT: 11: [B1.8].~D() (Implicit destructor)
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: D d = returnTemporary();
@@ -466,6 +475,7 @@ void simpleTemporary() {
// CHECK-NEXT: 4: [B2.3].operator bool
// CHECK-NEXT: 5: [B2.3]
// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK-NEXT: 7: (FullExprCleanup collected 1 MTEs)
// CHECK-NEXT: T: if [B2.6]
void temporaryInCondition() {
if (C());
@@ -477,12 +487,13 @@ void temporaryInCondition() {
// CXX11-NEXT: 2: [B2.1]
// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], C)
// CXX11-NEXT: 4: C c = C();
-// CXX11-NEXT: 5: c
-// CXX11-NEXT: 6: [B2.5] (ImplicitCastExpr, NoOp, const class C)
-// CXX11-NEXT: 7: [B2.6].operator bool
-// CXX11-NEXT: 8: [B2.6]
-// CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CXX11-NEXT: T: if [B2.9]
+// CXX11-NEXT: 5: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 6: c
+// CXX11-NEXT: 7: [B2.6] (ImplicitCastExpr, NoOp, const class C)
+// CXX11-NEXT: 8: [B2.7].operator bool
+// CXX11-NEXT: 9: [B2.7]
+// CXX11-NEXT: 10: [B2.9] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX11-NEXT: T: if [B2.10]
// CXX17: 1: C() (CXXConstructExpr, [B2.2], C)
// CXX17-NEXT: 2: C c = C();
// CXX17-NEXT: 3: c
@@ -501,14 +512,15 @@ void temporaryInConditionVariable() {
// CXX11-ELIDE-NEXT: 1: C() (CXXConstructExpr, [B2.2], [B2.3], C)
// CXX11-NOELIDE-NEXT: 1: C() (CXXConstructExpr, [B2.2], C)
// CXX11-NEXT: 2: [B2.1]
-// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], C)
-// CXX11-NEXT: 4: C c2 = C();
-// CXX11-NEXT: 5: c2
-// CXX11-NEXT: 6: [B2.5] (ImplicitCastExpr, NoOp, const class C)
-// CXX11-NEXT: 7: [B2.6].operator bool
-// CXX11-NEXT: 8: [B2.6]
-// CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CXX11-NEXT: T: for (...; [B2.9]; )
+// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.5], C)
+// CXX11-NEXT: 4: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 5: C c2 = C();
+// CXX11-NEXT: 6: c2
+// CXX11-NEXT: 7: [B2.6] (ImplicitCastExpr, NoOp, const class C)
+// CXX11-NEXT: 8: [B2.7].operator bool
+// CXX11-NEXT: 9: [B2.7]
+// CXX11-NEXT: 10: [B2.9] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX11-NEXT: T: for (...; [B2.10]; )
// CXX17-NEXT: 1: C() (CXXConstructExpr, [B2.2], C)
// CXX17-NEXT: 2: C c2 = C();
// CXX17-NEXT: 3: c2
@@ -534,14 +546,15 @@ void temporaryInForLoopConditionVariable() {
// CXX11-ELIDE: 1: C() (CXXConstructExpr, [B2.2], [B2.3], C)
// CXX11-NOELIDE: 1: C() (CXXConstructExpr, [B2.2], C)
// CXX11-NEXT: 2: [B2.1]
-// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], C)
-// CXX11-NEXT: 4: C c = C();
-// CXX11-NEXT: 5: c
-// CXX11-NEXT: 6: [B2.5] (ImplicitCastExpr, NoOp, const class C)
-// CXX11-NEXT: 7: [B2.6].operator bool
-// CXX11-NEXT: 8: [B2.6]
-// CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CXX11-NEXT: T: while [B2.9]
+// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.5], C)
+// CXX11-NEXT: 4: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 5: C c = C();
+// CXX11-NEXT: 6: c
+// CXX11-NEXT: 7: [B2.6] (ImplicitCastExpr, NoOp, const class C)
+// CXX11-NEXT: 8: [B2.7].operator bool
+// CXX11-NEXT: 9: [B2.7
+// CXX11-NEXT: 10: [B2.9] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX11-NEXT: T: while [B2.10]
// CXX17: 1: C() (CXXConstructExpr, [B2.2], C)
// CXX17-NEXT: 2: C c = C();
// CXX17-NEXT: 3: c
@@ -585,7 +598,8 @@ void simpleTemporary() {
// CHECK-NEXT: 5: [B2.4].operator bool
// CHECK-NEXT: 6: [B2.4]
// CHECK-NEXT: 7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK-NEXT: 8: ~D() (Temporary object destructor)
+// CHECK-NEXT: 8: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 9: ~D() (Temporary object destructor)
// CHECK-NEXT: T: if [B2.7]
void temporaryInCondition() {
if (D());
@@ -625,6 +639,7 @@ void referenceVariableWithInitializer() {
// CXX11-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const D)
// CXX11-NEXT: 3: [B4.2]
// CXX11-NEXT: 4: const D &d = coin ? D::get() : D(0);
+// CXX11-NEXT: 5: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: T: (Temp Dtor) [B6.3]
// CXX11: [B5]
// CXX11-NEXT: 1: D::get
@@ -737,14 +752,16 @@ class B {
// CXX11-NEXT: 8: [B1.7]
// CXX11-NEXT: 9: [B1.8] (CXXConstructExpr, [B1.10], B)
// CXX11-NEXT: 10: B b = A();
-// CXX11-NEXT: 11: ~B() (Temporary object destructor)
-// CXX11-NEXT: 12: [B1.10].~B() (Implicit destructor)
+// CXX11-NEXT: 11: (FullExprCleanup collected 2 MTEs)
+// CXX11-NEXT: 12: ~B() (Temporary object destructor)
+// CXX11-NEXT: 13: [B1.10].~B() (Implicit destructor)
// CXX17-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const A)
// CXX17-NEXT: 3: [B1.2]
// CXX17-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], B)
// CXX17-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, B)
// CXX17-NEXT: 6: B b = A();
-// CXX17-NEXT: 7: [B1.6].~B() (Implicit destructor)
+// CXX17-NEXT: 7: (FullExprCleanup collected 1 MTEs)
+// CXX17-NEXT: 8: [B1.6].~B() (Implicit destructor)
void implicitConstructionConversionFromTemporary() {
B b = A();
}
@@ -763,12 +780,14 @@ void implicitConstructionConversionFromTemporary() {
// CXX11-NEXT: 10: [B1.9]
// CXX11-NEXT: 11: [B1.10] (CXXConstructExpr, [B1.12], B)
// CXX11-NEXT: 12: B b = get();
-// CXX11-NEXT: 13: ~B() (Temporary object destructor)
-// CXX11-NEXT: 14: [B1.12].~B() (Implicit destructor)
+// CXX11-NEXT: 13: (FullExprCleanup collected 2 MTEs)
+// CXX11-NEXT: 14: ~B() (Temporary object destructor)
+// CXX11-NEXT: 15: [B1.12].~B() (Implicit destructor)
// CXX17-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], B)
// CXX17-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, B)
// CXX17-NEXT: 8: B b = get();
-// CXX17-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CXX17-NEXT: 9: (FullExprCleanup collected 1 MTEs)
+// CXX17-NEXT: 10: [B1.8].~B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValue() {
B b = get();
}
@@ -782,7 +801,8 @@ void implicitConstructionConversionFromFunctionValue() {
// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, NoOp, const B)
// CHECK-NEXT: 7: [B1.6]
// CHECK-NEXT: 8: const B &b = A();
-// CHECK-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CHECK-NEXT: 9: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 10: [B1.8].~B() (Implicit destructor)
void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
const B &b = A();
}
@@ -798,7 +818,8 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const B)
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: const B &b = get();
-// CHECK-NEXT: 11: [B1.10].~B() (Implicit destructor)
+// CHECK-NEXT: 11: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 12: [B1.10].~B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() {
const B &b = get(); // no-crash
}
@@ -854,8 +875,9 @@ void passArgument() {
// CXX11-NEXT: 10: [B1.9] (CXXConstructExpr, [B1.11], [B1.12]+1, D)
// CXX11-NEXT: 11: [B1.10] (BindTemporary)
// CXX11-NEXT: 12: [B1.2]([B1.5], [B1.11])
-// CXX11-NEXT: 13: ~D() (Temporary object destructor)
+// CXX11-NEXT: 13: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: 14: ~D() (Temporary object destructor)
+// CXX11-NEXT: 15: ~D() (Temporary object destructor)
// CXX17-NEXT: 3: C() (CXXConstructExpr, [B1.6]+0, C)
// CXX17-NEXT: 4: D() (CXXConstructExpr, [B1.5], [B1.6]+1, D)
// CXX17-NEXT: 5: [B1.4] (BindTemporary)
@@ -887,8 +909,9 @@ void passArgumentByReference() {
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], [B1.9]+0, D)
// CXX11-NEXT: 8: [B1.7] (BindTemporary)
// CXX11-NEXT: 9: [B1.2]([B1.8])
-// CXX11-NEXT: 10: ~D() (Temporary object destructor)
+// CXX11-NEXT: 10: (FullExprCleanup collected 1 MTEs)
// CXX11-NEXT: 11: ~D() (Temporary object destructor)
+// CXX11-NEXT: 12: ~D() (Temporary object destructor)
// CXX17-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.5]+0, D)
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: [B1.2]([B1.4])
@@ -905,7 +928,8 @@ void passArgumentWithDestructor() {
// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const D)
// CHECK-NEXT: 6: [B1.5]
// CHECK-NEXT: 7: [B1.2]([B1.6])
-// CHECK-NEXT: 8: ~D() (Temporary object destructor)
+// CHECK-NEXT: 8: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 9: ~D() (Temporary object destructor)
void passArgumentWithDestructorByReference() {
useDByReference(D());
}
@@ -924,8 +948,9 @@ void passArgumentWithDestructorByReference() {
// CXX11-NEXT: 9: [B1.8]
// CXX11-NEXT: 10: [B1.9] (CXXConstructExpr, [B1.11], E)
// CXX11-NEXT: 11: E e = E(D());
-// CXX11-NEXT: 12: ~D() (Temporary object destructor)
+// CXX11-NEXT: 12: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: 13: ~D() (Temporary object destructor)
+// CXX11-NEXT: 14: ~D() (Temporary object destructor)
// CXX17: 1: D() (CXXConstructExpr, [B1.2], [B1.3]+0, D)
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], E)
@@ -953,10 +978,11 @@ void passArgumentIntoAnotherConstructor() {
// CXX11-NEXT: 11: [B1.10] (CXXConstructExpr, [B1.12], [B1.13]+1, D)
// CXX11-NEXT: 12: [B1.11] (BindTemporary)
// CXX11-NEXT: 13: E([B1.6], [B1.12]) (CXXConstructExpr, E)
-// CXX11-NEXT: 14: ~D() (Temporary object destructor)
+// CXX11-NEXT: 14: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: 15: ~D() (Temporary object destructor)
// CXX11-NEXT: 16: ~D() (Temporary object destructor)
// CXX11-NEXT: 17: ~D() (Temporary object destructor)
+// CXX11-NEXT: 18: ~D() (Temporary object destructor)
// CXX17: 1: D() (CXXConstructExpr, [B1.2], [B1.5]+0, D)
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
// CXX17-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.5]+1, D)
@@ -1034,6 +1060,7 @@ int variadic(...);
// This code is quite exotic, so let's not test the CFG for it,
// but only make sure we don't crash.
+// CHECK-LABEL: void testCrashOnVariadicArgument()
void testCrashOnVariadicArgument() {
C c(variadic(0 ? c : 0)); // no-crash
}
diff --git a/clang/test/Analysis/cfg-rich-constructors.mm b/clang/test/Analysis/cfg-rich-constructors.mm
index f048f061e9fba..1b73c7ce55f76 100644
--- a/clang/test/Analysis/cfg-rich-constructors.mm
+++ b/clang/test/Analysis/cfg-rich-constructors.mm
@@ -30,8 +30,9 @@ -(D) bar;
// CXX11-NEXT: 8: [B1.7] (BindTemporary)
// Double brackets trigger FileCheck variables, escape.
// CXX11-NEXT: 9: {{\[}}[B1.2] foo:[B1.8]]
-// CXX11-NEXT: 10: ~D() (Temporary object destructor)
+// CXX11-NEXT: 10: (FullExprCleanup collected 1 MTEs)
// CXX11-NEXT: 11: ~D() (Temporary object destructor)
+// CXX11-NEXT: 12: ~D() (Temporary object destructor)
// CXX17-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.5]+0, D)
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// Double brackets trigger FileCheck variables, escape.
@@ -53,8 +54,9 @@ void passArgumentIntoMessage(E *e) {
// CXX11-NEXT: 6: [B1.5]
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], D)
// CXX11-NEXT: 8: D d = [e bar];
-// CXX11-NEXT: 9: ~D() (Temporary object destructor)
-// CXX11-NEXT: 10: [B1.8].~D() (Implicit destructor)
+// CXX11-NEXT: 9: (FullExprCleanup collected 1 MTEs)
+// CXX11-NEXT: 10: ~D() (Temporary object destructor)
+// CXX11-NEXT: 11: [B1.8].~D() (Implicit destructor)
// Double brackets trigger FileCheck variables, escape.
// CXX17-NEXT: 3: {{\[}}[B1.2] bar] (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
diff --git a/clang/test/Analysis/cfg.cpp b/clang/test/Analysis/cfg.cpp
index 2a88b73d27756..9a0a60110876b 100644
--- a/clang/test/Analysis/cfg.cpp
+++ b/clang/test/Analysis/cfg.cpp
@@ -390,6 +390,7 @@ void test_lifetime_extended_temporaries() {
3;
}
// CHECK: LifetimeExtend(4)
+ // CHECK-NEXT: (FullExprCleanup collected 2 MTEs)
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NEXT: : 4
diff --git a/clang/test/Analysis/missing-bind-temporary.cpp b/clang/test/Analysis/missing-bind-temporary.cpp
index 3d1af469dc01c..78bcb570a3ce5 100644
--- a/clang/test/Analysis/missing-bind-temporary.cpp
+++ b/clang/test/Analysis/missing-bind-temporary.cpp
@@ -28,8 +28,9 @@ class B {
// CHECK-NEXT: 7: [B1.6] (BindTemporary)
// CHECK-NEXT: 8: [B1.7]
// CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall)
-// CHECK-NEXT: 10: ~B() (Temporary object destructor)
-// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 10: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 11: ~B() (Temporary object destructor)
+// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor)
void foo(int) {
B i;
i = {};
@@ -65,8 +66,9 @@ class B {
// CHECK-NEXT: 7: [B1.6] (BindTemporary)
// CHECK-NEXT: 8: [B1.7]
// CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall)
-// CHECK-NEXT: 10: ~B() (Temporary object destructor)
-// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 10: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 11: ~B() (Temporary object destructor)
+// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
@@ -107,8 +109,9 @@ class B {
// CHECK-NEXT: 8: [B1.7] (BindTemporary)
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: [B1.5] = [B1.9] (OperatorCall)
-// CHECK-NEXT: 11: ~B() (Temporary object destructor)
-// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 11: (FullExprCleanup collected 1 MTEs)
+// CHECK-NEXT: 12: ~B() (Temporary object destructor)
+// CHECK-NEXT: 13: [B1.2].~B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
diff --git a/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp b/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp
index b496d261e92f4..22807741fdae1 100644
--- a/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ b/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -240,9 +240,10 @@ const C &bar3(bool coin) {
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const A)
// CHECK: 4: [B1.3]
// WARNINGS: 5: [B1.4] (CXXConstructExpr, A)
-// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.7], A)
-// CHECK: 6: ~A() (Temporary object destructor)
-// CHECK: 7: return [B1.5];
+// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.8], A)
+// CHECK: 6: (FullExprCleanup collected 1 MTEs)
+// CHECK: 7: ~A() (Temporary object destructor)
+// CHECK: 8: return [B1.5];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -300,9 +301,10 @@ const C &bar3(bool coin) {
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const A)
// CHECK: 4: [B1.3]
// WARNINGS: 5: [B1.4] (CXXConstructExpr, A)
-// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.7], A)
-// CHECK: 6: ~A() (Temporary object destructor)
-// CHECK: 7: return [B1.5];
+// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.8], A)
+// CHECK: 6: (FullExprCleanup collected 1 MTEs)
+// CHECK: 7: ~A() (Temporary object destructor)
+// CHECK: 8: return [B1.5];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -328,31 +330,33 @@ const C &bar3(bool coin) {
// CHECK: 14: int([B1.13]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 15: [B1.7] + [B1.14]
// CHECK: 16: int a = int(A()) + int(B());
-// CHECK: 17: ~B() (Temporary object destructor)
-// CHECK: 18: ~A() (Temporary object destructor)
-// CHECK: 19: foo
-// CHECK: 20: [B1.19] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
-// WARNINGS: 21: A() (CXXConstructExpr, A)
-// ANALYZER: 21: A() (CXXConstructExpr, [B1.22], [B1.23], A)
-// CHECK: 22: [B1.21] (BindTemporary)
-// CHECK: 23: [B1.22]
-// CHECK: 24: [B1.23].operator int
-// CHECK: 25: [B1.23]
-// CHECK: 26: [B1.25] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 27: int([B1.26]) (CXXFunctionalCastExpr, NoOp, int)
-// WARNINGS: 28: B() (CXXConstructExpr, B)
-// ANALYZER: 28: B() (CXXConstructExpr, [B1.29], [B1.30], B)
-// CHECK: 29: [B1.28] (BindTemporary)
-// CHECK: 30: [B1.29]
-// CHECK: 31: [B1.30].operator int
-// CHECK: 32: [B1.30]
-// CHECK: 33: [B1.32] (ImplicitCastExpr, UserDefinedConversion, int)
-// CHECK: 34: int([B1.33]) (CXXFunctionalCastExpr, NoOp, int)
-// CHECK: 35: [B1.27] + [B1.34]
-// CHECK: 36: [B1.20]([B1.35])
-// CHECK: 37: ~B() (Temporary object destructor)
-// CHECK: 38: ~A() (Temporary object destructor)
-// CHECK: 39: int b;
+// CHECK: 17: (FullExprCleanup collected 2 MTEs)
+// CHECK: 18: ~B() (Temporary object destructor)
+// CHECK: 19: ~A() (Temporary object destructor)
+// CHECK: 20: foo
+// CHECK: 21: [B1.20] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
+// WARNINGS: 22: A() (CXXConstructExpr, A)
+// ANALYZER: 22: A() (CXXConstructExpr, [B1.23], [B1.24], A)
+// CHECK: 23: [B1.22] (BindTemporary)
+// CHECK: 24: [B1.23]
+// CHECK: 25: [B1.24].operator int
+// CHECK: 26: [B1.24]
+// CHECK: 27: [B1.26] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 28: int([B1.27]) (CXXFunctionalCastExpr, NoOp, int)
+// WARNINGS: 29: B() (CXXConstructExpr, B)
+// ANALYZER: 29: B() (CXXConstructExpr, [B1.30], [B1.31], B)
+// CHECK: 30: [B1.29] (BindTemporary)
+// CHECK: 31: [B1.30]
+// CHECK: 32: [B1.31].operator int
+// CHECK: 33: [B1.31]
+// CHECK: 34: [B1.33] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 35: int([B1.34]) (CXXFunctionalCastExpr, NoOp, int)
+// CHECK: 36: [B1.28] + [B1.35]
+// CHECK: 37: [B1.21]([B1.36])
+// CHECK: 38: (FullExprCleanup collected 2 MTEs)
+// CHECK: 39: ~B() (Temporary object destructor)
+// CHECK: 40: ~A() (Temporary object destructor)
+// CHECK: 41: int b;
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -371,6 +375,7 @@ const C &bar3(bool coin) {
// CHECK: [B3]
// CHECK: 1: [B5.9] && [B4.6]
// CHECK: 2: [B5.3]([B3.1])
+// CHECK: 3: (FullExprCleanup collected 2 MTEs)
// CHECK: T: (Temp Dtor) [B4.2]
// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B2 B1
@@ -405,6 +410,7 @@ const C &bar3(bool coin) {
// CHECK: [B7]
// CHECK: 1: [B9.6] && [B8.6]
// CHECK: 2: bool a = A() && B();
+// CHECK: 3: (FullExprCleanup collected 2 MTEs)
// CHECK: T: (Temp Dtor) [B8.2]
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B6 B5
@@ -479,6 +485,7 @@ const C &bar3(bool coin) {
// CHECK: [B7]
// CHECK: 1: [B9.6] || [B8.6]
// CHECK: 2: bool a = A() || B();
+// CHECK: 3: (FullExprCleanup collected 2 MTEs)
// CHECK: T: (Temp Dtor) [B8.2]
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B6 B5
@@ -535,7 +542,8 @@ const C &bar3(bool coin) {
// CHECK: 5: [B4.4].operator bool
// CHECK: 6: [B4.4]
// CHECK: 7: [B4.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: 8: ~B() (Temporary object destructor)
+// CHECK: 8: (FullExprCleanup collected 1 MTEs)
+// CHECK: 9: ~B() (Temporary object destructor)
// CHECK: T: if [B4.7]
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B3 B2
@@ -558,6 +566,7 @@ const C &bar3(bool coin) {
// WARNINGS: 4: [B7.3] (CXXConstructExpr, A)
// ANALYZER: 4: [B7.3] (CXXConstructExpr, [B7.5], A)
// CHECK: 5: A a = B() ? A() : A(B());
+// CHECK: 6: (FullExprCleanup collected 6 MTEs)
// CHECK: T: (Temp Dtor) [B9.2]
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B6 B5
@@ -671,7 +680,8 @@ const C &bar3(bool coin) {
// CHECK: 4: [B3.3].operator bool
// CHECK: 5: [B3.3]
// CHECK: 6: [B3.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: 7: ~C() (Temporary object destructor)
+// CHECK: 7: (FullExprCleanup collected 1 MTEs)
+// CHECK: 8: ~C() (Temporary object destructor)
// CHECK: T: if [B3.6]
// CHECK: Preds (1): B4
// CHECK: Succs (2): B2 B1
@@ -703,12 +713,13 @@ const C &bar3(bool coin) {
// WARNINGS: 5: [B4.4] (CXXConstructExpr, C)
// ANALYZER: 5: [B4.4] (CXXConstructExpr, [B4.6], C)
// CHECK: 6: C c = C();
-// CHECK: 7: ~C() (Temporary object destructor)
-// CHECK: 8: c
-// CHECK: 9: [B4.8].operator bool
-// CHECK: 10: [B4.8]
-// CHECK: 11: [B4.10] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK: T: if [B4.11]
+// CHECK: 7: (FullExprCleanup collected 1 MTEs)
+// CHECK: 8: ~C() (Temporary object destructor)
+// CHECK: 9: c
+// CHECK: 10: [B4.9].operator bool
+// CHECK: 11: [B4.9]
+// CHECK: 12: [B4.11] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: T: if [B4.12]
// CHECK: Preds (1): B5
// CHECK: Succs (2): B3 B2
// CHECK: [B0 (EXIT)]
@@ -732,6 +743,7 @@ const C &bar3(bool coin) {
// CHECK: 3: [B3.2].operator bool
// CHECK: 4: [B3.2]
// CHECK: 5: [B3.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK: 6: (FullExprCleanup collected 1 MTEs)
// CHECK: T: if [B3.5]
// CHECK: Preds (1): B4
// CHECK: Succs (2): B2 B1
@@ -757,22 +769,24 @@ const C &bar3(bool coin) {
// CXX98-WARNINGS: 4: [B3.3] (CXXConstructExpr, D)
// CXX98-ANALYZER: 4: [B3.3] (CXXConstructExpr, [B3.5], D)
// CXX98: 5: D d = D();
-// CXX98: 6: d
-// CXX98: 7: [B3.6].operator bool
-// CXX98: 8: [B3.6]
-// CXX98: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CXX98: T: if [B3.9]
+// CXX98: 6: (FullExprCleanup collected 1 MTEs)
+// CXX98: 7: d
+// CXX98: 8: [B3.7].operator bool
+// CXX98: 9: [B3.7]
+// CXX98: 10: [B3.9] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX98: T: if [B3.10]
// CXX11-WARNINGS: 1: D() (CXXConstructExpr, D)
// CXX11-ANALYZER: 1: D() (CXXConstructExpr, [B3.2], [B3.3], D)
// CXX11: 2: [B3.1]
// CXX11-WARNINGS: 3: [B3.2] (CXXConstructExpr, D)
// CXX11-ANALYZER: 3: [B3.2] (CXXConstructExpr, [B3.4], D)
// CXX11: 4: D d = D();
-// CXX11: 5: d
-// CXX11: 6: [B3.5].operator bool
-// CXX11: 7: [B3.5]
-// CXX11: 8: [B3.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CXX11: T: if [B3.8]
+// CXX11: 5: (FullExprCleanup collected 1 MTEs)
+// CXX11: 6: d
+// CXX11: 7: [B3.6].operator bool
+// CXX11: 8: [B3.6]
+// CXX11: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CXX11: T: if [B3.9]
// CHECK: Preds (1): B4
// CHECK: Succs (2): B2 B1
// CHECK: [B0 (EXIT)]
@@ -802,6 +816,7 @@ const C &bar3(bool coin) {
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const A)
// CHECK: 3: [B4.2]
// CHECK: 4: [B7.3]([B4.3])
+// CHECK: 5: (FullExprCleanup collected 6 MTEs)
// CHECK: T: (Temp Dtor) [B6.2]
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B3 B2
@@ -867,6 +882,7 @@ const C &bar3(bool coin) {
// CHECK: 2: [B10.1] (ImplicitCastExpr, NoOp, const A)
// CHECK: 3: [B10.2]
// CHECK: 4: const A &a = B() ? A() : A(B());
+// CHECK: 5: (FullExprCleanup collected 5 MTEs)
// CHECK: T: (Temp Dtor) [B12.2]
// CHECK: Preds (2): B11 B12
// CHECK: Succs (2): B9 B8
@@ -940,6 +956,8 @@ const C &bar3(bool coin) {
// WARNINGS: 4: [B4.3] (CXXConstructExpr, A)
// ANALYZER: 4: [B4.3] (CXXConstructExpr, [B4.5], A)
// CHECK: 5: A a = A() ?: A();
+// CXX98: 6: (FullExprCleanup collected 4 MTEs)
+// CXX11: 6: (FullExprCleanup collected 2 MTEs)
// CHECK: T: (Temp Dtor) [B6.2]
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B3 B2
@@ -1002,6 +1020,8 @@ const C &bar3(bool coin) {
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const A)
// CHECK: 3: [B4.2]
// CHECK: 4: [B7.2]([B4.3])
+// CXX98: 5: (FullExprCleanup collected 4 MTEs)
+// CXX11: 5: (FullExprCleanup collected 2 MTEs)
// CHECK: T: (Temp Dtor) [B6.2]
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B3 B2
@@ -1052,6 +1072,8 @@ const C &bar3(bool coin) {
// CHECK: 2: [B9.1] (ImplicitCastExpr, NoOp, const A)
// CHECK: 3: [B9.2]
// CHECK: 4: const A &a = A() ?: A();
+// CXX98: 5: (FullExprCleanup collected 3 MTEs)
+// CXX11: 5: (FullExprCleanup collected 1 MTEs)
// CHECK: T: (Temp Dtor) [B11.2]
// CHECK: Preds (2): B10 B11
// CHECK: Succs (2): B8 B7
@@ -1103,9 +1125,10 @@ const C &bar3(bool coin) {
// WARNINGS: 5: [B1.4] (CXXConstructExpr, A)
// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.6], A)
// CHECK: 6: A a = A();
-// CHECK: 7: ~A() (Temporary object destructor)
-// CHECK: 8: int b;
-// CHECK: 9: [B1.6].~A() (Implicit destructor)
+// CHECK: 7: (FullExprCleanup collected 1 MTEs)
+// CHECK: 8: ~A() (Temporary object destructor)
+// CHECK: 9: int b;
+// CHECK: 10: [B1.6].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1127,9 +1150,10 @@ const C &bar3(bool coin) {
// CHECK: 10: [B1.9] (ImplicitCastExpr, NoOp, const A)
// CHECK: 11: [B1.10]
// CHECK: 12: [B1.7]([B1.11])
-// CHECK: 13: ~A() (Temporary object destructor)
-// CHECK: 14: int b;
-// CHECK: 15: [B1.5].~A() (Implicit destructor)
+// CHECK: 13: (FullExprCleanup collected 1 MTEs)
+// CHECK: 14: ~A() (Temporary object destructor)
+// CHECK: 15: int b;
+// CHECK: 16: [B1.5].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1147,9 +1171,10 @@ const C &bar3(bool coin) {
// WARNINGS: 7: [B1.6] (CXXConstructExpr, A)
// ANALYZER: 7: [B1.6] (CXXConstructExpr, [B1.8], A)
// CHECK: 8: A a = A::make();
-// CHECK: 9: ~A() (Temporary object destructor)
-// CHECK: 10: int b;
-// CHECK: 11: [B1.8].~A() (Implicit destructor)
+// CHECK: 9: (FullExprCleanup collected 1 MTEs)
+// CHECK: 10: ~A() (Temporary object destructor)
+// CHECK: 11: int b;
+// CHECK: 12: [B1.8].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1175,9 +1200,10 @@ const C &bar3(bool coin) {
// CHECK: 14: [B1.13] (ImplicitCastExpr, NoOp, const A)
// CHECK: 15: [B1.14]
// CHECK: 16: [B1.9]([B1.15])
-// CHECK: 17: ~A() (Temporary object destructor)
-// CHECK: 18: int b;
-// CHECK: 19: [B1.7].~A() (Implicit destructor)
+// CHECK: 17: (FullExprCleanup collected 1 MTEs)
+// CHECK: 18: ~A() (Temporary object destructor)
+// CHECK: 19: int b;
+// CHECK: 20: [B1.7].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1195,8 +1221,9 @@ const C &bar3(bool coin) {
// CHECK: 7: [B1.6] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 8: a
// CHECK: 9: [B1.8] = [B1.7]
-// CHECK: 10: ~A() (Temporary object destructor)
-// CHECK: 11: int b;
+// CHECK: 10: (FullExprCleanup collected 1 MTEs)
+// CHECK: 11: ~A() (Temporary object destructor)
+// CHECK: 12: int b;
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1222,9 +1249,10 @@ const C &bar3(bool coin) {
// CHECK: 14: int([B1.13]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 15: [B1.7] + [B1.14]
// CHECK: 16: a([B1.15]) (Member initializer)
-// CHECK: 17: ~B() (Temporary object destructor)
-// CHECK: 18: ~A() (Temporary object destructor)
-// CHECK: 19: b(/*implicit*/(int)0) (Member initializer)
+// CHECK: 17: (FullExprCleanup collected 2 MTEs)
+// CHECK: 18: ~B() (Temporary object destructor)
+// CHECK: 19: ~A() (Temporary object destructor)
+// CHECK: 20: b(/*implicit*/(int)0) (Member initializer)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1243,7 +1271,8 @@ const C &bar3(bool coin) {
// CHECK: 3: [B2.2] (BindTemporary)
// CHECK: [[MEMBER:[45]]]: [B2.{{[34]}}].f
// CHECK: {{[56]}}: [B2.[[MEMBER]]]()
-// CHECK: {{[67]}}: ~NoReturn() (Temporary object destructor)
+// CHECK: 7: (FullExprCleanup collected 1 MTEs)
+// CHECK: {{[78]}}: ~NoReturn() (Temporary object destructor)
// CHECK: Preds (1): B3
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1288,6 +1317,7 @@ const C &bar3(bool coin) {
// CHECK: Succs (1): B0
// CHECK: [B5]
// CHECK: 1: [B7.3] || [B6.7]
+// CHECK: 2: (FullExprCleanup collected 1 MTEs)
// CHECK: T: (Temp Dtor) [B6.4]
// CHECK: Preds (2): B6 B7
// CHECK: Succs (2): B4 B3
@@ -1339,6 +1369,7 @@ const C &bar3(bool coin) {
// CHECK: Succs (1): B0
// CHECK: [B5]
// CHECK: 1: [B8.3] || [B7.3] || [B6.7]
+// CHECK: 2: (FullExprCleanup collected 1 MTEs)
// CHECK: T: (Temp Dtor) [B6.4]
// CHECK: Preds (3): B6 B7 B8
// CHECK: Succs (2): B4 B3
@@ -1397,6 +1428,7 @@ const C &bar3(bool coin) {
// CHECK: Succs (1): B0
// CHECK: [B5]
// CHECK: 1: [B8.3] || [B7.2] || [B6.7]
+// CHECK: 2: (FullExprCleanup collected 1 MTEs)
// CHECK: T: (Temp Dtor) [B6.4]
// CHECK: Preds (3): B6 B7 B8
// CHECK: Succs (2): B4(Unreachable) B3
More information about the cfe-commits
mailing list