142 lines · cpp
1//===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines a CheckObjCInstMethSignature, a flow-insensitive check10// that determines if an Objective-C class interface incorrectly redefines11// the method signature in a subclass.12//13//===----------------------------------------------------------------------===//14 15#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"16#include "clang/Analysis/PathDiagnostic.h"17#include "clang/AST/ASTContext.h"18#include "clang/AST/DeclObjC.h"19#include "clang/AST/Type.h"20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"21#include "clang/StaticAnalyzer/Core/Checker.h"22#include "llvm/ADT/DenseMap.h"23#include "llvm/Support/raw_ostream.h"24 25using namespace clang;26using namespace ento;27 28static bool AreTypesCompatible(QualType Derived, QualType Ancestor,29 ASTContext &C) {30 31 // Right now don't compare the compatibility of pointers. That involves32 // looking at subtyping relationships. FIXME: Future patch.33 if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())34 return true;35 36 return C.typesAreCompatible(Derived, Ancestor);37}38 39static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,40 const ObjCMethodDecl *MethAncestor,41 BugReporter &BR, ASTContext &Ctx,42 const ObjCImplementationDecl *ID,43 const CheckerBase *Checker) {44 45 QualType ResDerived = MethDerived->getReturnType();46 QualType ResAncestor = MethAncestor->getReturnType();47 48 if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {49 std::string sbuf;50 llvm::raw_string_ostream os(sbuf);51 52 os << "The Objective-C class '"53 << *MethDerived->getClassInterface()54 << "', which is derived from class '"55 << *MethAncestor->getClassInterface()56 << "', defines the instance method '";57 MethDerived->getSelector().print(os);58 os << "' whose return type is '" << ResDerived59 << "'. A method with the same name (same selector) is also defined in "60 "class '"61 << *MethAncestor->getClassInterface() << "' and has a return type of '"62 << ResAncestor63 << "'. These two types are incompatible, and may result in undefined "64 "behavior for clients of these classes.";65 66 PathDiagnosticLocation MethDLoc =67 PathDiagnosticLocation::createBegin(MethDerived,68 BR.getSourceManager());69 70 BR.EmitBasicReport(71 MethDerived, Checker, "Incompatible instance method return type",72 categories::CoreFoundationObjectiveC, os.str(), MethDLoc);73 }74}75 76static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,77 BugReporter &BR,78 const CheckerBase *Checker) {79 80 const ObjCInterfaceDecl *D = ID->getClassInterface();81 const ObjCInterfaceDecl *C = D->getSuperClass();82 83 if (!C)84 return;85 86 ASTContext &Ctx = BR.getContext();87 88 // Build a DenseMap of the methods for quick querying.89 typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;90 MapTy IMeths;91 unsigned NumMethods = 0;92 93 for (auto *M : ID->instance_methods()) {94 IMeths[M->getSelector()] = M;95 ++NumMethods;96 }97 98 // Now recurse the class hierarchy chain looking for methods with the99 // same signatures.100 while (C && NumMethods) {101 for (const auto *M : C->instance_methods()) {102 Selector S = M->getSelector();103 104 MapTy::iterator MI = IMeths.find(S);105 106 if (MI == IMeths.end() || MI->second == nullptr)107 continue;108 109 --NumMethods;110 ObjCMethodDecl *MethDerived = MI->second;111 MI->second = nullptr;112 113 CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);114 }115 116 C = C->getSuperClass();117 }118}119 120//===----------------------------------------------------------------------===//121// ObjCMethSigsChecker122//===----------------------------------------------------------------------===//123 124namespace {125class ObjCMethSigsChecker : public Checker<126 check::ASTDecl<ObjCImplementationDecl> > {127public:128 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,129 BugReporter &BR) const {130 CheckObjCInstMethSignature(D, BR, this);131 }132};133}134 135void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {136 mgr.registerChecker<ObjCMethSigsChecker>();137}138 139bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) {140 return true;141}142