Index: tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
index 42792382264884cb3775ab8ae16f3da27f46769b..41e3e5cb143e93df50cb7ff3a7914fadfa342163 100644 |
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
@@ -5,16 +5,18 @@ |
// Changes Blink-style names to Chrome-style names. Currently transforms: |
// fields: |
// int m_operationCount => int operation_count_ |
-// variables: |
+// variables (including parameters): |
// int mySuperVariable => int my_super_variable |
// constants: |
// const int maxThings => const int kMaxThings |
-// free functions: |
+// free functions and methods: |
// void doThisThenThat() => void DoThisAndThat() |
+#include <assert.h> |
#include <algorithm> |
#include <memory> |
#include <string> |
+#include <unordered_set> |
#include "clang/AST/ASTContext.h" |
#include "clang/ASTMatchers/ASTMatchFinder.h" |
@@ -38,12 +40,14 @@ using llvm::StringRef; |
namespace { |
+AST_MATCHER(clang::FunctionDecl, isOverloadedOperator) { |
+ return Node.isOverloadedOperator(); |
+} |
+ |
constexpr char kBlinkFieldPrefix[] = "m_"; |
constexpr char kBlinkStaticMemberPrefix[] = "s_"; |
-bool GetNameForDecl(const clang::FunctionDecl& decl, |
- clang::ASTContext* context, |
- std::string& name) { |
+bool GetNameForDecl(const clang::FunctionDecl& decl, std::string& name) { |
name = decl.getNameAsString(); |
name[0] = clang::toUppercase(name[0]); |
return true; |
@@ -83,9 +87,7 @@ std::string CamelCaseToUnderscoreCase(StringRef input) { |
return output; |
} |
-bool GetNameForDecl(const clang::FieldDecl& decl, |
- clang::ASTContext* context, |
- std::string& name) { |
+bool GetNameForDecl(const clang::FieldDecl& decl, std::string& name) { |
StringRef original_name = decl.getName(); |
// Blink style field names are prefixed with `m_`. If this prefix isn't |
// present, assume it's already been converted to Google style. |
@@ -137,9 +139,7 @@ bool IsProbablyConst(const clang::VarDecl& decl) { |
clang::isa<clang::UserDefinedLiteral>(initializer); |
} |
-bool GetNameForDecl(const clang::VarDecl& decl, |
- clang::ASTContext* context, |
- std::string& name) { |
+bool GetNameForDecl(const clang::VarDecl& decl, std::string& name) { |
StringRef original_name = decl.getName(); |
// Nothing to do for unnamed parameters. |
@@ -173,71 +173,158 @@ bool GetNameForDecl(const clang::VarDecl& decl, |
return true; |
} |
-template <typename DeclNodeType, typename TargetTraits> |
-class RewriterCallbackBase : public MatchFinder::MatchCallback { |
+template <typename Type> |
+struct TargetNodeTraits; |
+ |
+template <> |
+struct TargetNodeTraits<clang::NamedDecl> { |
+ static constexpr char kName[] = "decl"; |
+ static clang::CharSourceRange GetRange(const clang::NamedDecl& decl) { |
+ return clang::CharSourceRange::getTokenRange(decl.getLocation()); |
+ } |
+}; |
+constexpr char TargetNodeTraits<clang::NamedDecl>::kName[]; |
+ |
+template <> |
+struct TargetNodeTraits<clang::MemberExpr> { |
+ static constexpr char kName[] = "expr"; |
+ static clang::CharSourceRange GetRange(const clang::MemberExpr& expr) { |
+ return clang::CharSourceRange::getTokenRange(expr.getMemberLoc()); |
+ } |
+}; |
+constexpr char TargetNodeTraits<clang::MemberExpr>::kName[]; |
+ |
+template <> |
+struct TargetNodeTraits<clang::DeclRefExpr> { |
+ static constexpr char kName[] = "expr"; |
+ static clang::CharSourceRange GetRange(const clang::DeclRefExpr& expr) { |
+ return clang::CharSourceRange::getTokenRange(expr.getLocation()); |
+ } |
+}; |
+constexpr char TargetNodeTraits<clang::DeclRefExpr>::kName[]; |
+ |
+template <> |
+struct TargetNodeTraits<clang::CXXCtorInitializer> { |
+ static constexpr char kName[] = "initializer"; |
+ static clang::CharSourceRange GetRange( |
+ const clang::CXXCtorInitializer& init) { |
+ return clang::CharSourceRange::getTokenRange(init.getSourceLocation()); |
+ } |
+}; |
+constexpr char TargetNodeTraits<clang::CXXCtorInitializer>::kName[]; |
+ |
+template <typename DeclNode, typename TargetNode> |
+class RewriterBase : public MatchFinder::MatchCallback { |
public: |
- explicit RewriterCallbackBase(Replacements* replacements) |
+ explicit RewriterBase(Replacements* replacements) |
: replacements_(replacements) {} |
- virtual void run(const MatchFinder::MatchResult& result) override { |
+ |
+ void run(const MatchFinder::MatchResult& result) override { |
std::string name; |
- if (!GetNameForDecl(*result.Nodes.getNodeAs<DeclNodeType>("decl"), |
- result.Context, name)) |
+ if (!GetNameForDecl(*result.Nodes.getNodeAs<DeclNode>("decl"), name)) |
return; |
- replacements_->emplace( |
- *result.SourceManager, |
- TargetTraits::GetRange( |
- *result.Nodes.getNodeAs<typename TargetTraits::NodeType>( |
- TargetTraits::kName)), |
- name); |
+ replacements_->emplace(*result.SourceManager, |
+ TargetNodeTraits<TargetNode>::GetRange( |
+ *result.Nodes.getNodeAs<TargetNode>( |
+ TargetNodeTraits<TargetNode>::kName)), |
+ name); |
} |
private: |
Replacements* const replacements_; |
}; |
-struct DeclTargetTraits { |
- using NodeType = clang::NamedDecl; |
- static constexpr char kName[] = "decl"; |
- static clang::CharSourceRange GetRange(const NodeType& decl) { |
- return clang::CharSourceRange::getTokenRange(decl.getLocation()); |
- } |
-}; |
-constexpr char DeclTargetTraits::kName[]; |
+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 FunctionDeclRewriter = |
+ RewriterBase<clang::FunctionDecl, clang::NamedDecl>; |
+using FunctionRefRewriter = |
+ RewriterBase<clang::FunctionDecl, clang::DeclRefExpr>; |
+using ConstructorInitializerRewriter = |
+ RewriterBase<clang::FieldDecl, clang::CXXCtorInitializer>; |
+ |
+// Helpers for rewriting methods. The tool needs to detect overrides of Blink |
+// methods, and uses two matchers to help accomplish this goal: |
+// - The first matcher matches all method declarations in Blink. When the |
+// callback rewrites the declaration, it also stores a pointer to the |
+// canonical declaration, to record it as a Blink method. |
+// - The second matcher matches all method declarations that are overrides. When |
+// the callback processes the match, it checks if its overriding a method that |
+// was marked as a Blink method. If so, it rewrites the declaration. |
+// - Because an override is determined based on inclusion in the set of Blink |
+// methods, the overridden methods matcher does not need to filter out special |
+// member functions: they get filtered out by virtue of the first matcher. |
+// |
+// This works because per the documentation on MatchFinder: |
+// The order of matches is guaranteed to be equivalent to doing a pre-order |
+// traversal on the AST, and applying the matchers in the order in which they |
+// were added to the MatchFinder. |
+// |
+// Since classes cannot forward declare their base classes, it is guaranteed |
+// that the base class methods will be seen before processing the overridden |
+// methods. |
+class MethodDeclRewriter |
+ : public RewriterBase<clang::CXXMethodDecl, clang::NamedDecl> { |
+ public: |
+ explicit MethodDeclRewriter(Replacements* replacements) |
+ : RewriterBase(replacements) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override { |
+ const clang::CXXMethodDecl* method_decl = |
+ result.Nodes.getNodeAs<clang::CXXMethodDecl>("decl"); |
+ // TODO(dcheng): Does this need to check for the override attribute, or is |
+ // this good enough? |
+ if (method_decl->size_overridden_methods() > 0) { |
+ if (!IsBlinkOverride(method_decl)) |
+ return; |
+ } else { |
+ blink_methods_.emplace(method_decl->getCanonicalDecl()); |
+ } |
-struct ExprTargetTraits { |
- using NodeType = clang::Expr; |
- static constexpr char kName[] = "expr"; |
- static clang::CharSourceRange GetRange(const NodeType& expr) { |
- return clang::CharSourceRange::getTokenRange(expr.getExprLoc()); |
+ RewriterBase::run(result); |
} |
-}; |
-constexpr char ExprTargetTraits::kName[]; |
-struct InitializerTargetTraits { |
- using NodeType = clang::CXXCtorInitializer; |
- static constexpr char kName[] = "initializer"; |
- static clang::CharSourceRange GetRange(const NodeType& init) { |
- return clang::CharSourceRange::getTokenRange(init.getSourceLocation()); |
+ bool IsBlinkOverride(const clang::CXXMethodDecl* decl) const { |
+ assert(decl->size_overridden_methods() > 0); |
+ for (auto it = decl->begin_overridden_methods(); |
+ it != decl->end_overridden_methods(); ++it) { |
+ if (blink_methods_.find((*it)->getCanonicalDecl()) != |
+ blink_methods_.end()) |
+ return true; |
+ } |
+ return false; |
} |
+ |
+ private: |
+ std::unordered_set<const clang::CXXMethodDecl*> blink_methods_; |
}; |
-constexpr char InitializerTargetTraits::kName[]; |
-using FunctionDeclRewriterCallback = |
- RewriterCallbackBase<clang::FunctionDecl, DeclTargetTraits>; |
-using FieldDeclRewriterCallback = |
- RewriterCallbackBase<clang::FieldDecl, DeclTargetTraits>; |
-using VarDeclRewriterCallback = |
- RewriterCallbackBase<clang::VarDecl, DeclTargetTraits>; |
+template <typename Base> |
+class FilteringMethodRewriter : public Base { |
+ public: |
+ FilteringMethodRewriter(const MethodDeclRewriter& decl_rewriter, |
+ Replacements* replacements) |
+ : Base(replacements), decl_rewriter_(decl_rewriter) {} |
+ |
+ void run(const MatchFinder::MatchResult& result) override { |
+ const clang::CXXMethodDecl* method_decl = |
+ result.Nodes.getNodeAs<clang::CXXMethodDecl>("decl"); |
+ if (method_decl->size_overridden_methods() > 0 && |
+ !decl_rewriter_.IsBlinkOverride(method_decl)) |
+ return; |
+ Base::run(result); |
+ } |
-using CallRewriterCallback = |
- RewriterCallbackBase<clang::FunctionDecl, ExprTargetTraits>; |
-using MemberRewriterCallback = |
- RewriterCallbackBase<clang::FieldDecl, ExprTargetTraits>; |
-using DeclRefRewriterCallback = |
- RewriterCallbackBase<clang::VarDecl, ExprTargetTraits>; |
+ private: |
+ const MethodDeclRewriter& decl_rewriter_; |
+}; |
-using ConstructorInitializerRewriterCallback = |
- RewriterCallbackBase<clang::FieldDecl, InitializerTargetTraits>; |
+using MethodRefRewriter = FilteringMethodRewriter< |
+ RewriterBase<clang::CXXMethodDecl, clang::DeclRefExpr>>; |
+using MethodMemberRewriter = FilteringMethodRewriter< |
+ RewriterBase<clang::CXXMethodDecl, clang::MemberExpr>>; |
} // namespace |
@@ -260,50 +347,137 @@ int main(int argc, const char* argv[]) { |
auto in_blink_namespace = |
decl(hasAncestor(namespaceDecl(anyOf(hasName("blink"), hasName("WTF"))))); |
- // Declaration handling (e.g. function definitions and variable definitions): |
- |
- // Note: for now, only rewrite standalone functions until the question of JS |
- // binding integration for class methods is resolved. |
- // TODO(dcheng): Since classes in public/ aren't directly web-exposed, just go |
- // ahead and rewrite those. |
- auto function_decl_matcher = |
- id("decl", functionDecl(unless(cxxMethodDecl()), in_blink_namespace)); |
- |
+ // Field and variable declarations ======== |
+ // Given |
+ // int x; |
+ // struct S { |
+ // int y; |
+ // }; |
+ // matches |x| and |y|. |
auto field_decl_matcher = id("decl", fieldDecl(in_blink_namespace)); |
auto var_decl_matcher = id("decl", varDecl(in_blink_namespace)); |
- FunctionDeclRewriterCallback function_decl_rewriter(&replacements); |
- match_finder.addMatcher(function_decl_matcher, &function_decl_rewriter); |
- FieldDeclRewriterCallback field_decl_rewriter(&replacements); |
+ FieldDeclRewriter field_decl_rewriter(&replacements); |
match_finder.addMatcher(field_decl_matcher, &field_decl_rewriter); |
- VarDeclRewriterCallback var_decl_rewriter(&replacements); |
+ |
+ VarDeclRewriter var_decl_rewriter(&replacements); |
match_finder.addMatcher(var_decl_matcher, &var_decl_rewriter); |
- // Expression handling (e.g. calling a Blink function or referencing a |
- // variable defined in Blink): |
- auto call_matcher = id("expr", callExpr(callee(function_decl_matcher))); |
+ // Field and variable references ======== |
+ // Given |
+ // bool x = true; |
+ // if (x) { |
+ // ... |
+ // } |
+ // matches |x| in if (x). |
auto member_matcher = id("expr", memberExpr(member(field_decl_matcher))); |
auto decl_ref_matcher = id("expr", declRefExpr(to(var_decl_matcher))); |
- CallRewriterCallback call_rewriter(&replacements); |
- match_finder.addMatcher(call_matcher, &call_rewriter); |
- MemberRewriterCallback member_rewriter(&replacements); |
+ MemberRewriter member_rewriter(&replacements); |
match_finder.addMatcher(member_matcher, &member_rewriter); |
- DeclRefRewriterCallback decl_ref_rewriter(&replacements); |
+ |
+ DeclRefRewriter decl_ref_rewriter(&replacements); |
match_finder.addMatcher(decl_ref_matcher, &decl_ref_rewriter); |
- // Function reference handling (e.g. getting a pointer to a function without |
- // calling it): |
+ // Non-method function declarations ======== |
+ // Given |
+ // void f(); |
+ // struct S { |
+ // void g(); |
+ // }; |
+ // matches |f| but not |g|. |
+ auto function_decl_matcher = |
+ id("decl", functionDecl(unless(cxxMethodDecl()), 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))); |
- match_finder.addMatcher(function_ref_matcher, &call_rewriter); |
- |
- // Initializer handling: |
+ FunctionRefRewriter function_ref_rewriter(&replacements); |
+ match_finder.addMatcher(function_ref_matcher, &function_ref_rewriter); |
+ |
+ // Method declarations ======== |
+ // Given |
+ // struct S { |
+ // void g(); |
+ // }; |
+ // matches |g|. |
+ // |
+ // Note: the AST matchers don't provide a good way to match against an |
+ // override from a given base class. Instead, the rewriter uses two matchers: |
+ // one that matches all method declarations in the Blink namespace, and |
+ // another which matches all overridden methods not in the Blink namespace. |
+ // The second list is filtered against the first list to determine which |
+ // methods are inherited from Blink classes and need to be rewritten. |
+ auto blink_method_decl_matcher = |
+ id("decl", cxxMethodDecl(unless(anyOf( |
+ // Overloaded operators have special names |
+ // and should never be renamed. |
+ isOverloadedOperator(), |
+ // Similarly, constructors and destructors |
+ // should not be considered for renaming. |
+ cxxConstructorDecl(), cxxDestructorDecl())), |
+ in_blink_namespace)); |
+ // Note that the matcher for overridden methods doesn't need to filter for |
+ // special member functions: see implementation of FunctionDeclRewriter for |
+ // the full explanation. |
+ auto non_blink_overridden_method_decl_matcher = |
+ id("decl", cxxMethodDecl(isOverride(), unless(in_blink_namespace))); |
+ MethodDeclRewriter method_decl_rewriter(&replacements); |
+ match_finder.addMatcher(blink_method_decl_matcher, &method_decl_rewriter); |
+ match_finder.addMatcher(non_blink_overridden_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 blink_method_ref_matcher = |
+ id("expr", declRefExpr(to(blink_method_decl_matcher))); |
+ auto non_blink_overridden_method_ref_matcher = |
+ id("expr", declRefExpr(to(non_blink_overridden_method_decl_matcher))); |
+ |
+ MethodRefRewriter method_ref_rewriter(method_decl_rewriter, &replacements); |
+ match_finder.addMatcher(blink_method_ref_matcher, &method_ref_rewriter); |
+ match_finder.addMatcher(non_blink_overridden_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 blink_method_member_matcher = |
+ id("expr", memberExpr(member(blink_method_decl_matcher))); |
+ auto non_blink_overridden_method_member_matcher = |
+ id("expr", memberExpr(member(non_blink_overridden_method_decl_matcher))); |
+ |
+ MethodMemberRewriter method_member_rewriter(method_decl_rewriter, |
+ &replacements); |
+ match_finder.addMatcher(blink_method_member_matcher, &method_member_rewriter); |
+ match_finder.addMatcher(non_blink_overridden_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(forField(field_decl_matcher))))); |
- ConstructorInitializerRewriterCallback constructor_initializer_rewriter( |
+ ConstructorInitializerRewriter constructor_initializer_rewriter( |
&replacements); |
match_finder.addMatcher(constructor_initializer_matcher, |
&constructor_initializer_rewriter); |