24#include "llvm/ADT/BitVector.h"
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/Support/SaveAndRestore.h"
37 llvm::DenseSet<const VarDecl *> &S;
39 bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S)
override {
40 SaveAndRestore inFinally(inEH,
true);
41 return DynamicRecursiveASTVisitor::TraverseObjCAtFinallyStmt(S);
44 bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S)
override {
45 SaveAndRestore inCatch(inEH,
true);
46 return DynamicRecursiveASTVisitor::TraverseObjCAtCatchStmt(S);
49 bool TraverseCXXCatchStmt(CXXCatchStmt *S)
override {
50 SaveAndRestore inCatch(inEH,
true);
51 return TraverseStmt(S->getHandlerBlock());
54 bool VisitDeclRefExpr(DeclRefExpr *DR)
override {
56 if (
const VarDecl *D = dyn_cast<VarDecl>(DR->
getDecl()))
61 EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
69 llvm::BitVector reachable;
71 ReachableCode(
const CFG &cfg)
72 : cfg(cfg), reachable(cfg.getNumBlockIDs(),
false) {}
74 void computeReachableBlocks();
76 bool isReachable(
const CFGBlock *block)
const {
82void ReachableCode::computeReachableBlocks() {
86 SmallVector<const CFGBlock*, 10> worklist;
89 while (!worklist.empty()) {
90 const CFGBlock *block = worklist.pop_back_val();
91 llvm::BitVector::reference isReachable = reachable[block->
getBlockID()];
96 for (
const CFGBlock *succ : block->
succs())
98 worklist.push_back(succ);
110 if (Op == BO_Assign || Op == BO_Comma) {
120class DeadStoresChecker :
public Checker<check::ASTCodeBody> {
122 bool ShowFixIts =
false;
123 bool WarnForDeadNestedAssignments =
true;
125 void checkASTCodeBody(
const Decl *D, AnalysisManager &Mgr,
126 BugReporter &BR)
const;
129class DeadStoreObs :
public LiveVariables::Observer {
133 const DeadStoresChecker *Checker;
134 AnalysisDeclContext* AC;
136 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
137 std::unique_ptr<ReachableCode> reachableCode;
138 const CFGBlock *currentBlock;
139 std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
141 enum DeadStoreKind {
Standard, Enclosing, DeadIncrement, DeadInit };
144 DeadStoreObs(
const CFG &cfg, ASTContext &ctx, BugReporter &br,
145 const DeadStoresChecker *checker, AnalysisDeclContext *ac,
147 llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
148 bool warnForDeadNestedAssignments)
149 : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
150 Escaped(escaped), currentBlock(
nullptr) {}
152 ~DeadStoreObs()
override {}
154 bool isLive(
const LiveVariables::LivenessValues &Live,
const VarDecl *D) {
160 InEH.reset(
new llvm::DenseSet<const VarDecl *>());
161 EHCodeVisitor
V(*InEH);
168 return InEH->count(D);
171 bool isSuppressed(SourceRange R) {
185 if (
Data.starts_with(
"/* iig"))
191 void Report(
const VarDecl *
V, DeadStoreKind dsk,
192 PathDiagnosticLocation L, SourceRange R) {
193 if (Escaped.count(
V))
198 if (!reachableCode) {
199 reachableCode.reset(
new ReachableCode(cfg));
200 reachableCode->computeReachableBlocks();
203 if (!reachableCode->isReachable(currentBlock))
210 llvm::raw_svector_ostream os(buf);
211 const char *BugType =
nullptr;
213 SmallVector<FixItHint, 1> Fixits;
217 BugType =
"Dead initialization";
218 os <<
"Value stored to '" << *
V
219 <<
"' during its initialization is never read";
221 ASTContext &ACtx =
V->getASTContext();
222 if (Checker->ShowFixIts) {
223 if (
V->getInit()->HasSideEffects(ACtx,
231 V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
232 SM, LO)->getEndLoc();
241 BugType =
"Dead increment";
244 if (!BugType) BugType =
"Dead assignment";
245 os <<
"Value stored to '" << *
V <<
"' is never read";
250 if (!Checker->WarnForDeadNestedAssignments)
252 BugType =
"Dead nested assignment";
253 os <<
"Although the value stored to '" << *
V
254 <<
"' is used in the enclosing expression, the value is never "
255 "actually read from '"
261 os.str(), L, R, Fixits);
264 void CheckVarDecl(
const VarDecl *VD,
const Expr *Ex,
const Expr *Val,
266 const LiveVariables::LivenessValues &Live) {
275 if (!isLive(Live, VD) &&
277 VD->
hasAttr<ObjCPreciseLifetimeAttr>())) {
279 PathDiagnosticLocation ExLoc =
285 void CheckDeclRef(
const DeclRefExpr *DR,
const Expr *Val, DeadStoreKind dsk,
286 const LiveVariables::LivenessValues& Live) {
287 if (
const VarDecl *VD = dyn_cast<VarDecl>(DR->
getDecl()))
288 CheckVarDecl(VD, DR, Val, dsk, Live);
291 bool isIncrement(VarDecl *VD,
const BinaryOperator* B) {
296 const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
301 const DeclRefExpr *DR;
314 void observeStmt(
const Stmt *S,
const CFGBlock *block,
315 const LiveVariables::LivenessValues &Live)
override {
317 currentBlock = block;
325 if (
const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
328 if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->
getLHS()))
329 if (VarDecl *VD = dyn_cast<VarDecl>(DR->
getDecl())) {
336 if (
T.isVolatileQualified())
345 if (
const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
346 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
352 : (isIncrement(VD,B) ? DeadIncrement :
Standard);
354 CheckVarDecl(VD, DR, B->
getRHS(), dsk, Live);
357 else if (
const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
358 if (!U->isIncrementOp() || U->isPrefix())
367 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
370 else if (
const DeclStmt *DS = dyn_cast<DeclStmt>(S))
373 for (
const auto *DI : DS->decls()) {
374 const auto *
V = dyn_cast<VarDecl>(DI);
379 if (
V->hasLocalStorage()) {
382 if (
V->getType()->getAs<ReferenceType>())
385 if (
const Expr *E =
V->getInit()) {
386 while (
const FullExpr *FE = dyn_cast<FullExpr>(E))
387 E = FE->getSubExpr();
401 if (!isLive(Live,
V) &&
402 !
V->hasAttr<UnusedAttr>() &&
403 !
V->hasAttr<ObjCPreciseLifetimeAttr>()) {
416 if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
417 if (
const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
436 PathDiagnosticLocation Loc =
438 Report(
V, DeadInit, Loc,
V->getInit()->getSourceRange());
447 bool isConstant(
const InitListExpr *Candidate)
const {
450 return llvm::all_of(Candidate->
inits(), [
this](
const Expr *
Init) {
451 return isConstant(Init->IgnoreParenCasts());
456 bool isConstant(
const Expr *E)
const {
462 if (
const auto *ILE = dyn_cast<InitListExpr>(E)) {
463 return isConstant(ILE);
479 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
481 void operator()(
const Stmt *S) {
485 if (
auto *LE = dyn_cast<LambdaExpr>(S)) {
486 findLambdaReferenceCaptures(LE);
490 const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
497 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
498 if (
const VarDecl *VD = dyn_cast<VarDecl>(DR->
getDecl()))
503 void findLambdaReferenceCaptures(
const LambdaExpr *LE) {
504 const CXXRecordDecl *LambdaClass =
LE->getLambdaClass();
505 llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
506 FieldDecl *ThisCaptureField;
509 for (
const LambdaCapture &
C :
LE->captures()) {
510 if (!
C.capturesVariable())
513 ValueDecl *VD =
C.getCapturedVar();
514 const FieldDecl *FD = CaptureFields[VD];
531void DeadStoresChecker::checkASTCodeBody(
const Decl *D, AnalysisManager &mgr,
532 BugReporter &BR)
const {
538 if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
539 if (FD->isTemplateInstantiation())
542 if (LiveVariables *L = mgr.
getAnalysis<LiveVariables>(D)) {
543 CFG &cfg = *mgr.
getCFG(D);
548 DeadStoreObs A(cfg, BR.
getContext(), BR,
this, AC, pmap, FS.Escaped,
549 WarnForDeadNestedAssignments);
550 L->runOnAllBlocks(A);
554void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
558 Chk->WarnForDeadNestedAssignments =
559 AnOpts.getCheckerBooleanOption(Chk,
"WarnForDeadNestedAssignments");
561 AnOpts.getCheckerBooleanOption(Chk,
"ShowFixIts");
564bool ento::shouldRegisterDeadStoresChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex)
SourceManager & getSourceManager()
const LangOptions & getLangOpts() const
const Decl * getDecl() const
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
static bool isCompoundAssignmentOp(Opcode Opc)
unsigned getBlockID() const
void VisitBlockStmts(Callback &O) const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
void getCaptureFields(llvm::DenseMap< const ValueDecl *, FieldDecl * > &Captures, FieldDecl *&ThisCapture) const
For a closure type, retrieve the mapping from captured variables and this to the non-static data memb...
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
bool isEvaluatable(const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects) const
isEvaluatable - Call EvaluateAsRValue to see if this expression can be constant folded without side-e...
@ NPC_ValueDependentIsNull
Specifies that a value-dependent expression of integral or dependent type should be considered a null...
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant.
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
ArrayRef< Expr * > inits()
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)
Finds the token that comes right after the given location.
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
bool isLive(const Expr *E) const
bool isConsumedExpr(Expr *E) const
Stmt * getParentIgnoreParenCasts(Stmt *) const
bool isConstQualified() const
Determine whether this type is const-qualified.
bool isValid() const
Return true if this is a valid SourceLocation object.
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
SourceLocation getBeginLoc() const LLVM_READONLY
bool isPointerType() const
bool isReferenceType() const
bool isScalarType() const
bool isObjCObjectPointerType() const
const T * getAs() const
Member-template getAs<specific type>'.
Expr * getSubExpr() const
bool hasGlobalStorage() const
Returns true for all variables that do not have local storage.
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
T * getAnalysis(Decl const *D)
ParentMap & getParentMap(Decl const *D)
CFG * getCFG(Decl const *D)
const SourceManager & getSourceManager()
ASTContext & getContext()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
const char *const UnusedCode
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR)
We aleady know the given DeclRefExpr is invalid for some reason, now figure out why and print appropr...
bool LE(InterpState &S, CodePtr OpPC)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
LanguageStandard Standard
Parse and format C++ constructs compatible with this standard.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
const FunctionProtoType * T
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
U cast(CodeGen::Address addr)