chromium/tools/clang/v8_handle_migrate/HandleMigrate.cpp

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A clang tool for the migration of Handle<T> to DirectHandle<T>.
// This is only useful for the V8 code base.

#include <clang/AST/ASTContext.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Refactoring.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/CommandLine.h>

#include <optional>
#include <regex>
#include <string>
#include <unordered_map>
#include <vector>

using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;

// Command line options.

static llvm::cl::OptionCategory my_tool_category(
    "Handle migration tool options");
static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);

constexpr int kVerboseNone = 0;
constexpr int kVerboseReportInterestingHandleUse = 1;
constexpr int kVerboseReportInterestingFunctionCall = 1;
constexpr int kVerboseReportImplicitHandleConversion = 2;
constexpr int kVerboseReportHandleDereference = 2;
constexpr int kVerboseReportInterestingHandleDecl = 3;
constexpr int kVerboseReportInterestingFunctions = 4;
constexpr int kVerboseWhereAreWe = 90;

static llvm::cl::opt<int> VERBOSE("verbose",
                                  llvm::cl::desc("Set verbosity level"),
                                  llvm::cl::value_desc("level"),
                                  llvm::cl::init(kVerboseNone),
                                  llvm::cl::cat(my_tool_category));

static llvm::cl::alias alias_for_VERBOSE("v",
                                         llvm::cl::desc("Alias for --verbose"),
                                         llvm::cl::aliasopt(VERBOSE),
                                         llvm::cl::cat(my_tool_category));

static llvm::cl::extrahelp verbosity_help(
    "Verbosity levels:\n"
    "\t0:\tquiet\n"
    "\t1:\tinteresting handle uses and function calls\n"
    "\t2:\timplicit handle conversions and dereferences\n"
    "\t3:\tinteresting handle declarations\n"
    "\t4:\tinteresting function declarations\n"
    "\t90:\ttrack AST source location\n"
    "\n");

static llvm::cl::opt<bool> only_in_main_file(
    "local",
    llvm::cl::desc("Process only declarations in main file(s), default false"),
    llvm::cl::init(false),
    llvm::cl::cat(my_tool_category));

// Boilerplate. (Using clang::ast_matchers naming convention for these.)
// ----------------------------------------------------------------------------

TypeMatcher relaxType(TypeMatcher t) {
  return anyOf(t, pointerType(pointee(t)), referenceType(pointee(t)));
}

StatementMatcher constructorWithOneArgument(StatementMatcher argument) {
  return cxxConstructExpr(argumentCountIs(1),
                          hasArgument(0, ignoringImplicit(argument)));
}

auto handleDecl = cxxRecordDecl(isSameOrDerivedFrom("::v8::internal::Handle"),
                                isTemplateInstantiation());
auto directHandleDecl =
    cxxRecordDecl(isSameOrDerivedFrom("::v8::internal::DirectHandle"),
                  isTemplateInstantiation());
TypeMatcher handleType = relaxType(hasDeclaration(handleDecl));
TypeMatcher directHandleType = relaxType(hasDeclaration(directHandleDecl));

// Database for storing interesting variables, functions, etc.
// ----------------------------------------------------------------------------

template <typename NodeType>
struct ASTNodeHash {
  size_t operator()(const NodeType* x) const {
    return x->getLocation().getHashValue();
  }
};

template <typename NodeType>
struct ASTNodeEquals {
  bool operator()(const NodeType* x, const NodeType* y) const {
    return x->getLocation() == y->getLocation();
  }
};

class InterestingFunction;

// A database with possibly interesting declarations of type Handle<T>.
class InterestingHandle {
 public:
  InterestingHandle(const InterestingHandle&) = delete;
  InterestingHandle(InterestingHandle&&) = default;

  static InterestingHandle* Insert(const VarDecl* decl, bool is_definition) {
    assert(decl != nullptr);
    auto it = interesting_.find(decl);
    if (it == interesting_.end()) {
      auto p =
          interesting_.emplace(decl, InterestingHandle{decl, is_definition});
      it = p.first;
    }
    return &it->second;
  }

