ObjCUnusedIVarsChecker.cpp 6.0 KB

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