Release the Settings API.
[chromium-blink-merge.git] / tools / clang / blink_gc_plugin / BlinkGCPlugin.cpp
blob47ab43c92997fcca0aef20f8aca6d848d7dd7feb
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.
7 //
8 // Errors are described at:
9 // http://www.chromium.org/developers/blink-gc-plugin-errors
11 #include "Config.h"
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;
22 using std::string;
24 namespace {
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) {}
121 bool enable_oilpan;
122 bool dump_graph;
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);
135 if (!spec)
136 return false;
137 switch (spec->getTemplateSpecializationKind()) {
138 case TSK_ImplicitInstantiation:
139 case TSK_ExplicitInstantiationDefinition:
140 return true;
141 case TSK_Undeclared:
142 case TSK_ExplicitSpecialization:
143 return false;
144 // TODO: unsupported cases.
145 case TSK_ExplicitInstantiationDeclaration:
146 return false;
148 assert(false && "Unknown template specialization kind");
151 // This visitor collects the entry points for the checker.
152 class CollectVisitor : public RecursiveASTVisitor<CollectVisitor> {
153 public:
154 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);
165 return true;
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);
172 return true;
175 private:
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> {
187 private:
188 // Simple visitor to determine if the content of a field might be collected
189 // during finalization.
190 class MightBeCollectedVisitor : public EdgeVisitor {
191 public:
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();
198 } else {
199 edge->AcceptMembers(this);
203 private:
204 bool might_be_collected_;
207 public:
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()) {
218 case OO_Arrow:
219 case OO_Subscript:
220 this->WalkUpFromCallExpr(expr);
221 default:
222 return true;
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;
233 return true;
236 bool VisitMemberExpr(MemberExpr* member) {
237 FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl());
238 if (!field)
239 return true;
241 RecordInfo* info = cache_->Lookup(field->getParent());
242 if (!info)
243 return true;
245 RecordInfo::Fields::iterator it = info->GetFields().find(field);
246 if (it == info->GetFields().end())
247 return true;
249 if (blacklist_context_ && MightBeCollected(&it->second))
250 finalized_fields_.push_back(std::make_pair(member, &it->second));
251 return true;
254 bool MightBeCollected(FieldPoint* point) {
255 MightBeCollectedVisitor visitor;
256 point->edge()->Accept(&visitor);
257 return visitor.might_be_collected();
260 private:
261 bool blacklist_context_;
262 Errors finalized_fields_;
263 RecordCache* cache_;
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> {
270 public:
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;
281 return true;
284 private:
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> {
293 public:
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();
308 ++it) {
309 if (it->first->getNameAsString() == name) {
310 MarkTraced(it);
311 break;
314 } else {
315 RecordInfo::Fields::iterator it = info_->GetFields().find(field);
316 if (it != info_->GetFields().end())
317 MarkTraced(it);
319 return true;
322 // If this is a weak callback function we only check field tracing.
323 if (IsWeakCallback())
324 return true;
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());
346 return true;
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();
362 return true;
365 private:
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())
374 return;
375 it->second.MarkTraced();
378 CXXMethodDecl* trace_;
379 RecordInfo* info_;
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 {
385 public:
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();
396 ++it) {
397 current_.push_back(&it->second);
398 it->second.edge()->Accept(this);
399 current_.pop_back();
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())
407 return;
409 // If the value is a part object, then continue checking for roots.
410 for (Context::iterator it = context().begin();
411 it != context().end();
412 ++it) {
413 if (!(*it)->IsCollection())
414 return;
416 ContainsGCRoots(edge->value());
419 void VisitPersistent(Persistent* edge) override {
420 gc_roots_.push_back(current_);
423 void AtCollection(Collection* edge) override {
424 if (edge->is_root())
425 gc_roots_.push_back(current_);
428 protected:
429 RootPath current_;
430 Errors gc_roots_;
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 {
438 public:
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();
454 ++it) {
455 context().clear();
456 current_ = &it->second;
457 current_->edge()->Accept(this);
459 return !invalid_fields_.empty();
462 void VisitMember(Member* edge) override {
463 if (managed_host_)
464 return;
465 // A member is allowed to appear in the context of a root.
466 for (Context::iterator it = context().begin();
467 it != context().end();
468 ++it) {
469 if ((*it)->Kind() == Edge::kRoot)
470 return;
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())
478 return;
480 if (!stack_allocated_host_ && edge->value()->IsStackAllocated()) {
481 invalid_fields_.push_back(std::make_pair(current_, edge));
482 return;
485 if (!Parent() || !edge->value()->IsGCAllocated())
486 return;
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()));
497 return;
500 return;
503 if (Parent()->IsRawPtr() || Parent()->IsRefPtr() || Parent()->IsOwnPtr()) {
504 invalid_fields_.push_back(std::make_pair(current_, Parent()));
505 return;
509 private:
510 const BlinkGCPluginOptions& options_;
511 FieldPoint* current_;
512 bool stack_allocated_host_;
513 bool managed_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 {
520 public:
521 BlinkGCPluginConsumer(CompilerInstance& instance,
522 const BlinkGCPluginOptions& options)
523 : instance_(instance),
524 diagnostic_(instance.getDiagnostics()),
525 options_(options),
526 json_(0) {
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) {
606 string err;
607 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
608 json_ = JsonWriter::from(instance_.createOutputFile(
609 "", // OutputPath
610 err, // Errors
611 true, // Binary
612 true, // RemoveFileOnSignal
613 instance_.getFrontendOpts().OutputFile, // BaseInput
614 "graph.json", // Extension
615 false, // UseTemporary
616 false, // CreateMissingDirectories
617 0, // ResultPathName
618 0)); // TempPathName
619 if (err.empty() && json_) {
620 json_->OpenList();
621 } else {
622 json_ = 0;
623 llvm::errs()
624 << "[blink-gc] "
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();
631 ++it) {
632 CheckRecord(cache_.Lookup(*it));
635 for (MethodVector::iterator it = visitor.trace_decls().begin();
636 it != visitor.trace_decls().end();
637 ++it) {
638 CheckTracingMethod(*it);
641 if (json_) {
642 json_->CloseList();
643 delete json_;
644 json_ = 0;
648 // Main entry for checking a record declaration.
649 void CheckRecord(RecordInfo* info) {
650 if (IsIgnored(info))
651 return;
653 CXXRecordDecl* record = info->record();
655 // TODO: what should we do to check unions?
656 if (record->isUnion())
657 return;
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();
665 ++it) {
666 CheckClass(cache_.Lookup(*it));
668 return;
671 CheckClass(info);
674 // Check a class-like object (eg, class, specialization, instantiation).
675 void CheckClass(RecordInfo* info) {
676 if (!info)
677 return;
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();
683 ++it) {
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);
701 CheckDispatch(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);
714 DumpClass(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)
733 return;
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()) {
740 if (!trace_dispatch)
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())
759 return;
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());
792 return;
795 // Don't require finalization of a mixin that has not yet been "mixed in".
796 if (info->IsGCMixin())
797 return;
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();
808 ++it) {
809 if (it->second.info()->NeedsFinalization())
810 NoteBaseRequiresFinalization(&it->second);
813 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
814 it != info->GetFields().end();
815 ++it) {
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))
825 return;
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();
833 ++it) {
834 // Check trace using each template instantiation as the holder.
835 if (IsTemplateInstantiation(*it))
836 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
838 return;
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();
863 ++it) {
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())
868 continue;
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();
879 ++it) {
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();
886 ++it) {
887 if (!it->second.IsProperlyTraced()) {
888 // Discontinue once an untraced-field error is found.
889 ReportFieldsRequireTracing(parent, trace);
890 break;
895 void DumpClass(RecordInfo* info) {
896 if (!json_)
897 return;
899 json_->OpenObject();
900 json_->Write("name", info->record()->getQualifiedNameAsString());
901 json_->Write("loc", GetLocString(info->record()->getLocStart()));
902 json_->CloseObject();
904 class DumpEdgeVisitor : public RecursiveEdgeVisitor {
905 public:
906 DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
907 void DumpEdge(RecordInfo* src,
908 RecordInfo* dst,
909 const string& lbl,
910 const Edge::LivenessKind& kind,
911 const string& loc) {
912 json_->OpenObject();
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);
918 json_->Write("ptr",
919 !Parent() ? "val" :
920 Parent()->IsRawPtr() ? "raw" :
921 Parent()->IsRefPtr() ? "ref" :
922 Parent()->IsOwnPtr() ? "own" :
923 (Parent()->IsMember() ||
924 Parent()->IsWeakMember()) ? "mem" :
925 "val");
926 json_->CloseObject();
929 void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) {
930 src_ = src;
931 point_ = point;
932 loc_ = 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())) {
941 kind = Edge::kWeak;
942 } else {
943 for (Context::iterator it = context().begin();
944 it != context().end();
945 ++it) {
946 Edge::LivenessKind pointer_kind = (*it)->Kind();
947 if (pointer_kind != Edge::kStrong) {
948 kind = pointer_kind;
949 break;
953 DumpEdge(
954 src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
957 private:
958 JsonWriter* json_;
959 RecordInfo* src_;
960 FieldPoint* point_;
961 string loc_;
964 DumpEdgeVisitor visitor(json_);
966 RecordInfo::Bases& bases = info->GetBases();
967 for (RecordInfo::Bases::iterator it = bases.begin();
968 it != bases.end();
969 ++it) {
970 visitor.DumpEdge(info,
971 it->second.info(),
972 "<super>",
973 Edge::kStrong,
974 GetLocString(it->second.spec().getLocStart()));
977 RecordInfo::Fields& fields = info->GetFields();
978 for (RecordInfo::Fields::iterator it = fields.begin();
979 it != fields.end();
980 ++it) {
981 visitor.DumpField(info,
982 &it->second,
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())
997 return "";
998 string loc_str;
999 llvm::raw_string_ostream OS(loc_str);
1000 OS << ploc.getFilename()
1001 << ":" << ploc.getLine()
1002 << ":" << ploc.getColumn();
1003 return OS.str();
1006 bool IsIgnored(RecordInfo* record) {
1007 return !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)
1018 return true;
1019 return options_.ignored_classes.find(info->name()) !=
1020 options_.ignored_classes.end();
1023 bool InIgnoredDirectory(RecordInfo* info) {
1024 string filename;
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)
1030 return true;
1031 return false;
1034 bool InCheckedNamespace(RecordInfo* info) {
1035 if (!info)
1036 return false;
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())
1043 return false;
1044 return options_.checked_namespaces.find(decl->getNameAsString()) !=
1045 options_.checked_namespaces.end();
1047 return false;
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.
1057 return false;
1059 *filename = ploc.getFilename();
1060 return true;
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_)
1068 << info->record();
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_)
1076 << info->record();
1077 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1078 it != info->GetFields().end();
1079 ++it) {
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_)
1100 << info->record();
1101 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1102 it != info->GetFields().end();
1103 ++it) {
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_)
1115 << info->record();
1116 for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
1117 it != errors->end();
1118 ++it) {
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();
1140 ++it) {
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);
1147 point = *path;
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();
1158 ++it) {
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_)
1173 << info->record();
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_)
1208 << info->record();
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,
1224 unsigned error) {
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_)
1301 << overridden;
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_;
1339 RecordCache cache_;
1340 JsonWriter* json_;
1343 class BlinkGCPluginAction : public PluginASTAction {
1344 public:
1345 BlinkGCPluginAction() {}
1347 protected:
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) {
1356 bool parsed = true;
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;
1363 } else {
1364 parsed = false;
1365 llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
1369 return parsed;
1372 private:
1373 BlinkGCPluginOptions options_;
1376 } // namespace
1378 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
1379 "blink-gc-plugin",
1380 "Check Blink GC invariants");