  static InterestingHandle* Lookup(const VarDecl* decl) {
    assert(decl != nullptr);
    auto it = interesting_.find(decl);
    if (it == interesting_.end()) {
      return nullptr;
    }
    return &it->second;
  }

  void Print(const SourceManager& source_manager) const {
    llvm::outs() << "    location: ";
    location_.print(llvm::outs(), source_manager);
    llvm::outs() << "\n";
    llvm::outs() << "    type: " << type_ << "\n";
  }

  void AddDependent(InterestingHandle* h) {
    // Marks `h` as a dependent of `this`: `this` is the parameter of a
    // function definition and `h` is the corresponding parameter of some
    // declaration of the same function.
    // 1. `this` is a definition and therefore must not be dependent to any
    // other definition.
    assert(dependent_to_ == nullptr);
    // 2. `h` must not be dependent to any other definition.
    assert(h->dependent_to_ == nullptr);
    // 3. `h` is in a declaration and therefore it must not have dependents.
    assert(h->list_of_dependent_.empty());
    list_of_dependent_.push_back(h);
    h->dependent_to_ = this;
  }

  void AddAsParameter(InterestingFunction* f) {
    if (function_ != nullptr && function_ != f) {
      llvm::outs()
          << "Warning: adding as a parameter to a different function\n";
    }
    function_ = f;
  }

  bool CanMigrate() const;
  Replacement GetReplacement() const { return replacement_.value(); }

  // This method registers a usage of an interesting variable. The first
  // parameter corresponds to the AST node where the variable is used,
  // whereas the second parameter advises if migration should be possible.
  // We want to disallow migration if a variable is used (at least once) in a
  // context where a `Handle<T>` is really required. When the AST is traversed
  // and a node with a variable usage is visited, the matcher callbacks will be
  // invoked consecutively for that node, in the order that they were added to
  // the match finder. These callbacks, independently from one another, may
  // invoke this method to advise whether migration should be possible.
  // Migration is prevented when this method is called for a variable's use for
  // the first time with migrate = false. If it has previously been called for
  // the same variable use with migrate = true, then migration is not prevented.
  // Evidently, the order in which the matcher callbacks are added to the finder
  // is very important. See the comment before `HandleUseVisitor` for a detailed
  // example.
  void RegisterUsage(const DeclRefExpr* use, bool migrate) {
    if (dependent_to_ != nullptr) {
      llvm::outs() << "Warning: use of handle that is marked as dependent\n";
    }
    if (use != previous_use_ && !migrate) {
      replacement_.reset();
    }
    previous_use_ = use;
  }

 private:
  InterestingHandle(const VarDecl* decl, bool is_definition)
      : is_definition_(is_definition),
        type_(decl->getType().getAsString()),
        location_(decl->getLocation()) {
    // If this is not a definition, bail out, otherwise let's be optimistic and
    // generate the replacement for migration!
    if (!is_definition_) {
      return;
    }

    // We get the |replacement_range| in a bit clumsy way, because clang docs
    // for QualifiedTypeLoc explicitly say that these objects "intentionally
    // do not provide source location for type qualifiers".
    const auto& source_manager = decl->getASTContext().getSourceManager();
    const auto& options = decl->getASTContext().getLangOpts();
    auto first_token_loc = source_manager.getSpellingLoc(decl->getBeginLoc());
    auto last_token_loc =
        source_manager.getSpellingLoc(decl->getTypeSpecEndLoc());
    auto end_loc =
        Lexer::getLocForEndOfToken(last_token_loc, 0, source_manager, options);
    auto range = CharSourceRange::getCharRange(first_token_loc, end_loc);

    auto original_text =
        Lexer::getSourceText(range, source_manager, options).str();
    std::regex re_handle("\\bHandle<");
    auto replacement_text =
        std::regex_replace(original_text, re_handle, "DirectHandle<");

    if (original_text != replacement_text) {
      replacement_.emplace(source_manager, range, replacement_text);
    }
  }

