1b0c1f8c0SStephane Moore //===--- SuperSelfCheck.cpp - clang-tidy ----------------------------------===//
2b0c1f8c0SStephane Moore //
3c874dd53SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c874dd53SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information.
5c874dd53SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b0c1f8c0SStephane Moore //
7b0c1f8c0SStephane Moore //===----------------------------------------------------------------------===//
8b0c1f8c0SStephane Moore
9b0c1f8c0SStephane Moore #include "SuperSelfCheck.h"
10b0c1f8c0SStephane Moore #include "clang/AST/ASTContext.h"
11b0c1f8c0SStephane Moore #include "clang/ASTMatchers/ASTMatchFinder.h"
12b0c1f8c0SStephane Moore
13b0c1f8c0SStephane Moore using namespace clang::ast_matchers;
14b0c1f8c0SStephane Moore
157d2ea6c4SCarlos Galvez namespace clang::tidy::objc {
16b0c1f8c0SStephane Moore
17b0c1f8c0SStephane Moore namespace {
18b0c1f8c0SStephane Moore
19282dc72cSDmitri Gribenko /// Matches Objective-C methods in the initializer family.
20b0c1f8c0SStephane Moore ///
21b0c1f8c0SStephane Moore /// Example matches -init and -initWithInt:.
22b0c1f8c0SStephane Moore /// (matcher = objcMethodDecl(isInitializer()))
23b0c1f8c0SStephane Moore /// \code
24b0c1f8c0SStephane Moore /// @interface Foo
25b0c1f8c0SStephane Moore /// - (instancetype)init;
26b0c1f8c0SStephane Moore /// - (instancetype)initWithInt:(int)i;
27b0c1f8c0SStephane Moore /// + (instancetype)init;
28b0c1f8c0SStephane Moore /// - (void)bar;
29b0c1f8c0SStephane Moore /// @end
30b0c1f8c0SStephane Moore /// \endcode
AST_MATCHER(ObjCMethodDecl,isInitializer)31b0c1f8c0SStephane Moore AST_MATCHER(ObjCMethodDecl, isInitializer) {
32b0c1f8c0SStephane Moore return Node.getMethodFamily() == OMF_init;
33b0c1f8c0SStephane Moore }
34b0c1f8c0SStephane Moore
35*11a411a4SPiotr Zegar /// Matches Objective-C implementations with interfaces that match
36*11a411a4SPiotr Zegar /// \c Base.
37*11a411a4SPiotr Zegar ///
38*11a411a4SPiotr Zegar /// Example matches implementation declarations for X.
39*11a411a4SPiotr Zegar /// (matcher = objcImplementationDecl(hasInterface(hasName("X"))))
40*11a411a4SPiotr Zegar /// \code
41*11a411a4SPiotr Zegar /// @interface X
42*11a411a4SPiotr Zegar /// @end
43*11a411a4SPiotr Zegar /// @implementation X
44*11a411a4SPiotr Zegar /// @end
45*11a411a4SPiotr Zegar /// @interface Y
46*11a411a4SPiotr Zegar // @end
47*11a411a4SPiotr Zegar /// @implementation Y
48*11a411a4SPiotr Zegar /// @end
49*11a411a4SPiotr Zegar /// \endcode
AST_MATCHER_P(ObjCImplementationDecl,hasInterface,ast_matchers::internal::Matcher<ObjCInterfaceDecl>,Base)50*11a411a4SPiotr Zegar AST_MATCHER_P(ObjCImplementationDecl, hasInterface,
51*11a411a4SPiotr Zegar ast_matchers::internal::Matcher<ObjCInterfaceDecl>, Base) {
52*11a411a4SPiotr Zegar const ObjCInterfaceDecl *InterfaceDecl = Node.getClassInterface();
53*11a411a4SPiotr Zegar return Base.matches(*InterfaceDecl, Finder, Builder);
54*11a411a4SPiotr Zegar }
55*11a411a4SPiotr Zegar
56282dc72cSDmitri Gribenko /// Matches Objective-C message expressions where the receiver is the
57b0c1f8c0SStephane Moore /// super instance.
58b0c1f8c0SStephane Moore ///
59b0c1f8c0SStephane Moore /// Example matches the invocations of -banana and -orange.
60b0c1f8c0SStephane Moore /// (matcher = objcMessageExpr(isMessagingSuperInstance()))
61b0c1f8c0SStephane Moore /// \code
62b0c1f8c0SStephane Moore /// - (void)banana {
63b0c1f8c0SStephane Moore /// [self apple]
64b0c1f8c0SStephane Moore /// [super banana];
65b0c1f8c0SStephane Moore /// [super orange];
66b0c1f8c0SStephane Moore /// }
67b0c1f8c0SStephane Moore /// \endcode
AST_MATCHER(ObjCMessageExpr,isMessagingSuperInstance)68b0c1f8c0SStephane Moore AST_MATCHER(ObjCMessageExpr, isMessagingSuperInstance) {
69b0c1f8c0SStephane Moore return Node.getReceiverKind() == ObjCMessageExpr::SuperInstance;
70b0c1f8c0SStephane Moore }
71b0c1f8c0SStephane Moore
72b0c1f8c0SStephane Moore } // namespace
73b0c1f8c0SStephane Moore
registerMatchers(MatchFinder * Finder)74b0c1f8c0SStephane Moore void SuperSelfCheck::registerMatchers(MatchFinder *Finder) {
75b0c1f8c0SStephane Moore Finder->addMatcher(
76a53cce94SStephane Moore objcMessageExpr(hasSelector("self"), isMessagingSuperInstance(),
77a53cce94SStephane Moore hasAncestor(objcMethodDecl(
78a53cce94SStephane Moore isInitializer(),
79a53cce94SStephane Moore hasDeclContext(objcImplementationDecl(hasInterface(
80a53cce94SStephane Moore isDerivedFrom(hasName("NSObject"))))))))
81b0c1f8c0SStephane Moore .bind("message"),
82b0c1f8c0SStephane Moore this);
83b0c1f8c0SStephane Moore }
84b0c1f8c0SStephane Moore
check(const MatchFinder::MatchResult & Result)85b0c1f8c0SStephane Moore void SuperSelfCheck::check(const MatchFinder::MatchResult &Result) {
86b0c1f8c0SStephane Moore const auto *Message = Result.Nodes.getNodeAs<ObjCMessageExpr>("message");
87b0c1f8c0SStephane Moore
88b0c1f8c0SStephane Moore auto Diag = diag(Message->getExprLoc(), "suspicious invocation of %0 in "
89b0c1f8c0SStephane Moore "initializer; did you mean to "
90b0c1f8c0SStephane Moore "invoke a superclass initializer?")
91b0c1f8c0SStephane Moore << Message->getMethodDecl();
92b0c1f8c0SStephane Moore
93b0c1f8c0SStephane Moore SourceLocation ReceiverLoc = Message->getReceiverRange().getBegin();
94b0c1f8c0SStephane Moore if (ReceiverLoc.isMacroID() || ReceiverLoc.isInvalid())
95b0c1f8c0SStephane Moore return;
96b0c1f8c0SStephane Moore
97b0c1f8c0SStephane Moore SourceLocation SelectorLoc = Message->getSelectorStartLoc();
98b0c1f8c0SStephane Moore if (SelectorLoc.isMacroID() || SelectorLoc.isInvalid())
99b0c1f8c0SStephane Moore return;
100b0c1f8c0SStephane Moore
101b0c1f8c0SStephane Moore Diag << FixItHint::CreateReplacement(Message->getSourceRange(),
102b0c1f8c0SStephane Moore StringRef("[super init]"));
103b0c1f8c0SStephane Moore }
104b0c1f8c0SStephane Moore
1057d2ea6c4SCarlos Galvez } // namespace clang::tidy::objc
106