xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //  This file defines a CheckObjCInstMethSignature, a flow-insenstive check
10*0b57cec5SDimitry Andric //  that determines if an Objective-C class interface incorrectly redefines
11*0b57cec5SDimitry Andric //  the method signature in a subclass.
12*0b57cec5SDimitry Andric //
13*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
14*0b57cec5SDimitry Andric 
15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
17*0b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
18*0b57cec5SDimitry Andric #include "clang/AST/Type.h"
19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
21*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
22*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
23*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
24*0b57cec5SDimitry Andric 
25*0b57cec5SDimitry Andric using namespace clang;
26*0b57cec5SDimitry Andric using namespace ento;
27*0b57cec5SDimitry Andric 
28*0b57cec5SDimitry Andric static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
29*0b57cec5SDimitry Andric                                ASTContext &C) {
30*0b57cec5SDimitry Andric 
31*0b57cec5SDimitry Andric   // Right now don't compare the compatibility of pointers.  That involves
32*0b57cec5SDimitry Andric   // looking at subtyping relationships.  FIXME: Future patch.
33*0b57cec5SDimitry Andric   if (Derived->isAnyPointerType() &&  Ancestor->isAnyPointerType())
34*0b57cec5SDimitry Andric     return true;
35*0b57cec5SDimitry Andric 
36*0b57cec5SDimitry Andric   return C.typesAreCompatible(Derived, Ancestor);
37*0b57cec5SDimitry Andric }
38*0b57cec5SDimitry Andric 
39*0b57cec5SDimitry Andric static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
40*0b57cec5SDimitry Andric                                const ObjCMethodDecl *MethAncestor,
41*0b57cec5SDimitry Andric                                BugReporter &BR, ASTContext &Ctx,
42*0b57cec5SDimitry Andric                                const ObjCImplementationDecl *ID,
43*0b57cec5SDimitry Andric                                const CheckerBase *Checker) {
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric   QualType ResDerived = MethDerived->getReturnType();
46*0b57cec5SDimitry Andric   QualType ResAncestor = MethAncestor->getReturnType();
47*0b57cec5SDimitry Andric 
48*0b57cec5SDimitry Andric   if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
49*0b57cec5SDimitry Andric     std::string sbuf;
50*0b57cec5SDimitry Andric     llvm::raw_string_ostream os(sbuf);
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric     os << "The Objective-C class '"
53*0b57cec5SDimitry Andric        << *MethDerived->getClassInterface()
54*0b57cec5SDimitry Andric        << "', which is derived from class '"
55*0b57cec5SDimitry Andric        << *MethAncestor->getClassInterface()
56*0b57cec5SDimitry Andric        << "', defines the instance method '";
57*0b57cec5SDimitry Andric     MethDerived->getSelector().print(os);
58*0b57cec5SDimitry Andric     os << "' whose return type is '"
59*0b57cec5SDimitry Andric        << ResDerived.getAsString()
60*0b57cec5SDimitry Andric        << "'.  A method with the same name (same selector) is also defined in "
61*0b57cec5SDimitry Andric           "class '"
62*0b57cec5SDimitry Andric        << *MethAncestor->getClassInterface()
63*0b57cec5SDimitry Andric        << "' and has a return type of '"
64*0b57cec5SDimitry Andric        << ResAncestor.getAsString()
65*0b57cec5SDimitry Andric        << "'.  These two types are incompatible, and may result in undefined "
66*0b57cec5SDimitry Andric           "behavior for clients of these classes.";
67*0b57cec5SDimitry Andric 
68*0b57cec5SDimitry Andric     PathDiagnosticLocation MethDLoc =
69*0b57cec5SDimitry Andric       PathDiagnosticLocation::createBegin(MethDerived,
70*0b57cec5SDimitry Andric                                           BR.getSourceManager());
71*0b57cec5SDimitry Andric 
72*0b57cec5SDimitry Andric     BR.EmitBasicReport(
73*0b57cec5SDimitry Andric         MethDerived, Checker, "Incompatible instance method return type",
74*0b57cec5SDimitry Andric         categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
75*0b57cec5SDimitry Andric   }
76*0b57cec5SDimitry Andric }
77*0b57cec5SDimitry Andric 
78*0b57cec5SDimitry Andric static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
79*0b57cec5SDimitry Andric                                        BugReporter &BR,
80*0b57cec5SDimitry Andric                                        const CheckerBase *Checker) {
81*0b57cec5SDimitry Andric 
82*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *D = ID->getClassInterface();
83*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *C = D->getSuperClass();
84*0b57cec5SDimitry Andric 
85*0b57cec5SDimitry Andric   if (!C)
86*0b57cec5SDimitry Andric     return;
87*0b57cec5SDimitry Andric 
88*0b57cec5SDimitry Andric   ASTContext &Ctx = BR.getContext();
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric   // Build a DenseMap of the methods for quick querying.
91*0b57cec5SDimitry Andric   typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
92*0b57cec5SDimitry Andric   MapTy IMeths;
93*0b57cec5SDimitry Andric   unsigned NumMethods = 0;
94*0b57cec5SDimitry Andric 
95*0b57cec5SDimitry Andric   for (auto *M : ID->instance_methods()) {
96*0b57cec5SDimitry Andric     IMeths[M->getSelector()] = M;
97*0b57cec5SDimitry Andric     ++NumMethods;
98*0b57cec5SDimitry Andric   }
99*0b57cec5SDimitry Andric 
100*0b57cec5SDimitry Andric   // Now recurse the class hierarchy chain looking for methods with the
101*0b57cec5SDimitry Andric   // same signatures.
102*0b57cec5SDimitry Andric   while (C && NumMethods) {
103*0b57cec5SDimitry Andric     for (const auto *M : C->instance_methods()) {
104*0b57cec5SDimitry Andric       Selector S = M->getSelector();
105*0b57cec5SDimitry Andric 
106*0b57cec5SDimitry Andric       MapTy::iterator MI = IMeths.find(S);
107*0b57cec5SDimitry Andric 
108*0b57cec5SDimitry Andric       if (MI == IMeths.end() || MI->second == nullptr)
109*0b57cec5SDimitry Andric         continue;
110*0b57cec5SDimitry Andric 
111*0b57cec5SDimitry Andric       --NumMethods;
112*0b57cec5SDimitry Andric       ObjCMethodDecl *MethDerived = MI->second;
113*0b57cec5SDimitry Andric       MI->second = nullptr;
114*0b57cec5SDimitry Andric 
115*0b57cec5SDimitry Andric       CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
116*0b57cec5SDimitry Andric     }
117*0b57cec5SDimitry Andric 
118*0b57cec5SDimitry Andric     C = C->getSuperClass();
119*0b57cec5SDimitry Andric   }
120*0b57cec5SDimitry Andric }
121*0b57cec5SDimitry Andric 
122*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
123*0b57cec5SDimitry Andric // ObjCMethSigsChecker
124*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
125*0b57cec5SDimitry Andric 
126*0b57cec5SDimitry Andric namespace {
127*0b57cec5SDimitry Andric class ObjCMethSigsChecker : public Checker<
128*0b57cec5SDimitry Andric                                       check::ASTDecl<ObjCImplementationDecl> > {
129*0b57cec5SDimitry Andric public:
130*0b57cec5SDimitry Andric   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
131*0b57cec5SDimitry Andric                     BugReporter &BR) const {
132*0b57cec5SDimitry Andric     CheckObjCInstMethSignature(D, BR, this);
133*0b57cec5SDimitry Andric   }
134*0b57cec5SDimitry Andric };
135*0b57cec5SDimitry Andric }
136*0b57cec5SDimitry Andric 
137*0b57cec5SDimitry Andric void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
138*0b57cec5SDimitry Andric   mgr.registerChecker<ObjCMethSigsChecker>();
139*0b57cec5SDimitry Andric }
140*0b57cec5SDimitry Andric 
141*0b57cec5SDimitry Andric bool ento::shouldRegisterObjCMethSigsChecker(const LangOptions &LO) {
142*0b57cec5SDimitry Andric   return true;
143*0b57cec5SDimitry Andric }
144