  bool is_definition_;
  std::string type_;
  SourceLocation location_;
  std::optional<Replacement> replacement_;
  const DeclRefExpr* previous_use_ = nullptr;

  // For parameters, this points to the corresponding function.
  InterestingFunction* function_ = nullptr;
  // For parameters of a function declaration, this points to the
  // corresponding parameter of the function definition.
  InterestingHandle* dependent_to_ = nullptr;
  // For parameters of a function definition, this contains a list
  // of all the corresponding parameters of function declarations (if any).
  std::vector<InterestingHandle*> list_of_dependent_;

  using Container = std::unordered_map<const VarDecl*,
                                       InterestingHandle,
                                       ASTNodeHash<VarDecl>,
                                       ASTNodeEquals<VarDecl>>;
  static Container interesting_;
};

InterestingHandle::Container InterestingHandle::interesting_;

// A database with all interesting functions.
class InterestingFunction {
 public:
  InterestingFunction(const InterestingFunction&) = delete;
  InterestingFunction(InterestingFunction&&) = default;

  static InterestingFunction* Insert(const FunctionDecl* decl) {
    assert(decl != nullptr);
    auto it = interesting_.find(decl);
    if (it == interesting_.end()) {
      auto p = interesting_.emplace(decl, InterestingFunction{decl});
      it = p.first;
    }
    return &it->second;
  }

  static InterestingFunction* Lookup(const FunctionDecl* decl) {
    assert(decl != nullptr);
    auto it = interesting_.find(decl);
    if (it == interesting_.end()) {
      return nullptr;
    }
    return &it->second;
  }

  void AddOrCheckParameter(const ParmVarDecl* param, InterestingHandle* p) {
    unsigned i = param->getFunctionScopeIndex();
    if (i >= parameters_.size()) {
      llvm::outs() << "Warning: parameter " << i
                   << " does not exist, there are only " << parameters_.size()
                   << " parameters\n";
      return;
    }
    if (parameters_[i] == nullptr) {
      parameters_[i] = p;
      p->AddAsParameter(this);
    } else if (parameters_[i] != p) {
      const auto& source_manager = param->getASTContext().getSourceManager();
      llvm::outs() << "Warning: parameter " << i << " has already been added\n";
      llvm::outs() << "  previous:\n";
      parameters_[i]->Print(source_manager);
      llvm::outs() << "  current:\n";
      p->Print(source_manager);
    }
  }

  void AddLocalVariable(InterestingHandle* v) { local_vars_.push_back(v); }

  const std::vector<InterestingHandle*>& parameters() const {
    return parameters_;
  }

  const std::vector<InterestingHandle*>& local_vars() const {
    return local_vars_;
  }

  bool is_special() const { return is_special_; }

  static std::set<Replacement> GetReplacements() {
    std::set<Replacement> result;
    for (const auto& [_, f] : interesting_) {
      for (InterestingHandle* p : f.parameters()) {
        if (p != nullptr && p->CanMigrate()) {
          result.insert(p->GetReplacement());
        }
      }
      for (InterestingHandle* v : f.local_vars()) {
        if (v->CanMigrate()) {
          result.insert(v->GetReplacement());
        }
      }
    }
    return result;
  }

 private:
  explicit InterestingFunction(const FunctionDecl* decl)
      : name_(decl->getQualifiedNameAsString()),
        location_(decl->getLocation()),
        parameters_(decl->getNumParams(), nullptr) {
    if (auto* method_decl = dyn_cast<CXXMethodDecl>(decl)) {
      is_special_ = method_decl->isVirtual();
    } else {
      is_special_ = decl->isTemplateInstantiation() ||
                    decl->isFunctionTemplateSpecialization();
    }
  }

  std::string name_;
  SourceLocation location_;
  bool is_special_ = false;
  std::vector<InterestingHandle*> parameters_;
  std::vector<InterestingHandle*> local_vars_;

