Skip to content

Commit ffca322

Browse files
committed
[clang-tidy] initial version of readability-convert-member-functions-to-static
Summary: Finds non-static member functions that can be made ``static``. I have run this check (repeatedly) over llvm-project. It made 1708 member functions ``static``. Out of those, I had to exclude 22 via ``NOLINT`` because their address was taken and stored in a variable of pointer-to-member type (e.g. passed to llvm::StringSwitch). It also made 243 member functions ``const``. (This is currently very conservative to have no false-positives and can hopefully be extended in the future.) You can find the results here: https://github.com/mgehre/llvm-project/commits/static_const_eval Reviewers: alexfh, aaron.ballman Subscribers: mgorny, xazax.hun, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D61749 llvm-svn: 366265
1 parent 40580d3 commit ffca322

8 files changed

+451
-0
lines changed

clang-tools-extra/clang-tidy/readability/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_clang_library(clangTidyReadabilityModule
55
BracesAroundStatementsCheck.cpp
66
ConstReturnTypeCheck.cpp
77
ContainerSizeEmptyCheck.cpp
8+
ConvertMemberFunctionsToStatic.cpp
89
DeleteNullPointerCheck.cpp
910
DeletedDefaultCheck.cpp
1011
ElseAfterReturnCheck.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//===--- ConvertMemberFunctionsToStatic.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 "ConvertMemberFunctionsToStatic.h"
11+
#include "clang/AST/ASTContext.h"
12+
#include "clang/AST/DeclCXX.h"
13+
#include "clang/AST/RecursiveASTVisitor.h"
14+
#include "clang/ASTMatchers/ASTMatchFinder.h"
15+
#include "clang/Basic/SourceLocation.h"
16+
17+
using namespace clang::ast_matchers;
18+
19+
namespace clang {
20+
namespace tidy {
21+
namespace readability {
22+
23+
AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
24+
25+
AST_MATCHER(CXXMethodDecl, hasTrivialBody) { return Node.hasTrivialBody(); }
26+
27+
AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
28+
return Node.isOverloadedOperator();
29+
}
30+
31+
AST_MATCHER(CXXRecordDecl, hasAnyDependentBases) {
32+
return Node.hasAnyDependentBases();
33+
}
34+
35+
AST_MATCHER(CXXMethodDecl, isTemplate) {
36+
return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate;
37+
}
38+
39+
AST_MATCHER(CXXMethodDecl, isDependentContext) {
40+
return Node.isDependentContext();
41+
}
42+
43+
AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) {
44+
const ASTContext &Ctxt = Finder->getASTContext();
45+
return clang::Lexer::makeFileCharRange(
46+
clang::CharSourceRange::getCharRange(
47+
Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()),
48+
Ctxt.getSourceManager(), Ctxt.getLangOpts())
49+
.isInvalid();
50+
}
51+
52+
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl,
53+
ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) {
54+
return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
55+
}
56+
57+
AST_MATCHER(CXXMethodDecl, usesThis) {
58+
class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> {
59+
public:
60+
bool Used = false;
61+
62+
bool VisitCXXThisExpr(const CXXThisExpr *E) {
63+
Used = true;
64+
return false; // Stop traversal.
65+
}
66+
} UsageOfThis;
67+
68+
// TraverseStmt does not modify its argument.
69+
UsageOfThis.TraverseStmt(const_cast<Stmt *>(Node.getBody()));
70+
71+
return UsageOfThis.Used;
72+
}
73+
74+
void ConvertMemberFunctionsToStatic::registerMatchers(MatchFinder *Finder) {
75+
Finder->addMatcher(
76+
cxxMethodDecl(
77+
isDefinition(), isUserProvided(),
78+
unless(anyOf(
79+
isExpansionInSystemHeader(), isVirtual(), isStatic(),
80+
hasTrivialBody(), isOverloadedOperator(), cxxConstructorDecl(),
81+
cxxDestructorDecl(), cxxConversionDecl(), isTemplate(),
82+
isDependentContext(),
83+
ofClass(anyOf(
84+
isLambda(),
85+
hasAnyDependentBases()) // Method might become virtual
86+
// depending on template base class.
87+
),
88+
isInsideMacroDefinition(),
89+
hasCanonicalDecl(isInsideMacroDefinition()), usesThis())))
90+
.bind("x"),
91+
this);
92+
}
93+
94+
/// \brief Obtain the original source code text from a SourceRange.
95+
static StringRef getStringFromRange(SourceManager &SourceMgr,
96+
const LangOptions &LangOpts,
97+
SourceRange Range) {
98+
if (SourceMgr.getFileID(Range.getBegin()) !=
99+
SourceMgr.getFileID(Range.getEnd()))
100+
return {};
101+
102+
return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
103+
LangOpts);
104+
}
105+
106+
static SourceRange getLocationOfConst(const TypeSourceInfo *TSI,
107+
SourceManager &SourceMgr,
108+
const LangOptions &LangOpts) {
109+
assert(TSI);
110+
const auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
111+
assert(FTL);
112+
113+
SourceRange Range{FTL.getRParenLoc().getLocWithOffset(1),
114+
FTL.getLocalRangeEnd()};
115+
// Inside Range, there might be other keywords and trailing return types.
116+
// Find the exact position of "const".
117+
StringRef Text = getStringFromRange(SourceMgr, LangOpts, Range);
118+
size_t Offset = Text.find("const");
119+
if (Offset == StringRef::npos)
120+
return {};
121+
122+
SourceLocation Start = Range.getBegin().getLocWithOffset(Offset);
123+
return {Start, Start.getLocWithOffset(strlen("const") - 1)};
124+
}
125+
126+
void ConvertMemberFunctionsToStatic::check(
127+
const MatchFinder::MatchResult &Result) {
128+
const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x");
129+
130+
// TODO: For out-of-line declarations, don't modify the source if the header
131+
// is excluded by the -header-filter option.
132+
DiagnosticBuilder Diag =
133+
diag(Definition->getLocation(), "method %0 can be made static")
134+
<< Definition;
135+
136+
// TODO: Would need to remove those in a fix-it.
137+
if (Definition->getMethodQualifiers().hasVolatile() ||
138+
Definition->getMethodQualifiers().hasRestrict() ||
139+
Definition->getRefQualifier() != RQ_None)
140+
return;
141+
142+
const CXXMethodDecl *Declaration = Definition->getCanonicalDecl();
143+
144+
if (Definition->isConst()) {
145+
// Make sure that we either remove 'const' on both declaration and
146+
// definition or emit no fix-it at all.
147+
SourceRange DefConst = getLocationOfConst(Definition->getTypeSourceInfo(),
148+
*Result.SourceManager,
149+
Result.Context->getLangOpts());
150+
151+
if (DefConst.isInvalid())
152+
return;
153+
154+
if (Declaration != Definition) {
155+
SourceRange DeclConst = getLocationOfConst(
156+
Declaration->getTypeSourceInfo(), *Result.SourceManager,
157+
Result.Context->getLangOpts());
158+
159+
if (DeclConst.isInvalid())
160+
return;
161+
Diag << FixItHint::CreateRemoval(DeclConst);
162+
}
163+
164+
// Remove existing 'const' from both declaration and definition.
165+
Diag << FixItHint::CreateRemoval(DefConst);
166+
}
167+
Diag << FixItHint::CreateInsertion(Declaration->getBeginLoc(), "static ");
168+
}
169+
170+
} // namespace readability
171+
} // namespace tidy
172+
} // namespace clang
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- ConvertMemberFunctionsToStatic.h - clang-tidy ----------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H
11+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H
12+
13+
#include "../ClangTidy.h"
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace readability {
18+
19+
/// This check finds C++ class methods than can be made static
20+
/// because they don't use the 'this' pointer.
21+
///
22+
/// For the user-facing documentation see:
23+
/// http://clang.llvm.org/extra/clang-tidy/checks/
24+
/// readability-convert-member-functions-to-static.html
25+
class ConvertMemberFunctionsToStatic : public ClangTidyCheck {
26+
public:
27+
ConvertMemberFunctionsToStatic(StringRef Name, ClangTidyContext *Context)
28+
: ClangTidyCheck(Name, Context) {}
29+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
30+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
31+
};
32+
33+
} // namespace readability
34+
} // namespace tidy
35+
} // namespace clang
36+
37+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONVERTMEMFUNCTOSTATIC_H

clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "BracesAroundStatementsCheck.h"
1414
#include "ConstReturnTypeCheck.h"
1515
#include "ContainerSizeEmptyCheck.h"
16+
#include "ConvertMemberFunctionsToStatic.h"
1617
#include "DeleteNullPointerCheck.h"
1718
#include "DeletedDefaultCheck.h"
1819
#include "ElseAfterReturnCheck.h"
@@ -57,6 +58,8 @@ class ReadabilityModule : public ClangTidyModule {
5758
"readability-const-return-type");
5859
CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
5960
"readability-container-size-empty");
61+
CheckFactories.registerCheck<ConvertMemberFunctionsToStatic>(
62+
"readability-convert-member-functions-to-static");
6063
CheckFactories.registerCheck<DeleteNullPointerCheck>(
6164
"readability-delete-null-pointer");
6265
CheckFactories.registerCheck<DeletedDefaultCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ Improvements to clang-tidy
230230
If set to true, the check will provide fix-its with literal initializers
231231
(``int i = 0;``) instead of curly braces (``int i{};``).
232232

233+
- New :doc:`readability-convert-member-functions-to-static
234+
<clang-tidy/checks/readability-convert-member-functions-to-static>` check.
235+
236+
Finds non-static member functions that can be made ``static``.
237+
233238
Improvements to include-fixer
234239
-----------------------------
235240

clang-tools-extra/docs/clang-tidy/checks/list.rst

+1
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ Clang-Tidy Checks
257257
readability-braces-around-statements
258258
readability-const-return-type
259259
readability-container-size-empty
260+
readability-convert-member-functions-to-static
260261
readability-delete-null-pointer
261262
readability-deleted-default
262263
readability-else-after-return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.. title:: clang-tidy - readability-convert-member-functions-to-static
2+
3+
readability-convert-member-functions-to-static
4+
==============================================
5+
6+
Finds non-static member functions that can be made ``static``
7+
because the functions don't use ``this``.
8+
9+
After applying modifications as suggested by the check, runnnig the check again
10+
might find more opportunities to mark member functions ``static``.
11+
12+
After making a member function ``static``, you might want to run the check
13+
`readability-static-accessed-through-instance` to replace calls like
14+
``Instance.method()`` by ``Class::method()``.

0 commit comments

Comments
 (0)