brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.8 KiB · a2af7b1 Raw
189 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9//  This file defines a CheckObjCUnusedIvars, a checker that10//  analyzes an Objective-C class's interface/implementation to determine if it11//  has any ivars that are never accessed.12//13//===----------------------------------------------------------------------===//14 15#include "clang/AST/Attr.h"16#include "clang/AST/DeclObjC.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprObjC.h"19#include "clang/Analysis/PathDiagnostic.h"20#include "clang/Basic/SourceManager.h"21#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"22#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"23#include "clang/StaticAnalyzer/Core/Checker.h"24#include "llvm/ADT/STLExtras.h"25 26using namespace clang;27using namespace ento;28 29enum IVarState { Unused, Used };30typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;31 32static void Scan(IvarUsageMap& M, const Stmt *S) {33  if (!S)34    return;35 36  if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {37    const ObjCIvarDecl *D = Ex->getDecl();38    IvarUsageMap::iterator I = M.find(D);39    if (I != M.end())40      I->second = Used;41    return;42  }43 44  // Blocks can reference an instance variable of a class.45  if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {46    Scan(M, BE->getBody());47    return;48  }49 50  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))51    for (const Expr *sub : POE->semantics()) {52      if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))53        sub = OVE->getSourceExpr();54      Scan(M, sub);55    }56 57  for (const Stmt *SubStmt : S->children())58    Scan(M, SubStmt);59}60 61static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {62  if (!D)63    return;64 65  const ObjCIvarDecl *ID = D->getPropertyIvarDecl();66 67  if (!ID)68    return;69 70  IvarUsageMap::iterator I = M.find(ID);71  if (I != M.end())72    I->second = Used;73}74 75static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {76  // Scan the methods for accesses.77  for (const auto *I : D->instance_methods())78    Scan(M, I->getBody());79 80  if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {81    // Scan for @synthesized property methods that act as setters/getters82    // to an ivar.83    for (const auto *I : ID->property_impls())84      Scan(M, I);85 86    // Scan the associated categories as well.87    for (const auto *Cat : ID->getClassInterface()->visible_categories()) {88      if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())89        Scan(M, CID);90    }91  }92}93 94static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,95                 const SourceManager &SM) {96  for (const auto *I : C->decls())97    if (const auto *FD = dyn_cast<FunctionDecl>(I)) {98      SourceLocation L = FD->getBeginLoc();99      if (SM.getFileID(L) == FID)100        Scan(M, FD->getBody());101    }102}103 104static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,105                                BugReporter &BR,106                                const CheckerBase *Checker) {107 108  const ObjCInterfaceDecl *ID = D->getClassInterface();109  IvarUsageMap M;110 111  // Iterate over the ivars.112  for (const auto *Ivar : ID->ivars()) {113    // Ignore ivars that...114    // (a) aren't private115    // (b) explicitly marked unused116    // (c) are iboutlets117    // (d) are unnamed bitfields118    if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||119        Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||120        Ivar->hasAttr<IBOutletCollectionAttr>() || Ivar->isUnnamedBitField())121      continue;122 123    M[Ivar] = Unused;124  }125 126  if (M.empty())127    return;128 129  // Now scan the implementation declaration.130  Scan(M, D);131 132  // Any potentially unused ivars?133  bool hasUnused = false;134  for (IVarState State : llvm::make_second_range(M))135    if (State == Unused) {136      hasUnused = true;137      break;138    }139 140  if (!hasUnused)141    return;142 143  // We found some potentially unused ivars.  Scan the entire translation unit144  // for functions inside the @implementation that reference these ivars.145  // FIXME: In the future hopefully we can just use the lexical DeclContext146  // to go from the ObjCImplementationDecl to the lexically "nested"147  // C functions.148  const SourceManager &SM = BR.getSourceManager();149  Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);150 151  // Find ivars that are unused.152  for (auto [Ivar, State] : M)153    if (State == Unused) {154      std::string sbuf;155      llvm::raw_string_ostream os(sbuf);156      os << "Instance variable '" << *Ivar << "' in class '" << *ID157         << "' is never used by the methods in its @implementation "158            "(although it may be used by category methods).";159 160      PathDiagnosticLocation L =161          PathDiagnosticLocation::create(Ivar, BR.getSourceManager());162      BR.EmitBasicReport(ID, Checker, "Unused instance variable",163                         "Optimization", os.str(), L);164    }165}166 167//===----------------------------------------------------------------------===//168// ObjCUnusedIvarsChecker169//===----------------------------------------------------------------------===//170 171namespace {172class ObjCUnusedIvarsChecker : public Checker<173                                      check::ASTDecl<ObjCImplementationDecl> > {174public:175  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,176                    BugReporter &BR) const {177    checkObjCUnusedIvar(D, BR, this);178  }179};180}181 182void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {183  mgr.registerChecker<ObjCUnusedIvarsChecker>();184}185 186bool ento::shouldRegisterObjCUnusedIvarsChecker(const CheckerManager &mgr) {187  return true;188}189