xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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