CheckObjCInstMethSignature.cpp revision 9b663716449b618ba0390b1dbebc54fa8e971124
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 "clang/StaticAnalyzer/Checkers/LocalCheckers.h" 17#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19#include "clang/AST/DeclObjC.h" 20#include "clang/AST/Type.h" 21#include "clang/AST/ASTContext.h" 22 23#include "llvm/ADT/DenseMap.h" 24#include "llvm/Support/raw_ostream.h" 25 26using namespace clang; 27using namespace ento; 28 29static 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 40static 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 BR.EmitBasicReport("Incompatible instance method return type", 69 os.str(), MethDerived->getLocStart()); 70 } 71} 72 73void ento::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, 74 BugReporter& BR) { 75 76 const ObjCInterfaceDecl* D = ID->getClassInterface(); 77 const ObjCInterfaceDecl* C = D->getSuperClass(); 78 79 if (!C) 80 return; 81 82 ASTContext& Ctx = BR.getContext(); 83 84 // Build a DenseMap of the methods for quick querying. 85 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 86 MapTy IMeths; 87 unsigned NumMethods = 0; 88 89 for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 90 E=ID->instmeth_end(); I!=E; ++I) { 91 92 ObjCMethodDecl* M = *I; 93 IMeths[M->getSelector()] = M; 94 ++NumMethods; 95 } 96 97 // Now recurse the class hierarchy chain looking for methods with the 98 // same signatures. 99 while (C && NumMethods) { 100 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 101 E=C->instmeth_end(); I!=E; ++I) { 102 103 ObjCMethodDecl* M = *I; 104 Selector S = M->getSelector(); 105 106 MapTy::iterator MI = IMeths.find(S); 107 108 if (MI == IMeths.end() || MI->second == 0) 109 continue; 110 111 --NumMethods; 112 ObjCMethodDecl* MethDerived = MI->second; 113 MI->second = 0; 114 115 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 116 } 117 118 C = C->getSuperClass(); 119 } 120} 121