  using Container = std::unordered_map<const FunctionDecl*,
                                       InterestingFunction,
                                       ASTNodeHash<FunctionDecl>,
                                       ASTNodeEquals<FunctionDecl>>;
  static Container interesting_;
};

InterestingFunction::Container InterestingFunction::interesting_;

bool InterestingHandle::CanMigrate() const {
  if (dependent_to_ != nullptr) {
    return dependent_to_->CanMigrate();
  }
  if (!is_definition_) {
    return false;
  }
  if (function_ != nullptr && function_->is_special()) {
    return false;
  }
  return replacement_.has_value();
}

// Keep track of where we are, in the AST.
// This is used only for debugging purposes.
// ----------------------------------------------------------------------------
class WhereWeAreVisitor : public MatchFinder::MatchCallback {
 private:
  static DeclarationMatcher matcher() { return decl().bind("decl"); }

 public:
  explicit WhereWeAreVisitor(MatchFinder& finder) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* decl = Result.Nodes.getNodeAs<Decl>("decl");
    assert(decl != nullptr);

    if (VERBOSE >= kVerboseWhereAreWe) {
      ASTContext* context = Result.Context;
      auto loc = decl->getBeginLoc();
      llvm::outs() << "At: " << decl->getDeclKindName() << " ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
    }
  }
};

// Find and record interesting functions:
// - some parameter is a Handle<T>, or
// - the result is a Handle<T>, or
// - is a method of Handle<T>, because the object pointed to by `this` is a
// `Handle<T>`.
// ----------------------------------------------------------------------------
class InterestingFunctionVisitor : public MatchFinder::MatchCallback {
 private:
  static DeclarationMatcher matcher() {
    return functionDecl(anyOf(hasAnyParameter(parmVarDecl(hasType(handleType))),
                              returns(handleType),
                              cxxMethodDecl(ofClass(handleDecl))))
        .bind("interesting-function");
  }

 public:
  explicit InterestingFunctionVisitor(MatchFinder& finder,
                                      bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* decl = Result.Nodes.getNodeAs<FunctionDecl>("interesting-function");
    assert(decl != nullptr);

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              decl->getBeginLoc())) {
        return;
      }
    }

    InterestingFunction::Insert(decl);

    if (VERBOSE >= kVerboseReportInterestingFunctions) {
      ASTContext* context = Result.Context;
      auto loc = decl->getBeginLoc();
      llvm::outs() << "Func: ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
      llvm::outs() << "  name " << decl->getQualifiedNameAsString() << "\n";
      for (const auto& param : decl->parameters()) {
        auto type = param->getOriginalType();
        llvm::outs() << "  param " << param->getFunctionScopeIndex()
                     << " of type " << type.getAsString() << "\n";
      }
      auto result_type = decl->getCallResultType();
      llvm::outs() << "  result " << result_type.getAsString() << "\n";
      llvm::outs() << "  templated kind " << decl->getTemplatedKind() << "\n";
    }
  }

 private:
  bool only_in_main_file_;
};

// Find and record interesting variables.
// - of type Handle<T>; and
// - local variables or parameters.
// ----------------------------------------------------------------------------
class HandleDeclVisitor : public MatchFinder::MatchCallback {
 private:
  static DeclarationMatcher matcher() {
    return anyOf(
        varDecl(allOf(hasLocalStorage(), hasType(handleType),
                      hasAncestor(functionDecl().bind("func-decl"))))
            .bind("var-decl"),
        bindingDecl(allOf(hasType(handleType),
                          hasAncestor(functionDecl().bind("func-decl"))))
            .bind("binding-decl"));
  }

