Refine CFG so that '&&' and '||' don't lead to extra confluence points when used in a branch, but

instead push the terminator for the branch down into the basic blocks of the subexpressions of '&&' and '||'
respectively.  This eliminates some artifical control-flow from the CFG and results in a more
compact CFG.

Note that this patch only alters the branches 'while', 'if' and 'for'.  This was complex enough for
one patch.  The remaining branches (e.g., do...while) can be handled in a separate patch, but they
weren't immediately tackled because they were less important.

It is possible that this patch introduces some subtle bugs, particularly w.r.t. to destructor placement.
I've tried to audit these changes, but it is also known that the destructor logic needs some refinement
in the area of '||' and '&&' regardless (i.e., their are known bugs).

llvm-svn: 160218
This commit is contained in:
Ted Kremenek 2012-07-14 05:04:10 +00:00
parent a16436f51e
commit b50e716bac
8 changed files with 634 additions and 427 deletions

View File

@ -344,6 +344,10 @@ private:
CFGBlock *VisitLabelStmt(LabelStmt *L);
CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc);
CFGBlock *VisitLogicalOperator(BinaryOperator *B);
std::pair<CFGBlock *, CFGBlock *> VisitLogicalOperator(BinaryOperator *B,
Stmt *Term,
CFGBlock *TrueBlock,
CFGBlock *FalseBlock);
CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc);
CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S);
@ -1170,46 +1174,98 @@ CFGBlock *CFGBuilder::VisitLogicalOperator(BinaryOperator *B) {
if (badCFG)
return 0;
// create the block evaluating the LHS
return VisitLogicalOperator(B, 0, ConfluenceBlock, ConfluenceBlock).first;
}
std::pair<CFGBlock*, CFGBlock*>
CFGBuilder::VisitLogicalOperator(BinaryOperator *B,
Stmt *Term,
CFGBlock *TrueBlock,
CFGBlock *FalseBlock) {
// Introspect the RHS. If it is a nested logical operation, we recursively
// build the CFG using this function. Otherwise, resort to default
// CFG construction behavior.
Expr *RHS = B->getRHS()->IgnoreParens();
CFGBlock *RHSBlock, *ExitBlock;
do {
if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS))
if (B_RHS->isLogicalOp()) {
llvm::tie(RHSBlock, ExitBlock) =
VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock);
break;
}
// The RHS is not a nested logical operation. Don't push the terminator
// down further, but instead visit RHS and construct the respective
// pieces of the CFG, and link up the RHSBlock with the terminator
// we have been provided.
ExitBlock = RHSBlock = createBlock(false);
if (!Term) {
assert(TrueBlock == FalseBlock);
addSuccessor(RHSBlock, TrueBlock);
}
else {
RHSBlock->setTerminator(Term);
TryResult KnownVal = tryEvaluateBool(RHS);
addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
}
Block = RHSBlock;
RHSBlock = addStmt(RHS);
}
while (false);
if (badCFG)
return std::make_pair((CFGBlock*)0, (CFGBlock*)0);
// Generate the blocks for evaluating the LHS.
Expr *LHS = B->getLHS()->IgnoreParens();
if (BinaryOperator *B_LHS = dyn_cast<BinaryOperator>(LHS))
if (B_LHS->isLogicalOp()) {
if (B->getOpcode() == BO_LOr)
FalseBlock = RHSBlock;
else
TrueBlock = RHSBlock;
// For the LHS, treat 'B' as the terminator that we want to sink
// into the nested branch. The RHS always gets the top-most
// terminator.
return VisitLogicalOperator(B_LHS, B, TrueBlock, FalseBlock);
}
// Create the block evaluating the LHS.
// This contains the '&&' or '||' as the terminator.
CFGBlock *LHSBlock = createBlock(false);
LHSBlock->setTerminator(B);
// create the block evaluating the RHS
Succ = ConfluenceBlock;
Block = NULL;
CFGBlock *RHSBlock = addStmt(B->getRHS());
if (RHSBlock) {
if (badCFG)
return 0;
} else {
// Create an empty block for cases where the RHS doesn't require
// any explicit statements in the CFG.
RHSBlock = createBlock();
}
// Generate the blocks for evaluating the LHS.
Block = LHSBlock;
CFGBlock *EntryLHSBlock = addStmt(B->getLHS());
CFGBlock *EntryLHSBlock = addStmt(LHS);
if (badCFG)
return std::make_pair((CFGBlock*)0, (CFGBlock*)0);
// See if this is a known constant.
TryResult KnownVal = tryEvaluateBool(B->getLHS());
if (KnownVal.isKnown() && (B->getOpcode() == BO_LOr))
KnownVal.negate();
TryResult KnownVal = tryEvaluateBool(LHS);
// Now link the LHSBlock with RHSBlock.
if (B->getOpcode() == BO_LOr) {
addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock);
addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock);
} else {
assert(B->getOpcode() == BO_LAnd);
addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock);
addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
}
return EntryLHSBlock;
return std::make_pair(EntryLHSBlock, ExitBlock);
}
CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
AddStmtChoice asc) {
// && or ||
@ -1646,6 +1702,18 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
}
}
// Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by
// having these handle the actual control-flow jump. Note that
// if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)"
// we resort to the old control-flow behavior. This special handling
// removes infeasible paths from the control-flow graph by having the
// control-flow transfer of '&&' or '||' go directly into the then/else
// blocks directly.
if (!I->getConditionVariable())
if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(I->getCond()))
if (Cond->isLogicalOp())
return VisitLogicalOperator(Cond, I, ThenBlock, ElseBlock).first;
// Now create a new block containing the if statement.
Block = createBlock(false);
@ -1796,24 +1864,92 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
CFGBlock *BodyBlock = 0, *TransitionBlock = 0;
// Now create the loop body.
{
assert(F->getBody());
// Save the current values for Block, Succ, continue and break targets.
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget);
// Create an empty block to represent the transition block for looping back
// to the head of the loop. If we have increment code, it will
// go in this block as well.
Block = Succ = TransitionBlock = createBlock(false);
TransitionBlock->setLoopTarget(F);
if (Stmt *I = F->getInc()) {
// Generate increment code in its own basic block. This is the target of
// continue statements.
Succ = addStmt(I);
}
// Finish up the increment (or empty) block if it hasn't been already.
if (Block) {
assert(Block == Succ);
if (badCFG)
return 0;
Block = 0;
}
// The starting block for the loop increment is the block that should
// represent the 'loop target' for looping back to the start of the loop.
ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos);
ContinueJumpTarget.block->setLoopTarget(F);
// Loop body should end with destructor of Condition variable (if any).
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(F->getBody()))
addLocalScopeAndDtors(F->getBody());
// Now populate the body block, and in the process create new blocks as we
// walk the body of the loop.
BodyBlock = addStmt(F->getBody());
if (!BodyBlock) {
// In the case of "for (...;...;...);" we can have a null BodyBlock.
// Use the continue jump target as the proxy for the body.
BodyBlock = ContinueJumpTarget.block;
}
else if (badCFG)
return 0;
}
// Because of short-circuit evaluation, the condition of the loop can span
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
// evaluate the condition.
CFGBlock *ExitConditionBlock = createBlock(false);
CFGBlock *EntryConditionBlock = ExitConditionBlock;
CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0;
// Set the terminator for the "exit" condition block.
do {
Expr *C = F->getCond();
// Specially handle logical operators, which have a slightly
// more optimal CFG representation.
if (BinaryOperator *Cond = dyn_cast_or_null<BinaryOperator>(C))
if (Cond->isLogicalOp()) {
llvm::tie(EntryConditionBlock, ExitConditionBlock) =
VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor);
break;
}
// The default case when not handling logical operators.
EntryConditionBlock = ExitConditionBlock = createBlock(false);
ExitConditionBlock->setTerminator(F);
// Now add the actual condition to the condition block. Because the condition
// itself may contain control-flow, new blocks may be created.
if (Stmt *C = F->getCond()) {
// See if this is a known constant.
TryResult KnownVal(true);
if (C) {
// Now add the actual condition to the condition block.
// Because the condition itself may contain control-flow, new blocks may
// be created. Thus we update "Succ" after adding the condition.
Block = ExitConditionBlock;
EntryConditionBlock = addStmt(C);
if (badCFG)
return 0;
assert(Block == EntryConditionBlock ||
(Block == 0 && EntryConditionBlock == Succ));
// If this block contains a condition variable, add both the condition
// variable and initializer to the CFG.
@ -1826,83 +1962,26 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
}
}
if (Block) {
if (badCFG)
return 0;
}
}
// The condition block is the implicit successor for the loop body as well as
// any code above the loop.
Succ = EntryConditionBlock;
// See if this is a known constant.
TryResult KnownVal(true);
if (F->getCond())
KnownVal = tryEvaluateBool(F->getCond());
// Now create the loop body.
{
assert(F->getBody());
// Save the current values for Block, Succ, and continue targets.
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget);
// Create a new block to contain the (bottom) of the loop body.
Block = NULL;
// Loop body should end with destructor of Condition variable (if any).
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F);
if (Stmt *I = F->getInc()) {
// Generate increment code in its own basic block. This is the target of
// continue statements.
Succ = addStmt(I);
} else {
// No increment code. Create a special, empty, block that is used as the
// target block for "looping back" to the start of the loop.
assert(Succ == EntryConditionBlock);
Succ = Block ? Block : createBlock();
}
// Finish up the increment (or empty) block if it hasn't been already.
if (Block) {
assert(Block == Succ);
if (badCFG)
return 0;
Block = 0;
}
ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos);
// The starting block for the loop increment is the block that should
// represent the 'loop target' for looping back to the start of the loop.
ContinueJumpTarget.block->setLoopTarget(F);
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(F->getBody()))
addLocalScopeAndDtors(F->getBody());
// Now populate the body block, and in the process create new blocks as we
// walk the body of the loop.
CFGBlock *BodyBlock = addStmt(F->getBody());
if (!BodyBlock)
BodyBlock = ContinueJumpTarget.block;//can happen for "for (...;...;...);"
else if (badCFG)
if (Block && badCFG)
return 0;
// This new body block is a successor to our "exit" condition block.
KnownVal = tryEvaluateBool(C);
}
// Add the loop body entry as a successor to the condition.
addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock);
}
// Link up the condition block with the code that follows the loop. (the
// false branch).
addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
} while (false);
// Link up the loop-back block to the entry condition block.
addSuccessor(TransitionBlock, EntryConditionBlock);
// The condition block is the implicit successor for any code above the loop.
Succ = EntryConditionBlock;
// If the loop contains initialization, create a new block for those
// statements. This block can also contain statements that precede the loop.
if (Stmt *I = F->getInit()) {
@ -2109,26 +2188,74 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
return 0;
LoopSuccessor = Block;
Block = 0;
} else
} else {
LoopSuccessor = Succ;
}
CFGBlock *BodyBlock = 0, *TransitionBlock = 0;
// Process the loop body.
{
assert(W->getBody());
// Save the current values for Block, Succ, continue and break targets.
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),
save_break(BreakJumpTarget);
// Create an empty block to represent the transition block for looping back
// to the head of the loop.
Succ = TransitionBlock = createBlock(false);
TransitionBlock->setLoopTarget(W);
ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos);
// All breaks should go to the code following the loop.
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
// Loop body should end with destructor of Condition variable (if any).
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(W->getBody()))
addLocalScopeAndDtors(W->getBody());
// Create the body. The returned block is the entry to the loop body.
BodyBlock = addStmt(W->getBody());
if (!BodyBlock)
BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;"
else if (Block && badCFG)
return 0;
}
// Because of short-circuit evaluation, the condition of the loop can span
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
// evaluate the condition.
CFGBlock *ExitConditionBlock = createBlock(false);
CFGBlock *EntryConditionBlock = ExitConditionBlock;
CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0;
// Set the terminator for the "exit" condition block.
do {
Expr *C = W->getCond();
// Specially handle logical operators, which have a slightly
// more optimal CFG representation.
if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C))
if (Cond->isLogicalOp()) {
llvm::tie(EntryConditionBlock, ExitConditionBlock) =
VisitLogicalOperator(Cond, W, BodyBlock,
LoopSuccessor);
break;
}
// The default case when not handling logical operators.
EntryConditionBlock = ExitConditionBlock = createBlock(false);
ExitConditionBlock->setTerminator(W);
// Now add the actual condition to the condition block. Because the condition
// itself may contain control-flow, new blocks may be created. Thus we update
// "Succ" after adding the condition.
if (Stmt *C = W->getCond()) {
// Now add the actual condition to the condition block.
// Because the condition itself may contain control-flow, new blocks may
// be created. Thus we update "Succ" after adding the condition.
Block = ExitConditionBlock;
EntryConditionBlock = addStmt(C);
// The condition might finish the current 'Block'.
Block = EntryConditionBlock;
Block = EntryConditionBlock = addStmt(C);
// If this block contains a condition variable, add both the condition
// variable and initializer to the CFG.
@ -2141,68 +2268,23 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
}
}
if (Block) {
if (badCFG)
if (Block && badCFG)
return 0;
}
}
// The condition block is the implicit successor for the loop body as well as
// any code above the loop.
Succ = EntryConditionBlock;
// See if this is a known constant.
const TryResult& KnownVal = tryEvaluateBool(W->getCond());
// Process the loop body.
{
assert(W->getBody());
// Save the current values for Block, Succ, and continue and break targets
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ);
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget),
save_break(BreakJumpTarget);
// Create an empty block to represent the transition block for looping back
// to the head of the loop.
Block = 0;
assert(Succ == EntryConditionBlock);
Succ = createBlock();
Succ->setLoopTarget(W);
ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos);
// All breaks should go to the code following the loop.
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);
// NULL out Block to force lazy instantiation of blocks for the body.
Block = NULL;
// Loop body should end with destructor of Condition variable (if any).
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(W->getBody()))
addLocalScopeAndDtors(W->getBody());
// Create the body. The returned block is the entry to the loop body.
CFGBlock *BodyBlock = addStmt(W->getBody());
if (!BodyBlock)
BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;"
else if (Block) {
if (badCFG)
return 0;
}
const TryResult& KnownVal = tryEvaluateBool(C);
// Add the loop body entry as a successor to the condition.
addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock);
}
// Link up the condition block with the code that follows the loop. (the
// false branch).
addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor);
} while(false);
// Link up the loop-back block to the entry condition block.
addSuccessor(TransitionBlock, EntryConditionBlock);
// There can be no more statements in the condition block since we loop back
// to this block. NULL out Block to force lazy creation of another block.
Block = NULL;

