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: 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 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 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