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
20using clang::ast_matchers::MatchFinder;
21using clang::ast_matchers::hasDeclaration;
22using clang::ast_matchers::hasName;
23using clang::ast_matchers::id;
24using clang::ast_matchers::loc;
25using clang::ast_matchers::qualType;
26using clang::ast_matchers::recordDecl;
27using clang::tooling::CommonOptionsParser;
28using clang::tooling::Replacement;
29using clang::tooling::Replacements;
30using llvm::StringRef;
31
32namespace {
33
34class 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
43void 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
66static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
67
68int 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