summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Analysis/CFG.cpp123
-rw-r--r--test/Analysis/auto-obj-dtors-cfg-output.cpp4
-rw-r--r--test/Analysis/cfg-rich-constructors.cpp33
-rw-r--r--test/Analysis/cfg-rich-constructors.mm3
-rw-r--r--test/Analysis/cfg.cpp4
-rw-r--r--test/Analysis/missing-bind-temporary.cpp6
-rw-r--r--test/Analysis/more-dtors-cfg-output.cpp317
-rw-r--r--test/Analysis/scopes-cfg-output.cpp4
-rw-r--r--test/Analysis/temporaries.cpp19
9 files changed, 423 insertions, 90 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index fb643464fa..33c47b24a9 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -525,7 +525,7 @@ private:
CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc);
CFGBlock *VisitCaseStmt(CaseStmt *C);
CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc);
- CFGBlock *VisitCompoundStmt(CompoundStmt *C);
+ CFGBlock *VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestructed);
CFGBlock *VisitConditionalOperator(AbstractConditionalOperator *C,
AddStmtChoice asc);
CFGBlock *VisitContinueStmt(ContinueStmt *C);
@@ -546,7 +546,8 @@ private:
CFGBlock *VisitDeclSubExpr(DeclStmt *DS);
CFGBlock *VisitDefaultStmt(DefaultStmt *D);
CFGBlock *VisitDoStmt(DoStmt *D);
- CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
+ CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E,
+ AddStmtChoice asc, bool ExternallyDestructed);
CFGBlock *VisitForStmt(ForStmt *F);
CFGBlock *VisitGotoStmt(GotoStmt *G);
CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc);
@@ -585,7 +586,8 @@ private:
CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc);
CFGBlock *VisitWhileStmt(WhileStmt *W);
- CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd);
+ CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd,
+ bool ExternallyDestructed = false);
CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc);
CFGBlock *VisitChildren(Stmt *S);
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
@@ -656,15 +658,17 @@ private:
// Visitors to walk an AST and generate destructors of temporaries in
// full expression.
- CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+ CFGBlock *VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
TempDtorContext &Context);
- CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, TempDtorContext &Context);
+ CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
+ TempDtorContext &Context);
CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
+ bool ExternallyDestructed,
TempDtorContext &Context);
CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
+ CXXBindTemporaryExpr *E, bool ExternallyDestructed, TempDtorContext &Context);
CFGBlock *VisitConditionalOperatorForTemporaryDtors(
- AbstractConditionalOperator *E, bool BindToTemporary,
+ AbstractConditionalOperator *E, bool ExternallyDestructed,
TempDtorContext &Context);
void InsertTempDtorDecisionBlock(const TempDtorContext &Context,
CFGBlock *FalseSucc = nullptr);
@@ -1575,7 +1579,7 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/false, Context);
}
}
@@ -2051,7 +2055,8 @@ CFGBuilder::prependAutomaticObjScopeEndWithTerminator(
/// Visit - Walk the subtree of a statement and add extra
/// blocks for ternary operators, &&, and ||. We also process "," and
/// DeclStmts (which may contain nested control-flow).
-CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
+CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
+ bool ExternallyDestructed) {
if (!S) {
badCFG = true;
return nullptr;
@@ -2096,7 +2101,7 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
return VisitChooseExpr(cast<ChooseExpr>(S), asc);
case Stmt::CompoundStmtClass:
- return VisitCompoundStmt(cast<CompoundStmt>(S));
+ return VisitCompoundStmt(cast<CompoundStmt>(S), ExternallyDestructed);
case Stmt::ConditionalOperatorClass:
return VisitConditionalOperator(cast<ConditionalOperator>(S), asc);
@@ -2108,7 +2113,8 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
return VisitCXXCatchStmt(cast<CXXCatchStmt>(S));
case Stmt::ExprWithCleanupsClass:
- return VisitExprWithCleanups(cast<ExprWithCleanups>(S), asc);
+ return VisitExprWithCleanups(cast<ExprWithCleanups>(S),
+ asc, ExternallyDestructed);
case Stmt::CXXDefaultArgExprClass:
case Stmt::CXXDefaultInitExprClass:
@@ -2603,7 +2609,7 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C,
return addStmt(C->getCond());
}
-CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
+CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestructed) {
LocalScope::const_iterator scopeBeginPos = ScopePos;
addLocalScopeForStmt(C);
@@ -2619,11 +2625,16 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
I != E; ++I ) {
// If we hit a segment of code just containing ';' (NullStmts), we can
// get a null block back. In such cases, just use the LastBlock
- if (CFGBlock *newBlock = addStmt(*I))
+ CFGBlock *newBlock = Visit(*I, AddStmtChoice::AlwaysAdd,
+ ExternallyDestructed);
+
+ if (newBlock)
LastBlock = newBlock;
if (badCFG)
return nullptr;
+
+ ExternallyDestructed = false;
}
return LastBlock;
@@ -2766,7 +2777,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/true, Context);
}
}
@@ -2980,9 +2991,18 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) {
if (!Block->hasNoReturnElement())
addSuccessor(Block, &cfg->getExit());
- // Add the return statement to the block. This may create new blocks if R
- // contains control-flow (short-circuit operations).
- return VisitStmt(S, AddStmtChoice::AlwaysAdd);
+ // Add the return statement to the block.
+ appendStmt(Block, S);
+
+ // Visit children
+ if (ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) {
+ Expr *O = RS->getRetValue();
+ if (O)
+ Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true);
+ return Block;
+ } else { // co_return
+ return VisitChildren(S);
+ }
}
CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
@@ -3014,7 +3034,7 @@ CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
}
CFGBlock *CFGBuilder::VisitSEHFinallyStmt(SEHFinallyStmt *FS) {
- return VisitCompoundStmt(FS->getBlock());
+ return VisitCompoundStmt(FS->getBlock(), /*ExternallyDestructed=*/false);
}
CFGBlock *CFGBuilder::VisitSEHLeaveStmt(SEHLeaveStmt *LS) {
@@ -3898,7 +3918,7 @@ CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) {
autoCreateBlock();
appendStmt(Block, SE);
}
- return VisitCompoundStmt(SE->getSubStmt());
+ return VisitCompoundStmt(SE->getSubStmt(), /*ExternallyDestructed=*/true);
}
CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
@@ -4363,12 +4383,12 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
}
CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
- AddStmtChoice asc) {
+ AddStmtChoice asc, bool ExternallyDestructed) {
if (BuildOpts.AddTemporaryDtors) {
// If adding implicit destructors visit the full expression for adding
// destructors of temporaries.
TempDtorContext Context;
- VisitForTemporaryDtors(E->getSubExpr(), false, Context);
+ VisitForTemporaryDtors(E->getSubExpr(), ExternallyDestructed, Context);
// Full expression has to be added as CFGStmt so it will be sequenced
// before destructors of it's temporaries.
@@ -4504,7 +4524,7 @@ CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) {
return addStmt(I->getTarget());
}
-CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
TempDtorContext &Context) {
assert(BuildOpts.AddImplicitDtors && BuildOpts.AddTemporaryDtors);
@@ -4515,28 +4535,32 @@ tryAgain:
}
switch (E->getStmtClass()) {
default:
- return VisitChildrenForTemporaryDtors(E, Context);
+ return VisitChildrenForTemporaryDtors(E, false, Context);
+
+ case Stmt::InitListExprClass:
+ return VisitChildrenForTemporaryDtors(E, ExternallyDestructed, Context);
case Stmt::BinaryOperatorClass:
return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E),
+ ExternallyDestructed,
Context);
case Stmt::CXXBindTemporaryExprClass:
return VisitCXXBindTemporaryExprForTemporaryDtors(
- cast<CXXBindTemporaryExpr>(E), BindToTemporary, Context);
+ cast<CXXBindTemporaryExpr>(E), ExternallyDestructed, Context);
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass:
return VisitConditionalOperatorForTemporaryDtors(
- cast<AbstractConditionalOperator>(E), BindToTemporary, Context);
+ cast<AbstractConditionalOperator>(E), ExternallyDestructed, Context);
case Stmt::ImplicitCastExprClass:
- // For implicit cast we want BindToTemporary to be passed further.
+ // For implicit cast we want ExternallyDestructed to be passed further.
E = cast<CastExpr>(E)->getSubExpr();
goto tryAgain;
case Stmt::CXXFunctionalCastExprClass:
- // For functional cast we want BindToTemporary to be passed further.
+ // For functional cast we want ExternallyDestructed to be passed further.
E = cast<CXXFunctionalCastExpr>(E)->getSubExpr();
goto tryAgain;
@@ -4550,7 +4574,7 @@ tryAgain:
case Stmt::MaterializeTemporaryExprClass: {
const MaterializeTemporaryExpr* MTE = cast<MaterializeTemporaryExpr>(E);
- BindToTemporary = (MTE->getStorageDuration() != SD_FullExpression);
+ ExternallyDestructed = (MTE->getStorageDuration() != SD_FullExpression);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
// Find the expression whose lifetime needs to be extended.
@@ -4561,7 +4585,7 @@ tryAgain:
// Visit the skipped comma operator left-hand sides for other temporaries.
for (const Expr *CommaLHS : CommaLHSs) {
VisitForTemporaryDtors(const_cast<Expr *>(CommaLHS),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/false, Context);
}
goto tryAgain;
}
@@ -4579,13 +4603,18 @@ tryAgain:
for (Expr *Init : LE->capture_inits()) {
if (Init) {
if (CFGBlock *R = VisitForTemporaryDtors(
- Init, /*BindToTemporary=*/false, Context))
+ Init, /*ExternallyDestructed=*/true, Context))
B = R;
}
}
return B;
}
+ case Stmt::StmtExprClass:
+ // Don't recurse into statement expressions; any cleanups inside them
+ // will be wrapped in their own ExprWithCleanups.
+ return Block;
+
case Stmt::CXXDefaultArgExprClass:
E = cast<CXXDefaultArgExpr>(E)->getExpr();
goto tryAgain;
@@ -4597,6 +4626,7 @@ tryAgain:
}
CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
+ bool ExternallyDestructed,
TempDtorContext &Context) {
if (isa<LambdaExpr>(E)) {
// Do not visit the children of lambdas; they have their own CFGs.
@@ -4610,14 +4640,22 @@ CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
CFGBlock *B = Block;
for (Stmt *Child : E->children())
if (Child)
- if (CFGBlock *R = VisitForTemporaryDtors(Child, false, Context))
+ if (CFGBlock *R = VisitForTemporaryDtors(Child, ExternallyDestructed, Context))
B = R;
return B;
}
CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
- BinaryOperator *E, TempDtorContext &Context) {
+ BinaryOperator *E, bool ExternallyDestructed, TempDtorContext &Context) {
+ if (E->isCommaOp()) {
+ // For comma operator LHS expression is visited
+ // before RHS expression. For destructors visit them in reverse order.
+ CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), ExternallyDestructed, Context);
+ CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
+ return LHSBlock ? LHSBlock : RHSBlock;
+ }
+
if (E->isLogicalOp()) {
VisitForTemporaryDtors(E->getLHS(), false, Context);
TryResult RHSExecuted = tryEvaluateBool(E->getLHS());
@@ -4652,10 +4690,11 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
}
CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
+ CXXBindTemporaryExpr *E, bool ExternallyDestructed, TempDtorContext &Context) {
// First add destructors for temporaries in subexpression.
- CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
- if (!BindToTemporary) {
+ // Because VisitCXXBindTemporaryExpr calls setDestructed:
+ CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), true, Context);
+ if (!ExternallyDestructed) {
// If lifetime of temporary is not prolonged (by assigning to constant
// reference) add destructor for it.
@@ -4703,7 +4742,7 @@ void CFGBuilder::InsertTempDtorDecisionBlock(const TempDtorContext &Context,
}
CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
- AbstractConditionalOperator *E, bool BindToTemporary,
+ AbstractConditionalOperator *E, bool ExternallyDestructed,
TempDtorContext &Context) {
VisitForTemporaryDtors(E->getCond(), false, Context);
CFGBlock *ConditionBlock = Block;
@@ -4714,14 +4753,14 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
TempDtorContext TrueContext(
bothKnownTrue(Context.KnownExecuted, ConditionVal));
- VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary, TrueContext);
+ VisitForTemporaryDtors(E->getTrueExpr(), ExternallyDestructed, TrueContext);
CFGBlock *TrueBlock = Block;
Block = ConditionBlock;
Succ = ConditionSucc;
TempDtorContext FalseContext(
bothKnownTrue(Context.KnownExecuted, NegatedVal));
- VisitForTemporaryDtors(E->getFalseExpr(), BindToTemporary, FalseContext);
+ VisitForTemporaryDtors(E->getFalseExpr(), ExternallyDestructed, FalseContext);
if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
InsertTempDtorDecisionBlock(FalseContext, TrueBlock);
@@ -5349,15 +5388,13 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const VarDecl *VD = DE->getVarDecl();
Helper.handleDecl(VD, OS);
- ASTContext &ACtx = VD->getASTContext();
QualType T = VD->getType();
if (T->isReferenceType())
T = getReferenceInitTemporaryType(VD->getInit(), nullptr);
- if (const ArrayType *AT = ACtx.getAsArrayType(T))
- T = ACtx.getBaseElementType(AT);
- OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
- OS << " (Implicit destructor)\n";
+ OS << ".~";
+ T.getUnqualifiedType().print(OS, PrintingPolicy(Helper.getLangOpts()));
+ OS << "() (Implicit destructor)\n";
} else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) {
const VarDecl *VD = DE->getVarDecl();
Helper.handleDecl(VD, OS);
diff --git a/test/Analysis/auto-obj-dtors-cfg-output.cpp b/test/Analysis/auto-obj-dtors-cfg-output.cpp
index 7e678a1ec7..716eaf3c0f 100644
--- a/test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ b/test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -334,7 +334,7 @@ void test_aggregate_array_lifetime_extension() {
// 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() (Implicit destructor)
+// CHECK-NEXT: 64: [B1.57].~D [2]() (Implicit destructor)
// CHECK-NEXT: 65: [B1.18].~D() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
@@ -363,7 +363,7 @@ void test_aggregate_with_nontrivial_own_destructor() {
// WARNINGS-NEXT: 3: (CXXConstructExpr, class A [0])
// ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], class A [0])
// CHECK-NEXT: 4: A b[0];
-// CHECK-NEXT: 5: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B1.2].~A [2]() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
// CHECK: [B0 (EXIT)]
diff --git a/test/Analysis/cfg-rich-constructors.cpp b/test/Analysis/cfg-rich-constructors.cpp
index cb1ed8eb6b..e33e57597d 100644
--- a/test/Analysis/cfg-rich-constructors.cpp
+++ b/test/Analysis/cfg-rich-constructors.cpp
@@ -412,7 +412,6 @@ public:
~D();
};
-// FIXME: There should be no temporary destructor in C++17.
// CHECK: return_stmt_with_dtor::D returnTemporary()
// 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)
@@ -422,15 +421,13 @@ public:
// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], class return_stmt_with_dtor::D)
// CXX11-NEXT: 6: ~return_stmt_with_dtor::D() (Temporary object destructor)
// CXX11-NEXT: 7: return [B1.5];
-// CXX17: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.4], [B1.2], class return_stmt_w
+// CXX17: 1: return_stmt_with_dtor::D() (CXXConstructExpr, [B1.3], [B1.2], class return_stmt_w
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
-// CXX17-NEXT: 3: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX17-NEXT: 4: return [B1.2];
+// CXX17-NEXT: 3: return [B1.2];
D returnTemporary() {
return D();
}
-// FIXME: There should be no temporary destructor in C++17.
// CHECK: void returnByValueIntoVariable()
// CHECK: 1: returnTemporary
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class return_stmt_with_dtor::D (*)(void))
@@ -442,12 +439,10 @@ D returnTemporary() {
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], class return_stmt_with_dtor::D)
// CXX11-NEXT: 8: return_stmt_with_dtor::D d = returnTemporary();
// CXX11-NEXT: 9: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX11-NEXT: 10: [B1.8].~D() (Implicit destructor)
+// CXX11-NEXT: 10: [B1.8].~return_stmt_with_dtor::D() (Implicit destructor)
// CXX17-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: return_stmt_with_dtor::D d = returnTemporary();
-// CXX17-NEXT: 6: ~return_stmt_with_dtor::D() (Temporary object destructor)
-// CXX17-NEXT: 7: [B1.5].~D() (Implicit destructor)
void returnByValueIntoVariable() {
D d = returnTemporary();
}
@@ -602,7 +597,7 @@ void temporaryInCondition() {
// CHECK-NEXT: 3: [B1.2] (BindTemporary)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d(0);
-// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceVariableWithConstructor() {
const D &d(0);
}
@@ -613,14 +608,14 @@ void referenceVariableWithConstructor() {
// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d = temporary_object_expr_with_dtors::D();
-// CHECK-NEXT: 6: [B1.5].~D() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.5].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceVariableWithInitializer() {
const D &d = D();
}
// CHECK: void referenceVariableWithTernaryOperator(bool coin)
// CXX11: [B1]
-// CXX11-NEXT: 1: [B4.4].~D() (Implicit destructor)
+// CXX11-NEXT: 1: [B4.4].~temporary_object_expr_with_dtors::D() (Implicit destructor)
// CXX11: [B2]
// CXX11-NEXT: 1: ~temporary_object_expr_with_dtors::D() (Temporary object destructor)
// CXX11: [B3]
@@ -660,7 +655,7 @@ void referenceVariableWithInitializer() {
// CXX17-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D)
// CXX17-NEXT: 3: [B1.2]
// CXX17-NEXT: 4: const temporary_object_expr_with_dtors::D &d = coin ? D::get() : temporary_object_expr_with_dtors::D(0);
-// CXX17-NEXT: 5: [B1.4].~D() (Implicit destructor)
+// CXX17-NEXT: 5: [B1.4].~temporary_object_expr_with_dtors::D() (Implicit destructor)
// CXX17: [B2]
// CXX17-NEXT: 1: D::get
// CXX17-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void))
@@ -686,7 +681,7 @@ void referenceVariableWithTernaryOperator(bool coin) {
// CHECK-NEXT: 4: temporary_object_expr_with_dtors::D([B1.3]) (CXXFunctionalCastExpr, ConstructorCon
// CHECK-NEXT: 5: [B1.4]
// CHECK-NEXT: 6: temporary_object_expr_with_dtors::D &&d = temporary_object_expr_with_dtors::D(1);
-// CHECK-NEXT: 7: [B1.6].~D() (Implicit destructor)
+// CHECK-NEXT: 7: [B1.6].~temporary_object_expr_with_dtors::D() (Implicit destructor)
void referenceWithFunctionalCast() {
D &&d = D(1);
}
@@ -743,13 +738,13 @@ public:
// CXX11-NEXT: 9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B)
// CXX11-NEXT: 10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
// CXX11-NEXT: 11: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CXX11-NEXT: 12: [B1.10].~B() (Implicit destructor)
+// CXX11-NEXT: 12: [B1.10].~implicit_constructor_conversion::B() (Implicit destructor)
// CXX17-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
// CXX17-NEXT: 3: [B1.2]
// CXX17-NEXT: 4: [B1.3] (CXXConstructExpr, [B1.6], class implicit_constructor_conversion::B)
// CXX17-NEXT: 5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
// CXX17-NEXT: 6: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
-// CXX17-NEXT: 7: [B1.6].~B() (Implicit destructor)
+// CXX17-NEXT: 7: [B1.6].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromTemporary() {
B b = A();
}
@@ -769,11 +764,11 @@ void implicitConstructionConversionFromTemporary() {
// CXX11-NEXT: 11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B)
// CXX11-NEXT: 12: implicit_constructor_conversion::B b = get();
// CXX11-NEXT: 13: ~implicit_constructor_conversion::B() (Temporary object destructor)
-// CXX11-NEXT: 14: [B1.12].~B() (Implicit destructor)
+// CXX11-NEXT: 14: [B1.12].~implicit_constructor_conversion::B() (Implicit destructor)
// CXX17-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], class implicit_constructor_conversion::B)
// CXX17-NEXT: 7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
// CXX17-NEXT: 8: implicit_constructor_conversion::B b = get();
-// CXX17-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CXX17-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValue() {
B b = get();
}
@@ -787,7 +782,7 @@ void implicitConstructionConversionFromFunctionValue() {
// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
// CHECK-NEXT: 7: [B1.6]
// CHECK-NEXT: 8: const implicit_constructor_conversion::B &b = implicit_constructor_conversion::A();
-// CHECK-NEXT: 9: [B1.8].~B() (Implicit destructor)
+// CHECK-NEXT: 9: [B1.8].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
const B &b = A();
}
@@ -803,7 +798,7 @@ void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: const implicit_constructor_conversion::B &b = get();
-// CHECK-NEXT: 11: [B1.10].~B() (Implicit destructor)
+// CHECK-NEXT: 11: [B1.10].~implicit_constructor_conversion::B() (Implicit destructor)
void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() {
const B &b = get(); // no-crash
}
diff --git a/test/Analysis/cfg-rich-constructors.mm b/test/Analysis/cfg-rich-constructors.mm
index 289094293e..e55928d4c5 100644
--- a/test/Analysis/cfg-rich-constructors.mm
+++ b/test/Analysis/cfg-rich-constructors.mm
@@ -59,8 +59,7 @@ void passArgumentIntoMessage(E *e) {
// CXX17-NEXT: 3: {{\[}}[B1.2] bar] (CXXRecordTypedCall, [B1.5], [B1.4])
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: D d = [e bar];
-// CXX17-NEXT: 6: ~D() (Temporary object destructor)
-// CXX17-NEXT: 7: [B1.5].~D() (Implicit destructor)
+// CXX17-NEXT: 6: [B1.5].~D() (Implicit destructor)
void returnObjectFromMessage(E *e) {
D d = [e bar];
}
diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp
index ea028e06f3..74f4d50f25 100644
--- a/test/Analysis/cfg.cpp
+++ b/test/Analysis/cfg.cpp
@@ -203,7 +203,7 @@ namespace NoReturnSingleSuccessor {
// CHECK-LABEL: int test1(int *x)
// CHECK: 1: 1
// CHECK-NEXT: 2: return
-// CHECK-NEXT: ~B() (Implicit destructor)
+// CHECK-NEXT: ~NoReturnSingleSuccessor::B() (Implicit destructor)
// CHECK-NEXT: Preds (1)
// CHECK-NEXT: Succs (1): B0
int test1(int *x) {
@@ -477,7 +477,7 @@ void test_lifetime_extended_temporaries() {
// WARNINGS-NEXT: 1: (CXXConstructExpr, struct pr37688_deleted_union_destructor::A)
// ANALYZER-NEXT: 1: (CXXConstructExpr, [B1.2], struct pr37688_deleted_union_destructor::A)
// CHECK-NEXT: 2: pr37688_deleted_union_destructor::A a;
-// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B1.2].~pr37688_deleted_union_destructor::A() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
// CHECK: [B0 (EXIT)]
diff --git a/test/Analysis/missing-bind-temporary.cpp b/test/Analysis/missing-bind-temporary.cpp
index 7be4e2ddf3..18204929bc 100644
--- a/test/Analysis/missing-bind-temporary.cpp
+++ b/test/Analysis/missing-bind-temporary.cpp
@@ -31,7 +31,7 @@ class B {
// CHECK-NEXT: 8: [B1.7]
// CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall)
// CHECK-NEXT: 10: ~variant_0::B() (Temporary object destructor)
-// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 11: [B1.2].~variant_0::B() (Implicit destructor)
void foo(int) {
B i;
i = {};
@@ -71,7 +71,7 @@ class B {
// CHECK-NEXT: 6: {} (CXXConstructExpr, class variant_1::B)
// CHECK-NEXT: 7: [B1.6]
// CHECK-NEXT: 8: [B1.5] = [B1.7] (OperatorCall)
-// CHECK-NEXT: 9: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 9: [B1.2].~variant_1::B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
@@ -114,7 +114,7 @@ public:
// CHECK-NEXT: 9: [B1.8]
// CHECK-NEXT: 10: [B1.5] = [B1.9] (OperatorCall)
// CHECK-NEXT: 11: ~variant_2::B() (Temporary object destructor)
-// CHECK-NEXT: 12: [B1.2].~B() (Implicit destructor)
+// CHECK-NEXT: 12: [B1.2].~variant_2::B() (Implicit destructor)
template <typename T> void foo(T) {
B i;
i = {};
diff --git a/test/Analysis/more-dtors-cfg-output.cpp b/test/Analysis/more-dtors-cfg-output.cpp
new file mode 100644
index 0000000000..c0df5953aa
--- /dev/null
+++ b/test/Analysis/more-dtors-cfg-output.cpp
@@ -0,0 +1,317 @@
+// RUN: rm -f %t.14 %t.2a
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1
+// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s
+// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s
+
+int puts(const char *);
+
+struct Foo {
+ Foo() = delete;
+#if CXX2A
+ // Guarantee that the elided examples are actually elided by deleting the
+ // copy constructor.
+ Foo(const Foo &) = delete;
+#else
+ // No elision support, so we need a copy constructor.
+ Foo(const Foo &);
+#endif
+ ~Foo();
+};
+
+struct TwoFoos {
+ Foo foo1, foo2;
+ ~TwoFoos();
+};
+
+Foo get_foo();
+
+struct Bar {
+ Bar();
+ Bar(const Bar &);
+ ~Bar();
+ Bar &operator=(const Bar &);
+};
+
+Bar get_bar();
+
+struct TwoBars {
+ Bar foo1, foo2;
+ ~TwoBars();
+};
+
+// Start of tests:
+
+void elided_assign() {
+ Foo x = get_foo();
+}
+// CHECK: void elided_assign()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_assign() {
+ Bar x = (const Bar &)get_bar();
+}
+// CHECK: void nonelided_assign()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_paren_init() {
+ Foo x(get_foo());
+}
+// CHECK: void elided_paren_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_paren_init() {
+ Bar x((const Bar &)get_bar());
+}
+// CHECK: void nonelided_paren_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_brace_init() {
+ Foo x{get_foo()};
+}
+// CHECK: void elided_brace_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Implicit destructor)
+
+void nonelided_brace_init() {
+ Bar x{(const Bar &)get_bar()};
+}
+// CHECK: void nonelided_brace_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Implicit destructor)
+
+void elided_lambda_capture_init() {
+ // The copy from get_foo() into the lambda should be elided. Should call
+ // the lambda's destructor, but not ~Foo() separately.
+ // (This syntax is C++14 'generalized lambda captures'.)
+ auto z = [x=get_foo()]() {};
+}
+// CHECK: void elided_lambda_capture_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
+
+void nonelided_lambda_capture_init() {
+ // Should call the lambda's destructor as well as ~Bar() for the temporary.
+ auto z = [x((const Bar &)get_bar())]() {};
+}
+// CHECK: void nonelided_lambda_capture_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
+
+Foo elided_return_stmt_expr() {
+ // Two copies, both elided in C++17.
+ return ({ get_foo(); });
+}
+// CHECK: Foo elided_return_stmt_expr()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void elided_stmt_expr() {
+ // One copy, elided in C++17.
+ ({ get_foo(); });
+}
+// CHECK: void elided_stmt_expr()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+
+void elided_stmt_expr_multiple_stmts() {
+ // Make sure that only the value returned out of a statement expression is
+ // elided.
+ ({ get_bar(); get_foo(); });
+}
+// CHECK: void elided_stmt_expr_multiple_stmts()
+// CHECK: ~Bar() (Temporary object destructor)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+
+void unelided_stmt_expr() {
+ ({ (const Bar &)get_bar(); });
+}
+// CHECK: void unelided_stmt_expr()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+
+void elided_aggregate_init() {
+ TwoFoos x{get_foo(), get_foo()};
+}
+// CHECK: void elided_aggregate_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~TwoFoos() (Implicit destructor)
+
+void nonelided_aggregate_init() {
+ TwoBars x{(const Bar &)get_bar(), (const Bar &)get_bar()};
+}
+// CHECK: void nonelided_aggregate_init()
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~Bar() (Temporary object destructor)
+// CHECK: ~TwoBars() (Implicit destructor)
+
+TwoFoos return_aggregate_init() {
+ return TwoFoos{get_foo(), get_foo()};
+}
+// CHECK: TwoFoos return_aggregate_init()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~TwoFoos() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void lifetime_extended() {
+ const Foo &x = (get_foo(), get_foo());
+ puts("one destroyed before, one destroyed after");
+}
+// CHECK: void lifetime_extended()
+// CHECK: ~Foo() (Temporary object destructor)
+// CHECK: one destroyed before, one destroyed after
+// CHECK: ~Foo() (Implicit destructor)
+
+void not_lifetime_extended() {
+ Foo x = (get_foo(), get_foo());
+ puts("one destroyed before, one destroyed after");
+}
+// CHECK: void not_lifetime_extended()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CHECK: ~Foo() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: one destroyed before, one destroyed after
+// CHECK: ~Foo() (Implicit destructor)
+
+void compound_literal() {
+ (void)(Bar[]){{}, {}};
+}
+// CHECK: void compound_literal()
+// CHECK: (CXXConstructExpr, struct Bar)
+// CHECK: (CXXConstructExpr, struct Bar)
+// CHECK: ~Bar [2]() (Temporary object destructor)
+
+Foo elided_return() {
+ return get_foo();
+}
+// CHECK: Foo elided_return()
+// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
+// CXX14: ~Foo() (Temporary object destructor)
+
+auto elided_return_lambda() {
+ return [x=get_foo()]() {};
+}
+// CHECK: (lambda at {{.*}}) elided_return_lambda()
+// CXX14: (CXXConstructExpr{{.*}}, class (lambda at {{.*}}))
+// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
+// CXX14: ~Foo() (Temporary object destructor)
+
+void const_auto_obj() {
+ const Bar bar;
+}
+// CHECK: void const_auto_obj()
+// CHECK: .~Bar() (Implicit destructor)
+
+void has_default_arg(Foo foo = get_foo());
+void test_default_arg() {
+ // FIXME: This emits a destructor but no constructor. Search CFG.cpp for
+ // 'PR13385' for details.
+ has_default_arg();
+}
+// CHECK: void test_default_arg()
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+struct DefaultArgInCtor {
+ DefaultArgInCtor(Foo foo = get_foo());
+ ~DefaultArgInCtor();
+};
+
+void default_ctor_with_default_arg() {
+ // FIXME: Default arguments are mishandled in two ways:
+ // - The constructor is not emitted at all (not specific to arrays; see fixme
+ // in CFG.cpp that mentions PR13385).
+ // - The destructor is emitted once, even though the default argument will be
+ // constructed and destructed once per array element.
+ // Ideally, the CFG would expand array constructions into a loop that
+ // constructs each array element, allowing default argument
+ // constructor/destructor calls to be correctly placed inside the loop.
+ DefaultArgInCtor qux[3];
+}
+// CHECK: void default_ctor_with_default_arg()
+// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [3]
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+// CHECK: .~DefaultArgInCtor [3]() (Implicit destructor)
+
+void new_default_ctor_with_default_arg(long count) {
+ // Same problems as above.
+ new DefaultArgInCtor[count];
+}
+// CHECK: void new_default_ctor_with_default_arg(long count)
+// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor []
+// CXX14: ~Foo() (Temporary object destructor)
+// CHECK: ~Foo() (Temporary object destructor)
+
+#if CXX2A
+// Boilerplate needed to test co_return:
+
+namespace std::experimental {
+ template <typename Promise>
+ struct coroutine_handle {
+ static coroutine_handle from_address(void *);
+ };
+}
+
+struct TestPromise {
+ TestPromise initial_suspend();
+ TestPromise final_suspend();
+ bool await_ready();
+ void await_suspend(const std::experimental::coroutine_handle<TestPromise> &);
+ void await_resume();
+ Foo return_value(const Bar &);
+ Bar get_return_object();
+ void unhandled_exception();
+};
+
+namespace std::experimental {
+ template <typename Ret, typename... Args>
+ struct coroutine_traits;
+ template <>
+ struct coroutine_traits<Bar> {
+ using promise_type = TestPromise;
+ };
+}
+
+Bar coreturn() {
+ co_return get_bar();
+ // This expands to something like:
+ // promise.return_value(get_bar());
+ // get_bar() is passed by reference to return_value() and is then destroyed;
+ // there is no equivalent of RVO. TestPromise::return_value also returns a
+ // Foo, which should be immediately destroyed.
+ // FIXME: The generated CFG completely ignores get_return_object().
+}
+// CXX2A: Bar coreturn()
+// CXX2A: ~Foo() (Temporary object destructor)
+// CXX2A: ~Bar() (Temporary object destructor)
+#endif
diff --git a/test/Analysis/scopes-cfg-output.cpp b/test/Analysis/scopes-cfg-output.cpp
index f8d84b60de..4b6e2a92c5 100644
--- a/test/Analysis/scopes-cfg-output.cpp
+++ b/test/Analysis/scopes-cfg-output.cpp
@@ -38,7 +38,7 @@ extern const bool UV;
// CHECK-NEXT: 3: A a[2];
// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], class A [0])
// CHECK-NEXT: 5: A b[0];
-// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B1.3].~A [2]() (Implicit destructor)
// CHECK-NEXT: 7: CFGScopeEnd(a)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
@@ -810,7 +810,7 @@ void test_for_compound_and_break() {
// CHECK-NEXT: 1: CFGScopeEnd(__end1)
// CHECK-NEXT: 2: CFGScopeEnd(__begin1)
// CHECK-NEXT: 3: CFGScopeEnd(__range1)
-// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B5.3].~A [10]() (Implicit destructor)
// CHECK-NEXT: 5: CFGScopeEnd(a)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp
index 6191abfb4d..012cef52f1 100644
--- a/test/Analysis/temporaries.cpp
+++ b/test/Analysis/temporaries.cpp
@@ -830,12 +830,7 @@ void test_ternary_temporary_with_copy(int coin) {
// On each branch the variable is constructed directly.
if (coin) {
clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
-#if __cplusplus < 201703L
clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
-#else
- // FIXME: Destructor called twice in C++17?
- clang_analyzer_eval(y == 2); // expected-warning{{TRUE}}
-#endif
clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}
@@ -843,12 +838,7 @@ void test_ternary_temporary_with_copy(int coin) {
clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}
-#if __cplusplus < 201703L
clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}
-#else
- // FIXME: Destructor called twice in C++17?
- clang_analyzer_eval(w == 2); // expected-warning{{TRUE}}
-#endif
}
}
} // namespace test_match_constructors_and_destructors
@@ -1055,16 +1045,11 @@ void foo(void (*bar4)(S)) {
#endif
bar2(S(2));
- // FIXME: Why are we losing information in C++17?
clang_analyzer_eval(glob == 2);
#ifdef TEMPORARY_DTORS
-#if __cplusplus < 201703L
- // expected-warning@-3{{TRUE}}
-#else
- // expected-warning@-5{{UNKNOWN}}
-#endif
+ // expected-warning@-2{{TRUE}}
#else
- // expected-warning@-8{{UNKNOWN}}
+ // expected-warning@-4{{UNKNOWN}}
#endif
C *c = new D();