 public:
  explicit HandleDeclVisitor(MatchFinder& finder,
                             bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* decl = Result.Nodes.getNodeAs<VarDecl>("var-decl");
    if (decl == nullptr) {
      auto* binding = Result.Nodes.getNodeAs<BindingDecl>("binding-decl");
      assert(binding != nullptr);
      decl = binding->getHoldingVar();
      assert(decl != nullptr);
    }

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              decl->getBeginLoc())) {
        return;
      }
    }

    auto* func_decl = Result.Nodes.getNodeAs<FunctionDecl>("func-decl");
    assert(func_decl != nullptr);

    if (auto* param = dyn_cast<ParmVarDecl>(decl)) {
      auto* ctxt = param->getDeclContext();
      assert(ctxt != nullptr);
      auto type = param->getType();
      auto* func = dyn_cast<FunctionDecl>(ctxt);
      if (func == nullptr) {
        // TODO(42203211): This may happen, for example, if a handle parameter
        // is part of some other parameter's type, e.g.
        //
        //     void f(std::function<void(Handle<HeapObject>)> g);
        //
        // Migrating higher-order functions is out of the scope of this tool
        // right now. For migrating the definition of `f` here, we would need to
        // check all its call sites and see what the actual function passed as
        // the `g` parameter is. If the actual parameter can be migrated in all
        // cases to a function expecting a `DirectHandle`, then `f` can be
        // migrated, otherwise it cannot.
        //
        // Such cases are ignored now and we expect that they be migrated
        // manually.
        llvm::outs() << "Warning: this parameter does not lead to function "
                        "declaration\n";
        ASTContext* context = Result.Context;
        auto loc = decl->getBeginLoc();
        llvm::outs() << "Decl parm: ";
        loc.print(llvm::outs(), context->getSourceManager());
        llvm::outs() << "\n";
        llvm::outs() << "  type " << type.getAsString() << "\n";
        llvm::outs() << "  index " << param->getFunctionScopeIndex() << "\n";
        return;
      }
      if (func != func_decl) {
        llvm::outs() << "Warning: function declaration mismatch\n";
        llvm::outs() << "  func: " << *func << "\n";
        llvm::outs() << "  func_decl: " << *func_decl << "\n";
      }
      auto* f = InterestingFunction::Lookup(func_decl);
      if (f != nullptr && !func_decl->isDefaulted() &&
          !func_decl->isTemplateInstantiation()) {
        auto* p = InterestingHandle::Lookup(param);
        if (p == nullptr) {
          bool is_definition = func_decl->hasBody();
          p = InterestingHandle::Insert(param, is_definition);
          // If there's no function definition, nothing to be done yet.
          if (auto* func_def = func_decl->getDefinition()) {
            if (func_def == func_decl) {
              // If this is the function definition.
              assert(is_definition);
              // Look for all registered declarations of this function.
              for (const auto& prev_decl : func_decl->redecls()) {
                if (auto* d = InterestingFunction::Lookup(prev_decl)) {
                  // Mark the corresponding parameter of the declaration as
                  // dependent to this entry.
                  auto* q = d->parameters()[param->getFunctionScopeIndex()];
                  if (q != nullptr) {
                    p->AddDependent(q);
                  }
                }
              }
            } else if (auto* d = InterestingFunction::Lookup(func_def)) {
              // If there is a registered function definition, mark this entry
              // as dependent to the corresponding parameter of the function
              // definition.
              auto* q = d->parameters()[param->getFunctionScopeIndex()];
              assert(q != nullptr);
              q->AddDependent(p);
            }
          }
        }
        f->AddOrCheckParameter(param, p);
      }

      if (VERBOSE >= kVerboseReportInterestingHandleDecl) {
        ASTContext* context = Result.Context;
        auto loc = decl->getBeginLoc();
        llvm::outs() << "Decl parm: ";
        loc.print(llvm::outs(), context->getSourceManager());
        llvm::outs() << "\n";
        llvm::outs() << "  type " << type.getAsString() << "\n";
        llvm::outs() << "  index " << param->getFunctionScopeIndex() << "\n";
        llvm::outs() << "  func " << func->getQualifiedNameAsString() << "\n";
      }
    } else {
      auto* p = InterestingHandle::Insert(decl, true);
      auto* f = InterestingFunction::Lookup(func_decl);
      if (!func_decl->isDefaulted() && !func_decl->isTemplateInstantiation()) {
        if (f == nullptr) {
          assert(func_decl->hasBody());
          f = InterestingFunction::Insert(func_decl);
        }
        assert(f != nullptr);
        f->AddLocalVariable(p);
      }

      if (VERBOSE >= kVerboseReportInterestingHandleDecl) {
        ASTContext* context = Result.Context;
        auto loc = decl->getBeginLoc();
        llvm::outs() << "Decl var: ";
        loc.print(llvm::outs(), context->getSourceManager());
        llvm::outs() << "\n"
                     << "  type " << decl->getType().getAsString() << "\n";
      }
    }
  }

 private:
  bool only_in_main_file_;
};

