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