1 //===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a CheckObjCInstMethSignature, a flow-insensitive check
10 // that determines if an Objective-C class interface incorrectly redefines
11 // the method signature in a subclass.
13 //===----------------------------------------------------------------------===//
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/Analysis/PathDiagnostic.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/DeclObjC.h"
19 #include "clang/AST/Type.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21 #include "clang/StaticAnalyzer/Core/Checker.h"
22 #include "llvm/ADT/DenseMap.h"
23 #include "llvm/Support/raw_ostream.h"
25 using namespace clang
;
28 static bool AreTypesCompatible(QualType Derived
, QualType Ancestor
,
31 // Right now don't compare the compatibility of pointers. That involves
32 // looking at subtyping relationships. FIXME: Future patch.
33 if (Derived
->isAnyPointerType() && Ancestor
->isAnyPointerType())
36 return C
.typesAreCompatible(Derived
, Ancestor
);
39 static void CompareReturnTypes(const ObjCMethodDecl
*MethDerived
,
40 const ObjCMethodDecl
*MethAncestor
,
41 BugReporter
&BR
, ASTContext
&Ctx
,
42 const ObjCImplementationDecl
*ID
,
43 const CheckerBase
*Checker
) {
45 QualType ResDerived
= MethDerived
->getReturnType();
46 QualType ResAncestor
= MethAncestor
->getReturnType();
48 if (!AreTypesCompatible(ResDerived
, ResAncestor
, Ctx
)) {
50 llvm::raw_string_ostream
os(sbuf
);
52 os
<< "The Objective-C class '"
53 << *MethDerived
->getClassInterface()
54 << "', which is derived from class '"
55 << *MethAncestor
->getClassInterface()
56 << "', defines the instance method '";
57 MethDerived
->getSelector().print(os
);
58 os
<< "' whose return type is '" << ResDerived
59 << "'. A method with the same name (same selector) is also defined in "
61 << *MethAncestor
->getClassInterface() << "' and has a return type of '"
63 << "'. These two types are incompatible, and may result in undefined "
64 "behavior for clients of these classes.";
66 PathDiagnosticLocation MethDLoc
=
67 PathDiagnosticLocation::createBegin(MethDerived
,
68 BR
.getSourceManager());
71 MethDerived
, Checker
, "Incompatible instance method return type",
72 categories::CoreFoundationObjectiveC
, os
.str(), MethDLoc
);
76 static void CheckObjCInstMethSignature(const ObjCImplementationDecl
*ID
,
78 const CheckerBase
*Checker
) {
80 const ObjCInterfaceDecl
*D
= ID
->getClassInterface();
81 const ObjCInterfaceDecl
*C
= D
->getSuperClass();
86 ASTContext
&Ctx
= BR
.getContext();
88 // Build a DenseMap of the methods for quick querying.
89 typedef llvm::DenseMap
<Selector
,ObjCMethodDecl
*> MapTy
;
91 unsigned NumMethods
= 0;
93 for (auto *M
: ID
->instance_methods()) {
94 IMeths
[M
->getSelector()] = M
;
98 // Now recurse the class hierarchy chain looking for methods with the
100 while (C
&& NumMethods
) {
101 for (const auto *M
: C
->instance_methods()) {
102 Selector S
= M
->getSelector();
104 MapTy::iterator MI
= IMeths
.find(S
);
106 if (MI
== IMeths
.end() || MI
->second
== nullptr)
110 ObjCMethodDecl
*MethDerived
= MI
->second
;
111 MI
->second
= nullptr;
113 CompareReturnTypes(MethDerived
, M
, BR
, Ctx
, ID
, Checker
);
116 C
= C
->getSuperClass();
120 //===----------------------------------------------------------------------===//
121 // ObjCMethSigsChecker
122 //===----------------------------------------------------------------------===//
125 class ObjCMethSigsChecker
: public Checker
<
126 check::ASTDecl
<ObjCImplementationDecl
> > {
128 void checkASTDecl(const ObjCImplementationDecl
*D
, AnalysisManager
& mgr
,
129 BugReporter
&BR
) const {
130 CheckObjCInstMethSignature(D
, BR
, this);
135 void ento::registerObjCMethSigsChecker(CheckerManager
&mgr
) {
136 mgr
.registerChecker
<ObjCMethSigsChecker
>();
139 bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager
&mgr
) {