// Find and process uses of handle parameters or variables.
//
// Tracking such uses is important, because we want to disallow the migration of
// a variable's type from `Handle<T>` to `DirectHandle<T>` if the variable is
// used (at least once) in a context where a `Handle<T>` is really required.
// In general, if a variable of type `Handle<T>` is used, we need to prevent the
// migration of a variable. This is the purpose of `HandleUseVisitor`. However,
// there are cases when such a variable is used in a manner that is compatible
// with a `DirectHandle<T>`, e.g., when the handle is dereferenced, or
// implicitly converted to a direct handle. In these cases there is no need to
// prevent the migration and this is the purpose of more specific visitors, such
// as `HandleDereferenceVisitor` or `ImplicitHandleToDirectHandleVisitor` below.
//
// As mentioned in the comment before `InterestingHandle::RegisterUsage`, the
// order in which visitors are executed is important. For a given variable
// usage, migration is prevented if the first executed visitor decides to
// prevent it.
//
// Consider the following example:
//
//     void consume_direct(DirectHandle<HeapObject> o);  /* line: 1 */
//     Handle<HeapObject> h;                             /* line: 2 */
//     consume_direct(h);                                /* line: 3 */
//     Tagged<Map> map = h->map();                       /* line: 4 */
//
// The use of variable `h` in line 3 will be processed by two visitors:
//
// 1. `HandleUseVisitor` (this one), which will claim that the use of this
//     variable is reason enough for disallowing its migration.
// 2. `ImplicitHandleToDirectHandleVisitor` (below), which will realize that
//    the handle is implicitly converted to a direct handle, therefore we can
//    allow its migration.
//
// Because visitor 1 is added last to the match finder (we rely on this),
// visitor 2 will run before visitor 1 for this node, thus not preventing the
// migration.
//
// Similarly, the use of variable `h` in line 4 will be processed by two
// visitors: first `HandleDereferenceVisitor` and then `HandleUseVisitor`, in
// this order, and migration will again not be prevented. As none of the
// variable's uses has prevented migration, the type of variable `h` in line 2
// will be migrated from `Handle<HeapObject>` to `DirectHandle<HeapObject>`.
// ----------------------------------------------------------------------------
class HandleUseVisitor : public MatchFinder::MatchCallback {
 public:
  static StatementMatcher matcher() {
    return declRefExpr(
               allOf(to(varDecl().bind("var-decl")), hasType(handleType),
                     hasAncestor(
                         functionDecl(allOf(isDefinition(), hasBody(stmt())))
                             .bind("func-def"))))
        .bind("handle-use");
  }

 public:
  explicit HandleUseVisitor(MatchFinder& finder, bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* use = Result.Nodes.getNodeAs<DeclRefExpr>("handle-use");
    assert(use != nullptr);
    auto* decl = Result.Nodes.getNodeAs<VarDecl>("var-decl");
    assert(decl != nullptr);
    auto* func = Result.Nodes.getNodeAs<FunctionDecl>("func-def");
    assert(func != nullptr);

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              use->getBeginLoc())) {
        return;
      }
    }

    auto* h = InterestingHandle::Lookup(decl);
    assert(h != nullptr || func->isDefaulted() ||
           func->isTemplateInstantiation());

    if (VERBOSE >= kVerboseReportInterestingHandleUse) {
      auto type = decl->getType();
      ASTContext* context = Result.Context;
      auto loc = use->getBeginLoc();
      llvm::outs() << "Use var: ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
      llvm::outs() << "  var of type " << type.getAsString() << "\n";
    }

    if (h != nullptr) {
      // This will disallow migration, unless some other more specific visitor
      // has already run and explicitly allowed migration for the same variable
      // usage. Here, we rely on the fact that `HandleUseVisitor` is added last
      // to the match finder, therefore it will run last for any given AST node.
      h->RegisterUsage(use, false);
    }
  }

 private:
  bool only_in_main_file_;
};

