OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 // | |
5 // This implements a Clang tool to convert all instances of std::string("") to | |
6 // std::string(). The latter is more efficient (as std::string doesn't have to | |
7 // take a copy of an empty string) and generates fewer instructions as well. It | |
8 // should be run using the tools/clang/scripts/run_tool.py helper. | |
Nico
2013/04/02 21:37:29
Either this or run_tool.py should have an example
| |
9 | |
10 #include "clang/ASTMatchers/ASTMatchers.h" | |
11 #include "clang/ASTMatchers/ASTMatchFinder.h" | |
12 #include "clang/Basic/SourceManager.h" | |
13 #include "clang/Frontend/FrontendActions.h" | |
14 #include "clang/Tooling/CommonOptionsParser.h" | |
15 #include "clang/Tooling/Refactoring.h" | |
16 #include "clang/Tooling/Tooling.h" | |
17 #include "llvm/Support/CommandLine.h" | |
18 | |
19 using clang::ast_matchers::MatchFinder; | |
20 using clang::ast_matchers::argumentCountIs; | |
21 using clang::ast_matchers::bindTemporaryExpr; | |
22 using clang::ast_matchers::constructorDecl; | |
23 using clang::ast_matchers::constructExpr; | |
24 using clang::ast_matchers::defaultArgExpr; | |
25 using clang::ast_matchers::expr; | |
26 using clang::ast_matchers::forEach; | |
27 using clang::ast_matchers::has; | |
28 using clang::ast_matchers::hasArgument; | |
29 using clang::ast_matchers::hasDeclaration; | |
30 using clang::ast_matchers::hasName; | |
31 using clang::ast_matchers::id; | |
32 using clang::ast_matchers::methodDecl; | |
33 using clang::ast_matchers::newExpr; | |
34 using clang::ast_matchers::ofClass; | |
35 using clang::ast_matchers::stringLiteral; | |
36 using clang::ast_matchers::varDecl; | |
37 using clang::tooling::CommonOptionsParser; | |
38 using clang::tooling::Replacement; | |
39 using clang::tooling::Replacements; | |
40 | |
41 namespace { | |
42 | |
43 // Handles replacements for stack and heap-allocated instances, e.g.: | |
44 // std::string a(""); | |
45 // std::string* b = new std::string(""); | |
46 class ConstructorCallback : public MatchFinder::MatchCallback { | |
47 public: | |
48 ConstructorCallback(Replacements* replacements) | |
49 : replacements_(replacements) {} | |
50 | |
51 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | |
52 | |
53 private: | |
54 Replacements* const replacements_; | |
55 }; | |
56 | |
57 // Handles replacements for invocations of std::string("") in an initializer | |
58 // list. | |
59 class InitializerCallback : public MatchFinder::MatchCallback { | |
60 public: | |
61 InitializerCallback(Replacements* replacements) | |
62 : replacements_(replacements) {} | |
63 | |
64 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | |
65 | |
66 private: | |
67 Replacements* const replacements_; | |
68 }; | |
69 | |
70 // Handles replacements for invocations of std::string("") in a temporary | |
71 // context, e.g. FunctionThatTakesString(std::string("")). Note that this | |
72 // handles implicits construction of std::string as well. | |
73 class TemporaryCallback : public MatchFinder::MatchCallback { | |
74 public: | |
75 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {} | |
76 | |
77 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | |
78 | |
79 private: | |
80 Replacements* const replacements_; | |
81 }; | |
82 | |
83 class EmptyStringConverter { | |
84 public: | |
85 explicit EmptyStringConverter(Replacements* replacements) | |
86 : constructor_callback_(replacements), | |
87 initializer_callback_(replacements), | |
88 temporary_callback_(replacements) {} | |
89 | |
90 void SetupMatchers(MatchFinder* match_finder); | |
91 | |
92 private: | |
93 ConstructorCallback constructor_callback_; | |
94 InitializerCallback initializer_callback_; | |
95 TemporaryCallback temporary_callback_; | |
96 }; | |
97 | |
98 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) { | |
99 const auto& constructor_call = | |
100 id("call", | |
101 constructExpr( | |
102 hasDeclaration(methodDecl(ofClass(hasName("std::basic_string")))), | |
103 argumentCountIs(2), | |
104 hasArgument(0, id("literal", stringLiteral())), | |
105 hasArgument(1, defaultArgExpr()))); | |
106 | |
107 // Note that expr(has()) in the matcher is significant; the Clang AST wraps | |
108 // calls to the std::string constructor with exprWithCleanups nodes. Without | |
109 // the expr(has()) matcher, the first and last rules would not match anything! | |
110 match_finder->addMatcher(varDecl(forEach(expr(has(constructor_call)))), | |
111 &constructor_callback_); | |
112 match_finder->addMatcher(newExpr(has(constructor_call)), | |
113 &constructor_callback_); | |
114 match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)), | |
115 &temporary_callback_); | |
116 match_finder->addMatcher( | |
117 constructorDecl(forEach(expr(has(constructor_call)))), | |
118 &initializer_callback_); | |
119 } | |
120 | |
121 void ConstructorCallback::run(const MatchFinder::MatchResult& result) { | |
122 const clang::StringLiteral* literal = | |
123 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | |
124 if (literal->getLength() > 0) | |
125 return; | |
126 | |
127 const clang::CXXConstructExpr* call = | |
128 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | |
129 clang::CharSourceRange range = | |
130 clang::CharSourceRange::getTokenRange(call->getParenRange()); | |
131 replacements_->insert(Replacement(*result.SourceManager, range, "")); | |
132 } | |
133 | |
134 void InitializerCallback::run(const MatchFinder::MatchResult& result) { | |
135 const clang::StringLiteral* literal = | |
136 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | |
137 if (literal->getLength() > 0) | |
138 return; | |
139 | |
140 const clang::CXXConstructExpr* call = | |
141 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | |
142 replacements_->insert(Replacement(*result.SourceManager, call, "")); | |
143 } | |
144 | |
145 void TemporaryCallback::run(const MatchFinder::MatchResult& result) { | |
146 const clang::StringLiteral* literal = | |
147 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | |
148 if (literal->getLength() > 0) | |
149 return; | |
150 | |
151 const clang::CXXConstructExpr* call = | |
152 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | |
153 // Differentiate between explicit and implicit calls to std::string's | |
154 // constructor. An implicitly generated constructor won't have a valid | |
155 // source range for the parenthesis. | |
156 clang::SourceRange range = call->getParenRange(); | |
157 if (range.isValid()) { | |
158 replacements_->insert(Replacement(*result.SourceManager, literal, "")); | |
159 } else { | |
160 replacements_->insert( | |
161 Replacement(*result.SourceManager, call, "std::string()")); | |
162 } | |
163 } | |
164 | |
165 } // namespace | |
166 | |
167 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | |
168 | |
169 int main(int argc, const char* argv[]) { | |
170 CommonOptionsParser options(argc, argv); | |
171 clang::tooling::ClangTool tool(options.getCompilations(), | |
172 options.getSourcePathList()); | |
173 | |
174 Replacements replacements; | |
175 EmptyStringConverter converter(&replacements); | |
176 MatchFinder match_finder; | |
177 converter.SetupMatchers(&match_finder); | |
178 | |
179 int result = | |
180 tool.run(clang::tooling::newFrontendActionFactory(&match_finder)); | |
181 if (result != 0) | |
182 return result; | |
183 | |
184 // Each replacement line should have the following format: | |
185 // r:<file path>:<offset>:<length>:<replacement text> | |
186 // Only the <replacement text> field can contain embedded ":" characters. | |
187 // TODO(dcheng): Use a more clever serialization. | |
188 llvm::outs() << "==== BEGIN EDITS ====\n"; | |
189 for (const Replacement& r : replacements) { | |
190 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" | |
191 << r.getLength() << ":" << r.getReplacementText() << "\n"; | |
192 } | |
193 llvm::outs() << "==== END EDITS ====\n"; | |
194 | |
195 return 0; | |
196 } | |
OLD | NEW |