annotate clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp @ 214:0cf2d4ade63d

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