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