 Chromium Code Reviews
 Chromium Code Reviews Issue 2274653002:
  Handling of UnresolvedUsingValueDecl nodes.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@blink-style-new-clang
    
  
    Issue 2274653002:
  Handling of UnresolvedUsingValueDecl nodes.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@blink-style-new-clang| 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 6567f77918d8e70960af1cadaeb32abaeafedc7f..85a9661d70f190f8cc0f4857067739b9de772cf6 100644 | 
| --- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp | 
| +++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp | 
| @@ -175,11 +175,13 @@ bool IsBlacklistedFunction(const clang::FunctionDecl& decl) { | 
| return decl.getName() == "swap"; | 
| } | 
| -bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) { | 
| - if (decl.isStatic()) | 
| +bool IsBlacklistedMethod(const std::string& name, | 
| 
dcheng
2016/09/10 04:00:55
Pass as llvm::StringRef here, so we don't need to
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.
This is how I started actually, but then go
 | 
| + const clang::CXXMethodDecl* decl) { | 
| + if (decl && decl->isStatic()) | 
| 
dcheng
2016/09/10 04:00:55
It feels like we should just have two versions of
 
Łukasz Anforowicz
2016/09/12 19:52:56
In the latest patchset I have 1) reverted changes
 | 
| return false; | 
| - clang::StringRef name = decl.getName(); | 
| + if (decl) | 
| + assert(decl->getName().str() == name); | 
| // These methods should never be renamed. | 
| static const char* kBlacklistMethods[] = {"trace", "traceImpl", "lock", | 
| @@ -191,9 +193,15 @@ bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) { | 
| // 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) { | 
| + bool is_iterator = false; | 
| + if (decl) { | 
| + std::string ret_type = decl->getReturnType().getAsString(); | 
| + is_iterator = ret_type.find("iterator") != std::string::npos || | 
| + ret_type.find("Iterator") != std::string::npos; | 
| 
dcheng
2016/09/10 04:00:55
For this, we can just have a simple shared method
 
Łukasz Anforowicz
2016/09/12 19:52:56
I think this comment is not applicable to the most
 | 
| + } else { | 
| + is_iterator = true; | 
| + } | 
| + if (is_iterator) { | 
| static const char* kIteratorBlacklist[] = {"begin", "end", "rbegin", | 
| "rend"}; | 
| for (const auto& b : kIteratorBlacklist) { | 
| @@ -205,8 +213,8 @@ bool IsBlacklistedMethod(const clang::CXXMethodDecl& decl) { | 
| // Subclasses of InspectorAgent will subclass "disable()" from both blink and | 
| // from gen/, which is problematic, but DevTools folks don't want to rename | 
| // it or split this up. So don't rename it at all. | 
| - if (name.equals("disable") && | 
| - IsMethodOverrideOf(decl, "blink::InspectorAgent")) | 
| + if (name == "disable" && | 
| + (!decl || IsMethodOverrideOf(*decl, "blink::InspectorAgent"))) | 
| return true; | 
| return false; | 
| @@ -217,7 +225,7 @@ AST_MATCHER(clang::FunctionDecl, isBlacklistedFunction) { | 
| } | 
| AST_MATCHER(clang::CXXMethodDecl, isBlacklistedMethod) { | 
| - return IsBlacklistedMethod(Node); | 
| + return IsBlacklistedMethod(Node.getName().str(), &Node); | 
| } | 
| // Helper to convert from a camelCaseName to camel_case_name. It uses some | 
| @@ -464,7 +472,6 @@ struct TargetNodeTraits<clang::NamedDecl> { | 
| return decl.getLocation(); | 
| } | 
| static const char* GetName() { return "decl"; } | 
| - static const char* GetType() { return "NamedDecl"; } | 
| 
dcheng
2016/09/10 04:00:55
While unused, these were helpful for debugging in
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.
 | 
| }; | 
| template <> | 
| @@ -473,7 +480,6 @@ struct TargetNodeTraits<clang::MemberExpr> { | 
| return expr.getMemberLoc(); | 
| } | 
| static const char* GetName() { return "expr"; } | 
| - static const char* GetType() { return "MemberExpr"; } | 
| }; | 
| template <> | 
| @@ -482,7 +488,6 @@ struct TargetNodeTraits<clang::DeclRefExpr> { | 
| return expr.getLocation(); | 
| } | 
| static const char* GetName() { return "expr"; } | 
| - static const char* GetType() { return "DeclRefExpr"; } | 
| }; | 
| template <> | 
| @@ -492,7 +497,6 @@ struct TargetNodeTraits<clang::CXXCtorInitializer> { | 
| return init.getSourceLocation(); | 
| } | 
| static const char* GetName() { return "initializer"; } | 
| - static const char* GetType() { return "CXXCtorInitializer"; } | 
| }; | 
| template <> | 
| @@ -501,7 +505,6 @@ struct TargetNodeTraits<clang::UnresolvedLookupExpr> { | 
| return expr.getNameLoc(); | 
| } | 
| static const char* GetName() { return "expr"; } | 
| - static const char* GetType() { return "UnresolvedLookupExpr"; } | 
| }; | 
| template <> | 
| @@ -510,17 +513,64 @@ struct TargetNodeTraits<clang::UnresolvedMemberExpr> { | 
| return expr.getMemberLoc(); | 
| } | 
| static const char* GetName() { return "expr"; } | 
| - 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"; } | 
| +}; | 
| + | 
| +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; | 
| + } | 
| + | 
| + void AddReplacement(const MatchFinder::MatchResult& result, | 
| + llvm::StringRef old_name, | 
| + std::string new_name) { | 
| + if (old_name == new_name) | 
| + return; | 
| + | 
| + clang::SourceLocation loc = | 
| + TargetNodeTraits<TargetNode>::GetLoc(GetTargetNode(result)); | 
| + 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_; | 
| + } | 
| + | 
| + private: | 
| + std::set<Replacement>* const replacements_; | 
| + std::unordered_map<std::string, std::string> replacement_names_; | 
| +}; | 
| + | 
| +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"); | 
| + assert(decl); | 
| // If false, there's no name to be renamed. | 
| if (!decl->getIdentifier()) | 
| return; | 
| @@ -542,55 +592,129 @@ class RewriterBase : public MatchFinder::MatchCallback { | 
| if (!GetNameForDecl(*decl, *context, new_name)) | 
| return; // If false, the name was not suitable for renaming. | 
| llvm::StringRef old_name = decl->getName(); | 
| - if (old_name == new_name) | 
| - 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)); | 
| + Base::AddReplacement(result, old_name, std::move(new_name)); | 
| } | 
| +}; | 
| 
dcheng
2016/09/10 04:00:55
Nit: let's move the DeclRewriterBase using decls u
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.
 | 
