clang API Documentation

ObjCMissingSuperCallChecker.cpp
Go to the documentation of this file.
00001 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file defines a ObjCMissingSuperCallChecker, a checker that
00011 //  analyzes a UIViewController implementation to determine if it
00012 //  correctly calls super in the methods where this is mandatory.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #include "ClangSACheckers.h"
00017 #include "clang/AST/DeclObjC.h"
00018 #include "clang/AST/Expr.h"
00019 #include "clang/AST/ExprObjC.h"
00020 #include "clang/AST/RecursiveASTVisitor.h"
00021 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
00022 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
00023 #include "clang/StaticAnalyzer/Core/Checker.h"
00024 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
00025 #include "llvm/ADT/SmallSet.h"
00026 #include "llvm/ADT/SmallString.h"
00027 #include "llvm/Support/raw_ostream.h"
00028 
00029 using namespace clang;
00030 using namespace ento;
00031 
00032 namespace {
00033 struct SelectorDescriptor {
00034   const char *SelectorName;
00035   unsigned ArgumentCount;
00036 };
00037 }
00038 
00039 //===----------------------------------------------------------------------===//
00040 // FindSuperCallVisitor - Identify specific calls to the superclass.
00041 //===----------------------------------------------------------------------===//
00042 
00043 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
00044 public:
00045   explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
00046 
00047   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
00048     if (E->getSelector() == Sel)
00049       if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
00050         DoesCallSuper = true;
00051 
00052     // Recurse if we didn't find the super call yet.
00053     return !DoesCallSuper; 
00054   }
00055 
00056   bool DoesCallSuper;
00057 
00058 private:
00059   Selector Sel;
00060 };
00061 
00062 //===----------------------------------------------------------------------===//
00063 // ObjCSuperCallChecker 
00064 //===----------------------------------------------------------------------===//
00065 
00066 namespace {
00067 class ObjCSuperCallChecker : public Checker<
00068                                       check::ASTDecl<ObjCImplementationDecl> > {
00069 public:
00070   ObjCSuperCallChecker() : IsInitialized(false) {}
00071 
00072   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
00073                     BugReporter &BR) const;
00074 private:
00075   bool isCheckableClass(const ObjCImplementationDecl *D,
00076                         StringRef &SuperclassName) const;
00077   void initializeSelectors(ASTContext &Ctx) const;
00078   void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
00079                      StringRef ClassName) const;
00080   mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
00081   mutable bool IsInitialized;
00082 };
00083 
00084 }
00085 
00086 /// \brief Determine whether the given class has a superclass that we want
00087 /// to check. The name of the found superclass is stored in SuperclassName.
00088 ///
00089 /// \param D The declaration to check for superclasses.
00090 /// \param[out] SuperclassName On return, the found superclass name.
00091 bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
00092                                             StringRef &SuperclassName) const {
00093   const ObjCInterfaceDecl *ID = D->getClassInterface();
00094   for ( ; ID ; ID = ID->getSuperClass())
00095   {
00096     SuperclassName = ID->getIdentifier()->getName();
00097     if (SelectorsForClass.count(SuperclassName))
00098       return true;
00099   }
00100   return false;
00101 }
00102 
00103 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
00104                                          ArrayRef<SelectorDescriptor> Sel,
00105                                          StringRef ClassName) const {
00106   llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
00107   // Fill the Selectors SmallSet with all selectors we want to check.
00108   for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
00109        I != E; ++I) {
00110     SelectorDescriptor Descriptor = *I;
00111     assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
00112 
00113     // Get the selector.
00114     IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
00115 
00116     Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
00117     ClassSelectors.insert(Sel);
00118   }
00119 }
00120 
00121 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
00122 
00123   { // Initialize selectors for: UIViewController
00124     const SelectorDescriptor Selectors[] = {
00125       { "addChildViewController", 1 },
00126       { "viewDidAppear", 1 },
00127       { "viewDidDisappear", 1 },
00128       { "viewWillAppear", 1 },
00129       { "viewWillDisappear", 1 },
00130       { "removeFromParentViewController", 0 },
00131       { "didReceiveMemoryWarning", 0 },
00132       { "viewDidUnload", 0 },
00133       { "viewDidLoad", 0 },
00134       { "viewWillUnload", 0 },
00135       { "updateViewConstraints", 0 },
00136       { "encodeRestorableStateWithCoder", 1 },
00137       { "restoreStateWithCoder", 1 }};
00138 
00139     fillSelectors(Ctx, Selectors, "UIViewController");
00140   }
00141 
00142   { // Initialize selectors for: UIResponder
00143     const SelectorDescriptor Selectors[] = {
00144       { "resignFirstResponder", 0 }};
00145 
00146     fillSelectors(Ctx, Selectors, "UIResponder");
00147   }
00148 
00149   { // Initialize selectors for: NSResponder
00150     const SelectorDescriptor Selectors[] = {
00151       { "encodeRestorableStateWithCoder", 1 },
00152       { "restoreStateWithCoder", 1 }};
00153 
00154     fillSelectors(Ctx, Selectors, "NSResponder");
00155   }
00156 
00157   { // Initialize selectors for: NSDocument
00158     const SelectorDescriptor Selectors[] = {
00159       { "encodeRestorableStateWithCoder", 1 },
00160       { "restoreStateWithCoder", 1 }};
00161 
00162     fillSelectors(Ctx, Selectors, "NSDocument");
00163   }
00164 
00165   IsInitialized = true;
00166 }
00167 
00168 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
00169                                         AnalysisManager &Mgr,
00170                                         BugReporter &BR) const {
00171   ASTContext &Ctx = BR.getContext();
00172 
00173   // We need to initialize the selector table once.
00174   if (!IsInitialized)
00175     initializeSelectors(Ctx);
00176 
00177   // Find out whether this class has a superclass that we are supposed to check.
00178   StringRef SuperclassName;
00179   if (!isCheckableClass(D, SuperclassName))
00180     return;
00181 
00182 
00183   // Iterate over all instance methods.
00184   for (auto *MD : D->instance_methods()) {
00185     Selector S = MD->getSelector();
00186     // Find out whether this is a selector that we want to check.
00187     if (!SelectorsForClass[SuperclassName].count(S))
00188       continue;
00189 
00190     // Check if the method calls its superclass implementation.
00191     if (MD->getBody())
00192     {
00193       FindSuperCallVisitor Visitor(S);
00194       Visitor.TraverseDecl(MD);
00195 
00196       // It doesn't call super, emit a diagnostic.
00197       if (!Visitor.DoesCallSuper) {
00198         PathDiagnosticLocation DLoc =
00199           PathDiagnosticLocation::createEnd(MD->getBody(),
00200                                             BR.getSourceManager(),
00201                                             Mgr.getAnalysisDeclContext(D));
00202 
00203         const char *Name = "Missing call to superclass";
00204         SmallString<320> Buf;
00205         llvm::raw_svector_ostream os(Buf);
00206 
00207         os << "The '" << S.getAsString() 
00208            << "' instance method in " << SuperclassName.str() << " subclass '"
00209            << *D << "' is missing a [super " << S.getAsString() << "] call";
00210 
00211         BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
00212                            os.str(), DLoc);
00213       }
00214     }
00215   }
00216 }
00217 
00218 
00219 //===----------------------------------------------------------------------===//
00220 // Check registration.
00221 //===----------------------------------------------------------------------===//
00222 
00223 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
00224   Mgr.registerChecker<ObjCSuperCallChecker>();
00225 }
00226 
00227 
00228 /*
00229  ToDo list for expanding this check in the future, the list is not exhaustive.
00230  There are also cases where calling super is suggested but not "mandatory".
00231  In addition to be able to check the classes and methods below, architectural
00232  improvements like being able to allow for the super-call to be done in a called
00233  method would be good too.
00234 
00235 UIDocument subclasses
00236 - finishedHandlingError:recovered: (is multi-arg)
00237 - finishedHandlingError:recovered: (is multi-arg)
00238 
00239 UIViewController subclasses
00240 - loadView (should *never* call super)
00241 - transitionFromViewController:toViewController:
00242          duration:options:animations:completion: (is multi-arg)
00243 
00244 UICollectionViewController subclasses
00245 - loadView (take care because UIViewController subclasses should NOT call super
00246             in loadView, but UICollectionViewController subclasses should)
00247 
00248 NSObject subclasses
00249 - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
00250 
00251 UIPopoverBackgroundView subclasses (some of those are class methods)
00252 - arrowDirection (should *never* call super)
00253 - arrowOffset (should *never* call super)
00254 - arrowBase (should *never* call super)
00255 - arrowHeight (should *never* call super)
00256 - contentViewInsets (should *never* call super)
00257 
00258 UITextSelectionRect subclasses (some of those are properties)
00259 - rect (should *never* call super)
00260 - range (should *never* call super)
00261 - writingDirection (should *never* call super)
00262 - isVertical (should *never* call super)
00263 - containsStart (should *never* call super)
00264 - containsEnd (should *never* call super)
00265 */