Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // Changes Blink-style names to Chrome-style names. Currently transforms: | 5 // Changes Blink-style names to Chrome-style names. Currently transforms: |
| 6 // fields: | 6 // fields: |
| 7 // int m_operationCount => int operation_count_ | 7 // int m_operationCount => int operation_count_ |
| 8 // variables (including parameters): | 8 // variables (including parameters): |
| 9 // int mySuperVariable => int my_super_variable | 9 // int mySuperVariable => int my_super_variable |
| 10 // constants: | 10 // constants: |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 #include "clang/Frontend/CompilerInstance.h" | 27 #include "clang/Frontend/CompilerInstance.h" |
| 28 #include "clang/Frontend/FrontendActions.h" | 28 #include "clang/Frontend/FrontendActions.h" |
| 29 #include "clang/Lex/MacroArgs.h" | 29 #include "clang/Lex/MacroArgs.h" |
| 30 #include "clang/Lex/Lexer.h" | 30 #include "clang/Lex/Lexer.h" |
| 31 #include "clang/Lex/PPCallbacks.h" | 31 #include "clang/Lex/PPCallbacks.h" |
| 32 #include "clang/Lex/Preprocessor.h" | 32 #include "clang/Lex/Preprocessor.h" |
| 33 #include "clang/Tooling/CommonOptionsParser.h" | 33 #include "clang/Tooling/CommonOptionsParser.h" |
| 34 #include "clang/Tooling/Refactoring.h" | 34 #include "clang/Tooling/Refactoring.h" |
| 35 #include "clang/Tooling/Tooling.h" | 35 #include "clang/Tooling/Tooling.h" |
| 36 #include "llvm/Support/CommandLine.h" | 36 #include "llvm/Support/CommandLine.h" |
| 37 #include "llvm/Support/ErrorOr.h" | |
| 38 #include "llvm/Support/LineIterator.h" | |
| 39 #include "llvm/Support/MemoryBuffer.h" | |
| 37 #include "llvm/Support/TargetSelect.h" | 40 #include "llvm/Support/TargetSelect.h" |
| 38 | 41 |
| 39 #include "EditTracker.h" | 42 #include "EditTracker.h" |
| 40 | 43 |
| 41 using namespace clang::ast_matchers; | 44 using namespace clang::ast_matchers; |
| 42 using clang::tooling::CommonOptionsParser; | 45 using clang::tooling::CommonOptionsParser; |
| 43 using clang::tooling::Replacement; | 46 using clang::tooling::Replacement; |
| 44 using llvm::StringRef; | 47 using llvm::StringRef; |
| 45 | 48 |
| 46 namespace { | 49 namespace { |
| 47 | 50 |
| 48 const char kBlinkFieldPrefix[] = "m_"; | 51 const char kBlinkFieldPrefix[] = "m_"; |
| 49 const char kBlinkStaticMemberPrefix[] = "s_"; | 52 const char kBlinkStaticMemberPrefix[] = "s_"; |
| 50 const char kGeneratedFileRegex[] = "^gen/|/gen/"; | 53 const char kGeneratedFileRegex[] = "^gen/|/gen/"; |
| 51 const char kGMockMethodNamePrefix[] = "gmock_"; | 54 const char kGMockMethodNamePrefix[] = "gmock_"; |
| 55 const char kMethodBlocklistParamName[] = "method-blocklist"; | |
| 52 | 56 |
| 53 template <typename MatcherType, typename NodeType> | 57 template <typename MatcherType, typename NodeType> |
| 54 bool IsMatching(const MatcherType& matcher, | 58 bool IsMatching(const MatcherType& matcher, |
| 55 const NodeType& node, | 59 const NodeType& node, |
| 56 clang::ASTContext& context) { | 60 clang::ASTContext& context) { |
| 57 return !match(matcher, node, context).empty(); | 61 return !match(matcher, node, context).empty(); |
| 58 } | 62 } |
| 59 | 63 |
| 60 const clang::ast_matchers::internal:: | 64 const clang::ast_matchers::internal:: |
| 61 VariadicDynCastAllOfMatcher<clang::Expr, clang::UnresolvedMemberExpr> | 65 VariadicDynCastAllOfMatcher<clang::Expr, clang::UnresolvedMemberExpr> |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 if (potentially_mocked_method->getNumParams() != Node.getNumParams()) | 116 if (potentially_mocked_method->getNumParams() != Node.getNumParams()) |
| 113 continue; | 117 continue; |
| 114 | 118 |
| 115 if (InnerMatcher.matches(*potentially_mocked_method, Finder, Builder)) | 119 if (InnerMatcher.matches(*potentially_mocked_method, Finder, Builder)) |
| 116 return true; | 120 return true; |
| 117 } | 121 } |
| 118 | 122 |
| 119 return false; | 123 return false; |
| 120 } | 124 } |
| 121 | 125 |
| 126 class MethodBlocklist { | |
| 127 public: | |
| 128 explicit MethodBlocklist(const std::string& filepath) { | |
| 129 if (!filepath.empty()) | |
| 130 ParseInputFile(filepath); | |
| 131 } | |
| 132 | |
| 133 bool Contains(const clang::CXXMethodDecl& method) const { | |
| 134 auto it = method_to_class_to_args_.find(method.getName()); | |
| 135 if (it == method_to_class_to_args_.end()) | |
| 136 return false; | |
| 137 | |
| 138 const clang::CXXRecordDecl* actual_class = method.getParent(); | |
| 139 // Hopefully |getParent()| can find the class even for non-inlined method | |
| 140 // definitions (where CXXRecordDecl is not a parent AST node of | |
| 141 // CXXMethodDecl). | |
| 142 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.
| |
| 143 | |
| 144 const llvm::StringMap<std::set<unsigned>>& class_to_args = it->second; | |
| 145 auto it2 = class_to_args.find(actual_class->getName()); | |
| 146 if (it2 == class_to_args.end()) | |
| 147 return false; | |
| 148 | |
| 149 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.
| |
| 150 unsigned max_actual_number_of_args = method.param_size(); | |
| 151 unsigned min_actual_number_of_args = max_actual_number_of_args; | |
| 152 for (const clang::ParmVarDecl* param : method.parameters()) { | |
| 153 if (param->hasInit()) | |
| 154 min_actual_number_of_args--; | |
| 155 } | |
| 156 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.
| |
| 157 set_of_expected_number_of_args.begin(), | |
| 158 set_of_expected_number_of_args.end(), | |
| 159 [min_actual_number_of_args, | |
| 160 max_actual_number_of_args](unsigned expected_number_of_args) { | |
| 161 return (min_actual_number_of_args <= expected_number_of_args) && | |
| 162 (expected_number_of_args <= max_actual_number_of_args); | |
| 163 }); | |
| 164 if (!got_expected_number_of_args) | |
| 165 return false; | |
| 166 | |
| 167 // No need to verify here that |actual_class| is in the |blink| namespace - | |
| 168 // this will be done by other matchers elsewhere. | |
| 169 | |
| 170 // TODO(lukasza): Do we need to consider return type and/or param types? | |
| 171 | |
| 172 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.
| |
| 173 } | |
| 174 | |
| 175 private: | |
| 176 // Each line is expected to have the following format: | |
| 177 // <class name>:::<method name>:::<number of arguments> | |
| 178 void ParseInputFile(const std::string& filepath) { | |
| 179 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file_or_err = | |
| 180 llvm::MemoryBuffer::getFile(filepath); | |
| 181 if (std::error_code err = file_or_err.getError()) { | |
| 182 llvm::errs() << "ERROR: Cannot open the file specified in --" | |
| 183 << kMethodBlocklistParamName << " argument: " << filepath | |
| 184 << ": " << err.message() << "\n"; | |
| 185 assert(false); | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#'); | |
| 190 for (; !it.is_at_eof(); ++it) { | |
| 191 llvm::StringRef line = it->trim(); | |
| 192 if (line.empty()) | |
| 193 continue; | |
| 194 | |
| 195 // Split the line into ':::'-delimited parts. | |
| 196 const size_t kExpectedNumberOfParts = 3; | |
| 197 llvm::SmallVector<llvm::StringRef, kExpectedNumberOfParts> parts; | |
| 198 line.split(parts, ":::"); | |
| 199 if (parts.size() != kExpectedNumberOfParts) { | |
| 200 llvm::errs() << "ERROR: Parsing error - expected " | |
| 201 << kExpectedNumberOfParts | |
| 202 << " ':::'-delimited parts: " << filepath << ":" | |
| 203 << it.line_number() << ": " << line << "\n"; | |
| 204 assert(false); | |
| 205 continue; | |
| 206 } | |
| 207 | |
| 208 // Parse individual parts. | |
| 209 llvm::StringRef class_name = parts[0]; | |
| 210 llvm::StringRef method_name = parts[1]; | |
| 211 unsigned number_of_method_args; | |
| 212 if (parts[2].getAsInteger(0, number_of_method_args)) { | |
| 213 llvm::errs() << "ERROR: Parsing error - '" << parts[2] << "' " | |
| 214 << "is not an unsigned integer: " << filepath << ":" | |
| 215 << it.line_number() << ": " << line << "\n"; | |
| 216 assert(false); | |
| 217 continue; | |
| 218 } | |
| 219 | |
| 220 // Store the new entry. | |
| 221 method_to_class_to_args_[method_name][class_name].insert( | |
| 222 number_of_method_args); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // Stores methods to blacklist in a map: | |
| 227 // method name -> class name -> set of all allowed numbers of arguments. | |
| 228 llvm::StringMap<llvm::StringMap<std::set<unsigned>>> method_to_class_to_args_; | |
| 229 }; | |
| 230 | |
| 231 AST_MATCHER_P(clang::CXXMethodDecl, | |
| 232 internalIsBlocklistedMethod, | |
| 233 MethodBlocklist, | |
| 234 Blocklist) { | |
| 235 return Blocklist.Contains(Node); | |
| 236 } | |
| 237 | |
| 238 clang::ast_matchers::internal::Matcher<clang::CXXMethodDecl> | |
| 239 isBlocklistedMethod(const std::string& method_blocklist_filepath) { | |
| 240 return internalIsBlocklistedMethod( | |
| 241 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'
| |
| 242 } | |
| 243 | |
| 122 // If |InnerMatcher| matches |top|, then the returned matcher will match: | 244 // If |InnerMatcher| matches |top|, then the returned matcher will match: |
| 123 // - |top::function| | 245 // - |top::function| |
| 124 // - |top::Class::method| | 246 // - |top::Class::method| |
| 125 // - |top::internal::Class::method| | 247 // - |top::internal::Class::method| |
| 126 AST_MATCHER_P( | 248 AST_MATCHER_P( |
| 127 clang::NestedNameSpecifier, | 249 clang::NestedNameSpecifier, |
| 128 hasTopLevelPrefix, | 250 hasTopLevelPrefix, |
| 129 clang::ast_matchers::internal::Matcher<clang::NestedNameSpecifier>, | 251 clang::ast_matchers::internal::Matcher<clang::NestedNameSpecifier>, |
| 130 InnerMatcher) { | 252 InnerMatcher) { |
| 131 const clang::NestedNameSpecifier* NodeToMatch = &Node; | 253 const clang::NestedNameSpecifier* NodeToMatch = &Node; |
| (...skipping 1136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1268 | 1390 |
| 1269 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 1391 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
| 1270 | 1392 |
| 1271 int main(int argc, const char* argv[]) { | 1393 int main(int argc, const char* argv[]) { |
| 1272 // TODO(dcheng): Clang tooling should do this itself. | 1394 // TODO(dcheng): Clang tooling should do this itself. |
| 1273 // http://llvm.org/bugs/show_bug.cgi?id=21627 | 1395 // http://llvm.org/bugs/show_bug.cgi?id=21627 |
| 1274 llvm::InitializeNativeTarget(); | 1396 llvm::InitializeNativeTarget(); |
| 1275 llvm::InitializeNativeTargetAsmParser(); | 1397 llvm::InitializeNativeTargetAsmParser(); |
| 1276 llvm::cl::OptionCategory category( | 1398 llvm::cl::OptionCategory category( |
| 1277 "rewrite_to_chrome_style: convert Blink style to Chrome style."); | 1399 "rewrite_to_chrome_style: convert Blink style to Chrome style."); |
| 1400 llvm::cl::opt<std::string> blocklisted_methods_file( | |
| 1401 kMethodBlocklistParamName, llvm::cl::value_desc("filepath"), | |
| 1402 llvm::cl::desc("file listing methods to be blocked (not renamed)")); | |
| 1278 CommonOptionsParser options(argc, argv, category); | 1403 CommonOptionsParser options(argc, argv, category); |
| 1279 clang::tooling::ClangTool tool(options.getCompilations(), | 1404 clang::tooling::ClangTool tool(options.getCompilations(), |
| 1280 options.getSourcePathList()); | 1405 options.getSourcePathList()); |
| 1281 | 1406 |
| 1282 MatchFinder match_finder; | 1407 MatchFinder match_finder; |
| 1283 std::set<Replacement> replacements; | 1408 std::set<Replacement> replacements; |
| 1284 | 1409 |
| 1285 // Blink namespace matchers ======== | 1410 // Blink namespace matchers ======== |
| 1286 auto blink_namespace_decl = | 1411 auto blink_namespace_decl = |
| 1287 namespaceDecl(anyOf(hasName("blink"), hasName("WTF")), | 1412 namespaceDecl(anyOf(hasName("blink"), hasName("WTF")), |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1419 // struct S { | 1544 // struct S { |
| 1420 // void g(); | 1545 // void g(); |
| 1421 // }; | 1546 // }; |
| 1422 // matches |g|. | 1547 // matches |g|. |
| 1423 // For a method to be considered for rewrite, it must not override something | 1548 // For a method to be considered for rewrite, it must not override something |
| 1424 // that we're not rewriting. Any methods that we would not normally consider | 1549 // that we're not rewriting. Any methods that we would not normally consider |
| 1425 // but that override something we are rewriting should also be rewritten. So | 1550 // but that override something we are rewriting should also be rewritten. So |
| 1426 // we use includeAllOverriddenMethods() to check these rules not just for the | 1551 // we use includeAllOverriddenMethods() to check these rules not just for the |
| 1427 // method being matched but for the methods it overrides also. | 1552 // method being matched but for the methods it overrides also. |
| 1428 auto is_blink_method = includeAllOverriddenMethods( | 1553 auto is_blink_method = includeAllOverriddenMethods( |
| 1429 allOf(in_blink_namespace, unless(isBlacklistedMethod()))); | 1554 allOf(in_blink_namespace, unless(isBlacklistedMethod()), |
| 1555 unless(isBlocklistedMethod(blocklisted_methods_file)))); | |
| 1430 auto method_decl_matcher = id( | 1556 auto method_decl_matcher = id( |
| 1431 "decl", | 1557 "decl", |
| 1432 cxxMethodDecl( | 1558 cxxMethodDecl( |
| 1433 unless(anyOf( | 1559 unless(anyOf( |
| 1434 // Overloaded operators have special names and should never be | 1560 // Overloaded operators have special names and should never be |
| 1435 // renamed. | 1561 // renamed. |
| 1436 isOverloadedOperator(), | 1562 isOverloadedOperator(), |
| 1437 // Similarly, constructors, destructors, and conversion | 1563 // Similarly, constructors, destructors, and conversion |
| 1438 // functions should not be considered for renaming. | 1564 // functions should not be considered for renaming. |
| 1439 cxxConstructorDecl(), cxxDestructorDecl(), cxxConversionDecl())), | 1565 cxxConstructorDecl(), cxxDestructorDecl(), cxxConversionDecl())), |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1667 for (const auto& r : replacements) { | 1793 for (const auto& r : replacements) { |
| 1668 std::string replacement_text = r.getReplacementText().str(); | 1794 std::string replacement_text = r.getReplacementText().str(); |
| 1669 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 1795 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
| 1670 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() | 1796 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() |
| 1671 << ":::" << r.getLength() << ":::" << replacement_text << "\n"; | 1797 << ":::" << r.getLength() << ":::" << replacement_text << "\n"; |
| 1672 } | 1798 } |
| 1673 llvm::outs() << "==== END EDITS ====\n"; | 1799 llvm::outs() << "==== END EDITS ====\n"; |
| 1674 | 1800 |
| 1675 return 0; | 1801 return 0; |
| 1676 } | 1802 } |
| OLD | NEW |