| 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 "clang/ASTMatchers/ASTMatchers.h" | 10 #include "clang/ASTMatchers/ASTMatchers.h" |
| 11 #include "clang/ASTMatchers/ASTMatchFinder.h" | 11 #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 12 #include "clang/Basic/SourceManager.h" | 12 #include "clang/Basic/SourceManager.h" |
| 13 #include "clang/Frontend/FrontendActions.h" | 13 #include "clang/Frontend/FrontendActions.h" |
| 14 #include "clang/Tooling/CommonOptionsParser.h" | 14 #include "clang/Tooling/CommonOptionsParser.h" |
| 15 #include "clang/Tooling/Refactoring.h" | 15 #include "clang/Tooling/Refactoring.h" |
| 16 #include "clang/Tooling/Tooling.h" | 16 #include "clang/Tooling/Tooling.h" |
| 17 #include "llvm/Support/CommandLine.h" | 17 #include "llvm/Support/CommandLine.h" |
| 18 | 18 |
| 19 using clang::ast_matchers::MatchFinder; | 19 using clang::ast_matchers::MatchFinder; |
| 20 using clang::ast_matchers::argumentCountIs; | 20 using clang::ast_matchers::argumentCountIs; |
| 21 using clang::ast_matchers::bindTemporaryExpr; | 21 using clang::ast_matchers::bindTemporaryExpr; |
| 22 using clang::ast_matchers::constructorDecl; | 22 using clang::ast_matchers::constructorDecl; |
| 23 using clang::ast_matchers::constructExpr; | 23 using clang::ast_matchers::constructExpr; |
| 24 using clang::ast_matchers::defaultArgExpr; | |
| 25 using clang::ast_matchers::expr; | 24 using clang::ast_matchers::expr; |
| 26 using clang::ast_matchers::forEach; | 25 using clang::ast_matchers::forEach; |
| 27 using clang::ast_matchers::has; | 26 using clang::ast_matchers::has; |
| 28 using clang::ast_matchers::hasArgument; | 27 using clang::ast_matchers::hasArgument; |
| 29 using clang::ast_matchers::hasDeclaration; | 28 using clang::ast_matchers::hasDeclaration; |
| 30 using clang::ast_matchers::hasName; | 29 using clang::ast_matchers::matchesName; |
| 31 using clang::ast_matchers::id; | 30 using clang::ast_matchers::id; |
| 32 using clang::ast_matchers::methodDecl; | 31 using clang::ast_matchers::methodDecl; |
| 33 using clang::ast_matchers::newExpr; | 32 using clang::ast_matchers::newExpr; |
| 34 using clang::ast_matchers::ofClass; | 33 using clang::ast_matchers::ofClass; |
| 35 using clang::ast_matchers::stringLiteral; | |
| 36 using clang::ast_matchers::varDecl; | 34 using clang::ast_matchers::varDecl; |
| 37 using clang::tooling::CommonOptionsParser; | 35 using clang::tooling::CommonOptionsParser; |
| 38 using clang::tooling::Replacement; | 36 using clang::tooling::Replacement; |
| 39 using clang::tooling::Replacements; | 37 using clang::tooling::Replacements; |
| 40 | 38 |
| 41 namespace { | 39 namespace { |
| 42 | 40 |
| 41 bool IsNullConstant(const clang::Expr& expr, clang::ASTContext* context) { |
| 42 return expr.isNullPointerConstant(*context, |
| 43 clang::Expr::NPC_ValueDependentIsNotNull) != |
| 44 clang::Expr::NPCK_NotNull; |
| 45 } |
| 46 |
| 43 // Handles replacements for stack and heap-allocated instances, e.g.: | 47 // Handles replacements for stack and heap-allocated instances, e.g.: |
| 44 // std::string a(""); | 48 // scoped_ptr<T> a(NULL); |
| 45 // std::string* b = new std::string(""); | 49 // scoped_ptr<T>* b = new scoped_ptr<T>(NULL); |
| 50 // ...though the latter should be pretty rare. |
| 46 class ConstructorCallback : public MatchFinder::MatchCallback { | 51 class ConstructorCallback : public MatchFinder::MatchCallback { |
| 47 public: | 52 public: |
| 48 ConstructorCallback(Replacements* replacements) | 53 ConstructorCallback(Replacements* replacements) |
| 49 : replacements_(replacements) {} | 54 : replacements_(replacements) {} |
| 50 | 55 |
| 51 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 56 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; |
| 52 | 57 |
| 53 private: | 58 private: |
| 54 Replacements* const replacements_; | 59 Replacements* const replacements_; |
| 55 }; | 60 }; |
| 56 | 61 |
| 57 // Handles replacements for invocations of std::string("") in an initializer | 62 // Handles replacements for invocations of scoped_ptr<T>(NULL) in an initializer |
| 58 // list. | 63 // list. |
| 59 class InitializerCallback : public MatchFinder::MatchCallback { | 64 class InitializerCallback : public MatchFinder::MatchCallback { |
| 60 public: | 65 public: |
| 61 InitializerCallback(Replacements* replacements) | 66 InitializerCallback(Replacements* replacements) |
| 62 : replacements_(replacements) {} | 67 : replacements_(replacements) {} |
| 63 | 68 |
| 64 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 69 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; |
| 65 | 70 |
| 66 private: | 71 private: |
| 67 Replacements* const replacements_; | 72 Replacements* const replacements_; |
| 68 }; | 73 }; |
| 69 | 74 |
| 70 // Handles replacements for invocations of std::string("") in a temporary | 75 // Handles replacements for invocations of scoped_ptr<T>(NULL) in a temporary |
| 71 // context, e.g. FunctionThatTakesString(std::string("")). Note that this | 76 // context, e.g. return scoped_ptr<T>(NULL). |
| 72 // handles implicits construction of std::string as well. | |
| 73 class TemporaryCallback : public MatchFinder::MatchCallback { | 77 class TemporaryCallback : public MatchFinder::MatchCallback { |
| 74 public: | 78 public: |
| 75 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {} | 79 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {} |
| 76 | 80 |
| 77 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; | 81 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE; |
| 78 | 82 |
| 79 private: | 83 private: |
| 80 Replacements* const replacements_; | 84 Replacements* const replacements_; |
| 81 }; | 85 }; |
| 82 | 86 |
| 83 class EmptyStringConverter { | 87 class EmptyStringConverter { |
| 84 public: | 88 public: |
| 85 explicit EmptyStringConverter(Replacements* replacements) | 89 explicit EmptyStringConverter(Replacements* replacements) |
| 86 : constructor_callback_(replacements), | 90 : constructor_callback_(replacements), |
| 87 initializer_callback_(replacements), | 91 initializer_callback_(replacements), |
| 88 temporary_callback_(replacements) {} | 92 temporary_callback_(replacements) {} |
| 89 | 93 |
| 90 void SetupMatchers(MatchFinder* match_finder); | 94 void SetupMatchers(MatchFinder* match_finder); |
| 91 | 95 |
| 92 private: | 96 private: |
| 93 ConstructorCallback constructor_callback_; | 97 ConstructorCallback constructor_callback_; |
| 94 InitializerCallback initializer_callback_; | 98 InitializerCallback initializer_callback_; |
| 95 TemporaryCallback temporary_callback_; | 99 TemporaryCallback temporary_callback_; |
| 96 }; | 100 }; |
| 97 | 101 |
| 98 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) { | 102 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) { |
| 99 const clang::ast_matchers::StatementMatcher& constructor_call = | 103 const char kPattern[] = "^::(scoped_ptr|scoped_ptr_malloc)$"; |
| 100 id("call", | 104 const clang::ast_matchers::StatementMatcher& constructor_call = id( |
| 101 constructExpr( | 105 "call", |
| 102 hasDeclaration(methodDecl(ofClass(hasName("std::basic_string")))), | 106 constructExpr(hasDeclaration(methodDecl(ofClass(matchesName(kPattern)))), |
| 103 argumentCountIs(2), | 107 argumentCountIs(1), |
| 104 hasArgument(0, id("literal", stringLiteral())), | 108 hasArgument(0, id("arg", expr())))); |
| 105 hasArgument(1, defaultArgExpr()))); | |
| 106 | 109 |
| 107 // Note that expr(has()) in the matcher is significant; the Clang AST wraps | 110 match_finder->addMatcher(varDecl(forEach(constructor_call)), |
| 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_); | 111 &constructor_callback_); |
| 112 match_finder->addMatcher(newExpr(has(constructor_call)), | 112 match_finder->addMatcher(newExpr(has(constructor_call)), |
| 113 &constructor_callback_); | 113 &constructor_callback_); |
| 114 match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)), | 114 match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)), |
| 115 &temporary_callback_); | 115 &temporary_callback_); |
| 116 match_finder->addMatcher( | 116 match_finder->addMatcher(constructorDecl(forEach(constructor_call)), |
| 117 constructorDecl(forEach(expr(has(constructor_call)))), | 117 &initializer_callback_); |
| 118 &initializer_callback_); | |
| 119 } | 118 } |
| 120 | 119 |
| 121 void ConstructorCallback::run(const MatchFinder::MatchResult& result) { | 120 void ConstructorCallback::run(const MatchFinder::MatchResult& result) { |
| 122 const clang::StringLiteral* literal = | 121 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); |
| 123 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 122 if (!IsNullConstant(*arg, result.Context)) |
| 124 if (literal->getLength() > 0) | |
| 125 return; | 123 return; |
| 126 | 124 |
| 127 const clang::CXXConstructExpr* call = | 125 const clang::CXXConstructExpr* call = |
| 128 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 126 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); |
| 129 clang::CharSourceRange range = | 127 clang::CharSourceRange range = |
| 130 clang::CharSourceRange::getTokenRange(call->getParenRange()); | 128 clang::CharSourceRange::getTokenRange(call->getParenRange()); |
| 131 replacements_->insert(Replacement(*result.SourceManager, range, "")); | 129 replacements_->insert(Replacement(*result.SourceManager, range, "")); |
| 132 } | 130 } |
| 133 | 131 |
| 134 void InitializerCallback::run(const MatchFinder::MatchResult& result) { | 132 void InitializerCallback::run(const MatchFinder::MatchResult& result) { |
| 135 const clang::StringLiteral* literal = | 133 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); |
| 136 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 134 if (!IsNullConstant(*arg, result.Context)) |
| 137 if (literal->getLength() > 0) | |
| 138 return; | 135 return; |
| 139 | 136 |
| 140 const clang::CXXConstructExpr* call = | 137 const clang::CXXConstructExpr* call = |
| 141 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 138 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); |
| 142 replacements_->insert(Replacement(*result.SourceManager, call, "")); | 139 replacements_->insert(Replacement(*result.SourceManager, call, "")); |
| 143 } | 140 } |
| 144 | 141 |
| 145 void TemporaryCallback::run(const MatchFinder::MatchResult& result) { | 142 void TemporaryCallback::run(const MatchFinder::MatchResult& result) { |
| 146 const clang::StringLiteral* literal = | 143 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); |
| 147 result.Nodes.getNodeAs<clang::StringLiteral>("literal"); | 144 if (!IsNullConstant(*arg, result.Context)) |
| 148 if (literal->getLength() > 0) | |
| 149 return; | 145 return; |
| 150 | 146 |
| 151 const clang::CXXConstructExpr* call = | 147 // TODO(dcheng): File a bug with clang. There should be an easier way to do |
| 152 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call"); | 148 // this replacement, but getTokenRange(call->getParenRange()) and the obvious |
| 153 // Differentiate between explicit and implicit calls to std::string's | 149 // (but incorrect) arg both don't work. The former is presumably just buggy, |
| 154 // constructor. An implicitly generated constructor won't have a valid | 150 // while the latter probably has to do with the fact that NULL is actually a |
| 155 // source range for the parenthesis. We do this because the matched expression | 151 // macro which expands to a built-in. |
| 156 // for |call| in the explicit case doesn't include the closing parenthesis. | 152 clang::SourceRange range = arg->getSourceRange(); |
| 157 clang::SourceRange range = call->getParenRange(); | 153 clang::SourceRange expansion_range( |
| 158 if (range.isValid()) { | 154 result.SourceManager->getExpansionLoc(range.getBegin()), |
| 159 replacements_->insert(Replacement(*result.SourceManager, literal, "")); | 155 result.SourceManager->getExpansionLoc(range.getEnd())); |
| 160 } else { | 156 replacements_->insert( |
| 161 replacements_->insert( | 157 Replacement(*result.SourceManager, |
| 162 Replacement(*result.SourceManager, | 158 clang::CharSourceRange::getTokenRange(expansion_range), |
| 163 call, | 159 "")); |
| 164 literal->isWide() ? "std::wstring()" : "std::string()")); | |
| 165 } | |
| 166 } | 160 } |
| 167 | 161 |
| 168 } // namespace | 162 } // namespace |
| 169 | 163 |
| 170 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 164 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
| 171 | 165 |
| 172 int main(int argc, const char* argv[]) { | 166 int main(int argc, const char* argv[]) { |
| 173 CommonOptionsParser options(argc, argv); | 167 CommonOptionsParser options(argc, argv); |
| 174 clang::tooling::ClangTool tool(options.getCompilations(), | 168 clang::tooling::ClangTool tool(options.getCompilations(), |
| 175 options.getSourcePathList()); | 169 options.getSourcePathList()); |
| 176 | 170 |
| 177 Replacements replacements; | 171 Replacements replacements; |
| 178 EmptyStringConverter converter(&replacements); | 172 EmptyStringConverter converter(&replacements); |
| 179 MatchFinder match_finder; | 173 MatchFinder match_finder; |
| 180 converter.SetupMatchers(&match_finder); | 174 converter.SetupMatchers(&match_finder); |
| 181 | 175 |
| 182 int result = | 176 int result = |
| 183 tool.run(clang::tooling::newFrontendActionFactory(&match_finder)); | 177 tool.run(clang::tooling::newFrontendActionFactory(&match_finder)); |
| 184 if (result != 0) | 178 if (result != 0) |
| 185 return result; | 179 return result; |
| 186 | 180 |
| 187 // Each replacement line should have the following format: | 181 // Each replacement line should have the following format: |
| 188 // r:<file path>:<offset>:<length>:<replacement text> | 182 // r:<file path>:<offset>:<length>:<replacement text> |
| 189 // Only the <replacement text> field can contain embedded ":" characters. | 183 // Only the <replacement text> field can contain embedded ":" characters. |
| 190 // TODO(dcheng): Use a more clever serialization. | 184 // TODO(dcheng): Use a more clever serialization. |
| 191 llvm::outs() << "==== BEGIN EDITS ====\n"; | 185 llvm::outs() << "==== BEGIN EDITS ====\n"; |
| 192 for (Replacements::const_iterator it = replacements.begin(); | 186 for (Replacements::const_iterator it = replacements.begin(); |
| 193 it != replacements.end(); ++it) { | 187 it != replacements.end(); |
| 188 ++it) { |
| 194 llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":" | 189 llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":" |
| 195 << it->getLength() << ":" << it->getReplacementText() << "\n"; | 190 << it->getLength() << ":" << it->getReplacementText() << "\n"; |
| 196 } | 191 } |
| 197 llvm::outs() << "==== END EDITS ====\n"; | 192 llvm::outs() << "==== END EDITS ====\n"; |
| 198 | 193 |
| 199 return 0; | 194 return 0; |
| 200 } | 195 } |
| OLD | NEW |