Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Unified Diff: tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp

Issue 2609473002: Renaming blink method names inside gmock's EXPECT_CALL macro invocation. (Closed)
Patch Set: Tweaks (from self-review + to make it work in actual cases under web/tests/). Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/clang/rewrite_to_chrome_style/tests/gmock-expected.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 a268739ccd31eddba1b9f780e521fc96fd8e74d4..e6fba51e2d61d58a208100a582f12f1875814d6f 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -44,6 +44,11 @@ namespace {
const char kBlinkFieldPrefix[] = "m_";
const char kBlinkStaticMemberPrefix[] = "s_";
const char kGeneratedFileRegex[] = "^gen/|/gen/";
+const char kGMockMethodNamePrefix[] = "gmock_";
+const int kGMockMaxArgOffset = 400; // ~8 lines of backtracking.
+const char kGMockExpectCallArgName[] = "call";
+const char kGMockExpectCallFilename[] =
+ "testing/gmock/include/gmock/gmock-spec-builders.h";
template <typename MatcherType, typename NodeType>
bool IsMatching(const MatcherType& matcher,
@@ -79,6 +84,41 @@ AST_MATCHER_P(clang::FunctionTemplateDecl,
return InnerMatcher.matches(*Node.getTemplatedDecl(), Finder, Builder);
}
+// Matches a CXXMethodDecl of a method declared via MOCK_METHODx macro if such
+// method mocks a method matched by the InnerMatcher. For example if "foo"
+// matcher matches "interfaceMethod", then mocksMethod(foo()) will match
+// "gmock_interfaceMethod" declared by MOCK_METHOD_x(interfaceMethod).
+AST_MATCHER_P(clang::CXXMethodDecl,
+ mocksMethod,
+ clang::ast_matchers::internal::Matcher<clang::CXXMethodDecl>,
+ InnerMatcher) {
+ if (!Node.getDeclName().isIdentifier())
+ return false;
+
+ llvm::StringRef method_name = Node.getName();
+ if (!method_name.startswith(kGMockMethodNamePrefix))
+ return false;
+
+ llvm::StringRef mocked_method_name =
+ method_name.substr(strlen(kGMockMethodNamePrefix));
+ for (const auto& potentially_mocked_method : Node.getParent()->methods()) {
+ if (!potentially_mocked_method->isVirtual())
+ continue;
+
+ clang::DeclarationName decl_name = potentially_mocked_method->getDeclName();
+ if (!decl_name.isIdentifier() ||
+ potentially_mocked_method->getName() != mocked_method_name)
+ continue;
+ if (potentially_mocked_method->getNumParams() != Node.getNumParams())
+ continue;
+
+ if (InnerMatcher.matches(*potentially_mocked_method, Finder, Builder))
+ return true;
+ }
+
+ return false;
+}
+
// If |InnerMatcher| matches |top|, then the returned matcher will match:
// - |top::function|
// - |top::Class::method|
@@ -677,6 +717,43 @@ struct TargetNodeTraits<clang::UnresolvedUsingValueDecl> {
static const char* GetType() { return "UnresolvedUsingValueDecl"; }
};
+bool JumpAboveMacroScratchSpace(const clang::SourceManager& source_manager,
+ clang::SourceLocation* loc) {
+ if (loc->isMacroID()) {
+ // Try to jump "above" the scratch buffer if |loc| is inside
+ // token##Concatenation.
+ const int kMaxJumps = 5;
+ bool verified_out_of_scratch_space = false;
+ for (int i = 0; i < kMaxJumps && !verified_out_of_scratch_space; i++) {
+ clang::SourceLocation spell = source_manager.getSpellingLoc(*loc);
+ verified_out_of_scratch_space =
+ source_manager.getBufferName(spell) != "<scratch space>";
+ if (!verified_out_of_scratch_space)
+ *loc = source_manager.getImmediateMacroCallerLoc(*loc);
+ }
+ if (!verified_out_of_scratch_space)
+ return false;
+ }
+
+ return true;
+}
+
+llvm::StringRef GetActualText(const clang::ASTContext& context,
+ const clang::SourceManager& source_manager,
+ clang::SourceLocation loc,
+ size_t length,
+ clang::CharSourceRange* out_range) {
dcheng 2017/01/06 06:58:45 Stylistically, I'm not a fan of optional output ar
Łukasz Anforowicz 2017/01/07 01:16:29 I've inlined GetActualText back into the only call
+ clang::SourceLocation spell = source_manager.getSpellingLoc(loc);
+ clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
+ spell, spell.getLocWithOffset(length));
+
+ if (out_range)
+ *out_range = range;
+
+ return clang::Lexer::getSourceText(range, source_manager,
+ context.getLangOpts());
+}
+
template <typename TargetNode>
class RewriterBase : public MatchFinder::MatchCallback {
public:
@@ -698,21 +775,8 @@ class RewriterBase : public MatchFinder::MatchCallback {
const clang::ASTContext& context = *result.Context;
const clang::SourceManager& source_manager = *result.SourceManager;
- if (loc.isMacroID()) {
- // Try to jump "above" the scratch buffer if |loc| is inside
- // token##Concatenation.
- const int kMaxJumps = 5;
- bool verified_out_of_scratch_space = false;
- for (int i = 0; i < kMaxJumps && !verified_out_of_scratch_space; i++) {
- clang::SourceLocation spell = source_manager.getSpellingLoc(loc);
- verified_out_of_scratch_space =
- source_manager.getBufferName(spell) != "<scratch space>";
- if (!verified_out_of_scratch_space)
- loc = source_manager.getImmediateMacroCallerLoc(loc);
- }
- if (!verified_out_of_scratch_space)
- return false;
- }
+ if (!JumpAboveMacroScratchSpace(source_manager, &loc))
+ return false;
// If the edit affects only the first character of the identifier, then
// narrow down the edit to only this single character. This is important
@@ -726,32 +790,37 @@ class RewriterBase : public MatchFinder::MatchCallback {
expected_old_text = expected_old_text.substr(0, 1);
new_text = new_text.substr(0, 1);
}
- clang::SourceLocation spell = source_manager.getSpellingLoc(loc);
- clang::CharSourceRange range = clang::CharSourceRange::getCharRange(
- spell, spell.getLocWithOffset(expected_old_text.size()));
// We need to ensure that |actual_old_text| is the same as
// |expected_old_text| - it can be different if |actual_old_text| contains
// a macro argument (see DEFINE_WITH_TOKEN_CONCATENATION2 in
// macros-original.cc testcase).
- StringRef actual_old_text = clang::Lexer::getSourceText(
- range, source_manager, context.getLangOpts());
+ clang::CharSourceRange range;
+ StringRef actual_old_text = GetActualText(context, source_manager, loc,
+ expected_old_text.size(), &range);
if (actual_old_text != expected_old_text)
return false;
if (replacement)
*replacement = Replacement(source_manager, range, new_text);
+
return true;
}
+ virtual clang::SourceLocation GetTargetLoc(
+ const MatchFinder::MatchResult& result) {
+ return TargetNodeTraits<TargetNode>::GetLoc(GetTargetNode(result));
+ }
+
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::SourceLocation loc = GetTargetLoc(result);
+ if (loc.isInvalid())
+ return;
Replacement replacement;
if (!GenerateReplacement(result, loc, old_name, new_name, &replacement))
@@ -776,9 +845,14 @@ class DeclRewriterBase : public RewriterBase<TargetNode> {
explicit DeclRewriterBase(std::set<Replacement>* replacements)
: Base(replacements) {}
- void run(const MatchFinder::MatchResult& result) override {
+ const DeclNode* GetDecl(const MatchFinder::MatchResult& result) {
const DeclNode* decl = result.Nodes.getNodeAs<DeclNode>("decl");
assert(decl);
+ return decl;
+ }
+
+ void run(const MatchFinder::MatchResult& result) override {
+ const DeclNode* decl = GetDecl(result);
llvm::StringRef old_name = decl->getName();
// Return early if there's no name to be renamed.
@@ -834,6 +908,72 @@ using UnresolvedMemberRewriter =
using UsingDeclRewriter = DeclRewriterBase<clang::UsingDecl, clang::NamedDecl>;
+class GMockMemberRewriter
+ : public DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr> {
+ public:
+ using Base = DeclRewriterBase<clang::CXXMethodDecl, clang::MemberExpr>;
+
+ explicit GMockMemberRewriter(std::set<Replacement>* replacements)
+ : Base(replacements) {}
+
+ clang::SourceLocation GetTargetLoc(
+ const MatchFinder::MatchResult& result) override {
+ const clang::SourceManager& source_manager = *result.SourceManager;
+ const clang::ASTContext& context = *result.Context;
+ const clang::CXXMethodDecl* decl = GetDecl(result);
+ llvm::StringRef old_name = decl->getName();
+
+ // This is the location of gmock_##MockedMethod identifier.
+ clang::SourceLocation target_loc = Base::GetTargetLoc(result);
+ if (!JumpAboveMacroScratchSpace(source_manager, &target_loc))
+ return clang::SourceLocation();
+
+ // Find |macro_arg_loc| that points at |methodName| in
+ // EXPECT_CALL(obj, methodName(...)).
+ clang::SourceLocation macro_arg_loc;
+ for (int offset = 0; offset > -kGMockMaxArgOffset; offset--) {
+ macro_arg_loc = target_loc.getLocWithOffset(offset);
+
+ // Check if |macro_arg_loc| contains the mocked method name.
+ llvm::StringRef actual_text_of_macro_arg = GetActualText(
+ context, source_manager, macro_arg_loc, old_name.size(), nullptr);
+ if (actual_text_of_macro_arg != old_name)
+ continue;
+
+ // Location inside macro definition where the macro argument is expanded.
+ clang::SourceLocation macro_param_expansion_loc;
+ if (!source_manager.isMacroArgExpansion(macro_arg_loc,
+ &macro_param_expansion_loc))
+ continue;
+
+ // Check if |macro_param_expansion_loc| points here:
+ // testing/gmock/include/gmock/gmock-spec-builders.h:1844:20:
+ // 1843: #define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
+ // 1844: ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE_...
+ // ^ here
+ // 1845: #define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, c...
+ clang::SourceLocation spell_loc =
+ source_manager.getSpellingLoc(macro_param_expansion_loc);
+ StringRef filename = source_manager.getFilename(spell_loc);
+ if (!filename.endswith(kGMockExpectCallFilename))
+ continue;
+
+ StringRef actual_text_of_macro_param_name =
+ GetActualText(context, source_manager, macro_param_expansion_loc,
+ strlen(kGMockExpectCallArgName), nullptr);
+ if (actual_text_of_macro_param_name != kGMockExpectCallArgName)
+ continue;
+
+ // Found it!
+ return macro_arg_loc;
+ }
+
+ llvm::errs() << "Couldn't rewrite GMock's EXPECT_CALL at "
+ << target_loc.printToString(source_manager) << "\n";
+ return clang::SourceLocation();
+ }
+};
+
clang::DeclarationName GetUnresolvedName(
const clang::UnresolvedMemberExpr& expr) {
return expr.getMemberName();
@@ -1156,7 +1296,7 @@ int main(int argc, const char* argv[]) {
// S s;
// s.g();
// void (S::*p)() = &S::g;
- // matches |&S::g| but not |s.g()|.
+ // matches |&S::g| but not |s.g|.
auto method_ref_matcher = id(
"expr", declRefExpr(to(method_decl_matcher),
// Ignore template substitutions.
@@ -1170,7 +1310,7 @@ int main(int argc, const char* argv[]) {
// S s;
// s.g();
// void (S::*p)() = &S::g;
- // matches |s.g()| but not |&S::g|.
+ // matches |s.g| but not |&S::g|.
auto method_member_matcher =
id("expr", memberExpr(member(method_decl_matcher)));
@@ -1339,6 +1479,18 @@ int main(int argc, const char* argv[]) {
match_finder.addMatcher(cxx_dependent_scope_member_expr_matcher,
&cxx_dependent_scope_member_expr_rewriter);
+ // GMock calls lookup ========
+ // Given
+ // EXPECT_CALL(obj, myMethod(...))
+ // will match obj.gmock_myMethod(...) call generated by the macro
+ // (but only if it mocks a Blink method).
+ auto gmock_member_matcher =
+ id("expr", memberExpr(hasDeclaration(
+ decl(cxxMethodDecl(mocksMethod(method_decl_matcher))))));
+ GMockMemberRewriter gmock_member_rewriter(&replacements);
+ match_finder.addMatcher(gmock_member_matcher, &gmock_member_rewriter);
+
+ // Run all the matchers.
std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
clang::tooling::newFrontendActionFactory(&match_finder);
int result = tool.run(factory.get());
« no previous file with comments | « no previous file | tools/clang/rewrite_to_chrome_style/tests/gmock-expected.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698