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 3c39350d311c0d1c6c26b1268a6b55d43ed8f0f8..547f85e31b80f529fff15ad1301cc8e61eb92de7 100644 |
| --- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
| +++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
| @@ -62,6 +62,14 @@ 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(); |
| } |
| @@ -165,6 +173,34 @@ AST_MATCHER_P(clang::CXXMethodDecl, |
| return MatchAllOverriddenMethods(Node, InnerMatcher, Finder, Builder); |
| } |
| +// Matches |x.m| CXXDependentScopeMemberExpr if InnerMatcher matches |x|. |
| +AST_MATCHER_P(clang::CXXDependentScopeMemberExpr, |
| + hasBaseExprMissingOrMatching, |
| + clang::ast_matchers::internal::Matcher<clang::Expr>, |
| + InnerMatcher) { |
| + clang::Expr* base_expr = Node.isImplicitAccess() ? nullptr : Node.getBase(); |
| + return !base_expr || InnerMatcher.matches(*base_expr, Finder, Builder); |
|
dcheng
2016/11/30 05:55:52
What's an implicit access look like? Why is it OK
Łukasz Anforowicz
2016/11/30 23:26:53
1. You are right that we should not return a match
dcheng
2016/12/01 07:15:06
I see. I think the usual preference is to have sma
Łukasz Anforowicz
2016/12/01 18:26:56
Acknowledged.
|
| +} |
| + |
| +// Matches |T::m| CXXDependentScopeMemberExpr if InnerMatcher matches |T|. |
| +AST_MATCHER_P( |
| + clang::CXXDependentScopeMemberExpr, |
| + hasQualifierMissingOrMatching, |
| + clang::ast_matchers::internal::Matcher<clang::NestedNameSpecifier>, |
| + InnerMatcher) { |
| + clang::NestedNameSpecifier* qual = Node.getQualifier(); |
| + return !qual || InnerMatcher.matches(*qual, Finder, Builder); |
|
dcheng
2016/11/30 05:55:52
Similar question here: why is it OK to return a ma
Łukasz Anforowicz
2016/11/30 23:26:53
See above.
|
| +} |
| + |
| +// 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) |
| @@ -531,6 +567,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()); |
| @@ -677,10 +731,39 @@ clang::DeclarationName GetUnresolvedName( |
| } |
| 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 */) { |
| + // Heuristic - looking only at the shape of AST, let's assume that |using |
| + // Base::foo| refers to a method. This heuristic can be refined by also |
| + // looking at the name of |decl| (this is done both for |
| + // UnresolvedUsingValueDecl and for clang::Expr in |
| + // GuessNameForUnresolvedDependentNode method). |
| + return true; |
| +} |
| + |
| template <typename TargetNode> |
| class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
| public: |
| @@ -691,12 +774,26 @@ class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
| void run(const MatchFinder::MatchResult& result) override { |
| const TargetNode& expr = Base::GetTargetNode(result); |
| - llvm::StringRef old_name = GetUnresolvedName(expr).getAsString(); |
| - std::string new_name; |
| - if (GuessNameForUnresolvedDependentNode(expr, *result.Context, old_name, |
| - new_name)) { |
| - Base::AddReplacement(result, old_name, std::move(new_name)); |
| + |
| + clang::DeclarationName unresolved_name = GetUnresolvedName(expr); |
| + switch (unresolved_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; |
| } |
| + |
| + std::string old_name = unresolved_name.getAsString(); |
| + std::string new_name = GuessNameForUnresolvedDependentNode( |
| + expr, *result.Context, old_name); |
| + if (!new_name.empty()) |
| + Base::AddReplacement(result, old_name, std::move(new_name)); |
| } |
| private: |
| @@ -706,25 +803,25 @@ class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
| // 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. |
| - bool GuessNameForUnresolvedDependentNode(const TargetNode& node, |
| + // |
| + // The guessed new name is returned (or an empty string if no name was |
| + // guessed). |
| + std::string GuessNameForUnresolvedDependentNode(const TargetNode& node, |
|
dcheng
2016/11/30 05:55:52
Can we keep this consistent with GetNameForDecl()?
Łukasz Anforowicz
2016/11/30 23:26:53
Done (I think).
There is some duplication for cal
dcheng
2016/12/01 07:15:06
I think I'm OK with passing old_name in; just havi
Łukasz Anforowicz
2016/12/01 18:26:57
Done.
|
| clang::ASTContext& context, |
| - llvm::StringRef old_name, |
| - std::string& new_name) { |
| + llvm::StringRef old_name) { |
| // |m_fieldName| -> |field_name_|. |
| if (old_name.startswith(kBlinkFieldPrefix)) { |
| - std::string field_name = old_name.str().substr(strlen(kBlinkFieldPrefix)); |
| - if (field_name.find('_') == std::string::npos) { |
| - new_name = CamelCaseToUnderscoreCase(field_name) + "_"; |
| - return true; |
| - } |
| + std::string field_name = old_name.substr(strlen(kBlinkFieldPrefix)); |
| + if (field_name.find('_') == std::string::npos) |
| + return CamelCaseToUnderscoreCase(field_name) + "_"; |
| } |
| // |T::myMethod(...)| -> |T::MyMethod(...)|. |
| - if ((old_name.find('_') == std::string::npos) && |
| + if ((old_name.find('_') == std::string::npos) && IsCallee(node, context) && |
| !IsBlacklistedFunctionOrMethodName(old_name)) { |
| - new_name = old_name; |
| + std::string new_name = old_name; |
| new_name[0] = clang::toUppercase(new_name[0]); |
| - return true; |
| + return new_name; |
| } |
| // In the future we can consider more heuristics: |
| @@ -732,7 +829,7 @@ class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
| // - "ALL_CAPS" |
| // - |T::myStaticField| -> |T::kMyStaticField| |
| // (but have to be careful not to rename |value| in WTF/TypeTraits.h?) |
| - return false; |
| + return std::string(); |
| } |
| }; |
| @@ -742,6 +839,12 @@ using UnresolvedDependentMemberRewriter = |
| using UnresolvedUsingValueDeclRewriter = |
| UnresolvedRewriterBase<clang::UnresolvedUsingValueDecl>; |
| +using DependentScopeDeclRefExprRewriter = |
| + UnresolvedRewriterBase<clang::DependentScopeDeclRefExpr>; |
| + |
| +using CXXDependentScopeMemberExprRewriter = |
| + UnresolvedRewriterBase<clang::CXXDependentScopeMemberExpr>; |
| + |
| } // namespace |
| static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
| @@ -1068,6 +1171,65 @@ 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> |
| + // - ... |
| + // TODO(lukasza): The matchers below can be simplified after |
| + // https://llvm.org/bugs/show_bug.cgi?id=30331 is fixed. |
| + // Simplified matchers: |
| + // auto blink_qual_type_base_matcher = |
| + // qualType(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))); |
| + auto blink_qual_type_bug_workaround_matcher1 = hasBaseType( |
| + anyOf(enumType(hasDeclaration(in_blink_namespace)), |
| + recordType(hasDeclaration(in_blink_namespace)), |
| + templateSpecializationType(hasDeclaration(in_blink_namespace)), |
| + templateTypeParmType(hasDeclaration(in_blink_namespace)), |
| + typedefType(hasDeclaration(in_blink_namespace)))); |
| + auto blink_qual_type_base_matcher = |
| + qualType(anyOf(blink_qual_type_bug_workaround_matcher1, |
| + hasBaseType(elaboratedType( |
| + namesType(blink_qual_type_bug_workaround_matcher1))))); |
| + auto blink_qual_type_matcher = |
| + qualType(anyOf(blink_qual_type_base_matcher, pointsTo(in_blink_namespace), |
| + references(in_blink_namespace))); |
| + |
| + // 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(allOf( |
| + hasBaseExprMissingOrMatching(hasType(blink_qual_type_matcher)), |
| + hasQualifierMissingOrMatching( |
| + specifiesType(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); |
| + |
| std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
| clang::tooling::newFrontendActionFactory(&match_finder); |
| int result = tool.run(factory.get()); |