xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file defines a DeadStores, a flow-sensitive checker that looks for
100b57cec5SDimitry Andric //  stores to variables that are no longer live.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
150b57cec5SDimitry Andric #include "clang/AST/Attr.h"
160b57cec5SDimitry Andric #include "clang/AST/ParentMap.h"
170b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
180b57cec5SDimitry Andric #include "clang/Analysis/Analyses/LiveVariables.h"
19fe6060f1SDimitry Andric #include "clang/Lex/Lexer.h"
20fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
240b57cec5SDimitry Andric #include "llvm/ADT/BitVector.h"
25fe6060f1SDimitry Andric #include "llvm/ADT/STLExtras.h"
260b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
270b57cec5SDimitry Andric #include "llvm/Support/SaveAndRestore.h"
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric using namespace clang;
300b57cec5SDimitry Andric using namespace ento;
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric namespace {
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric /// A simple visitor to record what VarDecls occur in EH-handling code.
350b57cec5SDimitry Andric class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
360b57cec5SDimitry Andric public:
370b57cec5SDimitry Andric   bool inEH;
380b57cec5SDimitry Andric   llvm::DenseSet<const VarDecl *> &S;
390b57cec5SDimitry Andric 
TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt * S)400b57cec5SDimitry Andric   bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
41bdd1243dSDimitry Andric     SaveAndRestore inFinally(inEH, true);
420b57cec5SDimitry Andric     return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
430b57cec5SDimitry Andric   }
440b57cec5SDimitry Andric 
TraverseObjCAtCatchStmt(ObjCAtCatchStmt * S)450b57cec5SDimitry Andric   bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
46bdd1243dSDimitry Andric     SaveAndRestore inCatch(inEH, true);
470b57cec5SDimitry Andric     return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
480b57cec5SDimitry Andric   }
490b57cec5SDimitry Andric 
TraverseCXXCatchStmt(CXXCatchStmt * S)500b57cec5SDimitry Andric   bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
51bdd1243dSDimitry Andric     SaveAndRestore inCatch(inEH, true);
520b57cec5SDimitry Andric     return TraverseStmt(S->getHandlerBlock());
530b57cec5SDimitry Andric   }
540b57cec5SDimitry Andric 
VisitDeclRefExpr(DeclRefExpr * DR)550b57cec5SDimitry Andric   bool VisitDeclRefExpr(DeclRefExpr *DR) {
560b57cec5SDimitry Andric     if (inEH)
570b57cec5SDimitry Andric       if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
580b57cec5SDimitry Andric         S.insert(D);
590b57cec5SDimitry Andric     return true;
600b57cec5SDimitry Andric   }
610b57cec5SDimitry Andric 
EHCodeVisitor(llvm::DenseSet<const VarDecl * > & S)620b57cec5SDimitry Andric   EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
630b57cec5SDimitry Andric   inEH(false), S(S) {}
640b57cec5SDimitry Andric };
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric // FIXME: Eventually migrate into its own file, and have it managed by
670b57cec5SDimitry Andric // AnalysisManager.
680b57cec5SDimitry Andric class ReachableCode {
690b57cec5SDimitry Andric   const CFG &cfg;
700b57cec5SDimitry Andric   llvm::BitVector reachable;
710b57cec5SDimitry Andric public:
ReachableCode(const CFG & cfg)720b57cec5SDimitry Andric   ReachableCode(const CFG &cfg)
730b57cec5SDimitry Andric     : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   void computeReachableBlocks();
760b57cec5SDimitry Andric 
isReachable(const CFGBlock * block) const770b57cec5SDimitry Andric   bool isReachable(const CFGBlock *block) const {
780b57cec5SDimitry Andric     return reachable[block->getBlockID()];
790b57cec5SDimitry Andric   }
800b57cec5SDimitry Andric };
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
computeReachableBlocks()830b57cec5SDimitry Andric void ReachableCode::computeReachableBlocks() {
840b57cec5SDimitry Andric   if (!cfg.getNumBlockIDs())
850b57cec5SDimitry Andric     return;
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric   SmallVector<const CFGBlock*, 10> worklist;
880b57cec5SDimitry Andric   worklist.push_back(&cfg.getEntry());
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   while (!worklist.empty()) {
910b57cec5SDimitry Andric     const CFGBlock *block = worklist.pop_back_val();
920b57cec5SDimitry Andric     llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
930b57cec5SDimitry Andric     if (isReachable)
940b57cec5SDimitry Andric       continue;
950b57cec5SDimitry Andric     isReachable = true;
9606c3fb27SDimitry Andric 
9706c3fb27SDimitry Andric     for (const CFGBlock *succ : block->succs())
9806c3fb27SDimitry Andric       if (succ)
990b57cec5SDimitry Andric         worklist.push_back(succ);
1000b57cec5SDimitry Andric   }
1010b57cec5SDimitry Andric }
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric static const Expr *
LookThroughTransitiveAssignmentsAndCommaOperators(const Expr * Ex)1040b57cec5SDimitry Andric LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
1050b57cec5SDimitry Andric   while (Ex) {
106bdd1243dSDimitry Andric     Ex = Ex->IgnoreParenCasts();
107bdd1243dSDimitry Andric     const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex);
1080b57cec5SDimitry Andric     if (!BO)
1090b57cec5SDimitry Andric       break;
11081ad6265SDimitry Andric     BinaryOperatorKind Op = BO->getOpcode();
11181ad6265SDimitry Andric     if (Op == BO_Assign || Op == BO_Comma) {
1120b57cec5SDimitry Andric       Ex = BO->getRHS();
1130b57cec5SDimitry Andric       continue;
1140b57cec5SDimitry Andric     }
1150b57cec5SDimitry Andric     break;
1160b57cec5SDimitry Andric   }
1170b57cec5SDimitry Andric   return Ex;
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric namespace {
121a7dea167SDimitry Andric class DeadStoresChecker : public Checker<check::ASTCodeBody> {
122a7dea167SDimitry Andric public:
123a7dea167SDimitry Andric   bool ShowFixIts = false;
124a7dea167SDimitry Andric   bool WarnForDeadNestedAssignments = true;
125a7dea167SDimitry Andric 
126a7dea167SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
127a7dea167SDimitry Andric                         BugReporter &BR) const;
128a7dea167SDimitry Andric };
129a7dea167SDimitry Andric 
1300b57cec5SDimitry Andric class DeadStoreObs : public LiveVariables::Observer {
1310b57cec5SDimitry Andric   const CFG &cfg;
1320b57cec5SDimitry Andric   ASTContext &Ctx;
1330b57cec5SDimitry Andric   BugReporter& BR;
134a7dea167SDimitry Andric   const DeadStoresChecker *Checker;
1350b57cec5SDimitry Andric   AnalysisDeclContext* AC;
1360b57cec5SDimitry Andric   ParentMap& Parents;
1370b57cec5SDimitry Andric   llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
1380b57cec5SDimitry Andric   std::unique_ptr<ReachableCode> reachableCode;
1390b57cec5SDimitry Andric   const CFGBlock *currentBlock;
1400b57cec5SDimitry Andric   std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric public:
DeadStoreObs(const CFG & cfg,ASTContext & ctx,BugReporter & br,const DeadStoresChecker * checker,AnalysisDeclContext * ac,ParentMap & parents,llvm::SmallPtrSet<const VarDecl *,20> & escaped,bool warnForDeadNestedAssignments)1450b57cec5SDimitry Andric   DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
146a7dea167SDimitry Andric                const DeadStoresChecker *checker, AnalysisDeclContext *ac,
1470b57cec5SDimitry Andric                ParentMap &parents,
148a7dea167SDimitry Andric                llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
149a7dea167SDimitry Andric                bool warnForDeadNestedAssignments)
1500b57cec5SDimitry Andric       : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
1510b57cec5SDimitry Andric         Escaped(escaped), currentBlock(nullptr) {}
1520b57cec5SDimitry Andric 
~DeadStoreObs()1530b57cec5SDimitry Andric   ~DeadStoreObs() override {}
1540b57cec5SDimitry Andric 
isLive(const LiveVariables::LivenessValues & Live,const VarDecl * D)1550b57cec5SDimitry Andric   bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
1560b57cec5SDimitry Andric     if (Live.isLive(D))
1570b57cec5SDimitry Andric       return true;
1580b57cec5SDimitry Andric     // Lazily construct the set that records which VarDecls are in
1590b57cec5SDimitry Andric     // EH code.
1600b57cec5SDimitry Andric     if (!InEH.get()) {
1610b57cec5SDimitry Andric       InEH.reset(new llvm::DenseSet<const VarDecl *>());
1620b57cec5SDimitry Andric       EHCodeVisitor V(*InEH.get());
1630b57cec5SDimitry Andric       V.TraverseStmt(AC->getBody());
1640b57cec5SDimitry Andric     }
1650b57cec5SDimitry Andric     // Treat all VarDecls that occur in EH code as being "always live"
1660b57cec5SDimitry Andric     // when considering to suppress dead stores.  Frequently stores
1670b57cec5SDimitry Andric     // are followed by reads in EH code, but we don't have the ability
1680b57cec5SDimitry Andric     // to analyze that yet.
1690b57cec5SDimitry Andric     return InEH->count(D);
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric 
isSuppressed(SourceRange R)1720b57cec5SDimitry Andric   bool isSuppressed(SourceRange R) {
1730b57cec5SDimitry Andric     SourceManager &SMgr = Ctx.getSourceManager();
1740b57cec5SDimitry Andric     SourceLocation Loc = R.getBegin();
1750b57cec5SDimitry Andric     if (!Loc.isValid())
1760b57cec5SDimitry Andric       return false;
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric     FileID FID = SMgr.getFileID(Loc);
1790b57cec5SDimitry Andric     bool Invalid = false;
1800b57cec5SDimitry Andric     StringRef Data = SMgr.getBufferData(FID, &Invalid);
1810b57cec5SDimitry Andric     if (Invalid)
1820b57cec5SDimitry Andric       return false;
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric     // Files autogenerated by DriverKit IIG contain some dead stores that
1850b57cec5SDimitry Andric     // we don't want to report.
186*5f757f3fSDimitry Andric     if (Data.starts_with("/* iig"))
1870b57cec5SDimitry Andric       return true;
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric     return false;
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric 
Report(const VarDecl * V,DeadStoreKind dsk,PathDiagnosticLocation L,SourceRange R)1920b57cec5SDimitry Andric   void Report(const VarDecl *V, DeadStoreKind dsk,
1930b57cec5SDimitry Andric               PathDiagnosticLocation L, SourceRange R) {
1940b57cec5SDimitry Andric     if (Escaped.count(V))
1950b57cec5SDimitry Andric       return;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric     // Compute reachable blocks within the CFG for trivial cases
1980b57cec5SDimitry Andric     // where a bogus dead store can be reported because itself is unreachable.
1990b57cec5SDimitry Andric     if (!reachableCode.get()) {
2000b57cec5SDimitry Andric       reachableCode.reset(new ReachableCode(cfg));
2010b57cec5SDimitry Andric       reachableCode->computeReachableBlocks();
2020b57cec5SDimitry Andric     }
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric     if (!reachableCode->isReachable(currentBlock))
2050b57cec5SDimitry Andric       return;
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric     if (isSuppressed(R))
2080b57cec5SDimitry Andric       return;
2090b57cec5SDimitry Andric 
2100b57cec5SDimitry Andric     SmallString<64> buf;
2110b57cec5SDimitry Andric     llvm::raw_svector_ostream os(buf);
2120b57cec5SDimitry Andric     const char *BugType = nullptr;
2130b57cec5SDimitry Andric 
214a7dea167SDimitry Andric     SmallVector<FixItHint, 1> Fixits;
215a7dea167SDimitry Andric 
2160b57cec5SDimitry Andric     switch (dsk) {
217a7dea167SDimitry Andric       case DeadInit: {
2180b57cec5SDimitry Andric         BugType = "Dead initialization";
2190b57cec5SDimitry Andric         os << "Value stored to '" << *V
2200b57cec5SDimitry Andric            << "' during its initialization is never read";
221a7dea167SDimitry Andric 
222a7dea167SDimitry Andric         ASTContext &ACtx = V->getASTContext();
223a7dea167SDimitry Andric         if (Checker->ShowFixIts) {
224a7dea167SDimitry Andric           if (V->getInit()->HasSideEffects(ACtx,
225a7dea167SDimitry Andric                                            /*IncludePossibleEffects=*/true)) {
2260b57cec5SDimitry Andric             break;
227a7dea167SDimitry Andric           }
228a7dea167SDimitry Andric           SourceManager &SM = ACtx.getSourceManager();
229a7dea167SDimitry Andric           const LangOptions &LO = ACtx.getLangOpts();
230a7dea167SDimitry Andric           SourceLocation L1 =
231a7dea167SDimitry Andric               Lexer::findNextToken(
232a7dea167SDimitry Andric                   V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
233a7dea167SDimitry Andric                   SM, LO)->getEndLoc();
234a7dea167SDimitry Andric           SourceLocation L2 =
235a7dea167SDimitry Andric               Lexer::getLocForEndOfToken(V->getInit()->getEndLoc(), 1, SM, LO);
236a7dea167SDimitry Andric           Fixits.push_back(FixItHint::CreateRemoval({L1, L2}));
237a7dea167SDimitry Andric         }
238a7dea167SDimitry Andric         break;
239a7dea167SDimitry Andric       }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric       case DeadIncrement:
2420b57cec5SDimitry Andric         BugType = "Dead increment";
243bdd1243dSDimitry Andric         [[fallthrough]];
2440b57cec5SDimitry Andric       case Standard:
2450b57cec5SDimitry Andric         if (!BugType) BugType = "Dead assignment";
2460b57cec5SDimitry Andric         os << "Value stored to '" << *V << "' is never read";
2470b57cec5SDimitry Andric         break;
2480b57cec5SDimitry Andric 
249a7dea167SDimitry Andric       // eg.: f((x = foo()))
2500b57cec5SDimitry Andric       case Enclosing:
251a7dea167SDimitry Andric         if (!Checker->WarnForDeadNestedAssignments)
2520b57cec5SDimitry Andric           return;
253a7dea167SDimitry Andric         BugType = "Dead nested assignment";
254a7dea167SDimitry Andric         os << "Although the value stored to '" << *V
255a7dea167SDimitry Andric            << "' is used in the enclosing expression, the value is never "
256a7dea167SDimitry Andric               "actually read from '"
257a7dea167SDimitry Andric            << *V << "'";
258a7dea167SDimitry Andric         break;
2590b57cec5SDimitry Andric     }
2600b57cec5SDimitry Andric 
261fe6060f1SDimitry Andric     BR.EmitBasicReport(AC->getDecl(), Checker, BugType, categories::UnusedCode,
262fe6060f1SDimitry Andric                        os.str(), L, R, Fixits);
2630b57cec5SDimitry Andric   }
2640b57cec5SDimitry Andric 
CheckVarDecl(const VarDecl * VD,const Expr * Ex,const Expr * Val,DeadStoreKind dsk,const LiveVariables::LivenessValues & Live)2650b57cec5SDimitry Andric   void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
2660b57cec5SDimitry Andric                     DeadStoreKind dsk,
2670b57cec5SDimitry Andric                     const LiveVariables::LivenessValues &Live) {
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric     if (!VD->hasLocalStorage())
2700b57cec5SDimitry Andric       return;
2710b57cec5SDimitry Andric     // Reference types confuse the dead stores checker.  Skip them
2720b57cec5SDimitry Andric     // for now.
2730b57cec5SDimitry Andric     if (VD->getType()->getAs<ReferenceType>())
2740b57cec5SDimitry Andric       return;
2750b57cec5SDimitry Andric 
2760b57cec5SDimitry Andric     if (!isLive(Live, VD) &&
2770b57cec5SDimitry Andric         !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
2780b57cec5SDimitry Andric           VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric       PathDiagnosticLocation ExLoc =
2810b57cec5SDimitry Andric         PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
2820b57cec5SDimitry Andric       Report(VD, dsk, ExLoc, Val->getSourceRange());
2830b57cec5SDimitry Andric     }
2840b57cec5SDimitry Andric   }
2850b57cec5SDimitry Andric 
CheckDeclRef(const DeclRefExpr * DR,const Expr * Val,DeadStoreKind dsk,const LiveVariables::LivenessValues & Live)2860b57cec5SDimitry Andric   void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
2870b57cec5SDimitry Andric                     const LiveVariables::LivenessValues& Live) {
2880b57cec5SDimitry Andric     if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
2890b57cec5SDimitry Andric       CheckVarDecl(VD, DR, Val, dsk, Live);
2900b57cec5SDimitry Andric   }
2910b57cec5SDimitry Andric 
isIncrement(VarDecl * VD,const BinaryOperator * B)2920b57cec5SDimitry Andric   bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
2930b57cec5SDimitry Andric     if (B->isCompoundAssignmentOp())
2940b57cec5SDimitry Andric       return true;
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric     const Expr *RHS = B->getRHS()->IgnoreParenCasts();
2970b57cec5SDimitry Andric     const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
2980b57cec5SDimitry Andric 
2990b57cec5SDimitry Andric     if (!BRHS)
3000b57cec5SDimitry Andric       return false;
3010b57cec5SDimitry Andric 
3020b57cec5SDimitry Andric     const DeclRefExpr *DR;
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric     if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
3050b57cec5SDimitry Andric       if (DR->getDecl() == VD)
3060b57cec5SDimitry Andric         return true;
3070b57cec5SDimitry Andric 
3080b57cec5SDimitry Andric     if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
3090b57cec5SDimitry Andric       if (DR->getDecl() == VD)
3100b57cec5SDimitry Andric         return true;
3110b57cec5SDimitry Andric 
3120b57cec5SDimitry Andric     return false;
3130b57cec5SDimitry Andric   }
3140b57cec5SDimitry Andric 
observeStmt(const Stmt * S,const CFGBlock * block,const LiveVariables::LivenessValues & Live)3150b57cec5SDimitry Andric   void observeStmt(const Stmt *S, const CFGBlock *block,
3160b57cec5SDimitry Andric                    const LiveVariables::LivenessValues &Live) override {
3170b57cec5SDimitry Andric 
3180b57cec5SDimitry Andric     currentBlock = block;
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric     // Skip statements in macros.
3210b57cec5SDimitry Andric     if (S->getBeginLoc().isMacroID())
3220b57cec5SDimitry Andric       return;
3230b57cec5SDimitry Andric 
3240b57cec5SDimitry Andric     // Only cover dead stores from regular assignments.  ++/-- dead stores
3250b57cec5SDimitry Andric     // have never flagged a real bug.
3260b57cec5SDimitry Andric     if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
3270b57cec5SDimitry Andric       if (!B->isAssignmentOp()) return; // Skip non-assignments.
3280b57cec5SDimitry Andric 
3290b57cec5SDimitry Andric       if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
3300b57cec5SDimitry Andric         if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
3310b57cec5SDimitry Andric           // Special case: check for assigning null to a pointer.
3320b57cec5SDimitry Andric           //  This is a common form of defensive programming.
3330b57cec5SDimitry Andric           const Expr *RHS =
3340b57cec5SDimitry Andric               LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
3350b57cec5SDimitry Andric 
3360b57cec5SDimitry Andric           QualType T = VD->getType();
3370b57cec5SDimitry Andric           if (T.isVolatileQualified())
3380b57cec5SDimitry Andric             return;
3390b57cec5SDimitry Andric           if (T->isPointerType() || T->isObjCObjectPointerType()) {
3400b57cec5SDimitry Andric             if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
3410b57cec5SDimitry Andric               return;
3420b57cec5SDimitry Andric           }
3430b57cec5SDimitry Andric 
3440b57cec5SDimitry Andric           // Special case: self-assignments.  These are often used to shut up
3450b57cec5SDimitry Andric           //  "unused variable" compiler warnings.
3460b57cec5SDimitry Andric           if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
3470b57cec5SDimitry Andric             if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
3480b57cec5SDimitry Andric               return;
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric           // Otherwise, issue a warning.
3510b57cec5SDimitry Andric           DeadStoreKind dsk = Parents.isConsumedExpr(B)
3520b57cec5SDimitry Andric                               ? Enclosing
3530b57cec5SDimitry Andric                               : (isIncrement(VD,B) ? DeadIncrement : Standard);
3540b57cec5SDimitry Andric 
3550b57cec5SDimitry Andric           CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
3560b57cec5SDimitry Andric         }
3570b57cec5SDimitry Andric     }
3580b57cec5SDimitry Andric     else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
3590b57cec5SDimitry Andric       if (!U->isIncrementOp() || U->isPrefix())
3600b57cec5SDimitry Andric         return;
3610b57cec5SDimitry Andric 
3620b57cec5SDimitry Andric       const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
3630b57cec5SDimitry Andric       if (!parent || !isa<ReturnStmt>(parent))
3640b57cec5SDimitry Andric         return;
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric       const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
3670b57cec5SDimitry Andric 
3680b57cec5SDimitry Andric       if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
3690b57cec5SDimitry Andric         CheckDeclRef(DR, U, DeadIncrement, Live);
3700b57cec5SDimitry Andric     }
3710b57cec5SDimitry Andric     else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
3720b57cec5SDimitry Andric       // Iterate through the decls.  Warn if any initializers are complex
3730b57cec5SDimitry Andric       // expressions that are not live (never used).
3740b57cec5SDimitry Andric       for (const auto *DI : DS->decls()) {
3750b57cec5SDimitry Andric         const auto *V = dyn_cast<VarDecl>(DI);
3760b57cec5SDimitry Andric 
3770b57cec5SDimitry Andric         if (!V)
3780b57cec5SDimitry Andric           continue;
3790b57cec5SDimitry Andric 
3800b57cec5SDimitry Andric         if (V->hasLocalStorage()) {
3810b57cec5SDimitry Andric           // Reference types confuse the dead stores checker.  Skip them
3820b57cec5SDimitry Andric           // for now.
3830b57cec5SDimitry Andric           if (V->getType()->getAs<ReferenceType>())
3840b57cec5SDimitry Andric             return;
3850b57cec5SDimitry Andric 
3860b57cec5SDimitry Andric           if (const Expr *E = V->getInit()) {
3870b57cec5SDimitry Andric             while (const FullExpr *FE = dyn_cast<FullExpr>(E))
3880b57cec5SDimitry Andric               E = FE->getSubExpr();
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric             // Look through transitive assignments, e.g.:
3910b57cec5SDimitry Andric             // int x = y = 0;
3920b57cec5SDimitry Andric             E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
3930b57cec5SDimitry Andric 
3940b57cec5SDimitry Andric             // Don't warn on C++ objects (yet) until we can show that their
3950b57cec5SDimitry Andric             // constructors/destructors don't have side effects.
3960b57cec5SDimitry Andric             if (isa<CXXConstructExpr>(E))
3970b57cec5SDimitry Andric               return;
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric             // A dead initialization is a variable that is dead after it
4000b57cec5SDimitry Andric             // is initialized.  We don't flag warnings for those variables
4010b57cec5SDimitry Andric             // marked 'unused' or 'objc_precise_lifetime'.
4020b57cec5SDimitry Andric             if (!isLive(Live, V) &&
4030b57cec5SDimitry Andric                 !V->hasAttr<UnusedAttr>() &&
4040b57cec5SDimitry Andric                 !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
4050b57cec5SDimitry Andric               // Special case: check for initializations with constants.
4060b57cec5SDimitry Andric               //
4070b57cec5SDimitry Andric               //  e.g. : int x = 0;
408fe6060f1SDimitry Andric               //         struct A = {0, 1};
409fe6060f1SDimitry Andric               //         struct B = {{0}, {1, 2}};
4100b57cec5SDimitry Andric               //
4110b57cec5SDimitry Andric               // If x is EVER assigned a new value later, don't issue
4120b57cec5SDimitry Andric               // a warning.  This is because such initialization can be
4130b57cec5SDimitry Andric               // due to defensive programming.
414fe6060f1SDimitry Andric               if (isConstant(E))
4150b57cec5SDimitry Andric                 return;
4160b57cec5SDimitry Andric 
417bdd1243dSDimitry Andric               if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
4180b57cec5SDimitry Andric                 if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
4190b57cec5SDimitry Andric                   // Special case: check for initialization from constant
4200b57cec5SDimitry Andric                   //  variables.
4210b57cec5SDimitry Andric                   //
4220b57cec5SDimitry Andric                   //  e.g. extern const int MyConstant;
4230b57cec5SDimitry Andric                   //       int x = MyConstant;
4240b57cec5SDimitry Andric                   //
4250b57cec5SDimitry Andric                   if (VD->hasGlobalStorage() &&
4260b57cec5SDimitry Andric                       VD->getType().isConstQualified())
4270b57cec5SDimitry Andric                     return;
4280b57cec5SDimitry Andric                   // Special case: check for initialization from scalar
4290b57cec5SDimitry Andric                   //  parameters.  This is often a form of defensive
4300b57cec5SDimitry Andric                   //  programming.  Non-scalars are still an error since
4310b57cec5SDimitry Andric                   //  because it more likely represents an actual algorithmic
4320b57cec5SDimitry Andric                   //  bug.
4330b57cec5SDimitry Andric                   if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
4340b57cec5SDimitry Andric                     return;
4350b57cec5SDimitry Andric                 }
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric               PathDiagnosticLocation Loc =
4380b57cec5SDimitry Andric                 PathDiagnosticLocation::create(V, BR.getSourceManager());
439bdd1243dSDimitry Andric               Report(V, DeadInit, Loc, V->getInit()->getSourceRange());
4400b57cec5SDimitry Andric             }
4410b57cec5SDimitry Andric           }
4420b57cec5SDimitry Andric         }
4430b57cec5SDimitry Andric       }
4440b57cec5SDimitry Andric   }
445fe6060f1SDimitry Andric 
446fe6060f1SDimitry Andric private:
447fe6060f1SDimitry Andric   /// Return true if the given init list can be interpreted as constant
isConstant(const InitListExpr * Candidate) const448fe6060f1SDimitry Andric   bool isConstant(const InitListExpr *Candidate) const {
449fe6060f1SDimitry Andric     // We consider init list to be constant if each member of the list can be
450fe6060f1SDimitry Andric     // interpreted as constant.
451bdd1243dSDimitry Andric     return llvm::all_of(Candidate->inits(), [this](const Expr *Init) {
452bdd1243dSDimitry Andric       return isConstant(Init->IgnoreParenCasts());
453bdd1243dSDimitry Andric     });
454fe6060f1SDimitry Andric   }
455fe6060f1SDimitry Andric 
456fe6060f1SDimitry Andric   /// Return true if the given expression can be interpreted as constant
isConstant(const Expr * E) const457fe6060f1SDimitry Andric   bool isConstant(const Expr *E) const {
458fe6060f1SDimitry Andric     // It looks like E itself is a constant
459fe6060f1SDimitry Andric     if (E->isEvaluatable(Ctx))
460fe6060f1SDimitry Andric       return true;
461fe6060f1SDimitry Andric 
462fe6060f1SDimitry Andric     // We should also allow defensive initialization of structs, i.e. { 0 }
463bdd1243dSDimitry Andric     if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
464fe6060f1SDimitry Andric       return isConstant(ILE);
465fe6060f1SDimitry Andric     }
466fe6060f1SDimitry Andric 
467fe6060f1SDimitry Andric     return false;
468fe6060f1SDimitry Andric   }
4690b57cec5SDimitry Andric };
4700b57cec5SDimitry Andric 
4710b57cec5SDimitry Andric } // end anonymous namespace
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4740b57cec5SDimitry Andric // Driver function to invoke the Dead-Stores checker on a CFG.
4750b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric namespace {
4780b57cec5SDimitry Andric class FindEscaped {
4790b57cec5SDimitry Andric public:
4800b57cec5SDimitry Andric   llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
4810b57cec5SDimitry Andric 
operator ()(const Stmt * S)4820b57cec5SDimitry Andric   void operator()(const Stmt *S) {
4830b57cec5SDimitry Andric     // Check for '&'. Any VarDecl whose address has been taken we treat as
4840b57cec5SDimitry Andric     // escaped.
4850b57cec5SDimitry Andric     // FIXME: What about references?
4860b57cec5SDimitry Andric     if (auto *LE = dyn_cast<LambdaExpr>(S)) {
4870b57cec5SDimitry Andric       findLambdaReferenceCaptures(LE);
4880b57cec5SDimitry Andric       return;
4890b57cec5SDimitry Andric     }
4900b57cec5SDimitry Andric 
4910b57cec5SDimitry Andric     const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
4920b57cec5SDimitry Andric     if (!U)
4930b57cec5SDimitry Andric       return;
4940b57cec5SDimitry Andric     if (U->getOpcode() != UO_AddrOf)
4950b57cec5SDimitry Andric       return;
4960b57cec5SDimitry Andric 
4970b57cec5SDimitry Andric     const Expr *E = U->getSubExpr()->IgnoreParenCasts();
4980b57cec5SDimitry Andric     if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
4990b57cec5SDimitry Andric       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
5000b57cec5SDimitry Andric         Escaped.insert(VD);
5010b57cec5SDimitry Andric   }
5020b57cec5SDimitry Andric 
5030b57cec5SDimitry Andric   // Treat local variables captured by reference in C++ lambdas as escaped.
findLambdaReferenceCaptures(const LambdaExpr * LE)5040b57cec5SDimitry Andric   void findLambdaReferenceCaptures(const LambdaExpr *LE)  {
5050b57cec5SDimitry Andric     const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
506bdd1243dSDimitry Andric     llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
5070b57cec5SDimitry Andric     FieldDecl *ThisCaptureField;
5080b57cec5SDimitry Andric     LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
5090b57cec5SDimitry Andric 
5100b57cec5SDimitry Andric     for (const LambdaCapture &C : LE->captures()) {
5110b57cec5SDimitry Andric       if (!C.capturesVariable())
5120b57cec5SDimitry Andric         continue;
5130b57cec5SDimitry Andric 
514bdd1243dSDimitry Andric       ValueDecl *VD = C.getCapturedVar();
5150b57cec5SDimitry Andric       const FieldDecl *FD = CaptureFields[VD];
516bdd1243dSDimitry Andric       if (!FD || !isa<VarDecl>(VD))
5170b57cec5SDimitry Andric         continue;
5180b57cec5SDimitry Andric 
5190b57cec5SDimitry Andric       // If the capture field is a reference type, it is capture-by-reference.
5200b57cec5SDimitry Andric       if (FD->getType()->isReferenceType())
521bdd1243dSDimitry Andric         Escaped.insert(cast<VarDecl>(VD));
5220b57cec5SDimitry Andric     }
5230b57cec5SDimitry Andric   }
5240b57cec5SDimitry Andric };
5250b57cec5SDimitry Andric } // end anonymous namespace
5260b57cec5SDimitry Andric 
5270b57cec5SDimitry Andric 
5280b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5290b57cec5SDimitry Andric // DeadStoresChecker
5300b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5310b57cec5SDimitry Andric 
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const532a7dea167SDimitry Andric void DeadStoresChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
5330b57cec5SDimitry Andric                                          BugReporter &BR) const {
5340b57cec5SDimitry Andric 
5350b57cec5SDimitry Andric   // Don't do anything for template instantiations.
5360b57cec5SDimitry Andric   // Proving that code in a template instantiation is "dead"
5370b57cec5SDimitry Andric   // means proving that it is dead in all instantiations.
5380b57cec5SDimitry Andric   // This same problem exists with -Wunreachable-code.
5390b57cec5SDimitry Andric   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
5400b57cec5SDimitry Andric     if (FD->isTemplateInstantiation())
5410b57cec5SDimitry Andric       return;
5420b57cec5SDimitry Andric 
5430b57cec5SDimitry Andric   if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
5440b57cec5SDimitry Andric     CFG &cfg = *mgr.getCFG(D);
5450b57cec5SDimitry Andric     AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
5460b57cec5SDimitry Andric     ParentMap &pmap = mgr.getParentMap(D);
5470b57cec5SDimitry Andric     FindEscaped FS;
5480b57cec5SDimitry Andric     cfg.VisitBlockStmts(FS);
549a7dea167SDimitry Andric     DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped,
550a7dea167SDimitry Andric                    WarnForDeadNestedAssignments);
5510b57cec5SDimitry Andric     L->runOnAllBlocks(A);
5520b57cec5SDimitry Andric   }
5530b57cec5SDimitry Andric }
5540b57cec5SDimitry Andric 
registerDeadStoresChecker(CheckerManager & Mgr)555a7dea167SDimitry Andric void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
556a7dea167SDimitry Andric   auto *Chk = Mgr.registerChecker<DeadStoresChecker>();
557a7dea167SDimitry Andric 
558a7dea167SDimitry Andric   const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
559a7dea167SDimitry Andric   Chk->WarnForDeadNestedAssignments =
560a7dea167SDimitry Andric       AnOpts.getCheckerBooleanOption(Chk, "WarnForDeadNestedAssignments");
561a7dea167SDimitry Andric   Chk->ShowFixIts =
562a7dea167SDimitry Andric       AnOpts.getCheckerBooleanOption(Chk, "ShowFixIts");
5630b57cec5SDimitry Andric }
5640b57cec5SDimitry Andric 
shouldRegisterDeadStoresChecker(const CheckerManager & mgr)5655ffd83dbSDimitry Andric bool ento::shouldRegisterDeadStoresChecker(const CheckerManager &mgr) {
5660b57cec5SDimitry Andric   return true;
5670b57cec5SDimitry Andric }
568