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 5df98a529aea727ad79d3e991669a5db34c12b36..3a1465f587c8d352927d045dbd9b2021d045e2d2 100644 |
| --- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
| +++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp |
| @@ -34,6 +34,9 @@ |
| #include "clang/Tooling/Refactoring.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "llvm/Support/CommandLine.h" |
| +#include "llvm/Support/ErrorOr.h" |
| +#include "llvm/Support/LineIterator.h" |
| +#include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "EditTracker.h" |
| @@ -49,6 +52,7 @@ const char kBlinkFieldPrefix[] = "m_"; |
| const char kBlinkStaticMemberPrefix[] = "s_"; |
| const char kGeneratedFileRegex[] = "^gen/|/gen/"; |
| const char kGMockMethodNamePrefix[] = "gmock_"; |
| +const char kMethodBlocklistParamName[] = "method-blocklist"; |
| template <typename MatcherType, typename NodeType> |
| bool IsMatching(const MatcherType& matcher, |
| @@ -119,6 +123,124 @@ AST_MATCHER_P(clang::CXXMethodDecl, |
| return false; |
| } |
| +class MethodBlocklist { |
| + public: |
| + explicit MethodBlocklist(const std::string& filepath) { |
| + if (!filepath.empty()) |
| + ParseInputFile(filepath); |
| + } |
| + |
| + bool Contains(const clang::CXXMethodDecl& method) const { |
| + auto it = method_to_class_to_args_.find(method.getName()); |
| + if (it == method_to_class_to_args_.end()) |
| + return false; |
| + |
| + const clang::CXXRecordDecl* actual_class = method.getParent(); |
| + // Hopefully |getParent()| can find the class even for non-inlined method |
| + // definitions (where CXXRecordDecl is not a parent AST node of |
| + // CXXMethodDecl). |
| + assert(actual_class); |
|
dcheng
2017/01/17 22:56:10
Nit: a lot of times, assert messages in LLVM look
Łukasz Anforowicz
2017/01/17 23:52:47
Done.
|
| + |
| + const llvm::StringMap<std::set<unsigned>>& class_to_args = it->second; |
| + auto it2 = class_to_args.find(actual_class->getName()); |
| + if (it2 == class_to_args.end()) |
| + return false; |
| + |
| + const std::set<unsigned>& set_of_expected_number_of_args = it2->second; |
|
dcheng
2017/01/17 22:56:10
Nit: from the context, it's not clear if expected
Łukasz Anforowicz
2017/01/17 23:52:47
Done.
|
| + unsigned max_actual_number_of_args = method.param_size(); |
| + unsigned min_actual_number_of_args = max_actual_number_of_args; |
| + for (const clang::ParmVarDecl* param : method.parameters()) { |
| + if (param->hasInit()) |
| + min_actual_number_of_args--; |
| + } |
| + bool got_expected_number_of_args = std::any_of( |
|
dcheng
2017/01/17 22:56:11
Nit: Rename got_expected_to_number_of_args => foun
Łukasz Anforowicz
2017/01/17 23:52:46
Done.
|
| + set_of_expected_number_of_args.begin(), |
| + set_of_expected_number_of_args.end(), |
| + [min_actual_number_of_args, |
| + max_actual_number_of_args](unsigned expected_number_of_args) { |
| + return (min_actual_number_of_args <= expected_number_of_args) && |
| + (expected_number_of_args <= max_actual_number_of_args); |
| + }); |
| + if (!got_expected_number_of_args) |
| + return false; |
| + |
| + // No need to verify here that |actual_class| is in the |blink| namespace - |
| + // this will be done by other matchers elsewhere. |
| + |
| + // TODO(lukasza): Do we need to consider return type and/or param types? |
| + |
| + return true; |
|
dcheng
2017/01/17 22:56:10
Then just return found_matching_arg_count directly
Łukasz Anforowicz
2017/01/17 23:52:47
Done.
|
| + } |
| + |
| + private: |
| + // Each line is expected to have the following format: |
| + // <class name>:::<method name>:::<number of arguments> |
| + void ParseInputFile(const std::string& filepath) { |
| + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file_or_err = |
| + llvm::MemoryBuffer::getFile(filepath); |
| + if (std::error_code err = file_or_err.getError()) { |
| + llvm::errs() << "ERROR: Cannot open the file specified in --" |
| + << kMethodBlocklistParamName << " argument: " << filepath |
| + << ": " << err.message() << "\n"; |
| + assert(false); |
| + return; |
| + } |
| + |
| + llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#'); |
| + for (; !it.is_at_eof(); ++it) { |
| + llvm::StringRef line = it->trim(); |
| + if (line.empty()) |
| + continue; |
| + |
| + // Split the line into ':::'-delimited parts. |
| + const size_t kExpectedNumberOfParts = 3; |
| + llvm::SmallVector<llvm::StringRef, kExpectedNumberOfParts> parts; |
| + line.split(parts, ":::"); |
| + if (parts.size() != kExpectedNumberOfParts) { |
| + llvm::errs() << "ERROR: Parsing error - expected " |
| + << kExpectedNumberOfParts |
| + << " ':::'-delimited parts: " << filepath << ":" |
| + << it.line_number() << ": " << line << "\n"; |
| + assert(false); |
| + continue; |
| + } |
| + |
| + // Parse individual parts. |
| + llvm::StringRef class_name = parts[0]; |
| + llvm::StringRef method_name = parts[1]; |
| + unsigned number_of_method_args; |
| + if (parts[2].getAsInteger(0, number_of_method_args)) { |
| + llvm::errs() << "ERROR: Parsing error - '" << parts[2] << "' " |
| + << "is not an unsigned integer: " << filepath << ":" |
| + << it.line_number() << ": " << line << "\n"; |
| + assert(false); |
| + continue; |
| + } |
| + |
| + // Store the new entry. |
| + method_to_class_to_args_[method_name][class_name].insert( |
| + number_of_method_args); |
| + } |
| + } |
| + |
| + // Stores methods to blacklist in a map: |
| + // method name -> class name -> set of all allowed numbers of arguments. |
| + llvm::StringMap<llvm::StringMap<std::set<unsigned>>> method_to_class_to_args_; |
| +}; |
| + |
| +AST_MATCHER_P(clang::CXXMethodDecl, |
| + internalIsBlocklistedMethod, |
| + MethodBlocklist, |
| + Blocklist) { |
| + return Blocklist.Contains(Node); |
| +} |
| + |
| +clang::ast_matchers::internal::Matcher<clang::CXXMethodDecl> |
| +isBlocklistedMethod(const std::string& method_blocklist_filepath) { |
| + return internalIsBlocklistedMethod( |
| + MethodBlocklist(method_blocklist_filepath)); |
|
dcheng
2017/01/17 22:59:50
Oh, one other thing: do we need to split this betw
Łukasz Anforowicz
2017/01/17 23:52:47
Done. I did it this way thinking that one day we'
|
| +} |
| + |
| // If |InnerMatcher| matches |top|, then the returned matcher will match: |
| // - |top::function| |
| // - |top::Class::method| |
| @@ -1275,6 +1397,9 @@ int main(int argc, const char* argv[]) { |
| llvm::InitializeNativeTargetAsmParser(); |
| llvm::cl::OptionCategory category( |
| "rewrite_to_chrome_style: convert Blink style to Chrome style."); |
| + llvm::cl::opt<std::string> blocklisted_methods_file( |
| + kMethodBlocklistParamName, llvm::cl::value_desc("filepath"), |
| + llvm::cl::desc("file listing methods to be blocked (not renamed)")); |
| CommonOptionsParser options(argc, argv, category); |
| clang::tooling::ClangTool tool(options.getCompilations(), |
| options.getSourcePathList()); |
| @@ -1426,7 +1551,8 @@ int main(int argc, const char* argv[]) { |
| // we use includeAllOverriddenMethods() to check these rules not just for the |
| // method being matched but for the methods it overrides also. |
| auto is_blink_method = includeAllOverriddenMethods( |
| - allOf(in_blink_namespace, unless(isBlacklistedMethod()))); |
| + allOf(in_blink_namespace, unless(isBlacklistedMethod()), |
| + unless(isBlocklistedMethod(blocklisted_methods_file)))); |
| auto method_decl_matcher = id( |
| "decl", |
| cxxMethodDecl( |