chromium/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Changes Blink-style names to Chrome-style names. Currently transforms:
//   fields:
//     int m_operationCount => int operation_count_
//   variables (including parameters):
//     int mySuperVariable => int my_super_variable
//   constants:
//     const int maxThings => const int kMaxThings
//   free functions and methods:
//     void doThisThenThat() => void DoThisAndThat()

#include <assert.h>
#include <algorithm>
#include <memory>
#include <set>
#include <string>

#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"

#include "EditTracker.h"

using namespace clang::ast_matchers;
using clang::tooling::CommonOptionsParser;
using clang::tooling::Replacement;
using llvm::StringRef;

namespace {

const char kBlinkFieldPrefix[] = "m_";
const char kBlinkStaticMemberPrefix[] = "s_";
const char kGMockMethodNamePrefix[] = "gmock_";
const char kMethodBlocklistParamName[] = "method-blocklist";

std::set<clang::SourceLocation>& GetRewrittenLocs() {
  static auto& locations = *new std::set<clang::SourceLocation>();
  return locations;
}

template <typename MatcherType, typename NodeType>
bool IsMatching(const MatcherType& matcher,
                const NodeType& node,
                clang::ASTContext& context) {
  return !match(matcher, node, context).empty();
}

const clang::ast_matchers::internal::
    VariadicDynCastAllOfMatcher<clang::Expr, clang::UnresolvedMemberExpr>
        unresolvedMemberExpr;

const clang::ast_matchers::internal::
    VariadicDynCastAllOfMatcher<clang::Expr, clang::DependentScopeDeclRefExpr>
        dependentScopeDeclRefExpr;

const clang::ast_matchers::internal::
    VariadicDynCastAllOfMatcher<clang::Expr, clang::CXXDependentScopeMemberExpr>
        cxxDependentScopeMemberExpr;

AST_MATCHER(clang::FunctionDecl, isOverloadedOperator) {
  return Node.isOverloadedOperator();
}

AST_MATCHER(clang::CXXMethodDecl, isInstanceMethod) {
  return Node.isInstance();
}

AST_MATCHER_P(clang::FunctionTemplateDecl,
              templatedDecl,
              clang::ast_matchers::internal::Matcher<clang::FunctionDecl>,
              InnerMatcher) {
  return InnerMatcher.matches(*Node.getTemplatedDecl(), Finder, Builder);
}

AST_MATCHER_P(clang::Decl,
              hasCanonicalDecl,
              clang::ast_matchers::internal::Matcher<clang::Decl>,
              InnerMatcher) {
  return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
}

// Matches a CXXMethodDecl of a method declared via MOCK_METHODx macro if such
// method mocks a method matched by the InnerMatcher.  For example if "foo"
// matcher matches "interfaceMethod", then mocksMethod(foo()) will match
// "gmock_interfaceMethod" declared by MOCK_METHOD_x(interfaceMethod).
AST_MATCHER_P(clang::CXXMethodDecl,
              mocksMethod,
              clang::ast_matchers::internal::Matcher<clang::CXXMethodDecl>,
              InnerMatcher) {
  if (!Node.getDeclName().isIdentifier())
    return false;

  llvm::StringRef method_name = Node.getName();
  if (!method_name.startswith(kGMockMethodNamePrefix))
    return false;

  llvm::StringRef mocked_method_name =
      method_name.substr(strlen(kGMockMethodNamePrefix));
  for (const auto& potentially_mocked_method : Node.getParent()->methods()) {
    clang::DeclarationName decl_name = potentially_mocked_method->getDeclName();
    if (!decl_name.isIdentifier() ||
        potentially_mocked_method->getName() != mocked_method_name)
      continue;
    if (potentially_mocked_method->getNumParams() != Node.getNumParams())
      continue;

    if (InnerMatcher.matches(*potentially_mocked_method, Finder, Builder))
      return true;
  }

  return false;
}

class MethodBlocklist {
 public:
  explicit MethodBlocklist(const std::string& filepath) {
    if (!filepath.empty())
      ParseInputFile(filepath);
  }

  bool Contains(const clang::FunctionDecl& method) const {
    if (!method.getDeclName().isIdentifier())
      return false;

    auto it = method_to_classes_.find(method.getName());
    if (it == method_to_classes_.end())
      return false;

    // |method_context| is either
    // 1) a CXXRecordDecl (i.e. blink::Document) or
    // 2) a NamespaceDecl (i.e. blink::DOMWindowTimers).
    const clang::NamedDecl* method_context =
        clang::dyn_cast<clang::NamedDecl>(method.getDeclContext());
    if (!method_context)
      return false;
    if (!method_context->getDeclName().isIdentifier())
      return false;

    const llvm::StringSet<>& classes = it->second;
    auto it2 = classes.find(method_context->getName());
    if (it2 == classes.end())
      return false;

    // No need to verify here that |actual_class| is in the |blink| namespace -
    // this will be done by other matchers elsewhere.

    // TODO(lukasza): Do we need to consider return type and/or param types?

    // TODO(lukasza): Do we need to consider param count?

    return true;
  }

 private:
  // Each line is expected to have the following format:
  // <class name>:::<method name>:::<number of arguments>
  void ParseInputFile(const std::string& filepath) {
    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file_or_err =
        llvm::MemoryBuffer::getFile(filepath);
    if (std::error_code err = file_or_err.getError()) {
      llvm::errs() << "ERROR: Cannot open the file specified in --"
                   << kMethodBlocklistParamName << " argument: " << filepath
                   << ": " << err.message() << "\n";
      assert(false);
      return;
    }

    llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#');
    for (; !it.is_at_eof(); ++it) {
      llvm::StringRef line = it->trim();
      if (line.empty())
        continue;

      // Split the line into ':::'-delimited parts.
      const size_t kExpectedNumberOfParts = 3;
      llvm::SmallVector<llvm::StringRef, kExpectedNumberOfParts> parts;
      line.split(parts, ":::");
      if (parts.size() != kExpectedNumberOfParts) {
        llvm::errs() << "ERROR: Parsing error - expected "
                     << kExpectedNumberOfParts
                     << " ':::'-delimited parts: " << filepath << ":"
                     << it.line_number() << ": " << line << "\n";
        assert(false);
        continue;
      }

      // Parse individual parts.
      llvm::StringRef class_name = parts[0];
      llvm::StringRef method_name = parts[1];
      // ignoring parts[2] - the (not so trustworthy) number of parameters.

      // Store the new entry.
      method_to_classes_[method_name].insert(class_name);
    }
  }

