xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp (revision c7ed09378e0b1808007f62355c3b6f788ce0398c)
1 //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines a DeadStores, a flow-sensitive checker that looks for
11 //  stores to variables that are no longer live.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
16 #include "clang/Analysis/Analyses/LiveVariables.h"
17 #include "clang/Analysis/Visitors/CFGRecStmtVisitor.h"
18 #include "clang/StaticAnalyzer/BugReporter/BugReporter.h"
19 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
20 #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
21 #include "clang/Basic/Diagnostic.h"
22 #include "clang/AST/ASTContext.h"
23 #include "clang/AST/ParentMap.h"
24 #include "llvm/ADT/SmallPtrSet.h"
25 
26 using namespace clang;
27 using namespace ento;
28 
29 namespace {
30 
31 class DeadStoreObs : public LiveVariables::ObserverTy {
32   ASTContext &Ctx;
33   BugReporter& BR;
34   ParentMap& Parents;
35   llvm::SmallPtrSet<VarDecl*, 20> Escaped;
36 
37   enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
38 
39 public:
40   DeadStoreObs(ASTContext &ctx, BugReporter& br, ParentMap& parents,
41                llvm::SmallPtrSet<VarDecl*, 20> &escaped)
42     : Ctx(ctx), BR(br), Parents(parents), Escaped(escaped) {}
43 
44   virtual ~DeadStoreObs() {}
45 
46   void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) {
47     if (Escaped.count(V))
48       return;
49 
50     std::string name = V->getNameAsString();
51 
52     const char* BugType = 0;
53     std::string msg;
54 
55     switch (dsk) {
56       default:
57         assert(false && "Impossible dead store type.");
58 
59       case DeadInit:
60         BugType = "Dead initialization";
61         msg = "Value stored to '" + name +
62           "' during its initialization is never read";
63         break;
64 
65       case DeadIncrement:
66         BugType = "Dead increment";
67       case Standard:
68         if (!BugType) BugType = "Dead assignment";
69         msg = "Value stored to '" + name + "' is never read";
70         break;
71 
72       case Enclosing:
73         // Don't report issues in this case, e.g.: "if (x = foo())",
74         // where 'x' is unused later.  We have yet to see a case where
75         // this is a real bug.
76         return;
77     }
78 
79     BR.EmitBasicReport(BugType, "Dead store", msg, L, R);
80   }
81 
82   void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
83                     DeadStoreKind dsk,
84                     const LiveVariables::AnalysisDataTy& AD,
85                     const LiveVariables::ValTy& Live) {
86 
87     if (!VD->hasLocalStorage())
88       return;
89     // Reference types confuse the dead stores checker.  Skip them
90     // for now.
91     if (VD->getType()->getAs<ReferenceType>())
92       return;
93 
94     if (!Live(VD, AD) &&
95         !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>()))
96       Report(VD, dsk, Ex->getSourceRange().getBegin(),
97              Val->getSourceRange());
98   }
99 
100   void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk,
101                     const LiveVariables::AnalysisDataTy& AD,
102                     const LiveVariables::ValTy& Live) {
103     if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
104       CheckVarDecl(VD, DR, Val, dsk, AD, Live);
105   }
106 
107   bool isIncrement(VarDecl* VD, BinaryOperator* B) {
108     if (B->isCompoundAssignmentOp())
109       return true;
110 
111     Expr* RHS = B->getRHS()->IgnoreParenCasts();
112     BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
113 
114     if (!BRHS)
115       return false;
116 
117     DeclRefExpr *DR;
118 
119     if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
120       if (DR->getDecl() == VD)
121         return true;
122 
123     if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
124       if (DR->getDecl() == VD)
125         return true;
126 
127     return false;
128   }
129 
130   virtual void ObserveStmt(Stmt* S,
131                            const LiveVariables::AnalysisDataTy& AD,
132                            const LiveVariables::ValTy& Live) {
133 
134     // Skip statements in macros.
135     if (S->getLocStart().isMacroID())
136       return;
137 
138     if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
139       if (!B->isAssignmentOp()) return; // Skip non-assignments.
140 
141       if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
142         if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
143           // Special case: check for assigning null to a pointer.
144           //  This is a common form of defensive programming.
145           QualType T = VD->getType();
146           if (T->isPointerType() || T->isObjCObjectPointerType()) {
147             if (B->getRHS()->isNullPointerConstant(Ctx,
148                                               Expr::NPC_ValueDependentIsNull))
149               return;
150           }
151 
152           Expr* RHS = B->getRHS()->IgnoreParenCasts();
153           // Special case: self-assignments.  These are often used to shut up
154           //  "unused variable" compiler warnings.
155           if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS))
156             if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
157               return;
158 
159           // Otherwise, issue a warning.
160           DeadStoreKind dsk = Parents.isConsumedExpr(B)
161                               ? Enclosing
162                               : (isIncrement(VD,B) ? DeadIncrement : Standard);
163 
164           CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live);
165         }
166     }
167     else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
168       if (!U->isIncrementOp())
169         return;
170 
171       // Handle: ++x within a subexpression.  The solution is not warn
172       //  about preincrements to dead variables when the preincrement occurs
173       //  as a subexpression.  This can lead to false negatives, e.g. "(++x);"
174       //  A generalized dead code checker should find such issues.
175       if (U->isPrefix() && Parents.isConsumedExpr(U))
176         return;
177 
178       Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
179 
180       if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
181         CheckDeclRef(DR, U, DeadIncrement, AD, Live);
182     }
183     else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
184       // Iterate through the decls.  Warn if any initializers are complex
185       // expressions that are not live (never used).
186       for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
187            DI != DE; ++DI) {
188 
189         VarDecl* V = dyn_cast<VarDecl>(*DI);
190 
191         if (!V)
192           continue;
193 
194         if (V->hasLocalStorage()) {
195           // Reference types confuse the dead stores checker.  Skip them
196           // for now.
197           if (V->getType()->getAs<ReferenceType>())
198             return;
199 
200           if (Expr* E = V->getInit()) {
201             // Don't warn on C++ objects (yet) until we can show that their
202             // constructors/destructors don't have side effects.
203             if (isa<CXXConstructExpr>(E))
204               return;
205 
206             if (isa<ExprWithCleanups>(E))
207               return;
208 
209             // A dead initialization is a variable that is dead after it
210             // is initialized.  We don't flag warnings for those variables
211             // marked 'unused'.
212             if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) {
213               // Special case: check for initializations with constants.
214               //
215               //  e.g. : int x = 0;
216               //
217               // If x is EVER assigned a new value later, don't issue
218               // a warning.  This is because such initialization can be
219               // due to defensive programming.
220               if (E->isConstantInitializer(Ctx, false))
221                 return;
222 
223               if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
224                 if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
225                   // Special case: check for initialization from constant
226                   //  variables.
227                   //
228                   //  e.g. extern const int MyConstant;
229                   //       int x = MyConstant;
230                   //
231                   if (VD->hasGlobalStorage() &&
232                       VD->getType().isConstQualified())
233                     return;
234                   // Special case: check for initialization from scalar
235                   //  parameters.  This is often a form of defensive
236                   //  programming.  Non-scalars are still an error since
237                   //  because it more likely represents an actual algorithmic
238                   //  bug.
239                   if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
240                     return;
241                 }
242 
243               Report(V, DeadInit, V->getLocation(), E->getSourceRange());
244             }
245           }
246         }
247       }
248   }
249 };
250 
251 } // end anonymous namespace
252 
253 //===----------------------------------------------------------------------===//
254 // Driver function to invoke the Dead-Stores checker on a CFG.
255 //===----------------------------------------------------------------------===//
256 
257 namespace {
258 class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{
259   CFG *cfg;
260 public:
261   FindEscaped(CFG *c) : cfg(c) {}
262 
263   CFG& getCFG() { return *cfg; }
264 
265   llvm::SmallPtrSet<VarDecl*, 20> Escaped;
266 
267   void VisitUnaryOperator(UnaryOperator* U) {
268     // Check for '&'.  Any VarDecl whose value has its address-taken we
269     // treat as escaped.
270     Expr* E = U->getSubExpr()->IgnoreParenCasts();
271     if (U->getOpcode() == UO_AddrOf)
272       if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E))
273         if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) {
274           Escaped.insert(VD);
275           return;
276         }
277     Visit(E);
278   }
279 };
280 } // end anonymous namespace
281 
282 
283 void ento::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap,
284                             BugReporter& BR) {
285   FindEscaped FS(&cfg);
286   FS.getCFG().VisitBlockStmts(FS);
287   DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped);
288   L.runOnAllBlocks(cfg, &A);
289 }
290