View File

@ -1208,6 +1208,45 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
return state->getSVal(Ex, LCtx);
}
static const Stmt *ResolveCondition(const Stmt *Condition,
const CFGBlock *B) {
if (const Expr *Ex = dyn_cast<Expr>(Condition))
Condition = Ex->IgnoreParens();
const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition);
if (!BO || !BO->isLogicalOp())
return Condition;
// For logical operations, we still have the case where some branches
// use the traditional "merge" approach and others sink the branch
// directly into the basic blocks representing the logical operation.
// We need to distinguish between those two cases here.
// The invariants are still shifting, but it is possible that the
// last element in a CFGBlock is not a CFGStmt. Look for the last
// CFGStmt as the value of the condition.
CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
for (; I != E; ++I) {
CFGElement Elem = *I;
CFGStmt *CS = dyn_cast<CFGStmt>(&Elem);
if (!CS)
continue;
if (CS->getStmt() != Condition)
break;
return Condition;
}
assert(I != E);
while (Condition) {
BO = dyn_cast<BinaryOperator>(Condition);
if (!BO || !BO->isLogicalOp())
return Condition;
Condition = BO->getRHS()->IgnoreParens();
}
llvm_unreachable("could not resolve condition");
}
void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
NodeBuilderContext& BldCtx,
ExplodedNode *Pred,
@ -1224,6 +1263,12 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
return;
}
// Resolve the condition in the precense of nested '||' and '&&'.
if (const Expr *Ex = dyn_cast<Expr>(Condition))
Condition = Ex->IgnoreParens();
Condition = ResolveCondition(Condition, BldCtx.getBlock());
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Condition->getLocStart(),
"Error evaluating branch");

