aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp')
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp148
1 files changed, 148 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
new file mode 100644
index 0000000000..552034938a
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
@@ -0,0 +1,148 @@
+//===--- ParentVirtualCallCheck.cpp - clang-tidy---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ParentVirtualCallCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
+#include <cctype>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
+
+static bool isParentOf(const CXXRecordDecl &Parent,
+ const CXXRecordDecl &ThisClass) {
+ if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
+ return true;
+ const CXXRecordDecl *ParentCanonicalDecl = Parent.getCanonicalDecl();
+ return llvm::any_of(ThisClass.bases(), [=](const CXXBaseSpecifier &Base) {
+ auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
+ assert(BaseDecl);
+ return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();
+ });
+}
+
+static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent,
+ const CXXRecordDecl &ThisClass,
+ const CXXMethodDecl &MemberDecl) {
+ BasesVector Result;
+ for (const auto &Base : ThisClass.bases()) {
+ const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
+ const CXXMethodDecl *ActualMemberDecl =
+ MemberDecl.getCorrespondingMethodInClass(BaseDecl);
+ if (!ActualMemberDecl)
+ continue;
+ // TypePtr is the nearest base class to ThisClass between ThisClass and
+ // GrandParent, where MemberDecl is overridden. TypePtr is the class the
+ // check proposes to fix to.
+ const Type *TypePtr = ActualMemberDecl->getThisType().getTypePtr();
+ const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
+ assert(RecordDeclType && "TypePtr is not a pointer to CXXRecordDecl!");
+ if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
+ Result.emplace_back(RecordDeclType);
+ }
+
+ return Result;
+}
+
+static std::string getNameAsString(const NamedDecl *Decl) {
+ std::string QualName;
+ llvm::raw_string_ostream OS(QualName);
+ PrintingPolicy PP(Decl->getASTContext().getPrintingPolicy());
+ PP.SuppressUnwrittenScope = true;
+ Decl->printQualifiedName(OS, PP);
+ return OS.str();
+}
+
+// Returns E as written in the source code. Used to handle 'using' and
+// 'typedef'ed names of grand-parent classes.
+static std::string getExprAsString(const clang::Expr &E,
+ clang::ASTContext &AC) {
+ std::string Text = tooling::fixit::getText(E, AC).str();
+ llvm::erase_if(Text, [](char C) {
+ return llvm::isSpace(static_cast<unsigned char>(C));
+ });
+ return Text;
+}
+
+void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ traverse(
+ TK_AsIs,
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDescendant(implicitCastExpr(
+ hasImplicitDestinationType(pointsTo(
+ type(anything()).bind("castToType"))),
+ hasSourceExpression(cxxThisExpr(hasType(
+ type(anything()).bind("thisType")))))))
+ .bind("member")),
+ callee(cxxMethodDecl(isVirtual())))),
+ this);
+}
+
+void ParentVirtualCallCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
+ assert(Member);
+
+ if (!Member->getQualifier())
+ return;
+
+ const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
+
+ const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>("thisType");
+ assert(ThisTypePtr);
+
+ const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
+ assert(ThisType);
+
+ const auto *CastToTypePtr = Result.Nodes.getNodeAs<Type>("castToType");
+ assert(CastToTypePtr);
+
+ const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
+ assert(CastToType);
+
+ if (isParentOf(*CastToType, *ThisType))
+ return;
+
+ const BasesVector Parents =
+ getParentsByGrandParent(*CastToType, *ThisType, *MemberDecl);
+
+ if (Parents.empty())
+ return;
+
+ std::string ParentsStr;
+ ParentsStr.reserve(30 * Parents.size());
+ for (const CXXRecordDecl *Parent : Parents) {
+ if (!ParentsStr.empty())
+ ParentsStr.append(" or ");
+ ParentsStr.append("'").append(getNameAsString(Parent)).append("'");
+ }
+
+ assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
+ auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),
+ "qualified name '%0' refers to a member overridden "
+ "in %plural{1:subclass|:subclasses}1; did you mean %2?")
+ << getExprAsString(*Member, *Result.Context)
+ << static_cast<unsigned>(Parents.size()) << ParentsStr;
+
+ // Propose a fix if there's only one parent class...
+ if (Parents.size() == 1 &&
+ // ...unless parent class is templated
+ !isa<ClassTemplateSpecializationDecl>(Parents.front()))
+ Diag << FixItHint::CreateReplacement(
+ Member->getQualifierLoc().getSourceRange(),
+ getNameAsString(Parents.front()) + "::");
+}
+
+} // namespace clang::tidy::bugprone