CheckObjCInstMethSignature.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
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 27using namespace clang; 28using namespace ento; 29 30static 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 41static 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 BR.EmitBasicReport("Incompatible instance method return type", 70 os.str(), MethDerived->getLocStart()); 71 } 72} 73 74static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, 75 BugReporter& BR) { 76 77 const ObjCInterfaceDecl* D = ID->getClassInterface(); 78 const ObjCInterfaceDecl* C = D->getSuperClass(); 79 80 if (!C) 81 return; 82 83 ASTContext& Ctx = BR.getContext(); 84 85 // Build a DenseMap of the methods for quick querying. 86 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 87 MapTy IMeths; 88 unsigned NumMethods = 0; 89 90 for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 91 E=ID->instmeth_end(); I!=E; ++I) { 92 93 ObjCMethodDecl* M = *I; 94 IMeths[M->getSelector()] = M; 95 ++NumMethods; 96 } 97 98 // Now recurse the class hierarchy chain looking for methods with the 99 // same signatures. 100 while (C && NumMethods) { 101 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 102 E=C->instmeth_end(); I!=E; ++I) { 103 104 ObjCMethodDecl* M = *I; 105 Selector S = M->getSelector(); 106 107 MapTy::iterator MI = IMeths.find(S); 108 109 if (MI == IMeths.end() || MI->second == 0) 110 continue; 111 112 --NumMethods; 113 ObjCMethodDecl* MethDerived = MI->second; 114 MI->second = 0; 115 116 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 117 } 118 119 C = C->getSuperClass(); 120 } 121} 122 123//===----------------------------------------------------------------------===// 124// ObjCMethSigsChecker 125//===----------------------------------------------------------------------===// 126 127namespace { 128class ObjCMethSigsChecker : public Checker< 129 check::ASTDecl<ObjCImplementationDecl> > { 130public: 131 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 132 BugReporter &BR) const { 133 CheckObjCInstMethSignature(D, BR); 134 } 135}; 136} 137 138void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { 139 mgr.registerChecker<ObjCMethSigsChecker>(); 140} 141