clang-tools 8.0.0

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