Chromium Code Reviews| 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. | |
| 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 = | |
|
tfarina
2013/04/08 01:17:37
Unfortunatelly I got the following error when runn
| |
| 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 |