• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
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
10 
11 #include "Config.h"
12 #include "JsonWriter.h"
13 #include "RecordInfo.h"
14 
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"
20 
21 using namespace clang;
22 using std::string;
23 
24 namespace {
25 
26 const char kClassMustLeftMostlyDeriveGC[] =
27     "[blink-gc] Class %0 must derive its GC base in the left-most position.";
28 
29 const char kClassRequiresTraceMethod[] =
30     "[blink-gc] Class %0 requires a trace method.";
31 
32 const char kBaseRequiresTracing[] =
33     "[blink-gc] Base class %0 of derived class %1 requires tracing.";
34 
35 const char kBaseRequiresTracingNote[] =
36     "[blink-gc] Untraced base class %0 declared here:";
37 
38 const char kFieldsRequireTracing[] =
39     "[blink-gc] Class %0 has untraced fields that require tracing.";
40 
41 const char kFieldRequiresTracingNote[] =
42     "[blink-gc] Untraced field %0 declared here:";
43 
44 const char kClassContainsInvalidFields[] =
45     "[blink-gc] Class %0 contains invalid fields.";
46 
47 const char kClassContainsGCRoot[] =
48     "[blink-gc] Class %0 contains GC root in field %1.";
49 
50 const char kClassRequiresFinalization[] =
51     "[blink-gc] Class %0 requires finalization.";
52 
53 const char kFinalizerAccessesFinalizedField[] =
54     "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
55 
56 const char kRawPtrToGCManagedClassNote[] =
57     "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
58 
59 const char kRefPtrToGCManagedClassNote[] =
60     "[blink-gc] RefPtr field %0 to a GC managed class declared here:";
61 
62 const char kOwnPtrToGCManagedClassNote[] =
63     "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
64 
65 const char kStackAllocatedFieldNote[] =
66     "[blink-gc] Stack-allocated field %0 declared here:";
67 
68 const char kMemberInUnmanagedClassNote[] =
69     "[blink-gc] Member field %0 in unmanaged class declared here:";
70 
71 const char kPartObjectToGCDerivedClassNote[] =
72     "[blink-gc] Part-object field %0 to a GC derived class declared here:";
73 
74 const char kPartObjectContainsGCRootNote[] =
75     "[blink-gc] Field %0 with embedded GC root in %1 declared here:";
76 
77 const char kFieldContainsGCRootNote[] =
78     "[blink-gc] Field %0 defining a GC root declared here:";
79 
80 const char kOverriddenNonVirtualTrace[] =
81     "[blink-gc] Class %0 overrides non-virtual trace of base class %1.";
82 
83 const char kOverriddenNonVirtualTraceNote[] =
84     "[blink-gc] Non-virtual trace method declared here:";
85 
86 const char kMissingTraceDispatchMethod[] =
87     "[blink-gc] Class %0 is missing manual trace dispatch.";
88 
89 const char kMissingFinalizeDispatchMethod[] =
90     "[blink-gc] Class %0 is missing manual finalize dispatch.";
91 
92 const char kVirtualAndManualDispatch[] =
93     "[blink-gc] Class %0 contains or inherits virtual methods"
94     " but implements manual dispatching.";
95 
96 const char kMissingTraceDispatch[] =
97     "[blink-gc] Missing dispatch to class %0 in manual trace dispatch.";
98 
99 const char kMissingFinalizeDispatch[] =
100     "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch.";
101 
102 const char kFinalizedFieldNote[] =
103     "[blink-gc] Potentially finalized field %0 declared here:";
104 
105 const char kUserDeclaredDestructorNote[] =
106     "[blink-gc] User-declared destructor declared here:";
107 
108 const char kUserDeclaredFinalizerNote[] =
109     "[blink-gc] User-declared finalizer declared here:";
110 
111 const char kBaseRequiresFinalizationNote[] =
112     "[blink-gc] Base class %0 requiring finalization declared here:";
113 
114 const char kFieldRequiresFinalizationNote[] =
115     "[blink-gc] Field %0 requiring finalization declared here:";
116 
117 const char kManualDispatchMethodNote[] =
118     "[blink-gc] Manual dispatch %0 declared here:";
119 
120 const char kDerivesNonStackAllocated[] =
121     "[blink-gc] Stack-allocated class %0 derives class %1"
122     " which is not stack allocated.";
123 
124 const char kClassOverridesNew[] =
125     "[blink-gc] Garbage collected class %0"
126     " is not permitted to override its new operator.";
127 
128 const char kClassDeclaresPureVirtualTrace[] =
129     "[blink-gc] Garbage collected class %0"
130     " is not permitted to declare a pure-virtual trace method.";
131 
132 struct BlinkGCPluginOptions {
BlinkGCPluginOptions__anon1df049130111::BlinkGCPluginOptions133   BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {}
134   bool enable_oilpan;
135   bool dump_graph;
136   std::set<std::string> ignored_classes;
137   std::set<std::string> checked_namespaces;
138   std::vector<std::string> ignored_directories;
139 };
140 
141 typedef std::vector<CXXRecordDecl*> RecordVector;
142 typedef std::vector<CXXMethodDecl*> MethodVector;
143 
144 // Test if a template specialization is an instantiation.
IsTemplateInstantiation(CXXRecordDecl * record)145 static bool IsTemplateInstantiation(CXXRecordDecl* record) {
146   ClassTemplateSpecializationDecl* spec =
147       dyn_cast<ClassTemplateSpecializationDecl>(record);
148   if (!spec)
149     return false;
150   switch (spec->getTemplateSpecializationKind()) {
151     case TSK_ImplicitInstantiation:
152     case TSK_ExplicitInstantiationDefinition:
153       return true;
154     case TSK_Undeclared:
155     case TSK_ExplicitSpecialization:
156       return false;
157     // TODO: unsupported cases.
158     case TSK_ExplicitInstantiationDeclaration:
159       return false;
160   }
161   assert(false && "Unknown template specialization kind");
162 }
163 
164 // This visitor collects the entry points for the checker.
165 class CollectVisitor : public RecursiveASTVisitor<CollectVisitor> {
166  public:
CollectVisitor()167   CollectVisitor() {}
168 
record_decls()169   RecordVector& record_decls() { return record_decls_; }
trace_decls()170   MethodVector& trace_decls() { return trace_decls_; }
171 
shouldVisitTemplateInstantiations()172   bool shouldVisitTemplateInstantiations() { return false; }
173 
174   // Collect record declarations, including nested declarations.
VisitCXXRecordDecl(CXXRecordDecl * record)175   bool VisitCXXRecordDecl(CXXRecordDecl* record) {
176     if (record->hasDefinition() && record->isCompleteDefinition())
177       record_decls_.push_back(record);
178     return true;
179   }
180 
181   // Collect tracing method definitions, but don't traverse method bodies.
TraverseCXXMethodDecl(CXXMethodDecl * method)182   bool TraverseCXXMethodDecl(CXXMethodDecl* method) {
183     if (method->isThisDeclarationADefinition() && Config::IsTraceMethod(method))
184       trace_decls_.push_back(method);
185     return true;
186   }
187 
188  private:
189   RecordVector record_decls_;
190   MethodVector trace_decls_;
191 };
192 
193 // This visitor checks that a finalizer method does not have invalid access to
194 // fields that are potentially finalized. A potentially finalized field is
195 // either a Member, a heap-allocated collection or an off-heap collection that
196 // contains Members.  Invalid uses are currently identified as passing the field
197 // as the argument of a procedure call or using the -> or [] operators on it.
198 class CheckFinalizerVisitor
199     : public RecursiveASTVisitor<CheckFinalizerVisitor> {
200  private:
201   // Simple visitor to determine if the content of a field might be collected
202   // during finalization.
203   class MightBeCollectedVisitor : public EdgeVisitor {
204    public:
MightBeCollectedVisitor()205     MightBeCollectedVisitor() : might_be_collected_(false) {}
might_be_collected()206     bool might_be_collected() { return might_be_collected_; }
VisitMember(Member * edge)207     void VisitMember(Member* edge) override { might_be_collected_ = true; }
VisitCollection(Collection * edge)208     void VisitCollection(Collection* edge) override {
209       if (edge->on_heap()) {
210         might_be_collected_ = !edge->is_root();
211       } else {
212         edge->AcceptMembers(this);
213       }
214     }
215 
216    private:
217     bool might_be_collected_;
218   };
219 
220  public:
221   typedef std::vector<std::pair<MemberExpr*, FieldPoint*> > Errors;
222 
CheckFinalizerVisitor(RecordCache * cache)223   CheckFinalizerVisitor(RecordCache* cache)
224       : blacklist_context_(false), cache_(cache) {}
225 
finalized_fields()226   Errors& finalized_fields() { return finalized_fields_; }
227 
WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr * expr)228   bool WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr* expr) {
229     // Only continue the walk-up if the operator is a blacklisted one.
230     switch (expr->getOperator()) {
231       case OO_Arrow:
232       case OO_Subscript:
233         this->WalkUpFromCallExpr(expr);
234       default:
235         return true;
236     }
237   }
238 
239   // We consider all non-operator calls to be blacklisted contexts.
WalkUpFromCallExpr(CallExpr * expr)240   bool WalkUpFromCallExpr(CallExpr* expr) {
241     bool prev_blacklist_context = blacklist_context_;
242     blacklist_context_ = true;
243     for (size_t i = 0; i < expr->getNumArgs(); ++i)
244       this->TraverseStmt(expr->getArg(i));
245     blacklist_context_ = prev_blacklist_context;
246     return true;
247   }
248 
VisitMemberExpr(MemberExpr * member)249   bool VisitMemberExpr(MemberExpr* member) {
250     FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl());
251     if (!field)
252       return true;
253 
254     RecordInfo* info = cache_->Lookup(field->getParent());
255     if (!info)
256       return true;
257 
258     RecordInfo::Fields::iterator it = info->GetFields().find(field);
259     if (it == info->GetFields().end())
260       return true;
261 
262     if (blacklist_context_ && MightBeCollected(&it->second))
263       finalized_fields_.push_back(std::make_pair(member, &it->second));
264     return true;
265   }
266 
MightBeCollected(FieldPoint * point)267   bool MightBeCollected(FieldPoint* point) {
268     MightBeCollectedVisitor visitor;
269     point->edge()->Accept(&visitor);
270     return visitor.might_be_collected();
271   }
272 
273  private:
274   bool blacklist_context_;
275   Errors finalized_fields_;
276   RecordCache* cache_;
277 };
278 
279 // This visitor checks that a method contains within its body, a call to a
280 // method on the provided receiver class. This is used to check manual
281 // dispatching for trace and finalize methods.
282 class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> {
283  public:
CheckDispatchVisitor(RecordInfo * receiver)284   CheckDispatchVisitor(RecordInfo* receiver)
285       : receiver_(receiver), dispatched_to_receiver_(false) {}
286 
dispatched_to_receiver()287   bool dispatched_to_receiver() { return dispatched_to_receiver_; }
288 
VisitMemberExpr(MemberExpr * member)289   bool VisitMemberExpr(MemberExpr* member) {
290     if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) {
291       if (fn->getParent() == receiver_->record())
292         dispatched_to_receiver_ = true;
293     }
294     return true;
295   }
296 
297  private:
298   RecordInfo* receiver_;
299   bool dispatched_to_receiver_;
300 };
301 
302 // This visitor checks a tracing method by traversing its body.
303 // - A member field is considered traced if it is referenced in the body.
304 // - A base is traced if a base-qualified call to a trace method is found.
305 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> {
306  public:
CheckTraceVisitor(CXXMethodDecl * trace,RecordInfo * info)307   CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info)
308       : trace_(trace), info_(info) {}
309 
310   // Allow recursive traversal by using VisitMemberExpr.
VisitMemberExpr(MemberExpr * member)311   bool VisitMemberExpr(MemberExpr* member) {
312     // If this member expression references a field decl, mark it as traced.
313     if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) {
314       if (IsTemplateInstantiation(info_->record())) {
315         // Pointer equality on fields does not work for template instantiations.
316         // The trace method refers to fields of the template definition which
317         // are different from the instantiated fields that need to be traced.
318         const string& name = field->getNameAsString();
319         for (RecordInfo::Fields::iterator it = info_->GetFields().begin();
320              it != info_->GetFields().end();
321              ++it) {
322           if (it->first->getNameAsString() == name) {
323             MarkTraced(it);
324             break;
325           }
326         }
327       } else {
328         RecordInfo::Fields::iterator it = info_->GetFields().find(field);
329         if (it != info_->GetFields().end())
330           MarkTraced(it);
331       }
332       return true;
333     }
334 
335     // If this is a weak callback function we only check field tracing.
336     if (IsWeakCallback())
337       return true;
338 
339     // For method calls, check tracing of bases and other special GC methods.
340     if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) {
341       const string& name = fn->getNameAsString();
342       // Check weak callbacks.
343       if (name == kRegisterWeakMembersName) {
344         if (fn->isTemplateInstantiation()) {
345           const TemplateArgumentList& args =
346               *fn->getTemplateSpecializationInfo()->TemplateArguments;
347           // The second template argument is the callback method.
348           if (args.size() > 1 &&
349               args[1].getKind() == TemplateArgument::Declaration) {
350             if (FunctionDecl* callback =
351                     dyn_cast<FunctionDecl>(args[1].getAsDecl())) {
352               if (callback->hasBody()) {
353                 CheckTraceVisitor nested_visitor(info_);
354                 nested_visitor.TraverseStmt(callback->getBody());
355               }
356             }
357           }
358         }
359         return true;
360       }
361 
362       // Currently, a manually dispatched class cannot have mixin bases (having
363       // one would add a vtable which we explicitly check against). This means
364       // that we can only make calls to a trace method of the same name. Revisit
365       // this if our mixin/vtable assumption changes.
366       if (Config::IsTraceMethod(fn) &&
367           fn->getName() == trace_->getName() &&
368           member->hasQualifier()) {
369         if (const Type* type = member->getQualifier()->getAsType()) {
370           if (CXXRecordDecl* decl = type->getAsCXXRecordDecl()) {
371             RecordInfo::Bases::iterator it = info_->GetBases().find(decl);
372             if (it != info_->GetBases().end())
373               it->second.MarkTraced();
374           }
375         }
376       }
377     }
378     return true;
379   }
380 
381  private:
382   // Nested checking for weak callbacks.
CheckTraceVisitor(RecordInfo * info)383   CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {}
384 
IsWeakCallback()385   bool IsWeakCallback() { return !trace_; }
386 
MarkTraced(RecordInfo::Fields::iterator it)387   void MarkTraced(RecordInfo::Fields::iterator it) {
388     // In a weak callback we can't mark strong fields as traced.
389     if (IsWeakCallback() && !it->second.edge()->IsWeakMember())
390       return;
391     it->second.MarkTraced();
392   }
393 
394   CXXMethodDecl* trace_;
395   RecordInfo* info_;
396 };
397 
398 // This visitor checks that the fields of a class and the fields of
399 // its part objects don't define GC roots.
400 class CheckGCRootsVisitor : public RecursiveEdgeVisitor {
401  public:
402   typedef std::vector<FieldPoint*> RootPath;
403   typedef std::vector<RootPath> Errors;
404 
CheckGCRootsVisitor()405   CheckGCRootsVisitor() {}
406 
gc_roots()407   Errors& gc_roots() { return gc_roots_; }
408 
ContainsGCRoots(RecordInfo * info)409   bool ContainsGCRoots(RecordInfo* info) {
410     for (RecordInfo::Fields::iterator it = info->GetFields().begin();
411          it != info->GetFields().end();
412          ++it) {
413       current_.push_back(&it->second);
414       it->second.edge()->Accept(this);
415       current_.pop_back();
416     }
417     return !gc_roots_.empty();
418   }
419 
VisitValue(Value * edge)420   void VisitValue(Value* edge) override {
421     // TODO: what should we do to check unions?
422     if (edge->value()->record()->isUnion())
423       return;
424 
425     // If the value is a part object, then continue checking for roots.
426     for (Context::iterator it = context().begin();
427          it != context().end();
428          ++it) {
429       if (!(*it)->IsCollection())
430         return;
431     }
432     ContainsGCRoots(edge->value());
433   }
434 
VisitPersistent(Persistent * edge)435   void VisitPersistent(Persistent* edge) override {
436     gc_roots_.push_back(current_);
437   }
438 
AtCollection(Collection * edge)439   void AtCollection(Collection* edge) override {
440     if (edge->is_root())
441       gc_roots_.push_back(current_);
442   }
443 
444  protected:
445   RootPath current_;
446   Errors gc_roots_;
447 };
448 
449 // This visitor checks that the fields of a class are "well formed".
450 // - OwnPtr, RefPtr and RawPtr must not point to a GC derived types.
451 // - Part objects must not be GC derived types.
452 // - An on-heap class must never contain GC roots.
453 // - Only stack-allocated types may point to stack-allocated types.
454 class CheckFieldsVisitor : public RecursiveEdgeVisitor {
455  public:
456 
457   enum Error {
458     kRawPtrToGCManaged,
459     kRefPtrToGCManaged,
460     kOwnPtrToGCManaged,
461     kMemberInUnmanaged,
462     kPtrFromHeapToStack,
463     kGCDerivedPartObject
464   };
465 
466   typedef std::vector<std::pair<FieldPoint*, Error> > Errors;
467 
CheckFieldsVisitor(const BlinkGCPluginOptions & options)468   CheckFieldsVisitor(const BlinkGCPluginOptions& options)
469       : options_(options), current_(0), stack_allocated_host_(false) {}
470 
invalid_fields()471   Errors& invalid_fields() { return invalid_fields_; }
472 
ContainsInvalidFields(RecordInfo * info)473   bool ContainsInvalidFields(RecordInfo* info) {
474     stack_allocated_host_ = info->IsStackAllocated();
475     managed_host_ = stack_allocated_host_ ||
476                     info->IsGCAllocated() ||
477                     info->IsNonNewable() ||
478                     info->IsOnlyPlacementNewable();
479     for (RecordInfo::Fields::iterator it = info->GetFields().begin();
480          it != info->GetFields().end();
481          ++it) {
482       context().clear();
483       current_ = &it->second;
484       current_->edge()->Accept(this);
485     }
486     return !invalid_fields_.empty();
487   }
488 
AtMember(Member * edge)489   void AtMember(Member* edge) override {
490     if (managed_host_)
491       return;
492     // A member is allowed to appear in the context of a root.
493     for (Context::iterator it = context().begin();
494          it != context().end();
495          ++it) {
496       if ((*it)->Kind() == Edge::kRoot)
497         return;
498     }
499     invalid_fields_.push_back(std::make_pair(current_, kMemberInUnmanaged));
500   }
501 
AtValue(Value * edge)502   void AtValue(Value* edge) override {
503     // TODO: what should we do to check unions?
504     if (edge->value()->record()->isUnion())
505       return;
506 
507     if (!stack_allocated_host_ && edge->value()->IsStackAllocated()) {
508       invalid_fields_.push_back(std::make_pair(current_, kPtrFromHeapToStack));
509       return;
510     }
511 
512     if (!Parent() &&
513         edge->value()->IsGCDerived() &&
514         !edge->value()->IsGCMixin()) {
515       invalid_fields_.push_back(std::make_pair(current_, kGCDerivedPartObject));
516       return;
517     }
518 
519     if (!Parent() || !edge->value()->IsGCAllocated())
520       return;
521 
522     // In transition mode, disallow  OwnPtr<T>, RawPtr<T> to GC allocated T's,
523     // also disallow T* in stack-allocated types.
524     if (options_.enable_oilpan) {
525       if (Parent()->IsOwnPtr() ||
526           Parent()->IsRawPtrClass() ||
527           (stack_allocated_host_ && Parent()->IsRawPtr())) {
528         invalid_fields_.push_back(std::make_pair(
529             current_, InvalidSmartPtr(Parent())));
530         return;
531       }
532 
533       return;
534     }
535 
536     if (Parent()->IsRawPtr() || Parent()->IsRefPtr() || Parent()->IsOwnPtr()) {
537       invalid_fields_.push_back(std::make_pair(
538           current_, InvalidSmartPtr(Parent())));
539       return;
540     }
541   }
542 
AtCollection(Collection * edge)543   void AtCollection(Collection* edge) override {
544     if (edge->on_heap() && Parent() && Parent()->IsOwnPtr())
545       invalid_fields_.push_back(std::make_pair(current_, kOwnPtrToGCManaged));
546   }
547 
548  private:
InvalidSmartPtr(Edge * ptr)549   Error InvalidSmartPtr(Edge* ptr) {
550     if (ptr->IsRawPtr())
551       return kRawPtrToGCManaged;
552     if (ptr->IsRefPtr())
553       return kRefPtrToGCManaged;
554     if (ptr->IsOwnPtr())
555       return kOwnPtrToGCManaged;
556     assert(false && "Unknown smart pointer kind");
557   }
558 
559   const BlinkGCPluginOptions& options_;
560   FieldPoint* current_;
561   bool stack_allocated_host_;
562   bool managed_host_;
563   Errors invalid_fields_;
564 };
565 
566 // Main class containing checks for various invariants of the Blink
567 // garbage collection infrastructure.
568 class BlinkGCPluginConsumer : public ASTConsumer {
569  public:
BlinkGCPluginConsumer(CompilerInstance & instance,const BlinkGCPluginOptions & options)570   BlinkGCPluginConsumer(CompilerInstance& instance,
571                         const BlinkGCPluginOptions& options)
572       : instance_(instance),
573         diagnostic_(instance.getDiagnostics()),
574         options_(options),
575         json_(0) {
576 
577     // Only check structures in the blink, WebCore and WebKit namespaces.
578     options_.checked_namespaces.insert("blink");
579     options_.checked_namespaces.insert("WebCore");
580     options_.checked_namespaces.insert("WebKit");
581 
582     // Ignore GC implementation files.
583     options_.ignored_directories.push_back("/heap/");
584 
585     // Register warning/error messages.
586     diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID(
587         getErrorLevel(), kClassMustLeftMostlyDeriveGC);
588     diag_class_requires_trace_method_ =
589         diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod);
590     diag_base_requires_tracing_ =
591         diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing);
592     diag_fields_require_tracing_ =
593         diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing);
594     diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID(
595         getErrorLevel(), kClassContainsInvalidFields);
596     diag_class_contains_gc_root_ =
597         diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
598     diag_class_requires_finalization_ = diagnostic_.getCustomDiagID(
599         getErrorLevel(), kClassRequiresFinalization);
600     diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
601         getErrorLevel(), kFinalizerAccessesFinalizedField);
602     diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID(
603         getErrorLevel(), kOverriddenNonVirtualTrace);
604     diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID(
605         getErrorLevel(), kMissingTraceDispatchMethod);
606     diag_missing_finalize_dispatch_method_ = diagnostic_.getCustomDiagID(
607         getErrorLevel(), kMissingFinalizeDispatchMethod);
608     diag_virtual_and_manual_dispatch_ =
609         diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch);
610     diag_missing_trace_dispatch_ =
611         diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch);
612     diag_missing_finalize_dispatch_ =
613         diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch);
614     diag_derives_non_stack_allocated_ =
615         diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated);
616     diag_class_overrides_new_ =
617         diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew);
618     diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID(
619         getErrorLevel(), kClassDeclaresPureVirtualTrace);
620 
621     // Register note messages.
622     diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID(
623         DiagnosticsEngine::Note, kBaseRequiresTracingNote);
624     diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
625         DiagnosticsEngine::Note, kFieldRequiresTracingNote);
626     diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
627         DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote);
628     diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
629         DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote);
630     diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
631         DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote);
632     diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID(
633         DiagnosticsEngine::Note, kStackAllocatedFieldNote);
634     diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
635         DiagnosticsEngine::Note, kMemberInUnmanagedClassNote);
636     diag_part_object_to_gc_derived_class_note_ = diagnostic_.getCustomDiagID(
637         DiagnosticsEngine::Note, kPartObjectToGCDerivedClassNote);
638     diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
639         DiagnosticsEngine::Note, kPartObjectContainsGCRootNote);
640     diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
641         DiagnosticsEngine::Note, kFieldContainsGCRootNote);
642     diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
643         DiagnosticsEngine::Note, kFinalizedFieldNote);
644     diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID(
645         DiagnosticsEngine::Note, kUserDeclaredDestructorNote);
646     diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID(
647         DiagnosticsEngine::Note, kUserDeclaredFinalizerNote);
648     diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID(
649         DiagnosticsEngine::Note, kBaseRequiresFinalizationNote);
650     diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID(
651         DiagnosticsEngine::Note, kFieldRequiresFinalizationNote);
652     diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID(
653         DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote);
654     diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID(
655         DiagnosticsEngine::Note, kManualDispatchMethodNote);
656   }
657 
HandleTranslationUnit(ASTContext & context)658   void HandleTranslationUnit(ASTContext& context) override {
659     CollectVisitor visitor;
660     visitor.TraverseDecl(context.getTranslationUnitDecl());
661 
662     if (options_.dump_graph) {
663       string err;
664       // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
665       json_ = JsonWriter::from(instance_.createOutputFile(
666           "",                                      // OutputPath
667           err,                                     // Errors
668           true,                                    // Binary
669           true,                                    // RemoveFileOnSignal
670           instance_.getFrontendOpts().OutputFile,  // BaseInput
671           "graph.json",                            // Extension
672           false,                                   // UseTemporary
673           false,                                   // CreateMissingDirectories
674           0,                                       // ResultPathName
675           0));                                     // TempPathName
676       if (err.empty() && json_) {
677         json_->OpenList();
678       } else {
679         json_ = 0;
680         llvm::errs()
681             << "[blink-gc] "
682             << "Failed to create an output file for the object graph.\n";
683       }
684     }
685 
686     for (RecordVector::iterator it = visitor.record_decls().begin();
687          it != visitor.record_decls().end();
688          ++it) {
689       CheckRecord(cache_.Lookup(*it));
690     }
691 
692     for (MethodVector::iterator it = visitor.trace_decls().begin();
693          it != visitor.trace_decls().end();
694          ++it) {
695       CheckTracingMethod(*it);
696     }
697 
698     if (json_) {
699       json_->CloseList();
700       delete json_;
701       json_ = 0;
702     }
703   }
704 
705   // Main entry for checking a record declaration.
CheckRecord(RecordInfo * info)706   void CheckRecord(RecordInfo* info) {
707     if (IsIgnored(info))
708       return;
709 
710     CXXRecordDecl* record = info->record();
711 
712     // TODO: what should we do to check unions?
713     if (record->isUnion())
714       return;
715 
716     // If this is the primary template declaration, check its specializations.
717     if (record->isThisDeclarationADefinition() &&
718         record->getDescribedClassTemplate()) {
719       ClassTemplateDecl* tmpl = record->getDescribedClassTemplate();
720       for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
721            it != tmpl->spec_end();
722            ++it) {
723         CheckClass(cache_.Lookup(*it));
724       }
725       return;
726     }
727 
728     CheckClass(info);
729   }
730 
731   // Check a class-like object (eg, class, specialization, instantiation).
CheckClass(RecordInfo * info)732   void CheckClass(RecordInfo* info) {
733     if (!info)
734       return;
735 
736     // Check consistency of stack-allocated hierarchies.
737     if (info->IsStackAllocated()) {
738       for (RecordInfo::Bases::iterator it = info->GetBases().begin();
739            it != info->GetBases().end();
740            ++it) {
741         if (!it->second.info()->IsStackAllocated())
742           ReportDerivesNonStackAllocated(info, &it->second);
743       }
744     }
745 
746     if (CXXMethodDecl* trace = info->GetTraceMethod()) {
747       if (trace->isPure())
748         ReportClassDeclaresPureVirtualTrace(info, trace);
749     } else if (info->RequiresTraceMethod()) {
750       ReportClassRequiresTraceMethod(info);
751     }
752 
753     {
754       CheckFieldsVisitor visitor(options_);
755       if (visitor.ContainsInvalidFields(info))
756         ReportClassContainsInvalidFields(info, &visitor.invalid_fields());
757     }
758 
759     if (info->IsGCDerived()) {
760 
761       if (!info->IsGCMixin()) {
762         CheckLeftMostDerived(info);
763         CheckDispatch(info);
764         if (CXXMethodDecl* newop = info->DeclaresNewOperator())
765           ReportClassOverridesNew(info, newop);
766       }
767 
768       {
769         CheckGCRootsVisitor visitor;
770         if (visitor.ContainsGCRoots(info))
771           ReportClassContainsGCRoots(info, &visitor.gc_roots());
772       }
773 
774       if (info->NeedsFinalization())
775         CheckFinalization(info);
776     }
777 
778     DumpClass(info);
779   }
780 
CheckLeftMostDerived(RecordInfo * info)781   void CheckLeftMostDerived(RecordInfo* info) {
782     CXXRecordDecl* left_most = info->record();
783     CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
784     while (it != left_most->bases_end()) {
785       left_most = it->getType()->getAsCXXRecordDecl();
786       it = left_most->bases_begin();
787     }
788     if (!Config::IsGCBase(left_most->getName()))
789       ReportClassMustLeftMostlyDeriveGC(info);
790   }
791 
CheckDispatch(RecordInfo * info)792   void CheckDispatch(RecordInfo* info) {
793     bool finalized = info->IsGCFinalized();
794     CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
795     CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
796     if (!trace_dispatch && !finalize_dispatch)
797       return;
798 
799     CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent()
800                                          : finalize_dispatch->getParent();
801 
802     // Check that dispatch methods are defined at the base.
803     if (base == info->record()) {
804       if (!trace_dispatch)
805         ReportMissingTraceDispatchMethod(info);
806       if (finalized && !finalize_dispatch)
807         ReportMissingFinalizeDispatchMethod(info);
808       if (!finalized && finalize_dispatch) {
809         ReportClassRequiresFinalization(info);
810         NoteUserDeclaredFinalizer(finalize_dispatch);
811       }
812     }
813 
814     // Check that classes implementing manual dispatch do not have vtables.
815     if (info->record()->isPolymorphic())
816       ReportVirtualAndManualDispatch(
817           info, trace_dispatch ? trace_dispatch : finalize_dispatch);
818 
819     // If this is a non-abstract class check that it is dispatched to.
820     // TODO: Create a global variant of this local check. We can only check if
821     // the dispatch body is known in this compilation unit.
822     if (info->IsConsideredAbstract())
823       return;
824 
825     const FunctionDecl* defn;
826 
827     if (trace_dispatch && trace_dispatch->isDefined(defn)) {
828       CheckDispatchVisitor visitor(info);
829       visitor.TraverseStmt(defn->getBody());
830       if (!visitor.dispatched_to_receiver())
831         ReportMissingTraceDispatch(defn, info);
832     }
833 
834     if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) {
835       CheckDispatchVisitor visitor(info);
836       visitor.TraverseStmt(defn->getBody());
837       if (!visitor.dispatched_to_receiver())
838         ReportMissingFinalizeDispatch(defn, info);
839     }
840   }
841 
842   // TODO: Should we collect destructors similar to trace methods?
CheckFinalization(RecordInfo * info)843   void CheckFinalization(RecordInfo* info) {
844     CXXDestructorDecl* dtor = info->record()->getDestructor();
845 
846     // For finalized classes, check the finalization method if possible.
847     if (info->IsGCFinalized()) {
848       if (dtor && dtor->hasBody()) {
849         CheckFinalizerVisitor visitor(&cache_);
850         visitor.TraverseCXXMethodDecl(dtor);
851         if (!visitor.finalized_fields().empty()) {
852           ReportFinalizerAccessesFinalizedFields(
853               dtor, &visitor.finalized_fields());
854         }
855       }
856       return;
857     }
858 
859     // Don't require finalization of a mixin that has not yet been "mixed in".
860     if (info->IsGCMixin())
861       return;
862 
863     // Report the finalization error, and proceed to print possible causes for
864     // the finalization requirement.
865     ReportClassRequiresFinalization(info);
866 
867     if (dtor && dtor->isUserProvided())
868       NoteUserDeclaredDestructor(dtor);
869 
870     for (RecordInfo::Bases::iterator it = info->GetBases().begin();
871          it != info->GetBases().end();
872          ++it) {
873       if (it->second.info()->NeedsFinalization())
874         NoteBaseRequiresFinalization(&it->second);
875     }
876 
877     for (RecordInfo::Fields::iterator it = info->GetFields().begin();
878          it != info->GetFields().end();
879          ++it) {
880       if (it->second.edge()->NeedsFinalization())
881         NoteField(&it->second, diag_field_requires_finalization_note_);
882     }
883   }
884 
885   // This is the main entry for tracing method definitions.
CheckTracingMethod(CXXMethodDecl * method)886   void CheckTracingMethod(CXXMethodDecl* method) {
887     RecordInfo* parent = cache_.Lookup(method->getParent());
888     if (IsIgnored(parent))
889       return;
890 
891     // Check templated tracing methods by checking the template instantiations.
892     // Specialized templates are handled as ordinary classes.
893     if (ClassTemplateDecl* tmpl =
894             parent->record()->getDescribedClassTemplate()) {
895       for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
896            it != tmpl->spec_end();
897            ++it) {
898         // Check trace using each template instantiation as the holder.
899         if (IsTemplateInstantiation(*it))
900           CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
901       }
902       return;
903     }
904 
905     CheckTraceOrDispatchMethod(parent, method);
906   }
907 
908   // Determine what type of tracing method this is (dispatch or trace).
CheckTraceOrDispatchMethod(RecordInfo * parent,CXXMethodDecl * method)909   void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) {
910     bool isTraceAfterDispatch;
911     if (Config::IsTraceMethod(method, &isTraceAfterDispatch)) {
912       if (isTraceAfterDispatch || !parent->GetTraceDispatchMethod()) {
913         CheckTraceMethod(parent, method, isTraceAfterDispatch);
914       }
915       // Dispatch methods are checked when we identify subclasses.
916     }
917   }
918 
919   // Check an actual trace method.
CheckTraceMethod(RecordInfo * parent,CXXMethodDecl * trace,bool isTraceAfterDispatch)920   void CheckTraceMethod(RecordInfo* parent,
921                         CXXMethodDecl* trace,
922                         bool isTraceAfterDispatch) {
923     // A non-virtual trace method must not override another trace.
924     if (!isTraceAfterDispatch && !trace->isVirtual()) {
925       for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
926            it != parent->GetBases().end();
927            ++it) {
928         RecordInfo* base = it->second.info();
929         // We allow mixin bases to contain a non-virtual trace since it will
930         // never be used for dispatching.
931         if (base->IsGCMixin())
932           continue;
933         if (CXXMethodDecl* other = base->InheritsNonVirtualTrace())
934           ReportOverriddenNonVirtualTrace(parent, trace, other);
935       }
936     }
937 
938     CheckTraceVisitor visitor(trace, parent);
939     visitor.TraverseCXXMethodDecl(trace);
940 
941     for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
942          it != parent->GetBases().end();
943          ++it) {
944       if (!it->second.IsProperlyTraced())
945         ReportBaseRequiresTracing(parent, trace, it->first);
946     }
947 
948     for (RecordInfo::Fields::iterator it = parent->GetFields().begin();
949          it != parent->GetFields().end();
950          ++it) {
951       if (!it->second.IsProperlyTraced()) {
952         // Discontinue once an untraced-field error is found.
953         ReportFieldsRequireTracing(parent, trace);
954         break;
955       }
956     }
957   }
958 
DumpClass(RecordInfo * info)959   void DumpClass(RecordInfo* info) {
960     if (!json_)
961       return;
962 
963     json_->OpenObject();
964     json_->Write("name", info->record()->getQualifiedNameAsString());
965     json_->Write("loc", GetLocString(info->record()->getLocStart()));
966     json_->CloseObject();
967 
968     class DumpEdgeVisitor : public RecursiveEdgeVisitor {
969      public:
970       DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
971       void DumpEdge(RecordInfo* src,
972                     RecordInfo* dst,
973                     const string& lbl,
974                     const Edge::LivenessKind& kind,
975                     const string& loc) {
976         json_->OpenObject();
977         json_->Write("src", src->record()->getQualifiedNameAsString());
978         json_->Write("dst", dst->record()->getQualifiedNameAsString());
979         json_->Write("lbl", lbl);
980         json_->Write("kind", kind);
981         json_->Write("loc", loc);
982         json_->Write("ptr",
983                      !Parent() ? "val" :
984                      Parent()->IsRawPtr() ? "raw" :
985                      Parent()->IsRefPtr() ? "ref" :
986                      Parent()->IsOwnPtr() ? "own" :
987                      (Parent()->IsMember() ||
988                       Parent()->IsWeakMember()) ? "mem" :
989                      "val");
990         json_->CloseObject();
991       }
992 
993       void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) {
994         src_ = src;
995         point_ = point;
996         loc_ = loc;
997         point_->edge()->Accept(this);
998       }
999 
1000       void AtValue(Value* e) override {
1001         // The liveness kind of a path from the point to this value
1002         // is given by the innermost place that is non-strong.
1003         Edge::LivenessKind kind = Edge::kStrong;
1004         if (Config::IsIgnoreCycleAnnotated(point_->field())) {
1005           kind = Edge::kWeak;
1006         } else {
1007           for (Context::iterator it = context().begin();
1008                it != context().end();
1009                ++it) {
1010             Edge::LivenessKind pointer_kind = (*it)->Kind();
1011             if (pointer_kind != Edge::kStrong) {
1012               kind = pointer_kind;
1013               break;
1014             }
1015           }
1016         }
1017         DumpEdge(
1018             src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
1019       }
1020 
1021      private:
1022       JsonWriter* json_;
1023       RecordInfo* src_;
1024       FieldPoint* point_;
1025       string loc_;
1026     };
1027 
1028     DumpEdgeVisitor visitor(json_);
1029 
1030     RecordInfo::Bases& bases = info->GetBases();
1031     for (RecordInfo::Bases::iterator it = bases.begin();
1032          it != bases.end();
1033          ++it) {
1034       visitor.DumpEdge(info,
1035                        it->second.info(),
1036                        "<super>",
1037                        Edge::kStrong,
1038                        GetLocString(it->second.spec().getLocStart()));
1039     }
1040 
1041     RecordInfo::Fields& fields = info->GetFields();
1042     for (RecordInfo::Fields::iterator it = fields.begin();
1043          it != fields.end();
1044          ++it) {
1045       visitor.DumpField(info,
1046                         &it->second,
1047                         GetLocString(it->second.field()->getLocStart()));
1048     }
1049   }
1050 
1051   // Adds either a warning or error, based on the current handling of -Werror.
getErrorLevel()1052   DiagnosticsEngine::Level getErrorLevel() {
1053     return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error
1054                                              : DiagnosticsEngine::Warning;
1055   }
1056 
GetLocString(SourceLocation loc)1057   const string GetLocString(SourceLocation loc) {
1058     const SourceManager& source_manager = instance_.getSourceManager();
1059     PresumedLoc ploc = source_manager.getPresumedLoc(loc);
1060     if (ploc.isInvalid())
1061       return "";
1062     string loc_str;
1063     llvm::raw_string_ostream OS(loc_str);
1064     OS << ploc.getFilename()
1065        << ":" << ploc.getLine()
1066        << ":" << ploc.getColumn();
1067     return OS.str();
1068   }
1069 
IsIgnored(RecordInfo * record)1070   bool IsIgnored(RecordInfo* record) {
1071     return !record ||
1072            !InCheckedNamespace(record) ||
1073            IsIgnoredClass(record) ||
1074            InIgnoredDirectory(record);
1075   }
1076 
IsIgnoredClass(RecordInfo * info)1077   bool IsIgnoredClass(RecordInfo* info) {
1078     // Ignore any class prefixed by SameSizeAs. These are used in
1079     // Blink to verify class sizes and don't need checking.
1080     const string SameSizeAs = "SameSizeAs";
1081     if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0)
1082       return true;
1083     return options_.ignored_classes.find(info->name()) !=
1084            options_.ignored_classes.end();
1085   }
1086 
InIgnoredDirectory(RecordInfo * info)1087   bool InIgnoredDirectory(RecordInfo* info) {
1088     string filename;
1089     if (!GetFilename(info->record()->getLocStart(), &filename))
1090       return false;  // TODO: should we ignore non-existing file locations?
1091     std::vector<string>::iterator it = options_.ignored_directories.begin();
1092     for (; it != options_.ignored_directories.end(); ++it)
1093       if (filename.find(*it) != string::npos)
1094         return true;
1095     return false;
1096   }
1097 
InCheckedNamespace(RecordInfo * info)1098   bool InCheckedNamespace(RecordInfo* info) {
1099     if (!info)
1100       return false;
1101     for (DeclContext* context = info->record()->getDeclContext();
1102          !context->isTranslationUnit();
1103          context = context->getParent()) {
1104       if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) {
1105         if (options_.checked_namespaces.find(decl->getNameAsString()) !=
1106             options_.checked_namespaces.end()) {
1107           return true;
1108         }
1109       }
1110     }
1111     return false;
1112   }
1113 
GetFilename(SourceLocation loc,string * filename)1114   bool GetFilename(SourceLocation loc, string* filename) {
1115     const SourceManager& source_manager = instance_.getSourceManager();
1116     SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
1117     PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
1118     if (ploc.isInvalid()) {
1119       // If we're in an invalid location, we're looking at things that aren't
1120       // actually stated in the source.
1121       return false;
1122     }
1123     *filename = ploc.getFilename();
1124     return true;
1125   }
1126 
ReportClassMustLeftMostlyDeriveGC(RecordInfo * info)1127   void ReportClassMustLeftMostlyDeriveGC(RecordInfo* info) {
1128     SourceLocation loc = info->record()->getInnerLocStart();
1129     SourceManager& manager = instance_.getSourceManager();
1130     FullSourceLoc full_loc(loc, manager);
1131     diagnostic_.Report(full_loc, diag_class_must_left_mostly_derive_gc_)
1132         << info->record();
1133   }
1134 
ReportClassRequiresTraceMethod(RecordInfo * info)1135   void ReportClassRequiresTraceMethod(RecordInfo* info) {
1136     SourceLocation loc = info->record()->getInnerLocStart();
1137     SourceManager& manager = instance_.getSourceManager();
1138     FullSourceLoc full_loc(loc, manager);
1139     diagnostic_.Report(full_loc, diag_class_requires_trace_method_)
1140         << info->record();
1141 
1142     for (RecordInfo::Bases::iterator it = info->GetBases().begin();
1143          it != info->GetBases().end();
1144          ++it) {
1145       if (it->second.NeedsTracing().IsNeeded())
1146         NoteBaseRequiresTracing(&it->second);
1147     }
1148 
1149     for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1150          it != info->GetFields().end();
1151          ++it) {
1152       if (!it->second.IsProperlyTraced())
1153         NoteFieldRequiresTracing(info, it->first);
1154     }
1155   }
1156 
ReportBaseRequiresTracing(RecordInfo * derived,CXXMethodDecl * trace,CXXRecordDecl * base)1157   void ReportBaseRequiresTracing(RecordInfo* derived,
1158                                  CXXMethodDecl* trace,
1159                                  CXXRecordDecl* base) {
1160     SourceLocation loc = trace->getLocStart();
1161     SourceManager& manager = instance_.getSourceManager();
1162     FullSourceLoc full_loc(loc, manager);
1163     diagnostic_.Report(full_loc, diag_base_requires_tracing_)
1164         << base << derived->record();
1165   }
1166 
ReportFieldsRequireTracing(RecordInfo * info,CXXMethodDecl * trace)1167   void ReportFieldsRequireTracing(RecordInfo* info, CXXMethodDecl* trace) {
1168     SourceLocation loc = trace->getLocStart();
1169     SourceManager& manager = instance_.getSourceManager();
1170     FullSourceLoc full_loc(loc, manager);
1171     diagnostic_.Report(full_loc, diag_fields_require_tracing_)
1172         << info->record();
1173     for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1174          it != info->GetFields().end();
1175          ++it) {
1176       if (!it->second.IsProperlyTraced())
1177         NoteFieldRequiresTracing(info, it->first);
1178     }
1179   }
1180 
ReportClassContainsInvalidFields(RecordInfo * info,CheckFieldsVisitor::Errors * errors)1181   void ReportClassContainsInvalidFields(RecordInfo* info,
1182                                         CheckFieldsVisitor::Errors* errors) {
1183     SourceLocation loc = info->record()->getLocStart();
1184     SourceManager& manager = instance_.getSourceManager();
1185     FullSourceLoc full_loc(loc, manager);
1186     diagnostic_.Report(full_loc, diag_class_contains_invalid_fields_)
1187         << info->record();
1188     for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
1189          it != errors->end();
1190          ++it) {
1191       unsigned error;
1192       if (it->second == CheckFieldsVisitor::kRawPtrToGCManaged) {
1193         error = diag_raw_ptr_to_gc_managed_class_note_;
1194       } else if (it->second == CheckFieldsVisitor::kRefPtrToGCManaged) {
1195         error = diag_ref_ptr_to_gc_managed_class_note_;
1196       } else if (it->second == CheckFieldsVisitor::kOwnPtrToGCManaged) {
1197         error = diag_own_ptr_to_gc_managed_class_note_;
1198       } else if (it->second == CheckFieldsVisitor::kMemberInUnmanaged) {
1199         error = diag_member_in_unmanaged_class_note_;
1200       } else if (it->second == CheckFieldsVisitor::kPtrFromHeapToStack) {
1201         error = diag_stack_allocated_field_note_;
1202       } else if (it->second == CheckFieldsVisitor::kGCDerivedPartObject) {
1203         error = diag_part_object_to_gc_derived_class_note_;
1204       } else {
1205         assert(false && "Unknown field error");
1206       }
1207       NoteField(it->first, error);
1208     }
1209   }
1210 
ReportClassContainsGCRoots(RecordInfo * info,CheckGCRootsVisitor::Errors * errors)1211   void ReportClassContainsGCRoots(RecordInfo* info,
1212                                   CheckGCRootsVisitor::Errors* errors) {
1213     SourceLocation loc = info->record()->getLocStart();
1214     SourceManager& manager = instance_.getSourceManager();
1215     FullSourceLoc full_loc(loc, manager);
1216     for (CheckGCRootsVisitor::Errors::iterator it = errors->begin();
1217          it != errors->end();
1218          ++it) {
1219       CheckGCRootsVisitor::RootPath::iterator path = it->begin();
1220       FieldPoint* point = *path;
1221       diagnostic_.Report(full_loc, diag_class_contains_gc_root_)
1222           << info->record() << point->field();
1223       while (++path != it->end()) {
1224         NotePartObjectContainsGCRoot(point);
1225         point = *path;
1226       }
1227       NoteFieldContainsGCRoot(point);
1228     }
1229   }
1230 
ReportFinalizerAccessesFinalizedFields(CXXMethodDecl * dtor,CheckFinalizerVisitor::Errors * fields)1231   void ReportFinalizerAccessesFinalizedFields(
1232       CXXMethodDecl* dtor,
1233       CheckFinalizerVisitor::Errors* fields) {
1234     for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
1235          it != fields->end();
1236          ++it) {
1237       SourceLocation loc = it->first->getLocStart();
1238       SourceManager& manager = instance_.getSourceManager();
1239       FullSourceLoc full_loc(loc, manager);
1240       diagnostic_.Report(full_loc, diag_finalizer_accesses_finalized_field_)
1241           << dtor << it->second->field();
1242       NoteField(it->second, diag_finalized_field_note_);
1243     }
1244   }
1245 
ReportClassRequiresFinalization(RecordInfo * info)1246   void ReportClassRequiresFinalization(RecordInfo* info) {
1247     SourceLocation loc = info->record()->getInnerLocStart();
1248     SourceManager& manager = instance_.getSourceManager();
1249     FullSourceLoc full_loc(loc, manager);
1250     diagnostic_.Report(full_loc, diag_class_requires_finalization_)
1251         << info->record();
1252   }
1253 
ReportOverriddenNonVirtualTrace(RecordInfo * info,CXXMethodDecl * trace,CXXMethodDecl * overridden)1254   void ReportOverriddenNonVirtualTrace(RecordInfo* info,
1255                                        CXXMethodDecl* trace,
1256                                        CXXMethodDecl* overridden) {
1257     SourceLocation loc = trace->getLocStart();
1258     SourceManager& manager = instance_.getSourceManager();
1259     FullSourceLoc full_loc(loc, manager);
1260     diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_)
1261         << info->record() << overridden->getParent();
1262     NoteOverriddenNonVirtualTrace(overridden);
1263   }
1264 
ReportMissingTraceDispatchMethod(RecordInfo * info)1265   void ReportMissingTraceDispatchMethod(RecordInfo* info) {
1266     ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_);
1267   }
1268 
ReportMissingFinalizeDispatchMethod(RecordInfo * info)1269   void ReportMissingFinalizeDispatchMethod(RecordInfo* info) {
1270     ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_);
1271   }
1272 
ReportMissingDispatchMethod(RecordInfo * info,unsigned error)1273   void ReportMissingDispatchMethod(RecordInfo* info, unsigned error) {
1274     SourceLocation loc = info->record()->getInnerLocStart();
1275     SourceManager& manager = instance_.getSourceManager();
1276     FullSourceLoc full_loc(loc, manager);
1277     diagnostic_.Report(full_loc, error) << info->record();
1278   }
1279 
ReportVirtualAndManualDispatch(RecordInfo * info,CXXMethodDecl * dispatch)1280   void ReportVirtualAndManualDispatch(RecordInfo* info,
1281                                       CXXMethodDecl* dispatch) {
1282     SourceLocation loc = info->record()->getInnerLocStart();
1283     SourceManager& manager = instance_.getSourceManager();
1284     FullSourceLoc full_loc(loc, manager);
1285     diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_)
1286         << info->record();
1287     NoteManualDispatchMethod(dispatch);
1288   }
1289 
ReportMissingTraceDispatch(const FunctionDecl * dispatch,RecordInfo * receiver)1290   void ReportMissingTraceDispatch(const FunctionDecl* dispatch,
1291                                   RecordInfo* receiver) {
1292     ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_);
1293   }
1294 
ReportMissingFinalizeDispatch(const FunctionDecl * dispatch,RecordInfo * receiver)1295   void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch,
1296                                      RecordInfo* receiver) {
1297     ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_);
1298   }
1299 
ReportMissingDispatch(const FunctionDecl * dispatch,RecordInfo * receiver,unsigned error)1300   void ReportMissingDispatch(const FunctionDecl* dispatch,
1301                              RecordInfo* receiver,
1302                              unsigned error) {
1303     SourceLocation loc = dispatch->getLocStart();
1304     SourceManager& manager = instance_.getSourceManager();
1305     FullSourceLoc full_loc(loc, manager);
1306     diagnostic_.Report(full_loc, error) << receiver->record();
1307   }
1308 
ReportDerivesNonStackAllocated(RecordInfo * info,BasePoint * base)1309   void ReportDerivesNonStackAllocated(RecordInfo* info, BasePoint* base) {
1310     SourceLocation loc = base->spec().getLocStart();
1311     SourceManager& manager = instance_.getSourceManager();
1312     FullSourceLoc full_loc(loc, manager);
1313     diagnostic_.Report(full_loc, diag_derives_non_stack_allocated_)
1314         << info->record() << base->info()->record();
1315   }
1316 
ReportClassOverridesNew(RecordInfo * info,CXXMethodDecl * newop)1317   void ReportClassOverridesNew(RecordInfo* info, CXXMethodDecl* newop) {
1318     SourceLocation loc = newop->getLocStart();
1319     SourceManager& manager = instance_.getSourceManager();
1320     FullSourceLoc full_loc(loc, manager);
1321     diagnostic_.Report(full_loc, diag_class_overrides_new_) << info->record();
1322   }
1323 
ReportClassDeclaresPureVirtualTrace(RecordInfo * info,CXXMethodDecl * trace)1324   void ReportClassDeclaresPureVirtualTrace(RecordInfo* info,
1325                                            CXXMethodDecl* trace) {
1326     SourceLocation loc = trace->getLocStart();
1327     SourceManager& manager = instance_.getSourceManager();
1328     FullSourceLoc full_loc(loc, manager);
1329     diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_)
1330         << info->record();
1331   }
1332 
NoteManualDispatchMethod(CXXMethodDecl * dispatch)1333   void NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
1334     SourceLocation loc = dispatch->getLocStart();
1335     SourceManager& manager = instance_.getSourceManager();
1336     FullSourceLoc full_loc(loc, manager);
1337     diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch;
1338   }
1339 
NoteBaseRequiresTracing(BasePoint * base)1340   void NoteBaseRequiresTracing(BasePoint* base) {
1341     SourceLocation loc = base->spec().getLocStart();
1342     SourceManager& manager = instance_.getSourceManager();
1343     FullSourceLoc full_loc(loc, manager);
1344     diagnostic_.Report(full_loc, diag_base_requires_tracing_note_)
1345         << base->info()->record();
1346   }
1347 
NoteFieldRequiresTracing(RecordInfo * holder,FieldDecl * field)1348   void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
1349     NoteField(field, diag_field_requires_tracing_note_);
1350   }
1351 
NotePartObjectContainsGCRoot(FieldPoint * point)1352   void NotePartObjectContainsGCRoot(FieldPoint* point) {
1353     FieldDecl* field = point->field();
1354     SourceLocation loc = field->getLocStart();
1355     SourceManager& manager = instance_.getSourceManager();
1356     FullSourceLoc full_loc(loc, manager);
1357     diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_)
1358         << field << field->getParent();
1359   }
1360 
NoteFieldContainsGCRoot(FieldPoint * point)1361   void NoteFieldContainsGCRoot(FieldPoint* point) {
1362     NoteField(point, diag_field_contains_gc_root_note_);
1363   }
1364 
NoteUserDeclaredDestructor(CXXMethodDecl * dtor)1365   void NoteUserDeclaredDestructor(CXXMethodDecl* dtor) {
1366     SourceLocation loc = dtor->getLocStart();
1367     SourceManager& manager = instance_.getSourceManager();
1368     FullSourceLoc full_loc(loc, manager);
1369     diagnostic_.Report(full_loc, diag_user_declared_destructor_note_);
1370   }
1371 
NoteUserDeclaredFinalizer(CXXMethodDecl * dtor)1372   void NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) {
1373     SourceLocation loc = dtor->getLocStart();
1374     SourceManager& manager = instance_.getSourceManager();
1375     FullSourceLoc full_loc(loc, manager);
1376     diagnostic_.Report(full_loc, diag_user_declared_finalizer_note_);
1377   }
1378 
NoteBaseRequiresFinalization(BasePoint * base)1379   void NoteBaseRequiresFinalization(BasePoint* base) {
1380     SourceLocation loc = base->spec().getLocStart();
1381     SourceManager& manager = instance_.getSourceManager();
1382     FullSourceLoc full_loc(loc, manager);
1383     diagnostic_.Report(full_loc, diag_base_requires_finalization_note_)
1384         << base->info()->record();
1385   }
1386 
NoteField(FieldPoint * point,unsigned note)1387   void NoteField(FieldPoint* point, unsigned note) {
1388     NoteField(point->field(), note);
1389   }
1390 
NoteField(FieldDecl * field,unsigned note)1391   void NoteField(FieldDecl* field, unsigned note) {
1392     SourceLocation loc = field->getLocStart();
1393     SourceManager& manager = instance_.getSourceManager();
1394     FullSourceLoc full_loc(loc, manager);
1395     diagnostic_.Report(full_loc, note) << field;
1396   }
1397 
NoteOverriddenNonVirtualTrace(CXXMethodDecl * overridden)1398   void NoteOverriddenNonVirtualTrace(CXXMethodDecl* overridden) {
1399     SourceLocation loc = overridden->getLocStart();
1400     SourceManager& manager = instance_.getSourceManager();
1401     FullSourceLoc full_loc(loc, manager);
1402     diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_)
1403         << overridden;
1404   }
1405 
1406   unsigned diag_class_must_left_mostly_derive_gc_;
1407   unsigned diag_class_requires_trace_method_;
1408   unsigned diag_base_requires_tracing_;
1409   unsigned diag_fields_require_tracing_;
1410   unsigned diag_class_contains_invalid_fields_;
1411   unsigned diag_class_contains_gc_root_;
1412   unsigned diag_class_requires_finalization_;
1413   unsigned diag_finalizer_accesses_finalized_field_;
1414   unsigned diag_overridden_non_virtual_trace_;
1415   unsigned diag_missing_trace_dispatch_method_;
1416   unsigned diag_missing_finalize_dispatch_method_;
1417   unsigned diag_virtual_and_manual_dispatch_;
1418   unsigned diag_missing_trace_dispatch_;
1419   unsigned diag_missing_finalize_dispatch_;
1420   unsigned diag_derives_non_stack_allocated_;
1421   unsigned diag_class_overrides_new_;
1422   unsigned diag_class_declares_pure_virtual_trace_;
1423 
1424   unsigned diag_base_requires_tracing_note_;
1425   unsigned diag_field_requires_tracing_note_;
1426   unsigned diag_raw_ptr_to_gc_managed_class_note_;
1427   unsigned diag_ref_ptr_to_gc_managed_class_note_;
1428   unsigned diag_own_ptr_to_gc_managed_class_note_;
1429   unsigned diag_stack_allocated_field_note_;
1430   unsigned diag_member_in_unmanaged_class_note_;
1431   unsigned diag_part_object_to_gc_derived_class_note_;
1432   unsigned diag_part_object_contains_gc_root_note_;
1433   unsigned diag_field_contains_gc_root_note_;
1434   unsigned diag_finalized_field_note_;
1435   unsigned diag_user_declared_destructor_note_;
1436   unsigned diag_user_declared_finalizer_note_;
1437   unsigned diag_base_requires_finalization_note_;
1438   unsigned diag_field_requires_finalization_note_;
1439   unsigned diag_overridden_non_virtual_trace_note_;
1440   unsigned diag_manual_dispatch_method_note_;
1441 
1442   CompilerInstance& instance_;
1443   DiagnosticsEngine& diagnostic_;
1444   BlinkGCPluginOptions options_;
1445   RecordCache cache_;
1446   JsonWriter* json_;
1447 };
1448 
1449 class BlinkGCPluginAction : public PluginASTAction {
1450  public:
BlinkGCPluginAction()1451   BlinkGCPluginAction() {}
1452 
1453  protected:
1454   // Overridden from PluginASTAction:
CreateASTConsumer(CompilerInstance & instance,llvm::StringRef ref)1455   virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
1456                                          llvm::StringRef ref) {
1457     return new BlinkGCPluginConsumer(instance, options_);
1458   }
1459 
ParseArgs(const CompilerInstance & instance,const std::vector<string> & args)1460   virtual bool ParseArgs(const CompilerInstance& instance,
1461                          const std::vector<string>& args) {
1462     bool parsed = true;
1463 
1464     for (size_t i = 0; i < args.size() && parsed; ++i) {
1465       if (args[i] == "enable-oilpan") {
1466         options_.enable_oilpan = true;
1467       } else if (args[i] == "dump-graph") {
1468         options_.dump_graph = true;
1469       } else {
1470         parsed = false;
1471         llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
1472       }
1473     }
1474 
1475     return parsed;
1476   }
1477 
1478  private:
1479   BlinkGCPluginOptions options_;
1480 };
1481 
1482 }  // namespace
1483 
1484 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
1485     "blink-gc-plugin",
1486     "Check Blink GC invariants");
1487