Files
TheRock/compiler/amd-llvm/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStaticCheck.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

212 lines
7.2 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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 "ConvertMemberFunctionsToStaticCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang::tidy::readability {
namespace {
AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
AST_MATCHER(CXXMethodDecl, hasTrivialBody) { return Node.hasTrivialBody(); }
AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
return Node.isOverloadedOperator();
}
AST_MATCHER(CXXRecordDecl, hasAnyDependentBases) {
return Node.hasAnyDependentBases();
}
AST_MATCHER(CXXMethodDecl, isTemplate) {
return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate;
}
AST_MATCHER(CXXMethodDecl, isDependentContext) {
return Node.isDependentContext();
}
AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) {
const ASTContext &Ctxt = Finder->getASTContext();
return Lexer::makeFileCharRange(
CharSourceRange::getCharRange(
Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()),
Ctxt.getSourceManager(), Ctxt.getLangOpts())
.isInvalid();
}
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl,
ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) {
return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
}
AST_MATCHER(CXXMethodDecl, usesThis) {
class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> {
public:
bool Used = false;
bool VisitCXXThisExpr(const CXXThisExpr *E) {
Used = true;
return false; // Stop traversal.
}
bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *E) {
if (E->isImplicitAccess()) {
Used = true;
return false;
}
return true; // Continue traversal.
}
// If we enter a class declaration, don't traverse into it as any usages of
// `this` will correspond to the nested class.
bool TraverseCXXRecordDecl(CXXRecordDecl *RD) { return true; }
} UsageOfThis;
// TraverseStmt does not modify its argument.
UsageOfThis.TraverseStmt(Node.getBody());
return UsageOfThis.Used;
}
AST_MATCHER(CXXMethodDecl, hasNonConstOverload) {
const auto *Method = &Node;
const DeclContext::lookup_result LookupResult =
Method->getParent()->lookup(Method->getNameInfo().getName());
if (LookupResult.isSingleResult())
return false;
auto HasSameParameterTypes = [](const CXXMethodDecl &MD1,
const CXXMethodDecl &MD2) {
if (MD1.getNumParams() != MD2.getNumParams())
return false;
for (unsigned I = 0, E = MD1.getNumParams(); I < E; ++I)
if (MD1.getParamDecl(I)->getType().getCanonicalType() !=
MD2.getParamDecl(I)->getType().getCanonicalType())
return false;
return true;
};
return llvm::any_of(
LookupResult, [Method, HasSameParameterTypes](const Decl *D) {
const auto *Overload = dyn_cast<CXXMethodDecl>(D);
return Overload && Overload != Method && !Overload->isConst() &&
HasSameParameterTypes(*Method, *Overload);
});
}
} // namespace
void ConvertMemberFunctionsToStaticCheck::registerMatchers(
MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(
isDefinition(), isUserProvided(),
unless(anyOf(
isVirtual(), isStatic(), hasTrivialBody(), isOverloadedOperator(),
cxxConstructorDecl(), cxxDestructorDecl(), cxxConversionDecl(),
isExplicitObjectMemberFunction(), isTemplate(),
isDependentContext(), allOf(isConst(), hasNonConstOverload()),
ofClass(anyOf(
isLambda(),
hasAnyDependentBases()) // Method might become virtual
// depending on template base class.
),
isInsideMacroDefinition(),
hasCanonicalDecl(isInsideMacroDefinition()), usesThis())))
.bind("x"),
this);
}
/// Obtain the original source code text from a SourceRange.
static StringRef getStringFromRange(SourceManager &SourceMgr,
const LangOptions &LangOpts,
SourceRange Range) {
if (SourceMgr.getFileID(Range.getBegin()) !=
SourceMgr.getFileID(Range.getEnd()))
return {};
return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
LangOpts);
}
static SourceRange getLocationOfConst(const TypeSourceInfo *TSI,
SourceManager &SourceMgr,
const LangOptions &LangOpts) {
assert(TSI);
const auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
assert(FTL);
const SourceRange Range{FTL.getRParenLoc().getLocWithOffset(1),
FTL.getLocalRangeEnd()};
// Inside Range, there might be other keywords and trailing return types.
// Find the exact position of "const".
const StringRef Text = getStringFromRange(SourceMgr, LangOpts, Range);
const size_t Offset = Text.find("const");
if (Offset == StringRef::npos)
return {};
const SourceLocation Start = Range.getBegin().getLocWithOffset(Offset);
return {Start, Start.getLocWithOffset(strlen("const") - 1)};
}
void ConvertMemberFunctionsToStaticCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x");
// TODO: For out-of-line declarations, don't modify the source if the header
// is excluded by the -header-filter option.
const DiagnosticBuilder Diag =
diag(Definition->getLocation(), "method %0 can be made static")
<< Definition;
// TODO: Would need to remove those in a fix-it.
if (Definition->getMethodQualifiers().hasVolatile() ||
Definition->getMethodQualifiers().hasRestrict() ||
Definition->getRefQualifier() != RQ_None)
return;
const CXXMethodDecl *Declaration = Definition->getCanonicalDecl();
if (Definition->isConst()) {
// Make sure that we either remove 'const' on both declaration and
// definition or emit no fix-it at all.
const SourceRange DefConst = getLocationOfConst(
Definition->getTypeSourceInfo(), *Result.SourceManager,
Result.Context->getLangOpts());
if (DefConst.isInvalid())
return;
if (Declaration != Definition) {
const SourceRange DeclConst = getLocationOfConst(
Declaration->getTypeSourceInfo(), *Result.SourceManager,
Result.Context->getLangOpts());
if (DeclConst.isInvalid())
return;
Diag << FixItHint::CreateRemoval(DeclConst);
}
// Remove existing 'const' from both declaration and definition.
Diag << FixItHint::CreateRemoval(DefConst);
}
Diag << FixItHint::CreateInsertion(Declaration->getBeginLoc(), "static ");
}
} // namespace clang::tidy::readability