View File

@ -499,48 +499,43 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
SVal X = state->getSVal(B, LCtx);
assert(X.isUndef());
const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
assert(Ex);
if (Ex == B->getRHS()) {
X = state->getSVal(Ex, LCtx);
// Handle undefined values.
if (X.isUndef()) {
Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
return;
ExplodedNode *N = Pred;
while (!isa<BlockEntrance>(N->getLocation())) {
ProgramPoint P = N->getLocation();
assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P));
(void) P;
assert(N->pred_size() == 1);
N = *N->pred_begin();
}
assert(N->pred_size() == 1);
N = *N->pred_begin();
BlockEdge BE = cast<BlockEdge>(N->getLocation());
SVal X;
DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
// We took the RHS. Because the value of the '&&' or '||' expression must
// evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
// or 1. Alternatively, we could take a lazy approach, and calculate this
// value later when necessary. We don't have the machinery in place for
// this right now, and since most logical expressions are used for branches,
// the payoff is not likely to be large. Instead, we do eager evaluation.
if (ProgramStateRef newState = state->assume(XD, true))
Bldr.generateNode(B, Pred,
newState->BindExpr(B, LCtx,
svalBuilder.makeIntVal(1U, B->getType())));
if (ProgramStateRef newState = state->assume(XD, false))
Bldr.generateNode(B, Pred,
newState->BindExpr(B, LCtx,
svalBuilder.makeIntVal(0U, B->getType())));
// Determine the value of the expression by introspecting how we
// got this location in the CFG. This requires looking at the previous
// block we were in and what kind of control-flow transfer was involved.
const CFGBlock *SrcBlock = BE.getSrc();
// The only terminator (if there is one) that makes sense is a logical op.
CFGTerminator T = SrcBlock->getTerminator();
if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) {
assert(Term->isLogicalOp());
assert(SrcBlock->succ_size() == 2);
// Did we take the true or false branch?
unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0;
X = svalBuilder.makeIntVal(constant, B->getType());
}
else {
// We took the LHS expression. Depending on whether we are '&&' or
// '||' we know what the value of the expression is via properties of
// the short-circuiting.
X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
B->getType());
Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
// If there is no terminator, by construction the last statement
// in SrcBlock is the value of the enclosing expression.
assert(!SrcBlock->empty());
CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin());
const Stmt *S = Elem.getStmt();
X = N->getState()->getSVal(S, Pred->getLocationContext());
}
Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
}
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,

