Change subexpressions to be visited in the CFG from left-to-right.

This is a more natural order of evaluation, and it is very important
for visualization in the static analyzer.  Within Xcode, the arrows
will not jump from right to left, which looks very visually jarring.
It also provides a more natural location for dataflow-based diagnostics.

Along the way, we found a case in the analyzer diagnostics where we
needed to indicate that a variable was "captured" by a block.

-fsyntax-only timings on sqlite3.c show no visible performance change,
although this is just one test case.

Fixes <rdar://problem/13016513>

llvm-svn: 174447
This commit is contained in:
Ted Kremenek 2013-02-05 22:00:19 +00:00
parent 44a40ca143
commit 8ae67871b4
8 changed files with 303 additions and 128 deletions

View File

@ -2207,6 +2207,15 @@ public:
return SubExprs+PREARGS_START+getNumPreArgs()+getNumArgs();
}
/// This method provides fast access to all the subexpressions of
/// a CallExpr without going through the slower virtual child_iterator
/// interface. This provides efficient reverse iteration of the
/// subexpressions. This is currently used for CFG construction.
ArrayRef<Stmt*> getRawSubExprs() {
return ArrayRef<Stmt*>(SubExprs,
getNumPreArgs() + PREARGS_START + getNumArgs());
}
/// getNumCommas - Return the number of commas that must have been present in
/// this function call.
unsigned getNumCommas() const { return NumArgs ? NumArgs - 1 : 0; }

View File

@ -662,6 +662,10 @@ public:
return *this;
}
};
/// Return the original region for a captured region, if
/// one exists.
const VarRegion *getOriginalRegion(const VarRegion *VR) const;
referenced_vars_iterator referenced_vars_begin() const;
referenced_vars_iterator referenced_vars_end() const;

View File

