CheckObjCInstMethSignature.cpp revision 0d8019e55c0f465bafc11b04aed691de95b9131d
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/Analysis/LocalCheckers.h" 17#include "clang/Analysis/PathDiagnostic.h" 18#include "clang/Analysis/PathSensitive/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 <sstream> 25 26using namespace clang; 27 28static 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->isPointerType() || Derived->isObjCQualifiedIdType()) && 34 (Ancestor->isPointerType() || Ancestor->isObjCQualifiedIdType())) 35 return true; 36 37 return C.typesAreCompatible(Derived, Ancestor); 38} 39 40static void CompareReturnTypes(ObjCMethodDecl* MethDerived, 41 ObjCMethodDecl* MethAncestor, 42 BugReporter& BR, ASTContext& Ctx, 43 ObjCImplementationDecl* ID) { 44 45 QualType ResDerived = MethDerived->getResultType(); 46 QualType ResAncestor = MethAncestor->getResultType(); 47 48 if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { 49 std::ostringstream os; 50 51 os << "The Objective-C class '" 52 << MethDerived->getClassInterface()->getName() 53 << "', which is derived from class '" 54 << MethAncestor->getClassInterface()->getName() 55 << "', defines the instance method '" 56 << MethDerived->getSelector().getName() 57 << "' whose return type is '" 58 << ResDerived.getAsString() 59 << "'. The same method (same selector) is defined in class 'B' and has " 60 "a return type of '" 61 << ResAncestor.getAsString() 62 << "'. These two types are incompatible, and may result in undefined " 63 "behavior for clients of these classes."; 64 65 // Refactor. 66 SimpleBugType BT("incompatible instance method return type"); 67 DiagCollector C(BT); 68 Diagnostic& Diag = BR.getDiagnostic(); 69 Diag.Report(&C, Ctx.getFullLoc(MethDerived->getLocStart()), 70 Diag.getCustomDiagID(Diagnostic::Warning, os.str().c_str()), 71 NULL, 0, NULL, 0); 72 73 for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I) 74 BR.EmitWarning(*I); 75 } 76} 77 78void clang::CheckObjCInstMethSignature(ObjCImplementationDecl* ID, 79 BugReporter& BR) { 80 81 ObjCInterfaceDecl* D = ID->getClassInterface(); 82 ObjCInterfaceDecl* C = D->getSuperClass(); 83 84 if (!C) 85 return; 86 87 // Build a DenseMap of the methods for quick querying. 88 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 89 MapTy IMeths; 90 unsigned NumMethods = 0; 91 92 for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 93 E=ID->instmeth_end(); I!=E; ++I) { 94 95 ObjCMethodDecl* M = *I; 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 ASTContext& Ctx = BR.getContext(); 103 104 while (C && NumMethods) { 105 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 106 E=C->instmeth_end(); I!=E; ++I) { 107 108 ObjCMethodDecl* M = *I; 109 Selector S = M->getSelector(); 110 111 MapTy::iterator MI = IMeths.find(S); 112 113 if (MI == IMeths.end() || MI->second == 0) 114 continue; 115 116 --NumMethods; 117 ObjCMethodDecl* MethDerived = MI->second; 118 MI->second = 0; 119 120 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 121 } 122 123 C = C->getSuperClass(); 124 } 125} 126