10b57cec5SDimitry Andric //===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
9349cc55cSDimitry Andric // This file defines a CheckObjCInstMethSignature, a flow-insensitive check
100b57cec5SDimitry Andric // that determines if an Objective-C class interface incorrectly redefines
110b57cec5SDimitry Andric // the method signature in a subclass.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16a7dea167SDimitry Andric #include "clang/Analysis/PathDiagnostic.h"
170b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
180b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
190b57cec5SDimitry Andric #include "clang/AST/Type.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
220b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
230b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
240b57cec5SDimitry Andric
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric
AreTypesCompatible(QualType Derived,QualType Ancestor,ASTContext & C)280b57cec5SDimitry Andric static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
290b57cec5SDimitry Andric ASTContext &C) {
300b57cec5SDimitry Andric
310b57cec5SDimitry Andric // Right now don't compare the compatibility of pointers. That involves
320b57cec5SDimitry Andric // looking at subtyping relationships. FIXME: Future patch.
330b57cec5SDimitry Andric if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())
340b57cec5SDimitry Andric return true;
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric return C.typesAreCompatible(Derived, Ancestor);
370b57cec5SDimitry Andric }
380b57cec5SDimitry Andric
CompareReturnTypes(const ObjCMethodDecl * MethDerived,const ObjCMethodDecl * MethAncestor,BugReporter & BR,ASTContext & Ctx,const ObjCImplementationDecl * ID,const CheckerBase * Checker)390b57cec5SDimitry Andric static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
400b57cec5SDimitry Andric const ObjCMethodDecl *MethAncestor,
410b57cec5SDimitry Andric BugReporter &BR, ASTContext &Ctx,
420b57cec5SDimitry Andric const ObjCImplementationDecl *ID,
430b57cec5SDimitry Andric const CheckerBase *Checker) {
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric QualType ResDerived = MethDerived->getReturnType();
460b57cec5SDimitry Andric QualType ResAncestor = MethAncestor->getReturnType();
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
490b57cec5SDimitry Andric std::string sbuf;
500b57cec5SDimitry Andric llvm::raw_string_ostream os(sbuf);
510b57cec5SDimitry Andric
520b57cec5SDimitry Andric os << "The Objective-C class '"
530b57cec5SDimitry Andric << *MethDerived->getClassInterface()
540b57cec5SDimitry Andric << "', which is derived from class '"
550b57cec5SDimitry Andric << *MethAncestor->getClassInterface()
560b57cec5SDimitry Andric << "', defines the instance method '";
570b57cec5SDimitry Andric MethDerived->getSelector().print(os);
58*81ad6265SDimitry Andric os << "' whose return type is '" << ResDerived
590b57cec5SDimitry Andric << "'. A method with the same name (same selector) is also defined in "
600b57cec5SDimitry Andric "class '"
61*81ad6265SDimitry Andric << *MethAncestor->getClassInterface() << "' and has a return type of '"
62*81ad6265SDimitry Andric << ResAncestor
630b57cec5SDimitry Andric << "'. These two types are incompatible, and may result in undefined "
640b57cec5SDimitry Andric "behavior for clients of these classes.";
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric PathDiagnosticLocation MethDLoc =
670b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(MethDerived,
680b57cec5SDimitry Andric BR.getSourceManager());
690b57cec5SDimitry Andric
700b57cec5SDimitry Andric BR.EmitBasicReport(
710b57cec5SDimitry Andric MethDerived, Checker, "Incompatible instance method return type",
720b57cec5SDimitry Andric categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric
CheckObjCInstMethSignature(const ObjCImplementationDecl * ID,BugReporter & BR,const CheckerBase * Checker)760b57cec5SDimitry Andric static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
770b57cec5SDimitry Andric BugReporter &BR,
780b57cec5SDimitry Andric const CheckerBase *Checker) {
790b57cec5SDimitry Andric
800b57cec5SDimitry Andric const ObjCInterfaceDecl *D = ID->getClassInterface();
810b57cec5SDimitry Andric const ObjCInterfaceDecl *C = D->getSuperClass();
820b57cec5SDimitry Andric
830b57cec5SDimitry Andric if (!C)
840b57cec5SDimitry Andric return;
850b57cec5SDimitry Andric
860b57cec5SDimitry Andric ASTContext &Ctx = BR.getContext();
870b57cec5SDimitry Andric
880b57cec5SDimitry Andric // Build a DenseMap of the methods for quick querying.
890b57cec5SDimitry Andric typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
900b57cec5SDimitry Andric MapTy IMeths;
910b57cec5SDimitry Andric unsigned NumMethods = 0;
920b57cec5SDimitry Andric
930b57cec5SDimitry Andric for (auto *M : ID->instance_methods()) {
940b57cec5SDimitry Andric IMeths[M->getSelector()] = M;
950b57cec5SDimitry Andric ++NumMethods;
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric
980b57cec5SDimitry Andric // Now recurse the class hierarchy chain looking for methods with the
990b57cec5SDimitry Andric // same signatures.
1000b57cec5SDimitry Andric while (C && NumMethods) {
1010b57cec5SDimitry Andric for (const auto *M : C->instance_methods()) {
1020b57cec5SDimitry Andric Selector S = M->getSelector();
1030b57cec5SDimitry Andric
1040b57cec5SDimitry Andric MapTy::iterator MI = IMeths.find(S);
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric if (MI == IMeths.end() || MI->second == nullptr)
1070b57cec5SDimitry Andric continue;
1080b57cec5SDimitry Andric
1090b57cec5SDimitry Andric --NumMethods;
1100b57cec5SDimitry Andric ObjCMethodDecl *MethDerived = MI->second;
1110b57cec5SDimitry Andric MI->second = nullptr;
1120b57cec5SDimitry Andric
1130b57cec5SDimitry Andric CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
1140b57cec5SDimitry Andric }
1150b57cec5SDimitry Andric
1160b57cec5SDimitry Andric C = C->getSuperClass();
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric
1200b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1210b57cec5SDimitry Andric // ObjCMethSigsChecker
1220b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1230b57cec5SDimitry Andric
1240b57cec5SDimitry Andric namespace {
1250b57cec5SDimitry Andric class ObjCMethSigsChecker : public Checker<
1260b57cec5SDimitry Andric check::ASTDecl<ObjCImplementationDecl> > {
1270b57cec5SDimitry Andric public:
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & mgr,BugReporter & BR) const1280b57cec5SDimitry Andric void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
1290b57cec5SDimitry Andric BugReporter &BR) const {
1300b57cec5SDimitry Andric CheckObjCInstMethSignature(D, BR, this);
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric };
1330b57cec5SDimitry Andric }
1340b57cec5SDimitry Andric
registerObjCMethSigsChecker(CheckerManager & mgr)1350b57cec5SDimitry Andric void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
1360b57cec5SDimitry Andric mgr.registerChecker<ObjCMethSigsChecker>();
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric
shouldRegisterObjCMethSigsChecker(const CheckerManager & mgr)1395ffd83dbSDimitry Andric bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) {
1400b57cec5SDimitry Andric return true;
1410b57cec5SDimitry Andric }
142