  // Stores methods to blocklist in a map:
  // method name -> class name -> set of all allowed numbers of arguments.
  llvm::StringMap<llvm::StringSet<>> method_to_classes_;
};

AST_MATCHER_P(clang::FunctionDecl,
              isBlocklistedMethod,
              MethodBlocklist,
              Blocklist) {
  return Blocklist.Contains(Node);
}

// If |InnerMatcher| matches |top|, then the returned matcher will match:
// - |top::function|
// - |top::Class::method|
// - |top::internal::Class::method|
AST_MATCHER_P(
    clang::NestedNameSpecifier,
    hasTopLevelPrefix,
    clang::ast_matchers::internal::Matcher<clang::NestedNameSpecifier>,
    InnerMatcher) {
  const clang::NestedNameSpecifier* NodeToMatch = &Node;
  while (NodeToMatch->getPrefix())
    NodeToMatch = NodeToMatch->getPrefix();
  return InnerMatcher.matches(*NodeToMatch, Finder, Builder);
}

// This will narrow CXXCtorInitializers down for both FieldDecls and
// IndirectFieldDecls (ie. anonymous unions and such). In both cases
// getAnyMember() will return a FieldDecl which we can match against.
AST_MATCHER_P(clang::CXXCtorInitializer,
              forAnyField,
              clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
              InnerMatcher) {
  const clang::FieldDecl* NodeAsDecl = Node.getAnyMember();
  return (NodeAsDecl != nullptr &&
          InnerMatcher.matches(*NodeAsDecl, Finder, Builder));
}

// Matches if all the overloads in the lookup set match the provided matcher.
AST_MATCHER_P(clang::OverloadExpr,
              allOverloadsMatch,
              clang::ast_matchers::internal::Matcher<clang::NamedDecl>,
              InnerMatcher) {
  if (Node.getNumDecls() == 0)
    return false;

  for (clang::NamedDecl* decl : Node.decls()) {
    if (!InnerMatcher.matches(*decl, Finder, Builder))
      return false;
  }
  return true;
}

void PrintForDiagnostics(clang::raw_ostream& os,
                         const clang::FunctionDecl& decl) {
  decl.getLocStart().print(os, decl.getASTContext().getSourceManager());
  os << ": ";
  decl.getNameForDiagnostic(os, decl.getASTContext().getPrintingPolicy(), true);
}

template <typename T>
bool MatchAllOverriddenMethods(
    const clang::CXXMethodDecl& decl,
    T&& inner_matcher,
    clang::ast_matchers::internal::ASTMatchFinder* finder,
    clang::ast_matchers::internal::BoundNodesTreeBuilder* builder) {
  bool override_matches = false;
  bool override_not_matches = false;

  for (auto it = decl.begin_overridden_methods();
       it != decl.end_overridden_methods(); ++it) {
    if (MatchAllOverriddenMethods(**it, inner_matcher, finder, builder))
      override_matches = true;
    else
      override_not_matches = true;
  }

  // If this fires we have a class overriding a method that matches, and a
  // method that does not match the inner matcher. In that case we will match
  // one ancestor method but not the other. If we rename one of the and not the
  // other it will break what this class overrides, disconnecting it from the
  // one we did not rename which creates a behaviour change. So assert and
  // demand the user to fix the code first (or add the method to our
  // blocklist T_T).
  if (override_matches && override_not_matches) {
    // blink::InternalSettings::trace method overrides
    // 1) blink::InternalSettingsGenerated::trace
    //    (won't be renamed because it is in generated code)
    // 2) blink::Supplement<blink::Page>::trace
    //    (will be renamed).
    // It is safe to rename blink::InternalSettings::trace, because
    // both 1 and 2 will both be renamed (#1 via manual changes of the code
    // generator for DOM bindings and #2 via the clang tool).
    auto internal_settings_class_decl = cxxRecordDecl(
        hasName("InternalSettings"),
        hasParent(namespaceDecl(hasName("blink"),
                                hasParent(translationUnitDecl()))));
    auto is_method_safe_to_rename = cxxMethodDecl(
        hasName("trace"),
        anyOf(hasParent(internal_settings_class_decl),  // in .h file
              has(nestedNameSpecifier(specifiesType(    // in .cpp file
                  hasDeclaration(internal_settings_class_decl))))));
    if (IsMatching(is_method_safe_to_rename, decl, decl.getASTContext()))
      return true;

    // For previously unknown conflicts, error out and require a human to
    // analyse the problem (rather than falling back to a potentially unsafe /
    // code semantics changing rename).
    llvm::errs() << "ERROR: ";
    PrintForDiagnostics(llvm::errs(), decl);
    llvm::errs() << " method overrides "
                 << "some virtual methods that will be automatically renamed "
                 << "and some that won't be renamed.";
    llvm::errs() << "\n";
    for (auto it = decl.begin_overridden_methods();
         it != decl.end_overridden_methods(); ++it) {
      if (MatchAllOverriddenMethods(**it, inner_matcher, finder, builder))
        llvm::errs() << "Overriden method that will be renamed: ";
      else
        llvm::errs() << "Overriden method that will not be renamed: ";
      PrintForDiagnostics(llvm::errs(), **it);
      llvm::errs() << "\n";
    }
    llvm::errs() << "\n";
    assert(false);
  }

  // If the method overrides something that doesn't match, so the method itself
  // doesn't match.
  if (override_not_matches)
    return false;

  // If the method overrides something that matches, so the method ifself
  // matches.
  if (override_matches)
    return true;

  return inner_matcher.matches(decl, finder, builder);
}

AST_MATCHER_P(clang::CXXMethodDecl,
              includeAllOverriddenMethods,
              clang::ast_matchers::internal::Matcher<clang::CXXMethodDecl>,
              InnerMatcher) {
  return MatchAllOverriddenMethods(Node, InnerMatcher, Finder, Builder);
}

// Matches |T::m| and/or |x->T::m| and/or |x->m| CXXDependentScopeMemberExpr
// if member |m| comes from a type that matches the InnerMatcher.
AST_MATCHER_P(clang::CXXDependentScopeMemberExpr,
              hasMemberFromType,
              clang::ast_matchers::internal::Matcher<clang::QualType>,
              InnerMatcher) {
  // Given |T::m| and/or |x->T::m| and/or |x->m| ...
  if (clang::NestedNameSpecifier* nestedNameSpecifier = Node.getQualifier()) {
    // ... if |T| is present, then InnerMatcher has to match |T|.
    clang::QualType qualType(nestedNameSpecifier->getAsType(), 0);
    return InnerMatcher.matches(qualType, Finder, Builder);
  } else {
    // ... if there is no |T|, then InnerMatcher has to match the type of |x|.
    clang::Expr* base_expr = Node.isImplicitAccess() ? nullptr : Node.getBase();
    return base_expr &&
           InnerMatcher.matches(base_expr->getType(), Finder, Builder);
  }
}

// Matches |const Class<T>&| QualType if InnerMatcher matches |Class<T>|.
AST_MATCHER_P(clang::QualType,
              hasBaseType,
              clang::ast_matchers::internal::Matcher<clang::Type>,
              InnerMatcher) {
  const clang::Type* type = Node.getTypePtrOrNull();
  return type && InnerMatcher.matches(*type, Finder, Builder);
}

bool IsMethodOverrideOf(const clang::CXXMethodDecl& decl,
                        const char* class_name) {
  if (decl.getParent()->getQualifiedNameAsString() == class_name)
    return true;
  for (auto it = decl.begin_overridden_methods();
       it != decl.end_overridden_methods(); ++it) {
    if (IsMethodOverrideOf(**it, class_name))
      return true;
  }
  return false;
}

bool IsBlacklistedFunctionName(llvm::StringRef name) {
  // https://crbug.com/672902: Method names with an underscore are typically
  // mimicked after std library / are typically not originating from Blink.
  // Do not rewrite such names (like push_back, emplace_back, etc.).
  if (name.find('_') != llvm::StringRef::npos)
    return true;

  return false;
}

bool IsBlacklistedFreeFunctionName(llvm::StringRef name) {
  // swap() functions should match the signature of std::swap for ADL tricks.
  return name == "swap";
}

bool IsBlacklistedInstanceMethodName(llvm::StringRef name) {
  static const char* kBlacklistedNames[] = {
      // We should avoid renaming the method names listed below, because
      // 1. They are used in templated code (e.g. in <algorithms>)
      // 2. They (begin+end) are used in range-based for syntax sugar
      //    - for (auto x : foo) { ... }  // <- foo.begin() will be called.
      "begin", "end", "rbegin", "rend", "lock", "unlock", "try_lock",

      // https://crbug.com/672902: Should not rewrite names that mimick methods
      // from std library.
      "at", "back", "empty", "erase", "front", "insert", "length", "size",
  };
  for (const auto& b : kBlacklistedNames) {
    if (name == b)
      return true;
  }
  return false;
}

bool IsBlacklistedMethodName(llvm::StringRef name) {
  return IsBlacklistedFunctionName(name) ||
         IsBlacklistedInstanceMethodName(name);
}

bool IsBlacklistedFunction(const clang::FunctionDecl& decl) {
  if (!decl.getDeclName().isIdentifier())
    return false;

  clang::StringRef name = decl.getName();
  return IsBlacklistedFunctionName(name) || IsBlacklistedFreeFunctionName(name);
}

bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) {
  if (!decl.getDeclName().isIdentifier())
    return false;

  clang::StringRef name = decl.getName();
  if (IsBlacklistedFunctionName(name))
    return true;

  // Remaining cases are only applicable to instance methods.
  if (decl.isStatic())
    return false;

  if (IsBlacklistedInstanceMethodName(name))
    return true;

  // Subclasses of InspectorAgent will subclass "disable()" from both blink and
  // from gen/, which is problematic, but DevTools folks don't want to rename
  // it or split this up. So don't rename it at all.
  if (name.equals("disable") &&
      IsMethodOverrideOf(decl, "blink::InspectorBaseAgent"))
    return true;

  return false;
}

AST_MATCHER(clang::FunctionDecl, isBlacklistedFunction) {
  return IsBlacklistedFunction(Node);
}

AST_MATCHER(clang::CXXMethodDecl, isBlacklistedMethod) {
  return IsBlacklistedMethod(Node);
}

bool IsKnownTraitName(clang::StringRef name) {
  // This set of names is globally a type trait throughout chromium.
  return name == "safeToCompareToEmptyOrDeleted";
}

AST_MATCHER(clang::VarDecl, isKnownTraitName) {
  return Node.getDeclName().isIdentifier() && IsKnownTraitName(Node.getName());
}

AST_MATCHER(clang::Decl, isDeclInGeneratedFile) {
  // This matcher mimics the built-in isExpansionInFileMatching matcher from
  // llvm/tools/clang/include/clang/ASTMatchers/ASTMatchers.h, except:
  // - It special cases some files (e.g. doesn't skip renaming of identifiers
  //   from gen/blink/core/ComputedStyleBase.h)

  const clang::SourceManager& source_manager =
      Node.getASTContext().getSourceManager();

  // TODO(lukasza): Consider using getSpellingLoc below.
  // The built-in isExpansionInFileMatching matcher uses getExpansionLoc below.
  // We could consider using getSpellingLoc (which properly handles things like
  // SETTINGS_GETTERS_AND_SETTERS macro which is defined in generated code
  // (gen/blink/core/SettingsMacros.h), but expanded in non-generated code
  // (third_party/WebKit/Source/core/frame/Settings.h).
  clang::SourceLocation loc =
      source_manager.getExpansionLoc(Node.getLocStart());

  // TODO(lukasza): jump out of scratch space if token concatenation was used.
  if (loc.isInvalid())
    return false;

  const clang::FileEntry* file_entry =
      source_manager.getFileEntryForID(source_manager.getFileID(loc));
  if (!file_entry)
    return false;

  bool is_generated_file = false;
  bool is_computed_style_base_cpp =
      llvm::sys::path::filename(file_entry->getName())
          .equals("ComputedStyleBase.h");
  for (auto it = llvm::sys::path::begin(file_entry->getName());
       it != llvm::sys::path::end(file_entry->getName()); ++it) {
    if (it->equals("gen")) {
      is_generated_file = true;
      break;
    }
  }
  // ComputedStyleBase is intentionally not treated as a generated file, since
  // style definitions are split between generated and non-generated code. It's
  // easier to have the tool just automatically rewrite references to generated
  // code as well, with a small manual patch to fix the code generators.
  return is_generated_file && !is_computed_style_base_cpp;
}

