1 //=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines a CheckObjCInstMethSignature, a flow-insenstive check 11 // that determines if an Objective-C class interface incorrectly redefines 12 // the method signature in a subclass. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.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/BugReporter/PathDiagnostic.h" 22 #include "clang/StaticAnalyzer/Core/Checker.h" 23 #include "llvm/ADT/DenseMap.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 using namespace clang; 27 using namespace ento; 28 29 static bool AreTypesCompatible(QualType Derived, QualType Ancestor, 30 ASTContext &C) { 31 32 // Right now don't compare the compatibility of pointers. That involves 33 // looking at subtyping relationships. FIXME: Future patch. 34 if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) 35 return true; 36 37 return C.typesAreCompatible(Derived, Ancestor); 38 } 39 40 static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, 41 const ObjCMethodDecl *MethAncestor, 42 BugReporter &BR, ASTContext &Ctx, 43 const ObjCImplementationDecl *ID) { 44 45 QualType ResDerived = MethDerived->getResultType(); 46 QualType ResAncestor = MethAncestor->getResultType(); 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().getAsString() 58 << "' 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(MethDerived, 73 "Incompatible instance method return type", 74 categories::CoreFoundationObjectiveC, 75 os.str(), MethDLoc); 76 } 77 } 78 79 static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, 80 BugReporter& BR) { 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 (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 96 E=ID->instmeth_end(); I!=E; ++I) { 97 98 ObjCMethodDecl *M = *I; 99 IMeths[M->getSelector()] = M; 100 ++NumMethods; 101 } 102 103 // Now recurse the class hierarchy chain looking for methods with the 104 // same signatures. 105 while (C && NumMethods) { 106 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 107 E=C->instmeth_end(); I!=E; ++I) { 108 109 ObjCMethodDecl *M = *I; 110 Selector S = M->getSelector(); 111 112 MapTy::iterator MI = IMeths.find(S); 113 114 if (MI == IMeths.end() || MI->second == 0) 115 continue; 116 117 --NumMethods; 118 ObjCMethodDecl *MethDerived = MI->second; 119 MI->second = 0; 120 121 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 122 } 123 124 C = C->getSuperClass(); 125 } 126 } 127 128 //===----------------------------------------------------------------------===// 129 // ObjCMethSigsChecker 130 //===----------------------------------------------------------------------===// 131 132 namespace { 133 class ObjCMethSigsChecker : public Checker< 134 check::ASTDecl<ObjCImplementationDecl> > { 135 public: 136 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 137 BugReporter &BR) const { 138 CheckObjCInstMethSignature(D, BR); 139 } 140 }; 141 } 142 143 void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { 144 mgr.registerChecker<ObjCMethSigsChecker>(); 145 } 146