1 // Copyright 2015 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 // Clang tool to change calls to scoper::Pass() to just use std::move().
6
7 #include <memory>
8 #include <string>
9
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/ASTMatchers/ASTMatchersMacros.h"
14 #include "clang/Basic/SourceManager.h"
15 #include "clang/Frontend/FrontendActions.h"
16 #include "clang/Lex/Lexer.h"
17 #include "clang/Tooling/CommonOptionsParser.h"
18 #include "clang/Tooling/Refactoring.h"
19 #include "clang/Tooling/Tooling.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/TargetSelect.h"
22
23 using namespace clang::ast_matchers;
24 using clang::tooling::CommonOptionsParser;
25 using clang::tooling::Replacement;
26 using clang::tooling::Replacements;
27 using llvm::StringRef;
28
29 namespace {
30
31 class RewriterCallback : public MatchFinder::MatchCallback {
32 public:
RewriterCallback(Replacements * replacements)33 explicit RewriterCallback(Replacements* replacements)
34 : replacements_(replacements) {}
35 virtual void run(const MatchFinder::MatchResult& result) override;
36
37 private:
38 Replacements* const replacements_;
39 };
40
run(const MatchFinder::MatchResult & result)41 void RewriterCallback::run(const MatchFinder::MatchResult& result) {
42 const clang::CXXMemberCallExpr* call_expr =
43 result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("expr");
44 const clang::MemberExpr* callee =
45 clang::dyn_cast<clang::MemberExpr>(call_expr->getCallee());
46 const bool is_arrow = callee->isArrow();
47 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
48
49 const char kMoveRefText[] = "std::move(";
50 const char kMovePtrText[] = "std::move(*";
51
52 auto err = replacements_->add(
53 Replacement(*result.SourceManager,
54 result.SourceManager->getSpellingLoc(arg->getLocStart()), 0,
55 is_arrow ? kMovePtrText : kMoveRefText));
56 assert(!err);
57
58 // Delete everything but the closing parentheses from the original call to
59 // Pass(): the closing parantheses is left to match up with the parantheses
60 // just inserted with std::move.
61 err = replacements_->add(Replacement(
62 *result.SourceManager,
63 clang::CharSourceRange::getCharRange(
64 result.SourceManager->getSpellingLoc(callee->getOperatorLoc()),
65 result.SourceManager->getSpellingLoc(call_expr->getRParenLoc())),
66 ""));
67 assert(!err);
68 }
69
70 } // namespace
71
72 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
73
main(int argc,const char * argv[])74 int main(int argc, const char* argv[]) {
75 // TODO(dcheng): Clang tooling should do this itself.
76 // http://llvm.org/bugs/show_bug.cgi?id=21627
77 llvm::InitializeNativeTarget();
78 llvm::InitializeNativeTargetAsmParser();
79 llvm::cl::OptionCategory category(
80 "C++11 modernization: change scoped::Pass() to std::move()");
81 CommonOptionsParser options(argc, argv, category);
82 clang::tooling::ClangTool tool(options.getCompilations(),
83 options.getSourcePathList());
84
85 MatchFinder match_finder;
86 Replacements replacements;
87
88 auto pass_matcher = id(
89 "expr",
90 cxxMemberCallExpr(
91 argumentCountIs(0),
92 callee(functionDecl(hasName("Pass"), returns(rValueReferenceType()))),
93 on(id("arg", expr()))));
94 RewriterCallback callback(&replacements);
95 match_finder.addMatcher(pass_matcher, &callback);
96
97 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
98 clang::tooling::newFrontendActionFactory(&match_finder);
99 int result = tool.run(factory.get());
100 if (result != 0)
101 return result;
102
103 // Serialization format is documented in tools/clang/scripts/run_tool.py
104 llvm::outs() << "==== BEGIN EDITS ====\n";
105 for (const auto& r : replacements) {
106 std::string replacement_text = r.getReplacementText().str();
107 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
108 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
109 << ":::" << r.getLength() << ":::" << replacement_text << "\n";
110 }
111 llvm::outs() << "==== END EDITS ====\n";
112
113 return 0;
114 }
115