| //===--- SuperSelfCheck.cpp - clang-tidy ----------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #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 { |
| |
| /// 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; |
| } |
| |
| /// Matches Objective-C implementations with interfaces that match |
| /// \c Base. |
| /// |
| /// Example matches implementation declarations for X. |
| /// (matcher = objcImplementationDecl(hasInterface(hasName("X")))) |
| /// \code |
| /// @interface X |
| /// @end |
| /// @implementation X |
| /// @end |
| /// @interface Y |
| // @end |
| /// @implementation Y |
| /// @end |
| /// \endcode |
| AST_MATCHER_P(ObjCImplementationDecl, hasInterface, |
| ast_matchers::internal::Matcher<ObjCInterfaceDecl>, Base) { |
| const ObjCInterfaceDecl *InterfaceDecl = Node.getClassInterface(); |
| return Base.matches(*InterfaceDecl, Finder, Builder); |
| } |
| |
| /// 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) { |
| Finder->addMatcher( |
| objcMessageExpr(hasSelector("self"), isMessagingSuperInstance(), |
| hasAncestor(objcMethodDecl( |
| isInitializer(), |
| hasDeclContext(objcImplementationDecl(hasInterface( |
| isDerivedFrom(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 |