View File

@ -416,121 +416,121 @@ void test_catch_copy() {
// CHECK: [B6 (ENTRY)]
// CHECK: Succs (1): B5
// CHECK: [B1]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: 1: [B4.4].~A() (Implicit destructor)
// CHECK: 2: [B5.2].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Preds (1): B4
// CHECK: Succs (1): B0
// CHECK: [B2]
// CHECK: 1: a
// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B2.6].operator int
// CHECK: 8: [B2.7]()
// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: while [B2.10]
// CHECK: Preds (2): B3 B5
// CHECK: Succs (2): B4 B1
// CHECK: Preds (1): B3
// CHECK: Succs (1): B4
// CHECK: [B3]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: [B3.2].~A() (Implicit destructor)
// CHECK: 4: [B4.4].~A() (Implicit destructor)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: [B4.2].~A() (Implicit destructor)
// CHECK: 4: [B2.4].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B3
// CHECK: 1: a
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B4.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B4.6].operator int
// CHECK: 8: [B4.7]()
// CHECK: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: while [B4.10]
// CHECK: Preds (2): B2 B5
// CHECK: Succs (2): B3 B1
// CHECK: [B5]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B6
// CHECK: Succs (1): B2
// CHECK: Succs (1): B4
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B12 (ENTRY)]
// CHECK: Succs (1): B11
// CHECK: [B1]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: 1: [B10.4].~A() (Implicit destructor)
// CHECK: 2: (CXXConstructExpr, class A)
// CHECK: 3: A e;
// CHECK: 4: [B1.3].~A() (Implicit destructor)
// CHECK: 5: [B11.2].~A() (Implicit destructor)
// CHECK: Preds (2): B9 B2
// CHECK: Preds (2): B8 B10
// CHECK: Succs (1): B0
// CHECK: [B2]
// CHECK: 1: a
// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B2.6].operator int
// CHECK: 8: [B2.7]()
// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: while [B2.10]
// CHECK: Preds (2): B3 B11
// CHECK: Succs (2): B10 B1
// CHECK: Preds (2): B3 B6
// CHECK: Succs (1): B10
// CHECK: [B3]
// CHECK: Preds (2): B4 B7
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A d;
// CHECK: 3: [B4.2].~A() (Implicit destructor)
// CHECK: 4: [B10.2].~A() (Implicit destructor)
// CHECK: 5: [B2.4].~A() (Implicit destructor)
// CHECK: Preds (1): B6
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 3: [B3.2].~A() (Implicit destructor)
// CHECK: 4: [B9.2].~A() (Implicit destructor)
// CHECK: 5: [B10.4].~A() (Implicit destructor)
// CHECK: Preds (1): B5
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: return;
// CHECK: 2: [B10.2].~A() (Implicit destructor)
// CHECK: 3: [B2.4].~A() (Implicit destructor)
// CHECK: 2: [B9.2].~A() (Implicit destructor)
// CHECK: 3: [B10.4].~A() (Implicit destructor)
// CHECK: 4: [B11.2].~A() (Implicit destructor)
// CHECK: Preds (1): B6
// CHECK: Preds (1): B5
// CHECK: Succs (1): B0
// CHECK: [B5]
// CHECK: 1: UV
// CHECK: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B5.2]
// CHECK: Preds (1): B7
// CHECK: Succs (2): B4 B3
// CHECK: [B6]
// CHECK: 1: UV
// CHECK: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B6.2]
// CHECK: Preds (1): B8
// CHECK: Succs (2): B5 B4
// CHECK: [B7]
// CHECK: 1: [B10.2].~A() (Implicit destructor)
// CHECK: 2: [B2.4].~A() (Implicit destructor)
// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: 2: [B10.4].~A() (Implicit destructor)
// CHECK: T: continue;
// CHECK: Preds (1): B8
// CHECK: Succs (1): B3
// CHECK: [B8]
// CHECK: Preds (1): B7
// CHECK: Succs (1): B2
// CHECK: [B7]
// CHECK: 1: UV
// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B8.2]
// CHECK: Preds (1): B10
// CHECK: Succs (2): B7 B6
// CHECK: [B9]
// CHECK: 1: [B10.2].~A() (Implicit destructor)
// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B7.2]
// CHECK: Preds (1): B9
// CHECK: Succs (2): B6 B5
// CHECK: [B8]
// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: break;
// CHECK: Preds (1): B10
// CHECK: Preds (1): B9
// CHECK: Succs (1): B1
// CHECK: [B10]
// CHECK: [B9]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: UV
// CHECK: 4: [B10.3] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B10.4]
// CHECK: Preds (1): B2
// CHECK: Succs (2): B9 B8
// CHECK: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B9.4]
// CHECK: Preds (1): B10
// CHECK: Succs (2): B8 B7
// CHECK: [B10]
// CHECK: 1: a
// CHECK: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B10.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B10.6].operator int
// CHECK: 8: [B10.7]()
// CHECK: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: while [B10.10]
// CHECK: Preds (2): B2 B11
// CHECK: Succs (2): B9 B1
// CHECK: [B11]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B12
// CHECK: Succs (1): B2
// CHECK: Succs (1): B10
// CHECK: [B0 (EXIT)]
// CHECK: Preds (2): B1 B5
// CHECK: Preds (2): B1 B4
// CHECK: [B4 (ENTRY)]
// CHECK: Succs (1): B2
// CHECK: [B1]
@ -717,124 +717,124 @@ void test_catch_copy() {
// CHECK: [B6 (ENTRY)]
// CHECK: Succs (1): B5
// CHECK: [B1]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: 1: [B4.4].~A() (Implicit destructor)
// CHECK: 2: [B5.2].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Preds (1): B4
// CHECK: Succs (1): B0
// CHECK: [B2]
// CHECK: 1: a
// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B2.6].operator int
// CHECK: 8: [B2.7]()
// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: for (...; [B2.10]; )
// CHECK: Preds (2): B3 B5
// CHECK: Succs (2): B4 B1
// CHECK: Preds (1): B3
// CHECK: Succs (1): B4
// CHECK: [B3]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: [B3.2].~A() (Implicit destructor)
// CHECK: 4: [B4.4].~A() (Implicit destructor)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A c;
// CHECK: 3: [B4.2].~A() (Implicit destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B3
// CHECK: 1: a
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B4.2] (CXXConstructExpr, class A)
// CHECK: 4: A b = a;
// CHECK: 5: b
// CHECK: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B4.6].operator int
// CHECK: 8: [B4.7]()
// CHECK: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: for (...; [B4.10]; )
// CHECK: Preds (2): B2 B5
// CHECK: Succs (2): B3 B1
// CHECK: [B5]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: Preds (1): B6
// CHECK: Succs (1): B2
// CHECK: Succs (1): B4
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B12 (ENTRY)]
// CHECK: Succs (1): B11
// CHECK: [B1]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: 1: [B10.4].~A() (Implicit destructor)
// CHECK: 2: [B11.4].~A() (Implicit destructor)
// CHECK: 3: (CXXConstructExpr, class A)
// CHECK: 4: A f;
// CHECK: 5: [B1.4].~A() (Implicit destructor)
// CHECK: 6: [B11.2].~A() (Implicit destructor)
// CHECK: Preds (2): B9 B2
// CHECK: Preds (2): B8 B10
// CHECK: Succs (1): B0
// CHECK: [B2]
// CHECK: 1: b
// CHECK: 2: [B2.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B2.2] (CXXConstructExpr, class A)
// CHECK: 4: A c = b;
// CHECK: 5: c
// CHECK: 6: [B2.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B2.6].operator int
// CHECK: 8: [B2.7]()
// CHECK: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B2.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: for (...; [B2.10]; )
// CHECK: Preds (2): B3 B11
// CHECK: Succs (2): B10 B1
// CHECK: Preds (2): B3 B6
// CHECK: Succs (1): B10
// CHECK: [B3]
// CHECK: 1: [B2.4].~A() (Implicit destructor)
// CHECK: Preds (2): B4 B7
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A e;
// CHECK: 3: [B4.2].~A() (Implicit destructor)
// CHECK: 4: [B10.2].~A() (Implicit destructor)
// CHECK: Preds (1): B6
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 3: [B3.2].~A() (Implicit destructor)
// CHECK: 4: [B9.2].~A() (Implicit destructor)
// CHECK: 5: [B10.4].~A() (Implicit destructor)
// CHECK: Preds (1): B5
// CHECK: Succs (1): B2
// CHECK: [B4]
// CHECK: 1: return;
// CHECK: 2: [B10.2].~A() (Implicit destructor)
// CHECK: 3: [B2.4].~A() (Implicit destructor)
// CHECK: 2: [B9.2].~A() (Implicit destructor)
// CHECK: 3: [B10.4].~A() (Implicit destructor)
// CHECK: 4: [B11.4].~A() (Implicit destructor)
// CHECK: 5: [B11.2].~A() (Implicit destructor)
// CHECK: Preds (1): B6
// CHECK: Preds (1): B5
// CHECK: Succs (1): B0
// CHECK: [B5]
// CHECK: 1: UV
// CHECK: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B5.2]
// CHECK: Preds (1): B7
// CHECK: Succs (2): B4 B3
// CHECK: [B6]
// CHECK: 1: UV
// CHECK: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B6.2]
// CHECK: Preds (1): B8
// CHECK: Succs (2): B5 B4
// CHECK: [B7]
// CHECK: 1: [B10.2].~A() (Implicit destructor)
// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: continue;
// CHECK: Preds (1): B8
// CHECK: Succs (1): B3
// CHECK: [B8]
// CHECK: Preds (1): B7
// CHECK: Succs (1): B2
// CHECK: [B7]
// CHECK: 1: UV
// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B8.2]
// CHECK: Preds (1): B10
// CHECK: Succs (2): B7 B6
// CHECK: [B9]
// CHECK: 1: [B10.2].~A() (Implicit destructor)
// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B7.2]
// CHECK: Preds (1): B9
// CHECK: Succs (2): B6 B5
// CHECK: [B8]
// CHECK: 1: [B9.2].~A() (Implicit destructor)
// CHECK: T: break;
// CHECK: Preds (1): B10
// CHECK: Preds (1): B9
// CHECK: Succs (1): B1
// CHECK: [B10]
// CHECK: [B9]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A d;
// CHECK: 3: UV
// CHECK: 4: [B10.3] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B10.4]
// CHECK: Preds (1): B2
// CHECK: Succs (2): B9 B8
// CHECK: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK: T: if [B9.4]
// CHECK: Preds (1): B10
// CHECK: Succs (2): B8 B7
// CHECK: [B10]
// CHECK: 1: b
// CHECK: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B10.2] (CXXConstructExpr, class A)
// CHECK: 4: A c = b;
// CHECK: 5: c
// CHECK: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 7: [B10.6].operator int
// CHECK: 8: [B10.7]()
// CHECK: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK: T: for (...; [B10.10]; )
// CHECK: Preds (2): B2 B11
// CHECK: Succs (2): B9 B1
// CHECK: [B11]
// CHECK: 1: (CXXConstructExpr, class A)
// CHECK: 2: A a;
// CHECK: 3: (CXXConstructExpr, class A)
// CHECK: 4: A b;
// CHECK: Preds (1): B12
// CHECK: Succs (1): B2
// CHECK: Succs (1): B10
// CHECK: [B0 (EXIT)]
// CHECK: Preds (2): B1 B5
// CHECK: Preds (2): B1 B4
// CHECK: [B3 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B1]
@ -862,3 +862,4 @@ void test_catch_copy() {
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (3): B2 B1 B3

View File

@ -1,4 +1,6 @@
// RUN: %clang -cc1 -analyze -analyzer-checker=debug.DumpDominators %s 2>&1 | FileCheck %s
// RUN: rm -f %t
// RUN: %clang -cc1 -analyze -analyzer-checker=debug.DumpDominators %s > %t 2>&1
// RUN: FileCheck --input-file=%t %s
// Test the DominatorsTree implementation with various control flows
int test1()
@ -24,13 +26,13 @@ int test1()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,2)
// CHECK: (2,8)
// CHECK: (3,4)
// CHECK: (4,7)
// CHECK: (5,7)
// CHECK: (1,7)
// CHECK: (2,3)
// CHECK: (3,6)
// CHECK: (4,6)
// CHECK: (5,6)
// CHECK: (6,7)
// CHECK: (7,2)
// CHECK: (7,8)
// CHECK: (8,9)
// CHECK: (9,9)
@ -55,9 +57,9 @@ int test2()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,6)
// CHECK: (2,6)
// CHECK: (2,3)
// CHECK: (3,4)
// CHECK: (4,2)
// CHECK: (4,6)
// CHECK: (5,6)
// CHECK: (6,7)
// CHECK: (7,7)
@ -83,11 +85,11 @@ int test3()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,7)
// CHECK: (2,7)
// CHECK: (2,5)
// CHECK: (3,4)
// CHECK: (4,2)
// CHECK: (4,5)
// CHECK: (5,6)
// CHECK: (6,4)
// CHECK: (6,7)
// CHECK: (7,8)
// CHECK: (8,8)
@ -108,16 +110,16 @@ int test4()
// CHECK: Immediate dominance tree (Node#,IDom#):
// CHECK: (0,1)
// CHECK: (1,2)
// CHECK: (2,11)
// CHECK: (3,10)
// CHECK: (4,10)
// CHECK: (5,6)
// CHECK: (6,4)
// CHECK: (7,10)
// CHECK: (1,10)
// CHECK: (2,9)
// CHECK: (3,4)
// CHECK: (4,5)
// CHECK: (5,9)
// CHECK: (6,7)
// CHECK: (7,8)
// CHECK: (8,9)
// CHECK: (9,7)
// CHECK: (10,2)
// CHECK: (9,10)
// CHECK: (10,11)
// CHECK: (11,12)
// CHECK: (12,12)
@ -163,3 +165,4 @@ int test5()
// CHECK: (10,11)
// CHECK: (11,11)

