24 #include "llvm/ADT/BitVector.h" 25 #include "llvm/ADT/SmallString.h" 26 #include "llvm/Support/SaveAndRestore.h" 28 using namespace clang;
41 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
46 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(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 {
82 void ReachableCode::computeReachableBlocks() {
83 if (!cfg.getNumBlockIDs())
87 worklist.push_back(&cfg.getEntry());
89 while (!worklist.empty()) {
90 const CFGBlock *block = worklist.pop_back_val();
91 llvm::BitVector::reference isReachable = reachable[block->
getBlockID()];
98 worklist.push_back(succ);
123 class DeadStoresChecker :
public Checker<check::ASTCodeBody> {
125 bool ShowFixIts =
false;
126 bool WarnForDeadNestedAssignments =
true;
128 void checkASTCodeBody(
const Decl *D, AnalysisManager &Mgr,
129 BugReporter &BR)
const;
136 const DeadStoresChecker *Checker;
139 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
140 std::unique_ptr<ReachableCode> reachableCode;
142 std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
144 enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
147 DeadStoreObs(
const CFG &cfg,
ASTContext &ctx, BugReporter &br,
150 llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
151 bool warnForDeadNestedAssignments)
152 : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
153 Escaped(escaped), currentBlock(
nullptr) {}
155 ~DeadStoreObs()
override {}
164 EHCodeVisitor
V(*InEH.get());
171 return InEH->count(D);
181 bool Invalid =
false;
188 if (Data.startswith(
"/* iig"))
194 void Report(
const VarDecl *
V, DeadStoreKind dsk,
196 if (Escaped.count(V))
201 if (!reachableCode.get()) {
202 reachableCode.reset(
new ReachableCode(cfg));
203 reachableCode->computeReachableBlocks();
206 if (!reachableCode->isReachable(currentBlock))
213 llvm::raw_svector_ostream os(buf);
214 const char *BugType =
nullptr;
220 BugType =
"Dead initialization";
221 os <<
"Value stored to '" << *V
222 <<
"' during its initialization is never read";
225 if (Checker->ShowFixIts) {
226 if (V->getInit()->HasSideEffects(ACtx,
234 V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
235 SM, LO)->getEndLoc();
244 BugType =
"Dead increment";
247 if (!BugType) BugType =
"Dead assignment";
248 os <<
"Value stored to '" << *V <<
"' is never read";
253 if (!Checker->WarnForDeadNestedAssignments)
255 BugType =
"Dead nested assignment";
256 os <<
"Although the value stored to '" << *V
257 <<
"' is used in the enclosing expression, the value is never " 258 "actually read from '" 263 BR.EmitBasicReport(AC->
getDecl(), Checker, BugType,
"Dead store", os.str(),
278 if (!isLive(Live, VD) &&
280 VD->
hasAttr<ObjCPreciseLifetimeAttr>())) {
282 PathDiagnosticLocation ExLoc =
288 void CheckDeclRef(
const DeclRefExpr *DR,
const Expr *Val, DeadStoreKind dsk,
291 CheckVarDecl(VD, DR, Val, dsk, Live);
320 currentBlock = block;
349 if (
const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
350 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
356 : (isIncrement(VD,B) ? DeadIncrement : Standard);
358 CheckVarDecl(VD, DR, B->
getRHS(), dsk, Live);
361 else if (
const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
362 if (!U->isIncrementOp() || U->isPrefix())
366 if (!parent || !isa<ReturnStmt>(parent))
371 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
372 CheckDeclRef(DR, U, DeadIncrement, Live);
374 else if (
const DeclStmt *DS = dyn_cast<DeclStmt>(S))
377 for (
const auto *DI : DS->decls()) {
378 const auto *V = dyn_cast<
VarDecl>(DI);
390 while (
const FullExpr *FE = dyn_cast<FullExpr>(E))
391 E = FE->getSubExpr();
399 if (isa<CXXConstructExpr>(E))
405 if (!isLive(Live, V) &&
407 !V->
hasAttr<ObjCPreciseLifetimeAttr>()) {
415 if (E->isEvaluatable(Ctx))
419 dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
420 if (
const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
439 PathDiagnosticLocation Loc =
458 llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
460 void operator()(
const Stmt *S) {
464 if (
auto *
LE = dyn_cast<LambdaExpr>(S)) {
465 findLambdaReferenceCaptures(
LE);
476 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
482 void findLambdaReferenceCaptures(
const LambdaExpr *
LE) {
484 llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
489 if (!C.capturesVariable())
492 VarDecl *VD = C.getCapturedVar();
510 void DeadStoresChecker::checkASTCodeBody(
const Decl *D, AnalysisManager &mgr,
511 BugReporter &BR)
const {
518 if (FD->isTemplateInstantiation())
522 CFG &cfg = *mgr.getCFG(D);
527 DeadStoreObs A(cfg, BR.getContext(), BR,
this, AC, pmap, FS.Escaped,
528 WarnForDeadNestedAssignments);
529 L->runOnAllBlocks(A);
533 void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
534 auto *Chk = Mgr.registerChecker<DeadStoresChecker>();
537 Chk->WarnForDeadNestedAssignments =
540 AnOpts.getCheckerBooleanOption(Chk,
"ShowFixIts");
543 bool ento::shouldRegisterDeadStoresChecker(
const LangOptions &LO) {
Defines the clang::ASTContext interface.
Represents a function declaration or definition.
A (possibly-)qualified type.
Stmt * getBody() const
Get the body of the Declaration.
succ_iterator succ_begin()
Stmt - This represents one statement.
unsigned getBlockID() const
Decl - This represents one declaration (or definition), e.g.
Stmt * getHandlerBlock() const
Describes the capture of a variable or of this, or of a C++1y init-capture.
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
bool isConsumedExpr(Expr *E) const
Represents a variable declaration or definition.
const T * getAs() const
Member-template getAs<specific type>'.
static bool isAssignmentOp(Opcode Opc)
CXXRecordDecl * getLambdaClass() const
Retrieve the class that corresponds to the lambda.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
FullExpr - Represents a "full-expression" node.
SourceLocation getBeginLoc() const LLVM_READONLY
Represents a member of a struct/union/class.
AnalysisDeclContext contains the context data for the function or method under analysis.
bool isReferenceType() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Represents Objective-C's @catch statement.
AdjacentBlocks::const_iterator const_succ_iterator
A builtin binary operation expression such as "x + y" or "x <= y".
bool isVolatileQualified() const
Determine whether this type is volatile-qualified.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
bool isScalarType() const
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Represents a single basic block in a source-level CFG.
This represents one expression.
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.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Specifies that a value-dependent expression of integral or dependent type should be considered a null...
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
bool isConstQualified() const
Determine whether this type is const-qualified.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Encodes a location in the source.
Expr * getSubExpr() const
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
static Optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Finds the token that comes right after the given location.
bool hasGlobalStorage() const
Returns true for all variables that do not have local storage.
const Decl * getDecl() const
bool isObjCObjectPointerType() const
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Dataflow Directional Tag Classes.
void VisitBlockStmts(CALLBACK &O) const
bool isValid() const
Return true if this is a valid SourceLocation object.
static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex)
bool isLive(const Stmt *S) const
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
const Expr * getInit() const
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
Base for LValueReferenceType and RValueReferenceType.
Represents Objective-C's @finally statement.
Stores options for the analyzer from the command line.
void getCaptureFields(llvm::DenseMap< const VarDecl *, FieldDecl *> &Captures, FieldDecl *&ThisCapture) const
For a closure type, retrieve the mapping from captured variables and this to the non-static data memb...
SourceManager & getSourceManager()
Represents a C++ struct/union/class.
static bool isCompoundAssignmentOp(Opcode Opc)
CXXCatchStmt - This represents a C++ catch block.
bool LE(InterpState &S, CodePtr OpPC)
capture_range captures() const
Retrieve this lambda's captures.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Stmt * getParentIgnoreParenCasts(Stmt *) const
A reference to a declared variable, function, enum, etc.
bool isPointerType() const
A trivial tuple used to represent a source range.
SourceLocation getBegin() const
const LangOptions & getLangOpts() const
This class handles loading and caching of source files into memory.