ObjCUnusedIVarsChecker.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- 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 CheckObjCUnusedIvars, a checker that
  11. // analyzes an Objective-C class's interface/implementation to determine if it
  12. // has any ivars that are never accessed.
  13. //
  14. //===----------------------------------------------------------------------===//
  15. #include "ClangSACheckers.h"
  16. #include "clang/AST/Attr.h"
  17. #include "clang/AST/DeclObjC.h"
  18. #include "clang/AST/Expr.h"
  19. #include "clang/AST/ExprObjC.h"
  20. #include "clang/Basic/LangOptions.h"
  21. #include "clang/Basic/SourceManager.h"
  22. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  23. #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
  24. #include "clang/StaticAnalyzer/Core/Checker.h"
  25. using namespace clang;
  26. using namespace ento;
  27. enum IVarState { Unused, Used };
  28. typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
  29. static void Scan(IvarUsageMap& M, const Stmt *S) {
  30. if (!S)
  31. return;
  32. if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
  33. const ObjCIvarDecl *D = Ex->getDecl();
  34. IvarUsageMap::iterator I = M.find(D);
  35. if (I != M.end())
  36. I->second = Used;
  37. return;
  38. }
  39. // Blocks can reference an instance variable of a class.
  40. if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
  41. Scan(M, BE->getBody());
  42. return;
  43. }
  44. if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
  45. for (PseudoObjectExpr::const_semantics_iterator
  46. i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
  47. const Expr *sub = *i;
  48. if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
  49. sub = OVE->getSourceExpr();
  50. Scan(M, sub);
  51. }
  52. for (const Stmt *SubStmt : S->children())
  53. Scan(M, SubStmt);
  54. }
  55. static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
  56. if (!D)
  57. return;
  58. const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
  59. if (!ID)
  60. return;
  61. IvarUsageMap::iterator I = M.find(ID);
  62. if (I != M.end())
  63. I->second = Used;
  64. }
  65. static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
  66. // Scan the methods for accesses.
  67. for (const auto *I : D->instance_methods())
  68. Scan(M, I->getBody());
  69. if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
  70. // Scan for @synthesized property methods that act as setters/getters
  71. // to an ivar.
  72. for (const auto *I : ID->property_impls())
  73. Scan(M, I);
  74. // Scan the associated categories as well.
  75. for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
  76. if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
  77. Scan(M, CID);
  78. }
  79. }
  80. }
  81. static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
  82. SourceManager &SM) {
  83. for (const auto *I : C->decls())
  84. if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
  85. SourceLocation L = FD->getLocStart();
  86. if (SM.getFileID(L) == FID)
  87. Scan(M, FD->getBody());
  88. }
  89. }
  90. static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
  91. BugReporter &BR,
  92. const CheckerBase *Checker) {
  93. const ObjCInterfaceDecl *ID = D->getClassInterface();
  94. IvarUsageMap M;
  95. // Iterate over the ivars.
  96. for (const auto *Ivar : ID->ivars()) {
  97. // Ignore ivars that...
  98. // (a) aren't private
  99. // (b) explicitly marked unused
  100. // (c) are iboutlets
  101. // (d) are unnamed bitfields
  102. if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
  103. Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
  104. Ivar->hasAttr<IBOutletCollectionAttr>() ||
  105. Ivar->isUnnamedBitfield())
  106. continue;
  107. M[Ivar] = Unused;
  108. }
  109. if (M.empty())
  110. return;
  111. // Now scan the implementation declaration.
  112. Scan(M, D);
  113. // Any potentially unused ivars?
  114. bool hasUnused = false;
  115. for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
  116. if (I->second == Unused) {
  117. hasUnused = true;
  118. break;
  119. }
  120. if (!hasUnused)
  121. return;
  122. // We found some potentially unused ivars. Scan the entire translation unit
  123. // for functions inside the @implementation that reference these ivars.
  124. // FIXME: In the future hopefully we can just use the lexical DeclContext
  125. // to go from the ObjCImplementationDecl to the lexically "nested"
  126. // C functions.
  127. SourceManager &SM = BR.getSourceManager();
  128. Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
  129. // Find ivars that are unused.
  130. for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
  131. if (I->second == Unused) {
  132. std::string sbuf;
  133. llvm::raw_string_ostream os(sbuf);
  134. os << "Instance variable '" << *I->first << "' in class '" << *ID
  135. << "' is never used by the methods in its @implementation "
  136. "(although it may be used by category methods).";
  137. PathDiagnosticLocation L =
  138. PathDiagnosticLocation::create(I->first, BR.getSourceManager());
  139. BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
  140. os.str(), L);
  141. }
  142. }
  143. //===----------------------------------------------------------------------===//
  144. // ObjCUnusedIvarsChecker
  145. //===----------------------------------------------------------------------===//
  146. namespace {
  147. class ObjCUnusedIvarsChecker : public Checker<
  148. check::ASTDecl<ObjCImplementationDecl> > {
  149. public:
  150. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
  151. BugReporter &BR) const {
  152. checkObjCUnusedIvar(D, BR, this);
  153. }
  154. };
  155. }
  156. void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
  157. mgr.registerChecker<ObjCUnusedIvarsChecker>();
  158. }