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 // raw_ptr<T>:
7 // Pointee* field_
8 // becomes:
9 // raw_ptr<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 raw_ptr<...>
65 // replaces a raw pointer.
66 const char kIncludePath[] = "base/memory/raw_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 raw_ptr<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 // raw_ptr<T> requires C++. In particular, attempting to #include
239 // "base/memory/raw_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::OpenCLCXX:
245 case clang::Language::ObjCXX:
246 return false;
247 }
248
249 assert(false && "Unrecognized clang::Language");
250 return true;
251 }
252
253 OutputSectionHelper edits_helper_;
254 OutputSectionHelper field_decl_filter_helper_;
255 clang::Language current_language_ = clang::Language::Unknown;
256 };
257
GetFilePath(const clang::SourceManager & source_manager,const clang::FieldDecl & field_decl)258 llvm::StringRef GetFilePath(const clang::SourceManager& source_manager,
259 const clang::FieldDecl& field_decl) {
260 clang::SourceLocation loc = field_decl.getSourceRange().getBegin();
261 if (loc.isInvalid() || !loc.isFileID())
262 return llvm::StringRef();
263
264 clang::FileID file_id = source_manager.getDecomposedLoc(loc).first;
265 const clang::FileEntry* file_entry =
266 source_manager.getFileEntryForID(file_id);
267 if (!file_entry)
268 return llvm::StringRef();
269
270 return file_entry->getName();
271 }
272
AST_MATCHER(clang::FieldDecl,isInThirdPartyLocation)273 AST_MATCHER(clang::FieldDecl, isInThirdPartyLocation) {
274 llvm::StringRef file_path =
275 GetFilePath(Finder->getASTContext().getSourceManager(), Node);
276
277 // Blink is part of the Chromium git repo, even though it contains
278 // "third_party" in its path.
279 if (file_path.contains("third_party/blink/"))
280 return false;
281
282 // Otherwise, just check if the paths contains the "third_party" substring.
283 // We don't want to rewrite content of such paths even if they are in the main
284 // Chromium git repository.
285 return file_path.contains("third_party");
286 }
287
AST_MATCHER(clang::FieldDecl,isInGeneratedLocation)288 AST_MATCHER(clang::FieldDecl, isInGeneratedLocation) {
289 llvm::StringRef file_path =
290 GetFilePath(Finder->getASTContext().getSourceManager(), Node);
291
292 return file_path.startswith("gen/") || file_path.contains("/gen/");
293 }
294
295 // Represents a filter file specified via cmdline.
296 class FilterFile {
297 public:
FilterFile(const llvm::cl::opt<std::string> & cmdline_param)298 explicit FilterFile(const llvm::cl::opt<std::string>& cmdline_param) {
299 ParseInputFile(cmdline_param);
300 }
301
302 FilterFile(const FilterFile&) = delete;
303 FilterFile& operator=(const FilterFile&) = delete;
304
305 // Returns true if any of the filter file lines is exactly equal to |line|.
ContainsLine(llvm::StringRef line) const306 bool ContainsLine(llvm::StringRef line) const {
307 auto it = file_lines_.find(line);
308 return it != file_lines_.end();
309 }
310
311 // Returns true if |string_to_match| matches based on the filter file lines.
312 // Filter file lines can contain both inclusions and exclusions in the filter.
313 // Only returns true if |string_to_match| both matches an inclusion filter and
314 // is *not* matched by an exclusion filter.
ContainsSubstringOf(llvm::StringRef string_to_match) const315 bool ContainsSubstringOf(llvm::StringRef string_to_match) const {
316 if (!inclusion_substring_regex_.hasValue()) {
317 std::vector<std::string> regex_escaped_inclusion_file_lines;
318 std::vector<std::string> regex_escaped_exclusion_file_lines;
319 regex_escaped_inclusion_file_lines.reserve(file_lines_.size());
320 for (const llvm::StringRef& file_line : file_lines_.keys()) {
321 if (file_line.startswith("!")) {
322 regex_escaped_exclusion_file_lines.push_back(
323 llvm::Regex::escape(file_line.substr(1)));
324 } else {
325 regex_escaped_inclusion_file_lines.push_back(
326 llvm::Regex::escape(file_line));
327 }
328 }
329 std::string inclusion_substring_regex_pattern =
330 llvm::join(regex_escaped_inclusion_file_lines.begin(),
331 regex_escaped_inclusion_file_lines.end(), "|");
332 inclusion_substring_regex_.emplace(inclusion_substring_regex_pattern);
333 std::string exclusion_substring_regex_pattern =
334 llvm::join(regex_escaped_exclusion_file_lines.begin(),
335 regex_escaped_exclusion_file_lines.end(), "|");
336 exclusion_substring_regex_.emplace(exclusion_substring_regex_pattern);
337 }
338 return inclusion_substring_regex_->match(string_to_match) &&
339 !exclusion_substring_regex_->match(string_to_match);
340 }
341
342 private:
343 // Expected file format:
344 // - '#' character starts a comment (which gets ignored).
345 // - Blank or whitespace-only or comment-only lines are ignored.
346 // - Other lines are expected to contain a fully-qualified name of a field
347 // like:
348 // autofill::AddressField::address1_ # some comment
349 // - Templates are represented without template arguments, like:
350 // WTF::HashTable::table_ # some comment
ParseInputFile(const llvm::cl::opt<std::string> & cmdline_param)351 void ParseInputFile(const llvm::cl::opt<std::string>& cmdline_param) {
352 std::string filepath = cmdline_param;
353 if (filepath.empty())
354 return;
355
356 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file_or_err =
357 llvm::MemoryBuffer::getFile(filepath);
358 if (std::error_code err = file_or_err.getError()) {
359 llvm::errs() << "ERROR: Cannot open the file specified in --"
360 << cmdline_param.ArgStr << " argument: " << filepath << ": "
361 << err.message() << "\n";
362 assert(false);
363 return;
364 }
365
366 llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#');
367 for (; !it.is_at_eof(); ++it) {
368 llvm::StringRef line = *it;
369
370 // Remove trailing comments.
371 size_t comment_start_pos = line.find('#');
372 if (comment_start_pos != llvm::StringRef::npos)
373 line = line.substr(0, comment_start_pos);
374 line = line.trim();
375
376 if (line.empty())
377 continue;
378
379 file_lines_.insert(line);
380 }
381 }
382
383 // Stores all file lines (after stripping comments and blank lines).
384 llvm::StringSet<> file_lines_;
385
386 // |file_lines_| is partitioned based on whether the line starts with a !
387 // (exclusion line) or not (inclusion line). Inclusion lines specify things to
388 // be matched by the filter. The exclusion lines specify what to force exclude
389 // from the filter. Lazily-constructed regex that matches strings that contain
390 // any of the inclusion lines in |file_lines_|.
391 mutable llvm::Optional<llvm::Regex> inclusion_substring_regex_;
392
393 // Lazily-constructed regex that matches strings that contain any of the
394 // exclusion lines in |file_lines_|.
395 mutable llvm::Optional<llvm::Regex> exclusion_substring_regex_;
396 };
397
AST_MATCHER_P(clang::FieldDecl,isFieldDeclListedInFilterFile,const FilterFile *,Filter)398 AST_MATCHER_P(clang::FieldDecl,
399 isFieldDeclListedInFilterFile,
400 const FilterFile*,
401 Filter) {
402 return Filter->ContainsLine(Node.getQualifiedNameAsString());
403 }
404
AST_MATCHER_P(clang::FieldDecl,isInLocationListedInFilterFile,const FilterFile *,Filter)405 AST_MATCHER_P(clang::FieldDecl,
406 isInLocationListedInFilterFile,
407 const FilterFile*,
408 Filter) {
409 llvm::StringRef file_path =
410 GetFilePath(Finder->getASTContext().getSourceManager(), Node);
411 return Filter->ContainsSubstringOf(file_path);
412 }
413
AST_MATCHER(clang::Decl,isInExternCContext)414 AST_MATCHER(clang::Decl, isInExternCContext) {
415 return Node.getLexicalDeclContext()->isExternCContext();
416 }
417
418 // Given:
419 // template <typename T, typename T2> class MyTemplate {}; // Node1 and Node4
420 // template <typename T2> class MyTemplate<int, T2> {}; // Node2
421 // template <> class MyTemplate<int, char> {}; // Node3
422 // void foo() {
423 // // This creates implicit template specialization (Node4) out of the
424 // // explicit template definition (Node1).
425 // MyTemplate<bool, double> v;
426 // }
427 // with the following AST nodes:
428 // ClassTemplateDecl MyTemplate - Node1
429 // | |-CXXRecordDecl class MyTemplate definition
430 // | `-ClassTemplateSpecializationDecl class MyTemplate definition - Node4
431 // ClassTemplatePartialSpecializationDecl class MyTemplate definition - Node2
432 // ClassTemplateSpecializationDecl class MyTemplate definition - Node3
433 //
434 // Matches AST node 4, but not AST node2 nor node3.
AST_MATCHER(clang::ClassTemplateSpecializationDecl,isImplicitClassTemplateSpecialization)435 AST_MATCHER(clang::ClassTemplateSpecializationDecl,
436 isImplicitClassTemplateSpecialization) {
437 return !Node.isExplicitSpecialization();
438 }
439
440 // Matches CXXRecordDecls that are classified as trivial:
441 // https://en.cppreference.com/w/cpp/named_req/TrivialType
AST_MATCHER(clang::CXXRecordDecl,isTrivial)442 AST_MATCHER(clang::CXXRecordDecl, isTrivial) {
443 return Node.isTrivial();
444 }
445
446 // Given:
447 // template <typename T, typename T2> void foo(T t, T2 t2) {}; // N1 and N4
448 // template <typename T2> void foo<int, T2>(int t, T2 t) {}; // N2
449 // template <> void foo<int, char>(int t, char t2) {}; // N3
450 // void foo() {
451 // // This creates implicit template specialization (N4) out of the
452 // // explicit template definition (N1).
453 // foo<bool, double>(true, 1.23);
454 // }
455 // with the following AST nodes:
456 // FunctionTemplateDecl foo
457 // |-FunctionDecl 0x191da68 foo 'void (T, T2)' // N1
458 // `-FunctionDecl 0x194bf08 foo 'void (bool, double)' // N4
459 // FunctionTemplateDecl foo
460 // `-FunctionDecl foo 'void (int, T2)' // N2
461 // FunctionDecl foo 'void (int, char)' // N3
462 //
463 // Matches AST node N4, but not AST nodes N1, N2 nor N3.
AST_MATCHER(clang::FunctionDecl,isImplicitFunctionTemplateSpecialization)464 AST_MATCHER(clang::FunctionDecl, isImplicitFunctionTemplateSpecialization) {
465 switch (Node.getTemplateSpecializationKind()) {
466 case clang::TSK_ImplicitInstantiation:
467 return true;
468 case clang::TSK_Undeclared:
469 case clang::TSK_ExplicitSpecialization:
470 case clang::TSK_ExplicitInstantiationDeclaration:
471 case clang::TSK_ExplicitInstantiationDefinition:
472 return false;
473 }
474 }
475
AST_MATCHER(clang::Type,anyCharType)476 AST_MATCHER(clang::Type, anyCharType) {
477 return Node.isAnyCharacterType();
478 }
479
AST_POLYMORPHIC_MATCHER(isInMacroLocation,AST_POLYMORPHIC_SUPPORTED_TYPES (clang::Decl,clang::Stmt,clang::TypeLoc))480 AST_POLYMORPHIC_MATCHER(isInMacroLocation,
481 AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl,
482 clang::Stmt,
483 clang::TypeLoc)) {
484 return Node.getBeginLoc().isMacroID();
485 }
486
487 // If |field_decl| declares a field in an implicit template specialization, then
488 // finds and returns the corresponding FieldDecl from the template definition.
489 // Otherwise, just returns the original |field_decl| argument.
GetExplicitDecl(const clang::FieldDecl * field_decl)490 const clang::FieldDecl* GetExplicitDecl(const clang::FieldDecl* field_decl) {
491 if (field_decl->isAnonymousStructOrUnion())
492 return field_decl; // Safe fallback - |field_decl| is not a pointer field.
493
494 const clang::CXXRecordDecl* record_decl =
495 clang::dyn_cast<clang::CXXRecordDecl>(field_decl->getParent());
496 if (!record_decl)
497 return field_decl; // Non-C++ records are never template instantiations.
498
499 const clang::CXXRecordDecl* pattern_decl =
500 record_decl->getTemplateInstantiationPattern();
501 if (!pattern_decl)
502 return field_decl; // |pattern_decl| is not a template instantiation.
503
504 if (record_decl->getTemplateSpecializationKind() !=
505 clang::TemplateSpecializationKind::TSK_ImplicitInstantiation) {
506 return field_decl; // |field_decl| was in an *explicit* specialization.
507 }
508
509 // Find the field decl with the same name in |pattern_decl|.
510 clang::DeclContextLookupResult lookup_result =
511 pattern_decl->lookup(field_decl->getDeclName());
512 assert(!lookup_result.empty());
513 const clang::NamedDecl* found_decl = lookup_result.front();
514 assert(found_decl);
515 field_decl = clang::dyn_cast<clang::FieldDecl>(found_decl);
516 assert(field_decl);
517 return field_decl;
518 }
519
520 // Given:
521 // template <typename T>
522 // class MyTemplate {
523 // T field; // This is an explicit field declaration.
524 // };
525 // void foo() {
526 // // This creates implicit template specialization for MyTemplate,
527 // // including an implicit |field| declaration.
528 // MyTemplate<int> v;
529 // v.field = 123;
530 // }
531 // and
532 // innerMatcher that will match the explicit |T field| declaration (but not
533 // necessarily the implicit template declarations),
534 // hasExplicitFieldDecl(innerMatcher) will match both explicit and implicit
535 // field declarations.
536 //
537 // For example, |member_expr_matcher| below will match |v.field| in the example
538 // above, even though the type of |v.field| is |int|, rather than |T| (matched
539 // by substTemplateTypeParmType()):
540 // auto explicit_field_decl_matcher =
541 // fieldDecl(hasType(substTemplateTypeParmType()));
542 // auto member_expr_matcher = memberExpr(member(fieldDecl(
543 // hasExplicitFieldDecl(explicit_field_decl_matcher))))
AST_MATCHER_P(clang::FieldDecl,hasExplicitFieldDecl,clang::ast_matchers::internal::Matcher<clang::FieldDecl>,InnerMatcher)544 AST_MATCHER_P(clang::FieldDecl,
545 hasExplicitFieldDecl,
546 clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
547 InnerMatcher) {
548 const clang::FieldDecl* explicit_field_decl = GetExplicitDecl(&Node);
549 return InnerMatcher.matches(*explicit_field_decl, Finder, Builder);
550 }
551
552 // If |original_param| declares a parameter in an implicit template
553 // specialization of a function or method, then finds and returns the
554 // corresponding ParmVarDecl from the template definition. Otherwise, just
555 // returns the |original_param| argument.
556 //
557 // Note: nullptr may be returned in rare, unimplemented cases.
GetExplicitDecl(const clang::ParmVarDecl * original_param)558 const clang::ParmVarDecl* GetExplicitDecl(
559 const clang::ParmVarDecl* original_param) {
560 const clang::FunctionDecl* original_func =
561 clang::dyn_cast<clang::FunctionDecl>(original_param->getDeclContext());
562 if (!original_func) {
563 // |!original_func| may happen when the ParmVarDecl is part of a
564 // FunctionType, but not part of a FunctionDecl:
565 // base::RepeatingCallback<void(int parm_var_decl_here)>
566 //
567 // In theory, |parm_var_decl_here| can also represent an implicit template
568 // specialization in this scenario. OTOH, it should be rare + shouldn't
569 // matter for this rewriter, so for now let's just return the
570 // |original_param|.
571 //
572 // TODO: Implement support for this scenario.
573 return nullptr;
574 }
575
576 const clang::FunctionDecl* pattern_func =
577 original_func->getTemplateInstantiationPattern();
578 if (!pattern_func) {
579 // |original_func| is not a template instantiation - return the
580 // |original_param|.
581 return original_param;
582 }
583
584 // See if |pattern_func| has a parameter that is a template parameter pack.
585 bool has_param_pack = false;
586 unsigned int index_of_param_pack = std::numeric_limits<unsigned int>::max();
587 for (unsigned int i = 0; i < pattern_func->getNumParams(); i++) {
588 const clang::ParmVarDecl* pattern_param = pattern_func->getParamDecl(i);
589 if (!pattern_param->isParameterPack())
590 continue;
591
592 if (has_param_pack) {
593 // TODO: Implement support for multiple parameter packs.
594 return nullptr;
595 }
596
597 has_param_pack = true;
598 index_of_param_pack = i;
599 }
600
601 // Find and return the corresponding ParmVarDecl from |pattern_func|.
602 unsigned int original_index = original_param->getFunctionScopeIndex();
603 unsigned int pattern_index = std::numeric_limits<unsigned int>::max();
604 if (!has_param_pack) {
605 pattern_index = original_index;
606 } else {
607 // |original_func| has parameters that look like this:
608 // l1, l2, l3, p1, p2, p3, t1, t2, t3
609 // where
610 // lN is a leading, non-pack parameter
611 // pN is an expansion of a template parameter pack
612 // tN is a trailing, non-pack parameter
613 // Using the knowledge above, let's adjust |pattern_index| as needed.
614 unsigned int leading_param_num = index_of_param_pack; // How many |lN|.
615 unsigned int pack_expansion_num = // How many |pN| above.
616 original_func->getNumParams() - pattern_func->getNumParams() + 1;
617 if (original_index < leading_param_num) {
618 // |original_param| is a leading, non-pack parameter.
619 pattern_index = original_index;
620 } else if (leading_param_num <= original_index &&
621 original_index < (leading_param_num + pack_expansion_num)) {
622 // |original_param| is an expansion of a template pack parameter.
623 pattern_index = index_of_param_pack;
624 } else if ((leading_param_num + pack_expansion_num) <= original_index) {
625 // |original_param| is a trailing, non-pack parameter.
626 pattern_index = original_index - pack_expansion_num + 1;
627 }
628 }
629 assert(pattern_index < pattern_func->getNumParams());
630 return pattern_func->getParamDecl(pattern_index);
631 }
632
AST_MATCHER_P(clang::ParmVarDecl,hasExplicitParmVarDecl,clang::ast_matchers::internal::Matcher<clang::ParmVarDecl>,InnerMatcher)633 AST_MATCHER_P(clang::ParmVarDecl,
634 hasExplicitParmVarDecl,
635 clang::ast_matchers::internal::Matcher<clang::ParmVarDecl>,
636 InnerMatcher) {
637 const clang::ParmVarDecl* explicit_param = GetExplicitDecl(&Node);
638 if (!explicit_param) {
639 // Rare, unimplemented case - fall back to returning "no match".
640 return false;
641 }
642
643 return InnerMatcher.matches(*explicit_param, Finder, Builder);
644 }
645
646 // Returns |true| if and only if:
647 // 1. |a| and |b| are in the same file (e.g. |false| is returned if any location
648 // is within macro scratch space or a similar location; similarly |false| is
649 // returned if |a| and |b| are in different files).
650 // 2. |a| and |b| overlap.
IsOverlapping(const clang::SourceManager & source_manager,const clang::SourceRange & a,const clang::SourceRange & b)651 bool IsOverlapping(const clang::SourceManager& source_manager,
652 const clang::SourceRange& a,
653 const clang::SourceRange& b) {
654 clang::FullSourceLoc a1(a.getBegin(), source_manager);
655 clang::FullSourceLoc a2(a.getEnd(), source_manager);
656 clang::FullSourceLoc b1(b.getBegin(), source_manager);
657 clang::FullSourceLoc b2(b.getEnd(), source_manager);
658
659 // Are all locations in a file?
660 if (!a1.isFileID() || !a2.isFileID() || !b1.isFileID() || !b2.isFileID())
661 return false;
662
663 // Are all locations in the same file?
664 if (a1.getFileID() != a2.getFileID() || a2.getFileID() != b1.getFileID() ||
665 b1.getFileID() != b2.getFileID()) {
666 return false;
667 }
668
669 // Check the 2 cases below:
670 // 1. A: |============|
671 // B: |===============|
672 // a1 b1 a2 b2
673 // or
674 // 2. A: |====================|
675 // B: |=======|
676 // a1 b1 b2 a2
677 bool b1_is_inside_a_range = a1.getFileOffset() <= b1.getFileOffset() &&
678 b1.getFileOffset() <= a2.getFileOffset();
679
680 // Check the 2 cases below:
681 // 1. B: |============|
682 // A: |===============|
683 // b1 a1 b2 a2
684 // or
685 // 2. B: |====================|
686 // A: |=======|
687 // b1 a1 a2 b2
688 bool a1_is_inside_b_range = b1.getFileOffset() <= a1.getFileOffset() &&
689 a1.getFileOffset() <= b2.getFileOffset();
690
691 return b1_is_inside_a_range || a1_is_inside_b_range;
692 }
693
694 // Matcher for FieldDecl that has a SourceRange that overlaps other declarations
695 // within the parent RecordDecl.
696 //
697 // Given
698 // struct MyStruct {
699 // int f;
700 // int f2, f3;
701 // struct S { int x } f4;
702 // };
703 // - doesn't match |f|
704 // - matches |f2| and |f3| (which overlap each other's location)
705 // - matches |f4| (which overlaps the location of |S|)
AST_MATCHER(clang::FieldDecl,overlapsOtherDeclsWithinRecordDecl)706 AST_MATCHER(clang::FieldDecl, overlapsOtherDeclsWithinRecordDecl) {
707 const clang::FieldDecl& self = Node;
708 const clang::SourceManager& source_manager =
709 Finder->getASTContext().getSourceManager();
710
711 const clang::RecordDecl* record_decl = self.getParent();
712 clang::SourceRange self_range(self.getBeginLoc(), self.getEndLoc());
713
714 auto is_overlapping_sibling = [&](const clang::Decl* other_decl) {
715 if (other_decl == &self)
716 return false;
717
718 clang::SourceRange other_range(other_decl->getBeginLoc(),
719 other_decl->getEndLoc());
720 return IsOverlapping(source_manager, self_range, other_range);
721 };
722 bool has_sibling_with_overlapping_location =
723 std::any_of(record_decl->decls_begin(), record_decl->decls_end(),
724 is_overlapping_sibling);
725 return has_sibling_with_overlapping_location;
726 }
727
728 // Matches clang::Type if
729 // 1) it represents a RecordDecl with a FieldDecl that matches the InnerMatcher
730 // (*all* such FieldDecls will be matched)
731 // or
732 // 2) it represents an array or a RecordDecl that nests the case #1
733 // (this recurses to any depth).
AST_MATCHER_P(clang::QualType,typeWithEmbeddedFieldDecl,clang::ast_matchers::internal::Matcher<clang::FieldDecl>,InnerMatcher)734 AST_MATCHER_P(clang::QualType,
735 typeWithEmbeddedFieldDecl,
736 clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
737 InnerMatcher) {
738 const clang::Type* type =
739 Node.getDesugaredType(Finder->getASTContext()).getTypePtrOrNull();
740 if (!type)
741 return false;
742
743 if (const clang::CXXRecordDecl* record_decl = type->getAsCXXRecordDecl()) {
744 auto matcher = recordDecl(forEach(fieldDecl(hasExplicitFieldDecl(anyOf(
745 InnerMatcher, hasType(typeWithEmbeddedFieldDecl(InnerMatcher)))))));
746 return matcher.matches(*record_decl, Finder, Builder);
747 }
748
749 if (type->isArrayType()) {
750 const clang::ArrayType* array_type =
751 Finder->getASTContext().getAsArrayType(Node);
752 auto matcher = typeWithEmbeddedFieldDecl(InnerMatcher);
753 return matcher.matches(array_type->getElementType(), Finder, Builder);
754 }
755
756 return false;
757 }
758
759 // forEachInitExprWithFieldDecl matches InitListExpr if it
760 // 1) evaluates to a RecordType
761 // 2) has a InitListExpr + FieldDecl pair that matches the submatcher args.
762 //
763 // forEachInitExprWithFieldDecl is based on and very similar to the builtin
764 // 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)765 AST_MATCHER_P2(clang::InitListExpr,
766 forEachInitExprWithFieldDecl,
767 clang::ast_matchers::internal::Matcher<clang::Expr>,
768 init_expr_matcher,
769 clang::ast_matchers::internal::Matcher<clang::FieldDecl>,
770 field_decl_matcher) {
771 const clang::InitListExpr& init_list_expr = Node;
772 const clang::Type* type = init_list_expr.getType()
773 .getDesugaredType(Finder->getASTContext())
774 .getTypePtrOrNull();
775 if (!type)
776 return false;
777 const clang::CXXRecordDecl* record_decl = type->getAsCXXRecordDecl();
778 if (!record_decl)
779 return false;
780
781 bool is_matching = false;
782 clang::ast_matchers::internal::BoundNodesTreeBuilder result;
783 const std::vector<const clang::FieldDecl*> field_decls(
784 record_decl->field_begin(), record_decl->field_end());
785 for (unsigned i = 0; i < init_list_expr.getNumInits(); i++) {
786 const clang::Expr* expr = init_list_expr.getInit(i);
787
788 const clang::FieldDecl* field_decl = nullptr;
789 if (const clang::ImplicitValueInitExpr* implicit_value_init_expr =
790 clang::dyn_cast<clang::ImplicitValueInitExpr>(expr)) {
791 continue; // Do not match implicit value initializers.
792 } else if (const clang::DesignatedInitExpr* designated_init_expr =
793 clang::dyn_cast<clang::DesignatedInitExpr>(expr)) {
794 // Nested designators are unsupported by C++.
795 if (designated_init_expr->size() != 1)
796 break;
797 expr = designated_init_expr->getInit();
798 field_decl = designated_init_expr->getDesignator(0)->getField();
799 } else {
800 if (i >= field_decls.size())
801 break;
802 field_decl = field_decls[i];
803 }
804
805 clang::ast_matchers::internal::BoundNodesTreeBuilder field_matches(
806 *Builder);
807 if (field_decl_matcher.matches(*field_decl, Finder, &field_matches)) {
808 clang::ast_matchers::internal::BoundNodesTreeBuilder expr_matches(
809 field_matches);
810 if (init_expr_matcher.matches(*expr, Finder, &expr_matches)) {
811 result.addMatch(expr_matches);
812 is_matching = true;
813 }
814 }
815 }
816
817 *Builder = std::move(result);
818 return is_matching;
819 }
820
821 // Rewrites |SomeClass* field| (matched as "affectedFieldDecl") into
822 // |raw_ptr<SomeClass> field| and for each file rewritten in such way adds an
823 // |#include "base/memory/raw_ptr.h"|.
824 class FieldDeclRewriter : public MatchFinder::MatchCallback {
825 public:
FieldDeclRewriter(OutputHelper * output_helper)826 explicit FieldDeclRewriter(OutputHelper* output_helper)
827 : output_helper_(output_helper) {}
828
829 FieldDeclRewriter(const FieldDeclRewriter&) = delete;
830 FieldDeclRewriter& operator=(const FieldDeclRewriter&) = delete;
831
run(const MatchFinder::MatchResult & result)832 void run(const MatchFinder::MatchResult& result) override {
833 const clang::ASTContext& ast_context = *result.Context;
834 const clang::SourceManager& source_manager = *result.SourceManager;
835
836 const clang::FieldDecl* field_decl =
837 result.Nodes.getNodeAs<clang::FieldDecl>("affectedFieldDecl");
838 assert(field_decl && "matcher should bind 'fieldDecl'");
839
840 const clang::TypeSourceInfo* type_source_info =
841 field_decl->getTypeSourceInfo();
842 assert(type_source_info && "assuming |type_source_info| is always present");
843
844 clang::QualType pointer_type = type_source_info->getType();
845 assert(type_source_info->getType()->isPointerType() &&
846 "matcher should only match pointer types");
847
848 // Calculate the |replacement_range|.
849 //
850 // Consider the following example:
851 // const Pointee* const field_name_;
852 // ^--------------------^ = |replacement_range|
853 // ^ = |field_decl->getLocation()|
854 // ^ = |field_decl->getBeginLoc()|
855 // ^ = PointerTypeLoc::getStarLoc
856 // ^------^ = TypeLoc::getSourceRange
857 //
858 // We get the |replacement_range| in a bit clumsy way, because clang docs
859 // for QualifiedTypeLoc explicitly say that these objects "intentionally do
860 // not provide source location for type qualifiers".
861 clang::SourceRange replacement_range(field_decl->getBeginLoc(),
862 field_decl->getLocation());
863
864 // Calculate |replacement_text|.
865 std::string replacement_text = GenerateNewText(ast_context, pointer_type);
866 if (field_decl->isMutable())
867 replacement_text.insert(0, "mutable ");
868
869 // Generate and print a replacement.
870 output_helper_->AddReplacement(source_manager, replacement_range,
871 replacement_text,
872 true /* should_add_include */);
873 }
874
875 private:
GenerateNewText(const clang::ASTContext & ast_context,const clang::QualType & pointer_type)876 std::string GenerateNewText(const clang::ASTContext& ast_context,
877 const clang::QualType& pointer_type) {
878 std::string result;
879
880 assert(pointer_type->isPointerType() && "caller must pass a pointer type!");
881 clang::QualType pointee_type = pointer_type->getPointeeType();
882
883 // Preserve qualifiers.
884 assert(!pointer_type.isRestrictQualified() &&
885 "|restrict| is a C-only qualifier and raw_ptr<T> needs C++");
886 if (pointer_type.isConstQualified())
887 result += "const ";
888 if (pointer_type.isVolatileQualified())
889 result += "volatile ";
890
891 // Convert pointee type to string.
892 clang::PrintingPolicy printing_policy(ast_context.getLangOpts());
893 printing_policy.SuppressScope = 1; // s/blink::Pointee/Pointee/
894 std::string pointee_type_as_string =
895 pointee_type.getAsString(printing_policy);
896 result += llvm::formatv("raw_ptr<{0}> ", pointee_type_as_string);
897
898 return result;
899 }
900
901 OutputHelper* const output_helper_;
902 };
903
904 // Rewrites |my_struct.ptr_field| (matched as "affectedMemberExpr") into
905 // |my_struct.ptr_field.get()|.
906 class AffectedExprRewriter : public MatchFinder::MatchCallback {
907 public:
AffectedExprRewriter(OutputHelper * output_helper)908 explicit AffectedExprRewriter(OutputHelper* output_helper)
909 : output_helper_(output_helper) {}
910
911 AffectedExprRewriter(const AffectedExprRewriter&) = delete;
912 AffectedExprRewriter& operator=(const AffectedExprRewriter&) = delete;
913
run(const MatchFinder::MatchResult & result)914 void run(const MatchFinder::MatchResult& result) override {
915 const clang::SourceManager& source_manager = *result.SourceManager;
916
917 const clang::MemberExpr* member_expr =
918 result.Nodes.getNodeAs<clang::MemberExpr>("affectedMemberExpr");
919 assert(member_expr && "matcher should bind 'affectedMemberExpr'");
920
921 clang::SourceLocation member_name_start = member_expr->getMemberLoc();
922 size_t member_name_length = member_expr->getMemberDecl()->getName().size();
923 clang::SourceLocation insertion_loc =
924 member_name_start.getLocWithOffset(member_name_length);
925
926 clang::SourceRange replacement_range(insertion_loc, insertion_loc);
927
928 output_helper_->AddReplacement(source_manager, replacement_range, ".get()");
929 }
930
931 private:
932 OutputHelper* const output_helper_;
933 };
934
935 // Emits problematic fields (matched as "affectedFieldDecl") as filtered fields.
936 class FilteredExprWriter : public MatchFinder::MatchCallback {
937 public:
FilteredExprWriter(OutputHelper * output_helper,llvm::StringRef filter_tag)938 FilteredExprWriter(OutputHelper* output_helper, llvm::StringRef filter_tag)
939 : output_helper_(output_helper), filter_tag_(filter_tag) {}
940
941 FilteredExprWriter(const FilteredExprWriter&) = delete;
942 FilteredExprWriter& operator=(const FilteredExprWriter&) = delete;
943
run(const MatchFinder::MatchResult & result)944 void run(const MatchFinder::MatchResult& result) override {
945 const clang::FieldDecl* field_decl =
946 result.Nodes.getNodeAs<clang::FieldDecl>("affectedFieldDecl");
947 assert(field_decl && "matcher should bind 'affectedFieldDecl'");
948
949 output_helper_->AddFilteredField(*field_decl, filter_tag_);
950 }
951
952 private:
953 OutputHelper* const output_helper_;
954 llvm::StringRef filter_tag_;
955 };
956
957 } // namespace
958
main(int argc,const char * argv[])959 int main(int argc, const char* argv[]) {
960 // TODO(dcheng): Clang tooling should do this itself.
961 // http://llvm.org/bugs/show_bug.cgi?id=21627
962 llvm::InitializeNativeTarget();
963 llvm::InitializeNativeTargetAsmParser();
964 llvm::cl::OptionCategory category(
965 "rewrite_raw_ptr_fields: changes |T* field_| to |raw_ptr<T> field_|.");
966 llvm::cl::opt<std::string> exclude_fields_param(
967 kExcludeFieldsParamName, llvm::cl::value_desc("filepath"),
968 llvm::cl::desc("file listing fields to be blocked (not rewritten)"));
969 llvm::cl::opt<std::string> exclude_paths_param(
970 kExcludePathsParamName, llvm::cl::value_desc("filepath"),
971 llvm::cl::desc("file listing paths to be blocked (not rewritten)"));
972 llvm::Expected<clang::tooling::CommonOptionsParser> options =
973 clang::tooling::CommonOptionsParser::create(argc, argv, category);
974 assert(static_cast<bool>(options)); // Should not return an error.
975 clang::tooling::ClangTool tool(options->getCompilations(),
976 options->getSourcePathList());
977
978 MatchFinder match_finder;
979 OutputHelper output_helper;
980
981 // Supported pointer types =========
982 // Given
983 // struct MyStrict {
984 // int* int_ptr;
985 // int i;
986 // int (*func_ptr)();
987 // int (MyStruct::* member_func_ptr)(char);
988 // int (*ptr_to_array_of_ints)[123]
989 // };
990 // matches |int*|, but not the other types.
991 auto supported_pointer_types_matcher =
992 pointerType(unless(pointee(hasUnqualifiedDesugaredType(
993 anyOf(functionType(), memberPointerType(), arrayType())))));
994
995 // Implicit field declarations =========
996 // Matches field declarations that do not explicitly appear in the source
997 // code:
998 // 1. fields of classes generated by the compiler to back capturing lambdas,
999 // 2. fields within an implicit class or function template specialization
1000 // (e.g. when a template is instantiated by a bit of code and there's no
1001 // explicit specialization for it).
1002 auto implicit_class_specialization_matcher =
1003 classTemplateSpecializationDecl(isImplicitClassTemplateSpecialization());
1004 auto implicit_function_specialization_matcher =
1005 functionDecl(isImplicitFunctionTemplateSpecialization());
1006 auto implicit_field_decl_matcher = fieldDecl(hasParent(cxxRecordDecl(anyOf(
1007 isLambda(), implicit_class_specialization_matcher,
1008 hasAncestor(decl(anyOf(implicit_class_specialization_matcher,
1009 implicit_function_specialization_matcher)))))));
1010
1011 // Field declarations =========
1012 // Given
1013 // struct S {
1014 // int* y;
1015 // };
1016 // matches |int* y|. Doesn't match:
1017 // - non-pointer types
1018 // - fields of lambda-supporting classes
1019 // - fields listed in the --exclude-fields cmdline param or located in paths
1020 // matched by --exclude-paths cmdline param
1021 // - "implicit" fields (i.e. field decls that are not explicitly present in
1022 // the source code)
1023 FilterFile fields_to_exclude(exclude_fields_param);
1024 FilterFile paths_to_exclude(exclude_paths_param);
1025 auto field_decl_matcher =
1026 fieldDecl(
1027 allOf(hasType(supported_pointer_types_matcher),
1028 unless(anyOf(isExpansionInSystemHeader(), isInExternCContext(),
1029 isInThirdPartyLocation(), isInGeneratedLocation(),
1030 isInLocationListedInFilterFile(&paths_to_exclude),
1031 isFieldDeclListedInFilterFile(&fields_to_exclude),
1032 implicit_field_decl_matcher))))
1033 .bind("affectedFieldDecl");
1034 FieldDeclRewriter field_decl_rewriter(&output_helper);
1035 match_finder.addMatcher(field_decl_matcher, &field_decl_rewriter);
1036
1037 // Matches expressions that used to return a value of type |SomeClass*|
1038 // but after the rewrite return an instance of |raw_ptr<SomeClass>|.
1039 // Many such expressions might need additional changes after the rewrite:
1040 // - Some expressions (printf args, const_cast args, etc.) might need |.get()|
1041 // appended.
1042 // - Using such expressions in specific contexts (e.g. as in-out arguments or
1043 // as a return value of a function returning references) may require
1044 // additional work and should cause related fields to be emitted as
1045 // candidates for the --field-filter-file parameter.
1046 auto affected_member_expr_matcher =
1047 memberExpr(member(fieldDecl(hasExplicitFieldDecl(field_decl_matcher))))
1048 .bind("affectedMemberExpr");
1049 auto affected_expr_matcher = ignoringImplicit(affected_member_expr_matcher);
1050
1051 // Places where |.get()| needs to be appended =========
1052 // Given
1053 // void foo(const S& s) {
1054 // printf("%p", s.y);
1055 // const_cast<...>(s.y)
1056 // reinterpret_cast<...>(s.y)
1057 // }
1058 // matches the |s.y| expr if it matches the |affected_expr_matcher| above.
1059 //
1060 // See also testcases in tests/affected-expr-original.cc
1061 auto affected_expr_that_needs_fixing_matcher = expr(allOf(
1062 affected_expr_matcher,
1063 hasParent(expr(anyOf(callExpr(callee(functionDecl(isVariadic()))),
1064 cxxConstCastExpr(), cxxReinterpretCastExpr())))));
1065 AffectedExprRewriter affected_expr_rewriter(&output_helper);
1066 match_finder.addMatcher(affected_expr_that_needs_fixing_matcher,
1067 &affected_expr_rewriter);
1068
1069 // Affected ternary operator args =========
1070 // Given
1071 // void foo(const S& s) {
1072 // cond ? s.y : ...
1073 // }
1074 // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1075 //
1076 // See also testcases in tests/affected-expr-original.cc
1077 auto affected_ternary_operator_arg_matcher =
1078 conditionalOperator(eachOf(hasTrueExpression(affected_expr_matcher),
1079 hasFalseExpression(affected_expr_matcher)));
1080 match_finder.addMatcher(affected_ternary_operator_arg_matcher,
1081 &affected_expr_rewriter);
1082
1083 // Affected string binary operator =========
1084 // Given
1085 // struct S { const char* y; }
1086 // void foo(const S& s) {
1087 // std::string other;
1088 // bool v1 = s.y == other;
1089 // std::string v2 = s.y + other;
1090 // }
1091 // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1092 //
1093 // See also testcases in tests/affected-expr-original.cc
1094 auto std_string_expr_matcher =
1095 expr(hasType(cxxRecordDecl(hasName("::std::basic_string"))));
1096 auto affected_string_binary_operator_arg_matcher = cxxOperatorCallExpr(
1097 hasAnyOverloadedOperatorName("+", "==", "!=", "<", "<=", ">", ">="),
1098 hasAnyArgument(std_string_expr_matcher),
1099 forEachArgumentWithParam(affected_expr_matcher, parmVarDecl()));
1100 match_finder.addMatcher(affected_string_binary_operator_arg_matcher,
1101 &affected_expr_rewriter);
1102
1103 // Calls to templated functions =========
1104 // Given
1105 // struct S { int* y; };
1106 // template <typename T>
1107 // void templatedFunc(T* arg) {}
1108 // void foo(const S& s) {
1109 // templatedFunc(s.y);
1110 // }
1111 // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1112 //
1113 // See also testcases in tests/affected-expr-original.cc
1114 auto templated_function_arg_matcher = forEachArgumentWithParam(
1115 affected_expr_matcher, parmVarDecl(hasType(qualType(allOf(
1116 findAll(qualType(substTemplateTypeParmType())),
1117 unless(referenceType()))))));
1118 match_finder.addMatcher(callExpr(templated_function_arg_matcher),
1119 &affected_expr_rewriter);
1120 // TODO(lukasza): It is unclear why |traverse| below is needed. Maybe it can
1121 // be removed if https://bugs.llvm.org/show_bug.cgi?id=46287 is fixed.
1122 match_finder.addMatcher(
1123 traverse(clang::TraversalKind::TK_AsIs,
1124 cxxConstructExpr(templated_function_arg_matcher)),
1125 &affected_expr_rewriter);
1126
1127 // Calls to constructors via an implicit cast =========
1128 // Given
1129 // struct I { I(int*) {} };
1130 // void bar(I i) {}
1131 // struct S { int* y; };
1132 // void foo(const S& s) {
1133 // bar(s.y); // implicit cast from |s.y| to I.
1134 // }
1135 // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1136 //
1137 // See also testcases in tests/affected-expr-original.cc
1138 auto implicit_ctor_expr_matcher = cxxConstructExpr(allOf(
1139 anyOf(hasParent(materializeTemporaryExpr()),
1140 hasParent(implicitCastExpr())),
1141 hasDeclaration(
1142 cxxConstructorDecl(allOf(parameterCountIs(1), unless(isExplicit())))),
1143 forEachArgumentWithParam(affected_expr_matcher, parmVarDecl())));
1144 match_finder.addMatcher(implicit_ctor_expr_matcher, &affected_expr_rewriter);
1145
1146 // |auto| type declarations =========
1147 // Given
1148 // struct S { int* y; };
1149 // void foo(const S& s) {
1150 // auto* p = s.y;
1151 // }
1152 // binds the |s.y| expr if it matches the |affected_expr_matcher| above.
1153 //
1154 // See also testcases in tests/affected-expr-original.cc
1155 auto auto_var_decl_matcher = declStmt(forEach(
1156 varDecl(allOf(hasType(pointerType(pointee(autoType()))),
1157 hasInitializer(anyOf(
1158 affected_expr_matcher,
1159 initListExpr(hasInit(0, affected_expr_matcher))))))));
1160 match_finder.addMatcher(auto_var_decl_matcher, &affected_expr_rewriter);
1161
1162 // address-of(affected-expr) =========
1163 // Given
1164 // ... &s.y ...
1165 // matches the |s.y| expr if it matches the |affected_member_expr_matcher|
1166 // above.
1167 //
1168 // See also the testcases in tests/gen-in-out-arg-test.cc.
1169 auto affected_addr_of_expr_matcher = expr(allOf(
1170 affected_expr_matcher, hasParent(unaryOperator(hasOperatorName("&")))));
1171 FilteredExprWriter filtered_addr_of_expr_writer(&output_helper, "addr-of");
1172 match_finder.addMatcher(affected_addr_of_expr_matcher,
1173 &filtered_addr_of_expr_writer);
1174
1175 // in-out reference arg =========
1176 // Given
1177 // struct S { SomeClass* ptr_field; };
1178 // void f(SomeClass*& in_out_arg) { ... }
1179 // template <typename T> void f2(T&& rvalue_ref_arg) { ... }
1180 // template <typename... Ts> void f3(Ts&&... rvalue_ref_args) { ... }
1181 // void bar() {
1182 // S s;
1183 // foo(s.ptr_field)
1184 // }
1185 // matches the |s.ptr_field| expr if it matches the
1186 // |affected_member_expr_matcher| and is passed as a function argument that
1187 // has |FooBar*&| type (like |f|, but unlike |f2| and |f3|).
1188 //
1189 // See also the testcases in tests/gen-in-out-arg-test.cc.
1190 auto affected_in_out_ref_arg_matcher = callExpr(forEachArgumentWithParam(
1191 affected_expr_matcher, hasExplicitParmVarDecl(hasType(qualType(
1192 allOf(referenceType(pointee(pointerType())),
1193 unless(rValueReferenceType())))))));
1194 FilteredExprWriter filtered_in_out_ref_arg_writer(&output_helper,
1195 "in-out-param-ref");
1196 match_finder.addMatcher(affected_in_out_ref_arg_matcher,
1197 &filtered_in_out_ref_arg_writer);
1198
1199 // See the doc comment for the overlapsOtherDeclsWithinRecordDecl matcher
1200 // and the testcases in tests/gen-overlaps-test.cc.
1201 auto overlapping_field_decl_matcher = fieldDecl(
1202 allOf(field_decl_matcher, overlapsOtherDeclsWithinRecordDecl()));
1203 FilteredExprWriter overlapping_field_decl_writer(&output_helper,
1204 "overlapping");
1205 match_finder.addMatcher(overlapping_field_decl_matcher,
1206 &overlapping_field_decl_writer);
1207
1208 // Matches fields initialized with a non-nullptr value in a constexpr
1209 // constructor. See also the testcase in tests/gen-constexpr-test.cc.
1210 auto non_nullptr_expr_matcher =
1211 expr(unless(ignoringImplicit(cxxNullPtrLiteralExpr())));
1212 auto constexpr_ctor_field_initializer_matcher = cxxConstructorDecl(
1213 allOf(isConstexpr(), forEachConstructorInitializer(allOf(
1214 forField(field_decl_matcher),
1215 withInitializer(non_nullptr_expr_matcher)))));
1216 FilteredExprWriter constexpr_ctor_field_initializer_writer(
1217 &output_helper, "constexpr-ctor-field-initializer");
1218 match_finder.addMatcher(constexpr_ctor_field_initializer_matcher,
1219 &constexpr_ctor_field_initializer_writer);
1220
1221 // Matches constexpr initializer list expressions that initialize a rewritable
1222 // field with a non-nullptr value. For more details and rationale see the
1223 // testcases in tests/gen-constexpr-test.cc.
1224 auto constexpr_var_initializer_matcher = varDecl(
1225 allOf(isConstexpr(),
1226 hasInitializer(findAll(initListExpr(forEachInitExprWithFieldDecl(
1227 non_nullptr_expr_matcher,
1228 hasExplicitFieldDecl(field_decl_matcher)))))));
1229 FilteredExprWriter constexpr_var_initializer_writer(
1230 &output_helper, "constexpr-var-initializer");
1231 match_finder.addMatcher(constexpr_var_initializer_matcher,
1232 &constexpr_var_initializer_writer);
1233
1234 // See the doc comment for the isInMacroLocation matcher
1235 // and the testcases in tests/gen-macro-test.cc.
1236 auto macro_field_decl_matcher =
1237 fieldDecl(allOf(field_decl_matcher, isInMacroLocation()));
1238 FilteredExprWriter macro_field_decl_writer(&output_helper, "macro");
1239 match_finder.addMatcher(macro_field_decl_matcher, ¯o_field_decl_writer);
1240
1241 // See the doc comment for the anyCharType matcher
1242 // and the testcases in tests/gen-char-test.cc.
1243 auto char_ptr_field_decl_matcher = fieldDecl(allOf(
1244 field_decl_matcher,
1245 hasType(pointerType(pointee(qualType(allOf(
1246 isConstQualified(), hasUnqualifiedDesugaredType(anyCharType()))))))));
1247 FilteredExprWriter char_ptr_field_decl_writer(&output_helper, "const-char");
1248 match_finder.addMatcher(char_ptr_field_decl_matcher,
1249 &char_ptr_field_decl_writer);
1250
1251 // See the testcases in tests/gen-global-destructor-test.cc.
1252 auto global_destructor_matcher =
1253 varDecl(allOf(hasGlobalStorage(),
1254 hasType(typeWithEmbeddedFieldDecl(field_decl_matcher))));
1255 FilteredExprWriter global_destructor_writer(&output_helper, "global-scope");
1256 match_finder.addMatcher(global_destructor_matcher, &global_destructor_writer);
1257
1258 // Matches fields in unions (both directly rewritable fields as well as union
1259 // fields that embed a struct that contains a rewritable field). See also the
1260 // testcases in tests/gen-unions-test.cc.
1261 auto union_field_decl_matcher = recordDecl(allOf(
1262 isUnion(), forEach(fieldDecl(anyOf(field_decl_matcher,
1263 hasType(typeWithEmbeddedFieldDecl(
1264 field_decl_matcher)))))));
1265 FilteredExprWriter union_field_decl_writer(&output_helper, "union");
1266 match_finder.addMatcher(union_field_decl_matcher, &union_field_decl_writer);
1267
1268 // Matches rewritable fields of struct `SomeStruct` if that struct happens to
1269 // be a destination type of a `reinterpret_cast<SomeStruct*>` cast and is a
1270 // trivial type (otherwise `reinterpret_cast<SomeStruct*>` wouldn't be valid
1271 // before the rewrite if it skipped non-trivial constructors).
1272 auto reinterpret_cast_struct_matcher =
1273 cxxReinterpretCastExpr(hasDestinationType(pointerType(pointee(
1274 hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
1275 allOf(forEach(field_decl_matcher), isTrivial())))))))));
1276 FilteredExprWriter reinterpret_cast_struct_writer(
1277 &output_helper, "reinterpret-cast-trivial-type");
1278 match_finder.addMatcher(reinterpret_cast_struct_matcher,
1279 &reinterpret_cast_struct_writer);
1280
1281 // Prepare and run the tool.
1282 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
1283 clang::tooling::newFrontendActionFactory(&match_finder, &output_helper);
1284 int result = tool.run(factory.get());
1285 if (result != 0)
1286 return result;
1287
1288 return 0;
1289 }
1290