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, ¯o_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