OLD | NEW |
---|---|
(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 } | |
OLD | NEW |