aboutsummaryrefslogtreecommitdiff
path: root/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp')
-rw-r--r--tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp998
1 files changed, 858 insertions, 140 deletions
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
index 3b42fc28..bc98ce47 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -14,10 +14,9 @@
#include <assert.h>
#include <algorithm>
-#include <fstream>
#include <memory>
+#include <set>
#include <string>
-#include <unordered_map>
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -25,20 +24,19 @@
#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/MacroArgs.h"
#include "clang/Lex/Lexer.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/TargetSelect.h"
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <sys/file.h>
-#include <unistd.h>
-#endif
+#include "EditTracker.h"
using namespace clang::ast_matchers;
using clang::tooling::CommonOptionsParser;
@@ -50,11 +48,27 @@ namespace {
const char kBlinkFieldPrefix[] = "m_";
const char kBlinkStaticMemberPrefix[] = "s_";
const char kGeneratedFileRegex[] = "^gen/|/gen/";
+const char kGMockMethodNamePrefix[] = "gmock_";
+
+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();
}
@@ -70,6 +84,41 @@ AST_MATCHER_P(clang::FunctionTemplateDecl,
return InnerMatcher.matches(*Node.getTemplatedDecl(), 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()) {
+ if (!potentially_mocked_method->isVirtual())
+ continue;
+
+ 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;
+}
+
// If |InnerMatcher| matches |top|, then the returned matcher will match:
// - |top::function|
// - |top::Class::method|
@@ -112,6 +161,13 @@ AST_MATCHER_P(clang::OverloadExpr,
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,
@@ -136,13 +192,54 @@ bool MatchAllOverriddenMethods(
// 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
// blacklist T_T).
- if (override_matches || override_not_matches)
- assert(override_matches != override_not_matches);
+ 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)
@@ -158,6 +255,34 @@ AST_MATCHER_P(clang::CXXMethodDecl,
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)
@@ -170,37 +295,61 @@ bool IsMethodOverrideOf(const clang::CXXMethodDecl& decl,
return false;
}
-bool IsBlacklistedFunction(const clang::FunctionDecl& decl) {
- // swap() functions should match the signature of std::swap for ADL tricks.
- return decl.getName() == "swap";
-}
+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;
-bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) {
- if (decl.isStatic())
- return false;
+ return false;
+}
- clang::StringRef name = decl.getName();
+bool IsBlacklistedFreeFunctionName(llvm::StringRef name) {
+ // swap() functions should match the signature of std::swap for ADL tricks.
+ return name == "swap";
+}
- // These methods should never be renamed.
- static const char* kBlacklistMethods[] = {"trace", "traceImpl", "lock",
- "unlock", "try_lock"};
- for (const auto& b : kBlacklistMethods) {
+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.
+ "back", "empty", "erase", "front", "insert",
+ };
+ for (const auto& b : kBlacklistedNames) {
if (name == b)
return true;
}
+ return false;
+}
- // Iterator methods shouldn't be renamed to work with stl and range-for
- // loops.
- std::string ret_type = decl.getReturnType().getAsString();
- if (ret_type.find("iterator") != std::string::npos ||
- ret_type.find("Iterator") != std::string::npos) {
- static const char* kIteratorBlacklist[] = {"begin", "end", "rbegin",
- "rend"};
- for (const auto& b : kIteratorBlacklist) {
- if (name == b)
- return true;
- }
- }
+bool IsBlacklistedMethodName(llvm::StringRef name) {
+ return IsBlacklistedFunctionName(name) ||
+ IsBlacklistedInstanceMethodName(name);
+}
+
+bool IsBlacklistedFunction(const clang::FunctionDecl& decl) {
+ clang::StringRef name = decl.getName();
+ return IsBlacklistedFunctionName(name) || IsBlacklistedFreeFunctionName(name);
+}
+
+bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) {
+ 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
@@ -258,6 +407,65 @@ std::string CamelCaseToUnderscoreCase(StringRef input) {
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();
@@ -267,6 +475,14 @@ bool IsProbablyConst(const clang::VarDecl& decl,
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)
@@ -276,24 +492,96 @@ bool IsProbablyConst(const clang::VarDecl& decl,
if (!initializer)
return false;
- // If the expression is dependent on a template input, then we are not
- // sure if it can be compile-time generated as calling isEvaluatable() is
- // not valid on |initializer|.
- // TODO(crbug.com/581218): We could probably look at each compiled
- // instantiation of the template and see if they are all compile-time
- // isEvaluable().
- if (initializer->isInstantiationDependent())
- return false;
-
- // If the expression can be evaluated at compile time, then it should have a
- // kFoo style name. Otherwise, not.
- return initializer->isEvaluatable(context);
+ 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[] = {
+ "animationWorklet",
+ "audioWorklet",
+ "binaryType",
+ "blob",
+ "channelCountMode",
+ "color",
+ "counterDirectives",
+ "document",
+ "emptyChromeClient",
+ "emptyEditorClient",
+ "emptySpellCheckerClient",
+ "entryType",
+ "error",
+ "fileUtilities",
+ "font",
+ "frame",
+ "frameBlameContext",
+ "frontend",
+ "hash",
+ "heapObjectHeader",
+ "iconURL",
+ "inputMethodController",
+ "inputType",
+ "layout",
+ "layoutBlock",
+ "layoutObject",
+ "layoutSize",
+ "length",
+ "lineCap",
+ "lineEndings",
+ "lineJoin",
+ "listItems",
+ "matchedProperties",
+ "midpointState",
+ "mouseEvent",
+ "name",
+ "navigationType",
+ "node",
+ "outcome",
+ "pagePopup",
+ "paintWorklet",
+ "path",
+ "processingInstruction",
+ "readyState",
+ "relList",
+ "resource",
+ "response",
+ "sandboxSupport",
+ "screenInfo",
+ "scrollAnimator",
+ "settings",
+ "signalingState",
+ "state",
+ "string",
+ "styleSheet",
+ "text",
+ "textAlign",
+ "textBaseline",
+ "theme",
+ "thread",
+ "timing",
+ "topLevelBlameContext",
+ "vector",
+ "widget",
+ "wordBoundaries",
+ "wrapperTypeInfo",
+ };
+ for (const auto& conflicting_method : kConflictingMethods) {
+ if (old_method_name == conflicting_method)
+ return true;
+ }
+
+ return false;
+}
+
+AST_MATCHER(clang::FunctionDecl, shouldPrefixFunctionName) {
+ return ShouldPrefixFunctionName(Node.getName().str());
+}
+
bool GetNameForDecl(const clang::FunctionDecl& decl,
clang::ASTContext& context,
std::string& name) {
@@ -302,14 +590,18 @@ bool GetNameForDecl(const clang::FunctionDecl& decl,
// 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(name), // hasString matches the type as spelled (Bar above).
- hasDeclaration(namedDecl(hasName(name))))); // hasDeclaration matches
- // resolved type (Foo above).
+ // 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|.
@@ -320,9 +612,18 @@ bool GetNameForDecl(const clang::FunctionDecl& decl,
hasDescendant(type_with_same_name_as_function)));
// https://crbug.com/582312: Prepend "Get" if method name conflicts with
// return type.
- auto conflict_matcher =
- functionDecl(returns(type_containing_same_name_as_function));
- if (!match(conflict_matcher, decl, context).empty())
+ 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;
@@ -417,6 +718,12 @@ bool GetNameForDecl(const clang::VarDecl& decl,
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());
@@ -509,6 +816,24 @@ struct TargetNodeTraits<clang::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());
@@ -536,84 +861,408 @@ struct TargetNodeTraits<clang::UnresolvedMemberExpr> {
static const char* GetType() { return "UnresolvedMemberExpr"; }
};
-template <typename DeclNode, typename TargetNode>
+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)
: replacements_(replacements) {}
+ 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)
+ *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, typename TargetNode>
+class DeclRewriterBase : public RewriterBase<TargetNode> {
+ public:
+ using Base = RewriterBase<TargetNode>;
+
+ explicit DeclRewriterBase(std::set<Replacement>* replacements)
+ : Base(replacements) {}
+
void run(const MatchFinder::MatchResult& result) override {
const DeclNode* decl = result.Nodes.getNodeAs<DeclNode>("decl");
- // If false, there's no name to be renamed.
+ assert(decl);
+ llvm::StringRef old_name = decl->getName();
+
+ // Return early if there's no name to be renamed.
if (!decl->getIdentifier())
return;
- clang::SourceLocation decl_loc =
- TargetNodeTraits<clang::NamedDecl>::GetLoc(*decl);
- if (decl_loc.isMacroID()) {
- // Get the location of the spelling of the declaration. If token pasting
- // was used this will be in "scratch space" and we don't know how to get
- // from there back to/ the actual macro with the foo##bar text. So just
- // don't replace in that case.
- clang::SourceLocation spell =
- result.SourceManager->getSpellingLoc(decl_loc);
- if (strcmp(result.SourceManager->getBufferName(spell),
- "<scratch space>") == 0)
- return;
- }
- clang::ASTContext* context = result.Context;
+
+ // Get the new name.
std::string new_name;
- if (!GetNameForDecl(*decl, *context, new_name))
+ if (!GetNameForDecl(*decl, *result.Context, new_name))
return; // If false, the name was not suitable for renaming.
- llvm::StringRef old_name = decl->getName();
- if (old_name == new_name)
+
+ // 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;
- clang::SourceLocation loc = TargetNodeTraits<TargetNode>::GetLoc(
- *result.Nodes.getNodeAs<TargetNode>(
- TargetNodeTraits<TargetNode>::GetName()));
- clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(loc);
- replacements_->emplace(*result.SourceManager, range, new_name);
- replacement_names_.emplace(old_name.str(), std::move(new_name));
- }
- const std::unordered_map<std::string, std::string>& replacement_names()
- const {
- return replacement_names_;
+ Base::AddReplacement(result, old_name, std::move(new_name));
}
-
- private:
- std::set<Replacement>* const replacements_;
- std::unordered_map<std::string, std::string> replacement_names_;
};
-using FieldDeclRewriter = RewriterBase<clang::FieldDecl, clang::NamedDecl>;
-using VarDeclRewriter = RewriterBase<clang::VarDecl, clang::NamedDecl>;
-using MemberRewriter = RewriterBase<clang::FieldDecl, clang::MemberExpr>;
-using DeclRefRewriter = RewriterBase<clang::VarDecl, clang::DeclRefExpr>;
-using FieldDeclRefRewriter = RewriterBase<clang::FieldDecl, clang::DeclRefExpr>;
+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 =
- RewriterBase<clang::FunctionDecl, clang::NamedDecl>;
+ DeclRewriterBase<clang::FunctionDecl, clang::NamedDecl>;
using FunctionRefRewriter =
- RewriterBase<clang::FunctionDecl, clang::DeclRefExpr>;
+ DeclRewriterBase<clang::FunctionDecl, clang::DeclRefExpr>;
using ConstructorInitializerRewriter =
- RewriterBase<clang::FieldDecl, clang::CXXCtorInitializer>;
+ DeclRewriterBase<clang::FieldDecl, clang::CXXCtorInitializer>;
-using MethodDeclRewriter = RewriterBase<clang::CXXMethodDecl, clang::NamedDecl>;
+using MethodDeclRewriter =
+ DeclRewriterBase<clang::CXXMethodDecl, clang::NamedDecl>;
using MethodRefRewriter =
- RewriterBase<clang::CXXMethodDecl, clang::DeclRefExpr>;
+ DeclRewriterBase<clang::CXXMethodDecl, clang::DeclRefExpr>;
using MethodMemberRewriter =
- RewriterBase<clang::CXXMethodDecl, clang::MemberExpr>;
+ DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr>;
using EnumConstantDeclRewriter =
- RewriterBase<clang::EnumConstantDecl, clang::NamedDecl>;
+ DeclRewriterBase<clang::EnumConstantDecl, clang::NamedDecl>;
using EnumConstantDeclRefRewriter =
- RewriterBase<clang::EnumConstantDecl, clang::DeclRefExpr>;
+ DeclRewriterBase<clang::EnumConstantDecl, clang::DeclRefExpr>;
using UnresolvedLookupRewriter =
- RewriterBase<clang::NamedDecl, clang::UnresolvedLookupExpr>;
+ DeclRewriterBase<clang::NamedDecl, clang::UnresolvedLookupExpr>;
using UnresolvedMemberRewriter =
- RewriterBase<clang::NamedDecl, clang::UnresolvedMemberExpr>;
+ 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 llvm::make_unique<GMockMemberRewriter::PPCallbacks>(this);
+ }
-using UsingDeclRewriter = RewriterBase<clang::UsingDecl, clang::NamedDecl>;
+ 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 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 = expect_call_to_2nd_arg.find(macro_call_loc);
+ if (it == expect_call_to_2nd_arg.end())
+ return clang::SourceLocation();
+ return it->second;
+ }
+
+ private:
+ std::map<clang::SourceLocation, clang::SourceLocation> expect_call_to_2nd_arg;
+
+ // Called from PPCallbacks with the locations of EXPECT_CALL macro invocation:
+ // Example:
+ // EXPECT_CALL(my_mock, myMethod(123, 456));
+ // ^- expansion_loc ^- actual_arg_loc
+ void RecordExpectCallMacroInvocation(clang::SourceLocation expansion_loc,
+ clang::SourceLocation second_arg_loc) {
+ expect_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")
+ return;
+
+ if (def.getMacroInfo()->getNumArgs() != 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_->RecordExpectCallMacroInvocation(
+ 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) {}
+
+ 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,
+ llvm::StringRef Filename) override {
+ compiler.getPreprocessor().addPPCallbacks(
+ gmock_member_rewriter_->CreatePreprocessorCallbacks());
+ return true;
+ }
+
+ private:
+ GMockMemberRewriter* gmock_member_rewriter_;
+};
} // namespace
@@ -637,13 +1286,19 @@ int main(int argc, const char* argv[]) {
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));
+ auto decl_under_blink_namespace =
+ decl(hasAncestor(blink_namespace_decl),
+ unless(hasAncestor(protocol_namespace_decl)));
// Given top-level compilation unit:
// void WTF::function() {}
@@ -669,8 +1324,8 @@ int main(int argc, const char* argv[]) {
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(
- booleanType(), enumType()))),
+ hasType(isConstQualified()),
+ hasType(type(anyOf(builtinType(), enumType()))),
unless(hasAncestor(recordDecl(
has(cxxMethodDecl(isUserProvided(), isInstanceMethod()))))));
auto var_decl_matcher =
@@ -800,7 +1455,7 @@ int main(int argc, const char* argv[]) {
// S s;
// s.g();
// void (S::*p)() = &S::g;
- // matches |&S::g| but not |s.g()|.
+ // matches |&S::g| but not |s.g|.
auto method_ref_matcher = id(
"expr", declRefExpr(to(method_decl_matcher),
// Ignore template substitutions.
@@ -814,7 +1469,7 @@ int main(int argc, const char* argv[]) {
// S s;
// s.g();
// void (S::*p)() = &S::g;
- // matches |s.g()| but not |&S::g|.
+ // matches |s.g| but not |&S::g|.
auto method_member_matcher =
id("expr", memberExpr(member(method_decl_matcher)));
@@ -885,7 +1540,7 @@ int main(int argc, const char* argv[]) {
match_finder.addMatcher(unresolved_lookup_matcher,
&unresolved_lookup_rewriter);
- // Unresolved member expressions ========
+ // 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(
@@ -899,6 +1554,36 @@ int main(int argc, const char* argv[]) {
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;
@@ -911,44 +1596,77 @@ int main(int argc, const char* argv[]) {
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(...))
+ // will match obj.gmock_myMethod(...) call generated by the macro
+ // (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);
+ clang::tooling::newFrontendActionFactory(&match_finder,
+ &source_file_callbacks);
int result = tool.run(factory.get());
if (result != 0)
return result;
-#if defined(_WIN32)
- HANDLE lockfd = CreateFile("rewrite-sym.lock", GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- OVERLAPPED overlapped = {};
- LockFileEx(lockfd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &overlapped);
-#else
- int lockfd = open("rewrite-sym.lock", O_RDWR | O_CREAT, 0666);
- while (flock(lockfd, LOCK_EX)) { // :D
- }
-#endif
-
- std::ofstream replacement_db_file("rewrite-sym.txt",
- std::ios_base::out | std::ios_base::app);
- for (const auto& p : field_decl_rewriter.replacement_names())
- replacement_db_file << "var:" << p.first << ":" << p.second << "\n";
- for (const auto& p : var_decl_rewriter.replacement_names())
- replacement_db_file << "var:" << p.first << ":" << p.second << "\n";
- for (const auto& p : enum_member_decl_rewriter.replacement_names())
- replacement_db_file << "enu:" << p.first << ":" << p.second << "\n";
- for (const auto& p : function_decl_rewriter.replacement_names())
- replacement_db_file << "fun:" << p.first << ":" << p.second << "\n";
- for (const auto& p : method_decl_rewriter.replacement_names())
- replacement_db_file << "fun:" << p.first << ":" << p.second << "\n";
- replacement_db_file.close();
-
-#if defined(_WIN32)
- UnlockFileEx(lockfd, 0, 1, 0, &overlapped);
- CloseHandle(lockfd);
-#else
- flock(lockfd, LOCK_UN);
- close(lockfd);
-#endif
+ // Supplemental data for the Blink rename rebase helper.
+ // TODO(dcheng): There's a lot of match rewriters missing from this list.
+ llvm::outs() << "==== BEGIN TRACKED EDITS ====\n";
+ field_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs());
+ var_decl_rewriter.edit_tracker().SerializeTo("var", llvm::outs());
+ enum_member_decl_rewriter.edit_tracker().SerializeTo("enu", llvm::outs());
+ function_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs());
+ method_decl_rewriter.edit_tracker().SerializeTo("fun", llvm::outs());
+ llvm::outs() << "==== END TRACKED EDITS ====\n";
// Serialization format is documented in tools/clang/scripts/run_tool.py
llvm::outs() << "==== BEGIN EDITS ====\n";