diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-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.cpp | 148 |
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 |