1 //===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a CheckObjCInstMethSignature, a flow-insenstive check 10 // that determines if an Objective-C class interface incorrectly redefines 11 // the method signature in a subclass. 12 // 13 //===----------------------------------------------------------------------===// 14 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" 24 25 using namespace clang; 26 using namespace ento; 27 28 static bool AreTypesCompatible(QualType Derived, QualType Ancestor, 29 ASTContext &C) { 30 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()) 34 return true; 35 36 return C.typesAreCompatible(Derived, Ancestor); 37 } 38 39 static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, 40 const ObjCMethodDecl *MethAncestor, 41 BugReporter &BR, ASTContext &Ctx, 42 const ObjCImplementationDecl *ID, 43 const CheckerBase *Checker) { 44 45 QualType ResDerived = MethDerived->getReturnType(); 46 QualType ResAncestor = MethAncestor->getReturnType(); 47 48 if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { 49 std::string sbuf; 50 llvm::raw_string_ostream os(sbuf); 51 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 '" 59 << ResDerived.getAsString() 60 << "'. A method with the same name (same selector) is also defined in " 61 "class '" 62 << *MethAncestor->getClassInterface() 63 << "' and has a return type of '" 64 << ResAncestor.getAsString() 65 << "'. These two types are incompatible, and may result in undefined " 66 "behavior for clients of these classes."; 67 68 PathDiagnosticLocation MethDLoc = 69 PathDiagnosticLocation::createBegin(MethDerived, 70 BR.getSourceManager()); 71 72 BR.EmitBasicReport( 73 MethDerived, Checker, "Incompatible instance method return type", 74 categories::CoreFoundationObjectiveC, os.str(), MethDLoc); 75 } 76 } 77 78 static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, 79 BugReporter &BR, 80 const CheckerBase *Checker) { 81 82 const ObjCInterfaceDecl *D = ID->getClassInterface(); 83 const ObjCInterfaceDecl *C = D->getSuperClass(); 84 85 if (!C) 86 return; 87 88 ASTContext &Ctx = BR.getContext(); 89 90 // Build a DenseMap of the methods for quick querying. 91 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 92 MapTy IMeths; 93 unsigned NumMethods = 0; 94 95 for (auto *M : ID->instance_methods()) { 96 IMeths[M->getSelector()] = M; 97 ++NumMethods; 98 } 99 100 // Now recurse the class hierarchy chain looking for methods with the 101 // same signatures. 102 while (C && NumMethods) { 103 for (const auto *M : C->instance_methods()) { 104 Selector S = M->getSelector(); 105 106 MapTy::iterator MI = IMeths.find(S); 107 108 if (MI == IMeths.end() || MI->second == nullptr) 109 continue; 110 111 --NumMethods; 112 ObjCMethodDecl *MethDerived = MI->second; 113 MI->second = nullptr; 114 115 CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); 116 } 117 118 C = C->getSuperClass(); 119 } 120 } 121 122 //===----------------------------------------------------------------------===// 123 // ObjCMethSigsChecker 124 //===----------------------------------------------------------------------===// 125 126 namespace { 127 class ObjCMethSigsChecker : public Checker< 128 check::ASTDecl<ObjCImplementationDecl> > { 129 public: 130 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 131 BugReporter &BR) const { 132 CheckObjCInstMethSignature(D, BR, this); 133 } 134 }; 135 } 136 137 void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { 138 mgr.registerChecker<ObjCMethSigsChecker>(); 139 } 140 141 bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) { 142 return true; 143 } 144