// Helper to convert from a camelCaseName to camel_case_name. It uses some
// heuristics to try to handle acronyms in camel case names correctly.
std::string CamelCaseToUnderscoreCase(StringRef input) {
  std::string output;
  bool needs_underscore = false;
  bool was_lowercase = false;
  bool was_uppercase = false;
  bool first_char = true;
  // Iterate in reverse to minimize the amount of backtracking.
  for (const unsigned char* i = input.bytes_end() - 1; i >= input.bytes_begin();
       --i) {
    char c = *i;
    bool is_lowercase = clang::isLowercase(c);
    bool is_uppercase = clang::isUppercase(c);
    c = clang::toLowercase(c);
    // Transitioning from upper to lower case requires an underscore. This is
    // needed to handle names with acronyms, e.g. handledHTTPRequest needs a '_'
    // in 'dH'. This is a complement to the non-acronym case further down.
    if (was_uppercase && is_lowercase)
      needs_underscore = true;
    if (needs_underscore) {
      output += '_';
      needs_underscore = false;
    }
    output += c;
    // Handles the non-acronym case: transitioning from lower to upper case
    // requires an underscore when emitting the next character, e.g. didLoad
    // needs a '_' in 'dL'.
    if (!first_char && was_lowercase && is_uppercase)
      needs_underscore = true;
    was_lowercase = is_lowercase;
    was_uppercase = is_uppercase;
    first_char = false;
  }
  std::reverse(output.begin(), output.end());
  return output;
}

bool CanBeEvaluatedAtCompileTime(const clang::Stmt* stmt,
                                 const clang::ASTContext& context) {
  auto* expr = clang::dyn_cast<clang::Expr>(stmt);
  if (!expr) {
    // If the statement is not an expression then it's a constant.
    return true;
  }

  // Function calls create non-consistent behaviour. For some template
  // instantiations they can be constexpr while for others they are not, which
  // changes the output of isEvaluatable().
  if (expr->hasNonTrivialCall(context))
    return false;

  // Recurse on children. If they are all const (or are uses of template
  // input) then the statement can be considered const. For whatever reason the
  // below checks can give different-and-less-consistent responses if we call
  // them on a complex expression than if we call them on the most primitive
  // pieces (some pieces would say false but the whole thing says true).
  for (auto* child : expr->children()) {
    if (!CanBeEvaluatedAtCompileTime(child, context))
      return false;
  }

  // If the expression depends on template input, we can not call
  // isEvaluatable() on it as it will do bad things/crash.
  if (!expr->isInstantiationDependent()) {
    // If the expression can be evaluated at compile time, then it should have a
    // kFoo style name. Otherwise, not.
    return expr->isEvaluatable(context);
  }

  // We do our best to figure out special cases as we come across them here, for
  // template dependent situations. Some cases in code are only considered
  // instantiation dependent for some template instantiations! Which is
  // terrible! So most importantly we try to match isEvaluatable in those cases.
  switch (expr->getStmtClass()) {
    case clang::Stmt::CXXThisExprClass:
      return false;
    case clang::Stmt::DeclRefExprClass: {
      auto* declref = clang::dyn_cast<clang::DeclRefExpr>(expr);
      auto* decl = declref->getDecl();
      if (auto* vardecl = clang::dyn_cast<clang::VarDecl>(decl)) {
        if (auto* initializer = vardecl->getInit())
          return CanBeEvaluatedAtCompileTime(initializer, context);
        return false;
      }
      break;
    }

    default:
      break;
  }

  // Otherwise, we consider depending on template parameters to not interfere
  // with being const.. with exceptions hopefully covered above.
  return true;
}

bool IsProbablyConst(const clang::VarDecl& decl,
                     const clang::ASTContext& context) {
  clang::QualType type = decl.getType();
  if (!type.isConstQualified())
    return false;

  if (type.isVolatileQualified())
    return false;

  if (decl.isConstexpr())
    return true;

  // Parameters should not be renamed to |kFooBar| style (even if they are
  // const and have an initializer (aka default value)).
  if (clang::isa<clang::ParmVarDecl>(&decl))
    return false;

  // http://google.github.io/styleguide/cppguide.html#Constant_Names
  // Static variables that are const-qualified should use kConstantStyle naming.
  if (decl.getStorageDuration() == clang::SD_Static)
    return true;

  const clang::Expr* initializer = decl.getInit();
  if (!initializer)
    return false;

  return CanBeEvaluatedAtCompileTime(initializer, context);
}

AST_MATCHER_P(clang::QualType, hasString, std::string, ExpectedString) {
  return ExpectedString == Node.getAsString();
}

bool ShouldPrefixFunctionName(const std::string& old_method_name) {
  // Functions that are named similarily to a type - they should be prefixed
  // with a "Get" prefix.
  static const char* kConflictingMethods[] = {"accumulatorMap",
                                              "animationWorklet",
                                              "attrNodeList",
                                              "audioWorklet",
                                              "binaryType",
                                              "blob",
                                              "channelCountMode",
                                              "color",
                                              "compositorElementId",
                                              "constructionStack",
                                              "controlSize",
                                              "counterDirectives",
                                              "counterMaps",
                                              "document",
                                              "dragOperation",
                                              "element",
                                              "emptyChromeClient",
                                              "emptyEditorClient",
                                              "emptySpellCheckerClient",
                                              "entryType",
                                              "error",
                                              "eventTargetDataMap",
                                              "fileUtilities",
                                              "font",
                                              "frame",
                                              "frameBlameContext",
                                              "frontend",
                                              "gridCell",
                                              "harfBuzzFontCache",
                                              "hash",
                                              "heapObjectHeader",
                                              "heapObjectSet",
                                              "iconURL",
                                              "image",
                                              "infoMap",
                                              "inputMethodController",
                                              "inputType",
                                              "interpolationTypes",
                                              "intervalArena",
                                              "layout",
                                              "layoutBlock",
                                              "layoutObject",
                                              "layoutSize",
                                              "lineCap",
                                              "lineEndings",
                                              "lineJoin",
                                              "listItems",
                                              "locationInBackingMap",
                                              "matchedProperties",
                                              "midpointState",
                                              "modifiers",
                                              "mouseEvent",
                                              "name",
                                              "navigationType",
                                              "node",
                                              "notificationManager",
                                              "originAccessMap",
                                              "outcome",
                                              "pagePopup",
                                              "paintWorklet",
                                              "path",
                                              "position",
                                              "presentationAttributeCache",
                                              "processingInstruction",
                                              "qualifiedNameCache",
                                              "readyState",
                                              "referrer",
                                              "referrerPolicy",
                                              "relList",
                                              "resource",
                                              "response",
                                              "restrictedKeyMap",
                                              "sandboxSupport",
                                              "screenInfo",
                                              "screenOrientationController",
                                              "scrollAnimator",
                                              "scrollbarPainterMap",
                                              "scrollbarSet",
                                              "selectionInDOMTree",
                                              "selectionInFlatTree",
                                              "selectionVisualRectMap",
                                              "selectorTextCache",
                                              "settings",
                                              "shadowRootType",
                                              "signalingState",
                                              "snapshotById",
                                              "state",
                                              "stickyConstraintsMap",
                                              "string",
                                              "styleSharingList",
                                              "styleSheet",
                                              "supplementable",
                                              "text",
                                              "textAlign",
                                              "textBaseline",
                                              "textDirection",
                                              "theme",
                                              "thread",
                                              "timing",
                                              "topLevelBlameContext",
                                              "type",
                                              "vector",
                                              "visibleSelection",
                                              "visibleSelectionInFlatTree",
                                              "weakHeapObjectSet",
                                              "webFrame",
                                              "widget",
                                              "wordBoundaries",
                                              "workerThread",
                                              "worldId",
                                              "worldMap",
                                              "wrapperTypeInfo"};
  for (const auto& conflicting_method : kConflictingMethods) {
    if (old_method_name == conflicting_method)
      return true;
  }

  return false;
}

AST_MATCHER(clang::FunctionDecl, shouldPrefixFunctionName) {
  return Node.getDeclName().isIdentifier() &&
      ShouldPrefixFunctionName(Node.getName().str());
}

