| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | // This clang plugin checks various invariants of the Blink garbage | 
 | // collection infrastructure. | 
 | // | 
 | // Errors are described at: | 
 | // http://www.chromium.org/developers/blink-gc-plugin-errors | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "Config.h" | 
 | #include "JsonWriter.h" | 
 | #include "RecordInfo.h" | 
 |  | 
 | #include "clang/AST/AST.h" | 
 | #include "clang/AST/ASTConsumer.h" | 
 | #include "clang/AST/RecursiveASTVisitor.h" | 
 | #include "clang/Frontend/CompilerInstance.h" | 
 | #include "clang/Frontend/FrontendPluginRegistry.h" | 
 | #include "clang/Sema/Sema.h" | 
 |  | 
 | using namespace clang; | 
 | using std::string; | 
 |  | 
 | namespace { | 
 |  | 
 | const char kClassMustLeftMostlyDeriveGC[] = | 
 |     "[blink-gc] Class %0 must derive its GC base in the left-most position."; | 
 |  | 
 | const char kClassRequiresTraceMethod[] = | 
 |     "[blink-gc] Class %0 requires a trace method."; | 
 |  | 
 | const char kBaseRequiresTracing[] = | 
 |     "[blink-gc] Base class %0 of derived class %1 requires tracing."; | 
 |  | 
 | const char kBaseRequiresTracingNote[] = | 
 |     "[blink-gc] Untraced base class %0 declared here:"; | 
 |  | 
 | const char kFieldsRequireTracing[] = | 
 |     "[blink-gc] Class %0 has untraced fields that require tracing."; | 
 |  | 
 | const char kFieldRequiresTracingNote[] = | 
 |     "[blink-gc] Untraced field %0 declared here:"; | 
 |  | 
 | const char kClassContainsInvalidFields[] = | 
 |     "[blink-gc] Class %0 contains invalid fields."; | 
 |  | 
 | const char kClassContainsGCRoot[] = | 
 |     "[blink-gc] Class %0 contains GC root in field %1."; | 
 |  | 
 | const char kClassRequiresFinalization[] = | 
 |     "[blink-gc] Class %0 requires finalization."; | 
 |  | 
 | const char kClassDoesNotRequireFinalization[] = | 
 |     "[blink-gc] Class %0 may not require finalization."; | 
 |  | 
 | const char kFinalizerAccessesFinalizedField[] = | 
 |     "[blink-gc] Finalizer %0 accesses potentially finalized field %1."; | 
 |  | 
 | const char kFinalizerAccessesEagerlyFinalizedField[] = | 
 |     "[blink-gc] Finalizer %0 accesses eagerly finalized field %1."; | 
 |  | 
 | const char kRawPtrToGCManagedClassNote[] = | 
 |     "[blink-gc] Raw pointer field %0 to a GC managed class declared here:"; | 
 |  | 
 | const char kRefPtrToGCManagedClassNote[] = | 
 |     "[blink-gc] RefPtr field %0 to a GC managed class declared here:"; | 
 |  | 
 | const char kOwnPtrToGCManagedClassNote[] = | 
 |     "[blink-gc] OwnPtr field %0 to a GC managed class declared here:"; | 
 |  | 
 | const char kMemberToGCUnmanagedClassNote[] = | 
 |     "[blink-gc] Member field %0 to non-GC managed class declared here:"; | 
 |  | 
 | const char kStackAllocatedFieldNote[] = | 
 |     "[blink-gc] Stack-allocated field %0 declared here:"; | 
 |  | 
 | const char kMemberInUnmanagedClassNote[] = | 
 |     "[blink-gc] Member field %0 in unmanaged class declared here:"; | 
 |  | 
 | const char kPartObjectToGCDerivedClassNote[] = | 
 |     "[blink-gc] Part-object field %0 to a GC derived class declared here:"; | 
 |  | 
 | const char kPartObjectContainsGCRootNote[] = | 
 |     "[blink-gc] Field %0 with embedded GC root in %1 declared here:"; | 
 |  | 
 | const char kFieldContainsGCRootNote[] = | 
 |     "[blink-gc] Field %0 defining a GC root declared here:"; | 
 |  | 
 | const char kOverriddenNonVirtualTrace[] = | 
 |     "[blink-gc] Class %0 overrides non-virtual trace of base class %1."; | 
 |  | 
 | const char kOverriddenNonVirtualTraceNote[] = | 
 |     "[blink-gc] Non-virtual trace method declared here:"; | 
 |  | 
 | const char kMissingTraceDispatchMethod[] = | 
 |     "[blink-gc] Class %0 is missing manual trace dispatch."; | 
 |  | 
 | const char kMissingFinalizeDispatchMethod[] = | 
 |     "[blink-gc] Class %0 is missing manual finalize dispatch."; | 
 |  | 
 | const char kVirtualAndManualDispatch[] = | 
 |     "[blink-gc] Class %0 contains or inherits virtual methods" | 
 |     " but implements manual dispatching."; | 
 |  | 
 | const char kMissingTraceDispatch[] = | 
 |     "[blink-gc] Missing dispatch to class %0 in manual trace dispatch."; | 
 |  | 
 | const char kMissingFinalizeDispatch[] = | 
 |     "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch."; | 
 |  | 
 | const char kFinalizedFieldNote[] = | 
 |     "[blink-gc] Potentially finalized field %0 declared here:"; | 
 |  | 
 | const char kEagerlyFinalizedFieldNote[] = | 
 |     "[blink-gc] Field %0 having eagerly finalized value, declared here:"; | 
 |  | 
 | const char kUserDeclaredDestructorNote[] = | 
 |     "[blink-gc] User-declared destructor declared here:"; | 
 |  | 
 | const char kUserDeclaredFinalizerNote[] = | 
 |     "[blink-gc] User-declared finalizer declared here:"; | 
 |  | 
 | const char kBaseRequiresFinalizationNote[] = | 
 |     "[blink-gc] Base class %0 requiring finalization declared here:"; | 
 |  | 
 | const char kFieldRequiresFinalizationNote[] = | 
 |     "[blink-gc] Field %0 requiring finalization declared here:"; | 
 |  | 
 | const char kManualDispatchMethodNote[] = | 
 |     "[blink-gc] Manual dispatch %0 declared here:"; | 
 |  | 
 | const char kDerivesNonStackAllocated[] = | 
 |     "[blink-gc] Stack-allocated class %0 derives class %1" | 
 |     " which is not stack allocated."; | 
 |  | 
 | const char kClassOverridesNew[] = | 
 |     "[blink-gc] Garbage collected class %0" | 
 |     " is not permitted to override its new operator."; | 
 |  | 
 | const char kClassDeclaresPureVirtualTrace[] = | 
 |     "[blink-gc] Garbage collected class %0" | 
 |     " is not permitted to declare a pure-virtual trace method."; | 
 |  | 
 | const char kLeftMostBaseMustBePolymorphic[] = | 
 |     "[blink-gc] Left-most base class %0 of derived class %1" | 
 |     " must be polymorphic."; | 
 |  | 
 | const char kBaseClassMustDeclareVirtualTrace[] = | 
 |     "[blink-gc] Left-most base class %0 of derived class %1" | 
 |     " must define a virtual trace method."; | 
 |  | 
 | const char kClassMustDeclareGCMixinTraceMethod[] = | 
 |     "[blink-gc] Class %0 which inherits from GarbageCollectedMixin must" | 
 |     " locally declare and override trace(Visitor*)"; | 
 |  | 
 | // Use a local RAV implementation to simply collect all FunctionDecls marked for | 
 | // late template parsing. This happens with the flag -fdelayed-template-parsing, | 
 | // which is on by default in MSVC-compatible mode. | 
 | std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) { | 
 |   struct Visitor : public RecursiveASTVisitor<Visitor> { | 
 |     bool VisitFunctionDecl(FunctionDecl* function_decl) { | 
 |       if (function_decl->isLateTemplateParsed()) | 
 |         late_parsed_decls.insert(function_decl); | 
 |       return true; | 
 |     } | 
 |  | 
 |     std::set<FunctionDecl*> late_parsed_decls; | 
 |   } v; | 
 |   v.TraverseDecl(decl); | 
 |   return v.late_parsed_decls; | 
 | } | 
 |  | 
 | struct BlinkGCPluginOptions { | 
 |   BlinkGCPluginOptions() | 
 |     : enable_oilpan(false) | 
 |     , dump_graph(false) | 
 |     , warn_raw_ptr(false) | 
 |     , warn_unneeded_finalizer(false) {} | 
 |   bool enable_oilpan; | 
 |   bool dump_graph; | 
 |   bool warn_raw_ptr; | 
 |   bool warn_unneeded_finalizer; | 
 |   std::set<std::string> ignored_classes; | 
 |   std::set<std::string> checked_namespaces; | 
 |   std::vector<std::string> ignored_directories; | 
 | }; | 
 |  | 
 | typedef std::vector<CXXRecordDecl*> RecordVector; | 
 | typedef std::vector<CXXMethodDecl*> MethodVector; | 
 |  | 
 | // Test if a template specialization is an instantiation. | 
 | static bool IsTemplateInstantiation(CXXRecordDecl* record) { | 
 |   ClassTemplateSpecializationDecl* spec = | 
 |       dyn_cast<ClassTemplateSpecializationDecl>(record); | 
 |   if (!spec) | 
 |     return false; | 
 |   switch (spec->getTemplateSpecializationKind()) { | 
 |     case TSK_ImplicitInstantiation: | 
 |     case TSK_ExplicitInstantiationDefinition: | 
 |       return true; | 
 |     case TSK_Undeclared: | 
 |     case TSK_ExplicitSpecialization: | 
 |       return false; | 
 |     // TODO: unsupported cases. | 
 |     case TSK_ExplicitInstantiationDeclaration: | 
 |       return false; | 
 |   } | 
 |   assert(false && "Unknown template specialization kind"); | 
 | } | 
 |  | 
 | // This visitor collects the entry points for the checker. | 
 | class CollectVisitor : public RecursiveASTVisitor<CollectVisitor> { | 
 |  public: | 
 |   CollectVisitor() {} | 
 |  | 
 |   RecordVector& record_decls() { return record_decls_; } | 
 |   MethodVector& trace_decls() { return trace_decls_; } | 
 |  | 
 |   bool shouldVisitTemplateInstantiations() { return false; } | 
 |  | 
 |   // Collect record declarations, including nested declarations. | 
 |   bool VisitCXXRecordDecl(CXXRecordDecl* record) { | 
 |     if (record->hasDefinition() && record->isCompleteDefinition()) | 
 |       record_decls_.push_back(record); | 
 |     return true; | 
 |   } | 
 |  | 
 |   // Collect tracing method definitions, but don't traverse method bodies. | 
 |   bool TraverseCXXMethodDecl(CXXMethodDecl* method) { | 
 |     if (method->isThisDeclarationADefinition() && Config::IsTraceMethod(method)) | 
 |       trace_decls_.push_back(method); | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   RecordVector record_decls_; | 
 |   MethodVector trace_decls_; | 
 | }; | 
 |  | 
 | // This visitor checks that a finalizer method does not have invalid access to | 
 | // fields that are potentially finalized. A potentially finalized field is | 
 | // either a Member, a heap-allocated collection or an off-heap collection that | 
 | // contains Members.  Invalid uses are currently identified as passing the field | 
 | // as the argument of a procedure call or using the -> or [] operators on it. | 
 | class CheckFinalizerVisitor | 
 |     : public RecursiveASTVisitor<CheckFinalizerVisitor> { | 
 |  private: | 
 |   // Simple visitor to determine if the content of a field might be collected | 
 |   // during finalization. | 
 |   class MightBeCollectedVisitor : public EdgeVisitor { | 
 |    public: | 
 |     MightBeCollectedVisitor(bool is_eagerly_finalized) | 
 |         : might_be_collected_(false) | 
 |         , is_eagerly_finalized_(is_eagerly_finalized) | 
 |         , as_eagerly_finalized_(false) {} | 
 |     bool might_be_collected() { return might_be_collected_; } | 
 |     bool as_eagerly_finalized() { return as_eagerly_finalized_; } | 
 |     void VisitMember(Member* edge) override { | 
 |       if (is_eagerly_finalized_) { | 
 |         if (edge->ptr()->IsValue()) { | 
 |           Value* member = static_cast<Value*>(edge->ptr()); | 
 |           if (member->value()->IsEagerlyFinalized()) { | 
 |             might_be_collected_ = true; | 
 |             as_eagerly_finalized_ = true; | 
 |           } | 
 |         } | 
 |         return; | 
 |       } | 
 |       might_be_collected_ = true; | 
 |     } | 
 |     void VisitCollection(Collection* edge) override { | 
 |       if (edge->on_heap() && !is_eagerly_finalized_) { | 
 |         might_be_collected_ = !edge->is_root(); | 
 |       } else { | 
 |         edge->AcceptMembers(this); | 
 |       } | 
 |     } | 
 |  | 
 |    private: | 
 |     bool might_be_collected_; | 
 |     bool is_eagerly_finalized_; | 
 |     bool as_eagerly_finalized_; | 
 |   }; | 
 |  | 
 |  public: | 
 |   class Error { | 
 |   public: | 
 |     Error(MemberExpr *member, | 
 |           bool as_eagerly_finalized, | 
 |           FieldPoint* field) | 
 |         : member_(member) | 
 |         , as_eagerly_finalized_(as_eagerly_finalized) | 
 |         , field_(field) {} | 
 |  | 
 |     MemberExpr* member_; | 
 |     bool as_eagerly_finalized_; | 
 |     FieldPoint* field_; | 
 |   }; | 
 |  | 
 |   typedef std::vector<Error> Errors; | 
 |  | 
 |   CheckFinalizerVisitor(RecordCache* cache, bool is_eagerly_finalized) | 
 |       : blacklist_context_(false) | 
 |       , cache_(cache) | 
 |       , is_eagerly_finalized_(is_eagerly_finalized) {} | 
 |  | 
 |   Errors& finalized_fields() { return finalized_fields_; } | 
 |  | 
 |   bool WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr* expr) { | 
 |     // Only continue the walk-up if the operator is a blacklisted one. | 
 |     switch (expr->getOperator()) { | 
 |       case OO_Arrow: | 
 |       case OO_Subscript: | 
 |         this->WalkUpFromCallExpr(expr); | 
 |       default: | 
 |         return true; | 
 |     } | 
 |   } | 
 |  | 
 |   // We consider all non-operator calls to be blacklisted contexts. | 
 |   bool WalkUpFromCallExpr(CallExpr* expr) { | 
 |     bool prev_blacklist_context = blacklist_context_; | 
 |     blacklist_context_ = true; | 
 |     for (size_t i = 0; i < expr->getNumArgs(); ++i) | 
 |       this->TraverseStmt(expr->getArg(i)); | 
 |     blacklist_context_ = prev_blacklist_context; | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool VisitMemberExpr(MemberExpr* member) { | 
 |     FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl()); | 
 |     if (!field) | 
 |       return true; | 
 |  | 
 |     RecordInfo* info = cache_->Lookup(field->getParent()); | 
 |     if (!info) | 
 |       return true; | 
 |  | 
 |     RecordInfo::Fields::iterator it = info->GetFields().find(field); | 
 |     if (it == info->GetFields().end()) | 
 |       return true; | 
 |  | 
 |     if (seen_members_.find(member) != seen_members_.end()) | 
 |       return true; | 
 |  | 
 |     bool as_eagerly_finalized = false; | 
 |     if (blacklist_context_ && | 
 |         MightBeCollected(&it->second, as_eagerly_finalized)) { | 
 |       finalized_fields_.push_back( | 
 |           Error(member, as_eagerly_finalized, &it->second)); | 
 |       seen_members_.insert(member); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool MightBeCollected(FieldPoint* point, bool& as_eagerly_finalized) { | 
 |     MightBeCollectedVisitor visitor(is_eagerly_finalized_); | 
 |     point->edge()->Accept(&visitor); | 
 |     as_eagerly_finalized = visitor.as_eagerly_finalized(); | 
 |     return visitor.might_be_collected(); | 
 |   } | 
 |  | 
 |  private: | 
 |   bool blacklist_context_; | 
 |   Errors finalized_fields_; | 
 |   std::set<MemberExpr*> seen_members_; | 
 |   RecordCache* cache_; | 
 |   bool is_eagerly_finalized_; | 
 | }; | 
 |  | 
 | // This visitor checks that a method contains within its body, a call to a | 
 | // method on the provided receiver class. This is used to check manual | 
 | // dispatching for trace and finalize methods. | 
 | class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> { | 
 |  public: | 
 |   CheckDispatchVisitor(RecordInfo* receiver) | 
 |       : receiver_(receiver), dispatched_to_receiver_(false) {} | 
 |  | 
 |   bool dispatched_to_receiver() { return dispatched_to_receiver_; } | 
 |  | 
 |   bool VisitMemberExpr(MemberExpr* member) { | 
 |     if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) { | 
 |       if (fn->getParent() == receiver_->record()) | 
 |         dispatched_to_receiver_ = true; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool VisitUnresolvedMemberExpr(UnresolvedMemberExpr* member) { | 
 |     for (Decl* decl : member->decls()) { | 
 |       if (CXXMethodDecl* method = dyn_cast<CXXMethodDecl>(decl)) { | 
 |         if (method->getParent() == receiver_->record() && | 
 |             Config::GetTraceMethodType(method) == | 
 |             Config::TRACE_AFTER_DISPATCH_METHOD) { | 
 |           dispatched_to_receiver_ = true; | 
 |           return true; | 
 |         } | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   RecordInfo* receiver_; | 
 |   bool dispatched_to_receiver_; | 
 | }; | 
 |  | 
 | // This visitor checks a tracing method by traversing its body. | 
 | // - A member field is considered traced if it is referenced in the body. | 
 | // - A base is traced if a base-qualified call to a trace method is found. | 
 | class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { | 
 |  public: | 
 |   CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info, RecordCache* cache) | 
 |       : trace_(trace), | 
 |         info_(info), | 
 |         cache_(cache), | 
 |         delegates_to_traceimpl_(false) { | 
 |   } | 
 |  | 
 |   bool delegates_to_traceimpl() const { return delegates_to_traceimpl_; } | 
 |  | 
 |   bool VisitMemberExpr(MemberExpr* member) { | 
 |     // In weak callbacks, consider any occurrence as a correct usage. | 
 |     // TODO: We really want to require that isAlive is checked on manually | 
 |     // processed weak fields. | 
 |     if (IsWeakCallback()) { | 
 |       if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) | 
 |         FoundField(field); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool VisitCallExpr(CallExpr* call) { | 
 |     // In weak callbacks we don't check calls (see VisitMemberExpr). | 
 |     if (IsWeakCallback()) | 
 |       return true; | 
 |  | 
 |     Expr* callee = call->getCallee(); | 
 |  | 
 |     // Trace calls from a templated derived class result in a | 
 |     // DependentScopeMemberExpr because the concrete trace call depends on the | 
 |     // instantiation of any shared template parameters. In this case the call is | 
 |     // "unresolved" and we resort to comparing the syntactic type names. | 
 |     if (CXXDependentScopeMemberExpr* expr = | 
 |         dyn_cast<CXXDependentScopeMemberExpr>(callee)) { | 
 |       CheckCXXDependentScopeMemberExpr(call, expr); | 
 |       return true; | 
 |     } | 
 |  | 
 |     // A tracing call will have either a |visitor| or a |m_field| argument. | 
 |     // A registerWeakMembers call will have a |this| argument. | 
 |     if (call->getNumArgs() != 1) | 
 |       return true; | 
 |     Expr* arg = call->getArg(0); | 
 |  | 
 |     if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { | 
 |       // This could be a trace call of a base class, as explained in the | 
 |       // comments of CheckTraceBaseCall(). | 
 |       if (CheckTraceBaseCall(call)) | 
 |         return true; | 
 |  | 
 |       if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) | 
 |         MarkAllWeakMembersTraced(); | 
 |  | 
 |       QualType base = expr->getBaseType(); | 
 |       if (!base->isPointerType()) | 
 |         return true; | 
 |       CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); | 
 |       if (decl) | 
 |         CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); | 
 |       if (Config::IsTraceImplName(expr->getMemberName().getAsString())) | 
 |         delegates_to_traceimpl_ = true; | 
 |       return true; | 
 |     } | 
 |  | 
 |     if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { | 
 |       if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) | 
 |         return true; | 
 |  | 
 |       if (Config::IsTraceImplName(expr->getMethodDecl()->getNameAsString())) { | 
 |         delegates_to_traceimpl_ = true; | 
 |         return true; | 
 |       } | 
 |     } | 
 |  | 
 |     CheckTraceBaseCall(call); | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   bool IsTraceCallName(const std::string& name) { | 
 |     if (trace_->getName() == kTraceImplName) | 
 |       return name == kTraceName; | 
 |     if (trace_->getName() == kTraceAfterDispatchImplName) | 
 |       return name == kTraceAfterDispatchName; | 
 |     // Currently, a manually dispatched class cannot have mixin bases (having | 
 |     // one would add a vtable which we explicitly check against). This means | 
 |     // that we can only make calls to a trace method of the same name. Revisit | 
 |     // this if our mixin/vtable assumption changes. | 
 |     return name == trace_->getName(); | 
 |   } | 
 |  | 
 |   CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { | 
 |     NestedNameSpecifier* qual = expr->getQualifier(); | 
 |     if (!qual) | 
 |       return 0; | 
 |  | 
 |     const Type* type = qual->getAsType(); | 
 |     if (!type) | 
 |       return 0; | 
 |  | 
 |     return RecordInfo::GetDependentTemplatedDecl(*type); | 
 |   } | 
 |  | 
 |   void CheckCXXDependentScopeMemberExpr(CallExpr* call, | 
 |                                         CXXDependentScopeMemberExpr* expr) { | 
 |     string fn_name = expr->getMember().getAsString(); | 
 |  | 
 |     // Check for VisitorDispatcher::trace(field) and | 
 |     // VisitorDispatcher::registerWeakMembers. | 
 |     if (!expr->isImplicitAccess()) { | 
 |       if (clang::DeclRefExpr* base_decl = | 
 |               clang::dyn_cast<clang::DeclRefExpr>(expr->getBase())) { | 
 |         if (Config::IsVisitorDispatcherType(base_decl->getType())) { | 
 |           if (call->getNumArgs() == 1 && fn_name == kTraceName) { | 
 |             FindFieldVisitor finder; | 
 |             finder.TraverseStmt(call->getArg(0)); | 
 |             if (finder.field()) | 
 |               FoundField(finder.field()); | 
 |  | 
 |             return; | 
 |           } else if (call->getNumArgs() == 1 && | 
 |                      fn_name == kRegisterWeakMembersName) { | 
 |             MarkAllWeakMembersTraced(); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); | 
 |     if (!tmpl) | 
 |       return; | 
 |  | 
 |     // Check for Super<T>::trace(visitor) | 
 |     if (call->getNumArgs() == 1 && IsTraceCallName(fn_name)) { | 
 |       RecordInfo::Bases::iterator it = info_->GetBases().begin(); | 
 |       for (; it != info_->GetBases().end(); ++it) { | 
 |         if (it->first->getName() == tmpl->getName()) | 
 |           it->second.MarkTraced(); | 
 |       } | 
 |     } | 
 |  | 
 |     // Check for TraceIfNeeded<T>::trace(visitor, &field) | 
 |     if (call->getNumArgs() == 2 && fn_name == kTraceName && | 
 |         tmpl->getName() == kTraceIfNeededName) { | 
 |       FindFieldVisitor finder; | 
 |       finder.TraverseStmt(call->getArg(1)); | 
 |       if (finder.field()) | 
 |         FoundField(finder.field()); | 
 |     } | 
 |   } | 
 |  | 
 |   bool CheckTraceBaseCall(CallExpr* call) { | 
 |     // Checks for "Base::trace(visitor)"-like calls. | 
 |  | 
 |     // Checking code for these two variables is shared among MemberExpr* case | 
 |     // and UnresolvedMemberCase* case below. | 
 |     // | 
 |     // For example, if we've got "Base::trace(visitor)" as |call|, | 
 |     // callee_record will be "Base", and func_name will be "trace". | 
 |     CXXRecordDecl* callee_record = nullptr; | 
 |     std::string func_name; | 
 |  | 
 |     if (MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee())) { | 
 |       if (!callee->hasQualifier()) | 
 |         return false; | 
 |  | 
 |       FunctionDecl* trace_decl = | 
 |           dyn_cast<FunctionDecl>(callee->getMemberDecl()); | 
 |       if (!trace_decl || !Config::IsTraceMethod(trace_decl)) | 
 |         return false; | 
 |  | 
 |       const Type* type = callee->getQualifier()->getAsType(); | 
 |       if (!type) | 
 |         return false; | 
 |  | 
 |       callee_record = type->getAsCXXRecordDecl(); | 
 |       func_name = trace_decl->getName(); | 
 |     } else if (UnresolvedMemberExpr* callee = | 
 |                dyn_cast<UnresolvedMemberExpr>(call->getCallee())) { | 
 |       // Callee part may become unresolved if the type of the argument | 
 |       // ("visitor") is a template parameter and the called function is | 
 |       // overloaded (i.e. trace(Visitor*) and | 
 |       // trace(InlinedGlobalMarkingVisitor)). | 
 |       // | 
 |       // Here, we try to find a function that looks like trace() from the | 
 |       // candidate overloaded functions, and if we find one, we assume it is | 
 |       // called here. | 
 |  | 
 |       CXXMethodDecl* trace_decl = nullptr; | 
 |       for (NamedDecl* named_decl : callee->decls()) { | 
 |         if (CXXMethodDecl* method_decl = dyn_cast<CXXMethodDecl>(named_decl)) { | 
 |           if (Config::IsTraceMethod(method_decl)) { | 
 |             trace_decl = method_decl; | 
 |             break; | 
 |           } | 
 |         } | 
 |       } | 
 |       if (!trace_decl) | 
 |         return false; | 
 |  | 
 |       // Check if the passed argument is named "visitor". | 
 |       if (call->getNumArgs() != 1) | 
 |         return false; | 
 |       DeclRefExpr* arg = dyn_cast<DeclRefExpr>(call->getArg(0)); | 
 |       if (!arg || arg->getNameInfo().getAsString() != kVisitorVarName) | 
 |         return false; | 
 |  | 
 |       callee_record = trace_decl->getParent(); | 
 |       func_name = callee->getMemberName().getAsString(); | 
 |     } | 
 |  | 
 |     if (!callee_record) | 
 |       return false; | 
 |  | 
 |     if (!IsTraceCallName(func_name)) | 
 |       return false; | 
 |  | 
 |     for (auto& base : info_->GetBases()) { | 
 |       // We want to deal with omitted trace() function in an intermediary | 
 |       // class in the class hierarchy, e.g.: | 
 |       //     class A : public GarbageCollected<A> { trace() { ... } }; | 
 |       //     class B : public A { /* No trace(); have nothing to trace. */ }; | 
 |       //     class C : public B { trace() { B::trace(visitor); } } | 
 |       // where, B::trace() is actually A::trace(), and in some cases we get | 
 |       // A as |callee_record| instead of B. We somehow need to mark B as | 
 |       // traced if we find A::trace() call. | 
 |       // | 
 |       // To solve this, here we keep going up the class hierarchy as long as | 
 |       // they are not required to have a trace method. The implementation is | 
 |       // a simple DFS, where |base_records| represents the set of base classes | 
 |       // we need to visit. | 
 |  | 
 |       std::vector<CXXRecordDecl*> base_records; | 
 |       base_records.push_back(base.first); | 
 |  | 
 |       while (!base_records.empty()) { | 
 |         CXXRecordDecl* base_record = base_records.back(); | 
 |         base_records.pop_back(); | 
 |  | 
 |         if (base_record == callee_record) { | 
 |           // If we find a matching trace method, pretend the user has written | 
 |           // a correct trace() method of the base; in the example above, we | 
 |           // find A::trace() here and mark B as correctly traced. | 
 |           base.second.MarkTraced(); | 
 |           return true; | 
 |         } | 
 |  | 
 |         if (RecordInfo* base_info = cache_->Lookup(base_record)) { | 
 |           if (!base_info->RequiresTraceMethod()) { | 
 |             // If this base class is not required to have a trace method, then | 
 |             // the actual trace method may be defined in an ancestor. | 
 |             for (auto& inner_base : base_info->GetBases()) | 
 |               base_records.push_back(inner_base.first); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool CheckTraceFieldCall(CXXMemberCallExpr* call) { | 
 |     return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), | 
 |                                call->getRecordDecl(), | 
 |                                call->getArg(0)); | 
 |   } | 
 |  | 
 |   bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { | 
 |     if (name != kTraceName || !Config::IsVisitor(callee->getName())) | 
 |       return false; | 
 |  | 
 |     FindFieldVisitor finder; | 
 |     finder.TraverseStmt(arg); | 
 |     if (finder.field()) | 
 |       FoundField(finder.field()); | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool CheckRegisterWeakMembers(CXXMemberCallExpr* call) { | 
 |     CXXMethodDecl* fn = call->getMethodDecl(); | 
 |     if (fn->getName() != kRegisterWeakMembersName) | 
 |       return false; | 
 |  | 
 |     if (fn->isTemplateInstantiation()) { | 
 |       const TemplateArgumentList& args = | 
 |           *fn->getTemplateSpecializationInfo()->TemplateArguments; | 
 |       // The second template argument is the callback method. | 
 |       if (args.size() > 1 && | 
 |           args[1].getKind() == TemplateArgument::Declaration) { | 
 |         if (FunctionDecl* callback = | 
 |             dyn_cast<FunctionDecl>(args[1].getAsDecl())) { | 
 |           if (callback->hasBody()) { | 
 |             CheckTraceVisitor nested_visitor(info_); | 
 |             nested_visitor.TraverseStmt(callback->getBody()); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { | 
 |    public: | 
 |     FindFieldVisitor() : member_(0), field_(0) {} | 
 |     MemberExpr* member() const { return member_; } | 
 |     FieldDecl* field() const { return field_; } | 
 |     bool TraverseMemberExpr(MemberExpr* member) { | 
 |       if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { | 
 |         member_ = member; | 
 |         field_ = field; | 
 |         return false; | 
 |       } | 
 |       return true; | 
 |     } | 
 |    private: | 
 |     MemberExpr* member_; | 
 |     FieldDecl* field_; | 
 |   }; | 
 |  | 
 |   // Nested checking for weak callbacks. | 
 |   CheckTraceVisitor(RecordInfo* info) | 
 |       : trace_(nullptr), info_(info), cache_(nullptr) {} | 
 |  | 
 |   bool IsWeakCallback() { return !trace_; } | 
 |  | 
 |   void MarkTraced(RecordInfo::Fields::iterator it) { | 
 |     // In a weak callback we can't mark strong fields as traced. | 
 |     if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) | 
 |       return; | 
 |     it->second.MarkTraced(); | 
 |   } | 
 |  | 
 |   void FoundField(FieldDecl* field) { | 
 |     if (IsTemplateInstantiation(info_->record())) { | 
 |       // Pointer equality on fields does not work for template instantiations. | 
 |       // The trace method refers to fields of the template definition which | 
 |       // are different from the instantiated fields that need to be traced. | 
 |       const string& name = field->getNameAsString(); | 
 |       for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); | 
 |            it != info_->GetFields().end(); | 
 |            ++it) { | 
 |         if (it->first->getNameAsString() == name) { | 
 |           MarkTraced(it); | 
 |           break; | 
 |         } | 
 |       } | 
 |     } else { | 
 |       RecordInfo::Fields::iterator it = info_->GetFields().find(field); | 
 |       if (it != info_->GetFields().end()) | 
 |         MarkTraced(it); | 
 |     } | 
 |   } | 
 |  | 
 |   void MarkAllWeakMembersTraced() { | 
 |     // If we find a call to registerWeakMembers which is unresolved we | 
 |     // unsoundly consider all weak members as traced. | 
 |     // TODO: Find out how to validate weak member tracing for unresolved call. | 
 |     for (auto& field : info_->GetFields()) { | 
 |       if (field.second.edge()->IsWeakMember()) | 
 |         field.second.MarkTraced(); | 
 |     } | 
 |   } | 
 |  | 
 |   CXXMethodDecl* trace_; | 
 |   RecordInfo* info_; | 
 |   RecordCache* cache_; | 
 |   bool delegates_to_traceimpl_; | 
 | }; | 
 |  | 
 | // This visitor checks that the fields of a class and the fields of | 
 | // its part objects don't define GC roots. | 
 | class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 
 |  public: | 
 |   typedef std::vector<FieldPoint*> RootPath; | 
 |   typedef std::set<RecordInfo*> VisitingSet; | 
 |   typedef std::vector<RootPath> Errors; | 
 |  | 
 |   CheckGCRootsVisitor() {} | 
 |  | 
 |   Errors& gc_roots() { return gc_roots_; } | 
 |  | 
 |   bool ContainsGCRoots(RecordInfo* info) { | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       current_.push_back(&it->second); | 
 |       it->second.edge()->Accept(this); | 
 |       current_.pop_back(); | 
 |     } | 
 |     return !gc_roots_.empty(); | 
 |   } | 
 |  | 
 |   void VisitValue(Value* edge) override { | 
 |     // TODO: what should we do to check unions? | 
 |     if (edge->value()->record()->isUnion()) | 
 |       return; | 
 |  | 
 |     // Prevent infinite regress for cyclic part objects. | 
 |     if (visiting_set_.find(edge->value()) != visiting_set_.end()) | 
 |       return; | 
 |  | 
 |     visiting_set_.insert(edge->value()); | 
 |     // If the value is a part object, then continue checking for roots. | 
 |     for (Context::iterator it = context().begin(); | 
 |          it != context().end(); | 
 |          ++it) { | 
 |       if (!(*it)->IsCollection()) | 
 |         return; | 
 |     } | 
 |     ContainsGCRoots(edge->value()); | 
 |     visiting_set_.erase(edge->value()); | 
 |   } | 
 |  | 
 |   void VisitPersistent(Persistent* edge) override { | 
 |     gc_roots_.push_back(current_); | 
 |   } | 
 |  | 
 |   void AtCollection(Collection* edge) override { | 
 |     if (edge->is_root()) | 
 |       gc_roots_.push_back(current_); | 
 |   } | 
 |  | 
 |  protected: | 
 |   RootPath current_; | 
 |   VisitingSet visiting_set_; | 
 |   Errors gc_roots_; | 
 | }; | 
 |  | 
 | // This visitor checks that the fields of a class are "well formed". | 
 | // - OwnPtr, RefPtr and RawPtr must not point to a GC derived types. | 
 | // - Part objects must not be GC derived types. | 
 | // - An on-heap class must never contain GC roots. | 
 | // - Only stack-allocated types may point to stack-allocated types. | 
 | class CheckFieldsVisitor : public RecursiveEdgeVisitor { | 
 |  public: | 
 |  | 
 |   enum Error { | 
 |     kRawPtrToGCManaged, | 
 |     kRawPtrToGCManagedWarning, | 
 |     kRefPtrToGCManaged, | 
 |     kOwnPtrToGCManaged, | 
 |     kMemberToGCUnmanaged, | 
 |     kMemberInUnmanaged, | 
 |     kPtrFromHeapToStack, | 
 |     kGCDerivedPartObject | 
 |   }; | 
 |  | 
 |   typedef std::vector<std::pair<FieldPoint*, Error> > Errors; | 
 |  | 
 |   CheckFieldsVisitor(const BlinkGCPluginOptions& options) | 
 |       : options_(options), current_(0), stack_allocated_host_(false) {} | 
 |  | 
 |   Errors& invalid_fields() { return invalid_fields_; } | 
 |  | 
 |   bool ContainsInvalidFields(RecordInfo* info) { | 
 |     stack_allocated_host_ = info->IsStackAllocated(); | 
 |     managed_host_ = stack_allocated_host_ || | 
 |                     info->IsGCAllocated() || | 
 |                     info->IsNonNewable() || | 
 |                     info->IsOnlyPlacementNewable(); | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       context().clear(); | 
 |       current_ = &it->second; | 
 |       current_->edge()->Accept(this); | 
 |     } | 
 |     return !invalid_fields_.empty(); | 
 |   } | 
 |  | 
 |   void AtMember(Member* edge) override { | 
 |     if (managed_host_) | 
 |       return; | 
 |     // A member is allowed to appear in the context of a root. | 
 |     for (Context::iterator it = context().begin(); | 
 |          it != context().end(); | 
 |          ++it) { | 
 |       if ((*it)->Kind() == Edge::kRoot) | 
 |         return; | 
 |     } | 
 |     invalid_fields_.push_back(std::make_pair(current_, kMemberInUnmanaged)); | 
 |   } | 
 |  | 
 |   void AtValue(Value* edge) override { | 
 |     // TODO: what should we do to check unions? | 
 |     if (edge->value()->record()->isUnion()) | 
 |       return; | 
 |  | 
 |     if (!stack_allocated_host_ && edge->value()->IsStackAllocated()) { | 
 |       invalid_fields_.push_back(std::make_pair(current_, kPtrFromHeapToStack)); | 
 |       return; | 
 |     } | 
 |  | 
 |     if (!Parent() && | 
 |         edge->value()->IsGCDerived() && | 
 |         !edge->value()->IsGCMixin()) { | 
 |       invalid_fields_.push_back(std::make_pair(current_, kGCDerivedPartObject)); | 
 |       return; | 
 |     } | 
 |  | 
 |     // If in a stack allocated context, be fairly insistent that T in Member<T> | 
 |     // is GC allocated, as stack allocated objects do not have a trace() | 
 |     // that separately verifies the validity of Member<T>. | 
 |     // | 
 |     // Notice that an error is only reported if T's definition is in scope; | 
 |     // we do not require that it must be brought into scope as that would | 
 |     // prevent declarations of mutually dependent class types. | 
 |     // | 
 |     // (Note: Member<>'s constructor will at run-time verify that the | 
 |     // pointer it wraps is indeed heap allocated.) | 
 |     if (stack_allocated_host_ && Parent() && Parent()->IsMember() && | 
 |         edge->value()->HasDefinition() && !edge->value()->IsGCAllocated()) { | 
 |       invalid_fields_.push_back(std::make_pair(current_, | 
 |                                                kMemberToGCUnmanaged)); | 
 |       return; | 
 |     } | 
 |  | 
 |     if (!Parent() || !edge->value()->IsGCAllocated()) | 
 |       return; | 
 |  | 
 |     // In transition mode, disallow  OwnPtr<T>, RawPtr<T> to GC allocated T's, | 
 |     // also disallow T* in stack-allocated types. | 
 |     if (options_.enable_oilpan) { | 
 |       if (Parent()->IsOwnPtr() || | 
 |           Parent()->IsRawPtrClass() || | 
 |           (stack_allocated_host_ && Parent()->IsRawPtr())) { | 
 |         invalid_fields_.push_back(std::make_pair( | 
 |             current_, InvalidSmartPtr(Parent()))); | 
 |         return; | 
 |       } | 
 |       if (options_.warn_raw_ptr && Parent()->IsRawPtr()) { | 
 |         invalid_fields_.push_back(std::make_pair( | 
 |             current_, kRawPtrToGCManagedWarning)); | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     if (Parent()->IsRawPtr() || Parent()->IsRefPtr() || Parent()->IsOwnPtr()) { | 
 |       invalid_fields_.push_back(std::make_pair( | 
 |           current_, InvalidSmartPtr(Parent()))); | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   void AtCollection(Collection* edge) override { | 
 |     if (edge->on_heap() && Parent() && Parent()->IsOwnPtr()) | 
 |       invalid_fields_.push_back(std::make_pair(current_, kOwnPtrToGCManaged)); | 
 |   } | 
 |  | 
 |  private: | 
 |   Error InvalidSmartPtr(Edge* ptr) { | 
 |     if (ptr->IsRawPtr()) | 
 |       return kRawPtrToGCManaged; | 
 |     if (ptr->IsRefPtr()) | 
 |       return kRefPtrToGCManaged; | 
 |     if (ptr->IsOwnPtr()) | 
 |       return kOwnPtrToGCManaged; | 
 |     assert(false && "Unknown smart pointer kind"); | 
 |   } | 
 |  | 
 |   const BlinkGCPluginOptions& options_; | 
 |   FieldPoint* current_; | 
 |   bool stack_allocated_host_; | 
 |   bool managed_host_; | 
 |   Errors invalid_fields_; | 
 | }; | 
 |  | 
 | class EmptyStmtVisitor | 
 |     : public RecursiveASTVisitor<EmptyStmtVisitor> { | 
 | public: | 
 |   static bool isEmpty(Stmt* stmt) { | 
 |     EmptyStmtVisitor visitor; | 
 |     visitor.TraverseStmt(stmt); | 
 |     return visitor.empty_; | 
 |   } | 
 |  | 
 |   bool WalkUpFromCompoundStmt(CompoundStmt* stmt) { | 
 |     empty_ = stmt->body_empty(); | 
 |     return false; | 
 |   } | 
 |   bool VisitStmt(Stmt*) { | 
 |     empty_ = false; | 
 |     return false; | 
 |   } | 
 | private: | 
 |   EmptyStmtVisitor() : empty_(true) {} | 
 |   bool empty_; | 
 | }; | 
 |  | 
 | // Main class containing checks for various invariants of the Blink | 
 | // garbage collection infrastructure. | 
 | class BlinkGCPluginConsumer : public ASTConsumer { | 
 |  public: | 
 |   BlinkGCPluginConsumer(CompilerInstance& instance, | 
 |                         const BlinkGCPluginOptions& options) | 
 |       : instance_(instance), | 
 |         diagnostic_(instance.getDiagnostics()), | 
 |         options_(options), | 
 |         json_(0) { | 
 |  | 
 |     // Only check structures in the blink and WebKit namespaces. | 
 |     options_.checked_namespaces.insert("blink"); | 
 |  | 
 |     // Ignore GC implementation files. | 
 |     options_.ignored_directories.push_back("/heap/"); | 
 |  | 
 |     // Register warning/error messages. | 
 |     diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kClassMustLeftMostlyDeriveGC); | 
 |     diag_class_requires_trace_method_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod); | 
 |     diag_base_requires_tracing_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing); | 
 |     diag_fields_require_tracing_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing); | 
 |     diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kClassContainsInvalidFields); | 
 |     diag_class_contains_invalid_fields_warning_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Warning, kClassContainsInvalidFields); | 
 |     diag_class_contains_gc_root_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot); | 
 |     diag_class_requires_finalization_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kClassRequiresFinalization); | 
 |     diag_class_does_not_require_finalization_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Warning, kClassDoesNotRequireFinalization); | 
 |     diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kFinalizerAccessesFinalizedField); | 
 |     diag_finalizer_eagerly_finalized_field_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kFinalizerAccessesEagerlyFinalizedField); | 
 |     diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kOverriddenNonVirtualTrace); | 
 |     diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kMissingTraceDispatchMethod); | 
 |     diag_missing_finalize_dispatch_method_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kMissingFinalizeDispatchMethod); | 
 |     diag_virtual_and_manual_dispatch_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch); | 
 |     diag_missing_trace_dispatch_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch); | 
 |     diag_missing_finalize_dispatch_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch); | 
 |     diag_derives_non_stack_allocated_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated); | 
 |     diag_class_overrides_new_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); | 
 |     diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kClassDeclaresPureVirtualTrace); | 
 |     diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kLeftMostBaseMustBePolymorphic); | 
 |     diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID( | 
 |         getErrorLevel(), kBaseClassMustDeclareVirtualTrace); | 
 |     diag_class_must_declare_gc_mixin_trace_method_ = | 
 |         diagnostic_.getCustomDiagID(getErrorLevel(), | 
 |                                     kClassMustDeclareGCMixinTraceMethod); | 
 |  | 
 |     // Register note messages. | 
 |     diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kBaseRequiresTracingNote); | 
 |     diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kFieldRequiresTracingNote); | 
 |     diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); | 
 |     diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); | 
 |     diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote); | 
 |     diag_member_to_gc_unmanaged_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kMemberToGCUnmanagedClassNote); | 
 |     diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kStackAllocatedFieldNote); | 
 |     diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kMemberInUnmanagedClassNote); | 
 |     diag_part_object_to_gc_derived_class_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kPartObjectToGCDerivedClassNote); | 
 |     diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kPartObjectContainsGCRootNote); | 
 |     diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kFieldContainsGCRootNote); | 
 |     diag_finalized_field_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kFinalizedFieldNote); | 
 |     diag_eagerly_finalized_field_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kEagerlyFinalizedFieldNote); | 
 |     diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kUserDeclaredDestructorNote); | 
 |     diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kUserDeclaredFinalizerNote); | 
 |     diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kBaseRequiresFinalizationNote); | 
 |     diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kFieldRequiresFinalizationNote); | 
 |     diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote); | 
 |     diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, kManualDispatchMethodNote); | 
 |   } | 
 |  | 
 |   void HandleTranslationUnit(ASTContext& context) override { | 
 |     // Don't run the plugin if the compilation unit is already invalid. | 
 |     if (diagnostic_.hasErrorOccurred()) | 
 |       return; | 
 |  | 
 |     ParseFunctionTemplates(context.getTranslationUnitDecl()); | 
 |  | 
 |     CollectVisitor visitor; | 
 |     visitor.TraverseDecl(context.getTranslationUnitDecl()); | 
 |  | 
 |     if (options_.dump_graph) { | 
 |       std::error_code err; | 
 |       // TODO: Make createDefaultOutputFile or a shorter createOutputFile work. | 
 |       json_ = JsonWriter::from(instance_.createOutputFile( | 
 |           "",                                      // OutputPath | 
 |           err,                                     // Errors | 
 |           true,                                    // Binary | 
 |           true,                                    // RemoveFileOnSignal | 
 |           instance_.getFrontendOpts().OutputFile,  // BaseInput | 
 |           "graph.json",                            // Extension | 
 |           false,                                   // UseTemporary | 
 |           false,                                   // CreateMissingDirectories | 
 |           0,                                       // ResultPathName | 
 |           0));                                     // TempPathName | 
 |       if (!err && json_) { | 
 |         json_->OpenList(); | 
 |       } else { | 
 |         json_ = 0; | 
 |         llvm::errs() | 
 |             << "[blink-gc] " | 
 |             << "Failed to create an output file for the object graph.\n"; | 
 |       } | 
 |     } | 
 |  | 
 |     for (RecordVector::iterator it = visitor.record_decls().begin(); | 
 |          it != visitor.record_decls().end(); | 
 |          ++it) { | 
 |       CheckRecord(cache_.Lookup(*it)); | 
 |     } | 
 |  | 
 |     for (MethodVector::iterator it = visitor.trace_decls().begin(); | 
 |          it != visitor.trace_decls().end(); | 
 |          ++it) { | 
 |       CheckTracingMethod(*it); | 
 |     } | 
 |  | 
 |     if (json_) { | 
 |       json_->CloseList(); | 
 |       delete json_; | 
 |       json_ = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   void ParseFunctionTemplates(TranslationUnitDecl* decl) { | 
 |     if (!instance_.getLangOpts().DelayedTemplateParsing) | 
 |       return;  // Nothing to do. | 
 |  | 
 |     std::set<FunctionDecl*> late_parsed_decls = | 
 |         GetLateParsedFunctionDecls(decl); | 
 |     clang::Sema& sema = instance_.getSema(); | 
 |  | 
 |     for (const FunctionDecl* fd : late_parsed_decls) { | 
 |       assert(fd->isLateTemplateParsed()); | 
 |  | 
 |       if (!Config::IsTraceMethod(fd)) | 
 |         continue; | 
 |  | 
 |       if (instance_.getSourceManager().isInSystemHeader( | 
 |               instance_.getSourceManager().getSpellingLoc(fd->getLocation()))) | 
 |         continue; | 
 |  | 
 |       // Force parsing and AST building of the yet-uninstantiated function | 
 |       // template trace method bodies. | 
 |       clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd]; | 
 |       sema.LateTemplateParser(sema.OpaqueParser, *lpt); | 
 |     } | 
 |   } | 
 |  | 
 |   // Main entry for checking a record declaration. | 
 |   void CheckRecord(RecordInfo* info) { | 
 |     if (IsIgnored(info)) | 
 |       return; | 
 |  | 
 |     CXXRecordDecl* record = info->record(); | 
 |  | 
 |     // TODO: what should we do to check unions? | 
 |     if (record->isUnion()) | 
 |       return; | 
 |  | 
 |     // If this is the primary template declaration, check its specializations. | 
 |     if (record->isThisDeclarationADefinition() && | 
 |         record->getDescribedClassTemplate()) { | 
 |       ClassTemplateDecl* tmpl = record->getDescribedClassTemplate(); | 
 |       for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); | 
 |            it != tmpl->spec_end(); | 
 |            ++it) { | 
 |         CheckClass(cache_.Lookup(*it)); | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     CheckClass(info); | 
 |   } | 
 |  | 
 |   // Check a class-like object (eg, class, specialization, instantiation). | 
 |   void CheckClass(RecordInfo* info) { | 
 |     if (!info) | 
 |       return; | 
 |  | 
 |     // Check consistency of stack-allocated hierarchies. | 
 |     if (info->IsStackAllocated()) { | 
 |       for (RecordInfo::Bases::iterator it = info->GetBases().begin(); | 
 |            it != info->GetBases().end(); | 
 |            ++it) { | 
 |         if (!it->second.info()->IsStackAllocated()) | 
 |           ReportDerivesNonStackAllocated(info, &it->second); | 
 |       } | 
 |     } | 
 |  | 
 |     if (CXXMethodDecl* trace = info->GetTraceMethod()) { | 
 |       if (trace->isPure()) | 
 |         ReportClassDeclaresPureVirtualTrace(info, trace); | 
 |     } else if (info->RequiresTraceMethod()) { | 
 |       ReportClassRequiresTraceMethod(info); | 
 |     } | 
 |  | 
 |     // Check polymorphic classes that are GC-derived or have a trace method. | 
 |     if (info->record()->hasDefinition() && info->record()->isPolymorphic()) { | 
 |       // TODO: Check classes that inherit a trace method. | 
 |       CXXMethodDecl* trace = info->GetTraceMethod(); | 
 |       if (trace || info->IsGCDerived()) | 
 |         CheckPolymorphicClass(info, trace); | 
 |     } | 
 |  | 
 |     { | 
 |       CheckFieldsVisitor visitor(options_); | 
 |       if (visitor.ContainsInvalidFields(info)) | 
 |         ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); | 
 |     } | 
 |  | 
 |     if (info->IsGCDerived()) { | 
 |  | 
 |       if (!info->IsGCMixin()) { | 
 |         CheckLeftMostDerived(info); | 
 |         CheckDispatch(info); | 
 |         if (CXXMethodDecl* newop = info->DeclaresNewOperator()) | 
 |           if (!Config::IsIgnoreAnnotated(newop)) | 
 |             ReportClassOverridesNew(info, newop); | 
 |         if (info->IsGCMixinInstance()) { | 
 |           // Require that declared GCMixin implementations | 
 |           // also provide a trace() override. | 
 |           if (info->DeclaresGCMixinMethods() | 
 |               && !info->DeclaresLocalTraceMethod()) | 
 |             ReportClassMustDeclareGCMixinTraceMethod(info); | 
 |         } | 
 |       } | 
 |  | 
 |       { | 
 |         CheckGCRootsVisitor visitor; | 
 |         if (visitor.ContainsGCRoots(info)) | 
 |           ReportClassContainsGCRoots(info, &visitor.gc_roots()); | 
 |       } | 
 |  | 
 |       if (info->NeedsFinalization()) | 
 |         CheckFinalization(info); | 
 |  | 
 |       if (options_.warn_unneeded_finalizer && info->IsGCFinalized()) | 
 |         CheckUnneededFinalization(info); | 
 |     } | 
 |  | 
 |     DumpClass(info); | 
 |   } | 
 |  | 
 |   CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) { | 
 |     const TemplateSpecializationType* tmpl_type = | 
 |         type.getAs<TemplateSpecializationType>(); | 
 |     if (!tmpl_type) | 
 |       return 0; | 
 |  | 
 |     TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); | 
 |     if (!tmpl_decl) | 
 |       return 0; | 
 |  | 
 |     return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); | 
 |   } | 
 |  | 
 |   // The GC infrastructure assumes that if the vtable of a polymorphic | 
 |   // base-class is not initialized for a given object (ie, it is partially | 
 |   // initialized) then the object does not need to be traced. Thus, we must | 
 |   // ensure that any polymorphic class with a trace method does not have any | 
 |   // tractable fields that are initialized before we are sure that the vtable | 
 |   // and the trace method are both defined.  There are two cases that need to | 
 |   // hold to satisfy that assumption: | 
 |   // | 
 |   // 1. If trace is virtual, then it must be defined in the left-most base. | 
 |   // This ensures that if the vtable is initialized then it contains a pointer | 
 |   // to the trace method. | 
 |   // | 
 |   // 2. If trace is non-virtual, then the trace method is defined and we must | 
 |   // ensure that the left-most base defines a vtable. This ensures that the | 
 |   // first thing to be initialized when constructing the object is the vtable | 
 |   // itself. | 
 |   void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { | 
 |     CXXRecordDecl* left_most = info->record(); | 
 |     CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 
 |     CXXRecordDecl* left_most_base = 0; | 
 |     while (it != left_most->bases_end()) { | 
 |       left_most_base = it->getType()->getAsCXXRecordDecl(); | 
 |       if (!left_most_base && it->getType()->isDependentType()) | 
 |         left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType()); | 
 |  | 
 |       // TODO: Find a way to correctly check actual instantiations | 
 |       // for dependent types. The escape below will be hit, eg, when | 
 |       // we have a primary template with no definition and | 
 |       // specializations for each case (such as SupplementBase) in | 
 |       // which case we don't succeed in checking the required | 
 |       // properties. | 
 |       if (!left_most_base || !left_most_base->hasDefinition()) | 
 |         return; | 
 |  | 
 |       StringRef name = left_most_base->getName(); | 
 |       // We know GCMixin base defines virtual trace. | 
 |       if (Config::IsGCMixinBase(name)) | 
 |         return; | 
 |  | 
 |       // Stop with the left-most prior to a safe polymorphic base (a safe base | 
 |       // is non-polymorphic and contains no fields). | 
 |       if (Config::IsSafePolymorphicBase(name)) | 
 |         break; | 
 |  | 
 |       left_most = left_most_base; | 
 |       it = left_most->bases_begin(); | 
 |     } | 
 |  | 
 |     if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { | 
 |  | 
 |       // Check condition (1): | 
 |       if (trace && trace->isVirtual()) { | 
 |         if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { | 
 |           if (trace->isVirtual()) | 
 |             return; | 
 |         } | 
 |         ReportBaseClassMustDeclareVirtualTrace(info, left_most); | 
 |         return; | 
 |       } | 
 |  | 
 |       // Check condition (2): | 
 |       if (DeclaresVirtualMethods(left_most)) | 
 |         return; | 
 |       if (left_most_base) { | 
 |         // Get the base next to the "safe polymorphic base" | 
 |         if (it != left_most->bases_end()) | 
 |           ++it; | 
 |         if (it != left_most->bases_end()) { | 
 |           if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { | 
 |             if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { | 
 |               if (DeclaresVirtualMethods(next_left_most)) | 
 |                 return; | 
 |               ReportLeftMostBaseMustBePolymorphic(info, next_left_most); | 
 |               return; | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 |       ReportLeftMostBaseMustBePolymorphic(info, left_most); | 
 |     } | 
 |   } | 
 |  | 
 |   CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { | 
 |     CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 
 |     while (it != left_most->bases_end()) { | 
 |       if (it->getType()->isDependentType()) | 
 |         left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType()); | 
 |       else | 
 |         left_most = it->getType()->getAsCXXRecordDecl(); | 
 |       if (!left_most || !left_most->hasDefinition()) | 
 |         return 0; | 
 |       it = left_most->bases_begin(); | 
 |     } | 
 |     return left_most; | 
 |   } | 
 |  | 
 |   bool DeclaresVirtualMethods(CXXRecordDecl* decl) { | 
 |     CXXRecordDecl::method_iterator it = decl->method_begin(); | 
 |     for (; it != decl->method_end(); ++it) | 
 |       if (it->isVirtual() && !it->isPure()) | 
 |         return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   void CheckLeftMostDerived(RecordInfo* info) { | 
 |     CXXRecordDecl* left_most = GetLeftMostBase(info->record()); | 
 |     if (!left_most) | 
 |       return; | 
 |     if (!Config::IsGCBase(left_most->getName())) | 
 |       ReportClassMustLeftMostlyDeriveGC(info); | 
 |   } | 
 |  | 
 |   void CheckDispatch(RecordInfo* info) { | 
 |     bool finalized = info->IsGCFinalized(); | 
 |     CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); | 
 |     CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); | 
 |     if (!trace_dispatch && !finalize_dispatch) | 
 |       return; | 
 |  | 
 |     CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent() | 
 |                                          : finalize_dispatch->getParent(); | 
 |  | 
 |     // Check that dispatch methods are defined at the base. | 
 |     if (base == info->record()) { | 
 |       if (!trace_dispatch) | 
 |         ReportMissingTraceDispatchMethod(info); | 
 |       if (finalized && !finalize_dispatch) | 
 |         ReportMissingFinalizeDispatchMethod(info); | 
 |       if (!finalized && finalize_dispatch) { | 
 |         ReportClassRequiresFinalization(info); | 
 |         NoteUserDeclaredFinalizer(finalize_dispatch); | 
 |       } | 
 |     } | 
 |  | 
 |     // Check that classes implementing manual dispatch do not have vtables. | 
 |     if (info->record()->isPolymorphic()) | 
 |       ReportVirtualAndManualDispatch( | 
 |           info, trace_dispatch ? trace_dispatch : finalize_dispatch); | 
 |  | 
 |     // If this is a non-abstract class check that it is dispatched to. | 
 |     // TODO: Create a global variant of this local check. We can only check if | 
 |     // the dispatch body is known in this compilation unit. | 
 |     if (info->IsConsideredAbstract()) | 
 |       return; | 
 |  | 
 |     const FunctionDecl* defn; | 
 |  | 
 |     if (trace_dispatch && trace_dispatch->isDefined(defn)) { | 
 |       CheckDispatchVisitor visitor(info); | 
 |       visitor.TraverseStmt(defn->getBody()); | 
 |       if (!visitor.dispatched_to_receiver()) | 
 |         ReportMissingTraceDispatch(defn, info); | 
 |     } | 
 |  | 
 |     if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) { | 
 |       CheckDispatchVisitor visitor(info); | 
 |       visitor.TraverseStmt(defn->getBody()); | 
 |       if (!visitor.dispatched_to_receiver()) | 
 |         ReportMissingFinalizeDispatch(defn, info); | 
 |     } | 
 |   } | 
 |  | 
 |   // TODO: Should we collect destructors similar to trace methods? | 
 |   void CheckFinalization(RecordInfo* info) { | 
 |     CXXDestructorDecl* dtor = info->record()->getDestructor(); | 
 |  | 
 |     // For finalized classes, check the finalization method if possible. | 
 |     if (info->IsGCFinalized()) { | 
 |       if (dtor && dtor->hasBody()) { | 
 |         CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized()); | 
 |         visitor.TraverseCXXMethodDecl(dtor); | 
 |         if (!visitor.finalized_fields().empty()) { | 
 |           ReportFinalizerAccessesFinalizedFields( | 
 |               dtor, &visitor.finalized_fields()); | 
 |         } | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     // Don't require finalization of a mixin that has not yet been "mixed in". | 
 |     if (info->IsGCMixin()) | 
 |       return; | 
 |  | 
 |     // Report the finalization error, and proceed to print possible causes for | 
 |     // the finalization requirement. | 
 |     ReportClassRequiresFinalization(info); | 
 |  | 
 |     if (dtor && dtor->isUserProvided()) | 
 |       NoteUserDeclaredDestructor(dtor); | 
 |  | 
 |     for (RecordInfo::Bases::iterator it = info->GetBases().begin(); | 
 |          it != info->GetBases().end(); | 
 |          ++it) { | 
 |       if (it->second.info()->NeedsFinalization()) | 
 |         NoteBaseRequiresFinalization(&it->second); | 
 |     } | 
 |  | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       if (it->second.edge()->NeedsFinalization()) | 
 |         NoteField(&it->second, diag_field_requires_finalization_note_); | 
 |     } | 
 |   } | 
 |  | 
 |   void CheckUnneededFinalization(RecordInfo* info) { | 
 |     if (!HasNonEmptyFinalizer(info)) | 
 |       ReportClassDoesNotRequireFinalization(info); | 
 |   } | 
 |  | 
 |   bool HasNonEmptyFinalizer(RecordInfo* info) { | 
 |     CXXDestructorDecl* dtor = info->record()->getDestructor(); | 
 |     if (dtor && dtor->isUserProvided()) { | 
 |       if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody())) | 
 |         return true; | 
 |     } | 
 |     for (RecordInfo::Bases::iterator it = info->GetBases().begin(); | 
 |          it != info->GetBases().end(); | 
 |          ++it) { | 
 |       if (HasNonEmptyFinalizer(it->second.info())) | 
 |         return true; | 
 |     } | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       if (it->second.edge()->NeedsFinalization()) | 
 |         return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   // This is the main entry for tracing method definitions. | 
 |   void CheckTracingMethod(CXXMethodDecl* method) { | 
 |     RecordInfo* parent = cache_.Lookup(method->getParent()); | 
 |     if (IsIgnored(parent)) | 
 |       return; | 
 |  | 
 |     // Check templated tracing methods by checking the template instantiations. | 
 |     // Specialized templates are handled as ordinary classes. | 
 |     if (ClassTemplateDecl* tmpl = | 
 |             parent->record()->getDescribedClassTemplate()) { | 
 |       for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); | 
 |            it != tmpl->spec_end(); | 
 |            ++it) { | 
 |         // Check trace using each template instantiation as the holder. | 
 |         if (IsTemplateInstantiation(*it)) | 
 |           CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     CheckTraceOrDispatchMethod(parent, method); | 
 |   } | 
 |  | 
 |   // Determine what type of tracing method this is (dispatch or trace). | 
 |   void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) { | 
 |     Config::TraceMethodType trace_type = Config::GetTraceMethodType(method); | 
 |     if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD || | 
 |         trace_type == Config::TRACE_AFTER_DISPATCH_IMPL_METHOD || | 
 |         !parent->GetTraceDispatchMethod()) { | 
 |       CheckTraceMethod(parent, method, trace_type); | 
 |     } | 
 |     // Dispatch methods are checked when we identify subclasses. | 
 |   } | 
 |  | 
 |   // Check an actual trace method. | 
 |   void CheckTraceMethod(RecordInfo* parent, | 
 |                         CXXMethodDecl* trace, | 
 |                         Config::TraceMethodType trace_type) { | 
 |     // A trace method must not override any non-virtual trace methods. | 
 |     if (trace_type == Config::TRACE_METHOD) { | 
 |       for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 
 |            it != parent->GetBases().end(); | 
 |            ++it) { | 
 |         RecordInfo* base = it->second.info(); | 
 |         if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 
 |           ReportOverriddenNonVirtualTrace(parent, trace, other); | 
 |       } | 
 |     } | 
 |  | 
 |     CheckTraceVisitor visitor(trace, parent, &cache_); | 
 |     visitor.TraverseCXXMethodDecl(trace); | 
 |  | 
 |     // Skip reporting if this trace method is a just delegate to | 
 |     // traceImpl (or traceAfterDispatchImpl) method. We will report on | 
 |     // CheckTraceMethod on traceImpl method. | 
 |     if (visitor.delegates_to_traceimpl()) | 
 |       return; | 
 |  | 
 |     for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 
 |          it != parent->GetBases().end(); | 
 |          ++it) { | 
 |       if (!it->second.IsProperlyTraced()) | 
 |         ReportBaseRequiresTracing(parent, trace, it->first); | 
 |     } | 
 |  | 
 |     for (RecordInfo::Fields::iterator it = parent->GetFields().begin(); | 
 |          it != parent->GetFields().end(); | 
 |          ++it) { | 
 |       if (!it->second.IsProperlyTraced()) { | 
 |         // Discontinue once an untraced-field error is found. | 
 |         ReportFieldsRequireTracing(parent, trace); | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void DumpClass(RecordInfo* info) { | 
 |     if (!json_) | 
 |       return; | 
 |  | 
 |     json_->OpenObject(); | 
 |     json_->Write("name", info->record()->getQualifiedNameAsString()); | 
 |     json_->Write("loc", GetLocString(info->record()->getLocStart())); | 
 |     json_->CloseObject(); | 
 |  | 
 |     class DumpEdgeVisitor : public RecursiveEdgeVisitor { | 
 |      public: | 
 |       DumpEdgeVisitor(JsonWriter* json) : json_(json) {} | 
 |       void DumpEdge(RecordInfo* src, | 
 |                     RecordInfo* dst, | 
 |                     const string& lbl, | 
 |                     const Edge::LivenessKind& kind, | 
 |                     const string& loc) { | 
 |         json_->OpenObject(); | 
 |         json_->Write("src", src->record()->getQualifiedNameAsString()); | 
 |         json_->Write("dst", dst->record()->getQualifiedNameAsString()); | 
 |         json_->Write("lbl", lbl); | 
 |         json_->Write("kind", kind); | 
 |         json_->Write("loc", loc); | 
 |         json_->Write("ptr", | 
 |                      !Parent() ? "val" : | 
 |                      Parent()->IsRawPtr() ? "raw" : | 
 |                      Parent()->IsRefPtr() ? "ref" : | 
 |                      Parent()->IsOwnPtr() ? "own" : | 
 |                      (Parent()->IsMember() || | 
 |                       Parent()->IsWeakMember()) ? "mem" : | 
 |                      "val"); | 
 |         json_->CloseObject(); | 
 |       } | 
 |  | 
 |       void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) { | 
 |         src_ = src; | 
 |         point_ = point; | 
 |         loc_ = loc; | 
 |         point_->edge()->Accept(this); | 
 |       } | 
 |  | 
 |       void AtValue(Value* e) override { | 
 |         // The liveness kind of a path from the point to this value | 
 |         // is given by the innermost place that is non-strong. | 
 |         Edge::LivenessKind kind = Edge::kStrong; | 
 |         if (Config::IsIgnoreCycleAnnotated(point_->field())) { | 
 |           kind = Edge::kWeak; | 
 |         } else { | 
 |           for (Context::iterator it = context().begin(); | 
 |                it != context().end(); | 
 |                ++it) { | 
 |             Edge::LivenessKind pointer_kind = (*it)->Kind(); | 
 |             if (pointer_kind != Edge::kStrong) { | 
 |               kind = pointer_kind; | 
 |               break; | 
 |             } | 
 |           } | 
 |         } | 
 |         DumpEdge( | 
 |             src_, e->value(), point_->field()->getNameAsString(), kind, loc_); | 
 |       } | 
 |  | 
 |      private: | 
 |       JsonWriter* json_; | 
 |       RecordInfo* src_; | 
 |       FieldPoint* point_; | 
 |       string loc_; | 
 |     }; | 
 |  | 
 |     DumpEdgeVisitor visitor(json_); | 
 |  | 
 |     RecordInfo::Bases& bases = info->GetBases(); | 
 |     for (RecordInfo::Bases::iterator it = bases.begin(); | 
 |          it != bases.end(); | 
 |          ++it) { | 
 |       visitor.DumpEdge(info, | 
 |                        it->second.info(), | 
 |                        "<super>", | 
 |                        Edge::kStrong, | 
 |                        GetLocString(it->second.spec().getLocStart())); | 
 |     } | 
 |  | 
 |     RecordInfo::Fields& fields = info->GetFields(); | 
 |     for (RecordInfo::Fields::iterator it = fields.begin(); | 
 |          it != fields.end(); | 
 |          ++it) { | 
 |       visitor.DumpField(info, | 
 |                         &it->second, | 
 |                         GetLocString(it->second.field()->getLocStart())); | 
 |     } | 
 |   } | 
 |  | 
 |   // Adds either a warning or error, based on the current handling of -Werror. | 
 |   DiagnosticsEngine::Level getErrorLevel() { | 
 |     return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error | 
 |                                              : DiagnosticsEngine::Warning; | 
 |   } | 
 |  | 
 |   const string GetLocString(SourceLocation loc) { | 
 |     const SourceManager& source_manager = instance_.getSourceManager(); | 
 |     PresumedLoc ploc = source_manager.getPresumedLoc(loc); | 
 |     if (ploc.isInvalid()) | 
 |       return ""; | 
 |     string loc_str; | 
 |     llvm::raw_string_ostream OS(loc_str); | 
 |     OS << ploc.getFilename() | 
 |        << ":" << ploc.getLine() | 
 |        << ":" << ploc.getColumn(); | 
 |     return OS.str(); | 
 |   } | 
 |  | 
 |   bool IsIgnored(RecordInfo* record) { | 
 |     return !record || | 
 |            !InCheckedNamespace(record) || | 
 |            IsIgnoredClass(record) || | 
 |            InIgnoredDirectory(record); | 
 |   } | 
 |  | 
 |   bool IsIgnoredClass(RecordInfo* info) { | 
 |     // Ignore any class prefixed by SameSizeAs. These are used in | 
 |     // Blink to verify class sizes and don't need checking. | 
 |     const string SameSizeAs = "SameSizeAs"; | 
 |     if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0) | 
 |       return true; | 
 |     return options_.ignored_classes.find(info->name()) != | 
 |            options_.ignored_classes.end(); | 
 |   } | 
 |  | 
 |   bool InIgnoredDirectory(RecordInfo* info) { | 
 |     string filename; | 
 |     if (!GetFilename(info->record()->getLocStart(), &filename)) | 
 |       return false;  // TODO: should we ignore non-existing file locations? | 
 | #if defined(LLVM_ON_WIN32) | 
 |     std::replace(filename.begin(), filename.end(), '\\', '/'); | 
 | #endif | 
 |     std::vector<string>::iterator it = options_.ignored_directories.begin(); | 
 |     for (; it != options_.ignored_directories.end(); ++it) | 
 |       if (filename.find(*it) != string::npos) | 
 |         return true; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool InCheckedNamespace(RecordInfo* info) { | 
 |     if (!info) | 
 |       return false; | 
 |     for (DeclContext* context = info->record()->getDeclContext(); | 
 |          !context->isTranslationUnit(); | 
 |          context = context->getParent()) { | 
 |       if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) { | 
 |         if (decl->isAnonymousNamespace()) | 
 |           return true; | 
 |         if (options_.checked_namespaces.find(decl->getNameAsString()) != | 
 |             options_.checked_namespaces.end()) { | 
 |           return true; | 
 |         } | 
 |       } | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool GetFilename(SourceLocation loc, string* filename) { | 
 |     const SourceManager& source_manager = instance_.getSourceManager(); | 
 |     SourceLocation spelling_location = source_manager.getSpellingLoc(loc); | 
 |     PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); | 
 |     if (ploc.isInvalid()) { | 
 |       // If we're in an invalid location, we're looking at things that aren't | 
 |       // actually stated in the source. | 
 |       return false; | 
 |     } | 
 |     *filename = ploc.getFilename(); | 
 |     return true; | 
 |   } | 
 |  | 
 |   void ReportClassMustLeftMostlyDeriveGC(RecordInfo* info) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_must_left_mostly_derive_gc_) | 
 |         << info->record(); | 
 |   } | 
 |  | 
 |   void ReportClassRequiresTraceMethod(RecordInfo* info) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_requires_trace_method_) | 
 |         << info->record(); | 
 |  | 
 |     for (RecordInfo::Bases::iterator it = info->GetBases().begin(); | 
 |          it != info->GetBases().end(); | 
 |          ++it) { | 
 |       if (it->second.NeedsTracing().IsNeeded()) | 
 |         NoteBaseRequiresTracing(&it->second); | 
 |     } | 
 |  | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       if (!it->second.IsProperlyTraced()) | 
 |         NoteFieldRequiresTracing(info, it->first); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReportBaseRequiresTracing(RecordInfo* derived, | 
 |                                  CXXMethodDecl* trace, | 
 |                                  CXXRecordDecl* base) { | 
 |     SourceLocation loc = trace->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_base_requires_tracing_) | 
 |         << base << derived->record(); | 
 |   } | 
 |  | 
 |   void ReportFieldsRequireTracing(RecordInfo* info, CXXMethodDecl* trace) { | 
 |     SourceLocation loc = trace->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_fields_require_tracing_) | 
 |         << info->record(); | 
 |     for (RecordInfo::Fields::iterator it = info->GetFields().begin(); | 
 |          it != info->GetFields().end(); | 
 |          ++it) { | 
 |       if (!it->second.IsProperlyTraced()) | 
 |         NoteFieldRequiresTracing(info, it->first); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReportClassContainsInvalidFields(RecordInfo* info, | 
 |                                         CheckFieldsVisitor::Errors* errors) { | 
 |     SourceLocation loc = info->record()->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     bool only_warnings = options_.warn_raw_ptr; | 
 |     for (CheckFieldsVisitor::Errors::iterator it = errors->begin(); | 
 |          only_warnings && it != errors->end(); | 
 |          ++it) { | 
 |       if (it->second != CheckFieldsVisitor::kRawPtrToGCManagedWarning) | 
 |         only_warnings = false; | 
 |     } | 
 |     diagnostic_.Report(full_loc, only_warnings ? | 
 |                        diag_class_contains_invalid_fields_warning_ : | 
 |                        diag_class_contains_invalid_fields_) | 
 |         << info->record(); | 
 |     for (CheckFieldsVisitor::Errors::iterator it = errors->begin(); | 
 |          it != errors->end(); | 
 |          ++it) { | 
 |       unsigned error; | 
 |       if (it->second == CheckFieldsVisitor::kRawPtrToGCManaged || | 
 |           it->second == CheckFieldsVisitor::kRawPtrToGCManagedWarning) { | 
 |         error = diag_raw_ptr_to_gc_managed_class_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kRefPtrToGCManaged) { | 
 |         error = diag_ref_ptr_to_gc_managed_class_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kOwnPtrToGCManaged) { | 
 |         error = diag_own_ptr_to_gc_managed_class_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kMemberToGCUnmanaged) { | 
 |         error = diag_member_to_gc_unmanaged_class_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kMemberInUnmanaged) { | 
 |         error = diag_member_in_unmanaged_class_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kPtrFromHeapToStack) { | 
 |         error = diag_stack_allocated_field_note_; | 
 |       } else if (it->second == CheckFieldsVisitor::kGCDerivedPartObject) { | 
 |         error = diag_part_object_to_gc_derived_class_note_; | 
 |       } else { | 
 |         assert(false && "Unknown field error"); | 
 |       } | 
 |       NoteField(it->first, error); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReportClassContainsGCRoots(RecordInfo* info, | 
 |                                   CheckGCRootsVisitor::Errors* errors) { | 
 |     SourceLocation loc = info->record()->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     for (CheckGCRootsVisitor::Errors::iterator it = errors->begin(); | 
 |          it != errors->end(); | 
 |          ++it) { | 
 |       CheckGCRootsVisitor::RootPath::iterator path = it->begin(); | 
 |       FieldPoint* point = *path; | 
 |       diagnostic_.Report(full_loc, diag_class_contains_gc_root_) | 
 |           << info->record() << point->field(); | 
 |       while (++path != it->end()) { | 
 |         NotePartObjectContainsGCRoot(point); | 
 |         point = *path; | 
 |       } | 
 |       NoteFieldContainsGCRoot(point); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReportFinalizerAccessesFinalizedFields( | 
 |       CXXMethodDecl* dtor, | 
 |       CheckFinalizerVisitor::Errors* fields) { | 
 |     for (CheckFinalizerVisitor::Errors::iterator it = fields->begin(); | 
 |          it != fields->end(); | 
 |          ++it) { | 
 |       SourceLocation loc = it->member_->getLocStart(); | 
 |       SourceManager& manager = instance_.getSourceManager(); | 
 |       bool as_eagerly_finalized = it->as_eagerly_finalized_; | 
 |       unsigned diag_error = as_eagerly_finalized ? | 
 |           diag_finalizer_eagerly_finalized_field_ : | 
 |           diag_finalizer_accesses_finalized_field_; | 
 |       unsigned diag_note = as_eagerly_finalized ? | 
 |           diag_eagerly_finalized_field_note_ : | 
 |           diag_finalized_field_note_; | 
 |       FullSourceLoc full_loc(loc, manager); | 
 |       diagnostic_.Report(full_loc, diag_error) | 
 |           << dtor << it->field_->field(); | 
 |       NoteField(it->field_, diag_note); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReportClassRequiresFinalization(RecordInfo* info) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_requires_finalization_) | 
 |         << info->record(); | 
 |   } | 
 |  | 
 |   void ReportClassDoesNotRequireFinalization(RecordInfo* info) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_does_not_require_finalization_) | 
 |         << info->record(); | 
 |   } | 
 |  | 
 |   void ReportClassMustDeclareGCMixinTraceMethod(RecordInfo* info) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report( | 
 |         full_loc, diag_class_must_declare_gc_mixin_trace_method_) | 
 |         << info->record(); | 
 |   } | 
 |  | 
 |   void ReportOverriddenNonVirtualTrace(RecordInfo* info, | 
 |                                        CXXMethodDecl* trace, | 
 |                                        CXXMethodDecl* overridden) { | 
 |     SourceLocation loc = trace->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_) | 
 |         << info->record() << overridden->getParent(); | 
 |     NoteOverriddenNonVirtualTrace(overridden); | 
 |   } | 
 |  | 
 |   void ReportMissingTraceDispatchMethod(RecordInfo* info) { | 
 |     ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_); | 
 |   } | 
 |  | 
 |   void ReportMissingFinalizeDispatchMethod(RecordInfo* info) { | 
 |     ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_); | 
 |   } | 
 |  | 
 |   void ReportMissingDispatchMethod(RecordInfo* info, unsigned error) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, error) << info->record(); | 
 |   } | 
 |  | 
 |   void ReportVirtualAndManualDispatch(RecordInfo* info, | 
 |                                       CXXMethodDecl* dispatch) { | 
 |     SourceLocation loc = info->record()->getInnerLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_) | 
 |         << info->record(); | 
 |     NoteManualDispatchMethod(dispatch); | 
 |   } | 
 |  | 
 |   void ReportMissingTraceDispatch(const FunctionDecl* dispatch, | 
 |                                   RecordInfo* receiver) { | 
 |     ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); | 
 |   } | 
 |  | 
 |   void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch, | 
 |                                      RecordInfo* receiver) { | 
 |     ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); | 
 |   } | 
 |  | 
 |   void ReportMissingDispatch(const FunctionDecl* dispatch, | 
 |                              RecordInfo* receiver, | 
 |                              unsigned error) { | 
 |     SourceLocation loc = dispatch->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, error) << receiver->record(); | 
 |   } | 
 |  | 
 |   void ReportDerivesNonStackAllocated(RecordInfo* info, BasePoint* base) { | 
 |     SourceLocation loc = base->spec().getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_derives_non_stack_allocated_) | 
 |         << info->record() << base->info()->record(); | 
 |   } | 
 |  | 
 |   void ReportClassOverridesNew(RecordInfo* info, CXXMethodDecl* newop) { | 
 |     SourceLocation loc = newop->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_overrides_new_) << info->record(); | 
 |   } | 
 |  | 
 |   void ReportClassDeclaresPureVirtualTrace(RecordInfo* info, | 
 |                                            CXXMethodDecl* trace) { | 
 |     SourceLocation loc = trace->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) | 
 |         << info->record(); | 
 |   } | 
 |  | 
 |   void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived, | 
 |                                            CXXRecordDecl* base) { | 
 |     SourceLocation loc = base->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_) | 
 |         << base << derived->record(); | 
 |   } | 
 |  | 
 |   void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived, | 
 |                                               CXXRecordDecl* base) { | 
 |     SourceLocation loc = base->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_) | 
 |         << base << derived->record(); | 
 |   } | 
 |  | 
 |   void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { | 
 |     SourceLocation loc = dispatch->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; | 
 |   } | 
 |  | 
 |   void NoteBaseRequiresTracing(BasePoint* base) { | 
 |     SourceLocation loc = base->spec().getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_base_requires_tracing_note_) | 
 |         << base->info()->record(); | 
 |   } | 
 |  | 
 |   void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) { | 
 |     NoteField(field, diag_field_requires_tracing_note_); | 
 |   } | 
 |  | 
 |   void NotePartObjectContainsGCRoot(FieldPoint* point) { | 
 |     FieldDecl* field = point->field(); | 
 |     SourceLocation loc = field->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_) | 
 |         << field << field->getParent(); | 
 |   } | 
 |  | 
 |   void NoteFieldContainsGCRoot(FieldPoint* point) { | 
 |     NoteField(point, diag_field_contains_gc_root_note_); | 
 |   } | 
 |  | 
 |   void NoteUserDeclaredDestructor(CXXMethodDecl* dtor) { | 
 |     SourceLocation loc = dtor->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_user_declared_destructor_note_); | 
 |   } | 
 |  | 
 |   void NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) { | 
 |     SourceLocation loc = dtor->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_user_declared_finalizer_note_); | 
 |   } | 
 |  | 
 |   void NoteBaseRequiresFinalization(BasePoint* base) { | 
 |     SourceLocation loc = base->spec().getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_base_requires_finalization_note_) | 
 |         << base->info()->record(); | 
 |   } | 
 |  | 
 |   void NoteField(FieldPoint* point, unsigned note) { | 
 |     NoteField(point->field(), note); | 
 |   } | 
 |  | 
 |   void NoteField(FieldDecl* field, unsigned note) { | 
 |     SourceLocation loc = field->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, note) << field; | 
 |   } | 
 |  | 
 |   void NoteOverriddenNonVirtualTrace(CXXMethodDecl* overridden) { | 
 |     SourceLocation loc = overridden->getLocStart(); | 
 |     SourceManager& manager = instance_.getSourceManager(); | 
 |     FullSourceLoc full_loc(loc, manager); | 
 |     diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_) | 
 |         << overridden; | 
 |   } | 
 |  | 
 |   unsigned diag_class_must_left_mostly_derive_gc_; | 
 |   unsigned diag_class_requires_trace_method_; | 
 |   unsigned diag_base_requires_tracing_; | 
 |   unsigned diag_fields_require_tracing_; | 
 |   unsigned diag_class_contains_invalid_fields_; | 
 |   unsigned diag_class_contains_invalid_fields_warning_; | 
 |   unsigned diag_class_contains_gc_root_; | 
 |   unsigned diag_class_requires_finalization_; | 
 |   unsigned diag_class_does_not_require_finalization_; | 
 |   unsigned diag_finalizer_accesses_finalized_field_; | 
 |   unsigned diag_finalizer_eagerly_finalized_field_; | 
 |   unsigned diag_overridden_non_virtual_trace_; | 
 |   unsigned diag_missing_trace_dispatch_method_; | 
 |   unsigned diag_missing_finalize_dispatch_method_; | 
 |   unsigned diag_virtual_and_manual_dispatch_; | 
 |   unsigned diag_missing_trace_dispatch_; | 
 |   unsigned diag_missing_finalize_dispatch_; | 
 |   unsigned diag_derives_non_stack_allocated_; | 
 |   unsigned diag_class_overrides_new_; | 
 |   unsigned diag_class_declares_pure_virtual_trace_; | 
 |   unsigned diag_left_most_base_must_be_polymorphic_; | 
 |   unsigned diag_base_class_must_declare_virtual_trace_; | 
 |   unsigned diag_class_must_declare_gc_mixin_trace_method_; | 
 |  | 
 |   unsigned diag_base_requires_tracing_note_; | 
 |   unsigned diag_field_requires_tracing_note_; | 
 |   unsigned diag_raw_ptr_to_gc_managed_class_note_; | 
 |   unsigned diag_ref_ptr_to_gc_managed_class_note_; | 
 |   unsigned diag_own_ptr_to_gc_managed_class_note_; | 
 |   unsigned diag_member_to_gc_unmanaged_class_note_; | 
 |   unsigned diag_stack_allocated_field_note_; | 
 |   unsigned diag_member_in_unmanaged_class_note_; | 
 |   unsigned diag_part_object_to_gc_derived_class_note_; | 
 |   unsigned diag_part_object_contains_gc_root_note_; | 
 |   unsigned diag_field_contains_gc_root_note_; | 
 |   unsigned diag_finalized_field_note_; | 
 |   unsigned diag_eagerly_finalized_field_note_; | 
 |   unsigned diag_user_declared_destructor_note_; | 
 |   unsigned diag_user_declared_finalizer_note_; | 
 |   unsigned diag_base_requires_finalization_note_; | 
 |   unsigned diag_field_requires_finalization_note_; | 
 |   unsigned diag_overridden_non_virtual_trace_note_; | 
 |   unsigned diag_manual_dispatch_method_note_; | 
 |  | 
 |   CompilerInstance& instance_; | 
 |   DiagnosticsEngine& diagnostic_; | 
 |   BlinkGCPluginOptions options_; | 
 |   RecordCache cache_; | 
 |   JsonWriter* json_; | 
 | }; | 
 |  | 
 | class BlinkGCPluginAction : public PluginASTAction { | 
 |  public: | 
 |   BlinkGCPluginAction() {} | 
 |  | 
 |  protected: | 
 |   // Overridden from PluginASTAction: | 
 |   virtual std::unique_ptr<ASTConsumer> CreateASTConsumer( | 
 |       CompilerInstance& instance, | 
 |       llvm::StringRef ref) { | 
 |     return llvm::make_unique<BlinkGCPluginConsumer>(instance, options_); | 
 |   } | 
 |  | 
 |   virtual bool ParseArgs(const CompilerInstance& instance, | 
 |                          const std::vector<string>& args) { | 
 |     bool parsed = true; | 
 |  | 
 |     for (size_t i = 0; i < args.size() && parsed; ++i) { | 
 |       if (args[i] == "enable-oilpan") { | 
 |         options_.enable_oilpan = true; | 
 |       } else if (args[i] == "dump-graph") { | 
 |         options_.dump_graph = true; | 
 |       } else if (args[i] == "warn-raw-ptr") { | 
 |         options_.warn_raw_ptr = true; | 
 |       } else if (args[i] == "warn-unneeded-finalizer") { | 
 |         options_.warn_unneeded_finalizer = true; | 
 |       } else { | 
 |         parsed = false; | 
 |         llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n"; | 
 |       } | 
 |     } | 
 |  | 
 |     return parsed; | 
 |   } | 
 |  | 
 |  private: | 
 |   BlinkGCPluginOptions options_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 
 |     "blink-gc-plugin", | 
 |     "Check Blink GC invariants"); |