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 |