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
13 #include "BlinkGCPluginOptions.h"
14 #include "CheckFieldsVisitor.h"
15 #include "CheckFinalizerVisitor.h"
16 #include "CheckGCRootsVisitor.h"
17 #include "CheckTraceVisitor.h"
18 #include "CollectVisitor.h"
20 #include "JsonWriter.h"
21 #include "RecordInfo.h"
23 #include "clang/AST/AST.h"
24 #include "clang/AST/ASTConsumer.h"
25 #include "clang/AST/RecursiveASTVisitor.h"
26 #include "clang/Frontend/CompilerInstance.h"
27 #include "clang/Frontend/FrontendPluginRegistry.h"
28 #include "clang/Sema/Sema.h"
30 using namespace clang
;
35 const char kClassMustLeftMostlyDeriveGC
[] =
36 "[blink-gc] Class %0 must derive its GC base in the left-most position.";
38 const char kClassRequiresTraceMethod
[] =
39 "[blink-gc] Class %0 requires a trace method.";
41 const char kBaseRequiresTracing
[] =
42 "[blink-gc] Base class %0 of derived class %1 requires tracing.";
44 const char kBaseRequiresTracingNote
[] =
45 "[blink-gc] Untraced base class %0 declared here:";
47 const char kFieldsRequireTracing
[] =
48 "[blink-gc] Class %0 has untraced fields that require tracing.";
50 const char kFieldRequiresTracingNote
[] =
51 "[blink-gc] Untraced field %0 declared here:";
53 const char kClassContainsInvalidFields
[] =
54 "[blink-gc] Class %0 contains invalid fields.";
56 const char kClassContainsGCRoot
[] =
57 "[blink-gc] Class %0 contains GC root in field %1.";
59 const char kClassRequiresFinalization
[] =
60 "[blink-gc] Class %0 requires finalization.";
62 const char kClassDoesNotRequireFinalization
[] =
63 "[blink-gc] Class %0 may not require finalization.";
65 const char kFinalizerAccessesFinalizedField
[] =
66 "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
68 const char kFinalizerAccessesEagerlyFinalizedField
[] =
69 "[blink-gc] Finalizer %0 accesses eagerly finalized field %1.";
71 const char kRawPtrToGCManagedClassNote
[] =
72 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
74 const char kRefPtrToGCManagedClassNote
[] =
75 "[blink-gc] RefPtr field %0 to a GC managed class declared here:";
77 const char kReferencePtrToGCManagedClassNote
[] =
78 "[blink-gc] Reference pointer field %0 to a GC managed class"
81 const char kOwnPtrToGCManagedClassNote
[] =
82 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
84 const char kMemberToGCUnmanagedClassNote
[] =
85 "[blink-gc] Member field %0 to non-GC managed class declared here:";
87 const char kStackAllocatedFieldNote
[] =
88 "[blink-gc] Stack-allocated field %0 declared here:";
90 const char kMemberInUnmanagedClassNote
[] =
91 "[blink-gc] Member field %0 in unmanaged class declared here:";
93 const char kPartObjectToGCDerivedClassNote
[] =
94 "[blink-gc] Part-object field %0 to a GC derived class declared here:";
96 const char kPartObjectContainsGCRootNote
[] =
97 "[blink-gc] Field %0 with embedded GC root in %1 declared here:";
99 const char kFieldContainsGCRootNote
[] =
100 "[blink-gc] Field %0 defining a GC root declared here:";
102 const char kOverriddenNonVirtualTrace
[] =
103 "[blink-gc] Class %0 overrides non-virtual trace of base class %1.";
105 const char kOverriddenNonVirtualTraceNote
[] =
106 "[blink-gc] Non-virtual trace method declared here:";
108 const char kMissingTraceDispatchMethod
[] =
109 "[blink-gc] Class %0 is missing manual trace dispatch.";
111 const char kMissingFinalizeDispatchMethod
[] =
112 "[blink-gc] Class %0 is missing manual finalize dispatch.";
114 const char kVirtualAndManualDispatch
[] =
115 "[blink-gc] Class %0 contains or inherits virtual methods"
116 " but implements manual dispatching.";
118 const char kMissingTraceDispatch
[] =
119 "[blink-gc] Missing dispatch to class %0 in manual trace dispatch.";
121 const char kMissingFinalizeDispatch
[] =
122 "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch.";
124 const char kFinalizedFieldNote
[] =
125 "[blink-gc] Potentially finalized field %0 declared here:";
127 const char kEagerlyFinalizedFieldNote
[] =
128 "[blink-gc] Field %0 having eagerly finalized value, declared here:";
130 const char kUserDeclaredDestructorNote
[] =
131 "[blink-gc] User-declared destructor declared here:";
133 const char kUserDeclaredFinalizerNote
[] =
134 "[blink-gc] User-declared finalizer declared here:";
136 const char kBaseRequiresFinalizationNote
[] =
137 "[blink-gc] Base class %0 requiring finalization declared here:";
139 const char kFieldRequiresFinalizationNote
[] =
140 "[blink-gc] Field %0 requiring finalization declared here:";
142 const char kManualDispatchMethodNote
[] =
143 "[blink-gc] Manual dispatch %0 declared here:";
145 const char kDerivesNonStackAllocated
[] =
146 "[blink-gc] Stack-allocated class %0 derives class %1"
147 " which is not stack allocated.";
149 const char kClassOverridesNew
[] =
150 "[blink-gc] Garbage collected class %0"
151 " is not permitted to override its new operator.";
153 const char kClassDeclaresPureVirtualTrace
[] =
154 "[blink-gc] Garbage collected class %0"
155 " is not permitted to declare a pure-virtual trace method.";
157 const char kLeftMostBaseMustBePolymorphic
[] =
158 "[blink-gc] Left-most base class %0 of derived class %1"
159 " must be polymorphic.";
161 const char kBaseClassMustDeclareVirtualTrace
[] =
162 "[blink-gc] Left-most base class %0 of derived class %1"
163 " must define a virtual trace method.";
165 const char kClassMustDeclareGCMixinTraceMethod
[] =
166 "[blink-gc] Class %0 which inherits from GarbageCollectedMixin must"
167 " locally declare and override trace(Visitor*)";
169 // Use a local RAV implementation to simply collect all FunctionDecls marked for
170 // late template parsing. This happens with the flag -fdelayed-template-parsing,
171 // which is on by default in MSVC-compatible mode.
172 std::set
<FunctionDecl
*> GetLateParsedFunctionDecls(TranslationUnitDecl
* decl
) {
173 struct Visitor
: public RecursiveASTVisitor
<Visitor
> {
174 bool VisitFunctionDecl(FunctionDecl
* function_decl
) {
175 if (function_decl
->isLateTemplateParsed())
176 late_parsed_decls
.insert(function_decl
);
180 std::set
<FunctionDecl
*> late_parsed_decls
;
182 v
.TraverseDecl(decl
);
183 return v
.late_parsed_decls
;
186 // This visitor checks that a method contains within its body, a call to a
187 // method on the provided receiver class. This is used to check manual
188 // dispatching for trace and finalize methods.
189 class CheckDispatchVisitor
: public RecursiveASTVisitor
<CheckDispatchVisitor
> {
191 CheckDispatchVisitor(RecordInfo
* receiver
)
192 : receiver_(receiver
), dispatched_to_receiver_(false) {}
194 bool dispatched_to_receiver() { return dispatched_to_receiver_
; }
196 bool VisitMemberExpr(MemberExpr
* member
) {
197 if (CXXMethodDecl
* fn
= dyn_cast
<CXXMethodDecl
>(member
->getMemberDecl())) {
198 if (fn
->getParent() == receiver_
->record())
199 dispatched_to_receiver_
= true;
204 bool VisitUnresolvedMemberExpr(UnresolvedMemberExpr
* member
) {
205 for (Decl
* decl
: member
->decls()) {
206 if (CXXMethodDecl
* method
= dyn_cast
<CXXMethodDecl
>(decl
)) {
207 if (method
->getParent() == receiver_
->record() &&
208 Config::GetTraceMethodType(method
) ==
209 Config::TRACE_AFTER_DISPATCH_METHOD
) {
210 dispatched_to_receiver_
= true;
219 RecordInfo
* receiver_
;
220 bool dispatched_to_receiver_
;
223 class EmptyStmtVisitor
224 : public RecursiveASTVisitor
<EmptyStmtVisitor
> {
226 static bool isEmpty(Stmt
* stmt
) {
227 EmptyStmtVisitor visitor
;
228 visitor
.TraverseStmt(stmt
);
229 return visitor
.empty_
;
232 bool WalkUpFromCompoundStmt(CompoundStmt
* stmt
) {
233 empty_
= stmt
->body_empty();
236 bool VisitStmt(Stmt
*) {
241 EmptyStmtVisitor() : empty_(true) {}
245 // Main class containing checks for various invariants of the Blink
246 // garbage collection infrastructure.
247 class BlinkGCPluginConsumer
: public ASTConsumer
{
249 BlinkGCPluginConsumer(CompilerInstance
& instance
,
250 const BlinkGCPluginOptions
& options
)
251 : instance_(instance
),
252 diagnostic_(instance
.getDiagnostics()),
256 // Only check structures in the blink and WebKit namespaces.
257 options_
.checked_namespaces
.insert("blink");
259 // Ignore GC implementation files.
260 options_
.ignored_directories
.push_back("/heap/");
262 // Register warning/error messages.
263 diag_class_must_left_mostly_derive_gc_
= diagnostic_
.getCustomDiagID(
264 getErrorLevel(), kClassMustLeftMostlyDeriveGC
);
265 diag_class_requires_trace_method_
=
266 diagnostic_
.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod
);
267 diag_base_requires_tracing_
=
268 diagnostic_
.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing
);
269 diag_fields_require_tracing_
=
270 diagnostic_
.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing
);
271 diag_class_contains_invalid_fields_
= diagnostic_
.getCustomDiagID(
272 getErrorLevel(), kClassContainsInvalidFields
);
273 diag_class_contains_invalid_fields_warning_
= diagnostic_
.getCustomDiagID(
274 DiagnosticsEngine::Warning
, kClassContainsInvalidFields
);
275 diag_class_contains_gc_root_
=
276 diagnostic_
.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot
);
277 diag_class_requires_finalization_
= diagnostic_
.getCustomDiagID(
278 getErrorLevel(), kClassRequiresFinalization
);
279 diag_class_does_not_require_finalization_
= diagnostic_
.getCustomDiagID(
280 DiagnosticsEngine::Warning
, kClassDoesNotRequireFinalization
);
281 diag_finalizer_accesses_finalized_field_
= diagnostic_
.getCustomDiagID(
282 getErrorLevel(), kFinalizerAccessesFinalizedField
);
283 diag_finalizer_eagerly_finalized_field_
= diagnostic_
.getCustomDiagID(
284 getErrorLevel(), kFinalizerAccessesEagerlyFinalizedField
);
285 diag_overridden_non_virtual_trace_
= diagnostic_
.getCustomDiagID(
286 getErrorLevel(), kOverriddenNonVirtualTrace
);
287 diag_missing_trace_dispatch_method_
= diagnostic_
.getCustomDiagID(
288 getErrorLevel(), kMissingTraceDispatchMethod
);
289 diag_missing_finalize_dispatch_method_
= diagnostic_
.getCustomDiagID(
290 getErrorLevel(), kMissingFinalizeDispatchMethod
);
291 diag_virtual_and_manual_dispatch_
=
292 diagnostic_
.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch
);
293 diag_missing_trace_dispatch_
=
294 diagnostic_
.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch
);
295 diag_missing_finalize_dispatch_
=
296 diagnostic_
.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch
);
297 diag_derives_non_stack_allocated_
=
298 diagnostic_
.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated
);
299 diag_class_overrides_new_
=
300 diagnostic_
.getCustomDiagID(getErrorLevel(), kClassOverridesNew
);
301 diag_class_declares_pure_virtual_trace_
= diagnostic_
.getCustomDiagID(
302 getErrorLevel(), kClassDeclaresPureVirtualTrace
);
303 diag_left_most_base_must_be_polymorphic_
= diagnostic_
.getCustomDiagID(
304 getErrorLevel(), kLeftMostBaseMustBePolymorphic
);
305 diag_base_class_must_declare_virtual_trace_
= diagnostic_
.getCustomDiagID(
306 getErrorLevel(), kBaseClassMustDeclareVirtualTrace
);
307 diag_class_must_declare_gc_mixin_trace_method_
=
308 diagnostic_
.getCustomDiagID(getErrorLevel(),
309 kClassMustDeclareGCMixinTraceMethod
);
311 // Register note messages.
312 diag_base_requires_tracing_note_
= diagnostic_
.getCustomDiagID(
313 DiagnosticsEngine::Note
, kBaseRequiresTracingNote
);
314 diag_field_requires_tracing_note_
= diagnostic_
.getCustomDiagID(
315 DiagnosticsEngine::Note
, kFieldRequiresTracingNote
);
316 diag_raw_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
317 DiagnosticsEngine::Note
, kRawPtrToGCManagedClassNote
);
318 diag_ref_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
319 DiagnosticsEngine::Note
, kRefPtrToGCManagedClassNote
);
320 diag_reference_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
321 DiagnosticsEngine::Note
, kReferencePtrToGCManagedClassNote
);
322 diag_own_ptr_to_gc_managed_class_note_
= diagnostic_
.getCustomDiagID(
323 DiagnosticsEngine::Note
, kOwnPtrToGCManagedClassNote
);
324 diag_member_to_gc_unmanaged_class_note_
= diagnostic_
.getCustomDiagID(
325 DiagnosticsEngine::Note
, kMemberToGCUnmanagedClassNote
);
326 diag_stack_allocated_field_note_
= diagnostic_
.getCustomDiagID(
327 DiagnosticsEngine::Note
, kStackAllocatedFieldNote
);
328 diag_member_in_unmanaged_class_note_
= diagnostic_
.getCustomDiagID(
329 DiagnosticsEngine::Note
, kMemberInUnmanagedClassNote
);
330 diag_part_object_to_gc_derived_class_note_
= diagnostic_
.getCustomDiagID(
331 DiagnosticsEngine::Note
, kPartObjectToGCDerivedClassNote
);
332 diag_part_object_contains_gc_root_note_
= diagnostic_
.getCustomDiagID(
333 DiagnosticsEngine::Note
, kPartObjectContainsGCRootNote
);
334 diag_field_contains_gc_root_note_
= diagnostic_
.getCustomDiagID(
335 DiagnosticsEngine::Note
, kFieldContainsGCRootNote
);
336 diag_finalized_field_note_
= diagnostic_
.getCustomDiagID(
337 DiagnosticsEngine::Note
, kFinalizedFieldNote
);
338 diag_eagerly_finalized_field_note_
= diagnostic_
.getCustomDiagID(
339 DiagnosticsEngine::Note
, kEagerlyFinalizedFieldNote
);
340 diag_user_declared_destructor_note_
= diagnostic_
.getCustomDiagID(
341 DiagnosticsEngine::Note
, kUserDeclaredDestructorNote
);
342 diag_user_declared_finalizer_note_
= diagnostic_
.getCustomDiagID(
343 DiagnosticsEngine::Note
, kUserDeclaredFinalizerNote
);
344 diag_base_requires_finalization_note_
= diagnostic_
.getCustomDiagID(
345 DiagnosticsEngine::Note
, kBaseRequiresFinalizationNote
);
346 diag_field_requires_finalization_note_
= diagnostic_
.getCustomDiagID(
347 DiagnosticsEngine::Note
, kFieldRequiresFinalizationNote
);
348 diag_overridden_non_virtual_trace_note_
= diagnostic_
.getCustomDiagID(
349 DiagnosticsEngine::Note
, kOverriddenNonVirtualTraceNote
);
350 diag_manual_dispatch_method_note_
= diagnostic_
.getCustomDiagID(
351 DiagnosticsEngine::Note
, kManualDispatchMethodNote
);
354 void HandleTranslationUnit(ASTContext
& context
) override
{
355 // Don't run the plugin if the compilation unit is already invalid.
356 if (diagnostic_
.hasErrorOccurred())
359 ParseFunctionTemplates(context
.getTranslationUnitDecl());
361 CollectVisitor visitor
;
362 visitor
.TraverseDecl(context
.getTranslationUnitDecl());
364 if (options_
.dump_graph
) {
366 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
367 json_
= JsonWriter::from(instance_
.createOutputFile(
371 true, // RemoveFileOnSignal
372 instance_
.getFrontendOpts().OutputFile
, // BaseInput
373 "graph.json", // Extension
374 false, // UseTemporary
375 false, // CreateMissingDirectories
384 << "Failed to create an output file for the object graph.\n";
388 for (CollectVisitor::RecordVector::iterator it
=
389 visitor
.record_decls().begin();
390 it
!= visitor
.record_decls().end();
392 CheckRecord(cache_
.Lookup(*it
));
395 for (CollectVisitor::MethodVector::iterator it
=
396 visitor
.trace_decls().begin();
397 it
!= visitor
.trace_decls().end();
399 CheckTracingMethod(*it
);
409 void ParseFunctionTemplates(TranslationUnitDecl
* decl
) {
410 if (!instance_
.getLangOpts().DelayedTemplateParsing
)
411 return; // Nothing to do.
413 std::set
<FunctionDecl
*> late_parsed_decls
=
414 GetLateParsedFunctionDecls(decl
);
415 clang::Sema
& sema
= instance_
.getSema();
417 for (const FunctionDecl
* fd
: late_parsed_decls
) {
418 assert(fd
->isLateTemplateParsed());
420 if (!Config::IsTraceMethod(fd
))
423 if (instance_
.getSourceManager().isInSystemHeader(
424 instance_
.getSourceManager().getSpellingLoc(fd
->getLocation())))
427 // Force parsing and AST building of the yet-uninstantiated function
428 // template trace method bodies.
429 clang::LateParsedTemplate
* lpt
= sema
.LateParsedTemplateMap
[fd
];
430 sema
.LateTemplateParser(sema
.OpaqueParser
, *lpt
);
434 // Main entry for checking a record declaration.
435 void CheckRecord(RecordInfo
* info
) {
439 CXXRecordDecl
* record
= info
->record();
441 // TODO: what should we do to check unions?
442 if (record
->isUnion())
445 // If this is the primary template declaration, check its specializations.
446 if (record
->isThisDeclarationADefinition() &&
447 record
->getDescribedClassTemplate()) {
448 ClassTemplateDecl
* tmpl
= record
->getDescribedClassTemplate();
449 for (ClassTemplateDecl::spec_iterator it
= tmpl
->spec_begin();
450 it
!= tmpl
->spec_end();
452 CheckClass(cache_
.Lookup(*it
));
460 // Check a class-like object (eg, class, specialization, instantiation).
461 void CheckClass(RecordInfo
* info
) {
465 // Check consistency of stack-allocated hierarchies.
466 if (info
->IsStackAllocated()) {
467 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
468 it
!= info
->GetBases().end();
470 if (!it
->second
.info()->IsStackAllocated())
471 ReportDerivesNonStackAllocated(info
, &it
->second
);
475 if (CXXMethodDecl
* trace
= info
->GetTraceMethod()) {
477 ReportClassDeclaresPureVirtualTrace(info
, trace
);
478 } else if (info
->RequiresTraceMethod()) {
479 ReportClassRequiresTraceMethod(info
);
482 // Check polymorphic classes that are GC-derived or have a trace method.
483 if (info
->record()->hasDefinition() && info
->record()->isPolymorphic()) {
484 // TODO: Check classes that inherit a trace method.
485 CXXMethodDecl
* trace
= info
->GetTraceMethod();
486 if (trace
|| info
->IsGCDerived())
487 CheckPolymorphicClass(info
, trace
);
491 CheckFieldsVisitor
visitor(options_
);
492 if (visitor
.ContainsInvalidFields(info
))
493 ReportClassContainsInvalidFields(info
, &visitor
.invalid_fields());
496 if (info
->IsGCDerived()) {
498 if (!info
->IsGCMixin()) {
499 CheckLeftMostDerived(info
);
501 if (CXXMethodDecl
* newop
= info
->DeclaresNewOperator())
502 if (!Config::IsIgnoreAnnotated(newop
))
503 ReportClassOverridesNew(info
, newop
);
504 if (info
->IsGCMixinInstance()) {
505 // Require that declared GCMixin implementations
506 // also provide a trace() override.
507 if (info
->DeclaresGCMixinMethods()
508 && !info
->DeclaresLocalTraceMethod())
509 ReportClassMustDeclareGCMixinTraceMethod(info
);
514 CheckGCRootsVisitor visitor
;
515 if (visitor
.ContainsGCRoots(info
))
516 ReportClassContainsGCRoots(info
, &visitor
.gc_roots());
519 if (info
->NeedsFinalization())
520 CheckFinalization(info
);
522 if (options_
.warn_unneeded_finalizer
&& info
->IsGCFinalized())
523 CheckUnneededFinalization(info
);
529 CXXRecordDecl
* GetDependentTemplatedDecl(const Type
& type
) {
530 const TemplateSpecializationType
* tmpl_type
=
531 type
.getAs
<TemplateSpecializationType
>();
535 TemplateDecl
* tmpl_decl
= tmpl_type
->getTemplateName().getAsTemplateDecl();
539 return dyn_cast
<CXXRecordDecl
>(tmpl_decl
->getTemplatedDecl());
542 // The GC infrastructure assumes that if the vtable of a polymorphic
543 // base-class is not initialized for a given object (ie, it is partially
544 // initialized) then the object does not need to be traced. Thus, we must
545 // ensure that any polymorphic class with a trace method does not have any
546 // tractable fields that are initialized before we are sure that the vtable
547 // and the trace method are both defined. There are two cases that need to
548 // hold to satisfy that assumption:
550 // 1. If trace is virtual, then it must be defined in the left-most base.
551 // This ensures that if the vtable is initialized then it contains a pointer
552 // to the trace method.
554 // 2. If trace is non-virtual, then the trace method is defined and we must
555 // ensure that the left-most base defines a vtable. This ensures that the
556 // first thing to be initialized when constructing the object is the vtable
558 void CheckPolymorphicClass(RecordInfo
* info
, CXXMethodDecl
* trace
) {
559 CXXRecordDecl
* left_most
= info
->record();
560 CXXRecordDecl::base_class_iterator it
= left_most
->bases_begin();
561 CXXRecordDecl
* left_most_base
= 0;
562 while (it
!= left_most
->bases_end()) {
563 left_most_base
= it
->getType()->getAsCXXRecordDecl();
564 if (!left_most_base
&& it
->getType()->isDependentType())
565 left_most_base
= RecordInfo::GetDependentTemplatedDecl(*it
->getType());
567 // TODO: Find a way to correctly check actual instantiations
568 // for dependent types. The escape below will be hit, eg, when
569 // we have a primary template with no definition and
570 // specializations for each case (such as SupplementBase) in
571 // which case we don't succeed in checking the required
573 if (!left_most_base
|| !left_most_base
->hasDefinition())
576 StringRef name
= left_most_base
->getName();
577 // We know GCMixin base defines virtual trace.
578 if (Config::IsGCMixinBase(name
))
581 // Stop with the left-most prior to a safe polymorphic base (a safe base
582 // is non-polymorphic and contains no fields).
583 if (Config::IsSafePolymorphicBase(name
))
586 left_most
= left_most_base
;
587 it
= left_most
->bases_begin();
590 if (RecordInfo
* left_most_info
= cache_
.Lookup(left_most
)) {
592 // Check condition (1):
593 if (trace
&& trace
->isVirtual()) {
594 if (CXXMethodDecl
* trace
= left_most_info
->GetTraceMethod()) {
595 if (trace
->isVirtual())
598 ReportBaseClassMustDeclareVirtualTrace(info
, left_most
);
602 // Check condition (2):
603 if (DeclaresVirtualMethods(left_most
))
605 if (left_most_base
) {
606 // Get the base next to the "safe polymorphic base"
607 if (it
!= left_most
->bases_end())
609 if (it
!= left_most
->bases_end()) {
610 if (CXXRecordDecl
* next_base
= it
->getType()->getAsCXXRecordDecl()) {
611 if (CXXRecordDecl
* next_left_most
= GetLeftMostBase(next_base
)) {
612 if (DeclaresVirtualMethods(next_left_most
))
614 ReportLeftMostBaseMustBePolymorphic(info
, next_left_most
);
620 ReportLeftMostBaseMustBePolymorphic(info
, left_most
);
624 CXXRecordDecl
* GetLeftMostBase(CXXRecordDecl
* left_most
) {
625 CXXRecordDecl::base_class_iterator it
= left_most
->bases_begin();
626 while (it
!= left_most
->bases_end()) {
627 if (it
->getType()->isDependentType())
628 left_most
= RecordInfo::GetDependentTemplatedDecl(*it
->getType());
630 left_most
= it
->getType()->getAsCXXRecordDecl();
631 if (!left_most
|| !left_most
->hasDefinition())
633 it
= left_most
->bases_begin();
638 bool DeclaresVirtualMethods(CXXRecordDecl
* decl
) {
639 CXXRecordDecl::method_iterator it
= decl
->method_begin();
640 for (; it
!= decl
->method_end(); ++it
)
641 if (it
->isVirtual() && !it
->isPure())
646 void CheckLeftMostDerived(RecordInfo
* info
) {
647 CXXRecordDecl
* left_most
= GetLeftMostBase(info
->record());
650 if (!Config::IsGCBase(left_most
->getName()))
651 ReportClassMustLeftMostlyDeriveGC(info
);
654 void CheckDispatch(RecordInfo
* info
) {
655 bool finalized
= info
->IsGCFinalized();
656 CXXMethodDecl
* trace_dispatch
= info
->GetTraceDispatchMethod();
657 CXXMethodDecl
* finalize_dispatch
= info
->GetFinalizeDispatchMethod();
658 if (!trace_dispatch
&& !finalize_dispatch
)
661 CXXRecordDecl
* base
= trace_dispatch
? trace_dispatch
->getParent()
662 : finalize_dispatch
->getParent();
664 // Check that dispatch methods are defined at the base.
665 if (base
== info
->record()) {
667 ReportMissingTraceDispatchMethod(info
);
668 if (finalized
&& !finalize_dispatch
)
669 ReportMissingFinalizeDispatchMethod(info
);
670 if (!finalized
&& finalize_dispatch
) {
671 ReportClassRequiresFinalization(info
);
672 NoteUserDeclaredFinalizer(finalize_dispatch
);
676 // Check that classes implementing manual dispatch do not have vtables.
677 if (info
->record()->isPolymorphic())
678 ReportVirtualAndManualDispatch(
679 info
, trace_dispatch
? trace_dispatch
: finalize_dispatch
);
681 // If this is a non-abstract class check that it is dispatched to.
682 // TODO: Create a global variant of this local check. We can only check if
683 // the dispatch body is known in this compilation unit.
684 if (info
->IsConsideredAbstract())
687 const FunctionDecl
* defn
;
689 if (trace_dispatch
&& trace_dispatch
->isDefined(defn
)) {
690 CheckDispatchVisitor
visitor(info
);
691 visitor
.TraverseStmt(defn
->getBody());
692 if (!visitor
.dispatched_to_receiver())
693 ReportMissingTraceDispatch(defn
, info
);
696 if (finalized
&& finalize_dispatch
&& finalize_dispatch
->isDefined(defn
)) {
697 CheckDispatchVisitor
visitor(info
);
698 visitor
.TraverseStmt(defn
->getBody());
699 if (!visitor
.dispatched_to_receiver())
700 ReportMissingFinalizeDispatch(defn
, info
);
704 // TODO: Should we collect destructors similar to trace methods?
705 void CheckFinalization(RecordInfo
* info
) {
706 CXXDestructorDecl
* dtor
= info
->record()->getDestructor();
708 // For finalized classes, check the finalization method if possible.
709 if (info
->IsGCFinalized()) {
710 if (dtor
&& dtor
->hasBody()) {
711 CheckFinalizerVisitor
visitor(&cache_
, info
->IsEagerlyFinalized());
712 visitor
.TraverseCXXMethodDecl(dtor
);
713 if (!visitor
.finalized_fields().empty()) {
714 ReportFinalizerAccessesFinalizedFields(
715 dtor
, &visitor
.finalized_fields());
721 // Don't require finalization of a mixin that has not yet been "mixed in".
722 if (info
->IsGCMixin())
725 // Report the finalization error, and proceed to print possible causes for
726 // the finalization requirement.
727 ReportClassRequiresFinalization(info
);
729 if (dtor
&& dtor
->isUserProvided())
730 NoteUserDeclaredDestructor(dtor
);
732 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
733 it
!= info
->GetBases().end();
735 if (it
->second
.info()->NeedsFinalization())
736 NoteBaseRequiresFinalization(&it
->second
);
739 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
740 it
!= info
->GetFields().end();
742 if (it
->second
.edge()->NeedsFinalization())
743 NoteField(&it
->second
, diag_field_requires_finalization_note_
);
747 void CheckUnneededFinalization(RecordInfo
* info
) {
748 if (!HasNonEmptyFinalizer(info
))
749 ReportClassDoesNotRequireFinalization(info
);
752 bool HasNonEmptyFinalizer(RecordInfo
* info
) {
753 CXXDestructorDecl
* dtor
= info
->record()->getDestructor();
754 if (dtor
&& dtor
->isUserProvided()) {
755 if (!dtor
->hasBody() || !EmptyStmtVisitor::isEmpty(dtor
->getBody()))
758 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
759 it
!= info
->GetBases().end();
761 if (HasNonEmptyFinalizer(it
->second
.info()))
764 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
765 it
!= info
->GetFields().end();
767 if (it
->second
.edge()->NeedsFinalization())
773 // This is the main entry for tracing method definitions.
774 void CheckTracingMethod(CXXMethodDecl
* method
) {
775 RecordInfo
* parent
= cache_
.Lookup(method
->getParent());
776 if (IsIgnored(parent
))
779 // Check templated tracing methods by checking the template instantiations.
780 // Specialized templates are handled as ordinary classes.
781 if (ClassTemplateDecl
* tmpl
=
782 parent
->record()->getDescribedClassTemplate()) {
783 for (ClassTemplateDecl::spec_iterator it
= tmpl
->spec_begin();
784 it
!= tmpl
->spec_end();
786 // Check trace using each template instantiation as the holder.
787 if (Config::IsTemplateInstantiation(*it
))
788 CheckTraceOrDispatchMethod(cache_
.Lookup(*it
), method
);
793 CheckTraceOrDispatchMethod(parent
, method
);
796 // Determine what type of tracing method this is (dispatch or trace).
797 void CheckTraceOrDispatchMethod(RecordInfo
* parent
, CXXMethodDecl
* method
) {
798 Config::TraceMethodType trace_type
= Config::GetTraceMethodType(method
);
799 if (trace_type
== Config::TRACE_AFTER_DISPATCH_METHOD
||
800 trace_type
== Config::TRACE_AFTER_DISPATCH_IMPL_METHOD
||
801 !parent
->GetTraceDispatchMethod()) {
802 CheckTraceMethod(parent
, method
, trace_type
);
804 // Dispatch methods are checked when we identify subclasses.
807 // Check an actual trace method.
808 void CheckTraceMethod(RecordInfo
* parent
,
809 CXXMethodDecl
* trace
,
810 Config::TraceMethodType trace_type
) {
811 // A trace method must not override any non-virtual trace methods.
812 if (trace_type
== Config::TRACE_METHOD
) {
813 for (RecordInfo::Bases::iterator it
= parent
->GetBases().begin();
814 it
!= parent
->GetBases().end();
816 RecordInfo
* base
= it
->second
.info();
817 if (CXXMethodDecl
* other
= base
->InheritsNonVirtualTrace())
818 ReportOverriddenNonVirtualTrace(parent
, trace
, other
);
822 CheckTraceVisitor
visitor(trace
, parent
, &cache_
);
823 visitor
.TraverseCXXMethodDecl(trace
);
825 // Skip reporting if this trace method is a just delegate to
826 // traceImpl (or traceAfterDispatchImpl) method. We will report on
827 // CheckTraceMethod on traceImpl method.
828 if (visitor
.delegates_to_traceimpl())
831 for (RecordInfo::Bases::iterator it
= parent
->GetBases().begin();
832 it
!= parent
->GetBases().end();
834 if (!it
->second
.IsProperlyTraced())
835 ReportBaseRequiresTracing(parent
, trace
, it
->first
);
838 for (RecordInfo::Fields::iterator it
= parent
->GetFields().begin();
839 it
!= parent
->GetFields().end();
841 if (!it
->second
.IsProperlyTraced()) {
842 // Discontinue once an untraced-field error is found.
843 ReportFieldsRequireTracing(parent
, trace
);
849 void DumpClass(RecordInfo
* info
) {
854 json_
->Write("name", info
->record()->getQualifiedNameAsString());
855 json_
->Write("loc", GetLocString(info
->record()->getLocStart()));
856 json_
->CloseObject();
858 class DumpEdgeVisitor
: public RecursiveEdgeVisitor
{
860 DumpEdgeVisitor(JsonWriter
* json
) : json_(json
) {}
861 void DumpEdge(RecordInfo
* src
,
864 const Edge::LivenessKind
& kind
,
867 json_
->Write("src", src
->record()->getQualifiedNameAsString());
868 json_
->Write("dst", dst
->record()->getQualifiedNameAsString());
869 json_
->Write("lbl", lbl
);
870 json_
->Write("kind", kind
);
871 json_
->Write("loc", loc
);
874 Parent()->IsRawPtr() ?
875 (static_cast<RawPtr
*>(Parent())->HasReferenceType() ?
876 "reference" : "raw") :
877 Parent()->IsRefPtr() ? "ref" :
878 Parent()->IsOwnPtr() ? "own" :
879 (Parent()->IsMember() ||
880 Parent()->IsWeakMember()) ? "mem" :
882 json_
->CloseObject();
885 void DumpField(RecordInfo
* src
, FieldPoint
* point
, const string
& loc
) {
889 point_
->edge()->Accept(this);
892 void AtValue(Value
* e
) override
{
893 // The liveness kind of a path from the point to this value
894 // is given by the innermost place that is non-strong.
895 Edge::LivenessKind kind
= Edge::kStrong
;
896 if (Config::IsIgnoreCycleAnnotated(point_
->field())) {
899 for (Context::iterator it
= context().begin();
900 it
!= context().end();
902 Edge::LivenessKind pointer_kind
= (*it
)->Kind();
903 if (pointer_kind
!= Edge::kStrong
) {
910 src_
, e
->value(), point_
->field()->getNameAsString(), kind
, loc_
);
920 DumpEdgeVisitor
visitor(json_
);
922 RecordInfo::Bases
& bases
= info
->GetBases();
923 for (RecordInfo::Bases::iterator it
= bases
.begin();
926 visitor
.DumpEdge(info
,
930 GetLocString(it
->second
.spec().getLocStart()));
933 RecordInfo::Fields
& fields
= info
->GetFields();
934 for (RecordInfo::Fields::iterator it
= fields
.begin();
937 visitor
.DumpField(info
,
939 GetLocString(it
->second
.field()->getLocStart()));
943 // Adds either a warning or error, based on the current handling of -Werror.
944 DiagnosticsEngine::Level
getErrorLevel() {
945 return diagnostic_
.getWarningsAsErrors() ? DiagnosticsEngine::Error
946 : DiagnosticsEngine::Warning
;
949 const string
GetLocString(SourceLocation loc
) {
950 const SourceManager
& source_manager
= instance_
.getSourceManager();
951 PresumedLoc ploc
= source_manager
.getPresumedLoc(loc
);
952 if (ploc
.isInvalid())
955 llvm::raw_string_ostream
OS(loc_str
);
956 OS
<< ploc
.getFilename()
957 << ":" << ploc
.getLine()
958 << ":" << ploc
.getColumn();
962 bool IsIgnored(RecordInfo
* record
) {
964 !InCheckedNamespace(record
) ||
965 IsIgnoredClass(record
) ||
966 InIgnoredDirectory(record
);
969 bool IsIgnoredClass(RecordInfo
* info
) {
970 // Ignore any class prefixed by SameSizeAs. These are used in
971 // Blink to verify class sizes and don't need checking.
972 const string SameSizeAs
= "SameSizeAs";
973 if (info
->name().compare(0, SameSizeAs
.size(), SameSizeAs
) == 0)
975 return options_
.ignored_classes
.find(info
->name()) !=
976 options_
.ignored_classes
.end();
979 bool InIgnoredDirectory(RecordInfo
* info
) {
981 if (!GetFilename(info
->record()->getLocStart(), &filename
))
982 return false; // TODO: should we ignore non-existing file locations?
983 #if defined(LLVM_ON_WIN32)
984 std::replace(filename
.begin(), filename
.end(), '\\', '/');
986 std::vector
<string
>::iterator it
= options_
.ignored_directories
.begin();
987 for (; it
!= options_
.ignored_directories
.end(); ++it
)
988 if (filename
.find(*it
) != string::npos
)
993 bool InCheckedNamespace(RecordInfo
* info
) {
996 for (DeclContext
* context
= info
->record()->getDeclContext();
997 !context
->isTranslationUnit();
998 context
= context
->getParent()) {
999 if (NamespaceDecl
* decl
= dyn_cast
<NamespaceDecl
>(context
)) {
1000 if (decl
->isAnonymousNamespace())
1002 if (options_
.checked_namespaces
.find(decl
->getNameAsString()) !=
1003 options_
.checked_namespaces
.end()) {
1011 bool GetFilename(SourceLocation loc
, string
* filename
) {
1012 const SourceManager
& source_manager
= instance_
.getSourceManager();
1013 SourceLocation spelling_location
= source_manager
.getSpellingLoc(loc
);
1014 PresumedLoc ploc
= source_manager
.getPresumedLoc(spelling_location
);
1015 if (ploc
.isInvalid()) {
1016 // If we're in an invalid location, we're looking at things that aren't
1017 // actually stated in the source.
1020 *filename
= ploc
.getFilename();
1024 void ReportClassMustLeftMostlyDeriveGC(RecordInfo
* info
) {
1025 SourceLocation loc
= info
->record()->getInnerLocStart();
1026 SourceManager
& manager
= instance_
.getSourceManager();
1027 FullSourceLoc
full_loc(loc
, manager
);
1028 diagnostic_
.Report(full_loc
, diag_class_must_left_mostly_derive_gc_
)
1032 void ReportClassRequiresTraceMethod(RecordInfo
* info
) {
1033 SourceLocation loc
= info
->record()->getInnerLocStart();
1034 SourceManager
& manager
= instance_
.getSourceManager();
1035 FullSourceLoc
full_loc(loc
, manager
);
1036 diagnostic_
.Report(full_loc
, diag_class_requires_trace_method_
)
1039 for (RecordInfo::Bases::iterator it
= info
->GetBases().begin();
1040 it
!= info
->GetBases().end();
1042 if (it
->second
.NeedsTracing().IsNeeded())
1043 NoteBaseRequiresTracing(&it
->second
);
1046 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
1047 it
!= info
->GetFields().end();
1049 if (!it
->second
.IsProperlyTraced())
1050 NoteFieldRequiresTracing(info
, it
->first
);
1054 void ReportBaseRequiresTracing(RecordInfo
* derived
,
1055 CXXMethodDecl
* trace
,
1056 CXXRecordDecl
* base
) {
1057 SourceLocation loc
= trace
->getLocStart();
1058 SourceManager
& manager
= instance_
.getSourceManager();
1059 FullSourceLoc
full_loc(loc
, manager
);
1060 diagnostic_
.Report(full_loc
, diag_base_requires_tracing_
)
1061 << base
<< derived
->record();
1064 void ReportFieldsRequireTracing(RecordInfo
* info
, CXXMethodDecl
* trace
) {
1065 SourceLocation loc
= trace
->getLocStart();
1066 SourceManager
& manager
= instance_
.getSourceManager();
1067 FullSourceLoc
full_loc(loc
, manager
);
1068 diagnostic_
.Report(full_loc
, diag_fields_require_tracing_
)
1070 for (RecordInfo::Fields::iterator it
= info
->GetFields().begin();
1071 it
!= info
->GetFields().end();
1073 if (!it
->second
.IsProperlyTraced())
1074 NoteFieldRequiresTracing(info
, it
->first
);
1078 void ReportClassContainsInvalidFields(RecordInfo
* info
,
1079 CheckFieldsVisitor::Errors
* errors
) {
1080 SourceLocation loc
= info
->record()->getLocStart();
1081 SourceManager
& manager
= instance_
.getSourceManager();
1082 FullSourceLoc
full_loc(loc
, manager
);
1083 bool only_warnings
= options_
.warn_raw_ptr
;
1084 for (CheckFieldsVisitor::Errors::iterator it
= errors
->begin();
1085 only_warnings
&& it
!= errors
->end();
1087 if (!CheckFieldsVisitor::IsWarning(it
->second
))
1088 only_warnings
= false;
1090 diagnostic_
.Report(full_loc
, only_warnings
?
1091 diag_class_contains_invalid_fields_warning_
:
1092 diag_class_contains_invalid_fields_
)
1094 for (CheckFieldsVisitor::Errors::iterator it
= errors
->begin();
1095 it
!= errors
->end();
1098 if (CheckFieldsVisitor::IsRawPtrError(it
->second
)) {
1099 error
= diag_raw_ptr_to_gc_managed_class_note_
;
1100 } else if (CheckFieldsVisitor::IsReferencePtrError(it
->second
)) {
1101 error
= diag_reference_ptr_to_gc_managed_class_note_
;
1102 } else if (it
->second
== CheckFieldsVisitor::kRefPtrToGCManaged
) {
1103 error
= diag_ref_ptr_to_gc_managed_class_note_
;
1104 } else if (it
->second
== CheckFieldsVisitor::kOwnPtrToGCManaged
) {
1105 error
= diag_own_ptr_to_gc_managed_class_note_
;
1106 } else if (it
->second
== CheckFieldsVisitor::kMemberToGCUnmanaged
) {
1107 error
= diag_member_to_gc_unmanaged_class_note_
;
1108 } else if (it
->second
== CheckFieldsVisitor::kMemberInUnmanaged
) {
1109 error
= diag_member_in_unmanaged_class_note_
;
1110 } else if (it
->second
== CheckFieldsVisitor::kPtrFromHeapToStack
) {
1111 error
= diag_stack_allocated_field_note_
;
1112 } else if (it
->second
== CheckFieldsVisitor::kGCDerivedPartObject
) {
1113 error
= diag_part_object_to_gc_derived_class_note_
;
1115 assert(false && "Unknown field error");
1117 NoteField(it
->first
, error
);
1121 void ReportClassContainsGCRoots(RecordInfo
* info
,
1122 CheckGCRootsVisitor::Errors
* errors
) {
1123 SourceLocation loc
= info
->record()->getLocStart();
1124 SourceManager
& manager
= instance_
.getSourceManager();
1125 FullSourceLoc
full_loc(loc
, manager
);
1126 for (CheckGCRootsVisitor::Errors::iterator it
= errors
->begin();
1127 it
!= errors
->end();
1129 CheckGCRootsVisitor::RootPath::iterator path
= it
->begin();
1130 FieldPoint
* point
= *path
;
1131 diagnostic_
.Report(full_loc
, diag_class_contains_gc_root_
)
1132 << info
->record() << point
->field();
1133 while (++path
!= it
->end()) {
1134 NotePartObjectContainsGCRoot(point
);
1137 NoteFieldContainsGCRoot(point
);
1141 void ReportFinalizerAccessesFinalizedFields(
1142 CXXMethodDecl
* dtor
,
1143 CheckFinalizerVisitor::Errors
* fields
) {
1144 for (CheckFinalizerVisitor::Errors::iterator it
= fields
->begin();
1145 it
!= fields
->end();
1147 SourceLocation loc
= it
->member
->getLocStart();
1148 SourceManager
& manager
= instance_
.getSourceManager();
1149 bool as_eagerly_finalized
= it
->as_eagerly_finalized
;
1150 unsigned diag_error
= as_eagerly_finalized
?
1151 diag_finalizer_eagerly_finalized_field_
:
1152 diag_finalizer_accesses_finalized_field_
;
1153 unsigned diag_note
= as_eagerly_finalized
?
1154 diag_eagerly_finalized_field_note_
:
1155 diag_finalized_field_note_
;
1156 FullSourceLoc
full_loc(loc
, manager
);
1157 diagnostic_
.Report(full_loc
, diag_error
)
1158 << dtor
<< it
->field
->field();
1159 NoteField(it
->field
, diag_note
);
1163 void ReportClassRequiresFinalization(RecordInfo
* info
) {
1164 SourceLocation loc
= info
->record()->getInnerLocStart();
1165 SourceManager
& manager
= instance_
.getSourceManager();
1166 FullSourceLoc
full_loc(loc
, manager
);
1167 diagnostic_
.Report(full_loc
, diag_class_requires_finalization_
)
1171 void ReportClassDoesNotRequireFinalization(RecordInfo
* info
) {
1172 SourceLocation loc
= info
->record()->getInnerLocStart();
1173 SourceManager
& manager
= instance_
.getSourceManager();
1174 FullSourceLoc
full_loc(loc
, manager
);
1175 diagnostic_
.Report(full_loc
, diag_class_does_not_require_finalization_
)
1179 void ReportClassMustDeclareGCMixinTraceMethod(RecordInfo
* info
) {
1180 SourceLocation loc
= info
->record()->getInnerLocStart();
1181 SourceManager
& manager
= instance_
.getSourceManager();
1182 FullSourceLoc
full_loc(loc
, manager
);
1184 full_loc
, diag_class_must_declare_gc_mixin_trace_method_
)
1188 void ReportOverriddenNonVirtualTrace(RecordInfo
* info
,
1189 CXXMethodDecl
* trace
,
1190 CXXMethodDecl
* overridden
) {
1191 SourceLocation loc
= trace
->getLocStart();
1192 SourceManager
& manager
= instance_
.getSourceManager();
1193 FullSourceLoc
full_loc(loc
, manager
);
1194 diagnostic_
.Report(full_loc
, diag_overridden_non_virtual_trace_
)
1195 << info
->record() << overridden
->getParent();
1196 NoteOverriddenNonVirtualTrace(overridden
);
1199 void ReportMissingTraceDispatchMethod(RecordInfo
* info
) {
1200 ReportMissingDispatchMethod(info
, diag_missing_trace_dispatch_method_
);
1203 void ReportMissingFinalizeDispatchMethod(RecordInfo
* info
) {
1204 ReportMissingDispatchMethod(info
, diag_missing_finalize_dispatch_method_
);
1207 void ReportMissingDispatchMethod(RecordInfo
* info
, unsigned error
) {
1208 SourceLocation loc
= info
->record()->getInnerLocStart();
1209 SourceManager
& manager
= instance_
.getSourceManager();
1210 FullSourceLoc
full_loc(loc
, manager
);
1211 diagnostic_
.Report(full_loc
, error
) << info
->record();
1214 void ReportVirtualAndManualDispatch(RecordInfo
* info
,
1215 CXXMethodDecl
* dispatch
) {
1216 SourceLocation loc
= info
->record()->getInnerLocStart();
1217 SourceManager
& manager
= instance_
.getSourceManager();
1218 FullSourceLoc
full_loc(loc
, manager
);
1219 diagnostic_
.Report(full_loc
, diag_virtual_and_manual_dispatch_
)
1221 NoteManualDispatchMethod(dispatch
);
1224 void ReportMissingTraceDispatch(const FunctionDecl
* dispatch
,
1225 RecordInfo
* receiver
) {
1226 ReportMissingDispatch(dispatch
, receiver
, diag_missing_trace_dispatch_
);
1229 void ReportMissingFinalizeDispatch(const FunctionDecl
* dispatch
,
1230 RecordInfo
* receiver
) {
1231 ReportMissingDispatch(dispatch
, receiver
, diag_missing_finalize_dispatch_
);
1234 void ReportMissingDispatch(const FunctionDecl
* dispatch
,
1235 RecordInfo
* receiver
,
1237 SourceLocation loc
= dispatch
->getLocStart();
1238 SourceManager
& manager
= instance_
.getSourceManager();
1239 FullSourceLoc
full_loc(loc
, manager
);
1240 diagnostic_
.Report(full_loc
, error
) << receiver
->record();
1243 void ReportDerivesNonStackAllocated(RecordInfo
* info
, BasePoint
* base
) {
1244 SourceLocation loc
= base
->spec().getLocStart();
1245 SourceManager
& manager
= instance_
.getSourceManager();
1246 FullSourceLoc
full_loc(loc
, manager
);
1247 diagnostic_
.Report(full_loc
, diag_derives_non_stack_allocated_
)
1248 << info
->record() << base
->info()->record();
1251 void ReportClassOverridesNew(RecordInfo
* info
, CXXMethodDecl
* newop
) {
1252 SourceLocation loc
= newop
->getLocStart();
1253 SourceManager
& manager
= instance_
.getSourceManager();
1254 FullSourceLoc
full_loc(loc
, manager
);
1255 diagnostic_
.Report(full_loc
, diag_class_overrides_new_
) << info
->record();
1258 void ReportClassDeclaresPureVirtualTrace(RecordInfo
* info
,
1259 CXXMethodDecl
* trace
) {
1260 SourceLocation loc
= trace
->getLocStart();
1261 SourceManager
& manager
= instance_
.getSourceManager();
1262 FullSourceLoc
full_loc(loc
, manager
);
1263 diagnostic_
.Report(full_loc
, diag_class_declares_pure_virtual_trace_
)
1267 void ReportLeftMostBaseMustBePolymorphic(RecordInfo
* derived
,
1268 CXXRecordDecl
* base
) {
1269 SourceLocation loc
= base
->getLocStart();
1270 SourceManager
& manager
= instance_
.getSourceManager();
1271 FullSourceLoc
full_loc(loc
, manager
);
1272 diagnostic_
.Report(full_loc
, diag_left_most_base_must_be_polymorphic_
)
1273 << base
<< derived
->record();
1276 void ReportBaseClassMustDeclareVirtualTrace(RecordInfo
* derived
,
1277 CXXRecordDecl
* base
) {
1278 SourceLocation loc
= base
->getLocStart();
1279 SourceManager
& manager
= instance_
.getSourceManager();
1280 FullSourceLoc
full_loc(loc
, manager
);
1281 diagnostic_
.Report(full_loc
, diag_base_class_must_declare_virtual_trace_
)
1282 << base
<< derived
->record();
1285 void NoteManualDispatchMethod(CXXMethodDecl
* dispatch
) {
1286 SourceLocation loc
= dispatch
->getLocStart();
1287 SourceManager
& manager
= instance_
.getSourceManager();
1288 FullSourceLoc
full_loc(loc
, manager
);
1289 diagnostic_
.Report(full_loc
, diag_manual_dispatch_method_note_
) << dispatch
;
1292 void NoteBaseRequiresTracing(BasePoint
* base
) {
1293 SourceLocation loc
= base
->spec().getLocStart();
1294 SourceManager
& manager
= instance_
.getSourceManager();
1295 FullSourceLoc
full_loc(loc
, manager
);
1296 diagnostic_
.Report(full_loc
, diag_base_requires_tracing_note_
)
1297 << base
->info()->record();
1300 void NoteFieldRequiresTracing(RecordInfo
* holder
, FieldDecl
* field
) {
1301 NoteField(field
, diag_field_requires_tracing_note_
);
1304 void NotePartObjectContainsGCRoot(FieldPoint
* point
) {
1305 FieldDecl
* field
= point
->field();
1306 SourceLocation loc
= field
->getLocStart();
1307 SourceManager
& manager
= instance_
.getSourceManager();
1308 FullSourceLoc
full_loc(loc
, manager
);
1309 diagnostic_
.Report(full_loc
, diag_part_object_contains_gc_root_note_
)
1310 << field
<< field
->getParent();
1313 void NoteFieldContainsGCRoot(FieldPoint
* point
) {
1314 NoteField(point
, diag_field_contains_gc_root_note_
);
1317 void NoteUserDeclaredDestructor(CXXMethodDecl
* dtor
) {
1318 SourceLocation loc
= dtor
->getLocStart();
1319 SourceManager
& manager
= instance_
.getSourceManager();
1320 FullSourceLoc
full_loc(loc
, manager
);
1321 diagnostic_
.Report(full_loc
, diag_user_declared_destructor_note_
);
1324 void NoteUserDeclaredFinalizer(CXXMethodDecl
* dtor
) {
1325 SourceLocation loc
= dtor
->getLocStart();
1326 SourceManager
& manager
= instance_
.getSourceManager();
1327 FullSourceLoc
full_loc(loc
, manager
);
1328 diagnostic_
.Report(full_loc
, diag_user_declared_finalizer_note_
);
1331 void NoteBaseRequiresFinalization(BasePoint
* base
) {
1332 SourceLocation loc
= base
->spec().getLocStart();
1333 SourceManager
& manager
= instance_
.getSourceManager();
1334 FullSourceLoc
full_loc(loc
, manager
);
1335 diagnostic_
.Report(full_loc
, diag_base_requires_finalization_note_
)
1336 << base
->info()->record();
1339 void NoteField(FieldPoint
* point
, unsigned note
) {
1340 NoteField(point
->field(), note
);
1343 void NoteField(FieldDecl
* field
, unsigned note
) {
1344 SourceLocation loc
= field
->getLocStart();
1345 SourceManager
& manager
= instance_
.getSourceManager();
1346 FullSourceLoc
full_loc(loc
, manager
);
1347 diagnostic_
.Report(full_loc
, note
) << field
;
1350 void NoteOverriddenNonVirtualTrace(CXXMethodDecl
* overridden
) {
1351 SourceLocation loc
= overridden
->getLocStart();
1352 SourceManager
& manager
= instance_
.getSourceManager();
1353 FullSourceLoc
full_loc(loc
, manager
);
1354 diagnostic_
.Report(full_loc
, diag_overridden_non_virtual_trace_note_
)
1358 unsigned diag_class_must_left_mostly_derive_gc_
;
1359 unsigned diag_class_requires_trace_method_
;
1360 unsigned diag_base_requires_tracing_
;
1361 unsigned diag_fields_require_tracing_
;
1362 unsigned diag_class_contains_invalid_fields_
;
1363 unsigned diag_class_contains_invalid_fields_warning_
;
1364 unsigned diag_class_contains_gc_root_
;
1365 unsigned diag_class_requires_finalization_
;
1366 unsigned diag_class_does_not_require_finalization_
;
1367 unsigned diag_finalizer_accesses_finalized_field_
;
1368 unsigned diag_finalizer_eagerly_finalized_field_
;
1369 unsigned diag_overridden_non_virtual_trace_
;
1370 unsigned diag_missing_trace_dispatch_method_
;
1371 unsigned diag_missing_finalize_dispatch_method_
;
1372 unsigned diag_virtual_and_manual_dispatch_
;
1373 unsigned diag_missing_trace_dispatch_
;
1374 unsigned diag_missing_finalize_dispatch_
;
1375 unsigned diag_derives_non_stack_allocated_
;
1376 unsigned diag_class_overrides_new_
;
1377 unsigned diag_class_declares_pure_virtual_trace_
;
1378 unsigned diag_left_most_base_must_be_polymorphic_
;
1379 unsigned diag_base_class_must_declare_virtual_trace_
;
1380 unsigned diag_class_must_declare_gc_mixin_trace_method_
;
1382 unsigned diag_base_requires_tracing_note_
;
1383 unsigned diag_field_requires_tracing_note_
;
1384 unsigned diag_raw_ptr_to_gc_managed_class_note_
;
1385 unsigned diag_ref_ptr_to_gc_managed_class_note_
;
1386 unsigned diag_reference_ptr_to_gc_managed_class_note_
;
1387 unsigned diag_own_ptr_to_gc_managed_class_note_
;
1388 unsigned diag_member_to_gc_unmanaged_class_note_
;
1389 unsigned diag_stack_allocated_field_note_
;
1390 unsigned diag_member_in_unmanaged_class_note_
;
1391 unsigned diag_part_object_to_gc_derived_class_note_
;
1392 unsigned diag_part_object_contains_gc_root_note_
;
1393 unsigned diag_field_contains_gc_root_note_
;
1394 unsigned diag_finalized_field_note_
;
1395 unsigned diag_eagerly_finalized_field_note_
;
1396 unsigned diag_user_declared_destructor_note_
;
1397 unsigned diag_user_declared_finalizer_note_
;
1398 unsigned diag_base_requires_finalization_note_
;
1399 unsigned diag_field_requires_finalization_note_
;
1400 unsigned diag_overridden_non_virtual_trace_note_
;
1401 unsigned diag_manual_dispatch_method_note_
;
1403 CompilerInstance
& instance_
;
1404 DiagnosticsEngine
& diagnostic_
;
1405 BlinkGCPluginOptions options_
;
1410 class BlinkGCPluginAction
: public PluginASTAction
{
1412 BlinkGCPluginAction() {}
1415 // Overridden from PluginASTAction:
1416 virtual std::unique_ptr
<ASTConsumer
> CreateASTConsumer(
1417 CompilerInstance
& instance
,
1418 llvm::StringRef ref
) {
1419 return llvm::make_unique
<BlinkGCPluginConsumer
>(instance
, options_
);
1422 virtual bool ParseArgs(const CompilerInstance
& instance
,
1423 const std::vector
<string
>& args
) {
1426 for (size_t i
= 0; i
< args
.size() && parsed
; ++i
) {
1427 if (args
[i
] == "enable-oilpan") {
1428 options_
.enable_oilpan
= true;
1429 } else if (args
[i
] == "dump-graph") {
1430 options_
.dump_graph
= true;
1431 } else if (args
[i
] == "warn-raw-ptr") {
1432 options_
.warn_raw_ptr
= true;
1433 } else if (args
[i
] == "warn-unneeded-finalizer") {
1434 options_
.warn_unneeded_finalizer
= true;
1437 llvm::errs() << "Unknown blink-gc-plugin argument: " << args
[i
] << "\n";
1445 BlinkGCPluginOptions options_
;
1450 static FrontendPluginRegistry::Add
<BlinkGCPluginAction
> X(
1452 "Check Blink GC invariants");