//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines a CheckObjCInstMethSignature, a flow-insenstive check // that determines if an Objective-C class interface incorrectly redefines // the method signature in a subclass. // //===----------------------------------------------------------------------===// #include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/AST/ASTContext.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" using namespace clang; static bool AreTypesCompatible(QualType Derived, QualType Ancestor, ASTContext& C) { // Right now don't compare the compatibility of pointers. That involves // looking at subtyping relationships. FIXME: Future patch. if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) return true; return C.typesAreCompatible(Derived, Ancestor); } static void CompareReturnTypes(ObjCMethodDecl* MethDerived, ObjCMethodDecl* MethAncestor, BugReporter& BR, ASTContext& Ctx, ObjCImplementationDecl* ID) { QualType ResDerived = MethDerived->getResultType(); QualType ResAncestor = MethAncestor->getResultType(); if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "The Objective-C class '" << MethDerived->getClassInterface()->getNameAsString() << "', which is derived from class '" << MethAncestor->getClassInterface()->getNameAsString() << "', defines the instance method '" << MethDerived->getSelector().getAsString() << "' whose return type is '" << ResDerived.getAsString() << "'. A method with the same name (same selector) is also defined in " "class '" << MethAncestor->getClassInterface()->getNameAsString() << "' and has a return type of '" << ResAncestor.getAsString() << "'. These two types are incompatible, and may result in undefined " "behavior for clients of these classes."; BR.EmitBasicReport("Incompatible instance method return type", os.str().c_str(), MethDerived->getLocStart()); } } void clang::CheckObjCInstMethSignature(ObjCImplementationDecl* ID, BugReporter& BR) { ObjCInterfaceDecl* D = ID->getClassInterface(); ObjCInterfaceDecl* C = D->getSuperClass(); if (!C) return; ASTContext& Ctx = BR.getContext(); // Build a DenseMap of the methods for quick querying. typedef llvm::DenseMap MapTy; MapTy IMeths; unsigned NumMethods = 0; for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), E=ID->instmeth_end(); I!=E; ++I) { ObjCMethodDecl* M = *I; IMeths[M->getSelector()] = M; ++NumMethods; } // Now recurse the class hierarchy chain looking for methods with the // same signatures. while (C && NumMethods) { for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), E=C->instmeth_end(); I!=E; ++I) { ObjCMethodDecl* M = *I; Selector S = M->getSelector(); MapTy::iterator MI = IMeths.find(S); if (MI == IMeths.end() || MI->second == 0) continue; --NumMethods; ObjCMethodDecl* MethDerived = MI->second; MI->second = 0; CompareReturnTypes(MethDerived, M, BR, Ctx, ID); } C = C->getSuperClass(); } }