1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This is implementation of a clang tool that rewrites raw pointer fields into
6 // CheckedPtr<T>:
7 //     Pointee* field_
8 // becomes:
9 //     CheckedPtr<Pointee> field_
10 //
11 // Note that the tool always emits two kinds of output:
12 // 1. Fields to exclude:
13 //    - FilteredExprWriter
14 // 2. Edit/replacement directives:
15 //    - FieldDeclRewriter
16 //    - AffectedExprRewriter
17 // The rewriter is expected to be used twice, in two passes:
18 // 1. Output from the 1st pass should be used to generate fields-to-ignore.txt
19 //    (or to augment the manually created exclusion list file)
20 // 2. The 2nd pass should use fields-to-ignore.txt from the first pass as input
21 //    for the --exclude-fields cmdline parameter.  The output from the 2nd pass
22 //    can be used to perform the actual rewrite via extract_edits.py and
23 //    apply_edits.py.
24 //
25 // For more details, see the doc here:
26 // https://docs.google.com/document/d/1chTvr3fSofQNV_PDPEHRyUgcJCQBgTDOOBriW9gIm9M
27 
28 #include <assert.h>
29 #include <algorithm>
30 #include <limits>
31 #include <memory>
32 #include <string>
33 #include <vector>
34 
35 #include "clang/AST/ASTContext.h"
36 #include "clang/ASTMatchers/ASTMatchFinder.h"
37 #include "clang/ASTMatchers/ASTMatchers.h"
38 #include "clang/ASTMatchers/ASTMatchersMacros.h"
39 #include "clang/Basic/CharInfo.h"
40 #include "clang/Basic/SourceLocation.h"
41 #include "clang/Basic/SourceManager.h"
42 #include "clang/Frontend/CompilerInstance.h"
43 #include "clang/Frontend/FrontendActions.h"
44 #include "clang/Lex/Lexer.h"
45 #include "clang/Lex/MacroArgs.h"
46 #include "clang/Lex/PPCallbacks.h"
47 #include "clang/Lex/Preprocessor.h"
48 #include "clang/Tooling/CommonOptionsParser.h"
49 #include "clang/Tooling/Refactoring.h"
50 #include "clang/Tooling/Tooling.h"
51 #include "llvm/ADT/StringExtras.h"
52 #include "llvm/Support/CommandLine.h"
53 #include "llvm/Support/ErrorOr.h"
54 #include "llvm/Support/FormatVariadic.h"
55 #include "llvm/Support/LineIterator.h"
56 #include "llvm/Support/MemoryBuffer.h"
57 #include "llvm/Support/Path.h"
58 #include "llvm/Support/TargetSelect.h"
59 
60 using namespace clang::ast_matchers;
61 
62 namespace {
63 
64 // Include path that needs to be added to all the files where CheckedPtr<...>
65 // replaces a raw pointer.
66 const char kIncludePath[] = "base/memory/checked_ptr.h";
67 
68 // Name of a cmdline parameter that can be used to specify a file listing fields
69 // that should not be rewritten to use CheckedPtr<T>.
70 //
71 // See also:
72 // - OutputSectionHelper
73 // - FilterFile
74 const char kExcludeFieldsParamName[] = "exclude-fields";
75 
76 // Name of a cmdline parameter that can be used to specify a file listing
77 // regular expressions describing paths that should be excluded from the
78 // rewrite.
79 //
80 // See also:
81 // - PathFilterFile
82 const char kExcludePathsParamName[] = "exclude-paths";
83 
84 // OutputSectionHelper helps gather and emit a section of output.
85 //
86 // The section of output is delimited in a way that makes it easy to extract it
87 // with sed like so:
88 //    $ DELIM = ...
89 //    $ cat ~/scratch/rewriter.out \
90 //        | sed '/^==== BEGIN $DELIM ====$/,/^==== END $DELIM ====$/{//!b};d' \
91 //        | sort | uniq > ~/scratch/some-out-of-band-output.txt
92 //    (For DELIM="EDITS", there is also tools/clang/scripts/extract_edits.py.)
93 //
94 // Each output line is deduped and may be followed by optional comment tags:
95 //        Some filter # tag1, tag2
96 //        Another filter # tag1, tag2, tag3
97 //        An output line with no comment tags
98 //
99 // The output lines are sorted.  This helps provide deterministic output (even
100 // if AST matchers start firing in a different order after benign clang
101 // changes).
102 //
103 // See also:
104 // - FilterFile
105 // - OutputHelper
106 class OutputSectionHelper {
107  public:
OutputSectionHelper(llvm::StringRef output_delimiter)108   explicit OutputSectionHelper(llvm::StringRef output_delimiter)
109       : output_delimiter_(output_delimiter.str()) {}
110 
111   OutputSectionHelper(const OutputSectionHelper&) = delete;
112   OutputSectionHelper& operator=(const OutputSectionHelper&) = delete;
113 
Add(llvm::StringRef output_line,llvm::StringRef tag="")114   void Add(llvm::StringRef output_line, llvm::StringRef tag = "") {
115     // Look up |tags| associated with |output_line|.  As a side effect of the
116     // lookup, |output_line| will be inserted if it wasn't already present in
117     // the map.
118     llvm::StringSet<>& tags = output_line_to_tags_[output_line];
119 
120     if (!tag.empty())
121       tags.insert(tag);
122   }
123 
Emit()124   void Emit() {
125     if (output_line_to_tags_.empty())
126       return;
127 
128     llvm::outs() << "==== BEGIN " << output_delimiter_ << " ====\n";
129     for (const llvm::StringRef& output_line :
130          GetSortedKeys(output_line_to_tags_)) {
131       llvm::outs() << output_line;
132 
133       const llvm::StringSet<>& tags = output_line_to_tags_[output_line];
134       if (!tags.empty()) {
135         std::vector<llvm::StringRef> sorted_tags = GetSortedKeys(tags);
136         std::string tags_comment =
137             llvm::join(sorted_tags.begin(), sorted_tags.end(), ", ");
138         llvm::outs() << "  # " << tags_comment;
139       }
140 
141       llvm::outs() << "\n";
142     }
143     llvm::outs() << "==== END " << output_delimiter_ << " ====\n";
144   }
145 
146  private:
147   template <typename TValue>
GetSortedKeys(const llvm::StringMap<TValue> & map)148   std::vector<llvm::StringRef> GetSortedKeys(
149       const llvm::StringMap<TValue>& map) {
150     std::vector<llvm::StringRef> sorted(map.keys().begin(), map.keys().end());
151     std::sort(sorted.begin(), sorted.end());
152     return sorted;
153   }
154 
155   std::string output_delimiter_;
156   llvm::StringMap<llvm::StringSet<>> output_line_to_tags_;
157 };
158 
159 // Output format is documented in //docs/clang_tool_refactoring.md
160 class OutputHelper : public clang::tooling::SourceFileCallbacks {
161  public:
OutputHelper()162   OutputHelper()
163       : edits_helper_("EDITS"), field_decl_filter_helper_("FIELD FILTERS") {}
164   ~OutputHelper() = default;
165 
166   OutputHelper(const OutputHelper&) = delete;
167   OutputHelper& operator=(const OutputHelper&) = delete;
168 
AddReplacement(const clang::SourceManager & source_manager,const clang::SourceRange & replacement_range,std::string replacement_text,bool should_add_include=false)169   void AddReplacement(const clang::SourceManager& source_manager,
170                       const clang::SourceRange& replacement_range,
171                       std::string replacement_text,
172                       bool should_add_include = false) {
173     clang::tooling::Replacement replacement(
174         source_manager, clang::CharSourceRange::getCharRange(replacement_range),
175         replacement_text);
176     llvm::StringRef file_path = replacement.getFilePath();
177     if (file_path.empty())
178       return;
179 
180     std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
181     std::string replacement_directive = llvm::formatv(
182         "r:::{0}:::{1}:::{2}:::{3}", file_path, replacement.getOffset(),
183         replacement.getLength(), replacement_text);
184     edits_helper_.Add(replacement_directive);
185 
186     if (should_add_include) {
187       std::string include_directive = llvm::formatv(
188           "include-user-header:::{0}:::-1:::-1:::{1}", file_path, kIncludePath);
189       edits_helper_.Add(include_directive);
190     }
191   }
192 
AddFilteredField(const clang::FieldDecl & field_decl,llvm::StringRef filter_tag)193   void AddFilteredField(const clang::FieldDecl& field_decl,
194                         llvm::StringRef filter_tag) {
195     std::string qualified_name = field_decl.getQualifiedNameAsString();
196     field_decl_filter_helper_.Add(qualified_name, filter_tag);
197   }
198 
199  private:
200   // clang::tooling::SourceFileCallbacks override:
handleBeginSource(clang::CompilerInstance & compiler)201   bool handleBeginSource(clang::CompilerInstance& compiler) override {
202     const clang::FrontendOptions& frontend_options = compiler.getFrontendOpts();
203 
204     assert((frontend_options.Inputs.size() == 1) &&
205            "run_tool.py should invoke the rewriter one file at a time");
206     const clang::FrontendInputFile& input_file = frontend_options.Inputs[0];
207     assert(input_file.isFile() &&
208            "run_tool.py should invoke the rewriter on actual files");
209 
210     current_language_ = input_file.getKind().getLanguage();
211 
212     return true;  // Report that |handleBeginSource| succeeded.
213   }
214 
215   // clang::tooling::SourceFileCallbacks override:
handleEndSource()216   void handleEndSource() override {
217     if (ShouldSuppressOutput())
218       return;
219 
220     edits_helper_.Emit();
221     field_decl_filter_helper_.Emit();
222   }
223 
ShouldSuppressOutput()224   bool ShouldSuppressOutput() {
225     switch (current_language_) {
226       case clang::Language::Unknown:
227       case clang::Language::Asm:
228       case clang::Language::LLVM_IR:
229       case clang::Language::OpenCL:
230       case clang::Language::CUDA:
231       case clang::Language::RenderScript:
232       case clang::Language::HIP:
233         // Rewriter can't handle rewriting the current input language.
234         return true;
235 
236       case clang::Language::C:
237       case clang::Language::ObjC:
238         // CheckedPtr requires C++.  In particular, attempting to #include
239         // "base/memory/checked_ptr.h" from C-only compilation units will lead
240         // to compilation errors.
241         return true;
242 
243       case clang::Language::CXX:
244       case clang::Language::ObjCXX:
245         return false;
246     }
247 
248     assert(false && "Unrecognized clang::Language");
249     return true;
250   }
251 
252   OutputSectionHelper edits_helper_;
253   OutputSectionHelper field_decl_filter_helper_;
254   clang::Language current_language_ = clang::Language::Unknown;
255 };
256 
GetFilePath(const clang::SourceManager & source_manager,const clang::FieldDecl & field_decl)257 llvm::StringRef GetFilePath(const clang::SourceManager& source_manager,
258                             const clang::FieldDecl& field_decl) {
259   clang::SourceLocation loc = field_decl.getSourceRange().getBegin();
260   if (loc.isInvalid() || !loc.isFileID())
261     return llvm::StringRef();
262 
263   clang::FileID file_id = source_manager.getDecomposedLoc(loc).first;
264   const clang::FileEntry* file_entry =
265       source_manager.getFileEntryForID(file_id);
266   if (!file_entry)
267     return llvm::StringRef();
268 
269   return file_entry->getName();
270 }
271 
AST_MATCHER(clang::FieldDecl,isInThirdPartyLocation)272 AST_MATCHER(clang::FieldDecl, isInThirdPartyLocation) {
273   llvm::StringRef file_path =
274       GetFilePath(Finder->getASTContext().getSourceManager(), Node);
275 
276   // Blink is part of the Chromium git repo, even though it contains
277   // "third_party" in its path.
278   if (file_path.contains("third_party/blink/"))
279     return false;
280 
281   // Otherwise, just check if the paths contains the "third_party" substring.
282   // We don't want to rewrite content of such paths even if they are in the main
283   // Chromium git repository.
284   return file_path.contains("third_party");
285 }
286 
AST_MATCHER(clang::FieldDecl,isInGeneratedLocation)287 AST_MATCHER(clang::FieldDecl, isInGeneratedLocation) {
288   llvm::StringRef file_path =
289       GetFilePath(Finder->getASTContext().getSourceManager(), Node);
290 
291   return file_path.startswith("gen/") || file_path.contains("/gen/");
292 }
293 
294 // Represents a filter file specified via cmdline.
295 class FilterFile {
296  public:
FilterFile(const llvm::cl::opt<std::string> & cmdline_param)297   explicit FilterFile(const llvm::cl::opt<std::string>& cmdline_param) {
298     ParseInputFile(cmdline_param);
299   }
300 
301   FilterFile(const FilterFile&) = delete;
302   FilterFile& operator=(const FilterFile&) = delete;
303 
304   // Returns true if any of the filter file lines is exactly equal to |line|.
ContainsLine(llvm::StringRef line) const305   bool ContainsLine(llvm::StringRef line) const {
306     auto it = file_lines_.find(line);
307     return it != file_lines_.end();
308   }
309 
310   // Returns true if any of the filter file lines is a substring of
311   // |string_to_match|.
ContainsSubstringOf(llvm::StringRef string_to_match) const312   bool ContainsSubstringOf(llvm::StringRef string_to_match) const {
313     if (!substring_regex_.hasValue()) {
314       std::vector<std::string> regex_escaped_file_lines;
315       regex_escaped_file_lines.reserve(file_lines_.size());
316       for (const llvm::StringRef& file_line : file_lines_.keys())
317         regex_escaped_file_lines.push_back(llvm::Regex::escape(file_line));
318       std::string substring_regex_pattern =
319           llvm::join(regex_escaped_file_lines.begin(),
320                      regex_escaped_file_lines.end(), "|");
321       substring_regex_.emplace(substring_regex_pattern);
322     }
323 
324     return substring_regex_->match(string_to_match);
325   }
326 
327  private:
328   // Expected file format:
329   // - '#' character starts a comment (which gets ignored).
330   // - Blank or whitespace-only or comment-only lines are ignored.
331   // - Other lines are expected to contain a fully-qualified name of a field
332   //   like:
333   //       autofill::AddressField::address1_ # some comment
334   // - Templates are represented without template arguments, like:
335   //       WTF::HashTable::table_ # some comment
ParseInputFile(const llvm::cl::opt<std::string> & cmdline_param)336   void ParseInputFile(const llvm::cl::opt<std::string>& cmdline_param) {
337     std::string filepath = cmdline_param;
338     if (filepath.empty())
339       return;
340 
341     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file_or_err =
342         llvm::MemoryBuffer::getFile(filepath);
343     if (std::error_code err = file_or_err.getError()) {
344       llvm::errs() << "ERROR: Cannot open the file specified in --"
345                    << cmdline_param.ArgStr << " argument: " << filepath << ": "
346                    << err.message() << "\n";
347       assert(false);
348       return;
349     }
350 
351     llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#');
352     for (; !it.is_at_eof(); ++it) {
353       llvm::StringRef line = *it;
354 
355       // Remove trailing comments.
356       size_t comment_start_pos = line.find('#');
357       if (comment_start_pos != llvm::StringRef::npos)
358         line = line.substr(0, comment_start_pos);
359       line = line.trim();
360 
361       if (line.empty())
362         continue;
363 
364       file_lines_.insert(line);
365     }
366   }
367 
368   // Stores all file lines (after stripping comments and blank lines).
369   llvm::StringSet<> file_lines_;
370 
371   // Lazily-constructed regex that matches strings that contain any of the
372   // |file_lines_|.
373   mutable llvm::Optional<llvm::Regex> substring_regex_;
374 };
375 
AST_MATCHER_P(clang::FieldDecl,isFieldDeclListedInFilterFile,const FilterFile *,Filter)376 AST_MATCHER_P(clang::FieldDecl,
377               isFieldDeclListedInFilterFile,
378               const FilterFile*,
379               Filter) {
380   return Filter->ContainsLine(Node.getQualifiedNameAsString());
381 }
382 
AST_MATCHER_P(clang::FieldDecl,isInLocationListedInFilterFile,const FilterFile *,Filter)383 AST_MATCHER_P(clang::FieldDecl,
384               isInLocationListedInFilterFile,
385               const FilterFile*,
386               Filter) {
387   llvm::StringRef file_path =
388       GetFilePath(Finder->getASTContext().getSourceManager(), Node);
389   return Filter->ContainsSubstringOf(file_path);
390 }
391 
AST_MATCHER(clang::Decl,isInExternCContext)392 AST_MATCHER(clang::Decl, isInExternCContext) {
393   return Node.getLexicalDeclContext()->isExternCContext();
394 }
395 
396 // Given:
397 //   template <typename T, typename T2> class MyTemplate {};  // Node1 and Node4
398 //   template <typename T2> class MyTemplate<int, T2> {};     // Node2
399 //   template <> class MyTemplate<int, char> {};              // Node3
400 //   void foo() {
401 //     // This creates implicit template specialization (Node4) out of the
402 //     // explicit template definition (Node1).
403 //     MyTemplate<bool, double> v;
404 //   }
405 // with the following AST nodes:
406 //   ClassTemplateDecl MyTemplate                                       - Node1
407 //   | |-CXXRecordDecl class MyTemplate definition
408 //   | `-ClassTemplateSpecializationDecl class MyTemplate definition    - Node4
409 //   ClassTemplatePartialSpecializationDecl class MyTemplate definition - Node2
410 //   ClassTemplateSpecializationDecl class MyTemplate definition        - Node3
411 //
412 // Matches AST node 4, but not AST node2 nor node3.
AST_MATCHER(clang::ClassTemplateSpecializationDecl,isImplicitClassTemplateSpecialization)413 AST_MATCHER(clang::ClassTemplateSpecializationDecl,
414             isImplicitClassTemplateSpecialization) {
415   return !Node.isExplicitSpecialization();
416 }
417 
418 // Given:
419 //   template <typename T, typename T2> void foo(T t, T2 t2) {};  // N1 and N4
420 //   template <typename T2> void foo<int, T2>(int t, T2 t) {};    // N2
421 //   template <> void foo<int, char>(int t, char t2) {};          // N3
422 //   void foo() {
423 //     // This creates implicit template specialization (N4) out of the
424 //     // explicit template definition (N1).
425 //     foo<bool, double>(true, 1.23);
426 //   }
427 // with the following AST nodes:
428 //   FunctionTemplateDecl foo
429 //   |-FunctionDecl 0x191da68 foo 'void (T, T2)'         // N1
430 //   `-FunctionDecl 0x194bf08 foo 'void (bool, double)'  // N4
431 //   FunctionTemplateDecl foo
432 //   `-FunctionDecl foo 'void (int, T2)'                 // N2
433 //   FunctionDecl foo 'void (int, char)'                 // N3
434 //
435 // Matches AST node N4, but not AST nodes N1, N2 nor N3.
AST_MATCHER(clang::FunctionDecl,isImplicitFunctionTemplateSpecialization)436 AST_MATCHER(clang::FunctionDecl, isImplicitFunctionTemplateSpecialization) {
437   switch (Node.getTemplateSpecializationKind()) {
438     case clang::TSK_ImplicitInstantiation:
439       return true;
440     case clang::TSK_Undeclared:
441     case clang::TSK_ExplicitSpecialization:
442     case clang::TSK_ExplicitInstantiationDeclaration:
443     case clang::TSK_ExplicitInstantiationDefinition:
444       return false;
445   }
446 }
447 
AST_POLYMORPHIC_MATCHER(isInMacroLocation,AST_POLYMORPHIC_SUPPORTED_TYPES (clang::Decl,clang::Stmt,clang::TypeLoc))448 AST_POLYMORPHIC_MATCHER(isInMacroLocation,
449                         AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl,
450                                                         clang::Stmt,
451                                                         clang::TypeLoc)) {
452   return Node.getBeginLoc().isMacroID();
453 }
454 
455 // If |field_decl| declares a field in an implicit template specialization, then
456 // finds and returns the corresponding FieldDecl from the template definition.
457 // Otherwise, just returns the original |field_decl| argument.
GetExplicitDecl(const clang::FieldDecl * field_decl)458 const clang::FieldDecl* GetExplicitDecl(const clang::FieldDecl* field_decl) {
459   if (field_decl->isAnonymousStructOrUnion())
460     return field_decl;  // Safe fallback - |field_decl| is not a pointer field.
461 
462   const clang::CXXRecordDecl* record_decl =
463       clang::dyn_cast<clang::CXXRecordDecl>(field_decl->getParent());
464   if (!record_decl)
465     return field_decl;  // Non-C++ records are never template instantiations.
466 
467   const clang::CXXRecordDecl* pattern_decl =
468       record_decl->getTemplateInstantiationPattern();
469   if (!pattern_decl)
470     return field_decl;  // |pattern_decl| is not a template instantiation.
471 
472   if (record_decl->getTemplateSpecializationKind() !=
473       clang::TemplateSpecializationKind::TSK_ImplicitInstantiation) {
474     return field_decl;  // |field_decl| was in an *explicit* specialization.
475   }
476 
477   // Find the field decl with the same name in |pattern_decl|.
478   clang::DeclContextLookupResult lookup_result =
479       pattern_decl->lookup(field_decl->getDeclName());
480   assert(!lookup_result.empty());
481   const clang::NamedDecl* found_decl = lookup_result.front();
482   assert(found_decl);
483   field_decl = clang::dyn_cast<clang::FieldDecl>(found_decl);
484   assert(field_decl);
485   return field_decl;
486 }
487 
488 // Given:
489 //   template <typename T>
490 //   class MyTemplate {
491 //     T field;  // This is an explicit field declaration.
492 //   };
493 //   void foo() {
494 //     // This creates implicit template specialization for MyTemplate,
495 //     // including an implicit |field| declaration.
496 //     MyTemplate<int> v;
497 //     v.field = 123;
498 //   }
499 // and
500 //   innerMatcher that will match the explicit |T field| declaration (but not
501 //   necessarily the implicit template declarations),
502 // hasExplicitFieldDecl(innerMatcher) will match both explicit and implicit
503 // field declarations.
504 //
505 // For example, |member_expr_matcher| below will match |v.field| in the example
506 // above, even though the type of |v.field| is |int|, rather than |T| (matched
507 // by substTemplateTypeParmType()):
508 //   auto explicit_field_decl_matcher =
509 //       fieldDecl(hasType(substTemplateTypeParmType()));
510 //   auto member_expr_matcher = memberExpr(member(fieldDecl(
511 //       hasExplicitFieldDecl(explicit_field_decl_matcher))))
AST_MATCHER_P(clang::FieldDecl,hasExplicitFieldDecl,clang::ast_matchers::internal::Matcher<clang::FieldDecl>,InnerMatcher)512 AST_MATCHER_P(clang::FieldDecl,
513               hasExplicitFieldDecl,
514               clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
515               InnerMatcher) {
516   const clang::FieldDecl* explicit_field_decl = GetExplicitDecl(&Node);
517   return InnerMatcher.matches(*explicit_field_decl, Finder, Builder);
518 }
519 
520 // If |original_param| declares a parameter in an implicit template
521 // specialization of a function or method, then finds and returns the
522 // corresponding ParmVarDecl from the template definition.  Otherwise, just
523 // returns the |original_param| argument.
524 //
525 // Note: nullptr may be returned in rare, unimplemented cases.
GetExplicitDecl(const clang::ParmVarDecl * original_param)526 const clang::ParmVarDecl* GetExplicitDecl(
527     const clang::ParmVarDecl* original_param) {
528   const clang::FunctionDecl* original_func =
529       clang::dyn_cast<clang::FunctionDecl>(original_param->getDeclContext());
530   if (!original_func) {
531     // |!original_func| may happen when the ParmVarDecl is part of a
532     // FunctionType, but not part of a FunctionDecl:
533     //     base::Callback<void(int parm_var_decl_here)>
534     //
535     // In theory, |parm_var_decl_here| can also represent an implicit template
536     // specialization in this scenario.  OTOH, it should be rare + shouldn't
537     // matter for this rewriter, so for now let's just return the
538     // |original_param|.
539     //
540     // TODO: Implement support for this scenario.
541     return nullptr;
542   }
543 
544   const clang::FunctionDecl* pattern_func =
545       original_func->getTemplateInstantiationPattern();
546   if (!pattern_func) {
547     // |original_func| is not a template instantiation - return the
548     // |original_param|.
549     return original_param;
550   }
551 
552   // See if |pattern_func| has a parameter that is a template parameter pack.
553   bool has_param_pack = false;
554   unsigned int index_of_param_pack = std::numeric_limits<unsigned int>::max();
555   for (unsigned int i = 0; i < pattern_func->getNumParams(); i++) {
556     const clang::ParmVarDecl* pattern_param = pattern_func->getParamDecl(i);
557     if (!pattern_param->isParameterPack())
558       continue;
559 
560     if (has_param_pack) {
561       // TODO: Implement support for multiple parameter packs.
562       return nullptr;
563     }
564 
565     has_param_pack = true;
566     index_of_param_pack = i;
567   }
568 
569   // Find and return the corresponding ParmVarDecl from |pattern_func|.
570   unsigned int original_index = original_param->getFunctionScopeIndex();
571   unsigned int pattern_index = std::numeric_limits<unsigned int>::max();
572   if (!has_param_pack) {
573     pattern_index = original_index;
574   } else {
575     // |original_func| has parameters that look like this:
576     //     l1, l2, l3, p1, p2, p3, t1, t2, t3
577     // where
578     //     lN is a leading, non-pack parameter
579     //     pN is an expansion of a template parameter pack
580     //     tN is a trailing, non-pack parameter
581     // Using the knowledge above, let's adjust |pattern_index| as needed.
582     unsigned int leading_param_num = index_of_param_pack;  // How many |lN|.
583     unsigned int pack_expansion_num =  // How many |pN| above.
584         original_func->getNumParams() - pattern_func->getNumParams() + 1;
585     if (original_index < leading_param_num) {
586       // |original_param| is a leading, non-pack parameter.
587       pattern_index = original_index;
588     } else if (leading_param_num <= original_index &&
589                original_index < (leading_param_num + pack_expansion_num)) {
590       // |original_param| is an expansion of a template pack parameter.
591       pattern_index = index_of_param_pack;
592     } else if ((leading_param_num + pack_expansion_num) <= original_index) {
593       // |original_param| is a trailing, non-pack parameter.
594       pattern_index = original_index - pack_expansion_num + 1;
595     }
596   }
597   assert(pattern_index < pattern_func->getNumParams());
598   return pattern_func->getParamDecl(pattern_index);
599 }
600 
AST_MATCHER_P(clang::ParmVarDecl,hasExplicitParmVarDecl,clang::ast_matchers::internal::Matcher<clang::ParmVarDecl>,InnerMatcher)601 AST_MATCHER_P(clang::ParmVarDecl,
602               hasExplicitParmVarDecl,
603               clang::ast_matchers::internal::Matcher<clang::ParmVarDecl>,
604               InnerMatcher) {
605   const clang::ParmVarDecl* explicit_param = GetExplicitDecl(&Node);
606   if (!explicit_param) {
607     // Rare, unimplemented case - fall back to returning "no match".
608     return false;
609   }
610 
611   return InnerMatcher.matches(*explicit_param, Finder, Builder);
612 }
613 
614 // Returns |true| if and only if:
615 // 1. |a| and |b| are in the same file (e.g. |false| is returned if any location
616 //    is within macro scratch space or a similar location;  similarly |false| is
617 //    returned if |a| and |b| are in different files).
618 // 2. |a| and |b| overlap.
IsOverlapping(const clang::SourceManager & source_manager,const clang::SourceRange & a,const clang::SourceRange & b)619 bool IsOverlapping(const clang::SourceManager& source_manager,
620                    const clang::SourceRange& a,
621                    const clang::SourceRange& b) {
622   clang::FullSourceLoc a1(a.getBegin(), source_manager);
623   clang::FullSourceLoc a2(a.getEnd(), source_manager);
624   clang::FullSourceLoc b1(b.getBegin(), source_manager);
625   clang::FullSourceLoc b2(b.getEnd(), source_manager);
626 
627   // Are all locations in a file?
628   if (!a1.isFileID() || !a2.isFileID() || !b1.isFileID() || !b2.isFileID())
629     return false;
630 
631   // Are all locations in the same file?
632   if (a1.getFileID() != a2.getFileID() || a2.getFileID() != b1.getFileID() ||
633       b1.getFileID() != b2.getFileID()) {
634     return false;
635   }
636 
637   // Check the 2 cases below:
638   // 1. A: |============|
639   //    B:      |===============|
640   //       a1   b1      a2      b2
641   // or
642   // 2. A: |====================|
643   //    B:      |=======|
644   //       a1   b1      b2      a2
645   bool b1_is_inside_a_range = a1.getFileOffset() <= b1.getFileOffset() &&
646                               b1.getFileOffset() <= a2.getFileOffset();
647 
648   // Check the 2 cases below:
649   // 1. B: |============|
650   //    A:      |===============|
651   //       b1   a1      b2      a2
652   // or
653   // 2. B: |====================|
654   //    A:      |=======|
655   //       b1   a1      a2      b2
656   bool a1_is_inside_b_range = b1.getFileOffset() <= a1.getFileOffset() &&
657                               a1.getFileOffset() <= b2.getFileOffset();
658 
659   return b1_is_inside_a_range || a1_is_inside_b_range;
660 }
661 
662 // Matcher for FieldDecl that has a SourceRange that overlaps other declarations
663 // within the parent RecordDecl.
664 //
665 // Given
666 //   struct MyStruct {
667 //     int f;
668 //     int f2, f3;
669 //     struct S { int x } f4;
670 //   };
671 // - doesn't match |f|
672 // - matches |f2| and |f3| (which overlap each other's location)
673 // - matches |f4| (which overlaps the location of |S|)
AST_MATCHER(clang::FieldDecl,overlapsOtherDeclsWithinRecordDecl)674 AST_MATCHER(clang::FieldDecl, overlapsOtherDeclsWithinRecordDecl) {
675   const clang::FieldDecl& self = Node;
676   const clang::SourceManager& source_manager =
677       Finder->getASTContext().getSourceManager();
678 
679   const clang::RecordDecl* record_decl = self.getParent();
680   clang::SourceRange self_range(self.getBeginLoc(), self.getEndLoc());
681 
682   auto is_overlapping_sibling = [&](const clang::Decl* other_decl) {
683     if (other_decl == &self)
684       return false;
685 
686     clang::SourceRange other_range(other_decl->getBeginLoc(),
687                                    other_decl->getEndLoc());
688     return IsOverlapping(source_manager, self_range, other_range);
689   };
690   bool has_sibling_with_overlapping_location =
691       std::any_of(record_decl->decls_begin(), record_decl->decls_end(),
692                   is_overlapping_sibling);
693   return has_sibling_with_overlapping_location;
694 }
695 
696 // Matches clang::Type if
697 // 1) it represents a RecordDecl with a FieldDecl that matches the InnerMatcher
698 //    (*all* such FieldDecls will be matched)
699 // or
700 // 2) it represents an array or a RecordDecl that nests the case #1
701 //    (this recurses to any depth).
AST_MATCHER_P(clang::QualType,typeWithEmbeddedFieldDecl,clang::ast_matchers::internal::Matcher<clang::FieldDecl>,InnerMatcher)702 AST_MATCHER_P(clang::QualType,
703               typeWithEmbeddedFieldDecl,
704               clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
705               InnerMatcher) {
706   const clang::Type* type =
707       Node.getDesugaredType(Finder->getASTContext()).getTypePtrOrNull();
708   if (!type)
709     return false;
710 
711   if (const clang::CXXRecordDecl* record_decl = type->getAsCXXRecordDecl()) {
712     auto matcher = recordDecl(forEach(fieldDecl(hasExplicitFieldDecl(anyOf(
713         InnerMatcher, hasType(typeWithEmbeddedFieldDecl(InnerMatcher)))))));
714     return matcher.matches(*record_decl, Finder, Builder);
715   }
716 
717   if (type->isArrayType()) {
718     const clang::ArrayType* array_type =
719         Finder->getASTContext().getAsArrayType(Node);
720     auto matcher = typeWithEmbeddedFieldDecl(InnerMatcher);
721     return matcher.matches(array_type->getElementType(), Finder, Builder);
722   }
723 
724   return false;
725 }
726 
727 // forEachInitExprWithFieldDecl matches InitListExpr if it
728 // 1) evaluates to a RecordType
729 // 2) has a InitListExpr + FieldDecl pair that matches the submatcher args.
730 //
731 // forEachInitExprWithFieldDecl is based on and very similar to the builtin
732 // forEachArgumentWithParam matcher.
AST_MATCHER_P2(clang::InitListExpr,forEachInitExprWithFieldDecl,clang::ast_matchers::internal::Matcher<clang::Expr>,init_expr_matcher,clang::ast_matchers::internal::Matcher<clang::FieldDecl>,field_decl_matcher)733 AST_MATCHER_P2(clang::InitListExpr,
734                forEachInitExprWithFieldDecl,
735                clang::ast_matchers::internal::Matcher<clang::Expr>,
736                init_expr_matcher,
737                clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
738                field_decl_matcher) {
739   const clang::InitListExpr& init_list_expr = Node;
740   const clang::Type* type = init_list_expr.getType()
741                                 .getDesugaredType(Finder->getASTContext())
742                                 .getTypePtrOrNull();
743   if (!type)
744     return false;
745   const clang::CXXRecordDecl* record_decl = type->getAsCXXRecordDecl();
746   if (!record_decl)
747     return false;
748 
749   bool is_matching = false;
750   clang::ast_matchers::internal::BoundNodesTreeBuilder result;
751   const std::vector<const clang::FieldDecl*> field_decls(
752       record_decl->field_begin(), record_decl->field_end());
753   for (unsigned i = 0; i < init_list_expr.getNumInits(); i++) {
754     const clang::Expr* expr = init_list_expr.getInit(i);
755 
756     const clang::FieldDecl* field_decl = nullptr;
757     if (const clang::ImplicitValueInitExpr* implicit_value_init_expr =
758             clang::dyn_cast<clang::ImplicitValueInitExpr>(expr)) {
759       continue;  // Do not match implicit value initializers.
760     } else if (const clang::DesignatedInitExpr* designated_init_expr =
761                    clang::dyn_cast<clang::DesignatedInitExpr>(expr)) {
762       // Nested designators are unsupported by C++.
763       if (designated_init_expr->size() != 1)
764         break;
765       expr = designated_init_expr->getInit();
766       field_decl = designated_init_expr->getDesignator(0)->getField();
767     } else {
768       if (i >= field_decls.size())
769         break;
770       field_decl = field_decls[i];
771     }
772 
773     clang::ast_matchers::internal::BoundNodesTreeBuilder field_matches(
774         *Builder);
775     if (field_decl_matcher.matches(*field_decl, Finder, &field_matches)) {
776       clang::ast_matchers::internal::BoundNodesTreeBuilder expr_matches(
777           field_matches);
778       if (init_expr_matcher.matches(*expr, Finder, &expr_matches)) {
779         result.addMatch(expr_matches);
780         is_matching = true;
781       }
782     }
783   }
784 
785   *Builder = std::move(result);
786   return is_matching;
787 }
788 
789 // Rewrites |SomeClass* field| (matched as "affectedFieldDecl") into
790 // |CheckedPtr<SomeClass> field| and for each file rewritten in such way adds an
791 // |#include "base/memory/checked_ptr.h"|.
792 class FieldDeclRewriter : public MatchFinder::MatchCallback {
793  public:
FieldDeclRewriter(OutputHelper * output_helper)794   explicit FieldDeclRewriter(OutputHelper* output_helper)
795       : output_helper_(output_helper) {}
796 
797   FieldDeclRewriter(const FieldDeclRewriter&) = delete;
798   FieldDeclRewriter& operator=(const FieldDeclRewriter&) = delete;
799 
run(const MatchFinder::MatchResult & result)800   void run(const MatchFinder::MatchResult& result) override {
801     const clang::ASTContext& ast_context = *result.Context;
802     const clang::SourceManager& source_manager = *result.SourceManager;
803 
804     const clang::FieldDecl* field_decl =
805         result.Nodes.getNodeAs<clang::FieldDecl>("affectedFieldDecl");
806     assert(field_decl && "matcher should bind 'fieldDecl'");
807 
808     const clang::TypeSourceInfo* type_source_info =
809         field_decl->getTypeSourceInfo();
810     assert(type_source_info && "assuming |type_source_info| is always present");
811 
812     clang::QualType pointer_type = type_source_info->getType();
813     assert(type_source_info->getType()->isPointerType() &&
814            "matcher should only match pointer types");
815 
816     // Calculate the |replacement_range|.
817     //
818     // Consider the following example:
819     //      const Pointee* const field_name_;
820     //      ^--------------------^  = |replacement_range|
821     //                           ^  = |field_decl->getLocation()|
822     //      ^                       = |field_decl->getBeginLoc()|
823     //                   ^          = PointerTypeLoc::getStarLoc
824     //            ^------^          = TypeLoc::getSourceRange
825     //
826     // We get the |replacement_range| in a bit clumsy way, because clang docs
827     // for QualifiedTypeLoc explicitly say that these objects "intentionally do
828     // not provide source location for type qualifiers".
829     clang::SourceRange replacement_range(field_decl->getBeginLoc(),
830                                          field_decl->getLocation());
831 
832     // Calculate |replacement_text|.
833     std::string replacement_text = GenerateNewText(ast_context, pointer_type);
834     if (field_decl->isMutable())
835       replacement_text.insert(0, "mutable ");
836 
837     // Generate and print a replacement.
838     output_helper_->AddReplacement(source_manager, replacement_range,
839                                    replacement_text,
840                                    true /* should_add_include */);
841   }
842 
843  private:
GenerateNewText(const clang::ASTContext & ast_context,const clang::QualType & pointer_type)844   std::string GenerateNewText(const clang::ASTContext& ast_context,
845                               const clang::QualType& pointer_type) {
846     std::string result;
847 
848     assert(pointer_type->isPointerType() && "caller must pass a pointer type!");
849     clang::QualType pointee_type = pointer_type->getPointeeType();
850 
851     // Preserve qualifiers.
852     assert(!pointer_type.isRestrictQualified() &&
853            "|restrict| is a C-only qualifier and CheckedPtr<T> needs C++");
854     if (pointer_type.isConstQualified())
855       result += "const ";
856     if (pointer_type.isVolatileQualified())
857       result += "volatile ";
858 
859     // Convert pointee type to string.
860     clang::PrintingPolicy printing_policy(ast_context.getLangOpts());
861     printing_policy.SuppressScope = 1;  // s/blink::Pointee/Pointee/
862     std::string pointee_type_as_string =
863         pointee_type.getAsString(printing_policy);
864     result += llvm::formatv("CheckedPtr<{0}> ", pointee_type_as_string);
865 
866     return result;
867   }
868 
869   OutputHelper* const output_helper_;
870 };
871 
872 // Rewrites |my_struct.ptr_field| (matched as "affectedMemberExpr") into
873 // |my_struct.ptr_field.get()|.
874 class AffectedExprRewriter : public MatchFinder::MatchCallback {
875  public:
AffectedExprRewriter(OutputHelper * output_helper)876   explicit AffectedExprRewriter(OutputHelper* output_helper)
877       : output_helper_(output_helper) {}
878 
879   AffectedExprRewriter(const AffectedExprRewriter&) = delete;
880   AffectedExprRewriter& operator=(const AffectedExprRewriter&) = delete;
881 
run(const MatchFinder::MatchResult & result)882   void run(const MatchFinder::MatchResult& result) override {
883     const clang::SourceManager& source_manager = *result.SourceManager;
884 
885     const clang::MemberExpr* member_expr =
886         result.Nodes.getNodeAs<clang::MemberExpr>("affectedMemberExpr");
887     assert(member_expr && "matcher should bind 'affectedMemberExpr'");
888 
889     clang::SourceLocation member_name_start = member_expr->getMemberLoc();
890     size_t member_name_length = member_expr->getMemberDecl()->getName().size();
891     clang::SourceLocation insertion_loc =
892         member_name_start.getLocWithOffset(member_name_length);
893 
894     clang::SourceRange replacement_range(insertion_loc, insertion_loc);
895 
896     output_helper_->AddReplacement(source_manager, replacement_range, ".get()");
897   }
898 
899  private:
900   OutputHelper* const output_helper_;
901 };
902 
903 // Emits problematic fields (matched as "affectedFieldDecl") as filtered fields.
904 class FilteredExprWriter : public MatchFinder::MatchCallback {
905  public:
FilteredExprWriter(OutputHelper * output_helper,llvm::StringRef filter_tag)906   FilteredExprWriter(OutputHelper* output_helper, llvm::StringRef filter_tag)
907       : output_helper_(output_helper), filter_tag_(filter_tag) {}
908 
909   FilteredExprWriter(const FilteredExprWriter&) = delete;
910   FilteredExprWriter& operator=(const FilteredExprWriter&) = delete;
911 
run(const MatchFinder::MatchResult & result)912   void run(const MatchFinder::MatchResult& result) override {
913     const clang::FieldDecl* field_decl =
914         result.Nodes.getNodeAs<clang::FieldDecl>("affectedFieldDecl");
915     assert(field_decl && "matcher should bind 'affectedFieldDecl'");
916 
917     output_helper_->AddFilteredField(*field_decl, filter_tag_);
918   }
919 
920  private:
921   OutputHelper* const output_helper_;
922   llvm::StringRef filter_tag_;
923 };
924 
925 }  // namespace
926 
main(int argc,const char * argv[])927 int main(int argc, const char* argv[]) {
928   // TODO(dcheng): Clang tooling should do this itself.
929   // http://llvm.org/bugs/show_bug.cgi?id=21627
930   llvm::InitializeNativeTarget();
931   llvm::InitializeNativeTargetAsmParser();
932   llvm::cl::OptionCategory category(
933       "rewrite_raw_ptr_fields: changes |T* field_| to |CheckedPtr<T> field_|.");
934   llvm::cl::opt<std::string> exclude_fields_param(
935       kExcludeFieldsParamName, llvm::cl::value_desc("filepath"),
936       llvm::cl::desc("file listing fields to be blocked (not rewritten)"));
937   llvm::cl::opt<std::string> exclude_paths_param(
938       kExcludePathsParamName, llvm::cl::value_desc("filepath"),
939       llvm::cl::desc("file listing paths to be blocked (not rewritten)"));
940   clang::tooling::CommonOptionsParser options(argc, argv, category);
941   clang::tooling::ClangTool tool(options.getCompilations(),
942                                  options.getSourcePathList());
943 
944   MatchFinder match_finder;
945   OutputHelper output_helper;
946 
947   // Supported pointer types =========
948   // Given
949   //   struct MyStrict {
950   //     int* int_ptr;
951   //     int i;
952   //     int (*func_ptr)();
953   //     int (MyStruct::* member_func_ptr)(char);
954   //     int (*ptr_to_array_of_ints)[123]
955   //   };
956   // matches |int*|, but not the other types.
957   auto supported_pointer_types_matcher =
958       pointerType(unless(pointee(hasUnqualifiedDesugaredType(
959           anyOf(functionType(), memberPointerType(), arrayType())))));
960 
961   // Implicit field declarations =========
962   // Matches field declarations that do not explicitly appear in the source
963   // code:
964   // 1. fields of classes generated by the compiler to back capturing lambdas,
965   // 2. fields within an implicit class or function template specialization
966   //    (e.g. when a template is instantiated by a bit of code and there's no
967   //    explicit specialization for it).
968   auto implicit_class_specialization_matcher =
969       classTemplateSpecializationDecl(isImplicitClassTemplateSpecialization());
970   auto implicit_function_specialization_matcher =
971       functionDecl(isImplicitFunctionTemplateSpecialization());
972   auto implicit_field_decl_matcher = fieldDecl(hasParent(cxxRecordDecl(anyOf(
973       isLambda(), implicit_class_specialization_matcher,
974       hasAncestor(decl(anyOf(implicit_class_specialization_matcher,
975                              implicit_function_specialization_matcher)))))));
976 
977   // Field declarations =========
978   // Given
979   //   struct S {
980   //     int* y;
981   //   };
982   // matches |int* y|.  Doesn't match:
983   // - non-pointer types
984   // - fields of lambda-supporting classes
985   // - fields listed in the --exclude-fields cmdline param or located in paths
986   //   matched by --exclude-paths cmdline param
987   // - "implicit" fields (i.e. field decls that are not explicitly present in
988   //   the source code)
989   FilterFile fields_to_exclude(exclude_fields_param);
990   FilterFile paths_to_exclude(exclude_paths_param);
991   auto field_decl_matcher =
992       fieldDecl(
993           allOf(hasType(supported_pointer_types_matcher),
994                 unless(anyOf(isExpansionInSystemHeader(), isInExternCContext(),
995                              isInThirdPartyLocation(), isInGeneratedLocation(),
996                              isInLocationListedInFilterFile(&paths_to_exclude),
997                              isFieldDeclListedInFilterFile(&fields_to_exclude),
998                              implicit_field_decl_matcher))))
999           .bind("affectedFieldDecl");
1000   FieldDeclRewriter field_decl_rewriter(&output_helper);
1001   match_finder.addMatcher(field_decl_matcher, &field_decl_rewriter);
1002 
1003   // Matches expressions that used to return a value of type |SomeClass*|
1004   // but after the rewrite return an instance of |CheckedPtr<SomeClass>|.
1005   // Many such expressions might need additional changes after the rewrite:
1006   // - Some expressions (printf args, const_cast args, etc.) might need |.get()|
1007   //   appended.
1008   // - Using such expressions in specific contexts (e.g. as in-out arguments or
1009   //   as a return value of a function returning references) may require
1010   //   additional work and should cause related fields to be emitted as
1011   //   candidates for the --field-filter-file parameter.
1012   auto affected_member_expr_matcher =
1013       memberExpr(member(fieldDecl(hasExplicitFieldDecl(field_decl_matcher))))
1014           .bind("affectedMemberExpr");
1015   auto affected_expr_matcher = ignoringImplicit(affected_member_expr_matcher);
1016 
1017   // Places where |.get()| needs to be appended =========
1018   // Given
1019   //   void foo(const S& s) {
1020   //     printf("%p", s.y);
1021   //     const_cast<...>(s.y)
1022   //     reinterpret_cast<...>(s.y)
1023   //   }
1024   // matches the |s.y| expr if it matches the |affected_expr_matcher| above.
1025   //
1026   // See also testcases in tests/affected-expr-original.cc
1027   auto affected_expr_that_needs_fixing_matcher = expr(allOf(
1028       affected_expr_matcher,
1029       hasParent(expr(anyOf(callExpr(callee(functionDecl(isVariadic()))),
1030                            cxxConstCastExpr(), cxxReinterpretCastExpr())))));
1031   AffectedExprRewriter affected_expr_rewriter(&output_helper);
1032   match_finder.addMatcher(affected_expr_that_needs_fixing_matcher,
1033                           &affected_expr_rewriter);
1034 
1035   // Affected ternary operator args =========
1036   // Given
1037   //   void foo(const S& s) {
1038   //     cond ? s.y : ...
1039   //   }
1040   // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1041   //
1042   // See also testcases in tests/affected-expr-original.cc
1043   auto affected_ternary_operator_arg_matcher =
1044       conditionalOperator(eachOf(hasTrueExpression(affected_expr_matcher),
1045                                  hasFalseExpression(affected_expr_matcher)));
1046   match_finder.addMatcher(affected_ternary_operator_arg_matcher,
1047                           &affected_expr_rewriter);
1048 
1049   // Calls to templated functions =========
1050   // Given
1051   //   struct S { int* y; };
1052   //   template <typename T>
1053   //   void templatedFunc(T* arg) {}
1054   //   void foo(const S& s) {
1055   //     templatedFunc(s.y);
1056   //   }
1057   // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1058   //
1059   // See also testcases in tests/affected-expr-original.cc
1060   auto templated_function_arg_matcher = forEachArgumentWithParam(
1061       affected_expr_matcher, parmVarDecl(hasType(qualType(allOf(
1062                                  findAll(qualType(substTemplateTypeParmType())),
1063                                  unless(referenceType()))))));
1064   match_finder.addMatcher(callExpr(templated_function_arg_matcher),
1065                           &affected_expr_rewriter);
1066   // TODO(lukasza): It is unclear why |traverse| below is needed.  Maybe it can
1067   // be removed if https://bugs.llvm.org/show_bug.cgi?id=46287 is fixed.
1068   match_finder.addMatcher(
1069       traverse(clang::ast_type_traits::TK_AsIs,
1070                cxxConstructExpr(templated_function_arg_matcher)),
1071       &affected_expr_rewriter);
1072 
1073   // Calls to constructors via an implicit cast =========
1074   // Given
1075   //   struct I { I(int*) {} };
1076   //   void bar(I i) {}
1077   //   struct S { int* y; };
1078   //   void foo(const S& s) {
1079   //     bar(s.y);  // implicit cast from |s.y| to I.
1080   //   }
1081   // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1082   //
1083   // See also testcases in tests/affected-expr-original.cc
1084   auto implicit_ctor_expr_matcher = implicitCastExpr(has(cxxConstructExpr(allOf(
1085       hasDeclaration(
1086           cxxConstructorDecl(allOf(parameterCountIs(1), unless(isExplicit())))),
1087       forEachArgumentWithParam(affected_expr_matcher, parmVarDecl())))));
1088   match_finder.addMatcher(implicit_ctor_expr_matcher, &affected_expr_rewriter);
1089 
1090   // |auto| type declarations =========
1091   // Given
1092   //   struct S { int* y; };
1093   //   void foo(const S& s) {
1094   //     auto* p = s.y;
1095   //   }
1096   // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1097   //
1098   // See also testcases in tests/affected-expr-original.cc
1099   auto auto_var_decl_matcher = declStmt(forEach(
1100       varDecl(allOf(hasType(pointerType(pointee(autoType()))),
1101                     hasInitializer(anyOf(
1102                         affected_expr_matcher,
1103                         initListExpr(hasInit(0, affected_expr_matcher))))))));
1104   match_finder.addMatcher(auto_var_decl_matcher, &affected_expr_rewriter);
1105 
1106   // address-of(affected-expr) =========
1107   // Given
1108   //   ... &s.y ...
1109   // matches the |s.y| expr if it matches the |affected_member_expr_matcher|
1110   // above.
1111   //
1112   // See also the testcases in tests/gen-in-out-arg-test.cc.
1113   auto affected_addr_of_expr_matcher = expr(allOf(
1114       affected_expr_matcher, hasParent(unaryOperator(hasOperatorName("&")))));
1115   FilteredExprWriter filtered_addr_of_expr_writer(&output_helper, "addr-of");
1116   match_finder.addMatcher(affected_addr_of_expr_matcher,
1117                           &filtered_addr_of_expr_writer);
1118 
1119   // in-out reference arg =========
1120   // Given
1121   //   struct S { SomeClass* ptr_field; };
1122   //   void f(SomeClass*& in_out_arg) { ... }
1123   //   template <typename T> void f2(T&& rvalue_ref_arg) { ... }
1124   //   template <typename... Ts> void f3(Ts&&... rvalue_ref_args) { ... }
1125   //   void bar() {
1126   //     S s;
1127   //     foo(s.ptr_field)
1128   //   }
1129   // matches the |s.ptr_field| expr if it matches the
1130   // |affected_member_expr_matcher| and is passed as a function argument that
1131   // has |FooBar*&| type (like |f|, but unlike |f2| and |f3|).
1132   //
1133   // See also the testcases in tests/gen-in-out-arg-test.cc.
1134   auto affected_in_out_ref_arg_matcher = callExpr(forEachArgumentWithParam(
1135       affected_expr_matcher, hasExplicitParmVarDecl(hasType(qualType(
1136                                  allOf(referenceType(pointee(pointerType())),
1137                                        unless(rValueReferenceType())))))));
1138   FilteredExprWriter filtered_in_out_ref_arg_writer(&output_helper,
1139                                                     "in-out-param-ref");
1140   match_finder.addMatcher(affected_in_out_ref_arg_matcher,
1141                           &filtered_in_out_ref_arg_writer);
1142 
1143   // See the doc comment for the overlapsOtherDeclsWithinRecordDecl matcher
1144   // and the testcases in tests/gen-overlaps-test.cc.
1145   auto overlapping_field_decl_matcher = fieldDecl(
1146       allOf(field_decl_matcher, overlapsOtherDeclsWithinRecordDecl()));
1147   FilteredExprWriter overlapping_field_decl_writer(&output_helper,
1148                                                    "overlapping");
1149   match_finder.addMatcher(overlapping_field_decl_matcher,
1150                           &overlapping_field_decl_writer);
1151 
1152   // Matches fields initialized with a non-nullptr value in a constexpr
1153   // constructor.  See also the testcase in tests/gen-constexpr-test.cc.
1154   auto non_nullptr_expr_matcher =
1155       expr(unless(ignoringImplicit(cxxNullPtrLiteralExpr())));
1156   auto constexpr_ctor_field_initializer_matcher = cxxConstructorDecl(
1157       allOf(isConstexpr(), forEachConstructorInitializer(allOf(
1158                                forField(field_decl_matcher),
1159                                withInitializer(non_nullptr_expr_matcher)))));
1160   FilteredExprWriter constexpr_ctor_field_initializer_writer(
1161       &output_helper, "constexpr-ctor-field-initializer");
1162   match_finder.addMatcher(constexpr_ctor_field_initializer_matcher,
1163                           &constexpr_ctor_field_initializer_writer);
1164 
1165   // Matches constexpr initializer list expressions that initialize a rewritable
1166   // field with a non-nullptr value.  For more details and rationale see the
1167   // testcases in tests/gen-constexpr-test.cc.
1168   auto constexpr_var_initializer_matcher = varDecl(
1169       allOf(isConstexpr(),
1170             hasInitializer(findAll(initListExpr(forEachInitExprWithFieldDecl(
1171                 non_nullptr_expr_matcher,
1172                 hasExplicitFieldDecl(field_decl_matcher)))))));
1173   FilteredExprWriter constexpr_var_initializer_writer(
1174       &output_helper, "constexpr-var-initializer");
1175   match_finder.addMatcher(constexpr_var_initializer_matcher,
1176                           &constexpr_var_initializer_writer);
1177 
1178   // See the doc comment for the isInMacroLocation matcher
1179   // and the testcases in tests/gen-macro-test.cc.
1180   auto macro_field_decl_matcher =
1181       fieldDecl(allOf(field_decl_matcher, isInMacroLocation()));
1182   FilteredExprWriter macro_field_decl_writer(&output_helper, "macro");
1183   match_finder.addMatcher(macro_field_decl_matcher, &macro_field_decl_writer);
1184 
1185   // See the testcases in tests/gen-global-destructor-test.cc.
1186   auto global_destructor_matcher =
1187       varDecl(allOf(hasGlobalStorage(),
1188                     hasType(typeWithEmbeddedFieldDecl(field_decl_matcher))));
1189   FilteredExprWriter global_destructor_writer(&output_helper, "global-scope");
1190   match_finder.addMatcher(global_destructor_matcher, &global_destructor_writer);
1191 
1192   // Matches CXXRecordDecls with a deleted operator new - e.g.
1193   // StructWithNoOperatorNew below:
1194   //     struct StructWithNoOperatorNew {
1195   //       void* operator new(size_t) = delete;
1196   //     };
1197   auto record_with_deleted_allocation_operator_type_matcher = cxxRecordDecl(
1198       hasMethod(allOf(hasOverloadedOperatorName("new"), isDeleted())));
1199   // Matches rewritable fields inside structs with no operator new.  See the
1200   // testcase in tests/gen-deleted-operator-new-test.cc
1201   auto field_in_record_with_deleted_operator_new_matcher = fieldDecl(
1202       allOf(field_decl_matcher,
1203             hasParent(record_with_deleted_allocation_operator_type_matcher)));
1204   FilteredExprWriter field_in_record_with_deleted_operator_new_writer(
1205       &output_helper, "embedder-has-no-operator-new");
1206   match_finder.addMatcher(field_in_record_with_deleted_operator_new_matcher,
1207                           &field_in_record_with_deleted_operator_new_writer);
1208   // Matches rewritable fields that contain a pointer, pointing to a pointee
1209   // with no operator new.  See the testcase in
1210   // tests/gen-deleted-operator-new-test.cc
1211   auto field_pointing_to_record_with_deleted_operator_new_matcher =
1212       fieldDecl(allOf(
1213           field_decl_matcher,
1214           hasType(pointerType(
1215               pointee(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
1216                   record_with_deleted_allocation_operator_type_matcher))))))));
1217   FilteredExprWriter field_pointing_to_record_with_deleted_operator_new_writer(
1218       &output_helper, "pointee-has-no-operator-new");
1219   match_finder.addMatcher(
1220       field_pointing_to_record_with_deleted_operator_new_matcher,
1221       &field_pointing_to_record_with_deleted_operator_new_writer);
1222 
1223   // Matches fields in unions (both directly rewritable fields as well as union
1224   // fields that embed a struct that contains a rewritable field).  See also the
1225   // testcases in tests/gen-unions-test.cc.
1226   auto union_field_decl_matcher = recordDecl(allOf(
1227       isUnion(), forEach(fieldDecl(anyOf(field_decl_matcher,
1228                                          hasType(typeWithEmbeddedFieldDecl(
1229                                              field_decl_matcher)))))));
1230   FilteredExprWriter union_field_decl_writer(&output_helper, "union");
1231   match_finder.addMatcher(union_field_decl_matcher, &union_field_decl_writer);
1232 
1233   // Prepare and run the tool.
1234   std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
1235       clang::tooling::newFrontendActionFactory(&match_finder, &output_helper);
1236   int result = tool.run(factory.get());
1237   if (result != 0)
1238     return result;
1239 
1240   return 0;
1241 }
1242