View File

@ -1,4 +1,6 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -cfg-add-implicit-dtors -cfg-add-initializers %s 2>&1 | FileCheck %s
// RUN: rm -f %t
// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -cfg-add-implicit-dtors -cfg-add-initializers %s > %t 2>&1
// RUN: FileCheck --input-file=%t %s
// XPASS: *
class A {
@ -106,9 +108,88 @@ TestCtorInits::TestCtorInits()
: a(int(A()) + int(B()))
, b() {}
// CHECK: [B1 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B1 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B1.1] (BindTemporary)
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 4: [B1.3]
// CHECK: 5: [B1.4] (CXXConstructExpr, class A)
// CHECK: 6: ~A() (Temporary object destructor)
// CHECK: 7: return [B1.5];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: false
// CHECK: 2: return [B1.1];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: 0
// CHECK: 2: return [B1.1];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B1 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B1 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: true
// CHECK: 2: return [B1.1];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: 1
// CHECK: 2: return [B1.1];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B1.1] (BindTemporary)
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 4: [B1.3]
// CHECK: 5: [B1.4] (CXXConstructExpr, class A)
// CHECK: 6: ~A() (Temporary object destructor)
// CHECK: 7: return [B1.5];
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B1.1] (BindTemporary)
@ -150,7 +231,7 @@ TestCtorInits::TestCtorInits()
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B10 (ENTRY)]
// CHECK: Succs (1): B8
// CHECK: Succs (1): B9
// CHECK: [B1]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: int b;
@ -161,62 +242,62 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
// CHECK: 1: [B4.6] && [B5.5]
// CHECK: 1: [B5.6] && [B4.5]
// CHECK: 2: foo
// CHECK: 3: [B3.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: [B3.3]([B3.1])
// CHECK: T: [B4.6] && ...
// CHECK: Preds (2): B5 B4
// CHECK: T: [B5.6] && ...
// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B2 B1
// CHECK: [B4]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B4.1] (BindTemporary)
// CHECK: 3: [B4.2].operator _Bool
// CHECK: 4: [B4.3]()
// CHECK: 5: [B4.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B5
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
// CHECK: 3: [B4.2] (BindTemporary)
// CHECK: 4: [B4.3].operator _Bool
// CHECK: 5: [B4.4]()
// CHECK: 6: [B4.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B4.6] && ...
// CHECK: 3: [B5.2] (BindTemporary)
// CHECK: 4: [B5.3].operator _Bool
// CHECK: 5: [B5.4]()
// CHECK: 6: [B5.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B5.6] && ...
// CHECK: Preds (2): B6 B7
// CHECK: Succs (2): B5 B3
// CHECK: [B5]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B5.1] (BindTemporary)
// CHECK: 3: [B5.2].operator _Bool
// CHECK: 4: [B5.3]()
// CHECK: 5: [B5.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B3
// CHECK: Succs (2): B4 B3
// CHECK: [B6]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: Preds (1): B7
// CHECK: Succs (1): B4
// CHECK: Succs (1): B5
// CHECK: [B7]
// CHECK: 1: [B8.5] && [B9.5]
// CHECK: 1: [B9.5] && [B8.5]
// CHECK: 2: bool a = A().operator _Bool() && B().operator _Bool();
// CHECK: T: [B8.5] && ...
// CHECK: Preds (2): B9 B8
// CHECK: Succs (2): B6 B4
// CHECK: T: [B9.5] && ...
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B6 B5
// CHECK: [B8]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B8.1] (BindTemporary)
// CHECK: 3: [B8.2].operator _Bool
// CHECK: 4: [B8.3]()
// CHECK: 5: [B8.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B8.5] && ...
// CHECK: Preds (1): B10
// CHECK: Succs (2): B9 B7
// CHECK: Preds (1): B9
// CHECK: Succs (1): B7
// CHECK: [B9]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B9.1] (BindTemporary)
// CHECK: 3: [B9.2].operator _Bool
// CHECK: 4: [B9.3]()
// CHECK: 5: [B9.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B8
// CHECK: Succs (1): B7
// CHECK: T: [B9.5] && ...
// CHECK: Preds (1): B10
// CHECK: Succs (2): B8 B7
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B10 (ENTRY)]
// CHECK: Succs (1): B8
// CHECK: Succs (1): B9
// CHECK: [B1]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: int b;
@ -227,58 +308,58 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
// CHECK: 1: [B4.6] || [B5.5]
// CHECK: 1: [B5.6] || [B4.5]
// CHECK: 2: foo
// CHECK: 3: [B3.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: [B3.3]([B3.1])
// CHECK: T: [B4.6] || ...
// CHECK: Preds (2): B5 B4
// CHECK: T: [B5.6] || ...
// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B1 B2
// CHECK: [B4]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B4.1] (BindTemporary)
// CHECK: 3: [B4.2].operator _Bool
// CHECK: 4: [B4.3]()
// CHECK: 5: [B4.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B5
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
// CHECK: 3: [B4.2] (BindTemporary)
// CHECK: 4: [B4.3].operator _Bool
// CHECK: 5: [B4.4]()
// CHECK: 6: [B4.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B4.6] || ...
// CHECK: 3: [B5.2] (BindTemporary)
// CHECK: 4: [B5.3].operator _Bool
// CHECK: 5: [B5.4]()
// CHECK: 6: [B5.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B5.6] || ...
// CHECK: Preds (2): B6 B7
// CHECK: Succs (2): B3 B5
// CHECK: [B5]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B5.1] (BindTemporary)
// CHECK: 3: [B5.2].operator _Bool
// CHECK: 4: [B5.3]()
// CHECK: 5: [B5.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B4
// CHECK: Succs (1): B3
// CHECK: Succs (2): B3 B4
// CHECK: [B6]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: Preds (1): B7
// CHECK: Succs (1): B4
// CHECK: Succs (1): B5
// CHECK: [B7]
// CHECK: 1: [B8.5] || [B9.5]
// CHECK: 1: [B9.5] || [B8.5]
// CHECK: 2: bool a = A().operator _Bool() || B().operator _Bool();
// CHECK: T: [B8.5] || ...
// CHECK: Preds (2): B9 B8
// CHECK: Succs (2): B4 B6
// CHECK: T: [B9.5] || ...
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B5 B6
// CHECK: [B8]
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 2: [B8.1] (BindTemporary)
// CHECK: 3: [B8.2].operator _Bool
// CHECK: 4: [B8.3]()
// CHECK: 5: [B8.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B8.5] || ...
// CHECK: Preds (1): B10
// CHECK: Succs (2): B7 B9
// CHECK: Preds (1): B9
// CHECK: Succs (1): B7
// CHECK: [B9]
// CHECK: 1: B() (CXXConstructExpr, class B)
// CHECK: 1: A() (CXXConstructExpr, class A)
// CHECK: 2: [B9.1] (BindTemporary)
// CHECK: 3: [B9.2].operator _Bool
// CHECK: 4: [B9.3]()
// CHECK: 5: [B9.4] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: Preds (1): B8
// CHECK: Succs (1): B7
// CHECK: T: [B9.5] || ...
// CHECK: Preds (1): B10
// CHECK: Succs (2): B7 B8
// CHECK: [B0 (EXIT)]
// CHECK: Preds (1): B1
// CHECK: [B11 (ENTRY)]

View File

@ -152,15 +152,15 @@ int test19() {
int test20() {
int z; // expected-note{{initialize the variable 'z' to silence this warning}}
if ((test19_aux1() + test19_aux2() && test19_aux1()) || test19_aux3(&z))
return z; // expected-warning{{variable 'z' may be uninitialized when used here}}
if ((test19_aux1() + test19_aux2() && test19_aux1()) || test19_aux3(&z)) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
return z; // expected-note {{uninitialized use occurs here}}
return 0;
}
int test21(int x, int y) {
int z; // expected-note{{initialize the variable 'z' to silence this warning}}
if ((x && y) || test19_aux3(&z) || test19_aux2())
return z; // expected-warning{{variable 'z' may be uninitialized when used here}}
if ((x && y) || test19_aux3(&z) || test19_aux2()) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
return z; // expected-note {{uninitialized use occurs here}}
return 0;
}

View File

@ -25,7 +25,7 @@ int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when us
int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}}
int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}}
void test () {
void test_stuff () {
int a = a; // no-warning: used to signal intended lack of initialization.
int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}}
int c = (c + c); // expected-warning {{variable 'c' is uninitialized when used within its own initialization}}