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/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20 #include "clang/AST/DeclObjC.h" 21 #include "clang/AST/Type.h" 22 #include "clang/AST/ASTContext.h" 23 24 #include "llvm/ADT/DenseMap.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace clang; 28 using namespace ento; 29 30 static bool AreTypesCompatible(QualType Derived, QualType Ancestor, 31 ASTContext &C) { 32 33 // Right now don't compare the compatibility of pointers. That involves 34 // looking at subtyping relationships. FIXME: Future patch. 35 if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) 36 return true; 37 38 return C.typesAreCompatible(Derived, Ancestor); 39 } 40 41 static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, 42 const ObjCMethodDecl *MethAncestor, 43 BugReporter &BR, ASTContext &Ctx, 44 const ObjCImplementationDecl *ID) { 45 46 QualType ResDerived = MethDerived->getResultType(); 47 QualType ResAncestor = MethAncestor->getResultType(); 48 49 if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { 50 std::string sbuf; 51 llvm::raw_string_ostream os(sbuf); 52 53 os << "The Objective-C class '" 54 << *MethDerived->getClassInterface() 55 << "', which is derived from class '" 56 << *MethAncestor->getClassInterface() 57 << "', defines the instance method '" 58 << MethDerived->getSelector().getAsString() 59 << "' whose return type is '" 60 << ResDerived.getAsString() 61 << "'. A method with the same name (same selector) is also defined in " 62 "class '" 63 << *MethAncestor->getClassInterface() 64 << "' and has a return type of '" 65 << ResAncestor.getAsString() 66 << "'. These two types are incompatible, and may result in undefined " 67 "behavior for clients of these classes."; 68 69 PathDiagnosticLocation MethDLoc = 70 PathDiagnosticLocation::createBegin(MethDerived, 71 BR.getSourceManager()); 72 73 BR.EmitBasicReport(MethDerived, 74 "Incompatible instance method return type", 75 categories::CoreFoundationObjectiveC, 76 os.str(), MethDLoc); 77 } 78 } 79 80 static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, 81 BugReporter& BR) { 82 83 const ObjCInterfaceDecl *D = ID->getClassInterface(); 84 const ObjCInterfaceDecl *C = D->getSuperClass(); 85 86 if (!C) 87 return; 88 89 ASTContext &Ctx = BR.getContext(); 90 91 // Build a DenseMap of the methods for quick querying. 92 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 93 MapTy IMeths; 94 unsigned NumMethods = 0; 95 96 for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 97 E=ID->instmeth_end(); I!=E; ++I) { 98 99 ObjCMethodDecl *M = *I; 100 IMeths[M->getSelector()] = M; 101 ++NumMethods; 102 } 103 104 // Now recurse the class hierarchy chain looking for methods with the 105 // same signatures. 106 while (C && NumMethods) { 107 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 108 E=C->instmeth_end(); I!=E; ++I) { 109 110 ObjCMethodDecl *M = *I; 111 Selector S = M->getSelector(); 112 113 MapTy::iterator MI = IMeths.find(S); 114 115 if (MI == IMeths.end() || MI->second == 0) 116 continue; 117 118 --NumMethods; 119 ObjCMethodDecl *MethDerived = MI->second; 120 MI->second = 0; 121 122 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 123 } 124 125 C = C->getSuperClass(); 126 } 127 } 128 129 //===----------------------------------------------------------------------===// 130 // ObjCMethSigsChecker 131 //===----------------------------------------------------------------------===// 132 133 namespace { 134 class ObjCMethSigsChecker : public Checker< 135 check::ASTDecl<ObjCImplementationDecl> > { 136 public: 137 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 138 BugReporter &BR) const { 139 CheckObjCInstMethSignature(D, BR); 140 } 141 }; 142 } 143 144 void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { 145 mgr.registerChecker<ObjCMethSigsChecker>(); 146 } 147