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

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

Issue 12746010: Implement clang tool that converts std::string("") to std::string(). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More script cleanup Created 7 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/clang/empty_string/Makefile » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
Nico 2013/04/01 16:19:35 Add a comment somewhere that gives a short overvie
dcheng 2013/04/01 20:11:30 Done.
5 #include "clang/ASTMatchers/ASTMatchers.h"
6 #include "clang/ASTMatchers/ASTMatchFinder.h"
7 #include "clang/Basic/SourceManager.h"
8 #include "clang/Frontend/FrontendActions.h"
9 #include "clang/Tooling/CommonOptionsParser.h"
10 #include "clang/Tooling/Refactoring.h"
11 #include "clang/Tooling/Tooling.h"
12 #include "llvm/Support/CommandLine.h"
13
14 using clang::ast_matchers::MatchFinder;
15 using clang::ast_matchers::argumentCountIs;
16 using clang::ast_matchers::bindTemporaryExpr;
17 using clang::ast_matchers::constructorDecl;
18 using clang::ast_matchers::constructExpr;
19 using clang::ast_matchers::defaultArgExpr;
20 using clang::ast_matchers::expr;
21 using clang::ast_matchers::forEach;
22 using clang::ast_matchers::has;
23 using clang::ast_matchers::hasArgument;
24 using clang::ast_matchers::hasDeclaration;
25 using clang::ast_matchers::hasName;
26 using clang::ast_matchers::id;
27 using clang::ast_matchers::methodDecl;
28 using clang::ast_matchers::newExpr;
29 using clang::ast_matchers::ofClass;
30 using clang::ast_matchers::stringLiteral;
31 using clang::ast_matchers::varDecl;
32 using clang::tooling::CommonOptionsParser;
33 using clang::tooling::Replacement;
34 using clang::tooling::Replacements;
35
36 namespace {
37
38 enum ReplacementMode {
39 kConstructorMode,
40 kInitializerMode,
41 kTemporaryMode,
Nico 2013/04/01 16:19:35 nit: trailing commas in enums are a c++11 feature
dcheng 2013/04/01 20:11:30 And now it's gone =)
42 };
43
44 class EmptyStringConverterCallback : public MatchFinder::MatchCallback {
45 public:
46 EmptyStringConverterCallback(ReplacementMode mode, Replacements* replacements)
Nico 2013/04/01 16:19:35 Maybe you want to use llvm style instead of chromi
dcheng 2013/04/01 20:11:30 It's hard to say. The Chrome plugin uses Chromium
47 : mode_(mode), replacements_(replacements) {}
48
49 virtual void run(const MatchFinder::MatchResult& result);
Nico 2013/04/01 16:19:35 There's LLVM_OVERRIDE I think (which expands to no
dcheng 2013/04/01 20:11:30 Done.
50
51 private:
52 const ReplacementMode mode_;
53 Replacements* const replacements_;
54 };
55
56 class EmptyStringConverter {
57 public:
58 explicit EmptyStringConverter(Replacements* replacements)
59 : constructor_callback_(kConstructorMode, replacements),
60 initializer_callback_(kInitializerMode, replacements),
61 temporary_callback_(kTemporaryMode, replacements) {}
62
63 void SetupMatchers(MatchFinder* match_finder);
64
65 private:
66 EmptyStringConverterCallback constructor_callback_;
67 EmptyStringConverterCallback initializer_callback_;
68 EmptyStringConverterCallback temporary_callback_;
69 };
70
71 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) {
72 const auto& constructor_call =
Nico 2013/04/01 16:19:35 …oh, you're going with c++11 mode. nevermind the c
Nico 2013/04/01 16:19:35 Also, since this is meant to be a demo, maybe add
dcheng 2013/04/01 20:11:30 This was originally meant to be a demo... but ther
73 id("call",
74 constructExpr(
75 hasDeclaration(methodDecl(ofClass(hasName("std::basic_string")))),
76 argumentCountIs(2),
77 hasArgument(0, id("literal", stringLiteral())),
78 hasArgument(1, defaultArgExpr())));
79
80 // Note that expr(has()) in the matcher is significant; the Clang AST wraps
81 // calls to the std::string constructor with exprWithCleanups nodes. Without
82 // the expr(has()) matcher, the first and last rules would not match anything!
83 match_finder->addMatcher(varDecl(forEach(expr(has(constructor_call)))),
84 &constructor_callback_);
85 match_finder->addMatcher(newExpr(has(constructor_call)),
86 &constructor_callback_);
87 match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)),
88 &temporary_callback_);
89 match_finder->addMatcher(
90 constructorDecl(forEach(expr(has(constructor_call)))),
91 &initializer_callback_);
92 }
93
94 void EmptyStringConverterCallback::run(const MatchFinder::MatchResult& result) {
95 const clang::StringLiteral* literal =
96 result.Nodes.getNodeAs<clang::StringLiteral>("literal");
97 if (literal->getLength() > 0)
98 return;
99
100 const clang::CXXConstructExpr* call =
101 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
102
103 switch (mode_) {
klimek 2013/04/01 16:30:33 I'd use 3 classes instead of the switch.
dcheng 2013/04/01 20:11:30 Done.
104 case kConstructorMode: {
105 clang::CharSourceRange range =
106 clang::CharSourceRange::getTokenRange(call->getParenRange());
107 replacements_->insert(Replacement(*result.SourceManager, range, ""));
108 break;
109 }
110 case kInitializerMode: {
111 replacements_->insert(Replacement(*result.SourceManager, call, ""));
112 break;
113 }
114 case kTemporaryMode: {
115 // Differentiate between explicit and implicit calls to std::string's
116 // constructor. An implicitly generated constructor won't have a valid
117 // source range for the parenthesis.
118 clang::SourceRange range = call->getParenRange();
119 if (range.isValid()) {
120 replacements_->insert(Replacement(*result.SourceManager, literal, ""));
121 } else {
122 replacements_->insert(
123 Replacement(*result.SourceManager, call, "std::string()"));
124 }
125 break;
126 }
127 }
128 }
129
130 } // namespace
131
132 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
133
134 int main(int argc, const char* argv[]) {
135 CommonOptionsParser options(argc, argv);
136 clang::tooling::ClangTool tool(options.getCompilations(),
137 options.getSourcePathList());
138
139 Replacements replacements;
140 EmptyStringConverter converter(&replacements);
141 MatchFinder match_finder;
142 converter.SetupMatchers(&match_finder);
143
144 int result =
145 tool.run(clang::tooling::newFrontendActionFactory(&match_finder));
146 if (result != 0)
147 return result;
148
149 // Each replacement line should have the following format:
150 // r:<file path>:<offset>:<length>:<replacement text>
151 // Only the <replacement text> field can contain embedded ":" characters.
152 // TODO(dcheng): Use a more clever serialization.
153 llvm::outs() << "==== BEGIN EDITS ====\n";
154 for (const Replacement& r : replacements) {
155 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":"
156 << r.getLength() << ":" << r.getReplacementText() << "\n";
157 }
158 llvm::outs() << "==== END EDITS ====\n";
159
160 return 0;
161 }
OLDNEW
« no previous file with comments | « no previous file | tools/clang/empty_string/Makefile » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698