cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / tools / clang / empty_string / EmptyStringConverter.cpp
blob28cc602d21c7fe99d02577eb1ac97ddc40ffd49b
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.
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"
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;
42 namespace {
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) {}
52 virtual void run(const MatchFinder::MatchResult& result) override;
54 private:
55 Replacements* const replacements_;
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) {}
65 virtual void run(const MatchFinder::MatchResult& result) override;
67 private:
68 Replacements* const replacements_;
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) {}
78 virtual void run(const MatchFinder::MatchResult& result) override;
80 private:
81 Replacements* const replacements_;
84 class EmptyStringConverter {
85 public:
86 explicit EmptyStringConverter(Replacements* replacements)
87 : constructor_callback_(replacements),
88 initializer_callback_(replacements),
89 temporary_callback_(replacements) {}
91 void SetupMatchers(MatchFinder* match_finder);
93 private:
94 ConstructorCallback constructor_callback_;
95 InitializerCallback initializer_callback_;
96 TemporaryCallback temporary_callback_;
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())));
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_);
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;
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, ""));
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;
141 const clang::CXXConstructExpr* call =
142 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
143 replacements_->insert(Replacement(*result.SourceManager, call, ""));
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;
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()"));
169 } // namespace
171 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
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());
179 Replacements replacements;
180 EmptyStringConverter converter(&replacements);
181 MatchFinder match_finder;
182 converter.SetupMatchers(&match_finder);
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;
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";
202 llvm::outs() << "==== END EDITS ====\n";
204 return 0;