bool GetNameForDecl(const clang::FunctionDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  name = decl.getName().str();
  name[0] = clang::toUppercase(name[0]);

  // Given
  //   class Foo {};
  //   class DerivedFoo : class Foo;
  //   using Bar = Foo;
  //   Bar f1();  // <- |Bar| would be matched by hasString("Bar") below.
  //   Bar f2();  // <- |Bar| would be matched by hasName("Foo") below.
  //   DerivedFoo f3();  // <- |DerivedFoo| matched by isDerivedFrom(...) below.
  // |type_with_same_name_as_function| matcher matches Bar and Foo return types.
  auto type_with_same_name_as_function = qualType(anyOf(
      // hasString matches the type as spelled (Bar above).
      hasString(name),
      // hasDeclaration matches resolved type (Foo or DerivedFoo above).
      hasDeclaration(namedDecl(hasName(name)))));

  // |type_containing_same_name_as_function| matcher will match all of the
  // return types below:
  // - Foo foo()  // Direct application of |type_with_same_name_as_function|.
  // - Foo* foo()  // |hasDescendant| traverses references/pointers.
  // - RefPtr<Foo> foo()  // |hasDescendant| traverses template arguments.
  auto type_containing_same_name_as_function =
      qualType(anyOf(type_with_same_name_as_function,
                     hasDescendant(type_with_same_name_as_function)));
  // https://crbug.com/582312: Prepend "Get" if method name conflicts with
  // return type.
  auto conflict_matcher = functionDecl(anyOf(
      // For functions and non-virtual or base method implementations just
      // compare with the immediate return type.
      functionDecl(returns(type_containing_same_name_as_function),
                   unless(cxxMethodDecl(isOverride()))),
      // For methods that override one or more methods, compare with the return
      // type of the *base* methods.
      cxxMethodDecl(isOverride(), forEachOverridden(returns(
                                      type_containing_same_name_as_function))),
      // And also check hardcoded list of function names to prefix with "Get".
      shouldPrefixFunctionName()));
  if (IsMatching(conflict_matcher, decl, context))
    name = "Get" + name;

  return true;
}

bool GetNameForDecl(const clang::EnumConstantDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  StringRef original_name = decl.getName();

  // If it's already correct leave it alone.
  if (original_name.size() >= 2 && original_name[0] == 'k' &&
      clang::isUppercase(original_name[1]))
    return false;

  bool is_shouty = true;
  for (char c : original_name) {
    if (!clang::isUppercase(c) && !clang::isDigit(c) && c != '_') {
      is_shouty = false;
      break;
    }
  }

  if (is_shouty)
    return false;

  name = 'k';  // k prefix on enum values.
  name += original_name;
  name[1] = clang::toUppercase(name[1]);
  return true;
}

bool GetNameForDecl(const clang::FieldDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  StringRef original_name = decl.getName();
  bool member_prefix = original_name.startswith(kBlinkFieldPrefix);

  StringRef rename_part = !member_prefix
                              ? original_name
                              : original_name.substr(strlen(kBlinkFieldPrefix));
  name = CamelCaseToUnderscoreCase(rename_part);

  // Assume that prefix of m_ was intentional and always replace it with a
  // suffix _.
  if (member_prefix && name.back() != '_')
    name += '_';

  return true;
}

bool GetNameForDecl(const clang::VarDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  StringRef original_name = decl.getName();

  // Nothing to do for unnamed parameters.
  if (clang::isa<clang::ParmVarDecl>(decl) && original_name.empty())
    return false;

  // This is a type trait that appears in consumers of WTF as well as inside
  // WTF. We want it to be named in this_style_of_case accordingly.
  if (IsKnownTraitName(original_name)) {
    name = CamelCaseToUnderscoreCase(original_name);
    return true;
  }

  // static class members match against VarDecls. Blink style dictates that
  // these should be prefixed with `s_`, so strip that off. Also check for `m_`
  // and strip that off too, for code that accidentally uses the wrong prefix.
  if (original_name.startswith(kBlinkStaticMemberPrefix))
    original_name = original_name.substr(strlen(kBlinkStaticMemberPrefix));
  else if (original_name.startswith(kBlinkFieldPrefix))
    original_name = original_name.substr(strlen(kBlinkFieldPrefix));

  bool is_const = IsProbablyConst(decl, context);
  if (is_const) {
    // Don't try to rename constants that already conform to Chrome style.
    if (original_name.size() >= 2 && original_name[0] == 'k' &&
        clang::isUppercase(original_name[1]))
      return false;
    // Or names are spelt with underscore casing. While they are actually
    // compile consts, the author wrote it explicitly as a variable not as
    // a constant (they would have used kFormat otherwise here), so preserve
    // it rather than try to mangle a kFormat out of it.
    if (original_name.find('_') != StringRef::npos)
      return false;

    name = 'k';
    name.append(original_name.data(), original_name.size());
    name[1] = clang::toUppercase(name[1]);
  } else {
    name = CamelCaseToUnderscoreCase(original_name);

    // Non-const variables with static storage duration at namespace scope are
    // prefixed with `g_' to reduce the likelihood of a naming collision.
    const clang::DeclContext* decl_context = decl.getDeclContext();
    if (name.find("g_") != 0 && decl.hasGlobalStorage() &&
        decl_context->isNamespace())
      name.insert(0, "g_");
  }

  // Static members end with _ just like other members, but constants should
  // not.
  if (!is_const && decl.isStaticDataMember()) {
    name += '_';
  }

  return true;
}

bool GetNameForDecl(const clang::FunctionTemplateDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  clang::FunctionDecl* templated_function = decl.getTemplatedDecl();
  return GetNameForDecl(*templated_function, context, name);
}

bool GetNameForDecl(const clang::NamedDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  if (auto* function = clang::dyn_cast<clang::FunctionDecl>(&decl))
    return GetNameForDecl(*function, context, name);
  if (auto* var = clang::dyn_cast<clang::VarDecl>(&decl))
    return GetNameForDecl(*var, context, name);
  if (auto* field = clang::dyn_cast<clang::FieldDecl>(&decl))
    return GetNameForDecl(*field, context, name);
  if (auto* function_template =
          clang::dyn_cast<clang::FunctionTemplateDecl>(&decl))
    return GetNameForDecl(*function_template, context, name);
  if (auto* enumc = clang::dyn_cast<clang::EnumConstantDecl>(&decl))
    return GetNameForDecl(*enumc, context, name);

  return false;
}

bool GetNameForDecl(const clang::UsingDecl& decl,
                    clang::ASTContext& context,
                    std::string& name) {
  assert(decl.shadow_size() > 0);

  // If a using declaration's targeted declaration is a set of overloaded
  // functions, it can introduce multiple shadowed declarations. Just using the
  // first one is OK, since overloaded functions have the same name, by
  // definition.
  return GetNameForDecl(*decl.shadow_begin()->getTargetDecl(), context, name);
}

template <typename Type>
struct TargetNodeTraits;

template <>
struct TargetNodeTraits<clang::NamedDecl> {
  static clang::SourceLocation GetLoc(const clang::NamedDecl& decl) {
    return decl.getLocation();
  }
  static const char* GetName() { return "decl"; }
  static const char* GetType() { return "NamedDecl"; }
};

template <>
struct TargetNodeTraits<clang::MemberExpr> {
  static clang::SourceLocation GetLoc(const clang::MemberExpr& expr) {
    return expr.getMemberLoc();
  }
  static const char* GetName() { return "expr"; }
  static const char* GetType() { return "MemberExpr"; }
};

template <>
struct TargetNodeTraits<clang::DeclRefExpr> {
  static clang::SourceLocation GetLoc(const clang::DeclRefExpr& expr) {
    return expr.getLocation();
  }
  static const char* GetName() { return "expr"; }
  static const char* GetType() { return "DeclRefExpr"; }
};

template <>
struct TargetNodeTraits<clang::DependentScopeDeclRefExpr> {
  static clang::SourceLocation GetLoc(
      const clang::DependentScopeDeclRefExpr& expr) {
    return expr.getLocation();
  }
  static const char* GetName() { return "expr"; }
};

template <>
struct TargetNodeTraits<clang::CXXDependentScopeMemberExpr> {
  static clang::SourceLocation GetLoc(
      const clang::CXXDependentScopeMemberExpr& expr) {
    return expr.getMemberLoc();
  }
  static const char* GetName() { return "expr"; }
};

template <>
struct TargetNodeTraits<clang::CXXCtorInitializer> {
  static clang::SourceLocation GetLoc(const clang::CXXCtorInitializer& init) {
    assert(init.isWritten());
    return init.getSourceLocation();
  }
  static const char* GetName() { return "initializer"; }
  static const char* GetType() { return "CXXCtorInitializer"; }
};

template <>
struct TargetNodeTraits<clang::UnresolvedLookupExpr> {
  static clang::SourceLocation GetLoc(const clang::UnresolvedLookupExpr& expr) {
    return expr.getNameLoc();
  }
  static const char* GetName() { return "expr"; }
  static const char* GetType() { return "UnresolvedLookupExpr"; }
};

template <>
struct TargetNodeTraits<clang::UnresolvedMemberExpr> {
  static clang::SourceLocation GetLoc(const clang::UnresolvedMemberExpr& expr) {
    return expr.getMemberLoc();
  }
  static const char* GetName() { return "expr"; }
  static const char* GetType() { return "UnresolvedMemberExpr"; }
};

