llvm-project/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp
Piotr Zegar b6f6be4b50 [clang-tidy][NFC] Remove duplicated code
Remove duplicated matchers by moving some of them to
utils/Matchers.h. Add some anonymous namespaces and
renamed some code to avoid ODR issues.
2024-03-31 14:58:27 +00:00

152 lines
5.3 KiB
C++

//===--- ConvertMemberFunctionsToStatic.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 "ConvertMemberFunctionsToStatic.h"
#include "../utils/Matchers.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, isOverloadedOperator) {
return Node.isOverloadedOperator();
}
AST_MATCHER(CXXMethodDecl, usesThis) {
class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> {
public:
bool Used = false;
bool VisitCXXThisExpr(const CXXThisExpr *E) {
Used = true;
return false; // Stop 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(const_cast<Stmt *>(Node.getBody()));
return UsageOfThis.Used;
}
} // namespace
void ConvertMemberFunctionsToStatic::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(
isDefinition(), isUserProvided(),
unless(anyOf(
isExpansionInSystemHeader(), isVirtual(),
matchers::isStaticMethod(), matchers::hasTrivialBody(),
isOverloadedOperator(), cxxConstructorDecl(), cxxDestructorDecl(),
cxxConversionDecl(), matchers::isTemplate(),
matchers::isDependentContext(),
ofClass(anyOf(
isLambda(),
matchers::hasAnyDependentBases()) // Method might become
// virtual depending on
// template base class.
),
matchers::isInsideMacroDefinition(),
matchers::hasCanonicalDecl(matchers::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);
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".
StringRef Text = getStringFromRange(SourceMgr, LangOpts, Range);
size_t Offset = Text.find("const");
if (Offset == StringRef::npos)
return {};
SourceLocation Start = Range.getBegin().getLocWithOffset(Offset);
return {Start, Start.getLocWithOffset(strlen("const") - 1)};
}
void ConvertMemberFunctionsToStatic::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.
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.
SourceRange DefConst = getLocationOfConst(Definition->getTypeSourceInfo(),
*Result.SourceManager,
Result.Context->getLangOpts());
if (DefConst.isInvalid())
return;
if (Declaration != Definition) {
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