blob: 6a382fef044576d3f61bad755f0033ecda9c3b55 [file] [log] [blame]
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +00001//===--- SuperSelfCheck.cpp - clang-tidy ----------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "SuperSelfCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18namespace objc {
19
20namespace {
21
Dmitri Gribenko282dc722019-08-22 11:32:57 +000022/// Matches Objective-C methods in the initializer family.
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000023///
24/// Example matches -init and -initWithInt:.
25/// (matcher = objcMethodDecl(isInitializer()))
26/// \code
27/// @interface Foo
28/// - (instancetype)init;
29/// - (instancetype)initWithInt:(int)i;
30/// + (instancetype)init;
31/// - (void)bar;
32/// @end
33/// \endcode
34AST_MATCHER(ObjCMethodDecl, isInitializer) {
35 return Node.getMethodFamily() == OMF_init;
36}
37
Dmitri Gribenko282dc722019-08-22 11:32:57 +000038/// Matches Objective-C implementations with interfaces that match
Stephane Moorea53cce92019-08-16 02:27:58 +000039/// \c Base.
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000040///
Stephane Moorea53cce92019-08-16 02:27:58 +000041/// Example matches implementation declarations for X.
42/// (matcher = objcImplementationDecl(hasInterface(hasName("X"))))
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000043/// \code
44/// @interface X
45/// @end
Stephane Moorea53cce92019-08-16 02:27:58 +000046/// @implementation X
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000047/// @end
Stephane Moorea53cce92019-08-16 02:27:58 +000048/// @interface Y
49// @end
50/// @implementation Y
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000051/// @end
52/// \endcode
Stephane Moorea53cce92019-08-16 02:27:58 +000053AST_MATCHER_P(ObjCImplementationDecl, hasInterface,
54 ast_matchers::internal::Matcher<ObjCInterfaceDecl>, Base) {
55 const ObjCInterfaceDecl *InterfaceDecl = Node.getClassInterface();
56 return Base.matches(*InterfaceDecl, Finder, Builder);
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000057}
58
Dmitri Gribenko282dc722019-08-22 11:32:57 +000059/// Matches Objective-C message expressions where the receiver is the
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000060/// super instance.
61///
62/// Example matches the invocations of -banana and -orange.
63/// (matcher = objcMessageExpr(isMessagingSuperInstance()))
64/// \code
65/// - (void)banana {
66/// [self apple]
67/// [super banana];
68/// [super orange];
69/// }
70/// \endcode
71AST_MATCHER(ObjCMessageExpr, isMessagingSuperInstance) {
72 return Node.getReceiverKind() == ObjCMessageExpr::SuperInstance;
73}
74
75} // namespace
76
77void SuperSelfCheck::registerMatchers(MatchFinder *Finder) {
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000078 Finder->addMatcher(
Stephane Moorea53cce92019-08-16 02:27:58 +000079 objcMessageExpr(hasSelector("self"), isMessagingSuperInstance(),
80 hasAncestor(objcMethodDecl(
81 isInitializer(),
82 hasDeclContext(objcImplementationDecl(hasInterface(
83 isDerivedFrom(hasName("NSObject"))))))))
Stephane Mooreb0c1f8c2019-04-17 22:29:06 +000084 .bind("message"),
85 this);
86}
87
88void SuperSelfCheck::check(const MatchFinder::MatchResult &Result) {
89 const auto *Message = Result.Nodes.getNodeAs<ObjCMessageExpr>("message");
90
91 auto Diag = diag(Message->getExprLoc(), "suspicious invocation of %0 in "
92 "initializer; did you mean to "
93 "invoke a superclass initializer?")
94 << Message->getMethodDecl();
95
96 SourceLocation ReceiverLoc = Message->getReceiverRange().getBegin();
97 if (ReceiverLoc.isMacroID() || ReceiverLoc.isInvalid())
98 return;
99
100 SourceLocation SelectorLoc = Message->getSelectorStartLoc();
101 if (SelectorLoc.isMacroID() || SelectorLoc.isInvalid())
102 return;
103
104 Diag << FixItHint::CreateReplacement(Message->getSourceRange(),
105 StringRef("[super init]"));
106}
107
108} // namespace objc
109} // namespace tidy
110} // namespace clang