template <>
struct TargetNodeTraits<clang::UnresolvedUsingValueDecl> {
  static clang::SourceLocation GetLoc(
      const clang::UnresolvedUsingValueDecl& decl) {
    return decl.getNameInfo().getLoc();
  }
  static const char* GetName() { return "decl"; }
  static const char* GetType() { return "UnresolvedUsingValueDecl"; }
};

template <typename TargetNode>
class RewriterBase : public MatchFinder::MatchCallback {
 public:
  explicit RewriterBase(std::set<Replacement>* replacements,
                        RenameCategory category)
      : replacements_(replacements), edit_tracker_(category) {}

  const TargetNode& GetTargetNode(const MatchFinder::MatchResult& result) {
    const TargetNode* target_node = result.Nodes.getNodeAs<TargetNode>(
        TargetNodeTraits<TargetNode>::GetName());
    assert(target_node);
    return *target_node;
  }

  bool GenerateReplacement(const MatchFinder::MatchResult& result,
                           clang::SourceLocation loc,
                           llvm::StringRef old_name,
                           std::string new_name,
                           Replacement* replacement) {
    const clang::ASTContext& context = *result.Context;
    const clang::SourceManager& source_manager = *result.SourceManager;

    if (loc.isMacroID()) {
      // Try to jump "above" the scratch buffer if |loc| is inside
      // token##Concatenation.
      const int kMaxJumps = 5;
      bool verified_out_of_scratch_space = false;
      for (int i = 0; i < kMaxJumps && !verified_out_of_scratch_space; i++) {
        clang::SourceLocation spell = source_manager.getSpellingLoc(loc);
        verified_out_of_scratch_space =
            source_manager.getBufferName(spell) != "<scratch space>";
        if (!verified_out_of_scratch_space)
          loc = source_manager.getImmediateMacroCallerLoc(loc);
      }
      if (!verified_out_of_scratch_space)
        return false;
    }

    // If the edit affects only the first character of the identifier, then
    // narrow down the edit to only this single character.  This is important
    // for dealing with toFooBar -> ToFooBar method renaming when the method
    // name is built using macro token concatenation like to##macroArgument - in
    // this case we should only rewrite "t" -> "T" and leave "o##macroArgument"
    // untouched.
    llvm::StringRef expected_old_text = old_name;
    llvm::StringRef new_text = new_name;
    if (loc.isMacroID() && expected_old_text.substr(1) == new_text.substr(1)) {
      expected_old_text = expected_old_text.substr(0, 1);
      new_text = new_text.substr(0, 1);
    }
    clang::SourceLocation spell = source_manager.getSpellingLoc(loc);
    clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
        spell, spell.getLocWithOffset(expected_old_text.size()));

    // We need to ensure that |actual_old_text| is the same as
    // |expected_old_text| - it can be different if |actual_old_text| contains
    // a macro argument (see DEFINE_WITH_TOKEN_CONCATENATION2 in
    // macros-original.cc testcase).
    StringRef actual_old_text = clang::Lexer::getSourceText(
        range, source_manager, context.getLangOpts());
    if (actual_old_text != expected_old_text)
      return false;

    if (replacement) {
      // If there's already a replacement for this location, don't emit any
      // other replacements to avoid potential naming conflicts. This is
      // primarily to avoid problems when a function and a parameter are defined
      // by the same macro argument.
      if (!GetRewrittenLocs().emplace(spell).second)
        return false;

      *replacement = Replacement(source_manager, range, new_text);
    }
    return true;
  }

  virtual clang::SourceLocation GetTargetLoc(
      const MatchFinder::MatchResult& result) {
    return TargetNodeTraits<TargetNode>::GetLoc(GetTargetNode(result));
  }

  void AddReplacement(const MatchFinder::MatchResult& result,
                      llvm::StringRef old_name,
                      std::string new_name) {
    if (old_name == new_name)
      return;

    clang::SourceLocation loc = GetTargetLoc(result);
    if (loc.isInvalid())
      return;

    Replacement replacement;
    if (!GenerateReplacement(result, loc, old_name, new_name, &replacement))
      return;

    replacements_->insert(std::move(replacement));
    edit_tracker_.Add(*result.SourceManager, loc, old_name, new_name);
  }

  const EditTracker* edit_tracker() const { return &edit_tracker_; }

 private:
  std::set<Replacement>* const replacements_;
  EditTracker edit_tracker_;
};

template <typename DeclNode>
RenameCategory GetCategory();
template <>
RenameCategory GetCategory<clang::FieldDecl>() {
  return RenameCategory::kField;
}
template <>
RenameCategory GetCategory<clang::VarDecl>() {
  return RenameCategory::kVariable;
}
template <>
RenameCategory GetCategory<clang::FunctionDecl>() {
  return RenameCategory::kFunction;
}
template <>
RenameCategory GetCategory<clang::CXXMethodDecl>() {
  return RenameCategory::kFunction;
}
template <>
RenameCategory GetCategory<clang::EnumConstantDecl>() {
  return RenameCategory::kEnumValue;
}
template <>
RenameCategory GetCategory<clang::NamedDecl>() {
  return RenameCategory::kUnresolved;
}
template <>
RenameCategory GetCategory<clang::UsingDecl>() {
  return RenameCategory::kUnresolved;
}

template <typename DeclNode, typename TargetNode>
class DeclRewriterBase : public RewriterBase<TargetNode> {
 public:
  using Base = RewriterBase<TargetNode>;

  explicit DeclRewriterBase(std::set<Replacement>* replacements)
      : Base(replacements, GetCategory<DeclNode>()) {}

  void run(const MatchFinder::MatchResult& result) override {
    const DeclNode* decl = result.Nodes.getNodeAs<DeclNode>("decl");
    if (!decl->getDeclName().isIdentifier())
      return;

    assert(decl);
    llvm::StringRef old_name = decl->getName();

    // Return early if there's no name to be renamed.
    if (!decl->getIdentifier())
      return;

    // Get the new name.
    std::string new_name;
    if (!GetNameForDecl(*decl, *result.Context, new_name))
      return;  // If false, the name was not suitable for renaming.

    // Check if we are able to rewrite the decl (to avoid rewriting if the
    // decl's identifier is part of macro##Token##Concatenation).
    clang::SourceLocation decl_loc =
        TargetNodeTraits<clang::NamedDecl>::GetLoc(*decl);
    if (!Base::GenerateReplacement(result, decl_loc, old_name, new_name,
                                   nullptr))
      return;

    Base::AddReplacement(result, old_name, std::move(new_name));
  }
};

using FieldDeclRewriter = DeclRewriterBase<clang::FieldDecl, clang::NamedDecl>;
using VarDeclRewriter = DeclRewriterBase<clang::VarDecl, clang::NamedDecl>;
using MemberRewriter = DeclRewriterBase<clang::FieldDecl, clang::MemberExpr>;
using DeclRefRewriter = DeclRewriterBase<clang::VarDecl, clang::DeclRefExpr>;
using FieldDeclRefRewriter =
    DeclRewriterBase<clang::FieldDecl, clang::DeclRefExpr>;
using FunctionDeclRewriter =
    DeclRewriterBase<clang::FunctionDecl, clang::NamedDecl>;
using FunctionRefRewriter =
    DeclRewriterBase<clang::FunctionDecl, clang::DeclRefExpr>;
using ConstructorInitializerRewriter =
    DeclRewriterBase<clang::FieldDecl, clang::CXXCtorInitializer>;

using MethodDeclRewriter =
    DeclRewriterBase<clang::CXXMethodDecl, clang::NamedDecl>;
using MethodRefRewriter =
    DeclRewriterBase<clang::CXXMethodDecl, clang::DeclRefExpr>;
using MethodMemberRewriter =
    DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr>;

using EnumConstantDeclRewriter =
    DeclRewriterBase<clang::EnumConstantDecl, clang::NamedDecl>;
using EnumConstantDeclRefRewriter =
    DeclRewriterBase<clang::EnumConstantDecl, clang::DeclRefExpr>;

using UnresolvedLookupRewriter =
    DeclRewriterBase<clang::NamedDecl, clang::UnresolvedLookupExpr>;
using UnresolvedMemberRewriter =
    DeclRewriterBase<clang::NamedDecl, clang::UnresolvedMemberExpr>;

using UsingDeclRewriter = DeclRewriterBase<clang::UsingDecl, clang::NamedDecl>;