@ -233,6 +233,43 @@ public:
}
};
class reverse_children {
llvm::SmallVector<Stmt *, 12> childrenBuf;
ArrayRef<Stmt*> children;
public:
reverse_children(Stmt *S);
typedef ArrayRef<Stmt*>::reverse_iterator iterator;
iterator begin() const { return children.rbegin(); }
iterator end() const { return children.rend(); }
};
reverse_children::reverse_children(Stmt *S) {
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
children = CE->getRawSubExprs();
return;
}
switch (S->getStmtClass()) {
case Stmt::InitListExprClass: {
InitListExpr *IE = cast<InitListExpr>(S);
children = llvm::makeArrayRef(reinterpret_cast<Stmt**>(IE->getInits()),
IE->getNumInits());
return;
}
default:
break;
}
// Default case for all other statements.
for (Stmt::child_range I = S->children(); I; ++I) {
childrenBuf.push_back(*I);
}
// This needs to be done *after* childrenBuf has been populated.
children = childrenBuf;
}
/// CFGBuilder - This class implements CFG construction from an AST.
/// The builder is stateful: an instance of the builder should be used to only
/// construct a single CFG.
@ -1166,14 +1203,19 @@ CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) {
}
/// VisitChildren - Visit the children of a Stmt.
CFGBlock *CFGBuilder::VisitChildren(Stmt *Terminator) {
CFGBlock *lastBlock = Block;
for (Stmt::child_range I = Terminator->children(); I; ++I)
if (Stmt *child = *I)
if (CFGBlock *b = Visit(child))
lastBlock = b;
CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
CFGBlock *B = Block;
return lastBlock;
// Visit the children in their reverse order so that they appear in
// left-to-right (natural) order in the CFG.
reverse_children RChildren(S);
for (reverse_children::iterator I = RChildren.begin(), E = RChildren.end();
I != E; ++I) {
if (Stmt *Child = *I)
if (CFGBlock *R = Visit(Child))
B = R;
}
return B;
}
CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A,
@ -3093,19 +3135,14 @@ tryAgain:
CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) {
// When visiting children for destructors we want to visit them in reverse
// order. Because there's no reverse iterator for children must to reverse
// them in helper vector.
typedef SmallVector<Stmt *, 4> ChildrenVect;
ChildrenVect ChildrenRev;
for (Stmt::child_range I = E->children(); I; ++I) {
if (*I) ChildrenRev.push_back(*I);
}
// order that they will appear in the CFG. Because the CFG is built
// bottom-up, this means we visit them in their natural order, which
// reverses them in the CFG.
CFGBlock *B = Block;
for (ChildrenVect::reverse_iterator I = ChildrenRev.rbegin(),
L = ChildrenRev.rend(); I != L; ++I) {
if (CFGBlock *R = VisitForTemporaryDtors(*I))
B = R;
for (Stmt::child_range I = E->children(); I; ++I) {
if (Stmt *Child = *I)
if (CFGBlock *R = VisitForTemporaryDtors(Child))
B = R;
}
return B;
}

View File

@ -433,42 +433,65 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
llvm::raw_svector_ostream os(sbuf);
if (const PostStmt *PS = StoreSite->getLocationAs<PostStmt>()) {
if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
const Stmt *S = PS->getStmt();
const char *action = 0;
const DeclStmt *DS = dyn_cast<DeclStmt>(S);
const VarRegion *VR = dyn_cast<VarRegion>(R);
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << "Variable '" << *VR->getDecl() << "' ";
if (DS) {
action = "initialized to ";
} else if (isa<BlockExpr>(S)) {
action = "captured by block as ";
if (VR) {
// See if we can get the BlockVarRegion.
ProgramStateRef State = StoreSite->getState();
SVal V = State->getSVal(S, PS->getLocationContext());
if (const BlockDataRegion *BDR =
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
V = State->getSVal(OriginalR);
BR.addVisitor(new FindLastStoreBRVisitor(V, OriginalR));
}
}
}
else
return NULL;
}
if (action) {
if (!R)
return 0;
os << "Variable '" << *VR->getDecl() << "' ";
if (isa<loc::ConcreteInt>(V)) {
bool b = false;
if (R->isBoundable()) {
if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
if (TR->getValueType()->isObjCObjectPointerType()) {
os << "initialized to nil";
os << action << "nil";
b = true;
}
}
}
if (!b)
os << "initialized to a null pointer value";
os << action << "a null pointer value";
}
else if (isa<nonloc::ConcreteInt>(V)) {
os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
os << action << cast<nonloc::ConcreteInt>(V).getValue();
}
else if (V.isUndef()) {
if (isa<VarRegion>(R)) {
const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
if (VD->getInit())
os << "initialized to a garbage value";
else
os << "declared without an initial value";
else if (DS) {
if (V.isUndef()) {
if (isa<VarRegion>(R)) {
const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
if (VD->getInit())
os << "initialized to a garbage value";
else
os << "declared without an initial value";
}
}
else {
os << "initialized here";
}
}
else {
os << "initialized here";
}
}
} else if (isa<CallEnter>(StoreSite->getLocation())) {

View File

@ -1302,3 +1302,13 @@ BlockDataRegion::referenced_vars_end() const {
return BlockDataRegion::referenced_vars_iterator(Vec->end(),
VecOriginal->end());
}
const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const {
for (referenced_vars_iterator I = referenced_vars_begin(),
E = referenced_vars_end();
I != E; ++I) {
if (I.getCapturedRegion() == R)
return I.getOriginalRegion();
}
return 0;
}

View File

@ -207,22 +207,22 @@ TestCtorInits::TestCtorInits()
// CHECK: 14: int a = int(A().operator int()) + int(B().operator int());
// CHECK: 15: ~B() (Temporary object destructor)
// CHECK: 16: ~A() (Temporary object destructor)
// CHECK: 17: A() (CXXConstructExpr, class A)
// CHECK: 18: [B1.17] (BindTemporary)
// CHECK: 19: [B1.18].operator int
// CHECK: 20: [B1.19]()
// CHECK: 21: [B1.20] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 22: int([B1.21]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 23: B() (CXXConstructExpr, class B)
// CHECK: 24: [B1.23] (BindTemporary)
// CHECK: 25: [B1.24].operator int
// CHECK: 26: [B1.25]()
// CHECK: 27: [B1.26] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 28: int([B1.27]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 29: [B1.22] + [B1.28]
// CHECK: 30: foo
// CHECK: 31: [B1.30] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 32: [B1.31]([B1.29])
// CHECK: 17: foo
// CHECK: 18: [B1.17] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 19: A() (CXXConstructExpr, class A)
// CHECK: 20: [B1.19] (BindTemporary)
// CHECK: 21: [B1.20].operator int
// CHECK: 22: [B1.21]()
// CHECK: 23: [B1.22] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 24: int([B1.23]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 25: B() (CXXConstructExpr, class B)
// CHECK: 26: [B1.25] (BindTemporary)
// CHECK: 27: [B1.26].operator int
// CHECK: 28: [B1.27]()
// CHECK: 29: [B1.28] (ImplicitCastExpr, UserDefinedConversion, int)
// CHECK: 30: int([B1.29]) (CXXFunctionalCastExpr, NoOp, int)
// CHECK: 31: [B1.24] + [B1.30]
// CHECK: 32: [B1.18]([B1.31])
// CHECK: 33: ~B() (Temporary object destructor)
// CHECK: 34: ~A() (Temporary object destructor)
// CHECK: 35: int b;
@ -242,11 +242,9 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
// 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: [B5.6] && ...
// CHECK: 1: [B5.8] && [B4.5]
// CHECK: 2: [B5.3]([B3.1])
// CHECK: T: [B5.8] && ...
// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B2 B1
// CHECK: [B4]
@ -259,12 +257,14 @@ TestCtorInits::TestCtorInits()
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
// 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: 2: foo
// CHECK: 3: [B5.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: A() (CXXConstructExpr, class A)
// CHECK: 5: [B5.4] (BindTemporary)
// CHECK: 6: [B5.5].operator _Bool
// CHECK: 7: [B5.6]()
// CHECK: 8: [B5.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B5.8] && ...
// CHECK: Preds (2): B6 B7
// CHECK: Succs (2): B4 B3
// CHECK: [B6]
@ -308,11 +308,9 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B3
// CHECK: Succs (1): B1
// CHECK: [B3]
// 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: [B5.6] || ...
// CHECK: 1: [B5.8] || [B4.5]
// CHECK: 2: [B5.3]([B3.1])
// CHECK: T: [B5.8] || ...
// CHECK: Preds (2): B4 B5
// CHECK: Succs (2): B1 B2
// CHECK: [B4]
@ -325,12 +323,14 @@ TestCtorInits::TestCtorInits()
// CHECK: Succs (1): B3
// CHECK: [B5]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
// 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: 2: foo
// CHECK: 3: [B5.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(_Bool))
// CHECK: 4: A() (CXXConstructExpr, class A)
// CHECK: 5: [B5.4] (BindTemporary)
// CHECK: 6: [B5.5].operator _Bool
// CHECK: 7: [B5.6]()
// CHECK: 8: [B5.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B5.8] || ...
// CHECK: Preds (2): B6 B7
// CHECK: Succs (2): B3 B4
// CHECK: [B6]
@ -370,17 +370,17 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (2): B2 B3
// CHECK: Succs (1): B0
// CHECK: [B2]
// CHECK: 1: 0
// CHECK: 2: foo
// CHECK: 3: [B2.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 4: [B2.3]([B2.1])
// CHECK: 1: foo
// CHECK: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 3: 0
// CHECK: 4: [B2.2]([B2.3])
// CHECK: Preds (1): B4
// CHECK: Succs (1): B1
// CHECK: [B3]
// CHECK: 1: 0
// CHECK: 2: foo
// CHECK: 3: [B3.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 4: [B3.3]([B3.1])
// CHECK: 1: foo
// CHECK: 2: [B3.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(int))
// CHECK: 3: 0
// CHECK: 4: [B3.2]([B3.3])
// CHECK: Preds (1): B4
// CHECK: Succs (1): B1
// CHECK: [B4]
@ -474,13 +474,11 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B4
// CHECK: Succs (1): B1
// CHECK: [B4]
// CHECK: 1: [B7.6] ? [B5.6] : [B6.15]
// CHECK: 1: [B7.8] ? [B5.6] : [B6.15]
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B4.2]
// CHECK: 4: foo
// CHECK: 5: [B4.4] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 6: [B4.5]([B4.3])
// CHECK: T: [B7.6] ? ... : ...
// CHECK: 4: [B7.3]([B4.3])
// CHECK: T: [B7.8] ? ... : ...
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B2 B3
// CHECK: [B5]
@ -512,12 +510,14 @@ TestCtorInits::TestCtorInits()
// CHECK: Succs (1): B4
// CHECK: [B7]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: 2: B() (CXXConstructExpr, class B)
// CHECK: 3: [B7.2] (BindTemporary)
// CHECK: 4: [B7.3].operator _Bool
// CHECK: 5: [B7.4]()
// CHECK: 6: [B7.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B7.6] ? ... : ...
// CHECK: 2: foo
// CHECK: 3: [B7.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 4: B() (CXXConstructExpr, class B)
// CHECK: 5: [B7.4] (BindTemporary)
// CHECK: 6: [B7.5].operator _Bool
// CHECK: 7: [B7.6]()
// CHECK: 8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B7.8] ? ... : ...
// CHECK: Preds (2): B8 B9
// CHECK: Succs (2): B5 B6
// CHECK: [B8]
@ -647,17 +647,15 @@ TestCtorInits::TestCtorInits()
// CHECK: Preds (1): B4
// CHECK: Succs (1): B1
// CHECK: [B4]
// CHECK: 1: [B7.3] ?: [B6.6]
// CHECK: 1: [B7.5] ?: [B6.6]
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B4.2]
// CHECK: 4: foo
// CHECK: 5: [B4.4] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 6: [B4.5]([B4.3])
// CHECK: T: [B7.6] ? ... : ...
// CHECK: 4: [B7.3]([B4.3])
// CHECK: T: [B7.8] ? ... : ...
// CHECK: Preds (2): B5 B6
// CHECK: Succs (2): B2 B3
// CHECK: [B5]
// CHECK: 1: [B7.3] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 1: [B7.5] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 2: [B5.1]
// CHECK: 3: [B5.2] (CXXConstructExpr, class A)
// CHECK: 4: [B5.3] (BindTemporary)
@ -674,12 +672,14 @@ TestCtorInits::TestCtorInits()
// CHECK: Succs (1): B4
// CHECK: [B7]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: A() (CXXConstructExpr, class A)
// CHECK: 3: [B7.2] (BindTemporary)
// CHECK: 4: [B7.3].operator _Bool
// CHECK: 5: [B7.4]()
// CHECK: 6: [B7.5] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B7.6] ? ... : ...
// CHECK: 2: foo
// CHECK: 3: [B7.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 4: A() (CXXConstructExpr, class A)
// CHECK: 5: [B7.4] (BindTemporary)
// CHECK: 6: [B7.5].operator _Bool
// CHECK: 7: [B7.6]()
// CHECK: 8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CHECK: T: [B7.8] ? ... : ...
// CHECK: Preds (2): B9 B8
// CHECK: Succs (2): B5 B6
// CHECK: [B8]
@ -745,13 +745,13 @@ TestCtorInits::TestCtorInits()
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 4: [B1.3]
// CHECK: 5: const A &a = A();
// CHECK: 6: A() (CXXConstructExpr, class A)
// CHECK: 7: [B1.6] (BindTemporary)
// CHECK: 8: [B1.7] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 9: [B1.8]
// CHECK: 10: foo
// CHECK: 11: [B1.10] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 12: [B1.11]([B1.9])
// CHECK: 6: foo
// CHECK: 7: [B1.6] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 8: A() (CXXConstructExpr, class A)
// CHECK: 9: [B1.8] (BindTemporary)
// CHECK: 10: [B1.9] (ImplicitCastExpr, NoOp, const class 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)
@ -787,15 +787,15 @@ TestCtorInits::TestCtorInits()
// CHECK: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 6: [B1.5]
// CHECK: 7: const A &a = A::make();
// CHECK: 8: A::make
// CHECK: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
// CHECK: 10: [B1.9]()
// CHECK: 11: [B1.10] (BindTemporary)
// CHECK: 12: [B1.11] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 13: [B1.12]
// CHECK: 14: foo
// CHECK: 15: [B1.14] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 16: [B1.15]([B1.13])
// CHECK: 8: foo
// CHECK: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// CHECK: 10: A::make
// CHECK: 11: [B1.10] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void))
// CHECK: 12: [B1.11]()
// CHECK: 13: [B1.12] (BindTemporary)
// CHECK: 14: [B1.13] (ImplicitCastExpr, NoOp, const class 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)

View File

@ -1418,12 +1418,12 @@ void test_inline_dispatch_once() {
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>190</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>190</integer>
// CHECK-NEXT: <key>col</key><integer>15</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@ -1435,6 +1435,35 @@ void test_inline_dispatch_once() {
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>190</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>190</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>194</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Variable &apos;p&apos; captured by block as a null pointer value</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Variable &apos;p&apos; captured by block as a null pointer value</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>190</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@ -1739,6 +1768,69 @@ void test_inline_dispatch_once() {
// CHECK-NEXT: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>200</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>200</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>201</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>201</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>201</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>201</integer>
// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>203</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Variable &apos;p&apos; captured by block as a null pointer value</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Variable &apos;p&apos; captured by block as a null pointer value</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>

View File

@ -80,8 +80,8 @@ void test2() {
- // expected-warning {{will never be executed}}
halt();
case 8:
i // expected-warning {{will never be executed}}
+=
i
+= // expected-warning {{will never be executed}}
halt();
case 9:
halt()
@ -93,8 +93,8 @@ void test2() {
case 11: {
int a[5];
live(),
a[halt() // expected-warning {{will never be executed}}
];
a[halt()
]; // expected-warning {{will never be executed}}
}
}
}