1 //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a DeadStores, a flow-sensitive checker that looks for
10 // stores to variables that are no longer live.
12 //===----------------------------------------------------------------------===//
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/Attr.h"
17 #include "clang/AST/ParentMap.h"
18 #include "clang/AST/RecursiveASTVisitor.h"
19 #include "clang/Analysis/Analyses/LiveVariables.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21 #include "clang/StaticAnalyzer/Core/Checker.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
23 #include "llvm/ADT/BitVector.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/Support/SaveAndRestore.h"
27 using namespace clang;
32 /// A simple visitor to record what VarDecls occur in EH-handling code.
33 class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
36 llvm::DenseSet<const VarDecl *> &S;
38 bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
39 SaveAndRestore<bool> inFinally(inEH, true);
40 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
43 bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
44 SaveAndRestore<bool> inCatch(inEH, true);
45 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
48 bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
49 SaveAndRestore<bool> inCatch(inEH, true);
50 return TraverseStmt(S->getHandlerBlock());
53 bool VisitDeclRefExpr(DeclRefExpr *DR) {
55 if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
60 EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
64 // FIXME: Eventually migrate into its own file, and have it managed by
68 llvm::BitVector reachable;
70 ReachableCode(const CFG &cfg)
71 : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
73 void computeReachableBlocks();
75 bool isReachable(const CFGBlock *block) const {
76 return reachable[block->getBlockID()];
81 void ReachableCode::computeReachableBlocks() {
82 if (!cfg.getNumBlockIDs())
85 SmallVector<const CFGBlock*, 10> worklist;
86 worklist.push_back(&cfg.getEntry());
88 while (!worklist.empty()) {
89 const CFGBlock *block = worklist.pop_back_val();
90 llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
94 for (CFGBlock::const_succ_iterator i = block->succ_begin(),
95 e = block->succ_end(); i != e; ++i)
96 if (const CFGBlock *succ = *i)
97 worklist.push_back(succ);
102 LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
104 const BinaryOperator *BO =
105 dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
108 if (BO->getOpcode() == BO_Assign) {
112 if (BO->getOpcode() == BO_Comma) {
122 class DeadStoreObs : public LiveVariables::Observer {
126 const CheckerBase *Checker;
127 AnalysisDeclContext* AC;
129 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
130 std::unique_ptr<ReachableCode> reachableCode;
131 const CFGBlock *currentBlock;
132 std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
134 enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
137 DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
138 const CheckerBase *checker, AnalysisDeclContext *ac,
140 llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
141 : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
142 Escaped(escaped), currentBlock(nullptr) {}
144 ~DeadStoreObs() override {}
146 bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
149 // Lazily construct the set that records which VarDecls are in
152 InEH.reset(new llvm::DenseSet<const VarDecl *>());
153 EHCodeVisitor V(*InEH.get());
154 V.TraverseStmt(AC->getBody());
156 // Treat all VarDecls that occur in EH code as being "always live"
157 // when considering to suppress dead stores. Frequently stores
158 // are followed by reads in EH code, but we don't have the ability
159 // to analyze that yet.
160 return InEH->count(D);
163 bool isSuppressed(SourceRange R) {
164 SourceManager &SMgr = Ctx.getSourceManager();
165 SourceLocation Loc = R.getBegin();
169 FileID FID = SMgr.getFileID(Loc);
170 bool Invalid = false;
171 StringRef Data = SMgr.getBufferData(FID, &Invalid);
175 // Files autogenerated by DriverKit IIG contain some dead stores that
176 // we don't want to report.
177 if (Data.startswith("/* iig"))
183 void Report(const VarDecl *V, DeadStoreKind dsk,
184 PathDiagnosticLocation L, SourceRange R) {
185 if (Escaped.count(V))
188 // Compute reachable blocks within the CFG for trivial cases
189 // where a bogus dead store can be reported because itself is unreachable.
190 if (!reachableCode.get()) {
191 reachableCode.reset(new ReachableCode(cfg));
192 reachableCode->computeReachableBlocks();
195 if (!reachableCode->isReachable(currentBlock))
202 llvm::raw_svector_ostream os(buf);
203 const char *BugType = nullptr;
207 BugType = "Dead initialization";
208 os << "Value stored to '" << *V
209 << "' during its initialization is never read";
213 BugType = "Dead increment";
216 if (!BugType) BugType = "Dead assignment";
217 os << "Value stored to '" << *V << "' is never read";
221 // Don't report issues in this case, e.g.: "if (x = foo())",
222 // where 'x' is unused later. We have yet to see a case where
223 // this is a real bug.
227 BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
231 void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
233 const LiveVariables::LivenessValues &Live) {
235 if (!VD->hasLocalStorage())
237 // Reference types confuse the dead stores checker. Skip them
239 if (VD->getType()->getAs<ReferenceType>())
242 if (!isLive(Live, VD) &&
243 !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
244 VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
246 PathDiagnosticLocation ExLoc =
247 PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
248 Report(VD, dsk, ExLoc, Val->getSourceRange());
252 void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
253 const LiveVariables::LivenessValues& Live) {
254 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
255 CheckVarDecl(VD, DR, Val, dsk, Live);
258 bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
259 if (B->isCompoundAssignmentOp())
262 const Expr *RHS = B->getRHS()->IgnoreParenCasts();
263 const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
268 const DeclRefExpr *DR;
270 if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
271 if (DR->getDecl() == VD)
274 if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
275 if (DR->getDecl() == VD)
281 void observeStmt(const Stmt *S, const CFGBlock *block,
282 const LiveVariables::LivenessValues &Live) override {
284 currentBlock = block;
286 // Skip statements in macros.
287 if (S->getBeginLoc().isMacroID())
290 // Only cover dead stores from regular assignments. ++/-- dead stores
291 // have never flagged a real bug.
292 if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
293 if (!B->isAssignmentOp()) return; // Skip non-assignments.
295 if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
296 if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
297 // Special case: check for assigning null to a pointer.
298 // This is a common form of defensive programming.
300 LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
301 RHS = RHS->IgnoreParenCasts();
303 QualType T = VD->getType();
304 if (T.isVolatileQualified())
306 if (T->isPointerType() || T->isObjCObjectPointerType()) {
307 if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
311 // Special case: self-assignments. These are often used to shut up
312 // "unused variable" compiler warnings.
313 if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
314 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
317 // Otherwise, issue a warning.
318 DeadStoreKind dsk = Parents.isConsumedExpr(B)
320 : (isIncrement(VD,B) ? DeadIncrement : Standard);
322 CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
325 else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
326 if (!U->isIncrementOp() || U->isPrefix())
329 const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
330 if (!parent || !isa<ReturnStmt>(parent))
333 const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
335 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
336 CheckDeclRef(DR, U, DeadIncrement, Live);
338 else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
339 // Iterate through the decls. Warn if any initializers are complex
340 // expressions that are not live (never used).
341 for (const auto *DI : DS->decls()) {
342 const auto *V = dyn_cast<VarDecl>(DI);
347 if (V->hasLocalStorage()) {
348 // Reference types confuse the dead stores checker. Skip them
350 if (V->getType()->getAs<ReferenceType>())
353 if (const Expr *E = V->getInit()) {
354 while (const FullExpr *FE = dyn_cast<FullExpr>(E))
355 E = FE->getSubExpr();
357 // Look through transitive assignments, e.g.:
359 E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
361 // Don't warn on C++ objects (yet) until we can show that their
362 // constructors/destructors don't have side effects.
363 if (isa<CXXConstructExpr>(E))
366 // A dead initialization is a variable that is dead after it
367 // is initialized. We don't flag warnings for those variables
368 // marked 'unused' or 'objc_precise_lifetime'.
369 if (!isLive(Live, V) &&
370 !V->hasAttr<UnusedAttr>() &&
371 !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
372 // Special case: check for initializations with constants.
376 // If x is EVER assigned a new value later, don't issue
377 // a warning. This is because such initialization can be
378 // due to defensive programming.
379 if (E->isEvaluatable(Ctx))
382 if (const DeclRefExpr *DRE =
383 dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
384 if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
385 // Special case: check for initialization from constant
388 // e.g. extern const int MyConstant;
389 // int x = MyConstant;
391 if (VD->hasGlobalStorage() &&
392 VD->getType().isConstQualified())
394 // Special case: check for initialization from scalar
395 // parameters. This is often a form of defensive
396 // programming. Non-scalars are still an error since
397 // because it more likely represents an actual algorithmic
399 if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
403 PathDiagnosticLocation Loc =
404 PathDiagnosticLocation::create(V, BR.getSourceManager());
405 Report(V, DeadInit, Loc, E->getSourceRange());
413 } // end anonymous namespace
415 //===----------------------------------------------------------------------===//
416 // Driver function to invoke the Dead-Stores checker on a CFG.
417 //===----------------------------------------------------------------------===//
422 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
424 void operator()(const Stmt *S) {
425 // Check for '&'. Any VarDecl whose address has been taken we treat as
427 // FIXME: What about references?
428 if (auto *LE = dyn_cast<LambdaExpr>(S)) {
429 findLambdaReferenceCaptures(LE);
433 const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
436 if (U->getOpcode() != UO_AddrOf)
439 const Expr *E = U->getSubExpr()->IgnoreParenCasts();
440 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
441 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
445 // Treat local variables captured by reference in C++ lambdas as escaped.
446 void findLambdaReferenceCaptures(const LambdaExpr *LE) {
447 const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
448 llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
449 FieldDecl *ThisCaptureField;
450 LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
452 for (const LambdaCapture &C : LE->captures()) {
453 if (!C.capturesVariable())
456 VarDecl *VD = C.getCapturedVar();
457 const FieldDecl *FD = CaptureFields[VD];
461 // If the capture field is a reference type, it is capture-by-reference.
462 if (FD->getType()->isReferenceType())
467 } // end anonymous namespace
470 //===----------------------------------------------------------------------===//
472 //===----------------------------------------------------------------------===//
475 class DeadStoresChecker : public Checker<check::ASTCodeBody> {
477 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
478 BugReporter &BR) const {
480 // Don't do anything for template instantiations.
481 // Proving that code in a template instantiation is "dead"
482 // means proving that it is dead in all instantiations.
483 // This same problem exists with -Wunreachable-code.
484 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
485 if (FD->isTemplateInstantiation())
488 if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
489 CFG &cfg = *mgr.getCFG(D);
490 AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
491 ParentMap &pmap = mgr.getParentMap(D);
493 cfg.VisitBlockStmts(FS);
494 DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
495 L->runOnAllBlocks(A);
501 void ento::registerDeadStoresChecker(CheckerManager &mgr) {
502 mgr.registerChecker<DeadStoresChecker>();
505 bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {