1 // Copyright 2014 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.
5 // This clang plugin checks various invariants of the Blink garbage
6 // collection infrastructure.
8 // Errors are described at:
9 // http://www.chromium.org/developers/blink-gc-plugin-errors
12 #include "JsonWriter.h"
13 #include "RecordInfo.h"
15 #include "clang/AST/AST.h"
16 #include "clang/AST/ASTConsumer.h"
17 #include "clang/AST/RecursiveASTVisitor.h"
18 #include "clang/Frontend/CompilerInstance.h"
19 #include "clang/Frontend/FrontendPluginRegistry.h"
21 using namespace clang;
26 const char kClassMustLeftMostlyDeriveGC[] =
27 "[blink-gc] Class %0 must derive its GC base in the left-most position.";
29 const char kClassRequiresTraceMethod[] =
30 "[blink-gc] Class %0 requires a trace method"
31 " because it contains fields that require tracing.";
33 const char kBaseRequiresTracing[] =
34 "[blink-gc] Base class %0 of derived class %1 requires tracing.";
36 const char kFieldsRequireTracing[] =
37 "[blink-gc] Class %0 has untraced fields that require tracing.";
39 const char kFieldRequiresTracingNote[] =
40 "[blink-gc] Untraced field %0 declared here:";
42 const char kClassContainsInvalidFields[] =
43 "[blink-gc] Class %0 contains invalid fields.";
45 const char kClassContainsGCRoot[] =
46 "[blink-gc] Class %0 contains GC root in field %1.";
48 const char kClassRequiresFinalization[] =
49 "[blink-gc] Class %0 requires finalization.";
51 const char kFinalizerAccessesFinalizedField[] =
52 "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
54 const char kRawPtrToGCManagedClassNote[] =
55 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
57 const char kRefPtrToGCManagedClassNote[] =
58 "[blink-gc] RefPtr field %0 to a GC managed class declared here:";
60 const char kOwnPtrToGCManagedClassNote[] =
61 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
63 const char kStackAllocatedFieldNote[] =
64 "[blink-gc] Stack-allocated field %0 declared here:";
66 const char kMemberInUnmanagedClassNote[] =
67 "[blink-gc] Member field %0 in unmanaged class declared here:";
69 const char kPartObjectContainsGCRoot[] =
70 "[blink-gc] Field %0 with embedded GC root in %1 declared here:";
72 const char kFieldContainsGCRoot[] =
73 "[blink-gc] Field %0 defining a GC root declared here:";
75 const char kOverriddenNonVirtualTrace[] =
76 "[blink-gc] Class %0 overrides non-virtual trace of base class %1.";
78 const char kOverriddenNonVirtualTraceNote[] =
79 "[blink-gc] Non-virtual trace method declared here:";
81 const char kMissingTraceDispatchMethod[] =
82 "[blink-gc] Class %0 is missing manual trace dispatch.";
84 const char kMissingFinalizeDispatchMethod[] =
85 "[blink-gc] Class %0 is missing manual finalize dispatch.";
87 const char kVirtualAndManualDispatch[] =
88 "[blink-gc] Class %0 contains or inherits virtual methods"
89 " but implements manual dispatching.";
91 const char kMissingTraceDispatch[] =
92 "[blink-gc] Missing dispatch to class %0 in manual trace dispatch.";
94 const char kMissingFinalizeDispatch[] =
95 "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch.";
97 const char kFinalizedFieldNote[] =
98 "[blink-gc] Potentially finalized field %0 declared here:";
100 const char kUserDeclaredDestructorNote[] =
101 "[blink-gc] User-declared destructor declared here:";
103 const char kUserDeclaredFinalizerNote[] =
104 "[blink-gc] User-declared finalizer declared here:";
106 const char kBaseRequiresFinalizationNote[] =
107 "[blink-gc] Base class %0 requiring finalization declared here:";
109 const char kFieldRequiresFinalizationNote[] =
110 "[blink-gc] Field %0 requiring finalization declared here:";
112 const char kManualDispatchMethodNote[] =
113 "[blink-gc] Manual dispatch %0 declared here:";
115 const char kDerivesNonStackAllocated[] =
116 "[blink-gc] Stack-allocated class %0 derives class %1"
117 " which is not stack allocated.";
119 const char kClassOverridesNew[] =
120 "[blink-gc] Garbage collected class %0"
121 " is not permitted to override its new operator.";
123 struct BlinkGCPluginOptions {
124 BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {}
127 std::set<std::string> ignored_classes;
128 std::set<std::string> checked_namespaces;
129 std::vector<std::string> ignored_directories;
132 typedef std::vector<CXXRecordDecl*> RecordVector;
133 typedef std::vector<CXXMethodDecl*> MethodVector;
135 // Test if a template specialization is an instantiation.
136 static bool IsTemplateInstantiation(CXXRecordDecl* record) {
137 ClassTemplateSpecializationDecl* spec =
138 dyn_cast<ClassTemplateSpecializationDecl>(record);
141 switch (spec->getTemplateSpecializationKind()) {
142 case TSK_ImplicitInstantiation:
143 case TSK_ExplicitInstantiationDefinition:
146 case TSK_ExplicitSpecialization:
148 // TODO: unsupported cases.
149 case TSK_ExplicitInstantiationDeclaration:
152 assert(false && "Unknown template specialization kind");
155 // This visitor collects the entry points for the checker.
156 class CollectVisitor : public RecursiveASTVisitor<CollectVisitor> {
160 RecordVector& record_decls() { return record_decls_; }
161 MethodVector& trace_decls() { return trace_decls_; }
163 bool shouldVisitTemplateInstantiations() { return false; }
165 // Collect record declarations, including nested declarations.
166 bool VisitCXXRecordDecl(CXXRecordDecl* record) {
167 if (record->hasDefinition() && record->isCompleteDefinition())
168 record_decls_.push_back(record);
172 // Collect tracing method definitions, but don't traverse method bodies.
173 bool TraverseCXXMethodDecl(CXXMethodDecl* method) {
174 if (method->isThisDeclarationADefinition() && Config::IsTraceMethod(method))
175 trace_decls_.push_back(method);
180 RecordVector record_decls_;
181 MethodVector trace_decls_;
184 // This visitor checks that a finalizer method does not have invalid access to
185 // fields that are potentially finalized. A potentially finalized field is
186 // either a Member, a heap-allocated collection or an off-heap collection that
187 // contains Members. Invalid uses are currently identified as passing the field
188 // as the argument of a procedure call or using the -> or [] operators on it.
189 class CheckFinalizerVisitor
190 : public RecursiveASTVisitor<CheckFinalizerVisitor> {
192 // Simple visitor to determine if the content of a field might be collected
193 // during finalization.
194 class MightBeCollectedVisitor : public EdgeVisitor {
196 MightBeCollectedVisitor() : might_be_collected_(false) {}
197 bool might_be_collected() { return might_be_collected_; }
198 void VisitMember(Member* edge) override { might_be_collected_ = true; }
199 void VisitCollection(Collection* edge) override {
200 if (edge->on_heap()) {
201 might_be_collected_ = !edge->is_root();
203 edge->AcceptMembers(this);
208 bool might_be_collected_;
212 typedef std::vector<std::pair<MemberExpr*, FieldPoint*> > Errors;
214 CheckFinalizerVisitor(RecordCache* cache)
215 : blacklist_context_(false), cache_(cache) {}
217 Errors& finalized_fields() { return finalized_fields_; }
219 bool WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr* expr) {
220 // Only continue the walk-up if the operator is a blacklisted one.
221 switch (expr->getOperator()) {
224 this->WalkUpFromCallExpr(expr);
230 // We consider all non-operator calls to be blacklisted contexts.
231 bool WalkUpFromCallExpr(CallExpr* expr) {
232 bool prev_blacklist_context = blacklist_context_;
233 blacklist_context_ = true;
234 for (size_t i = 0; i < expr->getNumArgs(); ++i)
235 this->TraverseStmt(expr->getArg(i));
236 blacklist_context_ = prev_blacklist_context;
240 bool VisitMemberExpr(MemberExpr* member) {
241 FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl());
245 RecordInfo* info = cache_->Lookup(field->getParent());
249 RecordInfo::Fields::iterator it = info->GetFields().find(field);
250 if (it == info->GetFields().end())
253 if (blacklist_context_ && MightBeCollected(&it->second))
254 finalized_fields_.push_back(std::make_pair(member, &it->second));
258 bool MightBeCollected(FieldPoint* point) {
259 MightBeCollectedVisitor visitor;
260 point->edge()->Accept(&visitor);
261 return visitor.might_be_collected();
265 bool blacklist_context_;
266 Errors finalized_fields_;
270 // This visitor checks that a method contains within its body, a call to a
271 // method on the provided receiver class. This is used to check manual
272 // dispatching for trace and finalize methods.
273 class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> {
275 CheckDispatchVisitor(RecordInfo* receiver)
276 : receiver_(receiver), dispatched_to_receiver_(false) {}
278 bool dispatched_to_receiver() { return dispatched_to_receiver_; }
280 bool VisitMemberExpr(MemberExpr* member) {
281 if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) {
282 if (fn->getParent() == receiver_->record())
283 dispatched_to_receiver_ = true;
289 RecordInfo* receiver_;
290 bool dispatched_to_receiver_;
293 // This visitor checks a tracing method by traversing its body.
294 // - A member field is considered traced if it is referenced in the body.
295 // - A base is traced if a base-qualified call to a trace method is found.
296 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> {
298 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info)
299 : trace_(trace), info_(info) {}
301 // Allow recursive traversal by using VisitMemberExpr.
302 bool VisitMemberExpr(MemberExpr* member) {
303 // If this member expression references a field decl, mark it as traced.
304 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) {
305 if (IsTemplateInstantiation(info_->record())) {
306 // Pointer equality on fields does not work for template instantiations.
307 // The trace method refers to fields of the template definition which
308 // are different from the instantiated fields that need to be traced.
309 const string& name = field->getNameAsString();
310 for (RecordInfo::Fields::iterator it = info_->GetFields().begin();
311 it != info_->GetFields().end();
313 if (it->first->getNameAsString() == name) {
319 RecordInfo::Fields::iterator it = info_->GetFields().find(field);
320 if (it != info_->GetFields().end())
326 // If this is a weak callback function we only check field tracing.
327 if (IsWeakCallback())
330 // For method calls, check tracing of bases and other special GC methods.
331 if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) {
332 const string& name = fn->getNameAsString();
333 // Check weak callbacks.
334 if (name == kRegisterWeakMembersName) {
335 if (fn->isTemplateInstantiation()) {
336 const TemplateArgumentList& args =
337 *fn->getTemplateSpecializationInfo()->TemplateArguments;
338 // The second template argument is the callback method.
339 if (args.size() > 1 &&
340 args[1].getKind() == TemplateArgument::Declaration) {
341 if (FunctionDecl* callback =
342 dyn_cast<FunctionDecl>(args[1].getAsDecl())) {
343 if (callback->hasBody()) {
344 CheckTraceVisitor nested_visitor(info_);
345 nested_visitor.TraverseStmt(callback->getBody());
353 // TODO: It is possible to have multiple bases, where one must be traced
354 // using a traceAfterDispatch. In such a case we should also check that
355 // the mixin does not add a vtable.
356 if (Config::IsTraceMethod(fn) && member->hasQualifier()) {
357 if (const Type* type = member->getQualifier()->getAsType()) {
358 if (CXXRecordDecl* decl = type->getAsCXXRecordDecl()) {
359 RecordInfo::Bases::iterator it = info_->GetBases().find(decl);
360 if (it != info_->GetBases().end())
361 it->second.MarkTraced();
370 // Nested checking for weak callbacks.
371 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {}
373 bool IsWeakCallback() { return !trace_; }
375 void MarkTraced(RecordInfo::Fields::iterator it) {
376 // In a weak callback we can't mark strong fields as traced.
377 if (IsWeakCallback() && !it->second.edge()->IsWeakMember())
379 it->second.MarkTraced();
382 CXXMethodDecl* trace_;
386 // This visitor checks that the fields of a class and the fields of
387 // its part objects don't define GC roots.
388 class CheckGCRootsVisitor : public RecursiveEdgeVisitor {
390 typedef std::vector<FieldPoint*> RootPath;
391 typedef std::vector<RootPath> Errors;
393 CheckGCRootsVisitor() {}
395 Errors& gc_roots() { return gc_roots_; }
397 bool ContainsGCRoots(RecordInfo* info) {
398 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
399 it != info->GetFields().end();
401 current_.push_back(&it->second);
402 it->second.edge()->Accept(this);
405 return !gc_roots_.empty();
408 void VisitValue(Value* edge) override {
409 // TODO: what should we do to check unions?
410 if (edge->value()->record()->isUnion())
413 // If the value is a part object, then continue checking for roots.
414 for (Context::iterator it = context().begin();
415 it != context().end();
417 if (!(*it)->IsCollection())
420 ContainsGCRoots(edge->value());
423 void VisitPersistent(Persistent* edge) override {
424 gc_roots_.push_back(current_);
427 void AtCollection(Collection* edge) override {
429 gc_roots_.push_back(current_);
437 // This visitor checks that the fields of a class are "well formed".
438 // - OwnPtr, RefPtr and RawPtr must not point to a GC derived types.
439 // - An on-heap class must never contain GC roots.
440 // - Only stack-allocated types may point to stack-allocated types.
441 class CheckFieldsVisitor : public RecursiveEdgeVisitor {
443 typedef std::vector<std::pair<FieldPoint*, Edge*> > Errors;
445 CheckFieldsVisitor(const BlinkGCPluginOptions& options)
446 : options_(options), current_(0), stack_allocated_host_(false) {}
448 Errors& invalid_fields() { return invalid_fields_; }
450 bool ContainsInvalidFields(RecordInfo* info) {
451 stack_allocated_host_ = info->IsStackAllocated();
452 managed_host_ = stack_allocated_host_ ||
453 info->IsGCAllocated() ||
454 info->IsNonNewable() ||
455 info->IsOnlyPlacementNewable();
456 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
457 it != info->GetFields().end();
460 current_ = &it->second;
461 current_->edge()->Accept(this);
463 return !invalid_fields_.empty();
466 void VisitMember(Member* edge) override {
469 // A member is allowed to appear in the context of a root.
470 for (Context::iterator it = context().begin();
471 it != context().end();
473 if ((*it)->Kind() == Edge::kRoot)
476 invalid_fields_.push_back(std::make_pair(current_, edge));
479 void VisitValue(Value* edge) override {
480 // TODO: what should we do to check unions?
481 if (edge->value()->record()->isUnion())
484 if (!stack_allocated_host_ && edge->value()->IsStackAllocated()) {
485 invalid_fields_.push_back(std::make_pair(current_, edge));
489 if (!Parent() || !edge->value()->IsGCAllocated())
492 // In transition mode, disallow OwnPtr<T>, RawPtr<T> to GC allocated T's,
493 // also disallow T* in stack-allocated types.
494 if (options_.enable_oilpan) {
495 if (Parent()->IsOwnPtr() ||
496 Parent()->IsRawPtrClass() ||
497 (stack_allocated_host_ && Parent()->IsRawPtr() &&
498 // TODO: Remove this exception once the node hierarchy is moved.
499 !edge->value()->IsTreeShared())) {
500 invalid_fields_.push_back(std::make_pair(current_, Parent()));
507 if (Parent()->IsRawPtr() || Parent()->IsRefPtr() || Parent()->IsOwnPtr()) {
508 invalid_fields_.push_back(std::make_pair(current_, Parent()));
514 const BlinkGCPluginOptions& options_;
515 FieldPoint* current_;
516 bool stack_allocated_host_;
518 Errors invalid_fields_;
521 // Main class containing checks for various invariants of the Blink
522 // garbage collection infrastructure.
523 class BlinkGCPluginConsumer : public ASTConsumer {
525 BlinkGCPluginConsumer(CompilerInstance& instance,
526 const BlinkGCPluginOptions& options)
527 : instance_(instance),
528 diagnostic_(instance.getDiagnostics()),
532 // Only check structures in the blink, WebCore and WebKit namespaces.
533 options_.checked_namespaces.insert("blink");
534 options_.checked_namespaces.insert("WebCore");
535 options_.checked_namespaces.insert("WebKit");
537 // Ignore GC implementation files.
538 options_.ignored_directories.push_back("/heap/");
540 // Register warning/error messages.
541 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID(
542 getErrorLevel(), kClassMustLeftMostlyDeriveGC);
543 diag_class_requires_trace_method_ =
544 diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod);
545 diag_base_requires_tracing_ =
546 diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing);
547 diag_fields_require_tracing_ =
548 diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing);
549 diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID(
550 getErrorLevel(), kClassContainsInvalidFields);
551 diag_class_contains_gc_root_ =
552 diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
553 diag_class_requires_finalization_ = diagnostic_.getCustomDiagID(
554 getErrorLevel(), kClassRequiresFinalization);
555 diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
556 getErrorLevel(), kFinalizerAccessesFinalizedField);
557 diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID(
558 getErrorLevel(), kOverriddenNonVirtualTrace);
559 diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID(
560 getErrorLevel(), kMissingTraceDispatchMethod);
561 diag_missing_finalize_dispatch_method_ = diagnostic_.getCustomDiagID(
562 getErrorLevel(), kMissingFinalizeDispatchMethod);
563 diag_virtual_and_manual_dispatch_ =
564 diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch);
565 diag_missing_trace_dispatch_ =
566 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch);
567 diag_missing_finalize_dispatch_ =
568 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch);
569 diag_derives_non_stack_allocated_ =
570 diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated);
571 diag_class_overrides_new_ =
572 diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew);
574 // Register note messages.
575 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
576 DiagnosticsEngine::Note, kFieldRequiresTracingNote);
577 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
578 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote);
579 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
580 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote);
581 diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
582 DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote);
583 diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID(
584 DiagnosticsEngine::Note, kStackAllocatedFieldNote);
585 diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
586 DiagnosticsEngine::Note, kMemberInUnmanagedClassNote);
587 diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
588 DiagnosticsEngine::Note, kPartObjectContainsGCRoot);
589 diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
590 DiagnosticsEngine::Note, kFieldContainsGCRoot);
591 diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
592 DiagnosticsEngine::Note, kFinalizedFieldNote);
593 diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID(
594 DiagnosticsEngine::Note, kUserDeclaredDestructorNote);
595 diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID(
596 DiagnosticsEngine::Note, kUserDeclaredFinalizerNote);
597 diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID(
598 DiagnosticsEngine::Note, kBaseRequiresFinalizationNote);
599 diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID(
600 DiagnosticsEngine::Note, kFieldRequiresFinalizationNote);
601 diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID(
602 DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote);
603 diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID(
604 DiagnosticsEngine::Note, kManualDispatchMethodNote);
607 void HandleTranslationUnit(ASTContext& context) override {
608 CollectVisitor visitor;
609 visitor.TraverseDecl(context.getTranslationUnitDecl());
611 if (options_.dump_graph) {
613 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
614 json_ = JsonWriter::from(instance_.createOutputFile(
618 true, // RemoveFileOnSignal
619 instance_.getFrontendOpts().OutputFile, // BaseInput
620 "graph.json", // Extension
621 false, // UseTemporary
622 false, // CreateMissingDirectories
625 if (err.empty() && json_) {
631 << "Failed to create an output file for the object graph.\n";
635 for (RecordVector::iterator it = visitor.record_decls().begin();
636 it != visitor.record_decls().end();
638 CheckRecord(cache_.Lookup(*it));
641 for (MethodVector::iterator it = visitor.trace_decls().begin();
642 it != visitor.trace_decls().end();
644 CheckTracingMethod(*it);
654 // Main entry for checking a record declaration.
655 void CheckRecord(RecordInfo* info) {
659 CXXRecordDecl* record = info->record();
661 // TODO: what should we do to check unions?
662 if (record->isUnion())
665 // If this is the primary template declaration, check its specializations.
666 if (record->isThisDeclarationADefinition() &&
667 record->getDescribedClassTemplate()) {
668 ClassTemplateDecl* tmpl = record->getDescribedClassTemplate();
669 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
670 it != tmpl->spec_end();
672 CheckClass(cache_.Lookup(*it));
680 // Check a class-like object (eg, class, specialization, instantiation).
681 void CheckClass(RecordInfo* info) {
685 // Check consistency of stack-allocated hierarchies.
686 if (info->IsStackAllocated()) {
687 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
688 it != info->GetBases().end();
690 if (!it->second.info()->IsStackAllocated())
691 ReportDerivesNonStackAllocated(info, &it->second);
695 if (info->RequiresTraceMethod() && !info->GetTraceMethod())
696 ReportClassRequiresTraceMethod(info);
699 CheckFieldsVisitor visitor(options_);
700 if (visitor.ContainsInvalidFields(info))
701 ReportClassContainsInvalidFields(info, &visitor.invalid_fields());
704 if (info->IsGCDerived()) {
705 CheckLeftMostDerived(info);
709 if (CXXMethodDecl* newop = info->DeclaresNewOperator())
710 ReportClassOverridesNew(info, newop);
712 // TODO: Remove this exception once TreeShared is properly traced.
713 if (!info->IsTreeShared()) {
714 CheckGCRootsVisitor visitor;
715 if (visitor.ContainsGCRoots(info))
716 ReportClassContainsGCRoots(info, &visitor.gc_roots());
719 if (info->NeedsFinalization())
720 CheckFinalization(info);
726 void CheckLeftMostDerived(RecordInfo* info) {
727 CXXRecordDecl* left_most = info->record();
728 CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
729 while (it != left_most->bases_end()) {
730 left_most = it->getType()->getAsCXXRecordDecl();
731 it = left_most->bases_begin();
733 if (!Config::IsGCBase(left_most->getName()))
734 ReportClassMustLeftMostlyDeriveGC(info);
737 void CheckDispatch(RecordInfo* info) {
738 bool finalized = info->IsGCFinalized();
739 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
740 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
741 if (!trace_dispatch && !finalize_dispatch)
744 CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent()
745 : finalize_dispatch->getParent();
747 // Check that dispatch methods are defined at the base.
748 if (base == info->record()) {
750 ReportMissingTraceDispatchMethod(info);
751 if (finalized && !finalize_dispatch)
752 ReportMissingFinalizeDispatchMethod(info);
753 if (!finalized && finalize_dispatch) {
754 ReportClassRequiresFinalization(info);
755 NoteUserDeclaredFinalizer(finalize_dispatch);
759 // Check that classes implementing manual dispatch do not have vtables.
760 if (info->record()->isPolymorphic())
761 ReportVirtualAndManualDispatch(
762 info, trace_dispatch ? trace_dispatch : finalize_dispatch);
764 // If this is a non-abstract class check that it is dispatched to.
765 // TODO: Create a global variant of this local check. We can only check if
766 // the dispatch body is known in this compilation unit.
767 if (info->IsConsideredAbstract())
770 const FunctionDecl* defn;
772 if (trace_dispatch && trace_dispatch->isDefined(defn)) {
773 CheckDispatchVisitor visitor(info);
774 visitor.TraverseStmt(defn->getBody());
775 if (!visitor.dispatched_to_receiver())
776 ReportMissingTraceDispatch(defn, info);
779 if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) {
780 CheckDispatchVisitor visitor(info);
781 visitor.TraverseStmt(defn->getBody());
782 if (!visitor.dispatched_to_receiver())
783 ReportMissingFinalizeDispatch(defn, info);
787 // TODO: Should we collect destructors similar to trace methods?
788 void CheckFinalization(RecordInfo* info) {
789 CXXDestructorDecl* dtor = info->record()->getDestructor();
791 // For finalized classes, check the finalization method if possible.
792 if (info->IsGCFinalized()) {
793 if (dtor && dtor->hasBody()) {
794 CheckFinalizerVisitor visitor(&cache_);
795 visitor.TraverseCXXMethodDecl(dtor);
796 if (!visitor.finalized_fields().empty()) {
797 ReportFinalizerAccessesFinalizedFields(
798 dtor, &visitor.finalized_fields());
804 // Don't require finalization of a mixin that has not yet been "mixed in".
805 if (info->IsGCMixin())
808 // Report the finalization error, and proceed to print possible causes for
809 // the finalization requirement.
810 ReportClassRequiresFinalization(info);
812 if (dtor && dtor->isUserProvided())
813 NoteUserDeclaredDestructor(dtor);
815 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
816 it != info->GetBases().end();
818 if (it->second.info()->NeedsFinalization())
819 NoteBaseRequiresFinalization(&it->second);
822 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
823 it != info->GetFields().end();
825 if (it->second.edge()->NeedsFinalization())
826 NoteField(&it->second, diag_field_requires_finalization_note_);
830 // This is the main entry for tracing method definitions.
831 void CheckTracingMethod(CXXMethodDecl* method) {
832 RecordInfo* parent = cache_.Lookup(method->getParent());
833 if (IsIgnored(parent))
836 // Check templated tracing methods by checking the template instantiations.
837 // Specialized templates are handled as ordinary classes.
838 if (ClassTemplateDecl* tmpl =
839 parent->record()->getDescribedClassTemplate()) {
840 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
841 it != tmpl->spec_end();
843 // Check trace using each template instantiation as the holder.
844 if (IsTemplateInstantiation(*it))
845 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
850 CheckTraceOrDispatchMethod(parent, method);
853 // Determine what type of tracing method this is (dispatch or trace).
854 void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) {
855 bool isTraceAfterDispatch;
856 if (Config::IsTraceMethod(method, &isTraceAfterDispatch)) {
857 if (isTraceAfterDispatch || !parent->GetTraceDispatchMethod()) {
858 CheckTraceMethod(parent, method, isTraceAfterDispatch);
860 // Dispatch methods are checked when we identify subclasses.
864 // Check an actual trace method.
865 void CheckTraceMethod(RecordInfo* parent,
866 CXXMethodDecl* trace,
867 bool isTraceAfterDispatch) {
868 // A non-virtual trace method must not override another trace.
869 if (!isTraceAfterDispatch && !trace->isVirtual()) {
870 for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
871 it != parent->GetBases().end();
873 RecordInfo* base = it->second.info();
874 // We allow mixin bases to contain a non-virtual trace since it will
875 // never be used for dispatching.
876 if (base->IsGCMixin())
878 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace())
879 ReportOverriddenNonVirtualTrace(parent, trace, other);
883 CheckTraceVisitor visitor(trace, parent);
884 visitor.TraverseCXXMethodDecl(trace);
886 for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
887 it != parent->GetBases().end();
889 if (!it->second.IsProperlyTraced())
890 ReportBaseRequiresTracing(parent, trace, it->first);
893 for (RecordInfo::Fields::iterator it = parent->GetFields().begin();
894 it != parent->GetFields().end();
896 if (!it->second.IsProperlyTraced()) {
897 // Discontinue once an untraced-field error is found.
898 ReportFieldsRequireTracing(parent, trace);
904 void DumpClass(RecordInfo* info) {
909 json_->Write("name", info->record()->getQualifiedNameAsString());
910 json_->Write("loc", GetLocString(info->record()->getLocStart()));
911 json_->CloseObject();
913 class DumpEdgeVisitor : public RecursiveEdgeVisitor {
915 DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
916 void DumpEdge(RecordInfo* src,
919 const Edge::LivenessKind& kind,
922 json_->Write("src", src->record()->getQualifiedNameAsString());
923 json_->Write("dst", dst->record()->getQualifiedNameAsString());
924 json_->Write("lbl", lbl);
925 json_->Write("kind", kind);
926 json_->Write("loc", loc);
929 Parent()->IsRawPtr() ? "raw" :
930 Parent()->IsRefPtr() ? "ref" :
931 Parent()->IsOwnPtr() ? "own" :
932 (Parent()->IsMember() ||
933 Parent()->IsWeakMember()) ? "mem" :
935 json_->CloseObject();
938 void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) {
942 point_->edge()->Accept(this);
945 void AtValue(Value* e) override {
946 // The liveness kind of a path from the point to this value
947 // is given by the innermost place that is non-strong.
948 Edge::LivenessKind kind = Edge::kStrong;
949 if (Config::IsIgnoreCycleAnnotated(point_->field())) {
952 for (Context::iterator it = context().begin();
953 it != context().end();
955 Edge::LivenessKind pointer_kind = (*it)->Kind();
956 if (pointer_kind != Edge::kStrong) {
963 src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
973 DumpEdgeVisitor visitor(json_);
975 RecordInfo::Bases& bases = info->GetBases();
976 for (RecordInfo::Bases::iterator it = bases.begin();
979 visitor.DumpEdge(info,
983 GetLocString(it->second.spec().getLocStart()));
986 RecordInfo::Fields& fields = info->GetFields();
987 for (RecordInfo::Fields::iterator it = fields.begin();
990 visitor.DumpField(info,
992 GetLocString(it->second.field()->getLocStart()));
996 // Adds either a warning or error, based on the current handling of -Werror.
997 DiagnosticsEngine::Level getErrorLevel() {
998 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error
999 : DiagnosticsEngine::Warning;
1002 const string GetLocString(SourceLocation loc) {
1003 const SourceManager& source_manager = instance_.getSourceManager();
1004 PresumedLoc ploc = source_manager.getPresumedLoc(loc);
1005 if (ploc.isInvalid())
1008 llvm::raw_string_ostream OS(loc_str);
1009 OS << ploc.getFilename()
1010 << ":" << ploc.getLine()
1011 << ":" << ploc.getColumn();
1015 bool IsIgnored(RecordInfo* record) {
1017 !InCheckedNamespace(record) ||
1018 IsIgnoredClass(record) ||
1019 InIgnoredDirectory(record);
1022 bool IsIgnoredClass(RecordInfo* info) {
1023 // Ignore any class prefixed by SameSizeAs. These are used in
1024 // Blink to verify class sizes and don't need checking.
1025 const string SameSizeAs = "SameSizeAs";
1026 if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0)
1028 return options_.ignored_classes.find(info->name()) !=
1029 options_.ignored_classes.end();
1032 bool InIgnoredDirectory(RecordInfo* info) {
1034 if (!GetFilename(info->record()->getLocStart(), &filename))
1035 return false; // TODO: should we ignore non-existing file locations?
1036 std::vector<string>::iterator it = options_.ignored_directories.begin();
1037 for (; it != options_.ignored_directories.end(); ++it)
1038 if (filename.find(*it) != string::npos)
1043 bool InCheckedNamespace(RecordInfo* info) {
1046 DeclContext* context = info->record()->getDeclContext();
1047 if (context->isRecord())
1048 return InCheckedNamespace(cache_.Lookup(context));
1049 if (context->isNamespace()) {
1050 const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context);
1051 if (decl->isAnonymousNamespace())
1053 return options_.checked_namespaces.find(decl->getNameAsString()) !=
1054 options_.checked_namespaces.end();
1059 bool GetFilename(SourceLocation loc, string* filename) {
1060 const SourceManager& source_manager = instance_.getSourceManager();
1061 SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
1062 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
1063 if (ploc.isInvalid()) {
1064 // If we're in an invalid location, we're looking at things that aren't
1065 // actually stated in the source.
1068 *filename = ploc.getFilename();
1072 void ReportClassMustLeftMostlyDeriveGC(RecordInfo* info) {
1073 SourceLocation loc = info->record()->getInnerLocStart();
1074 SourceManager& manager = instance_.getSourceManager();
1075 FullSourceLoc full_loc(loc, manager);
1076 diagnostic_.Report(full_loc, diag_class_must_left_mostly_derive_gc_)
1080 void ReportClassRequiresTraceMethod(RecordInfo* info) {
1081 SourceLocation loc = info->record()->getInnerLocStart();
1082 SourceManager& manager = instance_.getSourceManager();
1083 FullSourceLoc full_loc(loc, manager);
1084 diagnostic_.Report(full_loc, diag_class_requires_trace_method_)
1086 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1087 it != info->GetFields().end();
1089 if (!it->second.IsProperlyTraced())
1090 NoteFieldRequiresTracing(info, it->first);
1094 void ReportBaseRequiresTracing(RecordInfo* derived,
1095 CXXMethodDecl* trace,
1096 CXXRecordDecl* base) {
1097 SourceLocation loc = trace->getLocStart();
1098 SourceManager& manager = instance_.getSourceManager();
1099 FullSourceLoc full_loc(loc, manager);
1100 diagnostic_.Report(full_loc, diag_base_requires_tracing_)
1101 << base << derived->record();
1104 void ReportFieldsRequireTracing(RecordInfo* info, CXXMethodDecl* trace) {
1105 SourceLocation loc = trace->getLocStart();
1106 SourceManager& manager = instance_.getSourceManager();
1107 FullSourceLoc full_loc(loc, manager);
1108 diagnostic_.Report(full_loc, diag_fields_require_tracing_)
1110 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1111 it != info->GetFields().end();
1113 if (!it->second.IsProperlyTraced())
1114 NoteFieldRequiresTracing(info, it->first);
1118 void ReportClassContainsInvalidFields(RecordInfo* info,
1119 CheckFieldsVisitor::Errors* errors) {
1120 SourceLocation loc = info->record()->getLocStart();
1121 SourceManager& manager = instance_.getSourceManager();
1122 FullSourceLoc full_loc(loc, manager);
1123 diagnostic_.Report(full_loc, diag_class_contains_invalid_fields_)
1125 for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
1126 it != errors->end();
1128 if (it->second->IsRawPtr()) {
1129 NoteField(it->first, diag_raw_ptr_to_gc_managed_class_note_);
1130 } else if (it->second->IsRefPtr()) {
1131 NoteField(it->first, diag_ref_ptr_to_gc_managed_class_note_);
1132 } else if (it->second->IsOwnPtr()) {
1133 NoteField(it->first, diag_own_ptr_to_gc_managed_class_note_);
1134 } else if (it->second->IsMember()) {
1135 NoteField(it->first, diag_member_in_unmanaged_class_note_);
1136 } else if (it->second->IsValue()) {
1137 NoteField(it->first, diag_stack_allocated_field_note_);
1142 void ReportClassContainsGCRoots(RecordInfo* info,
1143 CheckGCRootsVisitor::Errors* errors) {
1144 SourceLocation loc = info->record()->getLocStart();
1145 SourceManager& manager = instance_.getSourceManager();
1146 FullSourceLoc full_loc(loc, manager);
1147 for (CheckGCRootsVisitor::Errors::iterator it = errors->begin();
1148 it != errors->end();
1150 CheckGCRootsVisitor::RootPath::iterator path = it->begin();
1151 FieldPoint* point = *path;
1152 diagnostic_.Report(full_loc, diag_class_contains_gc_root_)
1153 << info->record() << point->field();
1154 while (++path != it->end()) {
1155 NotePartObjectContainsGCRoot(point);
1158 NoteFieldContainsGCRoot(point);
1162 void ReportFinalizerAccessesFinalizedFields(
1163 CXXMethodDecl* dtor,
1164 CheckFinalizerVisitor::Errors* fields) {
1165 for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
1166 it != fields->end();
1168 SourceLocation loc = it->first->getLocStart();
1169 SourceManager& manager = instance_.getSourceManager();
1170 FullSourceLoc full_loc(loc, manager);
1171 diagnostic_.Report(full_loc, diag_finalizer_accesses_finalized_field_)
1172 << dtor << it->second->field();
1173 NoteField(it->second, diag_finalized_field_note_);
1177 void ReportClassRequiresFinalization(RecordInfo* info) {
1178 SourceLocation loc = info->record()->getInnerLocStart();
1179 SourceManager& manager = instance_.getSourceManager();
1180 FullSourceLoc full_loc(loc, manager);
1181 diagnostic_.Report(full_loc, diag_class_requires_finalization_)
1185 void ReportOverriddenNonVirtualTrace(RecordInfo* info,
1186 CXXMethodDecl* trace,
1187 CXXMethodDecl* overridden) {
1188 SourceLocation loc = trace->getLocStart();
1189 SourceManager& manager = instance_.getSourceManager();
1190 FullSourceLoc full_loc(loc, manager);
1191 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_)
1192 << info->record() << overridden->getParent();
1193 NoteOverriddenNonVirtualTrace(overridden);
1196 void ReportMissingTraceDispatchMethod(RecordInfo* info) {
1197 ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_);
1200 void ReportMissingFinalizeDispatchMethod(RecordInfo* info) {
1201 ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_);
1204 void ReportMissingDispatchMethod(RecordInfo* info, unsigned error) {
1205 SourceLocation loc = info->record()->getInnerLocStart();
1206 SourceManager& manager = instance_.getSourceManager();
1207 FullSourceLoc full_loc(loc, manager);
1208 diagnostic_.Report(full_loc, error) << info->record();
1211 void ReportVirtualAndManualDispatch(RecordInfo* info,
1212 CXXMethodDecl* dispatch) {
1213 SourceLocation loc = info->record()->getInnerLocStart();
1214 SourceManager& manager = instance_.getSourceManager();
1215 FullSourceLoc full_loc(loc, manager);
1216 diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_)
1218 NoteManualDispatchMethod(dispatch);
1221 void ReportMissingTraceDispatch(const FunctionDecl* dispatch,
1222 RecordInfo* receiver) {
1223 ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_);
1226 void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch,
1227 RecordInfo* receiver) {
1228 ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_);
1231 void ReportMissingDispatch(const FunctionDecl* dispatch,
1232 RecordInfo* receiver,
1234 SourceLocation loc = dispatch->getLocStart();
1235 SourceManager& manager = instance_.getSourceManager();
1236 FullSourceLoc full_loc(loc, manager);
1237 diagnostic_.Report(full_loc, error) << receiver->record();
1240 void ReportDerivesNonStackAllocated(RecordInfo* info, BasePoint* base) {
1241 SourceLocation loc = base->spec().getLocStart();
1242 SourceManager& manager = instance_.getSourceManager();
1243 FullSourceLoc full_loc(loc, manager);
1244 diagnostic_.Report(full_loc, diag_derives_non_stack_allocated_)
1245 << info->record() << base->info()->record();
1248 void ReportClassOverridesNew(RecordInfo* info, CXXMethodDecl* newop) {
1249 SourceLocation loc = newop->getLocStart();
1250 SourceManager& manager = instance_.getSourceManager();
1251 FullSourceLoc full_loc(loc, manager);
1252 diagnostic_.Report(full_loc, diag_class_overrides_new_) << info->record();
1255 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
1256 SourceLocation loc = dispatch->getLocStart();
1257 SourceManager& manager = instance_.getSourceManager();
1258 FullSourceLoc full_loc(loc, manager);
1259 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch;
1262 void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
1263 NoteField(field, diag_field_requires_tracing_note_);
1266 void NotePartObjectContainsGCRoot(FieldPoint* point) {
1267 FieldDecl* field = point->field();
1268 SourceLocation loc = field->getLocStart();
1269 SourceManager& manager = instance_.getSourceManager();
1270 FullSourceLoc full_loc(loc, manager);
1271 diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_)
1272 << field << field->getParent();
1275 void NoteFieldContainsGCRoot(FieldPoint* point) {
1276 NoteField(point, diag_field_contains_gc_root_note_);
1279 void NoteUserDeclaredDestructor(CXXMethodDecl* dtor) {
1280 SourceLocation loc = dtor->getLocStart();
1281 SourceManager& manager = instance_.getSourceManager();
1282 FullSourceLoc full_loc(loc, manager);
1283 diagnostic_.Report(full_loc, diag_user_declared_destructor_note_);
1286 void NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) {
1287 SourceLocation loc = dtor->getLocStart();
1288 SourceManager& manager = instance_.getSourceManager();
1289 FullSourceLoc full_loc(loc, manager);
1290 diagnostic_.Report(full_loc, diag_user_declared_finalizer_note_);
1293 void NoteBaseRequiresFinalization(BasePoint* base) {
1294 SourceLocation loc = base->spec().getLocStart();
1295 SourceManager& manager = instance_.getSourceManager();
1296 FullSourceLoc full_loc(loc, manager);
1297 diagnostic_.Report(full_loc, diag_base_requires_finalization_note_)
1298 << base->info()->record();
1301 void NoteField(FieldPoint* point, unsigned note) {
1302 NoteField(point->field(), note);
1305 void NoteField(FieldDecl* field, unsigned note) {
1306 SourceLocation loc = field->getLocStart();
1307 SourceManager& manager = instance_.getSourceManager();
1308 FullSourceLoc full_loc(loc, manager);
1309 diagnostic_.Report(full_loc, note) << field;
1312 void NoteOverriddenNonVirtualTrace(CXXMethodDecl* overridden) {
1313 SourceLocation loc = overridden->getLocStart();
1314 SourceManager& manager = instance_.getSourceManager();
1315 FullSourceLoc full_loc(loc, manager);
1316 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_)
1320 unsigned diag_class_must_left_mostly_derive_gc_;
1321 unsigned diag_class_requires_trace_method_;
1322 unsigned diag_base_requires_tracing_;
1323 unsigned diag_fields_require_tracing_;
1324 unsigned diag_class_contains_invalid_fields_;
1325 unsigned diag_class_contains_gc_root_;
1326 unsigned diag_class_requires_finalization_;
1327 unsigned diag_finalizer_accesses_finalized_field_;
1328 unsigned diag_overridden_non_virtual_trace_;
1329 unsigned diag_missing_trace_dispatch_method_;
1330 unsigned diag_missing_finalize_dispatch_method_;
1331 unsigned diag_virtual_and_manual_dispatch_;
1332 unsigned diag_missing_trace_dispatch_;
1333 unsigned diag_missing_finalize_dispatch_;
1334 unsigned diag_derives_non_stack_allocated_;
1335 unsigned diag_class_overrides_new_;
1337 unsigned diag_field_requires_tracing_note_;
1338 unsigned diag_raw_ptr_to_gc_managed_class_note_;
1339 unsigned diag_ref_ptr_to_gc_managed_class_note_;
1340 unsigned diag_own_ptr_to_gc_managed_class_note_;
1341 unsigned diag_stack_allocated_field_note_;
1342 unsigned diag_member_in_unmanaged_class_note_;
1343 unsigned diag_part_object_contains_gc_root_note_;
1344 unsigned diag_field_contains_gc_root_note_;
1345 unsigned diag_finalized_field_note_;
1346 unsigned diag_user_declared_destructor_note_;
1347 unsigned diag_user_declared_finalizer_note_;
1348 unsigned diag_base_requires_finalization_note_;
1349 unsigned diag_field_requires_finalization_note_;
1350 unsigned diag_overridden_non_virtual_trace_note_;
1351 unsigned diag_manual_dispatch_method_note_;
1353 CompilerInstance& instance_;
1354 DiagnosticsEngine& diagnostic_;
1355 BlinkGCPluginOptions options_;
1360 class BlinkGCPluginAction : public PluginASTAction {
1362 BlinkGCPluginAction() {}
1365 // Overridden from PluginASTAction:
1366 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
1367 llvm::StringRef ref) {
1368 return new BlinkGCPluginConsumer(instance, options_);
1371 virtual bool ParseArgs(const CompilerInstance& instance,
1372 const std::vector<string>& args) {
1375 for (size_t i = 0; i < args.size() && parsed; ++i) {
1376 if (args[i] == "enable-oilpan") {
1377 options_.enable_oilpan = true;
1378 } else if (args[i] == "dump-graph") {
1379 options_.dump_graph = true;
1382 llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
1390 BlinkGCPluginOptions options_;
1395 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
1397 "Check Blink GC invariants");