mirror of
https://github.com/opnsense/src.git
synced 2026-02-28 12:20:54 -05:00
203 lines
7.1 KiB
C++
203 lines
7.1 KiB
C++
//==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines a ObjCMissingSuperCallChecker, a checker that
|
|
// analyzes a UIViewController implementation to determine if it
|
|
// correctly calls super in the methods where this is mandatory.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ClangSACheckers.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
|
|
#include "clang/AST/ExprObjC.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
|
|
static bool isUIViewControllerSubclass(ASTContext &Ctx,
|
|
const ObjCImplementationDecl *D) {
|
|
IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController");
|
|
const ObjCInterfaceDecl *ID = D->getClassInterface();
|
|
|
|
for ( ; ID; ID = ID->getSuperClass())
|
|
if (ID->getIdentifier() == ViewControllerII)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// FindSuperCallVisitor - Identify specific calls to the superclass.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
|
|
public:
|
|
explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
|
|
|
|
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
|
|
if (E->getSelector() == Sel)
|
|
if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
|
|
DoesCallSuper = true;
|
|
|
|
// Recurse if we didn't find the super call yet.
|
|
return !DoesCallSuper;
|
|
}
|
|
|
|
bool DoesCallSuper;
|
|
|
|
private:
|
|
Selector Sel;
|
|
};
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ObjCSuperCallChecker
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class ObjCSuperCallChecker : public Checker<
|
|
check::ASTDecl<ObjCImplementationDecl> > {
|
|
public:
|
|
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
|
|
BugReporter &BR) const;
|
|
};
|
|
}
|
|
|
|
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
|
|
AnalysisManager &Mgr,
|
|
BugReporter &BR) const {
|
|
ASTContext &Ctx = BR.getContext();
|
|
|
|
if (!isUIViewControllerSubclass(Ctx, D))
|
|
return;
|
|
|
|
const char *SelectorNames[] =
|
|
{"addChildViewController", "viewDidAppear", "viewDidDisappear",
|
|
"viewWillAppear", "viewWillDisappear", "removeFromParentViewController",
|
|
"didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload",
|
|
"viewDidLoad"};
|
|
const unsigned SelectorArgumentCounts[] =
|
|
{1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
|
|
const size_t SelectorCount = llvm::array_lengthof(SelectorNames);
|
|
assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount);
|
|
|
|
// Fill the Selectors SmallSet with all selectors we want to check.
|
|
llvm::SmallSet<Selector, 16> Selectors;
|
|
for (size_t i = 0; i < SelectorCount; i++) {
|
|
unsigned ArgumentCount = SelectorArgumentCounts[i];
|
|
const char *SelectorCString = SelectorNames[i];
|
|
|
|
// Get the selector.
|
|
IdentifierInfo *II = &Ctx.Idents.get(SelectorCString);
|
|
Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II));
|
|
}
|
|
|
|
// Iterate over all instance methods.
|
|
for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
|
|
E = D->instmeth_end();
|
|
I != E; ++I) {
|
|
Selector S = (*I)->getSelector();
|
|
// Find out whether this is a selector that we want to check.
|
|
if (!Selectors.count(S))
|
|
continue;
|
|
|
|
ObjCMethodDecl *MD = *I;
|
|
|
|
// Check if the method calls its superclass implementation.
|
|
if (MD->getBody())
|
|
{
|
|
FindSuperCallVisitor Visitor(S);
|
|
Visitor.TraverseDecl(MD);
|
|
|
|
// It doesn't call super, emit a diagnostic.
|
|
if (!Visitor.DoesCallSuper) {
|
|
PathDiagnosticLocation DLoc =
|
|
PathDiagnosticLocation::createEnd(MD->getBody(),
|
|
BR.getSourceManager(),
|
|
Mgr.getAnalysisDeclContext(D));
|
|
|
|
const char *Name = "Missing call to superclass";
|
|
SmallString<256> Buf;
|
|
llvm::raw_svector_ostream os(Buf);
|
|
|
|
os << "The '" << S.getAsString()
|
|
<< "' instance method in UIViewController subclass '" << *D
|
|
<< "' is missing a [super " << S.getAsString() << "] call";
|
|
|
|
BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
|
|
os.str(), DLoc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Check registration.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
|
|
Mgr.registerChecker<ObjCSuperCallChecker>();
|
|
}
|
|
|
|
|
|
/*
|
|
ToDo list for expanding this check in the future, the list is not exhaustive.
|
|
There are also cases where calling super is suggested but not "mandatory".
|
|
In addition to be able to check the classes and methods below, architectural
|
|
improvements like being able to allow for the super-call to be done in a called
|
|
method would be good too.
|
|
|
|
*** trivial cases:
|
|
UIResponder subclasses
|
|
- resignFirstResponder
|
|
|
|
NSResponder subclasses
|
|
- cursorUpdate
|
|
|
|
*** more difficult cases:
|
|
|
|
UIDocument subclasses
|
|
- finishedHandlingError:recovered: (is multi-arg)
|
|
- finishedHandlingError:recovered: (is multi-arg)
|
|
|
|
UIViewController subclasses
|
|
- loadView (should *never* call super)
|
|
- transitionFromViewController:toViewController:
|
|
duration:options:animations:completion: (is multi-arg)
|
|
|
|
UICollectionViewController subclasses
|
|
- loadView (take care because UIViewController subclasses should NOT call super
|
|
in loadView, but UICollectionViewController subclasses should)
|
|
|
|
NSObject subclasses
|
|
- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
|
|
|
|
UIPopoverBackgroundView subclasses (some of those are class methods)
|
|
- arrowDirection (should *never* call super)
|
|
- arrowOffset (should *never* call super)
|
|
- arrowBase (should *never* call super)
|
|
- arrowHeight (should *never* call super)
|
|
- contentViewInsets (should *never* call super)
|
|
|
|
UITextSelectionRect subclasses (some of those are properties)
|
|
- rect (should *never* call super)
|
|
- range (should *never* call super)
|
|
- writingDirection (should *never* call super)
|
|
- isVertical (should *never* call super)
|
|
- containsStart (should *never* call super)
|
|
- containsEnd (should *never* call super)
|
|
*/
|