class GMockMemberRewriter
    : public DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr> {
 public:
  using Base = DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr>;

  explicit GMockMemberRewriter(std::set<Replacement>* replacements)
      : Base(replacements) {}

  std::unique_ptr<clang::PPCallbacks> CreatePreprocessorCallbacks() {
    return std::make_unique<GMockMemberRewriter::PPCallbacks>(this);
  }

  clang::SourceLocation GetTargetLoc(
      const MatchFinder::MatchResult& result) override {
    // Find location of the gmock_##MockedMethod identifier.
    clang::SourceLocation target_loc = Base::GetTargetLoc(result);

    // Find location of EXPECT_CALL or ON_CALL macro invocation.
    clang::SourceLocation macro_call_loc =
        result.SourceManager->getExpansionLoc(target_loc);

    // Map |macro_call_loc| to argument location (location of the method name
    // that needs renaming).
    auto it = gmock_macro_call_to_2nd_arg.find(macro_call_loc);
    if (it == gmock_macro_call_to_2nd_arg.end())
      return clang::SourceLocation();
    return it->second;
  }

 private:
  std::map<clang::SourceLocation, clang::SourceLocation>
      gmock_macro_call_to_2nd_arg;

  // Called from PPCallbacks with the locations of EXPECT_CALL and ON_CALL macro
  // invocation.  Example:
  //   EXPECT_CALL(my_mock, myMethod(123, 456));
  //   ^- expansion_loc     ^- actual_arg_loc
  void RecordGMockMacroInvocation(clang::SourceLocation expansion_loc,
                                  clang::SourceLocation second_arg_loc) {
    gmock_macro_call_to_2nd_arg[expansion_loc] = second_arg_loc;
  }

  class PPCallbacks : public clang::PPCallbacks {
   public:
    explicit PPCallbacks(GMockMemberRewriter* rewriter) : rewriter_(rewriter) {}
    ~PPCallbacks() override {}
    void MacroExpands(const clang::Token& name,
                      const clang::MacroDefinition& def,
                      clang::SourceRange range,
                      const clang::MacroArgs* args) override {
      clang::IdentifierInfo* id = name.getIdentifierInfo();
      if (!id)
        return;

      if (id->getName() != "EXPECT_CALL" && id->getName() != "ON_CALL")
        return;

      if (def.getMacroInfo()->getNumParams() != 2)
        return;

      // TODO(lukasza): Should check if def.getMacroInfo()->getDefinitionLoc()
      // is in testing/gmock/include/gmock/gmock-spec-builders.h but I don't
      // know how to get clang::SourceManager to call getFileName.

      rewriter_->RecordGMockMacroInvocation(
          name.getLocation(), args->getUnexpArgument(1)->getLocation());
    }

   private:
    GMockMemberRewriter* rewriter_;
  };
};

clang::DeclarationName GetUnresolvedName(
    const clang::UnresolvedMemberExpr& expr) {
  return expr.getMemberName();
}

clang::DeclarationName GetUnresolvedName(
    const clang::DependentScopeDeclRefExpr& expr) {
  return expr.getDeclName();
}

clang::DeclarationName GetUnresolvedName(
    const clang::CXXDependentScopeMemberExpr& expr) {
  return expr.getMember();
}

clang::DeclarationName GetUnresolvedName(
    const clang::UnresolvedUsingValueDecl& decl) {
  return decl.getDeclName();
}

// Returns whether |expr_node| is used as a callee in the AST (i.e. if
// |expr_node| needs to resolve to a method or a function).
bool IsCallee(const clang::Expr& expr, clang::ASTContext& context) {
  auto matcher = stmt(hasParent(callExpr(callee(equalsNode(&expr)))));
  return IsMatching(matcher, expr, context);
}

// Returns whether |decl| will be used as a callee in the AST (i.e. if the value
// brought by the using declaration will resolve to a method or a function).
bool IsCallee(const clang::UnresolvedUsingValueDecl& decl,
              clang::ASTContext& /* context */) {
  // Caller (i.e. GuessNameForUnresolvedDependentNode) should have already
  // filtered out fields before calling |IsCallee|.
  clang::IdentifierInfo* info = GetUnresolvedName(decl).getAsIdentifierInfo();
  assert(info);
  bool name_looks_like_a_field = info->getName().startswith(kBlinkFieldPrefix);
  assert(!name_looks_like_a_field);

  // Looking just at clang::UnresolvedUsingValueDecl, we cannot tell whether it
  // refers to something callable or not.  Since fields should have been already
  // filtered out before calling IsCallee (see the assert above), let's assume
  // that |using Base::foo| refers to a method.
  return true;
}

template <typename TargetNode>
class UnresolvedRewriterBase : public RewriterBase<TargetNode> {
 public:
  using Base = RewriterBase<TargetNode>;

  explicit UnresolvedRewriterBase(std::set<Replacement>* replacements)
      : RewriterBase<TargetNode>(replacements, RenameCategory::kUnresolved) {}

  void run(const MatchFinder::MatchResult& result) override {
    const TargetNode& node = Base::GetTargetNode(result);

    clang::DeclarationName decl_name = GetUnresolvedName(node);
    switch (decl_name.getNameKind()) {
      // Do not rewrite this:
      //   return operator T*();
      // into this:
      //   return Operator type - parameter - 0 - 0 * T * ();
      case clang::DeclarationName::NameKind::CXXConversionFunctionName:
      case clang::DeclarationName::NameKind::CXXOperatorName:
      case clang::DeclarationName::NameKind::CXXLiteralOperatorName:
        return;
      default:
        break;
    }

    // Make sure there is an old name + extract the old name.
    clang::IdentifierInfo* info = GetUnresolvedName(node).getAsIdentifierInfo();
    if (!info)
      return;
    llvm::StringRef old_name = info->getName();

    // Try to guess a new name.
    std::string new_name;
    if (GuessNameForUnresolvedDependentNode(node, *result.Context, old_name,
                                            new_name))
      Base::AddReplacement(result, old_name, std::move(new_name));
  }

 private:
  // This method calculates a new name for nodes that depend on template
  // parameters (http://en.cppreference.com/w/cpp/language/dependent_name).  The
  // renaming is based on crude heuristics, because such nodes are not bound to
  // a specific decl until template instantiation - at the point of rename, one
  // cannot tell whether the node will eventually resolve to a field / method /
  // constant / etc.
  //
  // The method returns false if no renaming should be done.
  // Otherwise the method returns true and sets |new_name|.
  bool GuessNameForUnresolvedDependentNode(const TargetNode& node,
                                           clang::ASTContext& context,
                                           llvm::StringRef old_name,
                                           std::string& new_name) {
    // |m_fieldName| -> |field_name_|.
    if (old_name.startswith(kBlinkFieldPrefix)) {
      std::string field_name = old_name.substr(strlen(kBlinkFieldPrefix));
      if (field_name.find('_') == std::string::npos) {
        new_name = CamelCaseToUnderscoreCase(field_name) + "_";
        return true;
      }
    }

    // |T::myMethod(...)| -> |T::MyMethod(...)|.
    if ((old_name.find('_') == std::string::npos) && IsCallee(node, context) &&
        !IsBlacklistedMethodName(old_name)) {
      new_name = old_name;
      new_name[0] = clang::toUppercase(old_name[0]);
      if (ShouldPrefixFunctionName(old_name))
        new_name = "Get" + new_name;
      return true;
    }

    // In the future we can consider more heuristics:
    // - "s_" and "g_" prefixes
    // - "ALL_CAPS"
    // - |T::myStaticField| -> |T::kMyStaticField|
    //   (but have to be careful not to rename |value| in WTF/TypeTraits.h?)
    return false;
  }
};

using UnresolvedDependentMemberRewriter =
    UnresolvedRewriterBase<clang::UnresolvedMemberExpr>;

using UnresolvedUsingValueDeclRewriter =
    UnresolvedRewriterBase<clang::UnresolvedUsingValueDecl>;

using DependentScopeDeclRefExprRewriter =
    UnresolvedRewriterBase<clang::DependentScopeDeclRefExpr>;

using CXXDependentScopeMemberExprRewriter =
    UnresolvedRewriterBase<clang::CXXDependentScopeMemberExpr>;

class SourceFileCallbacks : public clang::tooling::SourceFileCallbacks {
 public:
  explicit SourceFileCallbacks(GMockMemberRewriter* gmock_member_rewriter)
      : gmock_member_rewriter_(gmock_member_rewriter) {
    assert(gmock_member_rewriter);
  }

  ~SourceFileCallbacks() override {}

  // clang::tooling::SourceFileCallbacks override:
  bool handleBeginSource(clang::CompilerInstance& compiler) override {
    compiler.getPreprocessor().addPPCallbacks(
        gmock_member_rewriter_->CreatePreprocessorCallbacks());
    return true;
  }

 private:
  GMockMemberRewriter* gmock_member_rewriter_;
};

}  // namespace

static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);

