Home | History | Annotate | Download | only in rewrite_scoped_array
      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 //
      5 // This implements a Clang tool to rewrite all instances of scoped_array<T> to
      6 // scoped_ptr<T[]>. The former is being deprecated in favor of the latter, to
      7 // allow for an eventual transition from scoped_ptr to unique_ptr.
      8 
      9 #include "clang/AST/ASTContext.h"
     10 #include "clang/ASTMatchers/ASTMatchers.h"
     11 #include "clang/ASTMatchers/ASTMatchFinder.h"
     12 #include "clang/Basic/SourceManager.h"
     13 #include "clang/Frontend/FrontendActions.h"
     14 #include "clang/Lex/Lexer.h"
     15 #include "clang/Tooling/CommonOptionsParser.h"
     16 #include "clang/Tooling/Refactoring.h"
     17 #include "clang/Tooling/Tooling.h"
     18 #include "llvm/Support/CommandLine.h"
     19 
     20 using clang::ast_matchers::MatchFinder;
     21 using clang::ast_matchers::hasDeclaration;
     22 using clang::ast_matchers::hasName;
     23 using clang::ast_matchers::id;
     24 using clang::ast_matchers::loc;
     25 using clang::ast_matchers::qualType;
     26 using clang::ast_matchers::recordDecl;
     27 using clang::tooling::CommonOptionsParser;
     28 using clang::tooling::Replacement;
     29 using clang::tooling::Replacements;
     30 using llvm::StringRef;
     31 
     32 namespace {
     33 
     34 class RewriterCallback : public MatchFinder::MatchCallback {
     35  public:
     36   RewriterCallback(Replacements* replacements) : replacements_(replacements) {}
     37   virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
     38 
     39  private:
     40   Replacements* const replacements_;
     41 };
     42 
     43 void RewriterCallback::run(const MatchFinder::MatchResult& result) {
     44   const clang::TypeLoc type_location =
     45       *result.Nodes.getNodeAs<clang::TypeLoc>("loc");
     46   clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(
     47       result.SourceManager->getSpellingLoc(type_location.getLocStart()),
     48       result.SourceManager->getSpellingLoc(type_location.getLocEnd()));
     49   // TODO(dcheng): Log an error?
     50   if (!range.isValid())
     51     return;
     52   std::string replacement_text = clang::Lexer::getSourceText(
     53       range, *result.SourceManager, result.Context->getLangOpts());
     54   // TODO(dcheng): Log errors?
     55   if (!StringRef(replacement_text).startswith("scoped_array<") ||
     56       !StringRef(replacement_text).endswith(">"))
     57     return;
     58   replacement_text.replace(strlen("scoped_"), strlen("array"), "ptr");
     59   replacement_text.insert(replacement_text.size() - 1, "[]");
     60   replacements_->insert(
     61       Replacement(*result.SourceManager, range, replacement_text));
     62 }
     63 
     64 }  // namespace
     65 
     66 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
     67 
     68 int main(int argc, const char* argv[]) {
     69   CommonOptionsParser options(argc, argv);
     70   clang::tooling::ClangTool tool(options.getCompilations(),
     71                                  options.getSourcePathList());
     72 
     73   Replacements replacements;
     74   RewriterCallback callback(&replacements);
     75   MatchFinder match_finder;
     76   match_finder.addMatcher(
     77       id("loc",
     78          loc(qualType(hasDeclaration(recordDecl(hasName("::scoped_array")))))),
     79       &callback);
     80 
     81   int result =
     82       tool.run(clang::tooling::newFrontendActionFactory(&match_finder));
     83   if (result != 0)
     84     return result;
     85 
     86   // Serialization format is documented in tools/clang/scripts/run_tool.py
     87   llvm::outs() << "==== BEGIN EDITS ====\n";
     88   for (Replacements::const_iterator it = replacements.begin();
     89        it != replacements.end(); ++it) {
     90     llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":"
     91                  << it->getLength() << ":" << it->getReplacementText() << "\n";
     92   }
     93   llvm::outs() << "==== END EDITS ====\n";
     94 
     95   return 0;
     96 }
     97