Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: tools/clang/empty_string/EmptyStringConverter.cpp

Issue 1385193002: Bisect clang Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: 246985 Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 <memory>
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Frontend/FrontendActions.h"
15 #include "clang/Tooling/CommonOptionsParser.h"
16 #include "clang/Tooling/Refactoring.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "llvm/Support/CommandLine.h"
19
20 using clang::ast_matchers::MatchFinder;
21 using clang::ast_matchers::argumentCountIs;
22 using clang::ast_matchers::bindTemporaryExpr;
23 using clang::ast_matchers::constructorDecl;
24 using clang::ast_matchers::constructExpr;
25 using clang::ast_matchers::defaultArgExpr;
26 using clang::ast_matchers::expr;
27 using clang::ast_matchers::forEach;
28 using clang::ast_matchers::has;
29 using clang::ast_matchers::hasArgument;
30 using clang::ast_matchers::hasDeclaration;
31 using clang::ast_matchers::hasName;
32 using clang::ast_matchers::id;
33 using clang::ast_matchers::methodDecl;
34 using clang::ast_matchers::newExpr;
35 using clang::ast_matchers::ofClass;
36 using clang::ast_matchers::stringLiteral;
37 using clang::ast_matchers::varDecl;
38 using clang::tooling::CommonOptionsParser;
39 using clang::tooling::Replacement;
40 using clang::tooling::Replacements;
41
42 namespace {
43
44 // Handles replacements for stack and heap-allocated instances, e.g.:
45 // std::string a("");
46 // std::string* b = new std::string("");
47 class ConstructorCallback : public MatchFinder::MatchCallback {
48 public:
49 ConstructorCallback(Replacements* replacements)
50 : replacements_(replacements) {}
51
52 virtual void run(const MatchFinder::MatchResult& result) override;
53
54 private:
55 Replacements* const replacements_;
56 };
57
58 // Handles replacements for invocations of std::string("") in an initializer
59 // list.
60 class InitializerCallback : public MatchFinder::MatchCallback {
61 public:
62 InitializerCallback(Replacements* replacements)
63 : replacements_(replacements) {}
64
65 virtual void run(const MatchFinder::MatchResult& result) override;
66
67 private:
68 Replacements* const replacements_;
69 };
70
71 // Handles replacements for invocations of std::string("") in a temporary
72 // context, e.g. FunctionThatTakesString(std::string("")). Note that this
73 // handles implicits construction of std::string as well.
74 class TemporaryCallback : public MatchFinder::MatchCallback {
75 public:
76 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {}
77
78 virtual void run(const MatchFinder::MatchResult& result) override;
79
80 private:
81 Replacements* const replacements_;
82 };
83
84 class EmptyStringConverter {
85 public:
86 explicit EmptyStringConverter(Replacements* replacements)
87 : constructor_callback_(replacements),
88 initializer_callback_(replacements),
89 temporary_callback_(replacements) {}
90
91 void SetupMatchers(MatchFinder* match_finder);
92
93 private:
94 ConstructorCallback constructor_callback_;
95 InitializerCallback initializer_callback_;
96 TemporaryCallback temporary_callback_;
97 };
98
99 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) {
100 const clang::ast_matchers::StatementMatcher& constructor_call =
101 id("call",
102 constructExpr(
103 hasDeclaration(methodDecl(ofClass(hasName("std::basic_string")))),
104 argumentCountIs(2),
105 hasArgument(0, id("literal", stringLiteral())),
106 hasArgument(1, defaultArgExpr())));
107
108 // Note that expr(has()) in the matcher is significant; the Clang AST wraps
109 // calls to the std::string constructor with exprWithCleanups nodes. Without
110 // the expr(has()) matcher, the first and last rules would not match anything!
111 match_finder->addMatcher(varDecl(forEach(expr(has(constructor_call)))),
112 &constructor_callback_);
113 match_finder->addMatcher(newExpr(has(constructor_call)),
114 &constructor_callback_);
115 match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)),
116 &temporary_callback_);
117 match_finder->addMatcher(
118 constructorDecl(forEach(expr(has(constructor_call)))),
119 &initializer_callback_);
120 }
121
122 void ConstructorCallback::run(const MatchFinder::MatchResult& result) {
123 const clang::StringLiteral* literal =
124 result.Nodes.getNodeAs<clang::StringLiteral>("literal");
125 if (literal->getLength() > 0)
126 return;
127
128 const clang::CXXConstructExpr* call =
129 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
130 clang::CharSourceRange range =
131 clang::CharSourceRange::getTokenRange(call->getParenOrBraceRange());
132 replacements_->insert(Replacement(*result.SourceManager, range, ""));
133 }
134
135 void InitializerCallback::run(const MatchFinder::MatchResult& result) {
136 const clang::StringLiteral* literal =
137 result.Nodes.getNodeAs<clang::StringLiteral>("literal");
138 if (literal->getLength() > 0)
139 return;
140
141 const clang::CXXConstructExpr* call =
142 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
143 replacements_->insert(Replacement(*result.SourceManager, call, ""));
144 }
145
146 void TemporaryCallback::run(const MatchFinder::MatchResult& result) {
147 const clang::StringLiteral* literal =
148 result.Nodes.getNodeAs<clang::StringLiteral>("literal");
149 if (literal->getLength() > 0)
150 return;
151
152 const clang::CXXConstructExpr* call =
153 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
154 // Differentiate between explicit and implicit calls to std::string's
155 // constructor. An implicitly generated constructor won't have a valid
156 // source range for the parenthesis. We do this because the matched expression
157 // for |call| in the explicit case doesn't include the closing parenthesis.
158 clang::SourceRange range = call->getParenOrBraceRange();
159 if (range.isValid()) {
160 replacements_->insert(Replacement(*result.SourceManager, literal, ""));
161 } else {
162 replacements_->insert(
163 Replacement(*result.SourceManager,
164 call,
165 literal->isWide() ? "std::wstring()" : "std::string()"));
166 }
167 }
168
169 } // namespace
170
171 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
172
173 int main(int argc, const char* argv[]) {
174 llvm::cl::OptionCategory category("EmptyString Tool");
175 CommonOptionsParser options(argc, argv, category);
176 clang::tooling::ClangTool tool(options.getCompilations(),
177 options.getSourcePathList());
178
179 Replacements replacements;
180 EmptyStringConverter converter(&replacements);
181 MatchFinder match_finder;
182 converter.SetupMatchers(&match_finder);
183
184 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory =
185 clang::tooling::newFrontendActionFactory(&match_finder);
186 int result = tool.run(frontend_factory.get());
187 if (result != 0)
188 return result;
189
190 // Each replacement line should have the following format:
191 // r:<file path>:<offset>:<length>:<replacement text>
192 // Only the <replacement text> field can contain embedded ":" characters.
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.
197 llvm::outs() << "==== BEGIN EDITS ====\n";
198 for (const auto& r : replacements) {
199 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() << ":::"
200 << r.getLength() << ":::" << r.getReplacementText() << "\n";
201 }
202 llvm::outs() << "==== END EDITS ====\n";
203
204 return 0;
205 }
OLDNEW
« no previous file with comments | « tools/clang/empty_string/CMakeLists.txt ('k') | tools/clang/empty_string/tests/test-expected.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698