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