CheckObjCInstMethSignature.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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 '"
  50. << ResDerived.getAsString()
  51. << "'. A method with the same name (same selector) is also defined in "
  52. "class '"
  53. << *MethAncestor->getClassInterface()
  54. << "' and has a return type of '"
  55. << ResAncestor.getAsString()
  56. << "'. These two types are incompatible, and may result in undefined "
  57. "behavior for clients of these classes.";
  58. PathDiagnosticLocation MethDLoc =
  59. PathDiagnosticLocation::createBegin(MethDerived,
  60. BR.getSourceManager());
  61. BR.EmitBasicReport(
  62. MethDerived, Checker, "Incompatible instance method return type",
  63. categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
  64. }
  65. }
  66. static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
  67. BugReporter &BR,
  68. const CheckerBase *Checker) {
  69. const ObjCInterfaceDecl *D = ID->getClassInterface();
  70. const ObjCInterfaceDecl *C = D->getSuperClass();
  71. if (!C)
  72. return;
  73. ASTContext &Ctx = BR.getContext();
  74. // Build a DenseMap of the methods for quick querying.
  75. typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
  76. MapTy IMeths;
  77. unsigned NumMethods = 0;
  78. for (auto *M : ID->instance_methods()) {
  79. IMeths[M->getSelector()] = M;
  80. ++NumMethods;
  81. }
  82. // Now recurse the class hierarchy chain looking for methods with the
  83. // same signatures.
  84. while (C && NumMethods) {
  85. for (const auto *M : C->instance_methods()) {
  86. Selector S = M->getSelector();
  87. MapTy::iterator MI = IMeths.find(S);
  88. if (MI == IMeths.end() || MI->second == nullptr)
  89. continue;
  90. --NumMethods;
  91. ObjCMethodDecl *MethDerived = MI->second;
  92. MI->second = nullptr;
  93. CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
  94. }
  95. C = C->getSuperClass();
  96. }
  97. }
  98. //===----------------------------------------------------------------------===//
  99. // ObjCMethSigsChecker
  100. //===----------------------------------------------------------------------===//
  101. namespace {
  102. class ObjCMethSigsChecker : public Checker<
  103. check::ASTDecl<ObjCImplementationDecl> > {
  104. public:
  105. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
  106. BugReporter &BR) const {
  107. CheckObjCInstMethSignature(D, BR, this);
  108. }
  109. };
  110. }
  111. void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
  112. mgr.registerChecker<ObjCMethSigsChecker>();
  113. }
  114. bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) {
  115. return true;
  116. }