Chromium Code Reviews| 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..fc710dd73fa841102110487159451e65cc6ebfc6 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 |
|
danakj
2016/01/06 22:09:40
there are at least some constants named MaxThings.
dcheng
2016/01/08 01:41:11
It should (the heuristics for using kConstantStyle
|
| -// 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,165 @@ bool GetNameForDecl(const clang::VarDecl& decl, |
| return true; |
| } |
| -template <typename DeclNodeType, typename TargetTraits> |
| -class RewriterCallbackBase : public MatchFinder::MatchCallback { |
| - public: |
| - explicit RewriterCallbackBase(Replacements* replacements) |
| - : replacements_(replacements) {} |
| - virtual void run(const MatchFinder::MatchResult& result) override { |
| - std::string name; |
| - if (!GetNameForDecl(*result.Nodes.getNodeAs<DeclNodeType>("decl"), |
| - result.Context, name)) |
| - return; |
| - replacements_->emplace( |
| - *result.SourceManager, |
| - TargetTraits::GetRange( |
| - *result.Nodes.getNodeAs<typename TargetTraits::NodeType>( |
| - TargetTraits::kName)), |
| - name); |
| - } |
| +template <typename Type> |
| +struct TargetNodeTraits; |
| - private: |
| - Replacements* const replacements_; |
| -}; |
| - |
| -struct DeclTargetTraits { |
| - using NodeType = clang::NamedDecl; |
| +template <> |
| +struct TargetNodeTraits<clang::NamedDecl> { |
| static constexpr char kName[] = "decl"; |
| - static clang::CharSourceRange GetRange(const NodeType& decl) { |
| + static clang::CharSourceRange GetRange(const clang::NamedDecl& decl) { |
| return clang::CharSourceRange::getTokenRange(decl.getLocation()); |
| } |
| }; |
| -constexpr char DeclTargetTraits::kName[]; |
| +constexpr char TargetNodeTraits<clang::NamedDecl>::kName[]; |
| -struct ExprTargetTraits { |
| - using NodeType = clang::Expr; |
| +template <> |
| +struct TargetNodeTraits<clang::CallExpr> { |
| static constexpr char kName[] = "expr"; |
| - static clang::CharSourceRange GetRange(const NodeType& expr) { |
| + static clang::CharSourceRange GetRange(const clang::CallExpr& expr) { |
| return clang::CharSourceRange::getTokenRange(expr.getExprLoc()); |
| } |
| }; |
| -constexpr char ExprTargetTraits::kName[]; |
| +constexpr char TargetNodeTraits<clang::CallExpr>::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[]; |
| -struct InitializerTargetTraits { |
| - using NodeType = clang::CXXCtorInitializer; |
| +template <> |
| +struct TargetNodeTraits<clang::DeclRefExpr> { |
| + static constexpr char kName[] = "expr"; |
| + static clang::CharSourceRange GetRange(const clang::DeclRefExpr& expr) { |
| + return clang::CharSourceRange::getTokenRange(expr.getLocation()); |
|
danakj
2016/01/06 22:09:40
consider a comment on each of these why you chose
dcheng
2016/01/08 01:41:11
I'm not sure how helpful a comment would be. It's
|
| + } |
| +}; |
| +constexpr char TargetNodeTraits<clang::DeclRefExpr>::kName[]; |
| + |
| +template <> |
| +struct TargetNodeTraits<clang::CXXCtorInitializer> { |
| static constexpr char kName[] = "initializer"; |
| - static clang::CharSourceRange GetRange(const NodeType& init) { |
| + static clang::CharSourceRange GetRange( |
| + const clang::CXXCtorInitializer& init) { |
| return clang::CharSourceRange::getTokenRange(init.getSourceLocation()); |
| } |
| }; |
| -constexpr char InitializerTargetTraits::kName[]; |
| +constexpr char TargetNodeTraits<clang::CXXCtorInitializer>::kName[]; |
| + |
| +template <typename DeclNode, typename TargetNode> |
| +class RewriterBase : public MatchFinder::MatchCallback { |
| + public: |
| + explicit RewriterBase(Replacements* replacements) |
| + : replacements_(replacements) {} |
| -using FunctionDeclRewriterCallback = |
| - RewriterCallbackBase<clang::FunctionDecl, DeclTargetTraits>; |
| -using FieldDeclRewriterCallback = |
| - RewriterCallbackBase<clang::FieldDecl, DeclTargetTraits>; |
| -using VarDeclRewriterCallback = |
| - RewriterCallbackBase<clang::VarDecl, DeclTargetTraits>; |
| + void run(const MatchFinder::MatchResult& result) override { |
| + std::string name; |
| + if (!GetNameForDecl(*result.Nodes.getNodeAs<DeclNode>("decl"), name)) |
| + return; |
| + replacements_->emplace(*result.SourceManager, |
| + TargetNodeTraits<TargetNode>::GetRange( |
| + *result.Nodes.getNodeAs<TargetNode>( |
| + TargetNodeTraits<TargetNode>::kName)), |
| + name); |
| + } |
| -using CallRewriterCallback = |
| - RewriterCallbackBase<clang::FunctionDecl, ExprTargetTraits>; |
| -using MemberRewriterCallback = |
| - RewriterCallbackBase<clang::FieldDecl, ExprTargetTraits>; |
| -using DeclRefRewriterCallback = |
| - RewriterCallbackBase<clang::VarDecl, ExprTargetTraits>; |
| + private: |
| + Replacements* const replacements_; |
| +}; |
| -using ConstructorInitializerRewriterCallback = |
| - RewriterCallbackBase<clang::FieldDecl, InitializerTargetTraits>; |
| +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 ConstructorInitializerRewriter = |
| + RewriterBase<clang::FieldDecl, clang::CXXCtorInitializer>; |
| + |
| +// Helpers for rewriting functions. 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 FunctionDeclRewriter |
| + : public RewriterBase<clang::FunctionDecl, clang::NamedDecl> { |
| + public: |
| + explicit FunctionDeclRewriter(Replacements* replacements) |
| + : RewriterBase(replacements) {} |
| + |
| + void run(const MatchFinder::MatchResult& result) override { |
| + if (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()); |
| + } |
| + } |
| + |
| + RewriterBase::run(result); |
| + } |
| + |
| + 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_; |
| +}; |
| + |
| +template <typename BaseRewriter> |
| +class FilteringFunctionRewriter : public BaseRewriter { |
| + public: |
| + FilteringFunctionRewriter(const FunctionDeclRewriter& decl_rewriter, |
| + Replacements* replacements) |
| + : BaseRewriter(replacements), decl_rewriter_(decl_rewriter) {} |
| + |
| + void run(const MatchFinder::MatchResult& result) override { |
| + if (const clang::CXXMethodDecl* method_decl = |
| + result.Nodes.getNodeAs<clang::CXXMethodDecl>("decl")) { |
| + if (method_decl->size_overridden_methods() > 0 && |
| + !decl_rewriter_.IsBlinkOverride(method_decl)) |
| + return; |
| + } |
| + BaseRewriter::run(result); |
| + } |
| + |
| + private: |
| + const FunctionDeclRewriter& decl_rewriter_; |
| +}; |
| + |
| +using CallRewriter = FilteringFunctionRewriter< |
| + RewriterBase<clang::FunctionDecl, clang::CallExpr>>; |
| +using FunctionDeclRefRewriter = FilteringFunctionRewriter< |
| + RewriterBase<clang::FunctionDecl, clang::DeclRefExpr>>; |
| } // namespace |
| @@ -260,50 +354,77 @@ 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)); |
| - |
| + // Declarations of fields and variables: |
| 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))); |
| + // References to declared fields and variables: |
| 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): |
| + // The AST matchers don't provide a good way to match against an override from |
| + // a given class. Instead, use two matchers: one that matches the declarations |
| + // (in Blink) and another which matches all overrides and performs some |
| + // filtering. |
| + auto function_decl_matcher = |
| + id("decl", |
| + functionDecl(unless(anyOf(cxxMethodDecl(isOverride()), |
| + // 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 explanation. |
| + auto overridden_method_decl_matcher = id("decl", cxxMethodDecl(isOverride())); |
| + FunctionDeclRewriter function_decl_rewriter(&replacements); |
| + match_finder.addMatcher(function_decl_matcher, &function_decl_rewriter); |
| + match_finder.addMatcher(overridden_method_decl_matcher, |
| + &function_decl_rewriter); |
| + |
| + // Call handling: |
| + auto call_matcher = id("expr", callExpr(callee(function_decl_matcher))); |
| + auto overridden_call_matcher = |
| + id("expr", callExpr(callee(overridden_method_decl_matcher))); |
| + |
| + CallRewriter call_rewriter(function_decl_rewriter, &replacements); |
| + match_finder.addMatcher(call_matcher, &call_rewriter); |
| + match_finder.addMatcher(overridden_call_matcher, &call_rewriter); |
| + |
| + // Function reference handling (e.g. getting a pointer to a function): |
| auto function_ref_matcher = |
| id("expr", declRefExpr(to(function_decl_matcher))); |
| - match_finder.addMatcher(function_ref_matcher, &call_rewriter); |
| + // TODO(dcheng): apparently this bit of code isn't exercised in tests. |
| + auto overridden_method_ref_matcher = |
| + id("expr", declRefExpr(to(overridden_method_decl_matcher))); |
| + |
| + FunctionDeclRefRewriter function_decl_ref_rewriter(function_decl_rewriter, |
| + &replacements); |
| + match_finder.addMatcher(function_ref_matcher, &function_decl_ref_rewriter); |
| + match_finder.addMatcher(overridden_method_ref_matcher, |
| + &function_decl_ref_rewriter); |
| // Initializer handling: |
| 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); |