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 struct BlinkGCPluginOptions
{
120 BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {}
123 std::set
<std::string
> ignored_classes
;
124 std::set
<std::string
> checked_namespaces
;
125 std::vector
<std::string
> ignored_directories
;
128 typedef std::vector
<CXXRecordDecl
*> RecordVector
;
129 typedef std::vector
<CXXMethodDecl
*> MethodVector
;
131 // Test if a template specialization is an instantiation.
132 static bool IsTemplateInstantiation(CXXRecordDecl
* record
) {
133 ClassTemplateSpecializationDecl
* spec
=
134 dyn_cast
<ClassTemplateSpecializationDecl
>(record
);
137 switch (spec
->getTemplateSpecializationKind()) {
138 case TSK_ImplicitInstantiation
:
139 case TSK_ExplicitInstantiationDefinition
:
142 case TSK_ExplicitSpecialization
:
144 // TODO: unsupported cases.
145 case TSK_ExplicitInstantiationDeclaration
:
148 assert(false && "Unknown template specialization kind");
151 // This visitor collects the entry points for the checker.
152 class CollectVisitor
: public RecursiveASTVisitor
<CollectVisitor
> {
156 RecordVector
& record_decls() { return record_decls_
; }
157 MethodVector
& trace_decls() { return trace_decls_
; }
159 bool shouldVisitTemplateInstantiations() { return false; }
161 // Collect record declarations, including nested declarations.
162 bool VisitCXXRecordDecl(CXXRecordDecl
* record
) {
163 if (record
->hasDefinition() && record
->isCompleteDefinition())
164 record_decls_
.push_back(record
);
168 // Collect tracing method definitions, but don't traverse method bodies.
169 bool TraverseCXXMethodDecl(CXXMethodDecl
* method
) {
170 if (method
->isThisDeclarationADefinition() && Config::IsTraceMethod(method
))
171 trace_decls_
.push_back(method
);
176 RecordVector record_decls_
;
177 MethodVector trace_decls_
;
180 // This visitor checks that a finalizer method does not have invalid access to
181 // fields that are potentially finalized. A potentially finalized field is
182 // either a Member, a heap-allocated collection or an off-heap collection that
183 // contains Members. Invalid uses are currently identified as passing the field
184 // as the argument of a procedure call or using the -> or [] operators on it.
185 class CheckFinalizerVisitor
186 : public RecursiveASTVisitor
<CheckFinalizerVisitor
> {
188 // Simple visitor to determine if the content of a field might be collected
189 // during finalization.
190 class MightBeCollectedVisitor
: public EdgeVisitor
{
192 MightBeCollectedVisitor() : might_be_collected_(false) {}
193 bool might_be_collected() { return might_be_collected_
; }
194 void VisitMember(Member
* edge
) override
{ might_be_collected_
= true; }
195 void VisitCollection(Collection
* edge
) override
{
196 if (edge
->on_heap()) {
197 might_be_collected_
= !edge
->is_root();
199 edge
->AcceptMembers(this);
204 bool might_be_collected_
;
208 typedef std::vector
<std::pair
<MemberExpr
*, FieldPoint
*> > Errors
;
210 CheckFinalizerVisitor(RecordCache
* cache
)
211 : blacklist_context_(false), cache_(cache
) {}
213 Errors
& finalized_fields() { return finalized_fields_
; }
215 bool WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr
* expr
) {
216 // Only continue the walk-up if the operator is a blacklisted one.
217 switch (expr
->getOperator()) {
220 this->WalkUpFromCallExpr(expr
);
226 // We consider all non-operator calls to be blacklisted contexts.
227 bool WalkUpFromCallExpr(CallExpr
* expr
) {
228 bool prev_blacklist_context
= blacklist_context_
;
229 blacklist_context_
= true;
230 for (size_t i
= 0; i
< expr
->getNumArgs(); ++i
)
231 this->TraverseStmt(expr
->getArg(i
));
232 blacklist_context_
= prev_blacklist_context
;
236 bool VisitMemberExpr(MemberExpr
* member
) {
237 FieldDecl
* field
= dyn_cast
<FieldDecl
>(member
->getMemberDecl());
241 RecordInfo
* info
= cache_
->Lookup(field
->getParent());
245 RecordInfo::Fields::iterator it
= info
->GetFields().find(field
);
246 if (it
== info
->GetFields().end())
249 if (blacklist_context_
&& MightBeCollected(&it
->second
))
250 finalized_fields_
.push_back(std::make_pair(member
, &it
->second
));
254 bool MightBeCollected(FieldPoint
* point
) {
255 MightBeCollectedVisitor visitor
;
256 point
->edge()->Accept(&visitor
);
257 return visitor
.might_be_collected();
261 bool blacklist_context_
;
262 Errors finalized_fields_
;
266 // This visitor checks that a method contains within its body, a call to a
267 // method on the provided receiver class. This is used to check manual
268 // dispatching for trace and finalize methods.
269 class CheckDispatchVisitor
: public RecursiveASTVisitor
<CheckDispatchVisitor
> {
271 CheckDispatchVisitor(RecordInfo
* receiver
)
272 : receiver_(receiver
), dispatched_to_receiver_(false) {}
274 bool dispatched_to_receiver() { return dispatched_to_receiver_
; }
276 bool VisitMemberExpr(MemberExpr
* member
) {
277 if (CXXMethodDecl
* fn
= dyn_cast
<CXXMethodDecl
>(member
->getMemberDecl())) {
278 if (fn
->getParent() == receiver_
->record())
279 dispatched_to_receiver_
= true;
285 RecordInfo
* receiver_
;
286 bool dispatched_to_receiver_
;
289 // This visitor checks a tracing method by traversing its body.
290 // - A member field is considered traced if it is referenced in the body.
291 // - A base is traced if a base-qualified call to a trace method is found.
292 class CheckTraceVisitor
: public RecursiveASTVisitor
<CheckTraceVisitor
> {
294 CheckTraceVisitor(CXXMethodDecl
* trace
, RecordInfo
* info
)
295 : trace_(trace
), info_(info
) {}
297 // Allow recursive traversal by using VisitMemberExpr.
298 bool VisitMemberExpr(MemberExpr
* member
) {
299 // If this member expression references a field decl, mark it as traced.
300 if (FieldDecl
* field
= dyn_cast
<FieldDecl
>(member
->getMemberDecl())) {
301 if (IsTemplateInstantiation(info_
->record())) {
302 // Pointer equality on fields does not work for template instantiations.
303 // The trace method refers to fields of the template definition which
304 // are different from the instantiated fields that need to be traced.
305 const string
& name
= field
->getNameAsString();
306 for (RecordInfo::Fields::iterator it
= info_
->GetFields().begin();
307 it
!= info_
->GetFields().end();
309 if (it
->first
->getNameAsString() == name
) {
315 RecordInfo::Fields::iterator it
= info_
->GetFields().find(field
);
316 if (it
!= info_
->GetFields().end())
322 // If this is a weak callback function we only check field tracing.
323 if (IsWeakCallback())
326 // For method calls, check tracing of bases and other special GC methods.
327 if (CXXMethodDecl
* fn
= dyn_cast
<CXXMethodDecl
>(member
->getMemberDecl())) {
328 const string
& name
= fn
->getNameAsString();
329 // Check weak callbacks.
330 if (name
== kRegisterWeakMembersName
) {
331 if (fn
->isTemplateInstantiation()) {
332 const TemplateArgumentList
& args
=
333 *fn
->getTemplateSpecializationInfo()->TemplateArguments
;
334 // The second template argument is the callback method.
335 if (args
.size() > 1 &&
336 args
[1].getKind() == TemplateArgument::Declaration
) {
337 if (FunctionDecl
* callback
=
338 dyn_cast
<FunctionDecl
>(args
[1].getAsDecl())) {
339 if (callback
->hasBody()) {
340 CheckTraceVisitor
nested_visitor(info_
);
341 nested_visitor
.TraverseStmt(callback
->getBody());
349 // TODO: It is possible to have multiple bases, where one must be traced
350 // using a traceAfterDispatch. In such a case we should also check that
351 // the mixin does not add a vtable.
352 if (Config::IsTraceMethod(fn
) && member
->hasQualifier()) {
353 if (const Type
* type
= member
->getQualifier()->getAsType()) {
354 if (CXXRecordDecl
* decl
= type
->getAsCXXRecordDecl()) {
355 RecordInfo::Bases::iterator it
= info_
->GetBases().find(decl
);
356 if (it
!= info_
->GetBases().end())
357 it
->second
.MarkTraced();
366 // Nested checking for weak callbacks.
367 CheckTraceVisitor(RecordInfo
* info
) : trace_(0), info_(info
) {}
369 bool IsWeakCallback() { return !trace_
; }
371 void MarkTraced(RecordInfo::Fields::iterator it
) {
372 // In a weak callback we can't mark strong fields as traced.
373 if (IsWeakCallback() && !it
->second
.edge()->IsWeakMember())
375 it
->second
.MarkTraced();
378 CXXMethodDecl
* trace_
;
382 // This visitor checks that the fields of a class and the fields of
383 // its part objects don't define GC roots.
384 class CheckGCRootsVisitor
: public RecursiveEdgeVisitor
{
386 typedef std::vector
<FieldPoint
*> RootPath
;
387 typedef std::vector
<RootPath
> Errors
;
389 CheckGCRootsVisitor() {}
391 Errors
& gc_roots() { return gc_roots_
; }
393 bool ContainsGCRoots(RecordInfo
* info
) {
394 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
395 it
!= info
->GetFields().end();
397 current_
.push_back(&it
->second
);
398 it
->second
.edge()->Accept(this);
401 return !gc_roots_
.empty();
404 void VisitValue(Value
* edge
) override
{
405 // TODO: what should we do to check unions?
406 if (edge
->value()->record()->isUnion())
409 // If the value is a part object, then continue checking for roots.
410 for (Context::iterator it
= context().begin();
411 it
!= context().end();
413 if (!(*it
)->IsCollection())
416 ContainsGCRoots(edge
->value());
419 void VisitPersistent(Persistent
* edge
) override
{
420 gc_roots_
.push_back(current_
);
423 void AtCollection(Collection
* edge
) override
{
425 gc_roots_
.push_back(current_
);
433 // This visitor checks that the fields of a class are "well formed".
434 // - OwnPtr, RefPtr and RawPtr must not point to a GC derived types.
435 // - An on-heap class must never contain GC roots.
436 // - Only stack-allocated types may point to stack-allocated types.
437 class CheckFieldsVisitor
: public RecursiveEdgeVisitor
{
439 typedef std::vector
<std::pair
<FieldPoint
*, Edge
*> > Errors
;
441 CheckFieldsVisitor(const BlinkGCPluginOptions
& options
)
442 : options_(options
), current_(0), stack_allocated_host_(false) {}
444 Errors
& invalid_fields() { return invalid_fields_
; }
446 bool ContainsInvalidFields(RecordInfo
* info
) {
447 stack_allocated_host_
= info
->IsStackAllocated();
448 managed_host_
= stack_allocated_host_
||
449 info
->IsGCAllocated() ||
450 info
->IsNonNewable() ||
451 info
->IsOnlyPlacementNewable();
452 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
453 it
!= info
->GetFields().end();
456 current_
= &it
->second
;
457 current_
->edge()->Accept(this);
459 return !invalid_fields_
.empty();
462 void VisitMember(Member
* edge
) override
{
465 // A member is allowed to appear in the context of a root.
466 for (Context::iterator it
= context().begin();
467 it
!= context().end();
469 if ((*it
)->Kind() == Edge::kRoot
)
472 invalid_fields_
.push_back(std::make_pair(current_
, edge
));
475 void VisitValue(Value
* edge
) override
{
476 // TODO: what should we do to check unions?
477 if (edge
->value()->record()->isUnion())
480 if (!stack_allocated_host_
&& edge
->value()->IsStackAllocated()) {
481 invalid_fields_
.push_back(std::make_pair(current_
, edge
));
485 if (!Parent() || !edge
->value()->IsGCAllocated())
488 // In transition mode, disallow OwnPtr<T>, RawPtr<T> to GC allocated T's,
489 // also disallow T* in stack-allocated types.
490 if (options_
.enable_oilpan
) {
491 if (Parent()->IsOwnPtr() ||
492 Parent()->IsRawPtrClass() ||
493 (stack_allocated_host_
&& Parent()->IsRawPtr() &&
494 // TODO: Remove this exception once the node hierarchy is moved.
495 !edge
->value()->IsTreeShared())) {
496 invalid_fields_
.push_back(std::make_pair(current_
, Parent()));
503 if (Parent()->IsRawPtr() || Parent()->IsRefPtr() || Parent()->IsOwnPtr()) {
504 invalid_fields_
.push_back(std::make_pair(current_
, Parent()));
510 const BlinkGCPluginOptions
& options_
;
511 FieldPoint
* current_
;
512 bool stack_allocated_host_
;
514 Errors invalid_fields_
;
517 // Main class containing checks for various invariants of the Blink
518 // garbage collection infrastructure.
519 class BlinkGCPluginConsumer
: public ASTConsumer
{
521 BlinkGCPluginConsumer(CompilerInstance
& instance
,
522 const BlinkGCPluginOptions
& options
)
523 : instance_(instance
),
524 diagnostic_(instance
.getDiagnostics()),
528 // Only check structures in the blink, WebCore and WebKit namespaces.
529 options_
.checked_namespaces
.insert("blink");
530 options_
.checked_namespaces
.insert("WebCore");
531 options_
.checked_namespaces
.insert("WebKit");
533 // Ignore GC implementation files.
534 options_
.ignored_directories
.push_back("/heap/");
536 // Register warning/error messages.
537 diag_class_must_left_mostly_derive_gc_
= diagnostic_
.getCustomDiagID(
538 getErrorLevel(), kClassMustLeftMostlyDeriveGC
);
539 diag_class_requires_trace_method_
=
540 diagnostic_
.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod
);
541 diag_base_requires_tracing_
=
542 diagnostic_
.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing
);
543 diag_fields_require_tracing_
=
544 diagnostic_
.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing
);
545 diag_class_contains_invalid_fields_
= diagnostic_
.getCustomDiagID(
546 getErrorLevel(), kClassContainsInvalidFields
);
547 diag_class_contains_gc_root_
=
548 diagnostic_
.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot
);
549 diag_class_requires_finalization_
= diagnostic_
.getCustomDiagID(
550 getErrorLevel(), kClassRequiresFinalization
);
551 diag_finalizer_accesses_finalized_field_
= diagnostic_
.getCustomDiagID(
552 getErrorLevel(), kFinalizerAccessesFinalizedField
);
553 diag_overridden_non_virtual_trace_
= diagnostic_
.getCustomDiagID(
554 getErrorLevel(), kOverriddenNonVirtualTrace
);
555 diag_missing_trace_dispatch_method_
= diagnostic_
.getCustomDiagID(
556 getErrorLevel(), kMissingTraceDispatchMethod
);
557 diag_missing_finalize_dispatch_method_
= diagnostic_
.getCustomDiagID(
558 getErrorLevel(), kMissingFinalizeDispatchMethod
);
559 diag_virtual_and_manual_dispatch_
=
560 diagnostic_
.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch
);
561 diag_missing_trace_dispatch_
=
562 diagnostic_
.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch
);
563 diag_missing_finalize_dispatch_
=
564 diagnostic_
.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch
);
565 diag_derives_non_stack_allocated_
=
566 diagnostic_
.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated
);
568 // Register note messages.
569 diag_field_requires_tracing_note_
= diagnostic_
.getCustomDiagID(
570 DiagnosticsEngine::Note
, kFieldRequiresTracingNote
);
571 diag_raw_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
572 DiagnosticsEngine::Note
, kRawPtrToGCManagedClassNote
);
573 diag_ref_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
574 DiagnosticsEngine::Note
, kRefPtrToGCManagedClassNote
);
575 diag_own_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
576 DiagnosticsEngine::Note
, kOwnPtrToGCManagedClassNote
);
577 diag_stack_allocated_field_note_
= diagnostic_
.getCustomDiagID(
578 DiagnosticsEngine::Note
, kStackAllocatedFieldNote
);
579 diag_member_in_unmanaged_class_note_
= diagnostic_
.getCustomDiagID(
580 DiagnosticsEngine::Note
, kMemberInUnmanagedClassNote
);
581 diag_part_object_contains_gc_root_note_
= diagnostic_
.getCustomDiagID(
582 DiagnosticsEngine::Note
, kPartObjectContainsGCRoot
);
583 diag_field_contains_gc_root_note_
= diagnostic_
.getCustomDiagID(
584 DiagnosticsEngine::Note
, kFieldContainsGCRoot
);
585 diag_finalized_field_note_
= diagnostic_
.getCustomDiagID(
586 DiagnosticsEngine::Note
, kFinalizedFieldNote
);
587 diag_user_declared_destructor_note_
= diagnostic_
.getCustomDiagID(
588 DiagnosticsEngine::Note
, kUserDeclaredDestructorNote
);
589 diag_user_declared_finalizer_note_
= diagnostic_
.getCustomDiagID(
590 DiagnosticsEngine::Note
, kUserDeclaredFinalizerNote
);
591 diag_base_requires_finalization_note_
= diagnostic_
.getCustomDiagID(
592 DiagnosticsEngine::Note
, kBaseRequiresFinalizationNote
);
593 diag_field_requires_finalization_note_
= diagnostic_
.getCustomDiagID(
594 DiagnosticsEngine::Note
, kFieldRequiresFinalizationNote
);
595 diag_overridden_non_virtual_trace_note_
= diagnostic_
.getCustomDiagID(
596 DiagnosticsEngine::Note
, kOverriddenNonVirtualTraceNote
);
597 diag_manual_dispatch_method_note_
= diagnostic_
.getCustomDiagID(
598 DiagnosticsEngine::Note
, kManualDispatchMethodNote
);
601 void HandleTranslationUnit(ASTContext
& context
) override
{
602 CollectVisitor visitor
;
603 visitor
.TraverseDecl(context
.getTranslationUnitDecl());
605 if (options_
.dump_graph
) {
607 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
608 json_
= JsonWriter::from(instance_
.createOutputFile(
612 true, // RemoveFileOnSignal
613 instance_
.getFrontendOpts().OutputFile
, // BaseInput
614 "graph.json", // Extension
615 false, // UseTemporary
616 false, // CreateMissingDirectories
619 if (err
.empty() && json_
) {
625 << "Failed to create an output file for the object graph.\n";
629 for (RecordVector::iterator it
= visitor
.record_decls().begin();
630 it
!= visitor
.record_decls().end();
632 CheckRecord(cache_
.Lookup(*it
));
635 for (MethodVector::iterator it
= visitor
.trace_decls().begin();
636 it
!= visitor
.trace_decls().end();
638 CheckTracingMethod(*it
);
648 // Main entry for checking a record declaration.
649 void CheckRecord(RecordInfo
* info
) {
653 CXXRecordDecl
* record
= info
->record();
655 // TODO: what should we do to check unions?
656 if (record
->isUnion())
659 // If this is the primary template declaration, check its specializations.
660 if (record
->isThisDeclarationADefinition() &&
661 record
->getDescribedClassTemplate()) {
662 ClassTemplateDecl
* tmpl
= record
->getDescribedClassTemplate();
663 for (ClassTemplateDecl::spec_iterator it
= tmpl
->spec_begin();
664 it
!= tmpl
->spec_end();
666 CheckClass(cache_
.Lookup(*it
));
674 // Check a class-like object (eg, class, specialization, instantiation).
675 void CheckClass(RecordInfo
* info
) {
679 // Check consistency of stack-allocated hierarchies.
680 if (info
->IsStackAllocated()) {
681 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
682 it
!= info
->GetBases().end();
684 if (!it
->second
.info()->IsStackAllocated())
685 ReportDerivesNonStackAllocated(info
, &it
->second
);
689 if (info
->RequiresTraceMethod() && !info
->GetTraceMethod())
690 ReportClassRequiresTraceMethod(info
);
693 CheckFieldsVisitor
visitor(options_
);
694 if (visitor
.ContainsInvalidFields(info
))
695 ReportClassContainsInvalidFields(info
, &visitor
.invalid_fields());
698 if (info
->IsGCDerived()) {
699 CheckLeftMostDerived(info
);
703 // TODO: Remove this exception once TreeShared is properly traced.
704 if (!info
->IsTreeShared()) {
705 CheckGCRootsVisitor visitor
;
706 if (visitor
.ContainsGCRoots(info
))
707 ReportClassContainsGCRoots(info
, &visitor
.gc_roots());
710 if (info
->NeedsFinalization())
711 CheckFinalization(info
);
717 void CheckLeftMostDerived(RecordInfo
* info
) {
718 CXXRecordDecl
* left_most
= info
->record();
719 CXXRecordDecl::base_class_iterator it
= left_most
->bases_begin();
720 while (it
!= left_most
->bases_end()) {
721 left_most
= it
->getType()->getAsCXXRecordDecl();
722 it
= left_most
->bases_begin();
724 if (!Config::IsGCBase(left_most
->getName()))
725 ReportClassMustLeftMostlyDeriveGC(info
);
728 void CheckDispatch(RecordInfo
* info
) {
729 bool finalized
= info
->IsGCFinalized();
730 CXXMethodDecl
* trace_dispatch
= info
->GetTraceDispatchMethod();
731 CXXMethodDecl
* finalize_dispatch
= info
->GetFinalizeDispatchMethod();
732 if (!trace_dispatch
&& !finalize_dispatch
)
735 CXXRecordDecl
* base
= trace_dispatch
? trace_dispatch
->getParent()
736 : finalize_dispatch
->getParent();
738 // Check that dispatch methods are defined at the base.
739 if (base
== info
->record()) {
741 ReportMissingTraceDispatchMethod(info
);
742 if (finalized
&& !finalize_dispatch
)
743 ReportMissingFinalizeDispatchMethod(info
);
744 if (!finalized
&& finalize_dispatch
) {
745 ReportClassRequiresFinalization(info
);
746 NoteUserDeclaredFinalizer(finalize_dispatch
);
750 // Check that classes implementing manual dispatch do not have vtables.
751 if (info
->record()->isPolymorphic())
752 ReportVirtualAndManualDispatch(
753 info
, trace_dispatch
? trace_dispatch
: finalize_dispatch
);
755 // If this is a non-abstract class check that it is dispatched to.
756 // TODO: Create a global variant of this local check. We can only check if
757 // the dispatch body is known in this compilation unit.
758 if (info
->IsConsideredAbstract())
761 const FunctionDecl
* defn
;
763 if (trace_dispatch
&& trace_dispatch
->isDefined(defn
)) {
764 CheckDispatchVisitor
visitor(info
);
765 visitor
.TraverseStmt(defn
->getBody());
766 if (!visitor
.dispatched_to_receiver())
767 ReportMissingTraceDispatch(defn
, info
);
770 if (finalized
&& finalize_dispatch
&& finalize_dispatch
->isDefined(defn
)) {
771 CheckDispatchVisitor
visitor(info
);
772 visitor
.TraverseStmt(defn
->getBody());
773 if (!visitor
.dispatched_to_receiver())
774 ReportMissingFinalizeDispatch(defn
, info
);
778 // TODO: Should we collect destructors similar to trace methods?
779 void CheckFinalization(RecordInfo
* info
) {
780 CXXDestructorDecl
* dtor
= info
->record()->getDestructor();
782 // For finalized classes, check the finalization method if possible.
783 if (info
->IsGCFinalized()) {
784 if (dtor
&& dtor
->hasBody()) {
785 CheckFinalizerVisitor
visitor(&cache_
);
786 visitor
.TraverseCXXMethodDecl(dtor
);
787 if (!visitor
.finalized_fields().empty()) {
788 ReportFinalizerAccessesFinalizedFields(
789 dtor
, &visitor
.finalized_fields());
795 // Don't require finalization of a mixin that has not yet been "mixed in".
796 if (info
->IsGCMixin())
799 // Report the finalization error, and proceed to print possible causes for
800 // the finalization requirement.
801 ReportClassRequiresFinalization(info
);
803 if (dtor
&& dtor
->isUserProvided())
804 NoteUserDeclaredDestructor(dtor
);
806 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
807 it
!= info
->GetBases().end();
809 if (it
->second
.info()->NeedsFinalization())
810 NoteBaseRequiresFinalization(&it
->second
);
813 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
814 it
!= info
->GetFields().end();
816 if (it
->second
.edge()->NeedsFinalization())
817 NoteField(&it
->second
, diag_field_requires_finalization_note_
);
821 // This is the main entry for tracing method definitions.
822 void CheckTracingMethod(CXXMethodDecl
* method
) {
823 RecordInfo
* parent
= cache_
.Lookup(method
->getParent());
824 if (IsIgnored(parent
))
827 // Check templated tracing methods by checking the template instantiations.
828 // Specialized templates are handled as ordinary classes.
829 if (ClassTemplateDecl
* tmpl
=
830 parent
->record()->getDescribedClassTemplate()) {
831 for (ClassTemplateDecl::spec_iterator it
= tmpl
->spec_begin();
832 it
!= tmpl
->spec_end();
834 // Check trace using each template instantiation as the holder.
835 if (IsTemplateInstantiation(*it
))
836 CheckTraceOrDispatchMethod(cache_
.Lookup(*it
), method
);
841 CheckTraceOrDispatchMethod(parent
, method
);
844 // Determine what type of tracing method this is (dispatch or trace).
845 void CheckTraceOrDispatchMethod(RecordInfo
* parent
, CXXMethodDecl
* method
) {
846 bool isTraceAfterDispatch
;
847 if (Config::IsTraceMethod(method
, &isTraceAfterDispatch
)) {
848 if (isTraceAfterDispatch
|| !parent
->GetTraceDispatchMethod()) {
849 CheckTraceMethod(parent
, method
, isTraceAfterDispatch
);
851 // Dispatch methods are checked when we identify subclasses.
855 // Check an actual trace method.
856 void CheckTraceMethod(RecordInfo
* parent
,
857 CXXMethodDecl
* trace
,
858 bool isTraceAfterDispatch
) {
859 // A non-virtual trace method must not override another trace.
860 if (!isTraceAfterDispatch
&& !trace
->isVirtual()) {
861 for (RecordInfo::Bases::iterator it
= parent
->GetBases().begin();
862 it
!= parent
->GetBases().end();
864 RecordInfo
* base
= it
->second
.info();
865 // We allow mixin bases to contain a non-virtual trace since it will
866 // never be used for dispatching.
867 if (base
->IsGCMixin())
869 if (CXXMethodDecl
* other
= base
->InheritsNonVirtualTrace())
870 ReportOverriddenNonVirtualTrace(parent
, trace
, other
);
874 CheckTraceVisitor
visitor(trace
, parent
);
875 visitor
.TraverseCXXMethodDecl(trace
);
877 for (RecordInfo::Bases::iterator it
= parent
->GetBases().begin();
878 it
!= parent
->GetBases().end();
880 if (!it
->second
.IsProperlyTraced())
881 ReportBaseRequiresTracing(parent
, trace
, it
->first
);
884 for (RecordInfo::Fields::iterator it
= parent
->GetFields().begin();
885 it
!= parent
->GetFields().end();
887 if (!it
->second
.IsProperlyTraced()) {
888 // Discontinue once an untraced-field error is found.
889 ReportFieldsRequireTracing(parent
, trace
);
895 void DumpClass(RecordInfo
* info
) {
900 json_
->Write("name", info
->record()->getQualifiedNameAsString());
901 json_
->Write("loc", GetLocString(info
->record()->getLocStart()));
902 json_
->CloseObject();
904 class DumpEdgeVisitor
: public RecursiveEdgeVisitor
{
906 DumpEdgeVisitor(JsonWriter
* json
) : json_(json
) {}
907 void DumpEdge(RecordInfo
* src
,
910 const Edge::LivenessKind
& kind
,
913 json_
->Write("src", src
->record()->getQualifiedNameAsString());
914 json_
->Write("dst", dst
->record()->getQualifiedNameAsString());
915 json_
->Write("lbl", lbl
);
916 json_
->Write("kind", kind
);
917 json_
->Write("loc", loc
);
920 Parent()->IsRawPtr() ? "raw" :
921 Parent()->IsRefPtr() ? "ref" :
922 Parent()->IsOwnPtr() ? "own" :
923 (Parent()->IsMember() ||
924 Parent()->IsWeakMember()) ? "mem" :
926 json_
->CloseObject();
929 void DumpField(RecordInfo
* src
, FieldPoint
* point
, const string
& loc
) {
933 point_
->edge()->Accept(this);
936 void AtValue(Value
* e
) override
{
937 // The liveness kind of a path from the point to this value
938 // is given by the innermost place that is non-strong.
939 Edge::LivenessKind kind
= Edge::kStrong
;
940 if (Config::IsIgnoreCycleAnnotated(point_
->field())) {
943 for (Context::iterator it
= context().begin();
944 it
!= context().end();
946 Edge::LivenessKind pointer_kind
= (*it
)->Kind();
947 if (pointer_kind
!= Edge::kStrong
) {
954 src_
, e
->value(), point_
->field()->getNameAsString(), kind
, loc_
);
964 DumpEdgeVisitor
visitor(json_
);
966 RecordInfo::Bases
& bases
= info
->GetBases();
967 for (RecordInfo::Bases::iterator it
= bases
.begin();
970 visitor
.DumpEdge(info
,
974 GetLocString(it
->second
.spec().getLocStart()));
977 RecordInfo::Fields
& fields
= info
->GetFields();
978 for (RecordInfo::Fields::iterator it
= fields
.begin();
981 visitor
.DumpField(info
,
983 GetLocString(it
->second
.field()->getLocStart()));
987 // Adds either a warning or error, based on the current handling of -Werror.
988 DiagnosticsEngine::Level
getErrorLevel() {
989 return diagnostic_
.getWarningsAsErrors() ? DiagnosticsEngine::Error
990 : DiagnosticsEngine::Warning
;
993 const string
GetLocString(SourceLocation loc
) {
994 const SourceManager
& source_manager
= instance_
.getSourceManager();
995 PresumedLoc ploc
= source_manager
.getPresumedLoc(loc
);
996 if (ploc
.isInvalid())
999 llvm::raw_string_ostream
OS(loc_str
);
1000 OS
<< ploc
.getFilename()
1001 << ":" << ploc
.getLine()
1002 << ":" << ploc
.getColumn();
1006 bool IsIgnored(RecordInfo
* record
) {
1008 !InCheckedNamespace(record
) ||
1009 IsIgnoredClass(record
) ||
1010 InIgnoredDirectory(record
);
1013 bool IsIgnoredClass(RecordInfo
* info
) {
1014 // Ignore any class prefixed by SameSizeAs. These are used in
1015 // Blink to verify class sizes and don't need checking.
1016 const string SameSizeAs
= "SameSizeAs";
1017 if (info
->name().compare(0, SameSizeAs
.size(), SameSizeAs
) == 0)
1019 return options_
.ignored_classes
.find(info
->name()) !=
1020 options_
.ignored_classes
.end();
1023 bool InIgnoredDirectory(RecordInfo
* info
) {
1025 if (!GetFilename(info
->record()->getLocStart(), &filename
))
1026 return false; // TODO: should we ignore non-existing file locations?
1027 std::vector
<string
>::iterator it
= options_
.ignored_directories
.begin();
1028 for (; it
!= options_
.ignored_directories
.end(); ++it
)
1029 if (filename
.find(*it
) != string::npos
)
1034 bool InCheckedNamespace(RecordInfo
* info
) {
1037 DeclContext
* context
= info
->record()->getDeclContext();
1038 if (context
->isRecord())
1039 return InCheckedNamespace(cache_
.Lookup(context
));
1040 if (context
->isNamespace()) {
1041 const NamespaceDecl
* decl
= dyn_cast
<NamespaceDecl
>(context
);
1042 if (decl
->isAnonymousNamespace())
1044 return options_
.checked_namespaces
.find(decl
->getNameAsString()) !=
1045 options_
.checked_namespaces
.end();
1050 bool GetFilename(SourceLocation loc
, string
* filename
) {
1051 const SourceManager
& source_manager
= instance_
.getSourceManager();
1052 SourceLocation spelling_location
= source_manager
.getSpellingLoc(loc
);
1053 PresumedLoc ploc
= source_manager
.getPresumedLoc(spelling_location
);
1054 if (ploc
.isInvalid()) {
1055 // If we're in an invalid location, we're looking at things that aren't
1056 // actually stated in the source.
1059 *filename
= ploc
.getFilename();
1063 void ReportClassMustLeftMostlyDeriveGC(RecordInfo
* info
) {
1064 SourceLocation loc
= info
->record()->getInnerLocStart();
1065 SourceManager
& manager
= instance_
.getSourceManager();
1066 FullSourceLoc
full_loc(loc
, manager
);
1067 diagnostic_
.Report(full_loc
, diag_class_must_left_mostly_derive_gc_
)
1071 void ReportClassRequiresTraceMethod(RecordInfo
* info
) {
1072 SourceLocation loc
= info
->record()->getInnerLocStart();
1073 SourceManager
& manager
= instance_
.getSourceManager();
1074 FullSourceLoc
full_loc(loc
, manager
);
1075 diagnostic_
.Report(full_loc
, diag_class_requires_trace_method_
)
1077 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
1078 it
!= info
->GetFields().end();
1080 if (!it
->second
.IsProperlyTraced())
1081 NoteFieldRequiresTracing(info
, it
->first
);
1085 void ReportBaseRequiresTracing(RecordInfo
* derived
,
1086 CXXMethodDecl
* trace
,
1087 CXXRecordDecl
* base
) {
1088 SourceLocation loc
= trace
->getLocStart();
1089 SourceManager
& manager
= instance_
.getSourceManager();
1090 FullSourceLoc
full_loc(loc
, manager
);
1091 diagnostic_
.Report(full_loc
, diag_base_requires_tracing_
)
1092 << base
<< derived
->record();
1095 void ReportFieldsRequireTracing(RecordInfo
* info
, CXXMethodDecl
* trace
) {
1096 SourceLocation loc
= trace
->getLocStart();
1097 SourceManager
& manager
= instance_
.getSourceManager();
1098 FullSourceLoc
full_loc(loc
, manager
);
1099 diagnostic_
.Report(full_loc
, diag_fields_require_tracing_
)
1101 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
1102 it
!= info
->GetFields().end();
1104 if (!it
->second
.IsProperlyTraced())
1105 NoteFieldRequiresTracing(info
, it
->first
);
1109 void ReportClassContainsInvalidFields(RecordInfo
* info
,
1110 CheckFieldsVisitor::Errors
* errors
) {
1111 SourceLocation loc
= info
->record()->getLocStart();
1112 SourceManager
& manager
= instance_
.getSourceManager();
1113 FullSourceLoc
full_loc(loc
, manager
);
1114 diagnostic_
.Report(full_loc
, diag_class_contains_invalid_fields_
)
1116 for (CheckFieldsVisitor::Errors::iterator it
= errors
->begin();
1117 it
!= errors
->end();
1119 if (it
->second
->IsRawPtr()) {
1120 NoteField(it
->first
, diag_raw_ptr_to_gc_managed_class_note_
);
1121 } else if (it
->second
->IsRefPtr()) {
1122 NoteField(it
->first
, diag_ref_ptr_to_gc_managed_class_note_
);
1123 } else if (it
->second
->IsOwnPtr()) {
1124 NoteField(it
->first
, diag_own_ptr_to_gc_managed_class_note_
);
1125 } else if (it
->second
->IsMember()) {
1126 NoteField(it
->first
, diag_member_in_unmanaged_class_note_
);
1127 } else if (it
->second
->IsValue()) {
1128 NoteField(it
->first
, diag_stack_allocated_field_note_
);
1133 void ReportClassContainsGCRoots(RecordInfo
* info
,
1134 CheckGCRootsVisitor::Errors
* errors
) {
1135 SourceLocation loc
= info
->record()->getLocStart();
1136 SourceManager
& manager
= instance_
.getSourceManager();
1137 FullSourceLoc
full_loc(loc
, manager
);
1138 for (CheckGCRootsVisitor::Errors::iterator it
= errors
->begin();
1139 it
!= errors
->end();
1141 CheckGCRootsVisitor::RootPath::iterator path
= it
->begin();
1142 FieldPoint
* point
= *path
;
1143 diagnostic_
.Report(full_loc
, diag_class_contains_gc_root_
)
1144 << info
->record() << point
->field();
1145 while (++path
!= it
->end()) {
1146 NotePartObjectContainsGCRoot(point
);
1149 NoteFieldContainsGCRoot(point
);
1153 void ReportFinalizerAccessesFinalizedFields(
1154 CXXMethodDecl
* dtor
,
1155 CheckFinalizerVisitor::Errors
* fields
) {
1156 for (CheckFinalizerVisitor::Errors::iterator it
= fields
->begin();
1157 it
!= fields
->end();
1159 SourceLocation loc
= it
->first
->getLocStart();
1160 SourceManager
& manager
= instance_
.getSourceManager();
1161 FullSourceLoc
full_loc(loc
, manager
);
1162 diagnostic_
.Report(full_loc
, diag_finalizer_accesses_finalized_field_
)
1163 << dtor
<< it
->second
->field();
1164 NoteField(it
->second
, diag_finalized_field_note_
);
1168 void ReportClassRequiresFinalization(RecordInfo
* info
) {
1169 SourceLocation loc
= info
->record()->getInnerLocStart();
1170 SourceManager
& manager
= instance_
.getSourceManager();
1171 FullSourceLoc
full_loc(loc
, manager
);
1172 diagnostic_
.Report(full_loc
, diag_class_requires_finalization_
)
1176 void ReportOverriddenNonVirtualTrace(RecordInfo
* info
,
1177 CXXMethodDecl
* trace
,
1178 CXXMethodDecl
* overridden
) {
1179 SourceLocation loc
= trace
->getLocStart();
1180 SourceManager
& manager
= instance_
.getSourceManager();
1181 FullSourceLoc
full_loc(loc
, manager
);
1182 diagnostic_
.Report(full_loc
, diag_overridden_non_virtual_trace_
)
1183 << info
->record() << overridden
->getParent();
1184 NoteOverriddenNonVirtualTrace(overridden
);
1187 void ReportMissingTraceDispatchMethod(RecordInfo
* info
) {
1188 ReportMissingDispatchMethod(info
, diag_missing_trace_dispatch_method_
);
1191 void ReportMissingFinalizeDispatchMethod(RecordInfo
* info
) {
1192 ReportMissingDispatchMethod(info
, diag_missing_finalize_dispatch_method_
);
1195 void ReportMissingDispatchMethod(RecordInfo
* info
, unsigned error
) {
1196 SourceLocation loc
= info
->record()->getInnerLocStart();
1197 SourceManager
& manager
= instance_
.getSourceManager();
1198 FullSourceLoc
full_loc(loc
, manager
);
1199 diagnostic_
.Report(full_loc
, error
) << info
->record();
1202 void ReportVirtualAndManualDispatch(RecordInfo
* info
,
1203 CXXMethodDecl
* dispatch
) {
1204 SourceLocation loc
= info
->record()->getInnerLocStart();
1205 SourceManager
& manager
= instance_
.getSourceManager();
1206 FullSourceLoc
full_loc(loc
, manager
);
1207 diagnostic_
.Report(full_loc
, diag_virtual_and_manual_dispatch_
)
1209 NoteManualDispatchMethod(dispatch
);
1212 void ReportMissingTraceDispatch(const FunctionDecl
* dispatch
,
1213 RecordInfo
* receiver
) {
1214 ReportMissingDispatch(dispatch
, receiver
, diag_missing_trace_dispatch_
);
1217 void ReportMissingFinalizeDispatch(const FunctionDecl
* dispatch
,
1218 RecordInfo
* receiver
) {
1219 ReportMissingDispatch(dispatch
, receiver
, diag_missing_finalize_dispatch_
);
1222 void ReportMissingDispatch(const FunctionDecl
* dispatch
,
1223 RecordInfo
* receiver
,
1225 SourceLocation loc
= dispatch
->getLocStart();
1226 SourceManager
& manager
= instance_
.getSourceManager();
1227 FullSourceLoc
full_loc(loc
, manager
);
1228 diagnostic_
.Report(full_loc
, error
) << receiver
->record();
1231 void ReportDerivesNonStackAllocated(RecordInfo
* info
, BasePoint
* base
) {
1232 SourceLocation loc
= base
->spec().getLocStart();
1233 SourceManager
& manager
= instance_
.getSourceManager();
1234 FullSourceLoc
full_loc(loc
, manager
);
1235 diagnostic_
.Report(full_loc
, diag_derives_non_stack_allocated_
)
1236 << info
->record() << base
->info()->record();
1239 void NoteManualDispatchMethod(CXXMethodDecl
* dispatch
) {
1240 SourceLocation loc
= dispatch
->getLocStart();
1241 SourceManager
& manager
= instance_
.getSourceManager();
1242 FullSourceLoc
full_loc(loc
, manager
);
1243 diagnostic_
.Report(full_loc
, diag_manual_dispatch_method_note_
) << dispatch
;
1246 void NoteFieldRequiresTracing(RecordInfo
* holder
, FieldDecl
* field
) {
1247 NoteField(field
, diag_field_requires_tracing_note_
);
1250 void NotePartObjectContainsGCRoot(FieldPoint
* point
) {
1251 FieldDecl
* field
= point
->field();
1252 SourceLocation loc
= field
->getLocStart();
1253 SourceManager
& manager
= instance_
.getSourceManager();
1254 FullSourceLoc
full_loc(loc
, manager
);
1255 diagnostic_
.Report(full_loc
, diag_part_object_contains_gc_root_note_
)
1256 << field
<< field
->getParent();
1259 void NoteFieldContainsGCRoot(FieldPoint
* point
) {
1260 NoteField(point
, diag_field_contains_gc_root_note_
);
1263 void NoteUserDeclaredDestructor(CXXMethodDecl
* dtor
) {
1264 SourceLocation loc
= dtor
->getLocStart();
1265 SourceManager
& manager
= instance_
.getSourceManager();
1266 FullSourceLoc
full_loc(loc
, manager
);
1267 diagnostic_
.Report(full_loc
, diag_user_declared_destructor_note_
);
1270 void NoteUserDeclaredFinalizer(CXXMethodDecl
* dtor
) {
1271 SourceLocation loc
= dtor
->getLocStart();
1272 SourceManager
& manager
= instance_
.getSourceManager();
1273 FullSourceLoc
full_loc(loc
, manager
);
1274 diagnostic_
.Report(full_loc
, diag_user_declared_finalizer_note_
);
1277 void NoteBaseRequiresFinalization(BasePoint
* base
) {
1278 SourceLocation loc
= base
->spec().getLocStart();
1279 SourceManager
& manager
= instance_
.getSourceManager();
1280 FullSourceLoc
full_loc(loc
, manager
);
1281 diagnostic_
.Report(full_loc
, diag_base_requires_finalization_note_
)
1282 << base
->info()->record();
1285 void NoteField(FieldPoint
* point
, unsigned note
) {
1286 NoteField(point
->field(), note
);
1289 void NoteField(FieldDecl
* field
, unsigned note
) {
1290 SourceLocation loc
= field
->getLocStart();
1291 SourceManager
& manager
= instance_
.getSourceManager();
1292 FullSourceLoc
full_loc(loc
, manager
);
1293 diagnostic_
.Report(full_loc
, note
) << field
;
1296 void NoteOverriddenNonVirtualTrace(CXXMethodDecl
* overridden
) {
1297 SourceLocation loc
= overridden
->getLocStart();
1298 SourceManager
& manager
= instance_
.getSourceManager();
1299 FullSourceLoc
full_loc(loc
, manager
);
1300 diagnostic_
.Report(full_loc
, diag_overridden_non_virtual_trace_note_
)
1304 unsigned diag_class_must_left_mostly_derive_gc_
;
1305 unsigned diag_class_requires_trace_method_
;
1306 unsigned diag_base_requires_tracing_
;
1307 unsigned diag_fields_require_tracing_
;
1308 unsigned diag_class_contains_invalid_fields_
;
1309 unsigned diag_class_contains_gc_root_
;
1310 unsigned diag_class_requires_finalization_
;
1311 unsigned diag_finalizer_accesses_finalized_field_
;
1312 unsigned diag_overridden_non_virtual_trace_
;
1313 unsigned diag_missing_trace_dispatch_method_
;
1314 unsigned diag_missing_finalize_dispatch_method_
;
1315 unsigned diag_virtual_and_manual_dispatch_
;
1316 unsigned diag_missing_trace_dispatch_
;
1317 unsigned diag_missing_finalize_dispatch_
;
1318 unsigned diag_derives_non_stack_allocated_
;
1320 unsigned diag_field_requires_tracing_note_
;
1321 unsigned diag_raw_ptr_to_gc_managed_class_note_
;
1322 unsigned diag_ref_ptr_to_gc_managed_class_note_
;
1323 unsigned diag_own_ptr_to_gc_managed_class_note_
;
1324 unsigned diag_stack_allocated_field_note_
;
1325 unsigned diag_member_in_unmanaged_class_note_
;
1326 unsigned diag_part_object_contains_gc_root_note_
;
1327 unsigned diag_field_contains_gc_root_note_
;
1328 unsigned diag_finalized_field_note_
;
1329 unsigned diag_user_declared_destructor_note_
;
1330 unsigned diag_user_declared_finalizer_note_
;
1331 unsigned diag_base_requires_finalization_note_
;
1332 unsigned diag_field_requires_finalization_note_
;
1333 unsigned diag_overridden_non_virtual_trace_note_
;
1334 unsigned diag_manual_dispatch_method_note_
;
1336 CompilerInstance
& instance_
;
1337 DiagnosticsEngine
& diagnostic_
;
1338 BlinkGCPluginOptions options_
;
1343 class BlinkGCPluginAction
: public PluginASTAction
{
1345 BlinkGCPluginAction() {}
1348 // Overridden from PluginASTAction:
1349 virtual ASTConsumer
* CreateASTConsumer(CompilerInstance
& instance
,
1350 llvm::StringRef ref
) {
1351 return new BlinkGCPluginConsumer(instance
, options_
);
1354 virtual bool ParseArgs(const CompilerInstance
& instance
,
1355 const std::vector
<string
>& args
) {
1358 for (size_t i
= 0; i
< args
.size() && parsed
; ++i
) {
1359 if (args
[i
] == "enable-oilpan") {
1360 options_
.enable_oilpan
= true;
1361 } else if (args
[i
] == "dump-graph") {
1362 options_
.dump_graph
= true;
1365 llvm::errs() << "Unknown blink-gc-plugin argument: " << args
[i
] << "\n";
1373 BlinkGCPluginOptions options_
;
1378 static FrontendPluginRegistry::Add
<BlinkGCPluginAction
> X(
1380 "Check Blink GC invariants");