Chromium Code Reviews| 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 |