// Find and process calls to interesting functions.
// Currently, this does nothing interesting except for logging.
// ----------------------------------------------------------------------------
class CallExprWithHandleVisitor : public MatchFinder::MatchCallback {
 private:
  static StatementMatcher matcher() {
    return callExpr(hasAnyArgument(hasType(handleType))).bind("call-expr");
  }

 public:
  explicit CallExprWithHandleVisitor(MatchFinder& finder,
                                     bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* call = Result.Nodes.getNodeAs<CallExpr>("call-expr");
    assert(call != nullptr);

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              call->getBeginLoc())) {
        return;
      }
    }

    auto* decl = call->getCalleeDecl();
    // This happens when we have a call with an unresolved expression in a
    // template definition.
    if (decl == nullptr) {
      return;
    }
    auto* func = dyn_cast<FunctionDecl>(decl);
    if (func == nullptr) {
      llvm::outs() << "Warning: This is not a FunctionDecl but a "
                   << decl->getDeclKindName() << "\n";
      return;
    }

    if (VERBOSE >= kVerboseReportInterestingFunctionCall) {
      ASTContext* context = Result.Context;
      auto loc = call->getBeginLoc();
      llvm::outs() << "Call: ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
      llvm::outs() << "  callee " << func->getQualifiedNameAsString() << "\n";

      int i = 0;
      for (const auto& arg : call->arguments()) {
        auto type = arg->getType();
        llvm::outs() << "  param " << i << " of type " << type.getAsString()
                     << "\n";
        ++i;
      }
    }
  }

 private:
  bool only_in_main_file_;
};

// Implicit Handle<T> -> DirectHandle<T> conversions.
// They do not prevent handle migration.
// ----------------------------------------------------------------------------
class ImplicitHandleToDirectHandleVisitor : public MatchFinder::MatchCallback {
 private:
  static StatementMatcher matcher() {
    return implicitCastExpr(
               allOf(hasImplicitDestinationType(directHandleType),
                     hasCastKind(CK_ConstructorConversion),
                     hasSourceExpression(
                         constructorWithOneArgument(constructorWithOneArgument(
                             HandleUseVisitor::matcher())))))
        .bind("implicit-cast");
  }

 public:
  explicit ImplicitHandleToDirectHandleVisitor(MatchFinder& finder,
                                               bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* expr = Result.Nodes.getNodeAs<ImplicitCastExpr>("implicit-cast");
    assert(expr != nullptr);

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              expr->getBeginLoc())) {
        return;
      }
    }

    if (VERBOSE >= kVerboseReportImplicitHandleConversion) {
      ASTContext* context = Result.Context;
      auto loc = expr->getBeginLoc();
      llvm::outs() << "H->DH: ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
    }

    auto* use = Result.Nodes.getNodeAs<DeclRefExpr>("handle-use");
    assert(use != nullptr);
    auto* decl = Result.Nodes.getNodeAs<VarDecl>("var-decl");
    assert(decl != nullptr);

    auto* h = InterestingHandle::Lookup(decl);
    if (h != nullptr) {
      // This will allow migration for this variable usage.
      h->RegisterUsage(use, true);
    }
  }

 private:
  bool only_in_main_file_;
};