int main(int argc, const char* argv[]) {
  // TODO(dcheng): Clang tooling should do this itself.
  // http://llvm.org/bugs/show_bug.cgi?id=21627
  llvm::InitializeNativeTarget();
  llvm::InitializeNativeTargetAsmParser();
  llvm::cl::OptionCategory category(
      "rewrite_to_chrome_style: convert Blink style to Chrome style.");
  llvm::cl::opt<std::string> blocklisted_methods_file(
      kMethodBlocklistParamName, llvm::cl::value_desc("filepath"),
      llvm::cl::desc("file listing methods to be blocked (not renamed)"));
  CommonOptionsParser options(argc, argv, category);
  MethodBlocklist method_blocklist(blocklisted_methods_file);
  clang::tooling::ClangTool tool(options.getCompilations(),
                                 options.getSourcePathList());

  MatchFinder match_finder;
  std::set<Replacement> replacements;

  // Blink namespace matchers ========
  auto blink_namespace_decl =
      namespaceDecl(anyOf(hasName("blink"), hasName("WTF")),
                    hasParent(translationUnitDecl()));
  auto protocol_namespace_decl =
      namespaceDecl(hasName("protocol"),
                    hasParent(namespaceDecl(hasName("blink"),
                                            hasParent(translationUnitDecl()))));

  // Given top-level compilation unit:
  //   namespace WTF {
  //     void foo() {}
  //   }
  // matches |foo|.
  auto decl_under_blink_namespace =
      decl(hasAncestor(blink_namespace_decl),
           unless(hasAncestor(protocol_namespace_decl)));

  // Given top-level compilation unit:
  //   void WTF::function() {}
  //   void WTF::Class::method() {}
  // matches |WTF::function| and |WTF::Class::method| decls.
  auto decl_has_qualifier_to_blink_namespace =
      declaratorDecl(has(nestedNameSpecifier(
          hasTopLevelPrefix(specifiesNamespace(blink_namespace_decl)))));

  auto in_blink_namespace = decl(
      anyOf(decl_under_blink_namespace, decl_has_qualifier_to_blink_namespace,
            hasAncestor(decl_has_qualifier_to_blink_namespace)),
      unless(hasCanonicalDecl(isDeclInGeneratedFile())));

  // Field, variable, and enum declarations ========
  // Given
  //   int x;
  //   struct S {
  //     int y;
  //     enum { VALUE };
  //   };
  // matches |x|, |y|, and |VALUE|.
  auto field_decl_matcher = id("decl", fieldDecl(in_blink_namespace));
  auto is_type_trait_value =
      varDecl(hasName("value"), hasStaticStorageDuration(), isPublic(),
              hasType(isConstQualified()),
              hasType(type(anyOf(builtinType(), enumType()))),
              unless(hasAncestor(recordDecl(
                  has(cxxMethodDecl(isUserProvided(), isInstanceMethod()))))));
  auto var_decl_matcher =
      id("decl", varDecl(in_blink_namespace, unless(is_type_trait_value)));
  // For known trait names, rename every instance anywhere in the codebase.
  auto type_trait_decl_matcher = id("decl", varDecl(isKnownTraitName()));
  auto enum_member_decl_matcher =
      id("decl", enumConstantDecl(in_blink_namespace));

  FieldDeclRewriter field_decl_rewriter(&replacements);
  match_finder.addMatcher(field_decl_matcher, &field_decl_rewriter);

  VarDeclRewriter var_decl_rewriter(&replacements);
  match_finder.addMatcher(var_decl_matcher, &var_decl_rewriter);
  match_finder.addMatcher(type_trait_decl_matcher, &var_decl_rewriter);

  EnumConstantDeclRewriter enum_member_decl_rewriter(&replacements);
  match_finder.addMatcher(enum_member_decl_matcher, &enum_member_decl_rewriter);

  // Field, variable, and enum references ========
  // Given
  //   bool x = true;
  //   if (x) {
  //     ...
  //   }
  // matches |x| in if (x).
  auto member_matcher = id(
      "expr",
      memberExpr(
          member(field_decl_matcher),
          // Needed to avoid matching member references in functions (which will
          // be an ancestor of the member reference) synthesized by the
          // compiler, such as a synthesized copy constructor.
          // This skips explicitly defaulted functions as well, but that's OK:
          // there's nothing interesting to rewrite in those either.
          unless(hasAncestor(functionDecl(isDefaulted())))));
  auto decl_ref_matcher = id("expr", declRefExpr(to(var_decl_matcher)));
  auto type_trait_ref_matcher =
      id("expr", declRefExpr(to(type_trait_decl_matcher)));
  auto enum_member_ref_matcher =
      id("expr", declRefExpr(to(enum_member_decl_matcher)));

  MemberRewriter member_rewriter(&replacements);
  match_finder.addMatcher(member_matcher, &member_rewriter);

  DeclRefRewriter decl_ref_rewriter(&replacements);
  match_finder.addMatcher(decl_ref_matcher, &decl_ref_rewriter);
  match_finder.addMatcher(type_trait_ref_matcher, &decl_ref_rewriter);

  EnumConstantDeclRefRewriter enum_member_ref_rewriter(&replacements);
  match_finder.addMatcher(enum_member_ref_matcher, &enum_member_ref_rewriter);

  // Member references in a non-member context ========
  // Given
  //   struct S {
  //     typedef int U::*UnspecifiedBoolType;
  //     operator UnspecifiedBoolType() { return s_ ? &U::s_ : 0; }
  //     int s_;
  //   };
  // matches |&U::s_| but not |s_|.
  auto member_ref_matcher = id("expr", declRefExpr(to(field_decl_matcher)));

  FieldDeclRefRewriter member_ref_rewriter(&replacements);
  match_finder.addMatcher(member_ref_matcher, &member_ref_rewriter);

  // Non-method function declarations ========
  // Given
  //   void f();
  //   struct S {
  //     void g();
  //   };
  // matches |f| but not |g|.
  auto function_decl_matcher = id(
      "decl",
      functionDecl(
          unless(anyOf(
              // Methods are covered by the method matchers.
              cxxMethodDecl(),
              // Out-of-line overloaded operators have special names and should
              // never be renamed.
              isOverloadedOperator(),
              // Must be checked after filtering out overloaded operators to
              // prevent asserts about the identifier not being a simple name.
              isBlacklistedFunction(),
              // Functions that look like blocked static methods.
              isBlocklistedMethod(method_blocklist))),
          in_blink_namespace));
  FunctionDeclRewriter function_decl_rewriter(&replacements);
  match_finder.addMatcher(function_decl_matcher, &function_decl_rewriter);

  // Non-method function references ========
  // Given
  //   f();
  //   void (*p)() = &f;
  // matches |f()| and |&f|.
  auto function_ref_matcher = id(
      "expr", declRefExpr(to(function_decl_matcher),
                          // Ignore template substitutions.
                          unless(hasAncestor(substNonTypeTemplateParmExpr()))));
  FunctionRefRewriter function_ref_rewriter(&replacements);
  match_finder.addMatcher(function_ref_matcher, &function_ref_rewriter);

  // Method declarations ========
  // Given
  //   struct S {
  //     void g();
  //   };
  // matches |g|.
  // For a method to be considered for rewrite, it must not override something
  // that we're not rewriting. Any methods that we would not normally consider
  // but that override something we are rewriting should also be rewritten. So
  // we use includeAllOverriddenMethods() to check these rules not just for the
  // method being matched but for the methods it overrides also.
  auto is_blink_method = includeAllOverriddenMethods(
      allOf(in_blink_namespace,
            unless(anyOf(isBlacklistedMethod(),
                         isBlocklistedMethod(method_blocklist)))));
  auto method_decl_matcher = id(
      "decl",
      cxxMethodDecl(
          unless(anyOf(
              // Overloaded operators have special names and should never be
              // renamed.
              isOverloadedOperator(),
              // Similarly, constructors, destructors, and conversion
              // functions should not be considered for renaming.
              cxxConstructorDecl(), cxxDestructorDecl(), cxxConversionDecl())),
          // Check this last after excluding things, to avoid
          // asserts about overriding non-blink and blink for the
          // same method.
          is_blink_method));
  MethodDeclRewriter method_decl_rewriter(&replacements);
  match_finder.addMatcher(method_decl_matcher, &method_decl_rewriter);

  // Method references in a non-member context ========
  // Given
  //   S s;
  //   s.g();
  //   void (S::*p)() = &S::g;
  // matches |&S::g| but not |s.g|.
  auto method_ref_matcher = id(
      "expr", declRefExpr(to(method_decl_matcher),
                          // Ignore template substitutions.
                          unless(hasAncestor(substNonTypeTemplateParmExpr()))));

  MethodRefRewriter method_ref_rewriter(&replacements);
  match_finder.addMatcher(method_ref_matcher, &method_ref_rewriter);

  // Method references in a member context ========
  // Given
  //   S s;
  //   s.g();
  //   void (S::*p)() = &S::g;
  // matches |s.g| but not |&S::g|.
  auto method_member_matcher =
      id("expr", memberExpr(member(method_decl_matcher)));

  MethodMemberRewriter method_member_rewriter(&replacements);
  match_finder.addMatcher(method_member_matcher, &method_member_rewriter);

  // Initializers ========
  // Given
  //   struct S {
  //     int x;
  //     S() : x(2) {}
  //   };
  // matches each initializer in the constructor for S.
  auto constructor_initializer_matcher =
      cxxConstructorDecl(forEachConstructorInitializer(id(
          "initializer",
          cxxCtorInitializer(forAnyField(field_decl_matcher), isWritten()))));

  ConstructorInitializerRewriter constructor_initializer_rewriter(
      &replacements);
  match_finder.addMatcher(constructor_initializer_matcher,
                          &constructor_initializer_rewriter);

  // Unresolved lookup expressions ========
  // Given
  //   template<typename T> void F(T) { }
  //   template<void G(T)> H(T) { }
  //   H<F<int>>(...);
  // matches |F| in |H<F<int>>|.
  //
  // UnresolvedLookupExprs are similar to DeclRefExprs that reference a
  // FunctionDecl, but are used when a candidate FunctionDecl can't be selected.
  // This commonly happens inside uninstantiated template definitions for one of
  // two reasons:
  //
  // 1. If the candidate declaration is a dependent FunctionTemplateDecl, the
  //    actual overload can't be selected until template instantiation time.
  // 2. Alternatively, there might be multiple declarations in the candidate set
  //    if the candidate function has overloads. If any of the function
  //    arguments has a dependent type, then the actual overload can't be
  //    selected until instantiation time either.
  //
  // Another instance where UnresolvedLookupExprs can appear is in a template
  // argument list, like the provided example.
  auto function_template_decl_matcher =
      id("decl", functionTemplateDecl(templatedDecl(function_decl_matcher)));
  auto method_template_decl_matcher =
      id("decl", functionTemplateDecl(templatedDecl(method_decl_matcher)));
  auto unresolved_lookup_matcher = expr(id(
      "expr",
      unresolvedLookupExpr(
          // In order to automatically rename an unresolved lookup, the lookup
          // candidates must either all be Blink functions/function templates or
          // all be Blink methods/method templates. Otherwise, we might end up
          // in a situation where the naming could change depending on the
          // selected candidate.
          anyOf(allOverloadsMatch(anyOf(function_decl_matcher,
                                        function_template_decl_matcher)),
                // Note: this matches references to methods in a non-member
                // context, e.g. Template<&Class::Method>. This and the
                // UnresolvedMemberExpr matcher below are analogous to how the
                // rewriter has both a MemberRefRewriter matcher to rewrite
                // &T::method and a MethodMemberRewriter matcher to rewriter
                // t.method().
                allOverloadsMatch(anyOf(method_decl_matcher,
                                        method_template_decl_matcher))))));
  UnresolvedLookupRewriter unresolved_lookup_rewriter(&replacements);
  match_finder.addMatcher(unresolved_lookup_matcher,
                          &unresolved_lookup_rewriter);

  // Unresolved member expressions (for non-dependent fields / methods) ========
  // Similar to unresolved lookup expressions, but for methods in a member
  // context, e.g. var_with_templated_type.Method().
  auto unresolved_member_matcher = expr(id(
      "expr",
      unresolvedMemberExpr(
          // Similar to UnresolvedLookupExprs, all the candidate methods must be
          // Blink methods/method templates.
          allOverloadsMatch(
              anyOf(method_decl_matcher, method_template_decl_matcher)))));
  UnresolvedMemberRewriter unresolved_member_rewriter(&replacements);
  match_finder.addMatcher(unresolved_member_matcher,
                          &unresolved_member_rewriter);

  // Unresolved using value decls ========
  // Example:
  //  template <typename T>
  //  class BaseClass {
  //   public:
  //    unsigned long m_size;
  //  };
  //  template <typename T>
  //  class DerivedClass : protected BaseClass<T> {
  //   private:
  //    using Base = BaseClass<T>;
  //    using Base::m_size;  // <- |m_size| here is matched by
  //    void method() {      //    |unresolved_using_value_decl_matcher|.
  //      m_size = 123;  // <- |m_size| here is matched by
  //    }                //    |unresolved_dependent_using_matcher|.
  //  };
  auto unresolved_dependent_using_matcher =
      expr(id("expr", unresolvedMemberExpr(allOverloadsMatch(allOf(
                          in_blink_namespace, unresolvedUsingValueDecl())))));
  UnresolvedDependentMemberRewriter unresolved_dependent_member_rewriter(
      &replacements);
  match_finder.addMatcher(unresolved_dependent_using_matcher,
                          &unresolved_dependent_member_rewriter);
  auto unresolved_using_value_decl_matcher =
      decl(id("decl", unresolvedUsingValueDecl(in_blink_namespace)));
  UnresolvedUsingValueDeclRewriter unresolved_using_value_decl_rewriter(
      &replacements);
  match_finder.addMatcher(unresolved_using_value_decl_matcher,
                          &unresolved_using_value_decl_rewriter);

  // Using declarations ========
  // Given
  //   using blink::X;
  // matches |using blink::X|.
  auto using_decl_matcher = id(
      "decl", usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(anyOf(
                  var_decl_matcher, field_decl_matcher, function_decl_matcher,
                  method_decl_matcher, function_template_decl_matcher,
                  method_template_decl_matcher, enum_member_decl_matcher)))));
  UsingDeclRewriter using_decl_rewriter(&replacements);
  match_finder.addMatcher(using_decl_matcher, &using_decl_rewriter);

  // Matches any QualType that refers to a blink type:
  // - const blink::Foo&
  // - blink::Foo*
  // - blink::Foo<T>
  auto blink_qual_type_base_matcher = hasBaseType(hasUnqualifiedDesugaredType(
      anyOf(enumType(hasDeclaration(in_blink_namespace)),
            injectedClassNameType(hasDeclaration(in_blink_namespace)),
            recordType(hasDeclaration(in_blink_namespace)),
            templateSpecializationType(hasDeclaration(in_blink_namespace)),
            templateTypeParmType(hasDeclaration(in_blink_namespace)))));
  auto blink_qual_type_matcher = qualType(anyOf(
      blink_qual_type_base_matcher, pointsTo(blink_qual_type_base_matcher),
      references(blink_qual_type_base_matcher)));

  // Template-dependent decl lookup ========
  // Given
  //   template <typename T> void f() { T::foo(); }
  // matches |T::foo|.
  auto dependent_scope_decl_ref_expr_matcher =
      expr(id("expr", dependentScopeDeclRefExpr(has(nestedNameSpecifier(
                          specifiesType(blink_qual_type_matcher))))));
  DependentScopeDeclRefExprRewriter dependent_scope_decl_ref_expr_rewriter(
      &replacements);
  match_finder.addMatcher(dependent_scope_decl_ref_expr_matcher,
                          &dependent_scope_decl_ref_expr_rewriter);

  // Template-dependent member lookup ========
  // Given
  //   template <typename T>
  //   class Foo {
  //     void f() { T::foo(); }
  //     void g(T x) { x.bar(); }
  //   };
  // matches |T::foo| and |x.bar|.
  auto cxx_dependent_scope_member_expr_matcher =
      expr(id("expr", cxxDependentScopeMemberExpr(
                          hasMemberFromType(blink_qual_type_matcher))));
  CXXDependentScopeMemberExprRewriter cxx_dependent_scope_member_expr_rewriter(
      &replacements);
  match_finder.addMatcher(cxx_dependent_scope_member_expr_matcher,
                          &cxx_dependent_scope_member_expr_rewriter);

  // GMock calls lookup ========
  // Given
  //   EXPECT_CALL(obj, myMethod(...))
  // or
  //   ON_CALL(obj, myMethod(...))
  // will match obj.gmock_myMethod(...) call generated by the macros
  // (but only if it mocks a Blink method).
  auto gmock_member_matcher =
      id("expr", memberExpr(hasDeclaration(
                     decl(cxxMethodDecl(mocksMethod(method_decl_matcher))))));
  GMockMemberRewriter gmock_member_rewriter(&replacements);
  match_finder.addMatcher(gmock_member_matcher, &gmock_member_rewriter);

  // Prepare and run the tool.
  SourceFileCallbacks source_file_callbacks(&gmock_member_rewriter);
  std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
      clang::tooling::newFrontendActionFactory(&match_finder,
                                               &source_file_callbacks);
  int result = tool.run(factory.get());
  if (result != 0)
    return result;

  // Supplemental data for the Blink rename rebase helper.
  std::vector<const EditTracker*> all_edit_trackers{
      field_decl_rewriter.edit_tracker(),
      var_decl_rewriter.edit_tracker(),
      enum_member_decl_rewriter.edit_tracker(),
      member_rewriter.edit_tracker(),
      decl_ref_rewriter.edit_tracker(),
      enum_member_ref_rewriter.edit_tracker(),
      member_ref_rewriter.edit_tracker(),
      function_decl_rewriter.edit_tracker(),
      function_ref_rewriter.edit_tracker(),
      method_decl_rewriter.edit_tracker(),
      method_ref_rewriter.edit_tracker(),
      method_member_rewriter.edit_tracker(),
      constructor_initializer_rewriter.edit_tracker(),
      unresolved_lookup_rewriter.edit_tracker(),
      unresolved_member_rewriter.edit_tracker(),
      unresolved_dependent_member_rewriter.edit_tracker(),
      unresolved_using_value_decl_rewriter.edit_tracker(),
      using_decl_rewriter.edit_tracker(),
      dependent_scope_decl_ref_expr_rewriter.edit_tracker(),
      cxx_dependent_scope_member_expr_rewriter.edit_tracker(),
      gmock_member_rewriter.edit_tracker(),
  };
  llvm::outs() << "==== BEGIN TRACKED EDITS ====\n";
  for (const EditTracker* edit_tracker : all_edit_trackers)
    edit_tracker->SerializeTo(llvm::outs());
  llvm::outs() << "==== END TRACKED EDITS ====\n";

  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;
}