clang-tools  14.0.0git
ParentVirtualCallCheck.cpp
Go to the documentation of this file.
1 //===--- ParentVirtualCallCheck.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 
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 <algorithm>
16 #include <cctype>
17 
18 using namespace clang::ast_matchers;
19 
20 namespace clang {
21 namespace tidy {
22 namespace bugprone {
23 
24 using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
25 
26 static bool isParentOf(const CXXRecordDecl &Parent,
27  const CXXRecordDecl &ThisClass) {
28  if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
29  return true;
30  const CXXRecordDecl *ParentCanonicalDecl = Parent.getCanonicalDecl();
31  return ThisClass.bases_end() !=
32  llvm::find_if(ThisClass.bases(), [=](const CXXBaseSpecifier &Base) {
33  auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
34  assert(BaseDecl);
35  return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();
36  });
37 }
38 
39 static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent,
40  const CXXRecordDecl &ThisClass,
41  const CXXMethodDecl &MemberDecl) {
42  BasesVector Result;
43  for (const auto &Base : ThisClass.bases()) {
44  const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
45  const CXXMethodDecl *ActualMemberDecl =
46  MemberDecl.getCorrespondingMethodInClass(BaseDecl);
47  if (!ActualMemberDecl)
48  continue;
49  // TypePtr is the nearest base class to ThisClass between ThisClass and
50  // GrandParent, where MemberDecl is overridden. TypePtr is the class the
51  // check proposes to fix to.
52  const Type *TypePtr = ActualMemberDecl->getThisType().getTypePtr();
53  const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
54  assert(RecordDeclType && "TypePtr is not a pointer to CXXRecordDecl!");
55  if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
56  Result.emplace_back(RecordDeclType);
57  }
58 
59  return Result;
60 }
61 
62 static std::string getNameAsString(const NamedDecl *Decl) {
63  std::string QualName;
64  llvm::raw_string_ostream OS(QualName);
65  PrintingPolicy PP(Decl->getASTContext().getPrintingPolicy());
66  PP.SuppressUnwrittenScope = true;
67  Decl->printQualifiedName(OS, PP);
68  return OS.str();
69 }
70 
71 // Returns E as written in the source code. Used to handle 'using' and
72 // 'typedef'ed names of grand-parent classes.
73 static std::string getExprAsString(const clang::Expr &E,
74  clang::ASTContext &AC) {
75  std::string Text = tooling::fixit::getText(E, AC).str();
76  Text.erase(
77  llvm::remove_if(
78  Text,
79  [](char C) { return llvm::isSpace(static_cast<unsigned char>(C)); }),
80  Text.end());
81  return Text;
82 }
83 
84 void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {
85  Finder->addMatcher(
86  traverse(
87  TK_AsIs,
88  cxxMemberCallExpr(
89  callee(memberExpr(hasDescendant(implicitCastExpr(
90  hasImplicitDestinationType(pointsTo(
91  type(anything()).bind("castToType"))),
92  hasSourceExpression(cxxThisExpr(hasType(
93  type(anything()).bind("thisType")))))))
94  .bind("member")),
95  callee(cxxMethodDecl(isVirtual())))),
96  this);
97 }
98 
99 void ParentVirtualCallCheck::check(const MatchFinder::MatchResult &Result) {
100  const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
101  assert(Member);
102 
103  if (!Member->getQualifier())
104  return;
105 
106  const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
107 
108  const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>("thisType");
109  assert(ThisTypePtr);
110 
111  const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
112  assert(ThisType);
113 
114  const auto *CastToTypePtr = Result.Nodes.getNodeAs<Type>("castToType");
115  assert(CastToTypePtr);
116 
117  const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
118  assert(CastToType);
119 
120  if (isParentOf(*CastToType, *ThisType))
121  return;
122 
123  const BasesVector Parents =
124  getParentsByGrandParent(*CastToType, *ThisType, *MemberDecl);
125 
126  if (Parents.empty())
127  return;
128 
129  std::string ParentsStr;
130  ParentsStr.reserve(30 * Parents.size());
131  for (const CXXRecordDecl *Parent : Parents) {
132  if (!ParentsStr.empty())
133  ParentsStr.append(" or ");
134  ParentsStr.append("'").append(getNameAsString(Parent)).append("'");
135  }
136 
137  assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
138  auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),
139  "qualified name '%0' refers to a member overridden "
140  "in %plural{1:subclass|:subclasses}1; did you mean %2?")
141  << getExprAsString(*Member, *Result.Context)
142  << static_cast<unsigned>(Parents.size()) << ParentsStr;
143 
144  // Propose a fix if there's only one parent class...
145  if (Parents.size() == 1 &&
146  // ...unless parent class is templated
147  !isa<ClassTemplateSpecializationDecl>(Parents.front()))
148  Diag << FixItHint::CreateReplacement(
149  Member->getQualifierLoc().getSourceRange(),
150  getNameAsString(Parents.front()) + "::");
151 }
152 
153 } // namespace bugprone
154 } // namespace tidy
155 } // namespace clang
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:89
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::tidy::bugprone::BasesVector
llvm::SmallVector< const CXXRecordDecl *, 5 > BasesVector
Definition: ParentVirtualCallCheck.cpp:24
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
Text
std::string Text
Definition: HTMLGenerator.cpp:80
clang::tidy::bugprone::isParentOf
static bool isParentOf(const CXXRecordDecl &Parent, const CXXRecordDecl &ThisClass)
Definition: ParentVirtualCallCheck.cpp:26
clang::tidy::modernize::getText
static StringRef getText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:78
clang::tidy::bugprone::getExprAsString
static std::string getExprAsString(const clang::Expr &E, clang::ASTContext &AC)
Definition: ParentVirtualCallCheck.cpp:73
clang::ast_matchers
Definition: AbseilMatcher.h:14
ParentVirtualCallCheck.h
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::tidy::bugprone::getParentsByGrandParent
static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent, const CXXRecordDecl &ThisClass, const CXXMethodDecl &MemberDecl)
Definition: ParentVirtualCallCheck.cpp:39
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:259
Parent
const Node * Parent
Definition: ExtractFunction.cpp:152
clang::tidy::bugprone::getNameAsString
static std::string getNameAsString(const NamedDecl *Decl)
Definition: ParentVirtualCallCheck.cpp:62
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:163