1e5dd7070Spatrick //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines a DeadStores, a flow-sensitive checker that looks for
10e5dd7070Spatrick // stores to variables that are no longer live.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/AST/ASTContext.h"
15e5dd7070Spatrick #include "clang/AST/Attr.h"
16e5dd7070Spatrick #include "clang/AST/ParentMap.h"
17e5dd7070Spatrick #include "clang/AST/RecursiveASTVisitor.h"
18e5dd7070Spatrick #include "clang/Analysis/Analyses/LiveVariables.h"
19a9ac8606Spatrick #include "clang/Lex/Lexer.h"
20a9ac8606Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
22e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
24e5dd7070Spatrick #include "llvm/ADT/BitVector.h"
25a9ac8606Spatrick #include "llvm/ADT/STLExtras.h"
26e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
27e5dd7070Spatrick #include "llvm/Support/SaveAndRestore.h"
28e5dd7070Spatrick
29e5dd7070Spatrick using namespace clang;
30e5dd7070Spatrick using namespace ento;
31e5dd7070Spatrick
32e5dd7070Spatrick namespace {
33e5dd7070Spatrick
34e5dd7070Spatrick /// A simple visitor to record what VarDecls occur in EH-handling code.
35e5dd7070Spatrick class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
36e5dd7070Spatrick public:
37e5dd7070Spatrick bool inEH;
38e5dd7070Spatrick llvm::DenseSet<const VarDecl *> &S;
39e5dd7070Spatrick
TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt * S)40e5dd7070Spatrick bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
41*12c85518Srobert SaveAndRestore inFinally(inEH, true);
42e5dd7070Spatrick return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
43e5dd7070Spatrick }
44e5dd7070Spatrick
TraverseObjCAtCatchStmt(ObjCAtCatchStmt * S)45e5dd7070Spatrick bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
46*12c85518Srobert SaveAndRestore inCatch(inEH, true);
47e5dd7070Spatrick return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
48e5dd7070Spatrick }
49e5dd7070Spatrick
TraverseCXXCatchStmt(CXXCatchStmt * S)50e5dd7070Spatrick bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
51*12c85518Srobert SaveAndRestore inCatch(inEH, true);
52e5dd7070Spatrick return TraverseStmt(S->getHandlerBlock());
53e5dd7070Spatrick }
54e5dd7070Spatrick
VisitDeclRefExpr(DeclRefExpr * DR)55e5dd7070Spatrick bool VisitDeclRefExpr(DeclRefExpr *DR) {
56e5dd7070Spatrick if (inEH)
57e5dd7070Spatrick if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
58e5dd7070Spatrick S.insert(D);
59e5dd7070Spatrick return true;
60e5dd7070Spatrick }
61e5dd7070Spatrick
EHCodeVisitor(llvm::DenseSet<const VarDecl * > & S)62e5dd7070Spatrick EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
63e5dd7070Spatrick inEH(false), S(S) {}
64e5dd7070Spatrick };
65e5dd7070Spatrick
66e5dd7070Spatrick // FIXME: Eventually migrate into its own file, and have it managed by
67e5dd7070Spatrick // AnalysisManager.
68e5dd7070Spatrick class ReachableCode {
69e5dd7070Spatrick const CFG &cfg;
70e5dd7070Spatrick llvm::BitVector reachable;
71e5dd7070Spatrick public:
ReachableCode(const CFG & cfg)72e5dd7070Spatrick ReachableCode(const CFG &cfg)
73e5dd7070Spatrick : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
74e5dd7070Spatrick
75e5dd7070Spatrick void computeReachableBlocks();
76e5dd7070Spatrick
isReachable(const CFGBlock * block) const77e5dd7070Spatrick bool isReachable(const CFGBlock *block) const {
78e5dd7070Spatrick return reachable[block->getBlockID()];
79e5dd7070Spatrick }
80e5dd7070Spatrick };
81e5dd7070Spatrick }
82e5dd7070Spatrick
computeReachableBlocks()83e5dd7070Spatrick void ReachableCode::computeReachableBlocks() {
84e5dd7070Spatrick if (!cfg.getNumBlockIDs())
85e5dd7070Spatrick return;
86e5dd7070Spatrick
87e5dd7070Spatrick SmallVector<const CFGBlock*, 10> worklist;
88e5dd7070Spatrick worklist.push_back(&cfg.getEntry());
89e5dd7070Spatrick
90e5dd7070Spatrick while (!worklist.empty()) {
91e5dd7070Spatrick const CFGBlock *block = worklist.pop_back_val();
92e5dd7070Spatrick llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
93e5dd7070Spatrick if (isReachable)
94e5dd7070Spatrick continue;
95e5dd7070Spatrick isReachable = true;
96e5dd7070Spatrick for (CFGBlock::const_succ_iterator i = block->succ_begin(),
97e5dd7070Spatrick e = block->succ_end(); i != e; ++i)
98e5dd7070Spatrick if (const CFGBlock *succ = *i)
99e5dd7070Spatrick worklist.push_back(succ);
100e5dd7070Spatrick }
101e5dd7070Spatrick }
102e5dd7070Spatrick
103e5dd7070Spatrick static const Expr *
LookThroughTransitiveAssignmentsAndCommaOperators(const Expr * Ex)104e5dd7070Spatrick LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
105e5dd7070Spatrick while (Ex) {
106*12c85518Srobert Ex = Ex->IgnoreParenCasts();
107*12c85518Srobert const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex);
108e5dd7070Spatrick if (!BO)
109e5dd7070Spatrick break;
110*12c85518Srobert BinaryOperatorKind Op = BO->getOpcode();
111*12c85518Srobert if (Op == BO_Assign || Op == BO_Comma) {
112e5dd7070Spatrick Ex = BO->getRHS();
113e5dd7070Spatrick continue;
114e5dd7070Spatrick }
115e5dd7070Spatrick break;
116e5dd7070Spatrick }
117e5dd7070Spatrick return Ex;
118e5dd7070Spatrick }
119e5dd7070Spatrick
120e5dd7070Spatrick namespace {
121e5dd7070Spatrick class DeadStoresChecker : public Checker<check::ASTCodeBody> {
122e5dd7070Spatrick public:
123e5dd7070Spatrick bool ShowFixIts = false;
124e5dd7070Spatrick bool WarnForDeadNestedAssignments = true;
125e5dd7070Spatrick
126e5dd7070Spatrick void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
127e5dd7070Spatrick BugReporter &BR) const;
128e5dd7070Spatrick };
129e5dd7070Spatrick
130e5dd7070Spatrick class DeadStoreObs : public LiveVariables::Observer {
131e5dd7070Spatrick const CFG &cfg;
132e5dd7070Spatrick ASTContext &Ctx;
133e5dd7070Spatrick BugReporter& BR;
134e5dd7070Spatrick const DeadStoresChecker *Checker;
135e5dd7070Spatrick AnalysisDeclContext* AC;
136e5dd7070Spatrick ParentMap& Parents;
137e5dd7070Spatrick llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
138e5dd7070Spatrick std::unique_ptr<ReachableCode> reachableCode;
139e5dd7070Spatrick const CFGBlock *currentBlock;
140e5dd7070Spatrick std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
141e5dd7070Spatrick
142e5dd7070Spatrick enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
143e5dd7070Spatrick
144e5dd7070Spatrick public:
DeadStoreObs(const CFG & cfg,ASTContext & ctx,BugReporter & br,const DeadStoresChecker * checker,AnalysisDeclContext * ac,ParentMap & parents,llvm::SmallPtrSet<const VarDecl *,20> & escaped,bool warnForDeadNestedAssignments)145e5dd7070Spatrick DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
146e5dd7070Spatrick const DeadStoresChecker *checker, AnalysisDeclContext *ac,
147e5dd7070Spatrick ParentMap &parents,
148e5dd7070Spatrick llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
149e5dd7070Spatrick bool warnForDeadNestedAssignments)
150e5dd7070Spatrick : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
151e5dd7070Spatrick Escaped(escaped), currentBlock(nullptr) {}
152e5dd7070Spatrick
~DeadStoreObs()153e5dd7070Spatrick ~DeadStoreObs() override {}
154e5dd7070Spatrick
isLive(const LiveVariables::LivenessValues & Live,const VarDecl * D)155e5dd7070Spatrick bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
156e5dd7070Spatrick if (Live.isLive(D))
157e5dd7070Spatrick return true;
158e5dd7070Spatrick // Lazily construct the set that records which VarDecls are in
159e5dd7070Spatrick // EH code.
160e5dd7070Spatrick if (!InEH.get()) {
161e5dd7070Spatrick InEH.reset(new llvm::DenseSet<const VarDecl *>());
162e5dd7070Spatrick EHCodeVisitor V(*InEH.get());
163e5dd7070Spatrick V.TraverseStmt(AC->getBody());
164e5dd7070Spatrick }
165e5dd7070Spatrick // Treat all VarDecls that occur in EH code as being "always live"
166e5dd7070Spatrick // when considering to suppress dead stores. Frequently stores
167e5dd7070Spatrick // are followed by reads in EH code, but we don't have the ability
168e5dd7070Spatrick // to analyze that yet.
169e5dd7070Spatrick return InEH->count(D);
170e5dd7070Spatrick }
171e5dd7070Spatrick
isSuppressed(SourceRange R)172e5dd7070Spatrick bool isSuppressed(SourceRange R) {
173e5dd7070Spatrick SourceManager &SMgr = Ctx.getSourceManager();
174e5dd7070Spatrick SourceLocation Loc = R.getBegin();
175e5dd7070Spatrick if (!Loc.isValid())
176e5dd7070Spatrick return false;
177e5dd7070Spatrick
178e5dd7070Spatrick FileID FID = SMgr.getFileID(Loc);
179e5dd7070Spatrick bool Invalid = false;
180e5dd7070Spatrick StringRef Data = SMgr.getBufferData(FID, &Invalid);
181e5dd7070Spatrick if (Invalid)
182e5dd7070Spatrick return false;
183e5dd7070Spatrick
184e5dd7070Spatrick // Files autogenerated by DriverKit IIG contain some dead stores that
185e5dd7070Spatrick // we don't want to report.
186e5dd7070Spatrick if (Data.startswith("/* iig"))
187e5dd7070Spatrick return true;
188e5dd7070Spatrick
189e5dd7070Spatrick return false;
190e5dd7070Spatrick }
191e5dd7070Spatrick
Report(const VarDecl * V,DeadStoreKind dsk,PathDiagnosticLocation L,SourceRange R)192e5dd7070Spatrick void Report(const VarDecl *V, DeadStoreKind dsk,
193e5dd7070Spatrick PathDiagnosticLocation L, SourceRange R) {
194e5dd7070Spatrick if (Escaped.count(V))
195e5dd7070Spatrick return;
196e5dd7070Spatrick
197e5dd7070Spatrick // Compute reachable blocks within the CFG for trivial cases
198e5dd7070Spatrick // where a bogus dead store can be reported because itself is unreachable.
199e5dd7070Spatrick if (!reachableCode.get()) {
200e5dd7070Spatrick reachableCode.reset(new ReachableCode(cfg));
201e5dd7070Spatrick reachableCode->computeReachableBlocks();
202e5dd7070Spatrick }
203e5dd7070Spatrick
204e5dd7070Spatrick if (!reachableCode->isReachable(currentBlock))
205e5dd7070Spatrick return;
206e5dd7070Spatrick
207e5dd7070Spatrick if (isSuppressed(R))
208e5dd7070Spatrick return;
209e5dd7070Spatrick
210e5dd7070Spatrick SmallString<64> buf;
211e5dd7070Spatrick llvm::raw_svector_ostream os(buf);
212e5dd7070Spatrick const char *BugType = nullptr;
213e5dd7070Spatrick
214e5dd7070Spatrick SmallVector<FixItHint, 1> Fixits;
215e5dd7070Spatrick
216e5dd7070Spatrick switch (dsk) {
217e5dd7070Spatrick case DeadInit: {
218e5dd7070Spatrick BugType = "Dead initialization";
219e5dd7070Spatrick os << "Value stored to '" << *V
220e5dd7070Spatrick << "' during its initialization is never read";
221e5dd7070Spatrick
222e5dd7070Spatrick ASTContext &ACtx = V->getASTContext();
223e5dd7070Spatrick if (Checker->ShowFixIts) {
224e5dd7070Spatrick if (V->getInit()->HasSideEffects(ACtx,
225e5dd7070Spatrick /*IncludePossibleEffects=*/true)) {
226e5dd7070Spatrick break;
227e5dd7070Spatrick }
228e5dd7070Spatrick SourceManager &SM = ACtx.getSourceManager();
229e5dd7070Spatrick const LangOptions &LO = ACtx.getLangOpts();
230e5dd7070Spatrick SourceLocation L1 =
231e5dd7070Spatrick Lexer::findNextToken(
232e5dd7070Spatrick V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
233e5dd7070Spatrick SM, LO)->getEndLoc();
234e5dd7070Spatrick SourceLocation L2 =
235e5dd7070Spatrick Lexer::getLocForEndOfToken(V->getInit()->getEndLoc(), 1, SM, LO);
236e5dd7070Spatrick Fixits.push_back(FixItHint::CreateRemoval({L1, L2}));
237e5dd7070Spatrick }
238e5dd7070Spatrick break;
239e5dd7070Spatrick }
240e5dd7070Spatrick
241e5dd7070Spatrick case DeadIncrement:
242e5dd7070Spatrick BugType = "Dead increment";
243*12c85518Srobert [[fallthrough]];
244e5dd7070Spatrick case Standard:
245e5dd7070Spatrick if (!BugType) BugType = "Dead assignment";
246e5dd7070Spatrick os << "Value stored to '" << *V << "' is never read";
247e5dd7070Spatrick break;
248e5dd7070Spatrick
249e5dd7070Spatrick // eg.: f((x = foo()))
250e5dd7070Spatrick case Enclosing:
251e5dd7070Spatrick if (!Checker->WarnForDeadNestedAssignments)
252e5dd7070Spatrick return;
253e5dd7070Spatrick BugType = "Dead nested assignment";
254e5dd7070Spatrick os << "Although the value stored to '" << *V
255e5dd7070Spatrick << "' is used in the enclosing expression, the value is never "
256e5dd7070Spatrick "actually read from '"
257e5dd7070Spatrick << *V << "'";
258e5dd7070Spatrick break;
259e5dd7070Spatrick }
260e5dd7070Spatrick
261a9ac8606Spatrick BR.EmitBasicReport(AC->getDecl(), Checker, BugType, categories::UnusedCode,
262a9ac8606Spatrick os.str(), L, R, Fixits);
263e5dd7070Spatrick }
264e5dd7070Spatrick
CheckVarDecl(const VarDecl * VD,const Expr * Ex,const Expr * Val,DeadStoreKind dsk,const LiveVariables::LivenessValues & Live)265e5dd7070Spatrick void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
266e5dd7070Spatrick DeadStoreKind dsk,
267e5dd7070Spatrick const LiveVariables::LivenessValues &Live) {
268e5dd7070Spatrick
269e5dd7070Spatrick if (!VD->hasLocalStorage())
270e5dd7070Spatrick return;
271e5dd7070Spatrick // Reference types confuse the dead stores checker. Skip them
272e5dd7070Spatrick // for now.
273e5dd7070Spatrick if (VD->getType()->getAs<ReferenceType>())
274e5dd7070Spatrick return;
275e5dd7070Spatrick
276e5dd7070Spatrick if (!isLive(Live, VD) &&
277e5dd7070Spatrick !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
278e5dd7070Spatrick VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
279e5dd7070Spatrick
280e5dd7070Spatrick PathDiagnosticLocation ExLoc =
281e5dd7070Spatrick PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
282e5dd7070Spatrick Report(VD, dsk, ExLoc, Val->getSourceRange());
283e5dd7070Spatrick }
284e5dd7070Spatrick }
285e5dd7070Spatrick
CheckDeclRef(const DeclRefExpr * DR,const Expr * Val,DeadStoreKind dsk,const LiveVariables::LivenessValues & Live)286e5dd7070Spatrick void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
287e5dd7070Spatrick const LiveVariables::LivenessValues& Live) {
288e5dd7070Spatrick if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
289e5dd7070Spatrick CheckVarDecl(VD, DR, Val, dsk, Live);
290e5dd7070Spatrick }
291e5dd7070Spatrick
isIncrement(VarDecl * VD,const BinaryOperator * B)292e5dd7070Spatrick bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
293e5dd7070Spatrick if (B->isCompoundAssignmentOp())
294e5dd7070Spatrick return true;
295e5dd7070Spatrick
296e5dd7070Spatrick const Expr *RHS = B->getRHS()->IgnoreParenCasts();
297e5dd7070Spatrick const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
298e5dd7070Spatrick
299e5dd7070Spatrick if (!BRHS)
300e5dd7070Spatrick return false;
301e5dd7070Spatrick
302e5dd7070Spatrick const DeclRefExpr *DR;
303e5dd7070Spatrick
304e5dd7070Spatrick if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
305e5dd7070Spatrick if (DR->getDecl() == VD)
306e5dd7070Spatrick return true;
307e5dd7070Spatrick
308e5dd7070Spatrick if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
309e5dd7070Spatrick if (DR->getDecl() == VD)
310e5dd7070Spatrick return true;
311e5dd7070Spatrick
312e5dd7070Spatrick return false;
313e5dd7070Spatrick }
314e5dd7070Spatrick
observeStmt(const Stmt * S,const CFGBlock * block,const LiveVariables::LivenessValues & Live)315e5dd7070Spatrick void observeStmt(const Stmt *S, const CFGBlock *block,
316e5dd7070Spatrick const LiveVariables::LivenessValues &Live) override {
317e5dd7070Spatrick
318e5dd7070Spatrick currentBlock = block;
319e5dd7070Spatrick
320e5dd7070Spatrick // Skip statements in macros.
321e5dd7070Spatrick if (S->getBeginLoc().isMacroID())
322e5dd7070Spatrick return;
323e5dd7070Spatrick
324e5dd7070Spatrick // Only cover dead stores from regular assignments. ++/-- dead stores
325e5dd7070Spatrick // have never flagged a real bug.
326e5dd7070Spatrick if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
327e5dd7070Spatrick if (!B->isAssignmentOp()) return; // Skip non-assignments.
328e5dd7070Spatrick
329e5dd7070Spatrick if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
330e5dd7070Spatrick if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
331e5dd7070Spatrick // Special case: check for assigning null to a pointer.
332e5dd7070Spatrick // This is a common form of defensive programming.
333e5dd7070Spatrick const Expr *RHS =
334e5dd7070Spatrick LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
335e5dd7070Spatrick
336e5dd7070Spatrick QualType T = VD->getType();
337e5dd7070Spatrick if (T.isVolatileQualified())
338e5dd7070Spatrick return;
339e5dd7070Spatrick if (T->isPointerType() || T->isObjCObjectPointerType()) {
340e5dd7070Spatrick if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
341e5dd7070Spatrick return;
342e5dd7070Spatrick }
343e5dd7070Spatrick
344e5dd7070Spatrick // Special case: self-assignments. These are often used to shut up
345e5dd7070Spatrick // "unused variable" compiler warnings.
346e5dd7070Spatrick if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
347e5dd7070Spatrick if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
348e5dd7070Spatrick return;
349e5dd7070Spatrick
350e5dd7070Spatrick // Otherwise, issue a warning.
351e5dd7070Spatrick DeadStoreKind dsk = Parents.isConsumedExpr(B)
352e5dd7070Spatrick ? Enclosing
353e5dd7070Spatrick : (isIncrement(VD,B) ? DeadIncrement : Standard);
354e5dd7070Spatrick
355e5dd7070Spatrick CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
356e5dd7070Spatrick }
357e5dd7070Spatrick }
358e5dd7070Spatrick else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
359e5dd7070Spatrick if (!U->isIncrementOp() || U->isPrefix())
360e5dd7070Spatrick return;
361e5dd7070Spatrick
362e5dd7070Spatrick const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
363e5dd7070Spatrick if (!parent || !isa<ReturnStmt>(parent))
364e5dd7070Spatrick return;
365e5dd7070Spatrick
366e5dd7070Spatrick const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
367e5dd7070Spatrick
368e5dd7070Spatrick if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
369e5dd7070Spatrick CheckDeclRef(DR, U, DeadIncrement, Live);
370e5dd7070Spatrick }
371e5dd7070Spatrick else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
372e5dd7070Spatrick // Iterate through the decls. Warn if any initializers are complex
373e5dd7070Spatrick // expressions that are not live (never used).
374e5dd7070Spatrick for (const auto *DI : DS->decls()) {
375e5dd7070Spatrick const auto *V = dyn_cast<VarDecl>(DI);
376e5dd7070Spatrick
377e5dd7070Spatrick if (!V)
378e5dd7070Spatrick continue;
379e5dd7070Spatrick
380e5dd7070Spatrick if (V->hasLocalStorage()) {
381e5dd7070Spatrick // Reference types confuse the dead stores checker. Skip them
382e5dd7070Spatrick // for now.
383e5dd7070Spatrick if (V->getType()->getAs<ReferenceType>())
384e5dd7070Spatrick return;
385e5dd7070Spatrick
386e5dd7070Spatrick if (const Expr *E = V->getInit()) {
387e5dd7070Spatrick while (const FullExpr *FE = dyn_cast<FullExpr>(E))
388e5dd7070Spatrick E = FE->getSubExpr();
389e5dd7070Spatrick
390e5dd7070Spatrick // Look through transitive assignments, e.g.:
391e5dd7070Spatrick // int x = y = 0;
392e5dd7070Spatrick E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
393e5dd7070Spatrick
394e5dd7070Spatrick // Don't warn on C++ objects (yet) until we can show that their
395e5dd7070Spatrick // constructors/destructors don't have side effects.
396e5dd7070Spatrick if (isa<CXXConstructExpr>(E))
397e5dd7070Spatrick return;
398e5dd7070Spatrick
399e5dd7070Spatrick // A dead initialization is a variable that is dead after it
400e5dd7070Spatrick // is initialized. We don't flag warnings for those variables
401e5dd7070Spatrick // marked 'unused' or 'objc_precise_lifetime'.
402e5dd7070Spatrick if (!isLive(Live, V) &&
403e5dd7070Spatrick !V->hasAttr<UnusedAttr>() &&
404e5dd7070Spatrick !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
405e5dd7070Spatrick // Special case: check for initializations with constants.
406e5dd7070Spatrick //
407e5dd7070Spatrick // e.g. : int x = 0;
408a9ac8606Spatrick // struct A = {0, 1};
409a9ac8606Spatrick // struct B = {{0}, {1, 2}};
410e5dd7070Spatrick //
411e5dd7070Spatrick // If x is EVER assigned a new value later, don't issue
412e5dd7070Spatrick // a warning. This is because such initialization can be
413e5dd7070Spatrick // due to defensive programming.
414a9ac8606Spatrick if (isConstant(E))
415e5dd7070Spatrick return;
416e5dd7070Spatrick
417*12c85518Srobert if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
418e5dd7070Spatrick if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
419e5dd7070Spatrick // Special case: check for initialization from constant
420e5dd7070Spatrick // variables.
421e5dd7070Spatrick //
422e5dd7070Spatrick // e.g. extern const int MyConstant;
423e5dd7070Spatrick // int x = MyConstant;
424e5dd7070Spatrick //
425e5dd7070Spatrick if (VD->hasGlobalStorage() &&
426e5dd7070Spatrick VD->getType().isConstQualified())
427e5dd7070Spatrick return;
428e5dd7070Spatrick // Special case: check for initialization from scalar
429e5dd7070Spatrick // parameters. This is often a form of defensive
430e5dd7070Spatrick // programming. Non-scalars are still an error since
431e5dd7070Spatrick // because it more likely represents an actual algorithmic
432e5dd7070Spatrick // bug.
433e5dd7070Spatrick if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
434e5dd7070Spatrick return;
435e5dd7070Spatrick }
436e5dd7070Spatrick
437e5dd7070Spatrick PathDiagnosticLocation Loc =
438e5dd7070Spatrick PathDiagnosticLocation::create(V, BR.getSourceManager());
439*12c85518Srobert Report(V, DeadInit, Loc, V->getInit()->getSourceRange());
440e5dd7070Spatrick }
441e5dd7070Spatrick }
442e5dd7070Spatrick }
443e5dd7070Spatrick }
444e5dd7070Spatrick }
445a9ac8606Spatrick
446a9ac8606Spatrick private:
447a9ac8606Spatrick /// Return true if the given init list can be interpreted as constant
isConstant(const InitListExpr * Candidate) const448a9ac8606Spatrick bool isConstant(const InitListExpr *Candidate) const {
449a9ac8606Spatrick // We consider init list to be constant if each member of the list can be
450a9ac8606Spatrick // interpreted as constant.
451*12c85518Srobert return llvm::all_of(Candidate->inits(), [this](const Expr *Init) {
452*12c85518Srobert return isConstant(Init->IgnoreParenCasts());
453*12c85518Srobert });
454a9ac8606Spatrick }
455a9ac8606Spatrick
456a9ac8606Spatrick /// Return true if the given expression can be interpreted as constant
isConstant(const Expr * E) const457a9ac8606Spatrick bool isConstant(const Expr *E) const {
458a9ac8606Spatrick // It looks like E itself is a constant
459a9ac8606Spatrick if (E->isEvaluatable(Ctx))
460a9ac8606Spatrick return true;
461a9ac8606Spatrick
462a9ac8606Spatrick // We should also allow defensive initialization of structs, i.e. { 0 }
463*12c85518Srobert if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
464a9ac8606Spatrick return isConstant(ILE);
465a9ac8606Spatrick }
466a9ac8606Spatrick
467a9ac8606Spatrick return false;
468a9ac8606Spatrick }
469e5dd7070Spatrick };
470e5dd7070Spatrick
471e5dd7070Spatrick } // end anonymous namespace
472e5dd7070Spatrick
473e5dd7070Spatrick //===----------------------------------------------------------------------===//
474e5dd7070Spatrick // Driver function to invoke the Dead-Stores checker on a CFG.
475e5dd7070Spatrick //===----------------------------------------------------------------------===//
476e5dd7070Spatrick
477e5dd7070Spatrick namespace {
478e5dd7070Spatrick class FindEscaped {
479e5dd7070Spatrick public:
480e5dd7070Spatrick llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
481e5dd7070Spatrick
operator ()(const Stmt * S)482e5dd7070Spatrick void operator()(const Stmt *S) {
483e5dd7070Spatrick // Check for '&'. Any VarDecl whose address has been taken we treat as
484e5dd7070Spatrick // escaped.
485e5dd7070Spatrick // FIXME: What about references?
486e5dd7070Spatrick if (auto *LE = dyn_cast<LambdaExpr>(S)) {
487e5dd7070Spatrick findLambdaReferenceCaptures(LE);
488e5dd7070Spatrick return;
489e5dd7070Spatrick }
490e5dd7070Spatrick
491e5dd7070Spatrick const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
492e5dd7070Spatrick if (!U)
493e5dd7070Spatrick return;
494e5dd7070Spatrick if (U->getOpcode() != UO_AddrOf)
495e5dd7070Spatrick return;
496e5dd7070Spatrick
497e5dd7070Spatrick const Expr *E = U->getSubExpr()->IgnoreParenCasts();
498e5dd7070Spatrick if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
499e5dd7070Spatrick if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
500e5dd7070Spatrick Escaped.insert(VD);
501e5dd7070Spatrick }
502e5dd7070Spatrick
503e5dd7070Spatrick // Treat local variables captured by reference in C++ lambdas as escaped.
findLambdaReferenceCaptures(const LambdaExpr * LE)504e5dd7070Spatrick void findLambdaReferenceCaptures(const LambdaExpr *LE) {
505e5dd7070Spatrick const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
506*12c85518Srobert llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
507e5dd7070Spatrick FieldDecl *ThisCaptureField;
508e5dd7070Spatrick LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
509e5dd7070Spatrick
510e5dd7070Spatrick for (const LambdaCapture &C : LE->captures()) {
511e5dd7070Spatrick if (!C.capturesVariable())
512e5dd7070Spatrick continue;
513e5dd7070Spatrick
514*12c85518Srobert ValueDecl *VD = C.getCapturedVar();
515e5dd7070Spatrick const FieldDecl *FD = CaptureFields[VD];
516*12c85518Srobert if (!FD || !isa<VarDecl>(VD))
517e5dd7070Spatrick continue;
518e5dd7070Spatrick
519e5dd7070Spatrick // If the capture field is a reference type, it is capture-by-reference.
520e5dd7070Spatrick if (FD->getType()->isReferenceType())
521*12c85518Srobert Escaped.insert(cast<VarDecl>(VD));
522e5dd7070Spatrick }
523e5dd7070Spatrick }
524e5dd7070Spatrick };
525e5dd7070Spatrick } // end anonymous namespace
526e5dd7070Spatrick
527e5dd7070Spatrick
528e5dd7070Spatrick //===----------------------------------------------------------------------===//
529e5dd7070Spatrick // DeadStoresChecker
530e5dd7070Spatrick //===----------------------------------------------------------------------===//
531e5dd7070Spatrick
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const532e5dd7070Spatrick void DeadStoresChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
533e5dd7070Spatrick BugReporter &BR) const {
534e5dd7070Spatrick
535e5dd7070Spatrick // Don't do anything for template instantiations.
536e5dd7070Spatrick // Proving that code in a template instantiation is "dead"
537e5dd7070Spatrick // means proving that it is dead in all instantiations.
538e5dd7070Spatrick // This same problem exists with -Wunreachable-code.
539e5dd7070Spatrick if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
540e5dd7070Spatrick if (FD->isTemplateInstantiation())
541e5dd7070Spatrick return;
542e5dd7070Spatrick
543e5dd7070Spatrick if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
544e5dd7070Spatrick CFG &cfg = *mgr.getCFG(D);
545e5dd7070Spatrick AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
546e5dd7070Spatrick ParentMap &pmap = mgr.getParentMap(D);
547e5dd7070Spatrick FindEscaped FS;
548e5dd7070Spatrick cfg.VisitBlockStmts(FS);
549e5dd7070Spatrick DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped,
550e5dd7070Spatrick WarnForDeadNestedAssignments);
551e5dd7070Spatrick L->runOnAllBlocks(A);
552e5dd7070Spatrick }
553e5dd7070Spatrick }
554e5dd7070Spatrick
registerDeadStoresChecker(CheckerManager & Mgr)555e5dd7070Spatrick void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
556e5dd7070Spatrick auto *Chk = Mgr.registerChecker<DeadStoresChecker>();
557e5dd7070Spatrick
558e5dd7070Spatrick const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
559e5dd7070Spatrick Chk->WarnForDeadNestedAssignments =
560e5dd7070Spatrick AnOpts.getCheckerBooleanOption(Chk, "WarnForDeadNestedAssignments");
561e5dd7070Spatrick Chk->ShowFixIts =
562e5dd7070Spatrick AnOpts.getCheckerBooleanOption(Chk, "ShowFixIts");
563e5dd7070Spatrick }
564e5dd7070Spatrick
shouldRegisterDeadStoresChecker(const CheckerManager & mgr)565ec727ea7Spatrick bool ento::shouldRegisterDeadStoresChecker(const CheckerManager &mgr) {
566e5dd7070Spatrick return true;
567e5dd7070Spatrick }
568