Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp

Issue 192933002: Check that classes with non-trivial destructors have finalization support. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Edge.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // This clang plugin checks various invariants of the Blink garbage 5 // This clang plugin checks various invariants of the Blink garbage
6 // collection infrastructure. 6 // collection infrastructure.
7 // 7 //
8 // Errors are described at: 8 // Errors are described at:
9 // http://www.chromium.org/developers/blink-gc-plugin-errors 9 // http://www.chromium.org/developers/blink-gc-plugin-errors
10 10
(...skipping 23 matching lines...) Expand all
34 34
35 const char kFieldRequiresTracingNote[] = 35 const char kFieldRequiresTracingNote[] =
36 "[blink-gc] Untraced field %0 declared here:"; 36 "[blink-gc] Untraced field %0 declared here:";
37 37
38 const char kClassContainsInvalidFields[] = 38 const char kClassContainsInvalidFields[] =
39 "[blink-gc] Class %0 contains invalid fields."; 39 "[blink-gc] Class %0 contains invalid fields.";
40 40
41 const char kClassContainsGCRoot[] = 41 const char kClassContainsGCRoot[] =
42 "[blink-gc] Class %0 contains GC root in field %1."; 42 "[blink-gc] Class %0 contains GC root in field %1.";
43 43
44 const char kFinalizerInNonFinalizedClass[] = 44 const char kClassRequiresFinalization[] =
45 "[blink-gc] Non-finalized class %0 has a user-declared finalizer %1."; 45 "[blink-gc] Class %0 requires finalization.";
46 46
47 const char kFinalizerAccessesFinalizedField[] = 47 const char kFinalizerAccessesFinalizedField[] =
48 "[blink-gc] Finalizer %0 accesses potentially finalized field %1."; 48 "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
49 49
50 const char kRawPtrToGCManagedClassNote[] = 50 const char kRawPtrToGCManagedClassNote[] =
51 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:"; 51 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
52 52
53 const char kRefPtrToGCManagedClassNote[] = 53 const char kRefPtrToGCManagedClassNote[] =
54 "[blink-gc] RefPtr field %0 to a GC managed class declared here:"; 54 "[blink-gc] RefPtr field %0 to a GC managed class declared here:";
55 55
56 const char kOwnPtrToGCManagedClassNote[] = 56 const char kOwnPtrToGCManagedClassNote[] =
57 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:"; 57 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
58 58
59 const char kPartObjectContainsGCRoot[] = 59 const char kPartObjectContainsGCRoot[] =
60 "[blink-gc] Field %0 with embedded GC root in %1 declared here:"; 60 "[blink-gc] Field %0 with embedded GC root in %1 declared here:";
61 61
62 const char kFieldContainsGCRoot[] = 62 const char kFieldContainsGCRoot[] =
63 "[blink-gc] Field %0 defining a GC root declared here:"; 63 "[blink-gc] Field %0 defining a GC root declared here:";
64 64
65 const char kFinalizedFieldNote[] = 65 const char kFinalizedFieldNote[] =
66 "[blink-gc] Potentially finalized field %0 declared here:"; 66 "[blink-gc] Potentially finalized field %0 declared here:";
67 67
68 const char kUserDeclaredDestructorNote[] =
69 "[blink-gc] User-declared destructor declared here:";
70
71 const char kBaseRequiresFinalizationNote[] =
72 "[blink-gc] Base class %0 requiring finalization declared here:";
73
74 const char kFieldRequiresFinalizationNote[] =
75 "[blink-gc] Field %0 requiring finalization declared here:";
76
68 struct BlinkGCPluginOptions { 77 struct BlinkGCPluginOptions {
69 BlinkGCPluginOptions() : enable_oilpan(false) {} 78 BlinkGCPluginOptions() : enable_oilpan(false) {}
70 bool enable_oilpan; 79 bool enable_oilpan;
71 std::set<std::string> ignored_classes; 80 std::set<std::string> ignored_classes;
72 std::set<std::string> checked_namespaces; 81 std::set<std::string> checked_namespaces;
73 std::vector<std::string> ignored_directories; 82 std::vector<std::string> ignored_directories;
74 }; 83 };
75 84
76 typedef std::vector<CXXRecordDecl*> RecordVector; 85 typedef std::vector<CXXRecordDecl*> RecordVector;
77 typedef std::vector<CXXMethodDecl*> MethodVector; 86 typedef std::vector<CXXMethodDecl*> MethodVector;
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod); 411 diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod);
403 diag_base_requires_tracing_ = 412 diag_base_requires_tracing_ =
404 diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing); 413 diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing);
405 diag_fields_require_tracing_ = 414 diag_fields_require_tracing_ =
406 diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing); 415 diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing);
407 diag_class_contains_invalid_fields_ = 416 diag_class_contains_invalid_fields_ =
408 diagnostic_.getCustomDiagID(getErrorLevel(), 417 diagnostic_.getCustomDiagID(getErrorLevel(),
409 kClassContainsInvalidFields); 418 kClassContainsInvalidFields);
410 diag_class_contains_gc_root_ = 419 diag_class_contains_gc_root_ =
411 diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot); 420 diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
412 diag_finalizer_in_nonfinalized_class_ = diagnostic_.getCustomDiagID( 421 diag_class_requires_finalization_ = diagnostic_.getCustomDiagID(
413 getErrorLevel(), kFinalizerInNonFinalizedClass); 422 getErrorLevel(), kClassRequiresFinalization);
414 diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID( 423 diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
415 getErrorLevel(), kFinalizerAccessesFinalizedField); 424 getErrorLevel(), kFinalizerAccessesFinalizedField);
416 425
417 // Register note messages. 426 // Register note messages.
418 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( 427 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
419 DiagnosticsEngine::Note, kFieldRequiresTracingNote); 428 DiagnosticsEngine::Note, kFieldRequiresTracingNote);
420 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( 429 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
421 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); 430 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote);
422 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( 431 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
423 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); 432 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote);
424 diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( 433 diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
425 DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote); 434 DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote);
426 diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID( 435 diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
427 DiagnosticsEngine::Note, kPartObjectContainsGCRoot); 436 DiagnosticsEngine::Note, kPartObjectContainsGCRoot);
428 diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID( 437 diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
429 DiagnosticsEngine::Note, kFieldContainsGCRoot); 438 DiagnosticsEngine::Note, kFieldContainsGCRoot);
430 diag_finalized_field_note_ = diagnostic_.getCustomDiagID( 439 diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
431 DiagnosticsEngine::Note, kFinalizedFieldNote); 440 DiagnosticsEngine::Note, kFinalizedFieldNote);
441 diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID(
442 DiagnosticsEngine::Note, kUserDeclaredDestructorNote);
443 diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID(
444 DiagnosticsEngine::Note, kBaseRequiresFinalizationNote);
445 diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID(
446 DiagnosticsEngine::Note, kFieldRequiresFinalizationNote);
432 } 447 }
433 448
434 virtual void HandleTranslationUnit(ASTContext& context) { 449 virtual void HandleTranslationUnit(ASTContext& context) {
435 CollectVisitor visitor; 450 CollectVisitor visitor;
436 visitor.TraverseDecl(context.getTranslationUnitDecl()); 451 visitor.TraverseDecl(context.getTranslationUnitDecl());
437 452
438 for (RecordVector::iterator it = visitor.record_decls().begin(); 453 for (RecordVector::iterator it = visitor.record_decls().begin();
439 it != visitor.record_decls().end(); 454 it != visitor.record_decls().end();
440 ++it) { 455 ++it) {
441 CheckRecord(cache_.Lookup(*it)); 456 CheckRecord(cache_.Lookup(*it));
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 if (info->RequiresTraceMethod() && !info->GetTraceMethod()) 498 if (info->RequiresTraceMethod() && !info->GetTraceMethod())
484 ReportClassRequiresTraceMethod(info); 499 ReportClassRequiresTraceMethod(info);
485 500
486 { 501 {
487 CheckFieldsVisitor visitor(options_); 502 CheckFieldsVisitor visitor(options_);
488 if (visitor.ContainsInvalidFields(info)) 503 if (visitor.ContainsInvalidFields(info))
489 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); 504 ReportClassContainsInvalidFields(info, &visitor.invalid_fields());
490 } 505 }
491 506
492 if (info->IsGCDerived()) { 507 if (info->IsGCDerived()) {
493 { 508 CheckGCRootsVisitor visitor;
494 CheckGCRootsVisitor visitor; 509 if (visitor.ContainsGCRoots(info))
495 if (visitor.ContainsGCRoots(info)) 510 ReportClassContainsGCRoots(info, &visitor.gc_roots());
496 ReportClassContainsGCRoots(info, &visitor.gc_roots());
497 }
498 511
499 // TODO: check for non-user defined and non-trivial destructors too. 512 if (info->NeedsFinalization())
500 // TODO: support overridden finalize(). 513 CheckFinalization(info);
501 if (CXXDestructorDecl* dtor = info->record()->getDestructor()) {
502 if (dtor->isUserProvided() && !info->IsGCFinalized()) {
503 // Don't report if using transition types and the body is empty.
504 if (!options_.enable_oilpan) {
505 ReportFinalizerInNonFinalizedClass(info, dtor);
506 } else {
507 if (dtor->hasBody()) {
508 CompoundStmt* stmt = cast<CompoundStmt>(dtor->getBody());
509 if (stmt && !stmt->body_empty())
510 ReportFinalizerInNonFinalizedClass(info, dtor);
511 }
512 }
513 }
514
515 if (dtor->hasBody()) {
516 CheckFinalizerVisitor visitor(&cache_);
517 visitor.TraverseCXXMethodDecl(dtor);
518 if (!visitor.finalized_fields().empty()) {
519 ReportFinalizerAccessesFinalizedFields(dtor,
520 &visitor.finalized_fields());
521 }
522 }
523 }
524 } 514 }
525 } 515 }
526 516
517 void CheckFinalization(RecordInfo* info) {
518 // TODO: Should we collect destructors similar to trace methods?
519 // TODO: Check overridden finalize().
520 CXXDestructorDecl* dtor = info->record()->getDestructor();
521
522 // For finalized classes, check the finalization method if possible.
523 if (info->IsGCFinalized()) {
524 if (dtor && dtor->hasBody()) {
525 CheckFinalizerVisitor visitor(&cache_);
526 visitor.TraverseCXXMethodDecl(dtor);
527 if (!visitor.finalized_fields().empty()) {
528 ReportFinalizerAccessesFinalizedFields(
529 dtor, &visitor.finalized_fields());
530 }
531 }
532 return;
533 }
534
535 // Don't require finalization of a mixin that has not yet been "mixed in".
536 if (info->IsUnmixedGCMixin())
537 return;
538
539 // Report the finalization error, and proceed to print possible causes for
540 // the finalization requirement.
541 ReportClassRequiresFinalization(info);
542
543 if (dtor && dtor->isUserProvided())
544 NoteUserDeclaredDestructor(dtor);
545
546 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
547 it != info->GetBases().end();
548 ++it) {
549 if (it->second.info()->NeedsFinalization())
550 NoteBaseRequiresFinalization(&it->second);
551 }
552
553 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
554 it != info->GetFields().end();
555 ++it) {
556 if (it->second.edge()->NeedsFinalization())
557 NoteField(&it->second, diag_field_requires_finalization_note_);
558 }
559 }
560
527 // This is the main entry for tracing method definitions. 561 // This is the main entry for tracing method definitions.
528 void CheckTracingMethod(CXXMethodDecl* method) { 562 void CheckTracingMethod(CXXMethodDecl* method) {
529 RecordInfo* parent = cache_.Lookup(method->getParent()); 563 RecordInfo* parent = cache_.Lookup(method->getParent());
530 if (IsIgnored(parent)) 564 if (IsIgnored(parent))
531 return; 565 return;
532 566
533 // Check templated tracing methods by checking the template instantiations. 567 // Check templated tracing methods by checking the template instantiations.
534 // Specialized templates are handled as ordinary classes. 568 // Specialized templates are handled as ordinary classes.
535 if (ClassTemplateDecl* tmpl = 569 if (ClassTemplateDecl* tmpl =
536 parent->record()->getDescribedClassTemplate()) { 570 parent->record()->getDescribedClassTemplate()) {
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
718 diagnostic_.Report(full_loc, diag_class_contains_gc_root_) 752 diagnostic_.Report(full_loc, diag_class_contains_gc_root_)
719 << info->record() << point->field(); 753 << info->record() << point->field();
720 while (++path != it->end()) { 754 while (++path != it->end()) {
721 NotePartObjectContainsGCRoot(point); 755 NotePartObjectContainsGCRoot(point);
722 point = *path; 756 point = *path;
723 } 757 }
724 NoteFieldContainsGCRoot(point); 758 NoteFieldContainsGCRoot(point);
725 } 759 }
726 } 760 }
727 761
728 void ReportFinalizerInNonFinalizedClass(RecordInfo* info,
729 CXXMethodDecl* dtor) {
730 SourceLocation loc = dtor->getLocStart();
731 SourceManager& manager = instance_.getSourceManager();
732 FullSourceLoc full_loc(loc, manager);
733 diagnostic_.Report(full_loc, diag_finalizer_in_nonfinalized_class_)
734 << info->record() << dtor;
735 }
736
737 void ReportFinalizerAccessesFinalizedFields( 762 void ReportFinalizerAccessesFinalizedFields(
738 CXXMethodDecl* dtor, 763 CXXMethodDecl* dtor,
739 CheckFinalizerVisitor::Errors* fields) { 764 CheckFinalizerVisitor::Errors* fields) {
740 for (CheckFinalizerVisitor::Errors::iterator it = fields->begin(); 765 for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
741 it != fields->end(); 766 it != fields->end();
742 ++it) { 767 ++it) {
743 SourceLocation loc = it->first->getLocStart(); 768 SourceLocation loc = it->first->getLocStart();
744 SourceManager& manager = instance_.getSourceManager(); 769 SourceManager& manager = instance_.getSourceManager();
745 FullSourceLoc full_loc(loc, manager); 770 FullSourceLoc full_loc(loc, manager);
746 diagnostic_.Report(full_loc, diag_finalizer_accesses_finalized_field_) 771 diagnostic_.Report(full_loc, diag_finalizer_accesses_finalized_field_)
747 << dtor << it->second->field(); 772 << dtor << it->second->field();
748 NoteField(it->second, diag_finalized_field_note_); 773 NoteField(it->second, diag_finalized_field_note_);
749 } 774 }
750 } 775 }
751 776
777 void ReportClassRequiresFinalization(RecordInfo* info) {
778 SourceLocation loc = info->record()->getInnerLocStart();
779 SourceManager& manager = instance_.getSourceManager();
780 FullSourceLoc full_loc(loc, manager);
781 diagnostic_.Report(full_loc, diag_class_requires_finalization_)
782 << info->record();
783 }
784
752 void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) { 785 void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
753 NoteField(field, diag_field_requires_tracing_note_); 786 NoteField(field, diag_field_requires_tracing_note_);
754 } 787 }
755 788
756 void NotePartObjectContainsGCRoot(FieldPoint* point) { 789 void NotePartObjectContainsGCRoot(FieldPoint* point) {
757 FieldDecl* field = point->field(); 790 FieldDecl* field = point->field();
758 SourceLocation loc = field->getLocStart(); 791 SourceLocation loc = field->getLocStart();
759 SourceManager& manager = instance_.getSourceManager(); 792 SourceManager& manager = instance_.getSourceManager();
760 FullSourceLoc full_loc(loc, manager); 793 FullSourceLoc full_loc(loc, manager);
761 diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_) 794 diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_)
762 << field << field->getParent(); 795 << field << field->getParent();
763 } 796 }
764 797
765 void NoteFieldContainsGCRoot(FieldPoint* point) { 798 void NoteFieldContainsGCRoot(FieldPoint* point) {
766 NoteField(point, diag_field_contains_gc_root_note_); 799 NoteField(point, diag_field_contains_gc_root_note_);
767 } 800 }
768 801
802 void NoteUserDeclaredDestructor(CXXMethodDecl* dtor) {
803 SourceLocation loc = dtor->getLocStart();
804 SourceManager& manager = instance_.getSourceManager();
805 FullSourceLoc full_loc(loc, manager);
806 diagnostic_.Report(full_loc, diag_user_declared_destructor_note_);
807 }
808
809 void NoteBaseRequiresFinalization(BasePoint* base) {
810 SourceLocation loc = base->spec().getLocStart();
811 SourceManager& manager = instance_.getSourceManager();
812 FullSourceLoc full_loc(loc, manager);
813 diagnostic_.Report(full_loc, diag_base_requires_finalization_note_)
814 << base->info()->record();
815 }
816
769 void NoteField(FieldPoint* point, unsigned note) { 817 void NoteField(FieldPoint* point, unsigned note) {
770 NoteField(point->field(), note); 818 NoteField(point->field(), note);
771 } 819 }
772 820
773 void NoteField(FieldDecl* field, unsigned note) { 821 void NoteField(FieldDecl* field, unsigned note) {
774 SourceLocation loc = field->getLocStart(); 822 SourceLocation loc = field->getLocStart();
775 SourceManager& manager = instance_.getSourceManager(); 823 SourceManager& manager = instance_.getSourceManager();
776 FullSourceLoc full_loc(loc, manager); 824 FullSourceLoc full_loc(loc, manager);
777 diagnostic_.Report(full_loc, note) << field; 825 diagnostic_.Report(full_loc, note) << field;
778 } 826 }
779 827
780 unsigned diag_class_requires_trace_method_; 828 unsigned diag_class_requires_trace_method_;
781 unsigned diag_base_requires_tracing_; 829 unsigned diag_base_requires_tracing_;
782 unsigned diag_fields_require_tracing_; 830 unsigned diag_fields_require_tracing_;
783 unsigned diag_class_contains_invalid_fields_; 831 unsigned diag_class_contains_invalid_fields_;
784 unsigned diag_class_contains_gc_root_; 832 unsigned diag_class_contains_gc_root_;
785 unsigned diag_finalizer_in_nonfinalized_class_; 833 unsigned diag_class_requires_finalization_;
786 unsigned diag_finalizer_accesses_finalized_field_; 834 unsigned diag_finalizer_accesses_finalized_field_;
787 835
788 unsigned diag_field_requires_tracing_note_; 836 unsigned diag_field_requires_tracing_note_;
789 unsigned diag_raw_ptr_to_gc_managed_class_note_; 837 unsigned diag_raw_ptr_to_gc_managed_class_note_;
790 unsigned diag_ref_ptr_to_gc_managed_class_note_; 838 unsigned diag_ref_ptr_to_gc_managed_class_note_;
791 unsigned diag_own_ptr_to_gc_managed_class_note_; 839 unsigned diag_own_ptr_to_gc_managed_class_note_;
792 unsigned diag_part_object_contains_gc_root_note_; 840 unsigned diag_part_object_contains_gc_root_note_;
793 unsigned diag_field_contains_gc_root_note_; 841 unsigned diag_field_contains_gc_root_note_;
794 unsigned diag_finalized_field_note_; 842 unsigned diag_finalized_field_note_;
843 unsigned diag_user_declared_destructor_note_;
844 unsigned diag_base_requires_finalization_note_;
845 unsigned diag_field_requires_finalization_note_;
795 846
796 CompilerInstance& instance_; 847 CompilerInstance& instance_;
797 DiagnosticsEngine& diagnostic_; 848 DiagnosticsEngine& diagnostic_;
798 BlinkGCPluginOptions options_; 849 BlinkGCPluginOptions options_;
799 RecordCache cache_; 850 RecordCache cache_;
800 }; 851 };
801 852
802 class BlinkGCPluginAction : public PluginASTAction { 853 class BlinkGCPluginAction : public PluginASTAction {
803 public: 854 public:
804 BlinkGCPluginAction() {} 855 BlinkGCPluginAction() {}
(...skipping 23 matching lines...) Expand all
828 879
829 private: 880 private:
830 BlinkGCPluginOptions options_; 881 BlinkGCPluginOptions options_;
831 }; 882 };
832 883
833 } // namespace 884 } // namespace
834 885
835 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( 886 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
836 "blink-gc-plugin", 887 "blink-gc-plugin",
837 "Check Blink GC invariants"); 888 "Check Blink GC invariants");
OLDNEW
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Edge.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698