VirtualNearMissCheck.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. //===--- VirtualNearMissCheck.cpp - clang-tidy-----------------------------===//
  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. #include "VirtualNearMissCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/AST/CXXInheritance.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/Lex/Lexer.h"
  13. using namespace clang::ast_matchers;
  14. namespace clang::tidy::bugprone {
  15. namespace {
  16. AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
  17. AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
  18. return Node.isOverloadedOperator();
  19. }
  20. } // namespace
  21. /// Finds out if the given method overrides some method.
  22. static bool isOverrideMethod(const CXXMethodDecl *MD) {
  23. return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
  24. }
  25. /// Checks whether the return types are covariant, according to
  26. /// C++[class.virtual]p7.
  27. ///
  28. /// Similar with clang::Sema::CheckOverridingFunctionReturnType.
  29. /// \returns true if the return types of BaseMD and DerivedMD are covariant.
  30. static bool checkOverridingFunctionReturnType(const ASTContext *Context,
  31. const CXXMethodDecl *BaseMD,
  32. const CXXMethodDecl *DerivedMD) {
  33. QualType BaseReturnTy = BaseMD->getType()
  34. ->castAs<FunctionType>()
  35. ->getReturnType()
  36. .getCanonicalType();
  37. QualType DerivedReturnTy = DerivedMD->getType()
  38. ->castAs<FunctionType>()
  39. ->getReturnType()
  40. .getCanonicalType();
  41. if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
  42. return false;
  43. // Check if return types are identical.
  44. if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
  45. return true;
  46. /// Check if the return types are covariant.
  47. // Both types must be pointers or references to classes.
  48. if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
  49. !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
  50. return false;
  51. /// BTy is the class type in return type of BaseMD. For example,
  52. /// B* Base::md()
  53. /// While BRD is the declaration of B.
  54. QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
  55. QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
  56. const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
  57. const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
  58. if (DRD == nullptr || BRD == nullptr)
  59. return false;
  60. if (!DRD->hasDefinition() || !BRD->hasDefinition())
  61. return false;
  62. if (DRD == BRD)
  63. return true;
  64. if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
  65. // Begin checking whether the conversion from D to B is valid.
  66. CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
  67. /*DetectVirtual=*/false);
  68. // Check whether D is derived from B, and fill in a CXXBasePaths object.
  69. if (!DRD->isDerivedFrom(BRD, Paths))
  70. return false;
  71. // Check ambiguity.
  72. if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
  73. return false;
  74. // Check accessibility.
  75. // FIXME: We currently only support checking if B is accessible base class
  76. // of D, or D is the same class which DerivedMD is in.
  77. bool IsItself =
  78. DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
  79. bool HasPublicAccess = false;
  80. for (const auto &Path : Paths) {
  81. if (Path.Access == AS_public)
  82. HasPublicAccess = true;
  83. }
  84. if (!HasPublicAccess && !IsItself)
  85. return false;
  86. // End checking conversion from D to B.
  87. }
  88. // Both pointers or references should have the same cv-qualification.
  89. if (DerivedReturnTy.getLocalCVRQualifiers() !=
  90. BaseReturnTy.getLocalCVRQualifiers())
  91. return false;
  92. // The class type D should have the same cv-qualification as or less
  93. // cv-qualification than the class type B.
  94. if (DTy.isMoreQualifiedThan(BTy))
  95. return false;
  96. return true;
  97. }
  98. /// \returns decayed type for arrays and functions.
  99. static QualType getDecayedType(QualType Type) {
  100. if (const auto *Decayed = Type->getAs<DecayedType>())
  101. return Decayed->getDecayedType();
  102. return Type;
  103. }
  104. /// \returns true if the param types are the same.
  105. static bool checkParamTypes(const CXXMethodDecl *BaseMD,
  106. const CXXMethodDecl *DerivedMD) {
  107. unsigned NumParamA = BaseMD->getNumParams();
  108. unsigned NumParamB = DerivedMD->getNumParams();
  109. if (NumParamA != NumParamB)
  110. return false;
  111. for (unsigned I = 0; I < NumParamA; I++) {
  112. if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
  113. getDecayedType(
  114. DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
  115. return false;
  116. }
  117. return true;
  118. }
  119. /// \returns true if derived method can override base method except for the
  120. /// name.
  121. static bool checkOverrideWithoutName(const ASTContext *Context,
  122. const CXXMethodDecl *BaseMD,
  123. const CXXMethodDecl *DerivedMD) {
  124. if (BaseMD->isStatic() != DerivedMD->isStatic())
  125. return false;
  126. if (BaseMD->getType() == DerivedMD->getType())
  127. return true;
  128. // Now the function types are not identical. Then check if the return types
  129. // are covariant and if the param types are the same.
  130. if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
  131. return false;
  132. return checkParamTypes(BaseMD, DerivedMD);
  133. }
  134. /// Check whether BaseMD overrides DerivedMD.
  135. ///
  136. /// Prerequisite: the class which BaseMD is in should be a base class of that
  137. /// DerivedMD is in.
  138. static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
  139. const CXXMethodDecl *DerivedMD) {
  140. for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
  141. E = DerivedMD->end_overridden_methods();
  142. I != E; ++I) {
  143. const CXXMethodDecl *OverriddenMD = *I;
  144. if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
  145. return true;
  146. }
  147. return false;
  148. }
  149. bool VirtualNearMissCheck::isPossibleToBeOverridden(
  150. const CXXMethodDecl *BaseMD) {
  151. auto Iter = PossibleMap.find(BaseMD);
  152. if (Iter != PossibleMap.end())
  153. return Iter->second;
  154. bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
  155. !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
  156. !BaseMD->isOverloadedOperator() &&
  157. !isa<CXXConversionDecl>(BaseMD);
  158. PossibleMap[BaseMD] = IsPossible;
  159. return IsPossible;
  160. }
  161. bool VirtualNearMissCheck::isOverriddenByDerivedClass(
  162. const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
  163. auto Key = std::make_pair(BaseMD, DerivedRD);
  164. auto Iter = OverriddenMap.find(Key);
  165. if (Iter != OverriddenMap.end())
  166. return Iter->second;
  167. bool IsOverridden = false;
  168. for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
  169. if (!isOverrideMethod(DerivedMD))
  170. continue;
  171. if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
  172. IsOverridden = true;
  173. break;
  174. }
  175. }
  176. OverriddenMap[Key] = IsOverridden;
  177. return IsOverridden;
  178. }
  179. void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
  180. Finder->addMatcher(
  181. cxxMethodDecl(
  182. unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
  183. cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
  184. isOverloadedOperator())))
  185. .bind("method"),
  186. this);
  187. }
  188. void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
  189. const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
  190. assert(DerivedMD);
  191. const ASTContext *Context = Result.Context;
  192. const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
  193. assert(DerivedRD);
  194. for (const auto &BaseSpec : DerivedRD->bases()) {
  195. if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
  196. for (const auto *BaseMD : BaseRD->methods()) {
  197. if (!isPossibleToBeOverridden(BaseMD))
  198. continue;
  199. if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
  200. continue;
  201. unsigned EditDistance = BaseMD->getName().edit_distance(
  202. DerivedMD->getName(), EditDistanceThreshold);
  203. if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
  204. if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
  205. // A "virtual near miss" is found.
  206. auto Range = CharSourceRange::getTokenRange(
  207. SourceRange(DerivedMD->getLocation()));
  208. bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
  209. !DerivedMD->isTemplateInstantiation();
  210. auto Diag =
  211. diag(DerivedMD->getBeginLoc(),
  212. "method '%0' has a similar name and the same signature as "
  213. "virtual method '%1'; did you mean to override it?")
  214. << DerivedMD->getQualifiedNameAsString()
  215. << BaseMD->getQualifiedNameAsString();
  216. if (ApplyFix)
  217. Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
  218. }
  219. }
  220. }
  221. }
  222. }
  223. }
  224. } // namespace clang::tidy::bugprone