1 // Copyright 2015 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 #include "BlinkGCPluginConsumer.h" 6 7 #include <algorithm> 8 #include <set> 9 10 #include "CheckDispatchVisitor.h" 11 #include "CheckFieldsVisitor.h" 12 #include "CheckFinalizerVisitor.h" 13 #include "CheckGCRootsVisitor.h" 14 #include "CheckTraceVisitor.h" 15 #include "CollectVisitor.h" 16 #include "JsonWriter.h" 17 #include "RecordInfo.h" 18 #include "clang/AST/RecursiveASTVisitor.h" 19 #include "clang/Sema/Sema.h" 20 21 using namespace clang; 22 23 namespace { 24 25 // Use a local RAV implementation to simply collect all FunctionDecls marked for 26 // late template parsing. This happens with the flag -fdelayed-template-parsing, 27 // which is on by default in MSVC-compatible mode. 28 std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) { 29 struct Visitor : public RecursiveASTVisitor<Visitor> { 30 bool VisitFunctionDecl(FunctionDecl* function_decl) { 31 if (function_decl->isLateTemplateParsed()) 32 late_parsed_decls.insert(function_decl); 33 return true; 34 } 35 36 std::set<FunctionDecl*> late_parsed_decls; 37 } v; 38 v.TraverseDecl(decl); 39 return v.late_parsed_decls; 40 } 41 42 class EmptyStmtVisitor : public RecursiveASTVisitor<EmptyStmtVisitor> { 43 public: 44 static bool isEmpty(Stmt* stmt) { 45 EmptyStmtVisitor visitor; 46 visitor.TraverseStmt(stmt); 47 return visitor.empty_; 48 } 49 50 bool WalkUpFromCompoundStmt(CompoundStmt* stmt) { 51 empty_ = stmt->body_empty(); 52 return false; 53 } 54 bool VisitStmt(Stmt*) { 55 empty_ = false; 56 return false; 57 } 58 private: 59 EmptyStmtVisitor() : empty_(true) {} 60 bool empty_; 61 }; 62 63 } // namespace 64 65 BlinkGCPluginConsumer::BlinkGCPluginConsumer( 66 clang::CompilerInstance& instance, 67 const BlinkGCPluginOptions& options) 68 : instance_(instance), 69 reporter_(instance), 70 options_(options), 71 cache_(instance), 72 json_(0) { 73 // Only check structures in the blink and WebKit namespaces. 74 options_.checked_namespaces.insert("blink"); 75 76 // Ignore GC implementation files. 77 options_.ignored_directories.push_back("/heap/"); 78 } 79 80 void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) { 81 // Don't run the plugin if the compilation unit is already invalid. 82 if (reporter_.hasErrorOccurred()) 83 return; 84 85 ParseFunctionTemplates(context.getTranslationUnitDecl()); 86 87 CollectVisitor visitor; 88 visitor.TraverseDecl(context.getTranslationUnitDecl()); 89 90 if (options_.dump_graph) { 91 std::error_code err; 92 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work. 93 json_ = JsonWriter::from(instance_.createOutputFile( 94 "", // OutputPath 95 err, // Errors 96 true, // Binary 97 true, // RemoveFileOnSignal 98 instance_.getFrontendOpts().OutputFile, // BaseInput 99 "graph.json", // Extension 100 false, // UseTemporary 101 false, // CreateMissingDirectories 102 0, // ResultPathName 103 0)); // TempPathName 104 if (!err && json_) { 105 json_->OpenList(); 106 } else { 107 json_ = 0; 108 llvm::errs() 109 << "[blink-gc] " 110 << "Failed to create an output file for the object graph.\n"; 111 } 112 } 113 114 for (const auto& record : visitor.record_decls()) 115 CheckRecord(cache_.Lookup(record)); 116 117 for (const auto& method : visitor.trace_decls()) 118 CheckTracingMethod(method); 119 120 if (json_) { 121 json_->CloseList(); 122 delete json_; 123 json_ = 0; 124 } 125 } 126 127 void BlinkGCPluginConsumer::ParseFunctionTemplates(TranslationUnitDecl* decl) { 128 if (!instance_.getLangOpts().DelayedTemplateParsing) 129 return; // Nothing to do. 130 131 std::set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl); 132 clang::Sema& sema = instance_.getSema(); 133 134 for (const FunctionDecl* fd : late_parsed_decls) { 135 assert(fd->isLateTemplateParsed()); 136 137 if (!Config::IsTraceMethod(fd)) 138 continue; 139 140 if (instance_.getSourceManager().isInSystemHeader( 141 instance_.getSourceManager().getSpellingLoc(fd->getLocation()))) 142 continue; 143 144 // Force parsing and AST building of the yet-uninstantiated function 145 // template trace method bodies. 146 clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd]; 147 sema.LateTemplateParser(sema.OpaqueParser, *lpt); 148 } 149 } 150 151 void BlinkGCPluginConsumer::CheckRecord(RecordInfo* info) { 152 if (IsIgnored(info)) 153 return; 154 155 CXXRecordDecl* record = info->record(); 156 157 // TODO: what should we do to check unions? 158 if (record->isUnion()) 159 return; 160 161 // If this is the primary template declaration, check its specializations. 162 if (record->isThisDeclarationADefinition() && 163 record->getDescribedClassTemplate()) { 164 ClassTemplateDecl* tmpl = record->getDescribedClassTemplate(); 165 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); 166 it != tmpl->spec_end(); 167 ++it) { 168 CheckClass(cache_.Lookup(*it)); 169 } 170 return; 171 } 172 173 CheckClass(info); 174 } 175 176 void BlinkGCPluginConsumer::CheckClass(RecordInfo* info) { 177 if (!info) 178 return; 179 180 if (CXXMethodDecl* trace = info->GetTraceMethod()) { 181 if (trace->isPure()) 182 reporter_.ClassDeclaresPureVirtualTrace(info, trace); 183 } else if (info->RequiresTraceMethod()) { 184 reporter_.ClassRequiresTraceMethod(info); 185 } 186 187 // Check polymorphic classes that are GC-derived or have a trace method. 188 if (info->record()->hasDefinition() && info->record()->isPolymorphic()) { 189 // TODO: Check classes that inherit a trace method. 190 CXXMethodDecl* trace = info->GetTraceMethod(); 191 if (trace || info->IsGCDerived()) 192 CheckPolymorphicClass(info, trace); 193 } 194 195 { 196 CheckFieldsVisitor visitor; 197 if (visitor.ContainsInvalidFields(info)) 198 reporter_.ClassContainsInvalidFields(info, visitor.invalid_fields()); 199 } 200 201 if (info->IsGCDerived()) { 202 // It is illegal for a class to be both stack allocated and garbage 203 // collected. 204 if (info->IsStackAllocated()) { 205 for (auto& base : info->GetBases()) { 206 RecordInfo* base_info = base.second.info(); 207 if (Config::IsGCBase(base_info->name()) || base_info->IsGCDerived()) { 208 reporter_.StackAllocatedDerivesGarbageCollected(info, &base.second); 209 } 210 } 211 } 212 213 if (!info->IsGCMixin()) { 214 CheckLeftMostDerived(info); 215 CheckDispatch(info); 216 if (CXXMethodDecl* newop = info->DeclaresNewOperator()) 217 if (!Config::IsIgnoreAnnotated(newop)) 218 reporter_.ClassOverridesNew(info, newop); 219 } 220 221 { 222 CheckGCRootsVisitor visitor; 223 if (visitor.ContainsGCRoots(info)) 224 reporter_.ClassContainsGCRoots(info, visitor.gc_roots()); 225 } 226 227 if (info->NeedsFinalization()) 228 CheckFinalization(info); 229 230 if (options_.warn_unneeded_finalizer && info->IsGCFinalized()) 231 CheckUnneededFinalization(info); 232 } 233 234 DumpClass(info); 235 } 236 237 CXXRecordDecl* BlinkGCPluginConsumer::GetDependentTemplatedDecl( 238 const Type& type) { 239 const TemplateSpecializationType* tmpl_type = 240 type.getAs<TemplateSpecializationType>(); 241 if (!tmpl_type) 242 return 0; 243 244 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); 245 if (!tmpl_decl) 246 return 0; 247 248 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); 249 } 250 251 // The GC infrastructure assumes that if the vtable of a polymorphic 252 // base-class is not initialized for a given object (ie, it is partially 253 // initialized) then the object does not need to be traced. Thus, we must 254 // ensure that any polymorphic class with a trace method does not have any 255 // tractable fields that are initialized before we are sure that the vtable 256 // and the trace method are both defined. There are two cases that need to 257 // hold to satisfy that assumption: 258 // 259 // 1. If trace is virtual, then it must be defined in the left-most base. 260 // This ensures that if the vtable is initialized then it contains a pointer 261 // to the trace method. 262 // 263 // 2. If trace is non-virtual, then the trace method is defined and we must 264 // ensure that the left-most base defines a vtable. This ensures that the 265 // first thing to be initialized when constructing the object is the vtable 266 // itself. 267 void BlinkGCPluginConsumer::CheckPolymorphicClass( 268 RecordInfo* info, 269 CXXMethodDecl* trace) { 270 CXXRecordDecl* left_most = info->record(); 271 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); 272 CXXRecordDecl* left_most_base = 0; 273 while (it != left_most->bases_end()) { 274 left_most_base = it->getType()->getAsCXXRecordDecl(); 275 if (!left_most_base && it->getType()->isDependentType()) 276 left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType()); 277 278 // TODO: Find a way to correctly check actual instantiations 279 // for dependent types. The escape below will be hit, eg, when 280 // we have a primary template with no definition and 281 // specializations for each case (such as SupplementBase) in 282 // which case we don't succeed in checking the required 283 // properties. 284 if (!left_most_base || !left_most_base->hasDefinition()) 285 return; 286 287 StringRef name = left_most_base->getName(); 288 // We know GCMixin base defines virtual trace. 289 if (Config::IsGCMixinBase(name)) 290 return; 291 292 // Stop with the left-most prior to a safe polymorphic base (a safe base 293 // is non-polymorphic and contains no fields). 294 if (Config::IsSafePolymorphicBase(name)) 295 break; 296 297 left_most = left_most_base; 298 it = left_most->bases_begin(); 299 } 300 301 if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { 302 // Check condition (1): 303 if (trace && trace->isVirtual()) { 304 if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { 305 if (trace->isVirtual()) 306 return; 307 } 308 reporter_.BaseClassMustDeclareVirtualTrace(info, left_most); 309 return; 310 } 311 312 // Check condition (2): 313 if (DeclaresVirtualMethods(left_most)) 314 return; 315 if (left_most_base) { 316 // Get the base next to the "safe polymorphic base" 317 if (it != left_most->bases_end()) 318 ++it; 319 if (it != left_most->bases_end()) { 320 if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { 321 if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { 322 if (DeclaresVirtualMethods(next_left_most)) 323 return; 324 reporter_.LeftMostBaseMustBePolymorphic(info, next_left_most); 325 return; 326 } 327 } 328 } 329 } 330 reporter_.LeftMostBaseMustBePolymorphic(info, left_most); 331 } 332 } 333 334 CXXRecordDecl* BlinkGCPluginConsumer::GetLeftMostBase( 335 CXXRecordDecl* left_most) { 336 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); 337 while (it != left_most->bases_end()) { 338 if (it->getType()->isDependentType()) 339 left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType()); 340 else 341 left_most = it->getType()->getAsCXXRecordDecl(); 342 if (!left_most || !left_most->hasDefinition()) 343 return 0; 344 it = left_most->bases_begin(); 345 } 346 return left_most; 347 } 348 349 bool BlinkGCPluginConsumer::DeclaresVirtualMethods(CXXRecordDecl* decl) { 350 CXXRecordDecl::method_iterator it = decl->method_begin(); 351 for (; it != decl->method_end(); ++it) 352 if (it->isVirtual() && !it->isPure()) 353 return true; 354 return false; 355 } 356 357 void BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) { 358 CXXRecordDecl* left_most = GetLeftMostBase(info->record()); 359 if (!left_most) 360 return; 361 if (!Config::IsGCBase(left_most->getName())) 362 reporter_.ClassMustLeftMostlyDeriveGC(info); 363 } 364 365 void BlinkGCPluginConsumer::CheckDispatch(RecordInfo* info) { 366 bool finalized = info->IsGCFinalized(); 367 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); 368 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); 369 if (!trace_dispatch && !finalize_dispatch) 370 return; 371 372 CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent() 373 : finalize_dispatch->getParent(); 374 375 // Check that dispatch methods are defined at the base. 376 if (base == info->record()) { 377 if (!trace_dispatch) 378 reporter_.MissingTraceDispatchMethod(info); 379 if (finalized && !finalize_dispatch) 380 reporter_.MissingFinalizeDispatchMethod(info); 381 if (!finalized && finalize_dispatch) { 382 reporter_.ClassRequiresFinalization(info); 383 reporter_.NoteUserDeclaredFinalizer(finalize_dispatch); 384 } 385 } 386 387 // Check that classes implementing manual dispatch do not have vtables. 388 if (info->record()->isPolymorphic()) { 389 reporter_.VirtualAndManualDispatch( 390 info, trace_dispatch ? trace_dispatch : finalize_dispatch); 391 } 392 393 // If this is a non-abstract class check that it is dispatched to. 394 // TODO: Create a global variant of this local check. We can only check if 395 // the dispatch body is known in this compilation unit. 396 if (info->IsConsideredAbstract()) 397 return; 398 399 const FunctionDecl* defn; 400 401 if (trace_dispatch && trace_dispatch->isDefined(defn)) { 402 CheckDispatchVisitor visitor(info); 403 visitor.TraverseStmt(defn->getBody()); 404 if (!visitor.dispatched_to_receiver()) 405 reporter_.MissingTraceDispatch(defn, info); 406 } 407 408 if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) { 409 CheckDispatchVisitor visitor(info); 410 visitor.TraverseStmt(defn->getBody()); 411 if (!visitor.dispatched_to_receiver()) 412 reporter_.MissingFinalizeDispatch(defn, info); 413 } 414 } 415 416 // TODO: Should we collect destructors similar to trace methods? 417 void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) { 418 CXXDestructorDecl* dtor = info->record()->getDestructor(); 419 420 // For finalized classes, check the finalization method if possible. 421 if (info->IsGCFinalized()) { 422 if (dtor && dtor->hasBody()) { 423 CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized()); 424 visitor.TraverseCXXMethodDecl(dtor); 425 if (!visitor.finalized_fields().empty()) { 426 reporter_.FinalizerAccessesFinalizedFields( 427 dtor, visitor.finalized_fields()); 428 } 429 } 430 return; 431 } 432 433 // Don't require finalization of a mixin that has not yet been "mixed in". 434 if (info->IsGCMixin()) 435 return; 436 437 // Report the finalization error, and proceed to print possible causes for 438 // the finalization requirement. 439 reporter_.ClassRequiresFinalization(info); 440 441 if (dtor && dtor->isUserProvided()) 442 reporter_.NoteUserDeclaredDestructor(dtor); 443 444 for (auto& base : info->GetBases()) 445 if (base.second.info()->NeedsFinalization()) 446 reporter_.NoteBaseRequiresFinalization(&base.second); 447 448 for (auto& field : info->GetFields()) 449 if (field.second.edge()->NeedsFinalization()) 450 reporter_.NoteFieldRequiresFinalization(&field.second); 451 } 452 453 void BlinkGCPluginConsumer::CheckUnneededFinalization(RecordInfo* info) { 454 if (!HasNonEmptyFinalizer(info)) 455 reporter_.ClassDoesNotRequireFinalization(info); 456 } 457 458 bool BlinkGCPluginConsumer::HasNonEmptyFinalizer(RecordInfo* info) { 459 CXXDestructorDecl* dtor = info->record()->getDestructor(); 460 461 // If the destructor is virtual (or one of the bases are by way of the 462 // recursive call below), consider this class as having a non-empty 463 // finalizer. Not doing so runs counter to standard C++ reflexes like 464 // 465 // class A : public GarbageCollectedMixin { 466 // public: 467 // virtual ~A() { }; 468 // virtual void f() = 0; 469 // }; 470 // class B : public GarbageCollectedFinalized<B>, public A { 471 // USING_GARBAGE_COLLECTED_MIXIN(B); 472 // public: 473 // ~B() override { } 474 // void f() override { } 475 // }; 476 // 477 // and it is considered a step too far to report a warning for such 478 // explicit usage of empty destructors. 479 if (dtor && dtor->isVirtual()) 480 return true; 481 482 if (dtor && dtor->isUserProvided()) { 483 if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody())) 484 return true; 485 } 486 487 if (info->GetFinalizeDispatchMethod()) 488 return true; 489 490 for (auto& base : info->GetBases()) 491 if (HasNonEmptyFinalizer(base.second.info())) 492 return true; 493 494 for (auto& field : info->GetFields()) 495 if (field.second.edge()->NeedsFinalization()) 496 return true; 497 498 return false; 499 } 500 501 void BlinkGCPluginConsumer::CheckTracingMethod(CXXMethodDecl* method) { 502 RecordInfo* parent = cache_.Lookup(method->getParent()); 503 if (IsIgnored(parent)) 504 return; 505 506 // Check templated tracing methods by checking the template instantiations. 507 // Specialized templates are handled as ordinary classes. 508 if (ClassTemplateDecl* tmpl = 509 parent->record()->getDescribedClassTemplate()) { 510 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); 511 it != tmpl->spec_end(); 512 ++it) { 513 // Check trace using each template instantiation as the holder. 514 if (Config::IsTemplateInstantiation(*it)) 515 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); 516 } 517 return; 518 } 519 520 CheckTraceOrDispatchMethod(parent, method); 521 } 522 523 void BlinkGCPluginConsumer::CheckTraceOrDispatchMethod( 524 RecordInfo* parent, 525 CXXMethodDecl* method) { 526 Config::TraceMethodType trace_type = Config::GetTraceMethodType(method); 527 if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD || 528 trace_type == Config::TRACE_AFTER_DISPATCH_IMPL_METHOD || 529 !parent->GetTraceDispatchMethod()) { 530 CheckTraceMethod(parent, method, trace_type); 531 } 532 // Dispatch methods are checked when we identify subclasses. 533 } 534 535 void BlinkGCPluginConsumer::CheckTraceMethod( 536 RecordInfo* parent, 537 CXXMethodDecl* trace, 538 Config::TraceMethodType trace_type) { 539 // A trace method must not override any non-virtual trace methods. 540 if (trace_type == Config::TRACE_METHOD) { 541 for (auto& base : parent->GetBases()) 542 if (CXXMethodDecl* other = base.second.info()->InheritsNonVirtualTrace()) 543 reporter_.OverriddenNonVirtualTrace(parent, trace, other); 544 } 545 546 CheckTraceVisitor visitor(trace, parent, &cache_); 547 visitor.TraverseCXXMethodDecl(trace); 548 549 // Skip reporting if this trace method is a just delegate to 550 // traceImpl (or traceAfterDispatchImpl) method. We will report on 551 // CheckTraceMethod on traceImpl method. 552 if (visitor.delegates_to_traceimpl()) 553 return; 554 555 for (auto& base : parent->GetBases()) 556 if (!base.second.IsProperlyTraced()) 557 reporter_.BaseRequiresTracing(parent, trace, base.first); 558 559 for (auto& field : parent->GetFields()) { 560 if (!field.second.IsProperlyTraced() || 561 field.second.IsInproperlyTraced()) { 562 // Report one or more tracing-related field errors. 563 reporter_.FieldsImproperlyTraced(parent, trace); 564 break; 565 } 566 } 567 } 568 569 void BlinkGCPluginConsumer::DumpClass(RecordInfo* info) { 570 if (!json_) 571 return; 572 573 json_->OpenObject(); 574 json_->Write("name", info->record()->getQualifiedNameAsString()); 575 json_->Write("loc", GetLocString(info->record()->getLocStart())); 576 json_->CloseObject(); 577 578 class DumpEdgeVisitor : public RecursiveEdgeVisitor { 579 public: 580 DumpEdgeVisitor(JsonWriter* json) : json_(json) {} 581 void DumpEdge(RecordInfo* src, 582 RecordInfo* dst, 583 const std::string& lbl, 584 const Edge::LivenessKind& kind, 585 const std::string& loc) { 586 json_->OpenObject(); 587 json_->Write("src", src->record()->getQualifiedNameAsString()); 588 json_->Write("dst", dst->record()->getQualifiedNameAsString()); 589 json_->Write("lbl", lbl); 590 json_->Write("kind", kind); 591 json_->Write("loc", loc); 592 json_->Write("ptr", 593 !Parent() ? "val" : 594 Parent()->IsRawPtr() ? 595 (static_cast<RawPtr*>(Parent())->HasReferenceType() ? 596 "reference" : "raw") : 597 Parent()->IsRefPtr() ? "ref" : 598 Parent()->IsOwnPtr() ? "own" : 599 Parent()->IsUniquePtr() ? "unique" : 600 (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" : 601 "val"); 602 json_->CloseObject(); 603 } 604 605 void DumpField(RecordInfo* src, FieldPoint* point, const std::string& loc) { 606 src_ = src; 607 point_ = point; 608 loc_ = loc; 609 point_->edge()->Accept(this); 610 } 611 612 void AtValue(Value* e) override { 613 // The liveness kind of a path from the point to this value 614 // is given by the innermost place that is non-strong. 615 Edge::LivenessKind kind = Edge::kStrong; 616 if (Config::IsIgnoreCycleAnnotated(point_->field())) { 617 kind = Edge::kWeak; 618 } else { 619 for (Context::iterator it = context().begin(); 620 it != context().end(); 621 ++it) { 622 Edge::LivenessKind pointer_kind = (*it)->Kind(); 623 if (pointer_kind != Edge::kStrong) { 624 kind = pointer_kind; 625 break; 626 } 627 } 628 } 629 DumpEdge( 630 src_, e->value(), point_->field()->getNameAsString(), kind, loc_); 631 } 632 633 private: 634 JsonWriter* json_; 635 RecordInfo* src_; 636 FieldPoint* point_; 637 std::string loc_; 638 }; 639 640 DumpEdgeVisitor visitor(json_); 641 642 for (auto& base : info->GetBases()) 643 visitor.DumpEdge(info, 644 base.second.info(), 645 "<super>", 646 Edge::kStrong, 647 GetLocString(base.second.spec().getLocStart())); 648 649 for (auto& field : info->GetFields()) 650 visitor.DumpField(info, 651 &field.second, 652 GetLocString(field.second.field()->getLocStart())); 653 } 654 655 std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) { 656 const SourceManager& source_manager = instance_.getSourceManager(); 657 PresumedLoc ploc = source_manager.getPresumedLoc(loc); 658 if (ploc.isInvalid()) 659 return ""; 660 std::string loc_str; 661 llvm::raw_string_ostream os(loc_str); 662 os << ploc.getFilename() 663 << ":" << ploc.getLine() 664 << ":" << ploc.getColumn(); 665 return os.str(); 666 } 667 668 bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) { 669 return (!record || 670 !InCheckedNamespace(record) || 671 IsIgnoredClass(record) || 672 InIgnoredDirectory(record)); 673 } 674 675 bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) { 676 // Ignore any class prefixed by SameSizeAs. These are used in 677 // Blink to verify class sizes and don't need checking. 678 const std::string SameSizeAs = "SameSizeAs"; 679 if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0) 680 return true; 681 return (options_.ignored_classes.find(info->name()) != 682 options_.ignored_classes.end()); 683 } 684 685 bool BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) { 686 std::string filename; 687 if (!GetFilename(info->record()->getLocStart(), &filename)) 688 return false; // TODO: should we ignore non-existing file locations? 689 #if defined(LLVM_ON_WIN32) 690 std::replace(filename.begin(), filename.end(), '\\', '/'); 691 #endif 692 for (const auto& dir : options_.ignored_directories) 693 if (filename.find(dir) != std::string::npos) 694 return true; 695 return false; 696 } 697 698 bool BlinkGCPluginConsumer::InCheckedNamespace(RecordInfo* info) { 699 if (!info) 700 return false; 701 for (DeclContext* context = info->record()->getDeclContext(); 702 !context->isTranslationUnit(); 703 context = context->getParent()) { 704 if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) { 705 if (decl->isAnonymousNamespace()) 706 return true; 707 if (options_.checked_namespaces.find(decl->getNameAsString()) != 708 options_.checked_namespaces.end()) { 709 return true; 710 } 711 } 712 } 713 return false; 714 } 715 716 bool BlinkGCPluginConsumer::GetFilename(SourceLocation loc, 717 std::string* filename) { 718 const SourceManager& source_manager = instance_.getSourceManager(); 719 SourceLocation spelling_location = source_manager.getSpellingLoc(loc); 720 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); 721 if (ploc.isInvalid()) { 722 // If we're in an invalid location, we're looking at things that aren't 723 // actually stated in the source. 724 return false; 725 } 726 *filename = ploc.getFilename(); 727 return true; 728 } 729