| - const std::unordered_map<std::string, std::string>& replacement_names() | 
| - const { | 
| - return replacement_names_; | 
| +clang::DeclarationName GetUnresolvedName( | 
| + const clang::UnresolvedMemberExpr& expr) { | 
| + return expr.getMemberName(); | 
| +} | 
| + | 
| +clang::DeclarationName GetUnresolvedName( | 
| + const clang::UnresolvedUsingValueDecl& decl) { | 
| + return decl.getDeclName(); | 
| +} | 
| + | 
| +// Returns whether |expr| is a callee of a method or function call. | 
| +bool IsCallee(const clang::Expr& expr, clang::ASTContext& context) { | 
| 
dcheng
2016/09/10 04:00:55
Can this logic be encapsulated into a matcher?
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.  Thanks for helping figure out that I need |
 | 
| + auto parents = context.getParents(expr); | 
| + return std::all_of( | 
| + parents.begin(), parents.end(), | 
| + [&expr](const clang::ast_type_traits::DynTypedNode& parent) { | 
| + const clang::CallExpr* call = parent.get<clang::CallExpr>(); | 
| + return call && call->getCallee() == &expr; | 
| + }); | 
| +} | 
| + | 
| +bool IsCallee(const clang::UnresolvedUsingValueDecl& /* decl */, | 
| + clang::ASTContext& /* context */) { | 
| + // Heuristic - assuming that |using Base::foo| refers to a method. | 
| 
dcheng
2016/09/10 04:00:55
We can guess from the name though, right?
 
Łukasz Anforowicz
2016/09/12 19:52:56
Yes, we can guess from the name - we do it in Gues
 | 
| + 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& 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)); | 
| + } | 
| } | 
| private: | 
| - std::set<Replacement>* const replacements_; | 
| - std::unordered_map<std::string, std::string> replacement_names_; | 
| + // 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. | 
| + bool GuessNameForUnresolvedDependentNode(const TargetNode& node, | 
| + clang::ASTContext& context, | 
| + const std::string& old_name, | 
| 
dcheng
2016/09/10 04:00:56
Pass this as a llvm::StringRef.
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.
 | 
| + std::string& new_name) { | 
| + // |m_fieldName| -> |field_name_|. | 
| + if (old_name.substr(0, strlen(kBlinkFieldPrefix)) == kBlinkFieldPrefix) { | 
| 
dcheng
2016/09/10 04:00:55
Then this can just use llvm::StringRef::startswith
 
Łukasz Anforowicz
2016/09/12 19:52:56
Done.
 | 
| + std::string field_name = old_name.substr(strlen(kBlinkFieldPrefix)); | 
| 
Łukasz Anforowicz
2016/09/12 19:52:56
This is the reason why in the end I've used std::s
 | 
| + if (field_name.find('_') == std::string::npos) { | 
| + new_name = CamelCaseToUnderscoreCase(field_name) + "_"; | 
| + return true; | 
| + } | 
| + } | 
| + | 
| + if ((old_name.find('_') == std::string::npos) && IsCallee(node, context) && | 
| + !IsBlacklistedMethod(old_name, nullptr)) { | 
| + // |T::myMethod(...)| -> |T::MyMethod(...)|. | 
| + new_name = old_name; | 
| + new_name[0] = clang::toUppercase(new_name[0]); | 
| + 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 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 UnresolvedDependentMemberRewriter = | 
| + UnresolvedRewriterBase<clang::UnresolvedMemberExpr>; | 
| -using UsingDeclRewriter = RewriterBase<clang::UsingDecl, clang::NamedDecl>; | 
| +using UnresolvedUsingValueDeclRewriter = | 
| + UnresolvedRewriterBase<clang::UnresolvedUsingValueDecl>; | 
| + | 
| +using UsingDeclRewriter = DeclRewriterBase<clang::UsingDecl, clang::NamedDecl>; | 
| } // namespace | 
| @@ -862,7 +986,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( | 
| @@ -876,6 +1000,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; <- matched by |unresolved_using_value_decl_matcher|. | 
| + // void method() { | 
| + // m_size = 123; // <- |m_size| 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; |