24 #include "llvm/ADT/BitVector.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/Support/SaveAndRestore.h"
28 using namespace clang;
41 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
46 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
69 llvm::BitVector reachable;
71 ReachableCode(
const CFG &cfg)
72 : cfg(cfg), reachable(cfg.getNumBlockIDs(),
false) {}
74 void computeReachableBlocks();
76 bool isReachable(
const CFGBlock *block)
const {
82 void ReachableCode::computeReachableBlocks() {
83 if (!cfg.getNumBlockIDs())
87 worklist.push_back(&cfg.getEntry());
89 while (!worklist.empty()) {
90 const CFGBlock *block = worklist.pop_back_val();
91 llvm::BitVector::reference isReachable = reachable[block->
getBlockID()];
98 worklist.push_back(succ);
130 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
131 std::unique_ptr<ReachableCode> reachableCode;
133 std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
135 enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
141 llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
142 : cfg(cfg), Ctx(ctx), BR(br),
Checker(checker), AC(ac), Parents(parents),
143 Escaped(escaped), currentBlock(nullptr) {}
145 ~DeadStoreObs()
override {}
154 EHCodeVisitor V(*InEH.get());
155 V.TraverseStmt(AC->getBody());
161 return InEH->count(D);
164 void Report(
const VarDecl *V, DeadStoreKind dsk,
166 if (Escaped.count(V))
171 if (!reachableCode.get()) {
172 reachableCode.reset(
new ReachableCode(cfg));
173 reachableCode->computeReachableBlocks();
176 if (!reachableCode->isReachable(currentBlock))
180 llvm::raw_svector_ostream os(buf);
185 BugType =
"Dead initialization";
186 os <<
"Value stored to '" << *V
187 <<
"' during its initialization is never read";
191 BugType =
"Dead increment";
193 if (!BugType) BugType =
"Dead assignment";
194 os <<
"Value stored to '" << *V <<
"' is never read";
204 BR.EmitBasicReport(AC->getDecl(),
Checker, BugType,
"Dead store", os.str(),
219 if (!isLive(Live, VD) &&
221 VD->
hasAttr<ObjCPreciseLifetimeAttr>())) {
225 Report(VD, dsk, ExLoc, Val->getSourceRange());
229 void CheckDeclRef(
const DeclRefExpr *DR,
const Expr *Val, DeadStoreKind dsk,
232 CheckVarDecl(VD, DR, Val, dsk, Live);
261 currentBlock = block;
264 if (S->getLocStart().isMacroID())
288 if (
const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
289 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
293 DeadStoreKind dsk = Parents.isConsumedExpr(B)
295 : (isIncrement(VD,B) ? DeadIncrement : Standard);
297 CheckVarDecl(VD, DR, B->
getRHS(), dsk, Live);
300 else if (
const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
301 if (!U->isIncrementOp() || U->isPrefix())
304 const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
305 if (!parent || !isa<ReturnStmt>(parent))
310 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
311 CheckDeclRef(DR, U, DeadIncrement, Live);
313 else if (
const DeclStmt *DS = dyn_cast<DeclStmt>(S))
316 for (
const auto *DI : DS->decls()) {
317 const auto *V = dyn_cast<
VarDecl>(DI);
322 if (V->hasLocalStorage()) {
328 if (
const Expr *E = V->getInit()) {
330 dyn_cast<ExprWithCleanups>(E))
331 E = exprClean->getSubExpr();
339 if (isa<CXXConstructExpr>(E))
345 if (!isLive(Live, V) &&
346 !V->hasAttr<UnusedAttr>() &&
347 !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
355 if (E->isEvaluatable(Ctx))
359 dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
360 if (
const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
381 Report(V, DeadInit, Loc, E->getSourceRange());
398 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
400 void operator()(
const Stmt *S) {
411 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
424 class DeadStoresChecker :
public Checker<check::ASTCodeBody> {
434 if (FD->isTemplateInstantiation())
443 DeadStoreObs A(cfg, BR.
getContext(), BR,
this, AC, pmap, FS.Escaped);
444 L->runOnAllBlocks(A);
Defines the clang::ASTContext interface.
succ_iterator succ_begin()
bool hasGlobalStorage() const
Returns true for all variables that do not have local storage.
static bool isAssignmentOp(Opcode Opc)
bool isScalarType() const
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Represents Objective-C's @catch statement.
ASTContext & getContext()
Stmt * getHandlerBlock() const
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
A class that does preorder depth-first traversal on the entire Clang AST and visits each node...
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
void VisitBlockStmts(CALLBACK &O) const
Specifies that a value-dependent expression of integral or dependent type should be considered a null...
Expr * getSubExpr() const
unsigned getBlockID() const
CFG * getCFG(Decl const *D)
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
CHECKER * registerChecker()
Used to register checkers.
ParentMap & getParentMap(Decl const *D)
AdjacentBlocks::const_iterator const_succ_iterator
static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex)
bool isLive(const Stmt *S) const
Represents Objective-C's @finally statement.
static bool isCompoundAssignmentOp(Opcode Opc)
bool isObjCObjectPointerType() const
A reference to a declared variable, function, enum, etc. [C99 6.5.1p2].
A trivial tuple used to represent a source range.
bool isConstQualified() const
Determine whether this type is const-qualified.
T * getAnalysis(Decl const *D)
bool hasLocalStorage() const
bool isPointerType() const