123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines a CheckObjCUnusedIvars, a checker that
- // analyzes an Objective-C class's interface/implementation to determine if it
- // has any ivars that are never accessed.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/Analysis/PathDiagnostic.h"
- #include "clang/AST/Attr.h"
- #include "clang/AST/DeclObjC.h"
- #include "clang/AST/Expr.h"
- #include "clang/AST/ExprObjC.h"
- #include "clang/Basic/LangOptions.h"
- #include "clang/Basic/SourceManager.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- using namespace clang;
- using namespace ento;
- enum IVarState { Unused, Used };
- typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
- static void Scan(IvarUsageMap& M, const Stmt *S) {
- if (!S)
- return;
- if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
- const ObjCIvarDecl *D = Ex->getDecl();
- IvarUsageMap::iterator I = M.find(D);
- if (I != M.end())
- I->second = Used;
- return;
- }
- // Blocks can reference an instance variable of a class.
- if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
- Scan(M, BE->getBody());
- return;
- }
- if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
- for (PseudoObjectExpr::const_semantics_iterator
- i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
- const Expr *sub = *i;
- if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
- sub = OVE->getSourceExpr();
- Scan(M, sub);
- }
- for (const Stmt *SubStmt : S->children())
- Scan(M, SubStmt);
- }
- static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
- if (!D)
- return;
- const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
- if (!ID)
- return;
- IvarUsageMap::iterator I = M.find(ID);
- if (I != M.end())
- I->second = Used;
- }
- static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
- // Scan the methods for accesses.
- for (const auto *I : D->instance_methods())
- Scan(M, I->getBody());
- if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
- // Scan for @synthesized property methods that act as setters/getters
- // to an ivar.
- for (const auto *I : ID->property_impls())
- Scan(M, I);
- // Scan the associated categories as well.
- for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
- if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
- Scan(M, CID);
- }
- }
- }
- static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
- const SourceManager &SM) {
- for (const auto *I : C->decls())
- if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
- SourceLocation L = FD->getBeginLoc();
- if (SM.getFileID(L) == FID)
- Scan(M, FD->getBody());
- }
- }
- static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
- BugReporter &BR,
- const CheckerBase *Checker) {
- const ObjCInterfaceDecl *ID = D->getClassInterface();
- IvarUsageMap M;
- // Iterate over the ivars.
- for (const auto *Ivar : ID->ivars()) {
- // Ignore ivars that...
- // (a) aren't private
- // (b) explicitly marked unused
- // (c) are iboutlets
- // (d) are unnamed bitfields
- if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
- Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
- Ivar->hasAttr<IBOutletCollectionAttr>() ||
- Ivar->isUnnamedBitfield())
- continue;
- M[Ivar] = Unused;
- }
- if (M.empty())
- return;
- // Now scan the implementation declaration.
- Scan(M, D);
- // Any potentially unused ivars?
- bool hasUnused = false;
- for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
- if (I->second == Unused) {
- hasUnused = true;
- break;
- }
- if (!hasUnused)
- return;
- // We found some potentially unused ivars. Scan the entire translation unit
- // for functions inside the @implementation that reference these ivars.
- // FIXME: In the future hopefully we can just use the lexical DeclContext
- // to go from the ObjCImplementationDecl to the lexically "nested"
- // C functions.
- const SourceManager &SM = BR.getSourceManager();
- Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
- // Find ivars that are unused.
- for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
- if (I->second == Unused) {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
- os << "Instance variable '" << *I->first << "' in class '" << *ID
- << "' is never used by the methods in its @implementation "
- "(although it may be used by category methods).";
- PathDiagnosticLocation L =
- PathDiagnosticLocation::create(I->first, BR.getSourceManager());
- BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
- os.str(), L);
- }
- }
- //===----------------------------------------------------------------------===//
- // ObjCUnusedIvarsChecker
- //===----------------------------------------------------------------------===//
- namespace {
- class ObjCUnusedIvarsChecker : public Checker<
- check::ASTDecl<ObjCImplementationDecl> > {
- public:
- void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
- BugReporter &BR) const {
- checkObjCUnusedIvar(D, BR, this);
- }
- };
- }
- void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
- mgr.registerChecker<ObjCUnusedIvarsChecker>();
- }
- bool ento::shouldRegisterObjCUnusedIvarsChecker(const CheckerManager &mgr) {
- return true;
- }
|