brintos

brintos / llvm-project-archived public Read only

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