DeadStoresChecker.cpp revision 7df1234c2e62b2a23dc4417e527f941c20ebe858
1//==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines a DeadStores, a flow-sensitive checker that looks for 11// stores to variables that are no longer live. 12// 13//===----------------------------------------------------------------------===// 14 15#include "ClangSACheckers.h" 16#include "clang/StaticAnalyzer/Core/Checker.h" 17#include "clang/Analysis/Analyses/LiveVariables.h" 18#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" 19#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 21#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" 22#include "clang/Basic/Diagnostic.h" 23#include "clang/AST/ASTContext.h" 24#include "clang/AST/ParentMap.h" 25#include "llvm/ADT/SmallPtrSet.h" 26 27using namespace clang; 28using namespace ento; 29 30namespace { 31 32// FIXME: Eventually migrate into its own file, and have it managed by 33// AnalysisManager. 34class ReachableCode { 35 const CFG &cfg; 36 llvm::BitVector reachable; 37public: 38 ReachableCode(const CFG &cfg) 39 : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {} 40 41 void computeReachableBlocks(); 42 43 bool isReachable(const CFGBlock *block) const { 44 return reachable[block->getBlockID()]; 45 } 46}; 47} 48 49void ReachableCode::computeReachableBlocks() { 50 if (!cfg.getNumBlockIDs()) 51 return; 52 53 SmallVector<const CFGBlock*, 10> worklist; 54 worklist.push_back(&cfg.getEntry()); 55 56 while (!worklist.empty()) { 57 const CFGBlock *block = worklist.back(); 58 worklist.pop_back(); 59 llvm::BitVector::reference isReachable = reachable[block->getBlockID()]; 60 if (isReachable) 61 continue; 62 isReachable = true; 63 for (CFGBlock::const_succ_iterator i = block->succ_begin(), 64 e = block->succ_end(); i != e; ++i) 65 if (const CFGBlock *succ = *i) 66 worklist.push_back(succ); 67 } 68} 69 70namespace { 71class DeadStoreObs : public LiveVariables::Observer { 72 const CFG &cfg; 73 ASTContext &Ctx; 74 BugReporter& BR; 75 ParentMap& Parents; 76 llvm::SmallPtrSet<const VarDecl*, 20> Escaped; 77 llvm::OwningPtr<ReachableCode> reachableCode; 78 const CFGBlock *currentBlock; 79 80 enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; 81 82public: 83 DeadStoreObs(const CFG &cfg, ASTContext &ctx, 84 BugReporter& br, ParentMap& parents, 85 llvm::SmallPtrSet<const VarDecl*, 20> &escaped) 86 : cfg(cfg), Ctx(ctx), BR(br), Parents(parents), 87 Escaped(escaped), currentBlock(0) {} 88 89 virtual ~DeadStoreObs() {} 90 91 void Report(const VarDecl *V, DeadStoreKind dsk, 92 SourceLocation L, SourceRange R) { 93 if (Escaped.count(V)) 94 return; 95 96 // Compute reachable blocks within the CFG for trivial cases 97 // where a bogus dead store can be reported because itself is unreachable. 98 if (!reachableCode.get()) { 99 reachableCode.reset(new ReachableCode(cfg)); 100 reachableCode->computeReachableBlocks(); 101 } 102 103 if (!reachableCode->isReachable(currentBlock)) 104 return; 105 106 llvm::SmallString<64> buf; 107 llvm::raw_svector_ostream os(buf); 108 const char *BugType = 0; 109 110 switch (dsk) { 111 default: 112 llvm_unreachable("Impossible dead store type."); 113 114 case DeadInit: 115 BugType = "Dead initialization"; 116 os << "Value stored to '" << V 117 << "' during its initialization is never read"; 118 break; 119 120 case DeadIncrement: 121 BugType = "Dead increment"; 122 case Standard: 123 if (!BugType) BugType = "Dead assignment"; 124 os << "Value stored to '" << V << "' is never read"; 125 break; 126 127 case Enclosing: 128 // Don't report issues in this case, e.g.: "if (x = foo())", 129 // where 'x' is unused later. We have yet to see a case where 130 // this is a real bug. 131 return; 132 } 133 134 BR.EmitBasicReport(BugType, "Dead store", os.str(), L, R); 135 } 136 137 void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, 138 DeadStoreKind dsk, 139 const LiveVariables::LivenessValues &Live) { 140 141 if (!VD->hasLocalStorage()) 142 return; 143 // Reference types confuse the dead stores checker. Skip them 144 // for now. 145 if (VD->getType()->getAs<ReferenceType>()) 146 return; 147 148 if (!Live.isLive(VD) && 149 !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) 150 Report(VD, dsk, Ex->getSourceRange().getBegin(), 151 Val->getSourceRange()); 152 } 153 154 void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk, 155 const LiveVariables::LivenessValues& Live) { 156 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) 157 CheckVarDecl(VD, DR, Val, dsk, Live); 158 } 159 160 bool isIncrement(VarDecl *VD, const BinaryOperator* B) { 161 if (B->isCompoundAssignmentOp()) 162 return true; 163 164 const Expr *RHS = B->getRHS()->IgnoreParenCasts(); 165 const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); 166 167 if (!BRHS) 168 return false; 169 170 const DeclRefExpr *DR; 171 172 if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts()))) 173 if (DR->getDecl() == VD) 174 return true; 175 176 if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts()))) 177 if (DR->getDecl() == VD) 178 return true; 179 180 return false; 181 } 182 183 virtual void observeStmt(const Stmt *S, const CFGBlock *block, 184 const LiveVariables::LivenessValues &Live) { 185 186 currentBlock = block; 187 188 // Skip statements in macros. 189 if (S->getLocStart().isMacroID()) 190 return; 191 192 // Only cover dead stores from regular assignments. ++/-- dead stores 193 // have never flagged a real bug. 194 if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { 195 if (!B->isAssignmentOp()) return; // Skip non-assignments. 196 197 if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS())) 198 if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 199 // Special case: check for assigning null to a pointer. 200 // This is a common form of defensive programming. 201 QualType T = VD->getType(); 202 if (T->isPointerType() || T->isObjCObjectPointerType()) { 203 if (B->getRHS()->isNullPointerConstant(Ctx, 204 Expr::NPC_ValueDependentIsNull)) 205 return; 206 } 207 208 Expr *RHS = B->getRHS()->IgnoreParenCasts(); 209 // Special case: self-assignments. These are often used to shut up 210 // "unused variable" compiler warnings. 211 if (DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) 212 if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) 213 return; 214 215 // Otherwise, issue a warning. 216 DeadStoreKind dsk = Parents.isConsumedExpr(B) 217 ? Enclosing 218 : (isIncrement(VD,B) ? DeadIncrement : Standard); 219 220 CheckVarDecl(VD, DR, B->getRHS(), dsk, Live); 221 } 222 } 223 else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { 224 if (!U->isIncrementOp() || U->isPrefix()) 225 return; 226 227 const Stmt *parent = Parents.getParentIgnoreParenCasts(U); 228 if (!parent || !isa<ReturnStmt>(parent)) 229 return; 230 231 const Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); 232 233 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) 234 CheckDeclRef(DR, U, DeadIncrement, Live); 235 } 236 else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) 237 // Iterate through the decls. Warn if any initializers are complex 238 // expressions that are not live (never used). 239 for (DeclStmt::const_decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); 240 DI != DE; ++DI) { 241 242 VarDecl *V = dyn_cast<VarDecl>(*DI); 243 244 if (!V) 245 continue; 246 247 if (V->hasLocalStorage()) { 248 // Reference types confuse the dead stores checker. Skip them 249 // for now. 250 if (V->getType()->getAs<ReferenceType>()) 251 return; 252 253 if (Expr *E = V->getInit()) { 254 while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) 255 E = exprClean->getSubExpr(); 256 257 // Don't warn on C++ objects (yet) until we can show that their 258 // constructors/destructors don't have side effects. 259 if (isa<CXXConstructExpr>(E)) 260 return; 261 262 // A dead initialization is a variable that is dead after it 263 // is initialized. We don't flag warnings for those variables 264 // marked 'unused'. 265 if (!Live.isLive(V) && V->getAttr<UnusedAttr>() == 0) { 266 // Special case: check for initializations with constants. 267 // 268 // e.g. : int x = 0; 269 // 270 // If x is EVER assigned a new value later, don't issue 271 // a warning. This is because such initialization can be 272 // due to defensive programming. 273 if (E->isConstantInitializer(Ctx, false)) 274 return; 275 276 if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) 277 if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 278 // Special case: check for initialization from constant 279 // variables. 280 // 281 // e.g. extern const int MyConstant; 282 // int x = MyConstant; 283 // 284 if (VD->hasGlobalStorage() && 285 VD->getType().isConstQualified()) 286 return; 287 // Special case: check for initialization from scalar 288 // parameters. This is often a form of defensive 289 // programming. Non-scalars are still an error since 290 // because it more likely represents an actual algorithmic 291 // bug. 292 if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType()) 293 return; 294 } 295 296 Report(V, DeadInit, V->getLocation(), E->getSourceRange()); 297 } 298 } 299 } 300 } 301 } 302}; 303 304} // end anonymous namespace 305 306//===----------------------------------------------------------------------===// 307// Driver function to invoke the Dead-Stores checker on a CFG. 308//===----------------------------------------------------------------------===// 309 310namespace { 311class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{ 312 CFG *cfg; 313public: 314 FindEscaped(CFG *c) : cfg(c) {} 315 316 CFG& getCFG() { return *cfg; } 317 318 llvm::SmallPtrSet<const VarDecl*, 20> Escaped; 319 320 void VisitUnaryOperator(UnaryOperator* U) { 321 // Check for '&'. Any VarDecl whose value has its address-taken we 322 // treat as escaped. 323 Expr *E = U->getSubExpr()->IgnoreParenCasts(); 324 if (U->getOpcode() == UO_AddrOf) 325 if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) 326 if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 327 Escaped.insert(VD); 328 return; 329 } 330 Visit(E); 331 } 332}; 333} // end anonymous namespace 334 335 336//===----------------------------------------------------------------------===// 337// DeadStoresChecker 338//===----------------------------------------------------------------------===// 339 340namespace { 341class DeadStoresChecker : public Checker<check::ASTCodeBody> { 342public: 343 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 344 BugReporter &BR) const { 345 if (LiveVariables *L = mgr.getLiveVariables(D)) { 346 CFG &cfg = *mgr.getCFG(D); 347 ParentMap &pmap = mgr.getParentMap(D); 348 FindEscaped FS(&cfg); 349 FS.getCFG().VisitBlockStmts(FS); 350 DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped); 351 L->runOnAllBlocks(A); 352 } 353 } 354}; 355} 356 357void ento::registerDeadStoresChecker(CheckerManager &mgr) { 358 mgr.registerChecker<DeadStoresChecker>(); 359} 360