1 // Copyright 2016 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 #include <assert.h>
6 #include <stdlib.h>
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/ParentMap.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/ASTMatchers/ASTMatchersMacros.h"
16 #include "clang/Analysis/CFG.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Frontend/FrontendActions.h"
19 #include "clang/Lex/Lexer.h"
20 #include "clang/Tooling/CommonOptionsParser.h"
21 #include "clang/Tooling/Refactoring.h"
22 #include "clang/Tooling/Tooling.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/TargetSelect.h"
25
26 using Replacements = std::vector<clang::tooling::Replacement>;
27 using clang::ASTContext;
28 using clang::CFG;
29 using clang::CFGBlock;
30 using clang::CFGLifetimeEnds;
31 using clang::CFGStmt;
32 using clang::CallExpr;
33 using clang::Decl;
34 using clang::DeclRefExpr;
35 using clang::FunctionDecl;
36 using clang::LambdaExpr;
37 using clang::Stmt;
38 using clang::UnaryOperator;
39 using clang::ast_type_traits::DynTypedNode;
40 using clang::tooling::CommonOptionsParser;
41 using namespace clang::ast_matchers;
42
43 namespace {
44
45 class Rewriter {
46 public:
~Rewriter()47 virtual ~Rewriter() {}
48 };
49
50 // Removes unneeded base::Passed() on a parameter of base::BindOnce().
51 // Example:
52 // // Before
53 // base::BindOnce(&Foo, base::Passed(&bar));
54 // base::BindOnce(&Foo, base::Passed(std::move(baz)));
55 // base::BindOnce(&Foo, base::Passed(qux));
56 //
57 // // After
58 // base::BindOnce(&Foo, std::move(bar));
59 // base::BindOnce(&Foo, std::move(baz));
60 // base::BindOnce(&Foo, std::move(*qux));
61 class PassedToMoveRewriter : public MatchFinder::MatchCallback,
62 public Rewriter {
63 public:
PassedToMoveRewriter(Replacements * replacements)64 explicit PassedToMoveRewriter(Replacements* replacements)
65 : replacements_(replacements) {}
66
GetMatcher()67 StatementMatcher GetMatcher() {
68 auto is_passed = namedDecl(hasName("::base::Passed"));
69 auto is_bind_once_call = callee(namedDecl(hasName("::base::BindOnce")));
70
71 // Matches base::Passed() call on a base::BindOnce() argument.
72 return callExpr(is_bind_once_call,
73 hasAnyArgument(ignoringImplicit(
74 callExpr(callee(is_passed)).bind("target"))));
75 }
76
run(const MatchFinder::MatchResult & result)77 void run(const MatchFinder::MatchResult& result) override {
78 auto* target = result.Nodes.getNodeAs<CallExpr>("target");
79 auto* callee = target->getCallee()->IgnoreImpCasts();
80
81 auto* callee_decl = clang::dyn_cast<DeclRefExpr>(callee)->getDecl();
82 auto* passed_decl = clang::dyn_cast<FunctionDecl>(callee_decl);
83 auto* param_type = passed_decl->getParamDecl(0)->getType().getTypePtr();
84
85 if (param_type->isRValueReferenceType()) {
86 // base::Passed(xxx) -> xxx.
87 // The parameter type is already an rvalue reference.
88 // Example:
89 // std::unique_ptr<int> foo();
90 // std::unique_ptr<int> bar;
91 // base::Passed(foo());
92 // base::Passed(std::move(bar));
93 // In these cases, we can just remove base::Passed.
94 auto left = clang::CharSourceRange::getTokenRange(
95 result.SourceManager->getSpellingLoc(target->getBeginLoc()),
96 result.SourceManager->getSpellingLoc(target->getArg(0)->getExprLoc())
97 .getLocWithOffset(-1));
98 auto r_paren = clang::CharSourceRange::getTokenRange(
99 result.SourceManager->getSpellingLoc(target->getRParenLoc()),
100 result.SourceManager->getSpellingLoc(target->getRParenLoc()));
101 replacements_->emplace_back(*result.SourceManager, left, " ");
102 replacements_->emplace_back(*result.SourceManager, r_paren, " ");
103 return;
104 }
105
106 if (!param_type->isPointerType())
107 return;
108
109 auto* passed_arg = target->getArg(0)->IgnoreImpCasts();
110 if (auto* unary = clang::dyn_cast<clang::UnaryOperator>(passed_arg)) {
111 if (unary->getOpcode() == clang::UO_AddrOf) {
112 // base::Passed(&xxx) -> std::move(xxx).
113 auto left = clang::CharSourceRange::getTokenRange(
114 result.SourceManager->getSpellingLoc(target->getBeginLoc()),
115 result.SourceManager->getSpellingLoc(
116 target->getArg(0)->getExprLoc()));
117 replacements_->emplace_back(*result.SourceManager, left, "std::move(");
118 return;
119 }
120 }
121
122 // base::Passed(xxx) -> std::move(*xxx)
123 auto left = clang::CharSourceRange::getTokenRange(
124 result.SourceManager->getSpellingLoc(target->getBeginLoc()),
125 result.SourceManager->getSpellingLoc(target->getArg(0)->getExprLoc())
126 .getLocWithOffset(-1));
127 replacements_->emplace_back(*result.SourceManager, left, "std::move(*");
128 }
129
130 private:
131 Replacements* replacements_;
132 };
133
134 // Replace base::Bind() and base::BindRepeating() to base::BindOnce() where
135 // resulting callbacks are implicitly converted into base::OnceCallback.
136 // Example:
137 // // Before
138 // base::PostTask(FROM_HERE, base::BindRepeating(&Foo));
139 // base::OnceCallback<void()> cb = base::BindRepeating(&Foo);
140 //
141 // // After
142 // base::PostTask(FROM_HERE, base::BindOnce(&Foo));
143 // base::OnceCallback<void()> cb = base::BindOnce(&Foo);
144 class BindOnceRewriter : public MatchFinder::MatchCallback, public Rewriter {
145 public:
BindOnceRewriter(Replacements * replacements)146 explicit BindOnceRewriter(Replacements* replacements)
147 : replacements_(replacements) {}
148
GetMatcher()149 StatementMatcher GetMatcher() {
150 auto is_once_callback = hasType(hasCanonicalType(hasDeclaration(
151 classTemplateSpecializationDecl(hasName("::base::OnceCallback")))));
152 auto is_repeating_callback =
153 hasType(hasCanonicalType(hasDeclaration(classTemplateSpecializationDecl(
154 hasName("::base::RepeatingCallback")))));
155
156 auto bind_call =
157 callExpr(callee(namedDecl(anyOf(hasName("::base::Bind"),
158 hasName("::base::BindRepeating")))))
159 .bind("target");
160 auto parameter_construction =
161 cxxConstructExpr(is_repeating_callback, argumentCountIs(1),
162 hasArgument(0, ignoringImplicit(bind_call)));
163 auto constructor_conversion = cxxConstructExpr(
164 is_once_callback, argumentCountIs(1),
165 hasArgument(0, ignoringImplicit(parameter_construction)));
166 return implicitCastExpr(is_once_callback,
167 hasSourceExpression(constructor_conversion));
168 }
169
run(const MatchFinder::MatchResult & result)170 void run(const MatchFinder::MatchResult& result) override {
171 auto* target = result.Nodes.getNodeAs<clang::CallExpr>("target");
172 auto* callee = target->getCallee();
173 auto range = clang::CharSourceRange::getTokenRange(
174 result.SourceManager->getSpellingLoc(callee->getEndLoc()),
175 result.SourceManager->getSpellingLoc(callee->getEndLoc()));
176 replacements_->emplace_back(*result.SourceManager, range, "BindOnce");
177 }
178
179 private:
180 Replacements* replacements_;
181 };
182
183 // Converts pass-by-const-ref base::RepeatingCallback's to
184 // pass-by-value. Example:
185 // // Before
186 // using BarCallback = base::RepeatingCallback<void(void*)>;
187 // void Foo(const base::RepeatingCallback<void(int)>& cb);
188 // void Bar(const BarCallback& cb);
189 //
190 // // After
191 // using BarCallback = base::RepeatingCallback<void(void*)>;
192 // void Foo(base::RepeatingCallback<void(int)> cb);
193 // void Bar(BarCallback cb);
194 class PassByValueRewriter : public MatchFinder::MatchCallback, public Rewriter {
195 public:
PassByValueRewriter(Replacements * replacements)196 explicit PassByValueRewriter(Replacements* replacements)
197 : replacements_(replacements) {}
198
GetMatcher()199 DeclarationMatcher GetMatcher() {
200 auto is_repeating_callback =
201 namedDecl(hasName("::base::RepeatingCallback"));
202 return parmVarDecl(
203 hasType(hasCanonicalType(references(is_repeating_callback))))
204 .bind("target");
205 }
206
run(const MatchFinder::MatchResult & result)207 void run(const MatchFinder::MatchResult& result) override {
208 auto* target = result.Nodes.getNodeAs<clang::ParmVarDecl>("target");
209 auto qual_type = target->getType();
210 auto* ref_type =
211 clang::dyn_cast<clang::LValueReferenceType>(qual_type.getTypePtr());
212 if (!ref_type || !ref_type->getPointeeType().isLocalConstQualified())
213 return;
214
215 // Remove the leading `const` and the following `&`.
216 auto type_loc = target->getTypeSourceInfo()->getTypeLoc();
217 auto const_keyword = clang::CharSourceRange::getTokenRange(
218 result.SourceManager->getSpellingLoc(target->getBeginLoc()),
219 result.SourceManager->getSpellingLoc(target->getBeginLoc()));
220 auto lvalue_ref = clang::CharSourceRange::getTokenRange(
221 result.SourceManager->getSpellingLoc(type_loc.getEndLoc()),
222 result.SourceManager->getSpellingLoc(type_loc.getEndLoc()));
223 replacements_->emplace_back(*result.SourceManager, const_keyword, " ");
224 replacements_->emplace_back(*result.SourceManager, lvalue_ref, " ");
225 }
226
227 private:
228 Replacements* replacements_;
229 };
230
231 // Adds std::move() to base::RepeatingCallback<> where it looks relevant.
232 // Example:
233 // // Before
234 // void Foo(base::RepeatingCallback<void(int)> cb1) {
235 // base::RepeatingClosure cb2 = base::BindRepeating(cb1, 42);
236 // PostTask(FROM_HERE, cb2);
237 // }
238 //
239 // // After
240 // void Foo(base::RepeatingCallback<void(int)> cb1) {
241 // base::RepeatingClosure cb2 = base::BindRepeating(std::move(cb1), 42);
242 // PostTask(FROM_HERE, std::move(cb2));
243 // }
244 class AddStdMoveRewriter : public MatchFinder::MatchCallback, public Rewriter {
245 public:
AddStdMoveRewriter(Replacements * replacements)246 explicit AddStdMoveRewriter(Replacements* replacements)
247 : replacements_(replacements) {}
248
GetMatcher()249 StatementMatcher GetMatcher() {
250 return declRefExpr(
251 hasType(hasCanonicalType(hasDeclaration(
252 namedDecl(hasName("::base::RepeatingCallback"))))),
253 anyOf(hasAncestor(cxxConstructorDecl().bind("enclosing_ctor")),
254 hasAncestor(functionDecl().bind("enclosing_func")),
255 hasAncestor(lambdaExpr().bind("enclosing_lambda"))))
256 .bind("target");
257 }
258
259 // Build Control Flow Graph (CFG) for |stmt| and populate class members with
260 // the content of the graph. Returns true if the analysis finished
261 // successfully.
ExtractCFGContentToMembers(Stmt * stmt,ASTContext * context)262 bool ExtractCFGContentToMembers(Stmt* stmt, ASTContext* context) {
263 // Try to make a cache entry. The failure implies it's already in the cache.
264 auto inserted = cfg_cache_.emplace(stmt, nullptr);
265 if (!inserted.second)
266 return !!inserted.first->second;
267
268 std::unique_ptr<CFG>& cfg = inserted.first->second;
269 CFG::BuildOptions opts;
270 opts.AddInitializers = true;
271 opts.AddLifetime = true;
272 opts.AddStaticInitBranches = true;
273 cfg = CFG::buildCFG(nullptr, stmt, context, opts);
274
275 // CFG construction may fail. Report it to the caller.
276 if (!cfg)
277 return false;
278 if (!parent_map_)
279 parent_map_ = std::make_unique<clang::ParentMap>(stmt);
280 else
281 parent_map_->addStmt(stmt);
282
283 // Populate |top_stmts_|, that contains Stmts that is evaluated in its own
284 // CFGElement.
285 for (auto* block : *cfg) {
286 for (auto& elem : *block) {
287 if (auto stmt = elem.getAs<CFGStmt>())
288 top_stmts_.insert(stmt->getStmt());
289 }
290 }
291
292 // Populate |enclosing_block_|, that maps a Stmt to a CFGBlock that contains
293 // the Stmt.
294 std::function<void(const CFGBlock*, const Stmt*)> recursive_set_enclosing =
295 [&](const CFGBlock* block, const Stmt* stmt) {
296 enclosing_block_[stmt] = block;
297 for (auto* c : stmt->children()) {
298 if (!c)
299 continue;
300 if (top_stmts_.find(c) != top_stmts_.end())
301 continue;
302 recursive_set_enclosing(block, c);
303 }
304 };
305 for (auto* block : *cfg) {
306 for (auto& elem : *block) {
307 if (auto stmt = elem.getAs<CFGStmt>())
308 recursive_set_enclosing(block, stmt->getStmt());
309 }
310 }
311
312 return true;
313 }
314
EnclosingCxxStatement(const Stmt * stmt)315 const Stmt* EnclosingCxxStatement(const Stmt* stmt) {
316 while (true) {
317 const Stmt* parent = parent_map_->getParentIgnoreParenCasts(stmt);
318 assert(parent);
319 switch (parent->getStmtClass()) {
320 case Stmt::CompoundStmtClass:
321 case Stmt::ForStmtClass:
322 case Stmt::CXXForRangeStmtClass:
323 case Stmt::WhileStmtClass:
324 case Stmt::DoStmtClass:
325 case Stmt::IfStmtClass:
326
327 // Other candidates:
328 // Stmt::CXXTryStmtClass
329 // Stmt::CXXCatchStmtClass
330 // Stmt::CapturedStmtClass
331 // Stmt::SwitchStmtClass
332 // Stmt::SwitchCaseClass
333 return stmt;
334 default:
335 stmt = parent;
336 break;
337 }
338 }
339 }
340
WasPointerTaken(const Stmt * stmt,const Decl * decl)341 bool WasPointerTaken(const Stmt* stmt, const Decl* decl) {
342 std::function<bool(const Stmt*)> visit_stmt = [&](const Stmt* stmt) {
343 if (auto* op = clang::dyn_cast<UnaryOperator>(stmt)) {
344 if (op->getOpcode() == clang::UO_AddrOf) {
345 auto* ref = clang::dyn_cast<DeclRefExpr>(op->getSubExpr());
346 // |ref| may be null if the sub-expr has a dependent type.
347 if (ref && ref->getDecl() == decl)
348 return true;
349 }
350 }
351
352 for (auto* c : stmt->children()) {
353 if (!c)
354 continue;
355 if (visit_stmt(c))
356 return true;
357 }
358 return false;
359 };
360 return visit_stmt(stmt);
361 }
362
HasCapturingLambda(const Stmt * stmt,const Decl * decl)363 bool HasCapturingLambda(const Stmt* stmt, const Decl* decl) {
364 std::function<bool(const Stmt*)> visit_stmt = [&](const Stmt* stmt) {
365 if (auto* l = clang::dyn_cast<LambdaExpr>(stmt)) {
366 for (auto c : l->captures()) {
367 if (c.getCapturedVar() == decl)
368 return true;
369 }
370 }
371
372 for (auto* c : stmt->children()) {
373 if (!c)
374 continue;
375
376 if (visit_stmt(c))
377 return true;
378 }
379
380 return false;
381 };
382 return visit_stmt(stmt);
383 }
384
385 // Returns true if there are multiple occurrences to |decl| in one of C++
386 // statements in |stmt|.
HasUnorderedOccurrences(const Decl * decl,const Stmt * stmt)387 bool HasUnorderedOccurrences(const Decl* decl, const Stmt* stmt) {
388 int count = 0;
389 std::function<void(const Stmt*)> visit_stmt = [&](const Stmt* s) {
390 if (auto* ref = clang::dyn_cast<DeclRefExpr>(s)) {
391 if (ref->getDecl() == decl)
392 ++count;
393 }
394 for (auto* c : s->children()) {
395 if (!c)
396 continue;
397 visit_stmt(c);
398 }
399 };
400
401 visit_stmt(EnclosingCxxStatement(stmt));
402 return count > 1;
403 }
404
run(const MatchFinder::MatchResult & result)405 void run(const MatchFinder::MatchResult& result) override {
406 auto* target = result.Nodes.getNodeAs<clang::DeclRefExpr>("target");
407 auto* decl = clang::dyn_cast<clang::VarDecl>(target->getDecl());
408
409 // Other than local variables and parameters are out-of-scope.
410 if (!decl || !decl->isLocalVarDeclOrParm())
411 return;
412
413 auto qual_type = decl->getType();
414 // Qualified variables are out-of-scope. They are likely not movable.
415 if (qual_type.getCanonicalType().hasQualifiers())
416 return;
417
418 auto* type = qual_type.getTypePtr();
419 // References and pointers are out-of-scope.
420 if (type->isReferenceType() || type->isPointerType())
421 return;
422
423 Stmt* body = nullptr;
424 if (auto* ctor = result.Nodes.getNodeAs<LambdaExpr>("enclosing_ctor"))
425 return; // Skip constructor case for now. TBD.
426 else if (auto* func =
427 result.Nodes.getNodeAs<FunctionDecl>("enclosing_func"))
428 body = func->getBody();
429 else if (auto* lambda =
430 result.Nodes.getNodeAs<LambdaExpr>("enclosing_lambda"))
431 body = lambda->getBody();
432 else
433 return;
434
435 // Disable the replacement if there is a lambda that captures |decl|.
436 if (HasCapturingLambda(body, decl))
437 return;
438
439 // Disable the replacement if the pointer to |decl| is taken in the scope.
440 if (WasPointerTaken(body, decl))
441 return;
442
443 if (!ExtractCFGContentToMembers(body, result.Context))
444 return;
445
446 auto* parent = parent_map_->getParentIgnoreParenCasts(target);
447 if (auto* p = clang::dyn_cast<CallExpr>(parent)) {
448 auto* callee = p->getCalleeDecl();
449 // |callee| may be null if the CallExpr has an unresolved look up.
450 if (!callee)
451 return;
452 auto* callee_decl = clang::dyn_cast<clang::NamedDecl>(callee);
453 auto name = callee_decl->getQualifiedNameAsString();
454
455 // Disable the replacement if it's already in std::move() or
456 // std::forward().
457 if (name == "std::__1::move" || name == "std::__1::forward")
458 return;
459 } else if (parent->getStmtClass() == Stmt::ReturnStmtClass) {
460 // Disable the replacement if it's in a return statement.
461 return;
462 }
463
464 // If the same C++ statement contains multiple reference to the variable,
465 // don't insert std::move() to be conservative.
466 if (HasUnorderedOccurrences(decl, target))
467 return;
468
469 bool saw_reuse = false;
470 ForEachFollowingStmts(target, [&](const Stmt* stmt) {
471 if (auto* ref = clang::dyn_cast<DeclRefExpr>(stmt)) {
472 if (ref->getDecl() == decl) {
473 saw_reuse = true;
474 return false;
475 }
476 }
477
478 // TODO: Detect Reset() and operator=() to stop the traversal.
479 return true;
480 });
481 if (saw_reuse)
482 return;
483
484 replacements_->emplace_back(
485 *result.SourceManager,
486 result.SourceManager->getSpellingLoc(target->getBeginLoc()), 0,
487 "std::move(");
488 replacements_->emplace_back(
489 *result.SourceManager,
490 clang::Lexer::getLocForEndOfToken(target->getEndLoc(), 0,
491 *result.SourceManager,
492 result.Context->getLangOpts()),
493 0, ")");
494 }
495
496 // Invokes |handler| for each Stmt that follows |target| until it reaches the
497 // end of the lifetime of the variable that |target| references.
498 // If |handler| returns false, stops following the current control flow.
ForEachFollowingStmts(const DeclRefExpr * target,std::function<bool (const Stmt *)> handler)499 void ForEachFollowingStmts(const DeclRefExpr* target,
500 std::function<bool(const Stmt*)> handler) {
501 auto* decl = target->getDecl();
502 auto* block = enclosing_block_[target];
503
504 std::set<const clang::CFGBlock*> visited;
505 std::vector<const clang::CFGBlock*> stack = {block};
506
507 bool saw_target = false;
508 std::function<bool(const Stmt*)> visit_stmt = [&](const Stmt* s) {
509 for (auto* t : s->children()) {
510 if (!t)
511 continue;
512
513 // |t| is evaluated elsewhere if a sub-Stmt is in |top_stmt_|.
514 if (top_stmts_.find(t) != top_stmts_.end())
515 continue;
516
517 if (!visit_stmt(t))
518 return false;
519 }
520
521 if (!saw_target) {
522 if (s == target)
523 saw_target = true;
524 return true;
525 }
526
527 return handler(s);
528 };
529
530 bool visited_initial_block_twice = false;
531 while (!stack.empty()) {
532 auto* b = stack.back();
533 stack.pop_back();
534 if (!visited.insert(b).second) {
535 if (b != block || visited_initial_block_twice)
536 continue;
537 visited_initial_block_twice = true;
538 }
539
540 bool cont = true;
541 for (auto e : *b) {
542 if (auto s = e.getAs<CFGStmt>()) {
543 if (!visit_stmt(s->getStmt())) {
544 cont = false;
545 break;
546 }
547 } else if (auto l = e.getAs<CFGLifetimeEnds>()) {
548 if (l->getVarDecl() == decl) {
549 cont = false;
550 break;
551 }
552 }
553 }
554
555 if (cont) {
556 for (auto s : b->succs()) {
557 if (!s)
558 continue; // Unreachable block.
559 stack.push_back(s);
560 }
561 }
562 }
563 }
564
565 private:
566 // Function body to CFG.
567 std::map<const Stmt*, std::unique_ptr<CFG>> cfg_cache_;
568
569 // Statement to the enclosing CFGBlock.
570 std::map<const Stmt*, const CFGBlock*> enclosing_block_;
571
572 // Stmt to its parent Stmt.
573 std::unique_ptr<clang::ParentMap> parent_map_;
574
575 // A set of Stmt that a CFGElement has it directly.
576 std::set<const Stmt*> top_stmts_;
577
578 Replacements* replacements_;
579 };
580
581 llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
582 llvm::cl::OptionCategory rewriter_category("Rewriter Options");
583
584 llvm::cl::opt<std::string> rewriter_option(
585 "rewriter",
586 llvm::cl::desc(R"(One of the name of rewriter to apply.
587 Available rewriters are:
588 remove_unneeded_passed
589 bind_to_bind_once
590 pass_by_value
591 add_std_move
592 The default is remove_unneeded_passed.
593 )"),
594 llvm::cl::init("remove_unneeded_passed"),
595 llvm::cl::cat(rewriter_category));
596
597 } // namespace.
598
main(int argc,const char * argv[])599 int main(int argc, const char* argv[]) {
600 llvm::InitializeNativeTarget();
601 llvm::InitializeNativeTargetAsmParser();
602 CommonOptionsParser options(argc, argv, rewriter_category);
603 clang::tooling::ClangTool tool(options.getCompilations(),
604 options.getSourcePathList());
605
606 MatchFinder match_finder;
607 std::vector<clang::tooling::Replacement> replacements;
608
609 std::unique_ptr<Rewriter> rewriter;
610 if (rewriter_option == "remove_unneeded_passed") {
611 auto passed_to_move = std::make_unique<PassedToMoveRewriter>(&replacements);
612 match_finder.addMatcher(passed_to_move->GetMatcher(), passed_to_move.get());
613 rewriter = std::move(passed_to_move);
614 } else if (rewriter_option == "bind_to_bind_once") {
615 auto bind_once = std::make_unique<BindOnceRewriter>(&replacements);
616 match_finder.addMatcher(bind_once->GetMatcher(), bind_once.get());
617 rewriter = std::move(bind_once);
618 } else if (rewriter_option == "pass_by_value") {
619 auto pass_by_value = std::make_unique<PassByValueRewriter>(&replacements);
620 match_finder.addMatcher(pass_by_value->GetMatcher(), pass_by_value.get());
621 rewriter = std::move(pass_by_value);
622 } else if (rewriter_option == "add_std_move") {
623 auto add_std_move = std::make_unique<AddStdMoveRewriter>(&replacements);
624 match_finder.addMatcher(add_std_move->GetMatcher(), add_std_move.get());
625 rewriter = std::move(add_std_move);
626 } else {
627 abort();
628 }
629
630 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
631 clang::tooling::newFrontendActionFactory(&match_finder);
632 int result = tool.run(factory.get());
633 if (result != 0)
634 return result;
635
636 if (replacements.empty())
637 return 0;
638
639 // Serialization format is documented in tools/clang/scripts/run_tool.py
640 llvm::outs() << "==== BEGIN EDITS ====\n";
641 for (const auto& r : replacements) {
642 std::string replacement_text = r.getReplacementText().str();
643 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
644 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
645 << ":::" << r.getLength() << ":::" << replacement_text << "\n";
646 }
647 llvm::outs() << "==== END EDITS ====\n";
648
649 return 0;
650 }
651