#ifndef LLVM_CLANG_CFG_H
#define LLVM_CLANG_CFG_H
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
#include "clang/Analysis/Support/BumpVector.h"
#include <cassert>
class LangOptions;
class ASTContext;
+/// CFGElement - Represents a top-level expression in a basic block.
+class CFGElement {
+ llvm::PointerIntPair<Stmt *, 1> Data;
+public:
+ explicit CFGElement() {}
+ CFGElement(Stmt *S, bool lvalue) : Data(S, lvalue ? 1 : 0) {}
+ Stmt *getStmt() const { return Data.getPointer(); }
+ bool asLValue() const { return Data.getInt() == 1; }
+ operator Stmt*() const { return getStmt(); }
+};
+
/// CFGBlock - Represents a single basic block in a source-level CFG.
/// It consists of:
///
///
class CFGBlock {
class StatementList {
- typedef BumpVector<Stmt*> ImplTy;
+ typedef BumpVector<CFGElement> ImplTy;
ImplTy Impl;
public:
StatementList(BumpVectorContext &C) : Impl(C, 4) {}
typedef ImplTy::iterator reverse_iterator;
typedef ImplTy::const_iterator const_reverse_iterator;
- void push_back(Stmt *s, BumpVectorContext &C) { Impl.push_back(s, C); }
- Stmt *front() const { return Impl.back(); }
- Stmt *back() const { return Impl.front(); }
+ void push_back(CFGElement e, BumpVectorContext &C) { Impl.push_back(e, C); }
+ CFGElement front() const { return Impl.back(); }
+ CFGElement back() const { return Impl.front(); }
iterator begin() { return Impl.rbegin(); }
iterator end() { return Impl.rend(); }
const_reverse_iterator rbegin() const { return Impl.begin(); }
const_reverse_iterator rend() const { return Impl.end(); }
- Stmt* operator[](size_t i) const {
+ CFGElement operator[](size_t i) const {
assert(i < Impl.size());
return Impl[Impl.size() - 1 - i];
}
typedef StatementList::reverse_iterator reverse_iterator;
typedef StatementList::const_reverse_iterator const_reverse_iterator;
- Stmt* front() const { return Stmts.front(); }
- Stmt* back() const { return Stmts.back(); }
+ CFGElement front() const { return Stmts.front(); }
+ CFGElement back() const { return Stmts.back(); }
iterator begin() { return Stmts.begin(); }
iterator end() { return Stmts.end(); }
unsigned size() const { return Stmts.size(); }
bool empty() const { return Stmts.empty(); }
- Stmt* operator[](size_t i) const { return Stmts[i]; }
-
+ CFGElement operator[](size_t i) const { return Stmts[i]; }
// CFG iterators
typedef AdjacentBlocks::iterator pred_iterator;
Succs.push_back(Block, C);
}
- void appendStmt(Stmt* Statement, BumpVectorContext &C) {
- Stmts.push_back(Statement, C);
+ void appendStmt(Stmt* Statement, BumpVectorContext &C, bool asLValue) {
+ Stmts.push_back(CFGElement(Statement, asLValue), C);
}
};
namespace llvm {
+/// Implement simplify_type for CFGElement, so that we can dyn_cast from
+/// CFGElement to a specific Stmt class.
+template <> struct simplify_type<const ::clang::CFGElement> {
+ typedef ::clang::Stmt* SimpleType;
+ static SimpleType getSimplifiedValue(const ::clang::CFGElement &Val) {
+ return Val.getStmt();
+ }
+};
+
+template <> struct simplify_type< ::clang::CFGElement>
+ : public simplify_type<const ::clang::CFGElement> {};
+
// Traits for: CFGBlock
-template <> struct GraphTraits<clang::CFGBlock* > {
- typedef clang::CFGBlock NodeType;
- typedef clang::CFGBlock::succ_iterator ChildIteratorType;
+template <> struct GraphTraits< ::clang::CFGBlock* > {
+ typedef ::clang::CFGBlock NodeType;
+ typedef ::clang::CFGBlock::succ_iterator ChildIteratorType;
- static NodeType* getEntryNode(clang::CFGBlock* BB)
+ static NodeType* getEntryNode(::clang::CFGBlock* BB)
{ return BB; }
static inline ChildIteratorType child_begin(NodeType* N)
{ return N->succ_end(); }
};
-template <> struct GraphTraits<const clang::CFGBlock* > {
- typedef const clang::CFGBlock NodeType;
- typedef clang::CFGBlock::const_succ_iterator ChildIteratorType;
+template <> struct GraphTraits< const ::clang::CFGBlock* > {
+ typedef const ::clang::CFGBlock NodeType;
+ typedef ::clang::CFGBlock::const_succ_iterator ChildIteratorType;
static NodeType* getEntryNode(const clang::CFGBlock* BB)
{ return BB; }
{ return N->succ_end(); }
};
-template <> struct GraphTraits<Inverse<const clang::CFGBlock*> > {
- typedef const clang::CFGBlock NodeType;
- typedef clang::CFGBlock::const_pred_iterator ChildIteratorType;
+template <> struct GraphTraits<Inverse<const ::clang::CFGBlock*> > {
+ typedef const ::clang::CFGBlock NodeType;
+ typedef ::clang::CFGBlock::const_pred_iterator ChildIteratorType;
- static NodeType *getEntryNode(Inverse<const clang::CFGBlock*> G)
+ static NodeType *getEntryNode(Inverse<const ::clang::CFGBlock*> G)
{ return G.Graph; }
static inline ChildIteratorType child_begin(NodeType* N)
// Traits for: CFG
-template <> struct GraphTraits<clang::CFG* >
- : public GraphTraits<clang::CFGBlock* > {
+template <> struct GraphTraits< ::clang::CFG* >
+ : public GraphTraits< ::clang::CFGBlock* > {
- typedef clang::CFG::iterator nodes_iterator;
+ typedef ::clang::CFG::iterator nodes_iterator;
- static NodeType *getEntryNode(clang::CFG* F) { return &F->getEntry(); }
- static nodes_iterator nodes_begin(clang::CFG* F) { return F->begin(); }
- static nodes_iterator nodes_end(clang::CFG* F) { return F->end(); }
+ static NodeType *getEntryNode(::clang::CFG* F) { return &F->getEntry(); }
+ static nodes_iterator nodes_begin(::clang::CFG* F) { return F->begin(); }
+ static nodes_iterator nodes_end(::clang::CFG* F) { return F->end(); }
};
-template <> struct GraphTraits< const clang::CFG* >
- : public GraphTraits< const clang::CFGBlock* > {
+template <> struct GraphTraits<const ::clang::CFG* >
+ : public GraphTraits<const ::clang::CFGBlock* > {
- typedef clang::CFG::const_iterator nodes_iterator;
+ typedef ::clang::CFG::const_iterator nodes_iterator;
- static NodeType *getEntryNode( const clang::CFG* F) { return &F->getEntry(); }
- static nodes_iterator nodes_begin( const clang::CFG* F) { return F->begin(); }
- static nodes_iterator nodes_end( const clang::CFG* F) { return F->end(); }
+ static NodeType *getEntryNode( const ::clang::CFG* F) {
+ return &F->getEntry();
+ }
+ static nodes_iterator nodes_begin( const ::clang::CFG* F) {
+ return F->begin();
+ }
+ static nodes_iterator nodes_end( const ::clang::CFG* F) {
+ return F->end();
+ }
};
-template <> struct GraphTraits<Inverse<const clang::CFG*> >
- : public GraphTraits<Inverse<const clang::CFGBlock*> > {
+template <> struct GraphTraits<Inverse<const ::clang::CFG*> >
+ : public GraphTraits<Inverse<const ::clang::CFGBlock*> > {
- typedef clang::CFG::const_iterator nodes_iterator;
+ typedef ::clang::CFG::const_iterator nodes_iterator;
- static NodeType *getEntryNode(const clang::CFG* F) { return &F->getExit(); }
- static nodes_iterator nodes_begin(const clang::CFG* F) { return F->begin();}
- static nodes_iterator nodes_end(const clang::CFG* F) { return F->end(); }
+ static NodeType *getEntryNode(const ::clang::CFG* F) { return &F->getExit(); }
+ static nodes_iterator nodes_begin(const ::clang::CFG* F) { return F->begin();}
+ static nodes_iterator nodes_end(const ::clang::CFG* F) { return F->end(); }
};
-
} // end llvm namespace
-
#endif
void ProcessEndPath(GREndPathNodeBuilder& Builder);
- void ProcessStmt(Stmt* S, GRStmtNodeBuilder& Builder);
-
+ void ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder);
bool ProcessBlockEntrance(CFGBlock* Blk, const GRState* State,
GRBlockCounter BC);
/// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a block-level statement.
- void ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder);
+ void ProcessStmt(CFGElement E, GRStmtNodeBuilder& builder);
/// ProcessBlockEntrance - Called by GRCoreEngine when start processing
/// a CFGBlock. This method returns true if the analysis should continue
const GRState* St, SVal location, const void *tag = 0,
QualType LoadTy = QualType());
- // FIXME: 'tag' should be removed, and a LocationContext should be used
- // instead.
- void EvalLocation(ExplodedNodeSet &Dst, Stmt *S, ExplodedNode* Pred,
- const GRState* St, SVal location,
- const void *tag, bool isLoad);
-
// FIXME: 'tag' should be removed, and a LocationContext should be used
// instead.
void EvalStore(ExplodedNodeSet& Dst, Expr* AssignE, Expr* StoreE,
ExplodedNode* Pred, const GRState* St, SVal TargetLV, SVal Val,
const void *tag = 0);
+private:
+ void EvalLoadCommon(ExplodedNodeSet& Dst, Expr* Ex, ExplodedNode* Pred,
+ const GRState* St, SVal location, const void *tag,
+ QualType LoadTy);
+
+ // FIXME: 'tag' should be removed, and a LocationContext should be used
+ // instead.
+ void EvalLocation(ExplodedNodeSet &Dst, Stmt *S, ExplodedNode* Pred,
+ const GRState* St, SVal location,
+ const void *tag, bool isLoad);
};
} // end clang namespace
/// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a block-level statement.
- virtual void ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) = 0;
+ virtual void ProcessStmt(CFGElement E, GRStmtNodeBuilder& builder) = 0;
/// ProcessBlockEntrance - Called by GRCoreEngine when start processing
/// a CFGBlock. This method returns true if the analysis should continue
}
static inline bool IsLocType(QualType T) {
- return T->isAnyPointerType() || T->isBlockPointerType();
+ return T->isAnyPointerType() || T->isBlockPointerType() ||
+ T->isReferenceType();
}
};
return const_cast<CFGBlock*>(reinterpret_cast<const CFGBlock*>(getData1()));
}
- Stmt* getFirstStmt() const {
+ CFGElement getFirstElement() const {
const CFGBlock* B = getBlock();
- return B->empty() ? NULL : B->front();
+ return B->empty() ? CFGElement() : B->front();
+ }
+
+ Stmt *getFirstStmt() const {
+ return getFirstElement().getStmt();
}
static bool classof(const ProgramPoint* Location) {
Stmt* getLastStmt() const {
const CFGBlock* B = getBlock();
- return B->empty() ? NULL : B->back();
+ return B->empty() ? CFGElement() : B->back();
}
Stmt* getTerminator() const {
return D->getLocation();
}
+
+class AddStmtChoice {
+public:
+ enum Kind { NotAlwaysAdd = 0, AlwaysAdd, AlwaysAddAsLValue };
+public:
+ AddStmtChoice(Kind kind) : k(kind) {}
+ bool alwaysAdd() const { return k != NotAlwaysAdd; }
+ bool asLValue() const { return k == AlwaysAddAsLValue; }
+private:
+ Kind k;
+};
/// CFGBuilder - This class implements CFG construction from an AST.
/// The builder is stateful: an instance of the builder should be used to only
private:
// Visitors to walk an AST and construct the CFG.
- CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, bool alwaysAdd);
- CFGBlock *VisitBinaryOperator(BinaryOperator *B, bool alwaysAdd);
- CFGBlock *VisitBlockExpr(BlockExpr* E, bool alwaysAdd);
+ CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
+ CFGBlock *VisitBinaryOperator(BinaryOperator *B, AddStmtChoice asc);
+ CFGBlock *VisitBlockExpr(BlockExpr* E, AddStmtChoice asc);
CFGBlock *VisitBreakStmt(BreakStmt *B);
- CFGBlock *VisitCallExpr(CallExpr *C, bool alwaysAdd);
+ CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc);
CFGBlock *VisitCaseStmt(CaseStmt *C);
- CFGBlock *VisitChooseExpr(ChooseExpr *C);
+ CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc);
CFGBlock *VisitCompoundStmt(CompoundStmt *C);
- CFGBlock *VisitConditionalOperator(ConditionalOperator *C);
+ CFGBlock *VisitConditionalOperator(ConditionalOperator *C,
+ AddStmtChoice asc);
CFGBlock *VisitContinueStmt(ContinueStmt *C);
CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S) { return NYS(); }
CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T);
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
CFGBlock *VisitReturnStmt(ReturnStmt* R);
- CFGBlock *VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E, bool alwaysAdd);
- CFGBlock *VisitStmtExpr(StmtExpr *S, bool alwaysAdd);
+ CFGBlock *VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E, AddStmtChoice asc);
+ CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
CFGBlock *VisitSwitchStmt(SwitchStmt *S);
CFGBlock *VisitWhileStmt(WhileStmt *W);
- CFGBlock *Visit(Stmt *S, bool alwaysAdd = false);
- CFGBlock *VisitStmt(Stmt *S, bool alwaysAdd);
+ CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd);
+ CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc);
CFGBlock *VisitChildren(Stmt* S);
// NYS == Not Yet Supported
void autoCreateBlock() { if (!Block) Block = createBlock(); }
CFGBlock *createBlock(bool add_successor = true);
bool FinishBlock(CFGBlock* B);
- CFGBlock *addStmt(Stmt *S) { return Visit(S, true); }
+ CFGBlock *addStmt(Stmt *S, AddStmtChoice asc = AddStmtChoice::AlwaysAdd) {
+ return Visit(S, asc);
+ }
- void AppendStmt(CFGBlock *B, Stmt *S) {
- B->appendStmt(S, cfg->getBumpVectorContext());
+ void AppendStmt(CFGBlock *B, Stmt *S,
+ AddStmtChoice asc = AddStmtChoice::AlwaysAdd) {
+ B->appendStmt(S, cfg->getBumpVectorContext(), asc.asLValue());
}
void AddSuccessor(CFGBlock *B, CFGBlock *S) {
/// Visit - Walk the subtree of a statement and add extra
/// blocks for ternary operators, &&, and ||. We also process "," and
/// DeclStmts (which may contain nested control-flow).
-CFGBlock* CFGBuilder::Visit(Stmt * S, bool alwaysAdd) {
+CFGBlock* CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
tryAgain:
switch (S->getStmtClass()) {
default:
- return VisitStmt(S, alwaysAdd);
+ return VisitStmt(S, asc);
case Stmt::AddrLabelExprClass:
- return VisitAddrLabelExpr(cast<AddrLabelExpr>(S), alwaysAdd);
+ return VisitAddrLabelExpr(cast<AddrLabelExpr>(S), asc);
case Stmt::BinaryOperatorClass:
- return VisitBinaryOperator(cast<BinaryOperator>(S), alwaysAdd);
+ return VisitBinaryOperator(cast<BinaryOperator>(S), asc);
case Stmt::BlockExprClass:
- return VisitBlockExpr(cast<BlockExpr>(S), alwaysAdd);
+ return VisitBlockExpr(cast<BlockExpr>(S), asc);
case Stmt::BreakStmtClass:
return VisitBreakStmt(cast<BreakStmt>(S));
case Stmt::CallExprClass:
- return VisitCallExpr(cast<CallExpr>(S), alwaysAdd);
+ return VisitCallExpr(cast<CallExpr>(S), asc);
case Stmt::CaseStmtClass:
return VisitCaseStmt(cast<CaseStmt>(S));
case Stmt::ChooseExprClass:
- return VisitChooseExpr(cast<ChooseExpr>(S));
+ return VisitChooseExpr(cast<ChooseExpr>(S), asc);
case Stmt::CompoundStmtClass:
return VisitCompoundStmt(cast<CompoundStmt>(S));
case Stmt::ConditionalOperatorClass:
- return VisitConditionalOperator(cast<ConditionalOperator>(S));
+ return VisitConditionalOperator(cast<ConditionalOperator>(S), asc);
case Stmt::ContinueStmtClass:
return VisitContinueStmt(cast<ContinueStmt>(S));
return VisitReturnStmt(cast<ReturnStmt>(S));
case Stmt::SizeOfAlignOfExprClass:
- return VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), alwaysAdd);
+ return VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), asc);
case Stmt::StmtExprClass:
- return VisitStmtExpr(cast<StmtExpr>(S), alwaysAdd);
+ return VisitStmtExpr(cast<StmtExpr>(S), asc);
case Stmt::SwitchStmtClass:
return VisitSwitchStmt(cast<SwitchStmt>(S));
}
}
-CFGBlock *CFGBuilder::VisitStmt(Stmt *S, bool alwaysAdd) {
- if (alwaysAdd) {
+CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) {
+ if (asc.alwaysAdd()) {
autoCreateBlock();
- AppendStmt(Block, S);
+ AppendStmt(Block, S, asc);
}
return VisitChildren(S);
return B;
}
-CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A, bool alwaysAdd) {
+CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A,
+ AddStmtChoice asc) {
AddressTakenLabels.insert(A->getLabel());
- if (alwaysAdd) {
+ if (asc.alwaysAdd()) {
autoCreateBlock();
- AppendStmt(Block, A);
+ AppendStmt(Block, A, asc);
}
return Block;
}
-CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, bool alwaysAdd) {
+CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
+ AddStmtChoice asc) {
if (B->isLogicalOp()) { // && or ||
CFGBlock* ConfluenceBlock = Block ? Block : createBlock();
- AppendStmt(ConfluenceBlock, B);
+ AppendStmt(ConfluenceBlock, B, asc);
if (!FinishBlock(ConfluenceBlock))
return 0;
}
else if (B->getOpcode() == BinaryOperator::Comma) { // ,
autoCreateBlock();
- AppendStmt(Block, B);
+ AppendStmt(Block, B, asc);
addStmt(B->getRHS());
return addStmt(B->getLHS());
}
- return VisitStmt(B, alwaysAdd);
+ return VisitStmt(B, asc);
}
-CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, bool alwaysAdd) {
- if (alwaysAdd) {
+CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, AddStmtChoice asc) {
+ if (asc.alwaysAdd()) {
autoCreateBlock();
- AppendStmt(Block, E);
+ AppendStmt(Block, E, asc);
}
return Block;
}
return Block;
}
-CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, bool alwaysAdd) {
+CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
// If this is a call to a no-return function, this stops the block here.
bool NoReturn = false;
if (C->getCallee()->getType().getNoReturnAttr()) {
NoReturn = true;
if (!NoReturn)
- return VisitStmt(C, alwaysAdd);
+ return VisitStmt(C, asc);
if (Block && !FinishBlock(Block))
return 0;
// Create new block with no successor for the remaining pieces.
Block = createBlock(false);
- AppendStmt(Block, C);
+ AppendStmt(Block, C, asc);
// Wire this to the exit block directly.
AddSuccessor(Block, &cfg->getExit());
return VisitChildren(C);
}
-CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C) {
+CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C,
+ AddStmtChoice asc) {
CFGBlock* ConfluenceBlock = Block ? Block : createBlock();
- AppendStmt(ConfluenceBlock, C);
+ AppendStmt(ConfluenceBlock, C, asc);
if (!FinishBlock(ConfluenceBlock))
return 0;
return LastBlock;
}
-CFGBlock *CFGBuilder::VisitConditionalOperator(ConditionalOperator *C) {
+CFGBlock *CFGBuilder::VisitConditionalOperator(ConditionalOperator *C,
+ AddStmtChoice asc) {
// Create the confluence block that will "merge" the results of the ternary
// expression.
CFGBlock* ConfluenceBlock = Block ? Block : createBlock();
- AppendStmt(ConfluenceBlock, C);
+ AppendStmt(ConfluenceBlock, C, asc);
if (!FinishBlock(ConfluenceBlock))
return 0;
case Stmt::StringLiteralClass:
break;
default:
- Block = addStmt(Init);
+ Block = addStmt(Init,
+ VD->getType()->isReferenceType()
+ ? AddStmtChoice::AlwaysAddAsLValue
+ : AddStmtChoice::AlwaysAdd);
}
}
// Add the return statement to the block. This may create new blocks if R
// contains control-flow (short-circuit operations).
- return VisitStmt(R, true);
+ return VisitStmt(R, AddStmtChoice::AlwaysAdd);
}
CFGBlock* CFGBuilder::VisitLabelStmt(LabelStmt* L) {
// Walk the 'element' expression to see if there are any side-effects. We
// generate new blocks as necesary. We DON'T add the statement by default to
// the CFG unless it contains control-flow.
- EntryConditionBlock = Visit(S->getElement(), false);
+ EntryConditionBlock = Visit(S->getElement(), AddStmtChoice::NotAlwaysAdd);
if (Block) {
if (!FinishBlock(EntryConditionBlock))
return 0;
// Add the statement to the block. This may create new blocks if S contains
// control-flow (short-circuit operations).
- return VisitStmt(S, true);
+ return VisitStmt(S, AddStmtChoice::AlwaysAdd);
}
CFGBlock* CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr* T) {
// Add the statement to the block. This may create new blocks if S contains
// control-flow (short-circuit operations).
- return VisitStmt(T, true);
+ return VisitStmt(T, AddStmtChoice::AlwaysAdd);
}
CFGBlock *CFGBuilder::VisitDoStmt(DoStmt* D) {
}
CFGBlock *CFGBuilder::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E,
- bool alwaysAdd) {
+ AddStmtChoice asc) {
- if (alwaysAdd) {
+ if (asc.alwaysAdd()) {
autoCreateBlock();
AppendStmt(Block, E);
}
/// VisitStmtExpr - Utility method to handle (nested) statement
/// expressions (a GCC extension).
-CFGBlock* CFGBuilder::VisitStmtExpr(StmtExpr *SE, bool alwaysAdd) {
- if (alwaysAdd) {
+CFGBlock* CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) {
+ if (asc.alwaysAdd()) {
autoCreateBlock();
AppendStmt(Block, SE);
}
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
- if (VD->hasLocalStorage() && !Live(VD, AD) &&
+ if (!VD->hasLocalStorage())
+ return;
+ // Reference types confuse the dead stores checker. Skip them
+ // for now.
+ if (VD->getType()->getAs<ReferenceType>())
+ return;
+
+ if (!Live(VD, AD) &&
!(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>()))
Report(VD, dsk, Ex->getSourceRange().getBegin(),
Val->getSourceRange());
void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
-
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
CheckVarDecl(VD, DR, Val, dsk, AD, Live);
}
if (!V)
continue;
-
- if (V->hasLocalStorage())
+
+ if (V->hasLocalStorage()) {
+ // Reference types confuse the dead stores checker. Skip them
+ // for now.
+ if (V->getType()->getAs<ReferenceType>())
+ return;
+
if (Expr* E = V->getInit()) {
// Don't warn on C++ objects (yet) until we can show that their
// constructors/destructors don't have side effects.
Report(V, DeadInit, V->getLocation(), E->getSourceRange());
}
}
+ }
}
}
};
SubEngine.ProcessEndPath(Builder);
}
-void GRCoreEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& Builder) {
- SubEngine.ProcessStmt(S, Builder);
+void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) {
+ SubEngine.ProcessStmt(E, Builder);
}
bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const GRState* State,
WList->setBlockCounter(Counter);
// Process the entrance of the block.
- if (Stmt* S = L.getFirstStmt()) {
+ if (CFGElement E = L.getFirstElement()) {
GRStmtNodeBuilder Builder(L.getBlock(), 0, Pred, this,
SubEngine.getStateManager());
- ProcessStmt(S, Builder);
+ ProcessStmt(E, Builder);
}
else
HandleBlockExit(L.getBlock(), Pred);
using namespace clang;
using llvm::dyn_cast;
+using llvm::dyn_cast_or_null;
using llvm::cast;
using llvm::APSInt;
// Top-level transfer function logic (Dispatcher).
//===----------------------------------------------------------------------===//
-void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) {
-
+void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) {
+ CurrentStmt = CE.getStmt();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
- S->getLocStart(),
+ CurrentStmt->getLocStart(),
"Error evaluating statement");
Builder = &builder;
EntryNode = builder.getLastNode();
- CurrentStmt = S;
-
// Set up our simple checks.
if (BatchAuditor)
Builder->setAuditor(BatchAuditor.get());
// FIXME: This should soon be removed.
ExplodedNodeSet Tmp2;
- getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, S,
+ getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CurrentStmt,
CleanedState, SymReaper);
if (Checkers.empty())
Checker *checker = I->second;
for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end();
NI != NE; ++NI)
- checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, S, *NI,
- SymReaper, tag);
+ checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, CurrentStmt,
+ *NI, SymReaper, tag);
SrcSet = DstSet;
}
}
Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));
// Visit the statement.
- Visit(S, *I, Dst);
+ if (CE.asLValue())
+ VisitLValue(cast<Expr>(CurrentStmt), *I, Dst);
+ else
+ Visit(CurrentStmt, *I, Dst);
// Do we need to auto-generate a node? We only need to do this to generate
// a node with a "cleaned" state; GRCoreEngine will actually handle
if (Dst.size() == 1 && *Dst.begin() == EntryNode
&& !Builder->HasGeneratedNode && !HasAutoGenerated) {
HasAutoGenerated = true;
- builder.generateNode(S, GetState(EntryNode), *I);
+ builder.generateNode(CurrentStmt, GetState(EntryNode), *I);
}
}
SVal V = state->getLValue(VD, Pred->getLocationContext());
- if (asLValue)
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V),
- ProgramPoint::PostLValueKind);
+ if (asLValue) {
+ // For references, the 'lvalue' is the pointer address stored in the
+ // reference region.
+ if (VD->getType()->isReferenceType()) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
+
+ MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V,
+ ProgramPoint::PostLValueKind));
+ }
else
EvalLoad(Dst, Ex, Pred, state, V);
- return;
+ return;
} else if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!asLValue && "EnumConstantDecl does not have lvalue.");
const GRState* state, SVal location,
const void *tag, QualType LoadTy) {
+ // Are we loading from a region? This actually results in two loads; one
+ // to fetch the address of the referenced value and one to fetch the
+ // referenced value.
+ if (const TypedRegion *TR =
+ dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
+
+ QualType ValTy = TR->getValueType(getContext());
+ if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) {
+ static int loadReferenceTag = 0;
+ ExplodedNodeSet Tmp;
+ EvalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag,
+ getContext().getPointerType(RT->getPointeeType()));
+
+ // Perform the load from the referenced value.
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) {
+ state = GetState(*I);
+ location = state->getSVal(Ex);
+ EvalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy);
+ }
+ return;
+ }
+ }
+
+ EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy);
+}
+
+void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex,
+ ExplodedNode* Pred,
+ const GRState* state, SVal location,
+ const void *tag, QualType LoadTy) {
+
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
EvalLocation(Tmp, Ex, Pred, state, location, tag, true);
// time a function is called those values may not be current.
ExplodedNodeSet Tmp;
- if (InitEx)
- Visit(InitEx, Pred, Tmp);
+ if (InitEx) {
+ if (VD->getType()->isReferenceType())
+ VisitLValue(InitEx, Pred, Tmp);
+ else
+ Visit(InitEx, Pred, Tmp);
+ }
else
Tmp.Add(Pred);
// Binding directly to a symbolic region should be treated as binding
// to element 0.
QualType T = SR->getSymbol()->getType(getContext());
- T = T->getAs<PointerType>()->getPointeeType();
+
+ // FIXME: Is this the right way to handle symbols that are references?
+ if (const PointerType *PT = T->getAs<PointerType>())
+ T = PT->getPointeeType();
+ else
+ T = T->getAs<ReferenceType>()->getPointeeType();
+
R = GetElementZeroRegion(SR, T);
}
/// as another region.
SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R,
QualType castTy) {
+
+#ifndef NDEBUG
if (castTy.isNull())
return V;
+
+ ASTContext &Ctx = ValMgr.getContext();
+ QualType T = R->getValueType(Ctx);
+
+ // Automatically translate references to pointers.
+ if (const ReferenceType *RT = T->getAs<ReferenceType>())
+ T = Ctx.getPointerType(RT->getPointeeType());
+
+ assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, T));
+#endif
- assert(ValMgr.getContext().hasSameUnqualifiedType(castTy,
- R->getValueType(ValMgr.getContext())));
return V;
}
//===----------------------------------------------------------------------===//
int j;
-void f1() {
+void test1() {
int x = 4;
++x; // expected-warning{{never read}}
// Dead store checking involving constructors.
//===----------------------------------------------------------------------===//
-class Test1 {
+class Test2 {
int &x;
public:
- Test1(int &y) : x(y) {}
- ~Test1() { ++x; }
+ Test2(int &y) : x(y) {}
+ ~Test2() { ++x; }
};
-int test_ctor_1(int x) {
- { Test1 a(x); } // no-warning
+int test2(int x) {
+ { Test2 a(x); } // no-warning
return x;
}
+
+//===----------------------------------------------------------------------===//
+// Test references.
+//===----------------------------------------------------------------------===//
+
+void test3_a(int x) {
+ ++x; // expected-warning{{never read}}
+}
+
+void test3_b(int &x) {
+ ++x; // no-warninge
+}
+
+void test3_c(int x) {
+ int &y = x;
+ // Shows the limitation of dead stores tracking. The write is really
+ // dead since the value cannot escape the function.
+ ++y; // no-warning
+}
+
+void test3_d(int &x) {
+ int &y = x;
+ ++y; // no-warning
+}
+
+void test3_e(int &x) {
+ int &y = x;
+}
+