OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // This implements a Clang tool to convert all instances of std::string("") to | 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 | 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 | 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. | 8 // should be run using the tools/clang/scripts/run_tool.py helper. |
9 | 9 |
10 #include <memory> | |
10 #include "clang/ASTMatchers/ASTMatchers.h" | 11 #include "clang/ASTMatchers/ASTMatchers.h" |
11 #include "clang/ASTMatchers/ASTMatchFinder.h" | 12 #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 #include "clang/Basic/SourceManager.h" | 13 #include "clang/Basic/SourceManager.h" |
13 #include "clang/Frontend/FrontendActions.h" | 14 #include "clang/Frontend/FrontendActions.h" |
14 #include "clang/Tooling/CommonOptionsParser.h" | 15 #include "clang/Tooling/CommonOptionsParser.h" |
15 #include "clang/Tooling/Refactoring.h" | 16 #include "clang/Tooling/Refactoring.h" |
16 #include "clang/Tooling/Tooling.h" | 17 #include "clang/Tooling/Tooling.h" |
17 #include "llvm/Support/CommandLine.h" | 18 #include "llvm/Support/CommandLine.h" |
18 | 19 |
19 using clang::ast_matchers::MatchFinder; | 20 using clang::ast_matchers::MatchFinder; |
(...skipping 21 matching lines...) Expand all Loading... | |
41 namespace { | 42 namespace { |
42 | 43 |
43 // Handles replacements for stack and heap-allocated instances, e.g.: | 44 // Handles replacements for stack and heap-allocated instances, e.g.: |
44 // std::string a(""); | 45 // std::string a(""); |
45 // std::string* b = new std::string(""); | 46 // std::string* b = new std::string(""); |
46 class ConstructorCallback : public MatchFinder::MatchCallback { | 47 class ConstructorCallback : public MatchFinder::MatchCallback { |
47 public: | 48 public: |
48 ConstructorCallback(Replacements* replacements) | 49 ConstructorCallback(Replacements* replacements) |
49 : replacements_(replacements) {} | 50 : replacements_(replacements) {} |
50 | 51 |
51 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 52 virtual void run(const MatchFinder::MatchResult& result) override; |
52 | 53 |
53 private: | 54 private: |
54 Replacements* const replacements_; | 55 Replacements* const replacements_; |
55 }; | 56 }; |
56 | 57 |
57 // Handles replacements for invocations of std::string("") in an initializer | 58 // Handles replacements for invocations of std::string("") in an initializer |
58 // list. | 59 // list. |
59 class InitializerCallback : public MatchFinder::MatchCallback { | 60 class InitializerCallback : public MatchFinder::MatchCallback { |
60 public: | 61 public: |
61 InitializerCallback(Replacements* replacements) | 62 InitializerCallback(Replacements* replacements) |
62 : replacements_(replacements) {} | 63 : replacements_(replacements) {} |
63 | 64 |
64 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 65 virtual void run(const MatchFinder::MatchResult& result) override; |
65 | 66 |
66 private: | 67 private: |
67 Replacements* const replacements_; | 68 Replacements* const replacements_; |
68 }; | 69 }; |
69 | 70 |
70 // Handles replacements for invocations of std::string("") in a temporary | 71 // Handles replacements for invocations of std::string("") in a temporary |
71 // context, e.g. FunctionThatTakesString(std::string("")). Note that this | 72 // context, e.g. FunctionThatTakesString(std::string("")). Note that this |
72 // handles implicits construction of std::string as well. | 73 // handles implicits construction of std::string as well. |
73 class TemporaryCallback : public MatchFinder::MatchCallback { | 74 class TemporaryCallback : public MatchFinder::MatchCallback { |
74 public: | 75 public: |
75 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {} | 76 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {} |
76 | 77 |
77 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 78 virtual void run(const MatchFinder::MatchResult& result) override; |
78 | 79 |
79 private: | 80 private: |
80 Replacements* const replacements_; | 81 Replacements* const replacements_; |
81 }; | 82 }; |
82 | 83 |
83 class EmptyStringConverter { | 84 class EmptyStringConverter { |
84 public: | 85 public: |
85 explicit EmptyStringConverter(Replacements* replacements) | 86 explicit EmptyStringConverter(Replacements* replacements) |
86 : constructor_callback_(replacements), | 87 : constructor_callback_(replacements), |
87 initializer_callback_(replacements), | 88 initializer_callback_(replacements), |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 | 121 |
121 void ConstructorCallback::run(const MatchFinder::MatchResult& result) { | 122 void ConstructorCallback::run(const MatchFinder::MatchResult& result) { |
122 const clang::StringLiteral* literal = | 123 const clang::StringLiteral* literal = |
123 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 124 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); |
124 if (literal->getLength() > 0) | 125 if (literal->getLength() > 0) |
125 return; | 126 return; |
126 | 127 |
127 const clang::CXXConstructExpr* call = | 128 const clang::CXXConstructExpr* call = |
128 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 129 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); |
129 clang::CharSourceRange range = | 130 clang::CharSourceRange range = |
130 clang::CharSourceRange::getTokenRange(call->getParenRange()); | 131 clang::CharSourceRange::getTokenRange(call->getParenOrBraceRange()); |
131 replacements_->insert(Replacement(*result.SourceManager, range, "")); | 132 replacements_->insert(Replacement(*result.SourceManager, range, "")); |
132 } | 133 } |
133 | 134 |
134 void InitializerCallback::run(const MatchFinder::MatchResult& result) { | 135 void InitializerCallback::run(const MatchFinder::MatchResult& result) { |
135 const clang::StringLiteral* literal = | 136 const clang::StringLiteral* literal = |
136 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 137 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); |
137 if (literal->getLength() > 0) | 138 if (literal->getLength() > 0) |
138 return; | 139 return; |
139 | 140 |
140 const clang::CXXConstructExpr* call = | 141 const clang::CXXConstructExpr* call = |
141 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 142 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); |
142 replacements_->insert(Replacement(*result.SourceManager, call, "")); | 143 replacements_->insert(Replacement(*result.SourceManager, call, "")); |
143 } | 144 } |
144 | 145 |
145 void TemporaryCallback::run(const MatchFinder::MatchResult& result) { | 146 void TemporaryCallback::run(const MatchFinder::MatchResult& result) { |
146 const clang::StringLiteral* literal = | 147 const clang::StringLiteral* literal = |
147 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 148 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); |
148 if (literal->getLength() > 0) | 149 if (literal->getLength() > 0) |
149 return; | 150 return; |
150 | 151 |
151 const clang::CXXConstructExpr* call = | 152 const clang::CXXConstructExpr* call = |
152 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 153 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); |
153 // Differentiate between explicit and implicit calls to std::string's | 154 // Differentiate between explicit and implicit calls to std::string's |
154 // constructor. An implicitly generated constructor won't have a valid | 155 // constructor. An implicitly generated constructor won't have a valid |
155 // source range for the parenthesis. We do this because the matched expression | 156 // source range for the parenthesis. We do this because the matched expression |
156 // for |call| in the explicit case doesn't include the closing parenthesis. | 157 // for |call| in the explicit case doesn't include the closing parenthesis. |
157 clang::SourceRange range = call->getParenRange(); | 158 clang::SourceRange range = call->getParenOrBraceRange(); |
158 if (range.isValid()) { | 159 if (range.isValid()) { |
159 replacements_->insert(Replacement(*result.SourceManager, literal, "")); | 160 replacements_->insert(Replacement(*result.SourceManager, literal, "")); |
160 } else { | 161 } else { |
161 replacements_->insert( | 162 replacements_->insert( |
162 Replacement(*result.SourceManager, | 163 Replacement(*result.SourceManager, |
163 call, | 164 call, |
164 literal->isWide() ? "std::wstring()" : "std::string()")); | 165 literal->isWide() ? "std::wstring()" : "std::string()")); |
165 } | 166 } |
166 } | 167 } |
167 | 168 |
168 } // namespace | 169 } // namespace |
169 | 170 |
170 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 171 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
171 | 172 |
172 int main(int argc, const char* argv[]) { | 173 int main(int argc, const char* argv[]) { |
173 CommonOptionsParser options(argc, argv); | 174 llvm::cl::OptionCategory category("EmptyString Tool"); |
175 CommonOptionsParser options(argc, argv, category); | |
174 clang::tooling::ClangTool tool(options.getCompilations(), | 176 clang::tooling::ClangTool tool(options.getCompilations(), |
175 options.getSourcePathList()); | 177 options.getSourcePathList()); |
176 | 178 |
177 Replacements replacements; | 179 Replacements replacements; |
178 EmptyStringConverter converter(&replacements); | 180 EmptyStringConverter converter(&replacements); |
179 MatchFinder match_finder; | 181 MatchFinder match_finder; |
180 converter.SetupMatchers(&match_finder); | 182 converter.SetupMatchers(&match_finder); |
181 | 183 |
182 int result = | 184 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
183 tool.run(clang::tooling::newFrontendActionFactory(&match_finder)); | 185 clang::tooling::newFrontendActionFactory(&match_finder); |
Nico
2014/08/06 18:15:56
Does c++11 library stuff work in tools? Does the b
dcheng
2014/08/06 20:06:54
The build system expects a "modern" C++ library to
| |
186 int result = tool.run(frontend_factory.get()); | |
184 if (result != 0) | 187 if (result != 0) |
185 return result; | 188 return result; |
186 | 189 |
187 // Each replacement line should have the following format: | 190 // Each replacement line should have the following format: |
188 // r:<file path>:<offset>:<length>:<replacement text> | 191 // r:<file path>:<offset>:<length>:<replacement text> |
189 // Only the <replacement text> field can contain embedded ":" characters. | 192 // Only the <replacement text> field can contain embedded ":" characters. |
190 // TODO(dcheng): Use a more clever serialization. | 193 // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML |
194 // serialization and then use clang-apply-replacements, but that would require | |
195 // copying and pasting a larger amount of boilerplate for all Chrome clang | |
196 // tools. | |
191 llvm::outs() << "==== BEGIN EDITS ====\n"; | 197 llvm::outs() << "==== BEGIN EDITS ====\n"; |
192 for (Replacements::const_iterator it = replacements.begin(); | 198 for (const auto& r : replacements) { |
193 it != replacements.end(); ++it) { | 199 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" |
194 llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":" | 200 << r.getLength() << ":" << r.getReplacementText() << "\n"; |
195 << it->getLength() << ":" << it->getReplacementText() << "\n"; | |
196 } | 201 } |
197 llvm::outs() << "==== END EDITS ====\n"; | 202 llvm::outs() << "==== END EDITS ====\n"; |
198 | 203 |
199 return 0; | 204 return 0; |
200 } | 205 } |
OLD | NEW |