[analyzer][Liveness][NFC] Get rid of statement liveness, because such a thing doesn't exist
The summary and very short discussion in D82122 summarizes whats happening here. In short, liveness talks about variables, or expressions, anything that has a value. Well, statements just simply don't have a one. Differential Revision: https://reviews.llvm.org/D82598
This commit is contained in:
parent
01f5fcd829
commit
dd1d5488e4
|
@ -30,7 +30,7 @@ using a 'dot' format viewer (such as Graphviz on macOS) instead.
|
||||||
- debug.DumpLiveVars: Show the results of live variable analysis for each
|
- debug.DumpLiveVars: Show the results of live variable analysis for each
|
||||||
top-level function being analyzed.
|
top-level function being analyzed.
|
||||||
|
|
||||||
- debug.DumpLiveStmts: Show the results of live statement analysis for each
|
- debug.DumpLiveExprs: Show the results of live expression analysis for each
|
||||||
top-level function being analyzed.
|
top-level function being analyzed.
|
||||||
|
|
||||||
- debug.ViewExplodedGraph: Show the Exploded Graphs generated for the
|
- debug.ViewExplodedGraph: Show the Exploded Graphs generated for the
|
||||||
|
|
|
@ -30,22 +30,22 @@ public:
|
||||||
class LivenessValues {
|
class LivenessValues {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
llvm::ImmutableSet<const Stmt *> liveStmts;
|
llvm::ImmutableSet<const Expr *> liveExprs;
|
||||||
llvm::ImmutableSet<const VarDecl *> liveDecls;
|
llvm::ImmutableSet<const VarDecl *> liveDecls;
|
||||||
llvm::ImmutableSet<const BindingDecl *> liveBindings;
|
llvm::ImmutableSet<const BindingDecl *> liveBindings;
|
||||||
|
|
||||||
bool equals(const LivenessValues &V) const;
|
bool equals(const LivenessValues &V) const;
|
||||||
|
|
||||||
LivenessValues()
|
LivenessValues()
|
||||||
: liveStmts(nullptr), liveDecls(nullptr), liveBindings(nullptr) {}
|
: liveExprs(nullptr), liveDecls(nullptr), liveBindings(nullptr) {}
|
||||||
|
|
||||||
LivenessValues(llvm::ImmutableSet<const Stmt *> LiveStmts,
|
LivenessValues(llvm::ImmutableSet<const Expr *> liveExprs,
|
||||||
llvm::ImmutableSet<const VarDecl *> LiveDecls,
|
llvm::ImmutableSet<const VarDecl *> LiveDecls,
|
||||||
llvm::ImmutableSet<const BindingDecl *> LiveBindings)
|
llvm::ImmutableSet<const BindingDecl *> LiveBindings)
|
||||||
: liveStmts(LiveStmts), liveDecls(LiveDecls),
|
: liveExprs(liveExprs), liveDecls(LiveDecls),
|
||||||
liveBindings(LiveBindings) {}
|
liveBindings(LiveBindings) {}
|
||||||
|
|
||||||
bool isLive(const Stmt *S) const;
|
bool isLive(const Expr *E) const;
|
||||||
bool isLive(const VarDecl *D) const;
|
bool isLive(const VarDecl *D) const;
|
||||||
|
|
||||||
friend class LiveVariables;
|
friend class LiveVariables;
|
||||||
|
@ -83,17 +83,17 @@ public:
|
||||||
/// only returns liveness information for block-level expressions.
|
/// only returns liveness information for block-level expressions.
|
||||||
bool isLive(const Stmt *S, const VarDecl *D);
|
bool isLive(const Stmt *S, const VarDecl *D);
|
||||||
|
|
||||||
/// Returns true the block-level expression "value" is live
|
/// Returns true the block-level expression value is live
|
||||||
/// before the given block-level expression (see runOnAllBlocks).
|
/// before the given block-level expression (see runOnAllBlocks).
|
||||||
bool isLive(const Stmt *Loc, const Stmt *StmtVal);
|
bool isLive(const Stmt *Loc, const Expr *Val);
|
||||||
|
|
||||||
/// Print to stderr the variable liveness information associated with
|
/// Print to stderr the variable liveness information associated with
|
||||||
/// each basic block.
|
/// each basic block.
|
||||||
void dumpBlockLiveness(const SourceManager &M);
|
void dumpBlockLiveness(const SourceManager &M);
|
||||||
|
|
||||||
/// Print to stderr the statement liveness information associated with
|
/// Print to stderr the expression liveness information associated with
|
||||||
/// each basic block.
|
/// each basic block.
|
||||||
void dumpStmtLiveness(const SourceManager &M);
|
void dumpExprLiveness(const SourceManager &M);
|
||||||
|
|
||||||
void runOnAllBlocks(Observer &obs);
|
void runOnAllBlocks(Observer &obs);
|
||||||
|
|
||||||
|
|
|
@ -1478,8 +1478,8 @@ def LiveVariablesDumper : Checker<"DumpLiveVars">,
|
||||||
HelpText<"Print results of live variable analysis">,
|
HelpText<"Print results of live variable analysis">,
|
||||||
Documentation<NotDocumented>;
|
Documentation<NotDocumented>;
|
||||||
|
|
||||||
def LiveStatementsDumper : Checker<"DumpLiveStmts">,
|
def LiveExpressionsDumper : Checker<"DumpLiveExprs">,
|
||||||
HelpText<"Print results of live statement analysis">,
|
HelpText<"Print results of live expression analysis">,
|
||||||
Documentation<NotDocumented>;
|
Documentation<NotDocumented>;
|
||||||
|
|
||||||
def CFGViewer : Checker<"ViewCFG">,
|
def CFGViewer : Checker<"ViewCFG">,
|
||||||
|
|
|
@ -539,7 +539,7 @@ public:
|
||||||
|
|
||||||
bool isLive(SymbolRef sym);
|
bool isLive(SymbolRef sym);
|
||||||
bool isLiveRegion(const MemRegion *region);
|
bool isLiveRegion(const MemRegion *region);
|
||||||
bool isLive(const Stmt *ExprVal, const LocationContext *LCtx) const;
|
bool isLive(const Expr *ExprVal, const LocationContext *LCtx) const;
|
||||||
bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const;
|
bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const;
|
||||||
|
|
||||||
/// Unconditionally marks a symbol as live.
|
/// Unconditionally marks a symbol as live.
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace {
|
||||||
class LiveVariablesImpl {
|
class LiveVariablesImpl {
|
||||||
public:
|
public:
|
||||||
AnalysisDeclContext &analysisContext;
|
AnalysisDeclContext &analysisContext;
|
||||||
llvm::ImmutableSet<const Stmt *>::Factory SSetFact;
|
llvm::ImmutableSet<const Expr *>::Factory ESetFact;
|
||||||
llvm::ImmutableSet<const VarDecl *>::Factory DSetFact;
|
llvm::ImmutableSet<const VarDecl *>::Factory DSetFact;
|
||||||
llvm::ImmutableSet<const BindingDecl *>::Factory BSetFact;
|
llvm::ImmutableSet<const BindingDecl *>::Factory BSetFact;
|
||||||
llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksEndToLiveness;
|
llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksEndToLiveness;
|
||||||
|
@ -45,16 +45,15 @@ public:
|
||||||
LiveVariables::Observer *obs = nullptr);
|
LiveVariables::Observer *obs = nullptr);
|
||||||
|
|
||||||
void dumpBlockLiveness(const SourceManager& M);
|
void dumpBlockLiveness(const SourceManager& M);
|
||||||
void dumpStmtLiveness(const SourceManager& M);
|
void dumpExprLiveness(const SourceManager& M);
|
||||||
|
|
||||||
LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign)
|
LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign)
|
||||||
: analysisContext(ac),
|
: analysisContext(ac),
|
||||||
SSetFact(false), // Do not canonicalize ImmutableSets by default.
|
ESetFact(false), // Do not canonicalize ImmutableSets by default.
|
||||||
DSetFact(false), // This is a *major* performance win.
|
DSetFact(false), // This is a *major* performance win.
|
||||||
BSetFact(false),
|
BSetFact(false), killAtAssign(KillAtAssign) {}
|
||||||
killAtAssign(KillAtAssign) {}
|
|
||||||
};
|
};
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
static LiveVariablesImpl &getImpl(void *x) {
|
static LiveVariablesImpl &getImpl(void *x) {
|
||||||
return *((LiveVariablesImpl *) x);
|
return *((LiveVariablesImpl *) x);
|
||||||
|
@ -64,8 +63,8 @@ static LiveVariablesImpl &getImpl(void *x) {
|
||||||
// Operations and queries on LivenessValues.
|
// Operations and queries on LivenessValues.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
bool LiveVariables::LivenessValues::isLive(const Stmt *S) const {
|
bool LiveVariables::LivenessValues::isLive(const Expr *E) const {
|
||||||
return liveStmts.contains(S);
|
return liveExprs.contains(E);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const {
|
bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const {
|
||||||
|
@ -97,10 +96,10 @@ LiveVariables::LivenessValues
|
||||||
LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA,
|
LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA,
|
||||||
LiveVariables::LivenessValues valsB) {
|
LiveVariables::LivenessValues valsB) {
|
||||||
|
|
||||||
llvm::ImmutableSetRef<const Stmt *>
|
llvm::ImmutableSetRef<const Expr *> SSetRefA(
|
||||||
SSetRefA(valsA.liveStmts.getRootWithoutRetain(), SSetFact.getTreeFactory()),
|
valsA.liveExprs.getRootWithoutRetain(), ESetFact.getTreeFactory()),
|
||||||
SSetRefB(valsB.liveStmts.getRootWithoutRetain(), SSetFact.getTreeFactory());
|
SSetRefB(valsB.liveExprs.getRootWithoutRetain(),
|
||||||
|
ESetFact.getTreeFactory());
|
||||||
|
|
||||||
llvm::ImmutableSetRef<const VarDecl *>
|
llvm::ImmutableSetRef<const VarDecl *>
|
||||||
DSetRefA(valsA.liveDecls.getRootWithoutRetain(), DSetFact.getTreeFactory()),
|
DSetRefA(valsA.liveDecls.getRootWithoutRetain(), DSetFact.getTreeFactory()),
|
||||||
|
@ -122,7 +121,7 @@ LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LiveVariables::LivenessValues::equals(const LivenessValues &V) const {
|
bool LiveVariables::LivenessValues::equals(const LivenessValues &V) const {
|
||||||
return liveStmts == V.liveStmts && liveDecls == V.liveDecls;
|
return liveExprs == V.liveExprs && liveDecls == V.liveDecls;
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -141,8 +140,8 @@ bool LiveVariables::isLive(const Stmt *S, const VarDecl *D) {
|
||||||
return isAlwaysAlive(D) || getImpl(impl).stmtsToLiveness[S].isLive(D);
|
return isAlwaysAlive(D) || getImpl(impl).stmtsToLiveness[S].isLive(D);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LiveVariables::isLive(const Stmt *Loc, const Stmt *S) {
|
bool LiveVariables::isLive(const Stmt *Loc, const Expr *Val) {
|
||||||
return getImpl(impl).stmtsToLiveness[Loc].isLive(S);
|
return getImpl(impl).stmtsToLiveness[Loc].isLive(Val);
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -186,27 +185,27 @@ static const VariableArrayType *FindVA(QualType Ty) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Stmt *LookThroughStmt(const Stmt *S) {
|
static const Expr *LookThroughExpr(const Expr *E) {
|
||||||
while (S) {
|
while (E) {
|
||||||
if (const Expr *Ex = dyn_cast<Expr>(S))
|
if (const Expr *Ex = dyn_cast<Expr>(E))
|
||||||
S = Ex->IgnoreParens();
|
E = Ex->IgnoreParens();
|
||||||
if (const FullExpr *FE = dyn_cast<FullExpr>(S)) {
|
if (const FullExpr *FE = dyn_cast<FullExpr>(E)) {
|
||||||
S = FE->getSubExpr();
|
E = FE->getSubExpr();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) {
|
if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) {
|
||||||
S = OVE->getSourceExpr();
|
E = OVE->getSourceExpr();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return S;
|
return E;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddLiveStmt(llvm::ImmutableSet<const Stmt *> &Set,
|
static void AddLiveExpr(llvm::ImmutableSet<const Expr *> &Set,
|
||||||
llvm::ImmutableSet<const Stmt *>::Factory &F,
|
llvm::ImmutableSet<const Expr *>::Factory &F,
|
||||||
const Stmt *S) {
|
const Expr *E) {
|
||||||
Set = F.add(Set, LookThroughStmt(S));
|
Set = F.add(Set, LookThroughExpr(E));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferFunctions::Visit(Stmt *S) {
|
void TransferFunctions::Visit(Stmt *S) {
|
||||||
|
@ -215,8 +214,8 @@ void TransferFunctions::Visit(Stmt *S) {
|
||||||
|
|
||||||
StmtVisitor<TransferFunctions>::Visit(S);
|
StmtVisitor<TransferFunctions>::Visit(S);
|
||||||
|
|
||||||
if (isa<Expr>(S)) {
|
if (const auto *E = dyn_cast<Expr>(S)) {
|
||||||
val.liveStmts = LV.SSetFact.remove(val.liveStmts, S);
|
val.liveExprs = LV.ESetFact.remove(val.liveExprs, E);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark all children expressions live.
|
// Mark all children expressions live.
|
||||||
|
@ -233,7 +232,7 @@ void TransferFunctions::Visit(Stmt *S) {
|
||||||
// Include the implicit "this" pointer as being live.
|
// Include the implicit "this" pointer as being live.
|
||||||
CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(S);
|
CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(S);
|
||||||
if (Expr *ImplicitObj = CE->getImplicitObjectArgument()) {
|
if (Expr *ImplicitObj = CE->getImplicitObjectArgument()) {
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, ImplicitObj);
|
AddLiveExpr(val.liveExprs, LV.ESetFact, ImplicitObj);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -250,7 +249,7 @@ void TransferFunctions::Visit(Stmt *S) {
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) {
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) {
|
||||||
for (const VariableArrayType* VA = FindVA(VD->getType());
|
for (const VariableArrayType* VA = FindVA(VD->getType());
|
||||||
VA != nullptr; VA = FindVA(VA->getElementType())) {
|
VA != nullptr; VA = FindVA(VA->getElementType())) {
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, VA->getSizeExpr());
|
AddLiveExpr(val.liveExprs, LV.ESetFact, VA->getSizeExpr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -263,7 +262,7 @@ void TransferFunctions::Visit(Stmt *S) {
|
||||||
if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child))
|
if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child))
|
||||||
child = OV->getSourceExpr();
|
child = OV->getSourceExpr();
|
||||||
child = child->IgnoreParens();
|
child = child->IgnoreParens();
|
||||||
val.liveStmts = LV.SSetFact.add(val.liveStmts, child);
|
val.liveExprs = LV.ESetFact.add(val.liveExprs, child);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,36 +283,39 @@ void TransferFunctions::Visit(Stmt *S) {
|
||||||
// If one of the branches is an expression rather than a compound
|
// If one of the branches is an expression rather than a compound
|
||||||
// statement, it will be bad if we mark it as live at the terminator
|
// statement, it will be bad if we mark it as live at the terminator
|
||||||
// of the if-statement (i.e., immediately after the condition expression).
|
// of the if-statement (i.e., immediately after the condition expression).
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, cast<IfStmt>(S)->getCond());
|
AddLiveExpr(val.liveExprs, LV.ESetFact, cast<IfStmt>(S)->getCond());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Stmt::WhileStmtClass: {
|
case Stmt::WhileStmtClass: {
|
||||||
// If the loop body is an expression rather than a compound statement,
|
// If the loop body is an expression rather than a compound statement,
|
||||||
// it will be bad if we mark it as live at the terminator of the loop
|
// it will be bad if we mark it as live at the terminator of the loop
|
||||||
// (i.e., immediately after the condition expression).
|
// (i.e., immediately after the condition expression).
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, cast<WhileStmt>(S)->getCond());
|
AddLiveExpr(val.liveExprs, LV.ESetFact, cast<WhileStmt>(S)->getCond());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Stmt::DoStmtClass: {
|
case Stmt::DoStmtClass: {
|
||||||
// If the loop body is an expression rather than a compound statement,
|
// If the loop body is an expression rather than a compound statement,
|
||||||
// it will be bad if we mark it as live at the terminator of the loop
|
// it will be bad if we mark it as live at the terminator of the loop
|
||||||
// (i.e., immediately after the condition expression).
|
// (i.e., immediately after the condition expression).
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, cast<DoStmt>(S)->getCond());
|
AddLiveExpr(val.liveExprs, LV.ESetFact, cast<DoStmt>(S)->getCond());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Stmt::ForStmtClass: {
|
case Stmt::ForStmtClass: {
|
||||||
// If the loop body is an expression rather than a compound statement,
|
// If the loop body is an expression rather than a compound statement,
|
||||||
// it will be bad if we mark it as live at the terminator of the loop
|
// it will be bad if we mark it as live at the terminator of the loop
|
||||||
// (i.e., immediately after the condition expression).
|
// (i.e., immediately after the condition expression).
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, cast<ForStmt>(S)->getCond());
|
AddLiveExpr(val.liveExprs, LV.ESetFact, cast<ForStmt>(S)->getCond());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK + FIXME: What is this? One could only guess that this is an attempt to
|
||||||
|
// fish for live values, for example, arguments from a call expression.
|
||||||
|
// Maybe we could take inspiration from UninitializedVariable analysis?
|
||||||
for (Stmt *Child : S->children()) {
|
for (Stmt *Child : S->children()) {
|
||||||
if (Child)
|
if (const auto *E = dyn_cast_or_null<Expr>(Child))
|
||||||
AddLiveStmt(val.liveStmts, LV.SSetFact, Child);
|
AddLiveExpr(val.liveExprs, LV.ESetFact, E);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +418,7 @@ VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE)
|
||||||
const Expr *subEx = UE->getArgumentExpr();
|
const Expr *subEx = UE->getArgumentExpr();
|
||||||
if (subEx->getType()->isVariableArrayType()) {
|
if (subEx->getType()->isVariableArrayType()) {
|
||||||
assert(subEx->isLValue());
|
assert(subEx->isLValue());
|
||||||
val.liveStmts = LV.SSetFact.add(val.liveStmts, subEx->IgnoreParens());
|
val.liveExprs = LV.ESetFact.add(val.liveExprs, subEx->IgnoreParens());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,19 +615,19 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) {
|
||||||
llvm::errs() << "\n";
|
llvm::errs() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void LiveVariables::dumpStmtLiveness(const SourceManager &M) {
|
void LiveVariables::dumpExprLiveness(const SourceManager &M) {
|
||||||
getImpl(impl).dumpStmtLiveness(M);
|
getImpl(impl).dumpExprLiveness(M);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LiveVariablesImpl::dumpStmtLiveness(const SourceManager &M) {
|
void LiveVariablesImpl::dumpExprLiveness(const SourceManager &M) {
|
||||||
// Don't iterate over blockEndsToLiveness directly because it's not sorted.
|
// Don't iterate over blockEndsToLiveness directly because it's not sorted.
|
||||||
for (auto I : *analysisContext.getCFG()) {
|
for (const CFGBlock *B : *analysisContext.getCFG()) {
|
||||||
|
|
||||||
llvm::errs() << "\n[ B" << I->getBlockID()
|
llvm::errs() << "\n[ B" << B->getBlockID()
|
||||||
<< " (live statements at block exit) ]\n";
|
<< " (live expressions at block exit) ]\n";
|
||||||
for (auto S : blocksEndToLiveness[I].liveStmts) {
|
for (const Expr *E : blocksEndToLiveness[B].liveExprs) {
|
||||||
llvm::errs() << "\n";
|
llvm::errs() << "\n";
|
||||||
S->dump();
|
E->dump();
|
||||||
}
|
}
|
||||||
llvm::errs() << "\n";
|
llvm::errs() << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,21 +131,21 @@ bool ento::shouldRegisterLiveVariablesDumper(const CheckerManager &mgr) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class LiveStatementsDumper : public Checker<check::ASTCodeBody> {
|
class LiveExpressionsDumper : public Checker<check::ASTCodeBody> {
|
||||||
public:
|
public:
|
||||||
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
|
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
|
||||||
BugReporter &BR) const {
|
BugReporter &BR) const {
|
||||||
if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D))
|
if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D))
|
||||||
L->dumpStmtLiveness(Mgr.getSourceManager());
|
L->dumpExprLiveness(Mgr.getSourceManager());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ento::registerLiveStatementsDumper(CheckerManager &mgr) {
|
void ento::registerLiveExpressionsDumper(CheckerManager &mgr) {
|
||||||
mgr.registerChecker<LiveStatementsDumper>();
|
mgr.registerChecker<LiveExpressionsDumper>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ento::shouldRegisterLiveStatementsDumper(const CheckerManager &mgr) {
|
bool ento::shouldRegisterLiveExpressionsDumper(const CheckerManager &mgr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,19 +191,15 @@ EnvironmentManager::removeDeadBindings(Environment Env,
|
||||||
F.getTreeFactory());
|
F.getTreeFactory());
|
||||||
|
|
||||||
// Iterate over the block-expr bindings.
|
// Iterate over the block-expr bindings.
|
||||||
for (Environment::iterator I = Env.begin(), E = Env.end();
|
for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
|
||||||
I != E; ++I) {
|
|
||||||
const EnvironmentEntry &BlkExpr = I.getKey();
|
const EnvironmentEntry &BlkExpr = I.getKey();
|
||||||
const SVal &X = I.getData();
|
const SVal &X = I.getData();
|
||||||
|
|
||||||
const bool IsBlkExprLive =
|
const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt());
|
||||||
SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
|
if (!E)
|
||||||
|
continue;
|
||||||
|
|
||||||
assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
|
if (SymReaper.isLive(E, BlkExpr.getLocationContext())) {
|
||||||
"Only Exprs can be live, LivenessAnalysis argues about the liveness "
|
|
||||||
"of *values*!");
|
|
||||||
|
|
||||||
if (IsBlkExprLive) {
|
|
||||||
// Copy the binding to the new map.
|
// Copy the binding to the new map.
|
||||||
EBMapRef = EBMapRef.add(BlkExpr, X);
|
EBMapRef = EBMapRef.add(BlkExpr, X);
|
||||||
|
|
||||||
|
|
|
@ -489,7 +489,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
|
SymbolReaper::isLive(const Expr *ExprVal, const LocationContext *ELCtx) const {
|
||||||
if (LCtx == nullptr)
|
if (LCtx == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_analyze_cc1 -w -analyzer-checker=debug.DumpLiveStmts %s 2>&1\
|
// RUN: %clang_analyze_cc1 -w -analyzer-checker=debug.DumpLiveExprs %s 2>&1\
|
||||||
// RUN: | FileCheck %s
|
// RUN: | FileCheck %s
|
||||||
|
|
||||||
int coin();
|
int coin();
|
||||||
|
@ -7,13 +7,24 @@ int coin();
|
||||||
int testThatDumperWorks(int x, int y, int z) {
|
int testThatDumperWorks(int x, int y, int z) {
|
||||||
return x ? y : z;
|
return x ? y : z;
|
||||||
}
|
}
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
|
||||||
|
// [B5 (ENTRY)]
|
||||||
|
// |
|
||||||
|
// V
|
||||||
|
// [B4 (x)] ? [B2 (y)] : [B3 (z)]
|
||||||
|
// \ /
|
||||||
|
// ---|----
|
||||||
|
// V
|
||||||
|
// [B1] --> [B0 (EXIT)]
|
||||||
|
// return
|
||||||
|
|
||||||
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B1 (live statements at block exit) ]
|
// CHECK: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B2 (live statements at block exit) ]
|
// CHECK: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
@ -24,7 +35,7 @@ int testThatDumperWorks(int x, int y, int z) {
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B3 (live statements at block exit) ]
|
// CHECK: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
@ -33,7 +44,7 @@ int testThatDumperWorks(int x, int y, int z) {
|
||||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} <IntegralToBoolean>
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} <IntegralToBoolean>
|
||||||
// CHECK-NEXT: `-ImplicitCastExpr {{.*}} <LValueToRValue>
|
// CHECK-NEXT: `-ImplicitCastExpr {{.*}} <LValueToRValue>
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
||||||
// CHECK: [ B4 (live statements at block exit) ]
|
// CHECK: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
@ -44,7 +55,7 @@ int testThatDumperWorks(int x, int y, int z) {
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'x' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B5 (live statements at block exit) ]
|
// CHECK: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
// CHECK-NEXT: DeclRefExpr {{.*}} 'y' 'int'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
@ -61,22 +72,22 @@ void testIfBranchExpression(bool flag) {
|
||||||
e;
|
e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B1 (live statements at block exit) ]
|
// CHECK: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B2 (live statements at block exit) ]
|
// CHECK: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B3 (live statements at block exit) ]
|
// CHECK: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B4 (live statements at block exit) ]
|
// CHECK: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B5 (live statements at block exit) ]
|
// CHECK: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
|
@ -89,22 +100,22 @@ void testWhileBodyExpression(bool flag) {
|
||||||
e;
|
e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B1 (live statements at block exit) ]
|
// CHECK: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B2 (live statements at block exit) ]
|
// CHECK: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B3 (live statements at block exit) ]
|
// CHECK: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B4 (live statements at block exit) ]
|
// CHECK: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B5 (live statements at block exit) ]
|
// CHECK: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
|
@ -118,22 +129,22 @@ void testDoWhileBodyExpression(bool flag) {
|
||||||
while (coin());
|
while (coin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B1 (live statements at block exit) ]
|
// CHECK: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B2 (live statements at block exit) ]
|
// CHECK: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B3 (live statements at block exit) ]
|
// CHECK: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B4 (live statements at block exit) ]
|
// CHECK: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B5 (live statements at block exit) ]
|
// CHECK: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
|
@ -146,22 +157,39 @@ void testForBodyExpression(bool flag) {
|
||||||
e;
|
e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B1 (live statements at block exit) ]
|
// CHECK: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B2 (live statements at block exit) ]
|
// CHECK: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B3 (live statements at block exit) ]
|
// CHECK: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B4 (live statements at block exit) ]
|
// CHECK: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK: [ B5 (live statements at block exit) ]
|
// CHECK: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
|
void clang_analyzer_eval(bool);
|
||||||
|
|
||||||
|
void test_lambda_refcapture() {
|
||||||
|
int a = 6;
|
||||||
|
[&](int &a) { a = 42; }(a);
|
||||||
|
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-NEXT: [ B1 (live expressions at block exit) ]
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-NEXT: [ B2 (live expressions at block exit) ]
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-EMPTY:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// RUN: %clang_analyze_cc1 -w -fblocks %s \
|
// RUN: %clang_analyze_cc1 -w -fblocks %s \
|
||||||
// RUN: -analyzer-checker=debug.DumpLiveStmts \
|
// RUN: -analyzer-checker=debug.DumpLiveExprs \
|
||||||
// RUN: 2>&1 | FileCheck %s
|
// RUN: 2>&1 | FileCheck %s
|
||||||
|
|
||||||
@interface Item
|
@interface Item
|
||||||
|
@ -18,25 +18,25 @@ struct RAII {
|
||||||
public:
|
public:
|
||||||
RAII(Blk blk): blk(blk) {}
|
RAII(Blk blk): blk(blk) {}
|
||||||
|
|
||||||
// CHECK: [ B0 (live statements at block exit) ]
|
// CHECK: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B1 (live statements at block exit) ]
|
// CHECK-NEXT: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B2 (live statements at block exit) ]
|
// CHECK-NEXT: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
~RAII() { blk(); }
|
~RAII() { blk(); }
|
||||||
|
|
||||||
// CHECK-NEXT: [ B0 (live statements at block exit) ]
|
// CHECK-NEXT: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B1 (live statements at block exit) ]
|
// CHECK-NEXT: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B2 (live statements at block exit) ]
|
// CHECK-NEXT: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
};
|
};
|
||||||
|
@ -45,57 +45,37 @@ void foo(Collection *coll) {
|
||||||
RAII raii(^{});
|
RAII raii(^{});
|
||||||
for (Item *item in coll) {}
|
for (Item *item in coll) {}
|
||||||
}
|
}
|
||||||
// CHECK-NEXT: [ B0 (live statements at block exit) ]
|
// CHECK-NEXT: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B1 (live statements at block exit) ]
|
// CHECK-NEXT: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B2 (live statements at block exit) ]
|
// CHECK-NEXT: [ B2 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: DeclStmt {{.*}}
|
|
||||||
// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *'
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: CompoundStmt {{.*}}
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-NEXT: [ B3 (live expressions at block exit) ]
|
||||||
// CHECK-NEXT: [ B3 (live statements at block exit) ]
|
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: DeclStmt {{.*}}
|
|
||||||
// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *'
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: CompoundStmt {{.*}}
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-NEXT: [ B4 (live expressions at block exit) ]
|
||||||
// CHECK-NEXT: [ B4 (live statements at block exit) ]
|
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: DeclStmt {{.*}}
|
|
||||||
// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *'
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' <LValueToRValue>
|
||||||
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *'
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: CompoundStmt {{.*}}
|
// CHECK-EMPTY:
|
||||||
|
// CHECK-NEXT: [ B5 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B5 (live statements at block exit) ]
|
// CHECK-NEXT: [ B0 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: DeclStmt {{.*}}
|
|
||||||
// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *'
|
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: CompoundStmt {{.*}}
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-NEXT: [ B0 (live statements at block exit) ]
|
// CHECK-NEXT: [ B1 (live expressions at block exit) ]
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-EMPTY:
|
|
||||||
// CHECK-NEXT: [ B1 (live statements at block exit) ]
|
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
// CHECK-EMPTY:
|
// CHECK-EMPTY:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue