//===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a CheckObjCInstMethSignature, a flow-insensitive check // that determines if an Objective-C class interface incorrectly redefines // the method signature in a subclass. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; 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(const ObjCMethodDecl *MethDerived, const ObjCMethodDecl *MethAncestor, BugReporter &BR, ASTContext &Ctx, const ObjCImplementationDecl *ID, const CheckerBase *Checker) { QualType ResDerived = MethDerived->getReturnType(); QualType ResAncestor = MethAncestor->getReturnType(); if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "The Objective-C class '" << *MethDerived->getClassInterface() << "', which is derived from class '" << *MethAncestor->getClassInterface() << "', defines the instance method '"; MethDerived->getSelector().print(os); os << "' whose return type is '" << ResDerived << "'. A method with the same name (same selector) is also defined in " "class '" << *MethAncestor->getClassInterface() << "' and has a return type of '" << ResAncestor << "'. These two types are incompatible, and may result in undefined " "behavior for clients of these classes."; PathDiagnosticLocation MethDLoc = PathDiagnosticLocation::createBegin(MethDerived, BR.getSourceManager()); BR.EmitBasicReport( MethDerived, Checker, "Incompatible instance method return type", categories::CoreFoundationObjectiveC, os.str(), MethDLoc); } } static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, BugReporter &BR, const CheckerBase *Checker) { const ObjCInterfaceDecl *D = ID->getClassInterface(); const 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 (auto *M : ID->instance_methods()) { IMeths[M->getSelector()] = M; ++NumMethods; } // Now recurse the class hierarchy chain looking for methods with the // same signatures. while (C && NumMethods) { for (const auto *M : C->instance_methods()) { Selector S = M->getSelector(); MapTy::iterator MI = IMeths.find(S); if (MI == IMeths.end() || MI->second == nullptr) continue; --NumMethods; ObjCMethodDecl *MethDerived = MI->second; MI->second = nullptr; CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); } C = C->getSuperClass(); } } //===----------------------------------------------------------------------===// // ObjCMethSigsChecker //===----------------------------------------------------------------------===// namespace { class ObjCMethSigsChecker : public Checker< check::ASTDecl > { public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { CheckObjCInstMethSignature(D, BR, this); } }; } void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { mgr.registerChecker(); } bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) { return true; }