diff options
Diffstat (limited to 'clang-tidy/objc/SuperSelfCheck.cpp')
-rw-r--r-- | clang-tidy/objc/SuperSelfCheck.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/clang-tidy/objc/SuperSelfCheck.cpp b/clang-tidy/objc/SuperSelfCheck.cpp new file mode 100644 index 00000000..7aafd66a --- /dev/null +++ b/clang-tidy/objc/SuperSelfCheck.cpp @@ -0,0 +1,127 @@ +//===--- SuperSelfCheck.cpp - clang-tidy ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SuperSelfCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace objc { + +namespace { + +/// \brief Matches Objective-C methods in the initializer family. +/// +/// Example matches -init and -initWithInt:. +/// (matcher = objcMethodDecl(isInitializer())) +/// \code +/// @interface Foo +/// - (instancetype)init; +/// - (instancetype)initWithInt:(int)i; +/// + (instancetype)init; +/// - (void)bar; +/// @end +/// \endcode +AST_MATCHER(ObjCMethodDecl, isInitializer) { + return Node.getMethodFamily() == OMF_init; +} + +/// \brief Matches Objective-C implementations of classes that directly or +/// indirectly have a superclass matching \c InterfaceDecl. +/// +/// Note that a class is not considered to be a subclass of itself. +/// +/// Example matches implementation declarations for Y and Z. +/// (matcher = objcInterfaceDecl(isSubclassOf(hasName("X")))) +/// \code +/// @interface X +/// @end +/// @interface Y : X +/// @end +/// @implementation Y // directly derived +/// @end +/// @interface Z : Y +/// @end +/// @implementation Z // indirectly derived +/// @end +/// \endcode +AST_MATCHER_P(ObjCImplementationDecl, isSubclassOf, + ast_matchers::internal::Matcher<ObjCInterfaceDecl>, + InterfaceDecl) { + // Check if any of the superclasses of the class match. + for (const ObjCInterfaceDecl *SuperClass = + Node.getClassInterface()->getSuperClass(); + SuperClass != nullptr; SuperClass = SuperClass->getSuperClass()) { + if (InterfaceDecl.matches(*SuperClass, Finder, Builder)) + return true; + } + + // No matches found. + return false; +} + +/// \brief Matches Objective-C message expressions where the receiver is the +/// super instance. +/// +/// Example matches the invocations of -banana and -orange. +/// (matcher = objcMessageExpr(isMessagingSuperInstance())) +/// \code +/// - (void)banana { +/// [self apple] +/// [super banana]; +/// [super orange]; +/// } +/// \endcode +AST_MATCHER(ObjCMessageExpr, isMessagingSuperInstance) { + return Node.getReceiverKind() == ObjCMessageExpr::SuperInstance; +} + +} // namespace + +void SuperSelfCheck::registerMatchers(MatchFinder *Finder) { + // This check should only be applied to Objective-C sources. + if (!getLangOpts().ObjC) + return; + + Finder->addMatcher( + objcMessageExpr( + hasSelector("self"), isMessagingSuperInstance(), + hasAncestor(objcMethodDecl(isInitializer(), + hasDeclContext(objcImplementationDecl( + isSubclassOf(hasName("NSObject"))))))) + .bind("message"), + this); +} + +void SuperSelfCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Message = Result.Nodes.getNodeAs<ObjCMessageExpr>("message"); + + auto Diag = diag(Message->getExprLoc(), "suspicious invocation of %0 in " + "initializer; did you mean to " + "invoke a superclass initializer?") + << Message->getMethodDecl(); + + SourceLocation ReceiverLoc = Message->getReceiverRange().getBegin(); + if (ReceiverLoc.isMacroID() || ReceiverLoc.isInvalid()) + return; + + SourceLocation SelectorLoc = Message->getSelectorStartLoc(); + if (SelectorLoc.isMacroID() || SelectorLoc.isInvalid()) + return; + + Diag << FixItHint::CreateReplacement(Message->getSourceRange(), + StringRef("[super init]")); +} + +} // namespace objc +} // namespace tidy +} // namespace clang |