brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.4 KiB · 3c0ced9 Raw
148 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 "ParentVirtualCallCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Tooling/FixIt.h"13#include "llvm/ADT/STLExtras.h"14#include "llvm/ADT/SmallVector.h"15#include <cctype>16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::bugprone {20 21using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;22 23static bool isParentOf(const CXXRecordDecl &Parent,24                       const CXXRecordDecl &ThisClass) {25  if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())26    return true;27  const CXXRecordDecl *ParentCanonicalDecl = Parent.getCanonicalDecl();28  return llvm::any_of(ThisClass.bases(), [=](const CXXBaseSpecifier &Base) {29    auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();30    assert(BaseDecl);31    return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();32  });33}34 35static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent,36                                           const CXXRecordDecl &ThisClass,37                                           const CXXMethodDecl &MemberDecl) {38  BasesVector Result;39  for (const auto &Base : ThisClass.bases()) {40    const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();41    const CXXMethodDecl *ActualMemberDecl =42        MemberDecl.getCorrespondingMethodInClass(BaseDecl);43    if (!ActualMemberDecl)44      continue;45    // TypePtr is the nearest base class to ThisClass between ThisClass and46    // GrandParent, where MemberDecl is overridden. TypePtr is the class the47    // check proposes to fix to.48    const Type *TypePtr = ActualMemberDecl->getThisType().getTypePtr();49    const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();50    assert(RecordDeclType && "TypePtr is not a pointer to CXXRecordDecl!");51    if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))52      Result.emplace_back(RecordDeclType);53  }54 55  return Result;56}57 58static std::string getNameAsString(const NamedDecl *Decl) {59  std::string QualName;60  llvm::raw_string_ostream OS(QualName);61  PrintingPolicy PP(Decl->getASTContext().getPrintingPolicy());62  PP.SuppressUnwrittenScope = true;63  Decl->printQualifiedName(OS, PP);64  return OS.str();65}66 67// Returns E as written in the source code. Used to handle 'using' and68// 'typedef'ed names of grand-parent classes.69static std::string getExprAsString(const clang::Expr &E,70                                   clang::ASTContext &AC) {71  std::string Text = tooling::fixit::getText(E, AC).str();72  llvm::erase_if(Text, [](char C) {73    return llvm::isSpace(static_cast<unsigned char>(C));74  });75  return Text;76}77 78void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {79  Finder->addMatcher(80      traverse(81          TK_AsIs,82          cxxMemberCallExpr(83              callee(memberExpr(hasDescendant(implicitCastExpr(84                                    hasImplicitDestinationType(pointsTo(85                                        type(anything()).bind("castToType"))),86                                    hasSourceExpression(cxxThisExpr(hasType(87                                        type(anything()).bind("thisType")))))))88                         .bind("member")),89              callee(cxxMethodDecl(isVirtual())))),90      this);91}92 93void ParentVirtualCallCheck::check(const MatchFinder::MatchResult &Result) {94  const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");95  assert(Member);96 97  if (!Member->getQualifier())98    return;99 100  const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());101 102  const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>("thisType");103  assert(ThisTypePtr);104 105  const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();106  assert(ThisType);107 108  const auto *CastToTypePtr = Result.Nodes.getNodeAs<Type>("castToType");109  assert(CastToTypePtr);110 111  const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();112  assert(CastToType);113 114  if (isParentOf(*CastToType, *ThisType))115    return;116 117  const BasesVector Parents =118      getParentsByGrandParent(*CastToType, *ThisType, *MemberDecl);119 120  if (Parents.empty())121    return;122 123  std::string ParentsStr;124  ParentsStr.reserve(30 * Parents.size());125  for (const CXXRecordDecl *Parent : Parents) {126    if (!ParentsStr.empty())127      ParentsStr.append(" or ");128    ParentsStr.append("'").append(getNameAsString(Parent)).append("'");129  }130 131  assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());132  auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),133                   "qualified name '%0' refers to a member overridden "134                   "in %plural{1:subclass|:subclasses}1; did you mean %2?")135              << getExprAsString(*Member, *Result.Context)136              << static_cast<unsigned>(Parents.size()) << ParentsStr;137 138  // Propose a fix if there's only one parent class...139  if (Parents.size() == 1 &&140      // ...unless parent class is templated141      !isa<ClassTemplateSpecializationDecl>(Parents.front()))142    Diag << FixItHint::CreateReplacement(143        Member->getQualifierLoc().getSourceRange(),144        getNameAsString(Parents.front()) + "::");145}146 147} // namespace clang::tidy::bugprone148