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

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: Self-review. Created 3 years, 11 months 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
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 c3d3356d2542a138cf35bdd3f4e086eb357d4983..d9c5ac32b9843c75c70eb1ff4516b01f5c4909d7 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -24,8 +24,12 @@
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
@@ -44,6 +48,7 @@ namespace {
const char kBlinkFieldPrefix[] = "m_";
const char kBlinkStaticMemberPrefix[] = "s_";
const char kGeneratedFileRegex[] = "^gen/|/gen/";
+const char kGMockMethodNamePrefix[] = "gmock_";
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())
dcheng 2017/01/09 19:27:47 It's too bad it's not easy to do something like us
Łukasz Anforowicz 2017/01/09 20:11:27 Acknowledged.
+ 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|
@@ -793,14 +833,20 @@ class RewriterBase : public MatchFinder::MatchCallback {
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))
@@ -883,6 +929,78 @@ 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) {}
+
+ std::unique_ptr<clang::PPCallbacks> CreatePreprocessorCallbacks() {
+ return llvm::make_unique<GMockMemberRewriter::PPCallbacks>(this);
+ }
+
+ clang::SourceLocation GetTargetLoc(
+ const MatchFinder::MatchResult& result) override {
+ // Find location of the gmock_##MockedMethod identifier.
+ clang::SourceLocation target_loc = Base::GetTargetLoc(result);
+
+ // Find location of EXPECT_CALL macro invocation.
+ clang::SourceLocation macro_call_loc =
+ result.SourceManager->getExpansionLoc(target_loc);
+
+ // Map |macro_call_loc| to argument location (location of the method name
+ // that needs renaming).
+ auto it = expect_call_to_2nd_arg.find(macro_call_loc);
+ if (it == expect_call_to_2nd_arg.end())
+ return clang::SourceLocation();
+ return it->second;
+ }
+
+ private:
+ std::map<clang::SourceLocation, clang::SourceLocation> expect_call_to_2nd_arg;
+
+ // Called from PPCallbacks with the locations of EXPECT_CALL macro invocation:
+ // Example:
+ // EXPECT_CALL(my_mock, myMethod(123, 456));
+ // ^- expansion_loc ^- actual_arg_loc
+ void RecordExpectCallMacroInvocation(clang::SourceLocation expansion_loc,
+ clang::SourceLocation second_arg_loc) {
+ expect_call_to_2nd_arg[expansion_loc] = second_arg_loc;
+ }
+
+ class PPCallbacks : public clang::PPCallbacks {
+ public:
+ PPCallbacks(GMockMemberRewriter* rewriter) : rewriter_(rewriter) {}
dcheng 2017/01/09 19:27:47 Nit: explicit
Łukasz Anforowicz 2017/01/09 20:11:27 Ooops. Done.
+ ~PPCallbacks() override {}
+ void MacroExpands(const clang::Token& name,
+ const clang::MacroDefinition& def,
+ clang::SourceRange range,
+ const clang::MacroArgs* args) override {
+ clang::IdentifierInfo* id = name.getIdentifierInfo();
+ if (!id)
+ return;
+
+ if (id->getName() != "EXPECT_CALL")
+ return;
+
+ if (def.getMacroInfo()->getNumArgs() != 2)
+ return;
+
+ // TODO(lukasza): Should check if def.getMacroInfo()->getDefinitionLoc()
+ // is in testing/gmock/include/gmock/gmock-spec-builders.h but I don't
+ // know how to get clang::SourceManager to call getFileName.
+
+ rewriter_->RecordExpectCallMacroInvocation(
+ name.getLocation(), args->getUnexpArgument(1)->getLocation());
+ }
+
+ private:
+ GMockMemberRewriter* rewriter_;
+ };
+};
+
clang::DeclarationName GetUnresolvedName(
const clang::UnresolvedMemberExpr& expr) {
return expr.getMemberName();
@@ -1020,6 +1138,37 @@ using DependentScopeDeclRefExprRewriter =
using CXXDependentScopeMemberExprRewriter =
UnresolvedRewriterBase<clang::CXXDependentScopeMemberExpr>;
+class SourceFileCallbacks : public clang::tooling::SourceFileCallbacks {
+ public:
+ SourceFileCallbacks() : source_counter_(0) {}
+ ~SourceFileCallbacks() override {}
+
+ void AddPPCallbacks(std::unique_ptr<clang::PPCallbacks> new_callbacks) {
+ if (!pp_callbacks_) {
+ pp_callbacks_ = std::move(new_callbacks);
+ } else {
+ pp_callbacks_ = llvm::make_unique<clang::PPChainedCallbacks>(
+ std::move(new_callbacks), std::move(pp_callbacks_));
dcheng 2017/01/09 19:27:47 Do we need chaining support? It seems like we our
Łukasz Anforowicz 2017/01/09 20:11:27 Done.
+ }
+ }
+
+ // clang::tooling::SourceFileCallbacks override:
+ bool handleBeginSource(clang::CompilerInstance& compiler,
+ llvm::StringRef Filename) override {
+ source_counter_++;
+ assert(source_counter_ == 1); // We only have *one* pp_callbacks_.
dcheng 2017/01/09 19:27:47 What happens if the tool is run on multiple files
Łukasz Anforowicz 2017/01/09 20:11:27 Fixed by creating a new PPCallbacks every time.
+
+ if (pp_callbacks_)
+ compiler.getPreprocessor().addPPCallbacks(std::move(pp_callbacks_));
dcheng 2017/01/09 19:27:47 Similarly, I think we should be able to assert pp_
Łukasz Anforowicz 2017/01/09 20:11:27 See above - we are now creating a new PPCallbacks
+
+ return true;
+ }
+
+ private:
+ int source_counter_;
+ std::unique_ptr<clang::PPCallbacks> pp_callbacks_;
+};
+
} // namespace
static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
@@ -1205,7 +1354,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.
@@ -1219,7 +1368,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)));
@@ -1388,8 +1537,24 @@ 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.
dcheng 2017/01/09 19:27:47 This comment seems a bit out-of-place, since it lo
Łukasz Anforowicz 2017/01/09 20:11:27 I changed the comment - does it look better now?
+ SourceFileCallbacks source_file_callbacks;
+ source_file_callbacks.AddPPCallbacks(
+ gmock_member_rewriter.CreatePreprocessorCallbacks());
std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
- clang::tooling::newFrontendActionFactory(&match_finder);
+ clang::tooling::newFrontendActionFactory(&match_finder,
+ &source_file_callbacks);
int result = tool.run(factory.get());
if (result != 0)
return result;

Powered by Google App Engine
This is Rietveld 408576698