// Handle<T>::operator* and Handle<T>::operator->
// They do not prevent handle migration.
// ----------------------------------------------------------------------------
class HandleDereferenceVisitor : public MatchFinder::MatchCallback {
 private:
  static StatementMatcher matcher() {
    return cxxOperatorCallExpr(
               hasAnyOverloadedOperatorName("*", "->"),
               hasAnyArgument(ignoringImplicit(HandleUseVisitor::matcher())))
        .bind("handle-deref");
  }

 public:
  explicit HandleDereferenceVisitor(MatchFinder& finder,
                                    bool only_in_main_file = false)
      : only_in_main_file_(only_in_main_file) {
    finder.addMatcher(matcher(), this);
  }

  void run(MatchFinder::MatchResult const& Result) override {
    auto* expr = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("handle-deref");
    assert(expr != nullptr);

    if (only_in_main_file_) {
      ASTContext* context = Result.Context;
      if (!context->getSourceManager().isWrittenInMainFile(
              expr->getBeginLoc())) {
        return;
      }
    }

    if (VERBOSE >= kVerboseReportHandleDereference) {
      ASTContext* context = Result.Context;
      auto loc = expr->getBeginLoc();
      llvm::outs() << "Handle deref: ";
      loc.print(llvm::outs(), context->getSourceManager());
      llvm::outs() << "\n";
    }

    auto* use = Result.Nodes.getNodeAs<DeclRefExpr>("handle-use");
    assert(use != nullptr);
    auto* decl = Result.Nodes.getNodeAs<VarDecl>("var-decl");
    assert(decl != nullptr);

    auto* h = InterestingHandle::Lookup(decl);
    if (h != nullptr) {
      // This will allow migration for this variable usage.
      h->RegisterUsage(use, true);
    }
  }

 private:
  bool only_in_main_file_;
};

// Main program.
// ----------------------------------------------------------------------------

int main(int argc, const char* argv[]) {
  auto expected_parser =
      CommonOptionsParser::create(argc, argv, my_tool_category);
  if (!expected_parser) {
    // Fail gracefully for unsupported options.
    llvm::errs() << expected_parser.takeError();
    return 1;
  }
  CommonOptionsParser& options_parser = expected_parser.get();
  ClangTool Tool(options_parser.getCompilations(),
                 options_parser.getSourcePathList());

  MatchFinder finder;
  std::optional<WhereWeAreVisitor> where_we_are_visitor;
  if (VERBOSE >= kVerboseWhereAreWe) {
    where_we_are_visitor.emplace(finder);
  }

  // These populate the database of functions and handle declarations.
  InterestingFunctionVisitor interesting_function_visitor(finder,
                                                          only_in_main_file);
  HandleDeclVisitor handle_decl_visitor(finder, only_in_main_file);
  // These allow migration in some special cases.
  HandleDereferenceVisitor handle_deref_callback(finder, only_in_main_file);
  ImplicitHandleToDirectHandleVisitor implicit_conversion_visitor(
      finder, only_in_main_file);
  // This is not used, except for logging.
  std::optional<CallExprWithHandleVisitor> call_expr_with_handle_visitor;
  if (VERBOSE >= kVerboseReportInterestingFunctionCall) {
    call_expr_with_handle_visitor.emplace(finder, only_in_main_file);
  }
  // This needs to be last, to disallow migration in all other cases.
  HandleUseVisitor handle_use_callback(finder, only_in_main_file);

  int error_code = Tool.run(newFrontendActionFactory(&finder).get());
  if (error_code) {
    return error_code;
  }

  std::set<Replacement> replacements = InterestingFunction::GetReplacements();
  if (replacements.empty()) {
    return 0;
  }

  // Serialization format is documented in tools/clang/scripts/run_tool.py
  llvm::outs() << "==== BEGIN EDITS ====\n";
  for (const auto& r : replacements) {
    std::string replacement_text = r.getReplacementText().str();
    std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
    llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset()
                 << ":::" << r.getLength() << ":::" << replacement_text << "\n";
  }
  llvm::outs() << "==== END EDITS ====\n";

  return 0;
}