24#include "llvm/ADT/BitVector.h"
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/ADT/SmallString.h"
27#include "llvm/Support/SaveAndRestore.h"
38 llvm::DenseSet<const VarDecl *> &S;
42 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
47 return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
62 EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
70 llvm::BitVector reachable;
72 ReachableCode(
const CFG &cfg)
73 : cfg(cfg), reachable(cfg.getNumBlockIDs(),
false) {}
75 void computeReachableBlocks();
77 bool isReachable(
const CFGBlock *block)
const {
83void ReachableCode::computeReachableBlocks() {
84 if (!cfg.getNumBlockIDs())
88 worklist.push_back(&cfg.getEntry());
90 while (!worklist.empty()) {
91 const CFGBlock *block = worklist.pop_back_val();
92 llvm::BitVector::reference isReachable = reachable[block->
getBlockID()];
99 worklist.push_back(succ);
111 if (Op == BO_Assign || Op == BO_Comma) {
121class DeadStoresChecker :
public Checker<check::ASTCodeBody> {
123 bool ShowFixIts =
false;
124 bool WarnForDeadNestedAssignments =
true;
134 const DeadStoresChecker *
Checker;
138 std::unique_ptr<ReachableCode> reachableCode;
140 std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
142 enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
149 bool warnForDeadNestedAssignments)
150 : cfg(cfg), Ctx(ctx), BR(br),
Checker(checker), AC(ac), Parents(parents),
151 Escaped(escaped), currentBlock(nullptr) {}
153 ~DeadStoreObs()
override {}
161 InEH.reset(
new llvm::DenseSet<const VarDecl *>());
162 EHCodeVisitor
V(*InEH.get());
163 V.TraverseStmt(AC->getBody());
169 return InEH->count(
D);
186 if (
Data.starts_with(
"/* iig"))
192 void Report(
const VarDecl *
V, DeadStoreKind dsk,
194 if (Escaped.count(
V))
199 if (!reachableCode.get()) {
200 reachableCode.reset(
new ReachableCode(cfg));
201 reachableCode->computeReachableBlocks();
204 if (!reachableCode->isReachable(currentBlock))
211 llvm::raw_svector_ostream os(buf);
218 BugType =
"Dead initialization";
219 os <<
"Value stored to '" << *
V
220 <<
"' during its initialization is never read";
224 if (
V->getInit()->HasSideEffects(ACtx,
232 V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
233 SM, LO)->getEndLoc();
246 os <<
"Value stored to '" << *
V <<
"' is never read";
251 if (!
Checker->WarnForDeadNestedAssignments)
253 BugType =
"Dead nested assignment";
254 os <<
"Although the value stored to '" << *
V
255 <<
"' is used in the enclosing expression, the value is never "
256 "actually read from '"
262 os.str(), L, R, Fixits);
278 VD->
hasAttr<ObjCPreciseLifetimeAttr>())) {
289 CheckVarDecl(VD, DR, Val, dsk, Live);
318 currentBlock = block;
321 if (S->getBeginLoc().isMacroID())
337 if (
T.isVolatileQualified())
346 if (
const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
347 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
353 : (isIncrement(VD,B) ? DeadIncrement : Standard);
355 CheckVarDecl(VD, DR, B->
getRHS(), dsk, Live);
359 if (!
U->isIncrementOp() ||
U->isPrefix())
363 if (!parent || !isa<ReturnStmt>(parent))
366 const Expr *Ex =
U->getSubExpr()->IgnoreParenCasts();
368 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
371 else if (
const DeclStmt *DS = dyn_cast<DeclStmt>(S))
374 for (
const auto *DI : DS->decls()) {
375 const auto *
V = dyn_cast<VarDecl>(DI);
380 if (
V->hasLocalStorage()) {
386 if (
const Expr *
E =
V->getInit()) {
387 while (
const FullExpr *FE = dyn_cast<FullExpr>(
E))
388 E = FE->getSubExpr();
396 if (isa<CXXConstructExpr>(
E))
403 !
V->hasAttr<UnusedAttr>() &&
404 !
V->hasAttr<ObjCPreciseLifetimeAttr>()) {
418 if (
const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
439 Report(
V, DeadInit,
Loc,
V->getInit()->getSourceRange());
451 return llvm::all_of(Candidate->
inits(), [
this](
const Expr *
Init) {
452 return isConstant(Init->IgnoreParenCasts());
457 bool isConstant(
const Expr *
E)
const {
463 if (
const auto *ILE = dyn_cast<InitListExpr>(
E)) {
464 return isConstant(ILE);
482 void operator()(
const Stmt *S) {
486 if (
auto *LE = dyn_cast<LambdaExpr>(S)) {
487 findLambdaReferenceCaptures(LE);
494 if (
U->getOpcode() != UO_AddrOf)
497 const Expr *
E =
U->getSubExpr()->IgnoreParenCasts();
504 void findLambdaReferenceCaptures(
const LambdaExpr *LE) {
506 llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
511 if (!
C.capturesVariable())
516 if (!FD || !isa<VarDecl>(VD))
521 Escaped.insert(cast<VarDecl>(VD));
540 if (FD->isTemplateInstantiation())
549 DeadStoreObs A(cfg, BR.
getContext(), BR,
this, AC, pmap, FS.Escaped,
550 WarnForDeadNestedAssignments);
551 L->runOnAllBlocks(A);
559 Chk->WarnForDeadNestedAssignments =
562 AnOpts.getCheckerBooleanOption(Chk,
"ShowFixIts");
565bool ento::shouldRegisterDeadStoresChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
const LangOptions & getLangOpts() const
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Stores options for the analyzer from the command line.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
static bool isCompoundAssignmentOp(Opcode Opc)
Represents a single basic block in a source-level CFG.
unsigned getBlockID() const
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
void VisitBlockStmts(Callback &O) const
CXXCatchStmt - This represents a C++ catch block.
Represents a C++ struct/union/class.
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...
A reference to a declared variable, function, enum, etc.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Decl - This represents one declaration (or definition), e.g.
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.
Represents a member of a struct/union/class.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
FullExpr - Represents a "full-expression" node.
Represents a function declaration or definition.
Describes an C or C++ initializer list.
ArrayRef< Expr * > inits()
Describes the capture of a variable or of this, or of a C++1y init-capture.
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
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
virtual void observeStmt(const Stmt *S, const CFGBlock *currentBlock, const LivenessValues &V)
A callback invoked right before invoking the liveness transfer function on the given statement.
bool isLive(const CFGBlock *B, const VarDecl *D)
Return true if a variable is live at the end of a specified block.
Represents Objective-C's @catch statement.
Represents Objective-C's @finally statement.
bool isConsumedExpr(Expr *E) const
Stmt * getParentIgnoreParenCasts(Stmt *) const
A (possibly-)qualified type.
bool isConstQualified() const
Determine whether this type is const-qualified.
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue=nullptr)
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
Base for LValueReferenceType and RValueReferenceType.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
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.
A trivial tuple used to represent a source range.
SourceLocation getBegin() const
Stmt - This represents one statement.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isPointerType() const
bool isReferenceType() const
bool isScalarType() const
bool isObjCObjectPointerType() const
const T * getAs() const
Member-template getAs<specific type>'.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Represents a variable declaration or definition.
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)
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
ASTContext & getContext()
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
bool isValid() const =delete
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.
const FunctionProtoType * T