clang API Documentation
00001 //=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a CheckObjCInstMethSignature, a flow-insenstive check 00011 // that determines if an Objective-C class interface incorrectly redefines 00012 // the method signature in a subclass. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "ClangSACheckers.h" 00017 #include "clang/StaticAnalyzer/Core/Checker.h" 00018 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 00019 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00020 #include "clang/AST/DeclObjC.h" 00021 #include "clang/AST/Type.h" 00022 #include "clang/AST/ASTContext.h" 00023 00024 #include "llvm/ADT/DenseMap.h" 00025 #include "llvm/Support/raw_ostream.h" 00026 00027 using namespace clang; 00028 using namespace ento; 00029 00030 static bool AreTypesCompatible(QualType Derived, QualType Ancestor, 00031 ASTContext &C) { 00032 00033 // Right now don't compare the compatibility of pointers. That involves 00034 // looking at subtyping relationships. FIXME: Future patch. 00035 if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) 00036 return true; 00037 00038 return C.typesAreCompatible(Derived, Ancestor); 00039 } 00040 00041 static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, 00042 const ObjCMethodDecl *MethAncestor, 00043 BugReporter &BR, ASTContext &Ctx, 00044 const ObjCImplementationDecl *ID) { 00045 00046 QualType ResDerived = MethDerived->getResultType(); 00047 QualType ResAncestor = MethAncestor->getResultType(); 00048 00049 if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { 00050 std::string sbuf; 00051 llvm::raw_string_ostream os(sbuf); 00052 00053 os << "The Objective-C class '" 00054 << *MethDerived->getClassInterface() 00055 << "', which is derived from class '" 00056 << *MethAncestor->getClassInterface() 00057 << "', defines the instance method '" 00058 << MethDerived->getSelector().getAsString() 00059 << "' whose return type is '" 00060 << ResDerived.getAsString() 00061 << "'. A method with the same name (same selector) is also defined in " 00062 "class '" 00063 << *MethAncestor->getClassInterface() 00064 << "' and has a return type of '" 00065 << ResAncestor.getAsString() 00066 << "'. These two types are incompatible, and may result in undefined " 00067 "behavior for clients of these classes."; 00068 00069 PathDiagnosticLocation MethDLoc = 00070 PathDiagnosticLocation::createBegin(MethDerived, 00071 BR.getSourceManager()); 00072 00073 BR.EmitBasicReport(MethDerived, 00074 "Incompatible instance method return type", 00075 categories::CoreFoundationObjectiveC, 00076 os.str(), MethDLoc); 00077 } 00078 } 00079 00080 static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, 00081 BugReporter& BR) { 00082 00083 const ObjCInterfaceDecl *D = ID->getClassInterface(); 00084 const ObjCInterfaceDecl *C = D->getSuperClass(); 00085 00086 if (!C) 00087 return; 00088 00089 ASTContext &Ctx = BR.getContext(); 00090 00091 // Build a DenseMap of the methods for quick querying. 00092 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; 00093 MapTy IMeths; 00094 unsigned NumMethods = 0; 00095 00096 for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), 00097 E=ID->instmeth_end(); I!=E; ++I) { 00098 00099 ObjCMethodDecl *M = *I; 00100 IMeths[M->getSelector()] = M; 00101 ++NumMethods; 00102 } 00103 00104 // Now recurse the class hierarchy chain looking for methods with the 00105 // same signatures. 00106 while (C && NumMethods) { 00107 for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), 00108 E=C->instmeth_end(); I!=E; ++I) { 00109 00110 ObjCMethodDecl *M = *I; 00111 Selector S = M->getSelector(); 00112 00113 MapTy::iterator MI = IMeths.find(S); 00114 00115 if (MI == IMeths.end() || MI->second == 0) 00116 continue; 00117 00118 --NumMethods; 00119 ObjCMethodDecl *MethDerived = MI->second; 00120 MI->second = 0; 00121 00122 CompareReturnTypes(MethDerived, M, BR, Ctx, ID); 00123 } 00124 00125 C = C->getSuperClass(); 00126 } 00127 } 00128 00129 //===----------------------------------------------------------------------===// 00130 // ObjCMethSigsChecker 00131 //===----------------------------------------------------------------------===// 00132 00133 namespace { 00134 class ObjCMethSigsChecker : public Checker< 00135 check::ASTDecl<ObjCImplementationDecl> > { 00136 public: 00137 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 00138 BugReporter &BR) const { 00139 CheckObjCInstMethSignature(D, BR); 00140 } 00141 }; 00142 } 00143 00144 void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { 00145 mgr.registerChecker<ObjCMethSigsChecker>(); 00146 }