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