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 { 133 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. 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: 167 CollectVisitor() {} 168 169 RecordVector& record_decls() { return record_decls_; } 170 MethodVector& trace_decls() { return trace_decls_; } 171 172 bool shouldVisitTemplateInstantiations() { return false; } 173 174 // Collect record declarations, including nested declarations. 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. 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: 205 MightBeCollectedVisitor() : might_be_collected_(false) {} 206 bool might_be_collected() { return might_be_collected_; } 207 void VisitMember(Member* edge) override { might_be_collected_ = true; } 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 223 CheckFinalizerVisitor(RecordCache* cache) 224 : blacklist_context_(false), cache_(cache) {} 225 226 Errors& finalized_fields() { return finalized_fields_; } 227 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. 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 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 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: 284 CheckDispatchVisitor(RecordInfo* receiver) 285 : receiver_(receiver), dispatched_to_receiver_(false) {} 286 287 bool dispatched_to_receiver() { return dispatched_to_receiver_; } 288 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: 307 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) 308 : trace_(trace), info_(info) {} 309 310 // Allow recursive traversal by using VisitMemberExpr. 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. 383 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} 384 385 bool IsWeakCallback() { return !trace_; } 386 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 405 CheckGCRootsVisitor() {} 406 407 Errors& gc_roots() { return gc_roots_; } 408 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 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 435 void VisitPersistent(Persistent* edge) override { 436 gc_roots_.push_back(current_); 437 } 438 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 468 CheckFieldsVisitor(const BlinkGCPluginOptions& options) 469 : options_(options), current_(0), stack_allocated_host_(false) {} 470 471 Errors& invalid_fields() { return invalid_fields_; } 472 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 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 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 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: 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: 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 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. 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). 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 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 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? 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. 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). 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. 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 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. 1052 DiagnosticsEngine::Level getErrorLevel() { 1053 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error 1054 : DiagnosticsEngine::Warning; 1055 } 1056 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 1070 bool IsIgnored(RecordInfo* record) { 1071 return !record || 1072 !InCheckedNamespace(record) || 1073 IsIgnoredClass(record) || 1074 InIgnoredDirectory(record); 1075 } 1076 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 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 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 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 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 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 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 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 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 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 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 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 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 1265 void ReportMissingTraceDispatchMethod(RecordInfo* info) { 1266 ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_); 1267 } 1268 1269 void ReportMissingFinalizeDispatchMethod(RecordInfo* info) { 1270 ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_); 1271 } 1272 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 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 1290 void ReportMissingTraceDispatch(const FunctionDecl* dispatch, 1291 RecordInfo* receiver) { 1292 ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); 1293 } 1294 1295 void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch, 1296 RecordInfo* receiver) { 1297 ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); 1298 } 1299 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 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 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 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 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 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 1348 void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) { 1349 NoteField(field, diag_field_requires_tracing_note_); 1350 } 1351 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 1361 void NoteFieldContainsGCRoot(FieldPoint* point) { 1362 NoteField(point, diag_field_contains_gc_root_note_); 1363 } 1364 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 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 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 1387 void NoteField(FieldPoint* point, unsigned note) { 1388 NoteField(point->field(), note); 1389 } 1390 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 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: 1451 BlinkGCPluginAction() {} 1452 1453 protected: 1454 // Overridden from PluginASTAction: 1455 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance, 1456 llvm::StringRef ref) { 1457 return new BlinkGCPluginConsumer(instance, options_); 1458 } 1459 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