CheckObjCInstMethSignature.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file defines a CheckObjCInstMethSignature, a flow-insensitive check
  10. // that determines if an Objective-C class interface incorrectly redefines
  11. // the method signature in a subclass.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/Analysis/PathDiagnostic.h"
  16. #include "clang/AST/ASTContext.h"
  17. #include "clang/AST/DeclObjC.h"
  18. #include "clang/AST/Type.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "llvm/ADT/DenseMap.h"
  22. #include "llvm/Support/raw_ostream.h"
  23. using namespace clang;
  24. using namespace ento;
  25. static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
  26. ASTContext &C) {
  27. // Right now don't compare the compatibility of pointers. That involves
  28. // looking at subtyping relationships. FIXME: Future patch.
  29. if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())
  30. return true;
  31. return C.typesAreCompatible(Derived, Ancestor);
  32. }
  33. static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
  34. const ObjCMethodDecl *MethAncestor,
  35. BugReporter &BR, ASTContext &Ctx,
  36. const ObjCImplementationDecl *ID,
  37. const CheckerBase *Checker) {
  38. QualType ResDerived = MethDerived->getReturnType();
  39. QualType ResAncestor = MethAncestor->getReturnType();
  40. if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
  41. std::string sbuf;
  42. llvm::raw_string_ostream os(sbuf);
  43. os << "The Objective-C class '"
  44. << *MethDerived->getClassInterface()
  45. << "', which is derived from class '"
  46. << *MethAncestor->getClassInterface()
  47. << "', defines the instance method '";
  48. MethDerived->getSelector().print(os);
  49. os << "' whose return type is '" << ResDerived
  50. << "'. A method with the same name (same selector) is also defined in "
  51. "class '"
  52. << *MethAncestor->getClassInterface() << "' and has a return type of '"
  53. << ResAncestor
  54. << "'. These two types are incompatible, and may result in undefined "
  55. "behavior for clients of these classes.";
  56. PathDiagnosticLocation MethDLoc =
  57. PathDiagnosticLocation::createBegin(MethDerived,
  58. BR.getSourceManager());
  59. BR.EmitBasicReport(
  60. MethDerived, Checker, "Incompatible instance method return type",
  61. categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
  62. }
  63. }
  64. static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
  65. BugReporter &BR,
  66. const CheckerBase *Checker) {
  67. const ObjCInterfaceDecl *D = ID->getClassInterface();
  68. const ObjCInterfaceDecl *C = D->getSuperClass();
  69. if (!C)
  70. return;
  71. ASTContext &Ctx = BR.getContext();
  72. // Build a DenseMap of the methods for quick querying.
  73. typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
  74. MapTy IMeths;
  75. unsigned NumMethods = 0;
  76. for (auto *M : ID->instance_methods()) {
  77. IMeths[M->getSelector()] = M;
  78. ++NumMethods;
  79. }
  80. // Now recurse the class hierarchy chain looking for methods with the
  81. // same signatures.
  82. while (C && NumMethods) {
  83. for (const auto *M : C->instance_methods()) {
  84. Selector S = M->getSelector();
  85. MapTy::iterator MI = IMeths.find(S);
  86. if (MI == IMeths.end() || MI->second == nullptr)
  87. continue;
  88. --NumMethods;
  89. ObjCMethodDecl *MethDerived = MI->second;
  90. MI->second = nullptr;
  91. CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
  92. }
  93. C = C->getSuperClass();
  94. }
  95. }
  96. //===----------------------------------------------------------------------===//
  97. // ObjCMethSigsChecker
  98. //===----------------------------------------------------------------------===//
  99. namespace {
  100. class ObjCMethSigsChecker : public Checker<
  101. check::ASTDecl<ObjCImplementationDecl> > {
  102. public:
  103. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
  104. BugReporter &BR) const {
  105. CheckObjCInstMethSignature(D, BR, this);
  106. }
  107. };
  108. }
  109. void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
  110. mgr.registerChecker<ObjCMethSigsChecker>();
  111. }
  112. bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) {
  113. return true;
  114. }