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