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..305cfbe3d5cff3fe9e64de56c23dbfa307050dfc 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 |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) |
@@ -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,45 @@ 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 */) { |
+ // 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: |
@@ -690,13 +779,33 @@ class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
: RewriterBase<TargetNode>(replacements) {} |
void run(const MatchFinder::MatchResult& result) override { |
- const TargetNode& expr = Base::GetTargetNode(result); |
- llvm::StringRef old_name = GetUnresolvedName(expr).getAsString(); |
+ 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(expr, *result.Context, old_name, |
- new_name)) { |
+ if (GuessNameForUnresolvedDependentNode(node, *result.Context, old_name, |
+ new_name)) |
Base::AddReplacement(result, old_name, std::move(new_name)); |
- } |
} |
private: |
@@ -706,13 +815,16 @@ 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. |
+ // |
+ // 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.str().substr(strlen(kBlinkFieldPrefix)); |
+ std::string field_name = old_name.substr(strlen(kBlinkFieldPrefix)); |
if (field_name.find('_') == std::string::npos) { |
new_name = CamelCaseToUnderscoreCase(field_name) + "_"; |
return true; |
@@ -720,10 +832,10 @@ class UnresolvedRewriterBase : public RewriterBase<TargetNode> { |
} |
// |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; |
- new_name[0] = clang::toUppercase(new_name[0]); |
+ new_name[0] = clang::toUppercase(old_name[0]); |
return true; |
} |
@@ -742,6 +854,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 +1186,62 @@ 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( |
+ 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); |
+ |
std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
clang::tooling::newFrontendActionFactory(&match_finder); |
int result = tool.run(factory.get()); |