xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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