Index: tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp |
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp b/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c7f64200150c58303b54a9fc129c51f0eccaede0 |
--- /dev/null |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp |
@@ -0,0 +1,1328 @@ |
+// Copyright 2015 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. |
+ |
+#include "BlinkGCPluginConsumer.h" |
+ |
+#include <algorithm> |
+#include <set> |
+ |
+#include "CheckDispatchVisitor.h" |
+#include "CheckTraceVisitor.h" |
+#include "CollectVisitor.h" |
+#include "JsonWriter.h" |
+#include "RecordInfo.h" |
+#include "clang/AST/RecursiveASTVisitor.h" |
+#include "clang/Sema/Sema.h" |
+ |
+using namespace clang; |
+ |
+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 kReferencePtrToGCManagedClassNote[] = |
+ "[blink-gc] Reference pointer 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; |
+} |
+ |
+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_; |
+}; |
+ |
+} // namespace |
+ |
+BlinkGCPluginConsumer::BlinkGCPluginConsumer( |
+ clang::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_reference_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
+ DiagnosticsEngine::Note, kReferencePtrToGCManagedClassNote); |
+ 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 BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) { |
+ // 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 (CollectVisitor::RecordVector::iterator it = |
+ visitor.record_decls().begin(); |
+ it != visitor.record_decls().end(); |
+ ++it) { |
+ CheckRecord(cache_.Lookup(*it)); |
+ } |
+ |
+ for (CollectVisitor::MethodVector::iterator it = |
+ visitor.trace_decls().begin(); |
+ it != visitor.trace_decls().end(); |
+ ++it) { |
+ CheckTracingMethod(*it); |
+ } |
+ |
+ if (json_) { |
+ json_->CloseList(); |
+ delete json_; |
+ json_ = 0; |
+ } |
+} |
+ |
+void BlinkGCPluginConsumer::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); |
+ } |
+} |
+ |
+void BlinkGCPluginConsumer::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); |
+} |
+ |
+void BlinkGCPluginConsumer::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* BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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* BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) { |
+ CXXRecordDecl* left_most = GetLeftMostBase(info->record()); |
+ if (!left_most) |
+ return; |
+ if (!Config::IsGCBase(left_most->getName())) |
+ ReportClassMustLeftMostlyDeriveGC(info); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::CheckUnneededFinalization(RecordInfo* info) { |
+ if (!HasNonEmptyFinalizer(info)) |
+ ReportClassDoesNotRequireFinalization(info); |
+} |
+ |
+bool BlinkGCPluginConsumer::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; |
+} |
+ |
+void BlinkGCPluginConsumer::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 (Config::IsTemplateInstantiation(*it)) |
+ CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); |
+ } |
+ return; |
+ } |
+ |
+ CheckTraceOrDispatchMethod(parent, method); |
+} |
+ |
+void BlinkGCPluginConsumer::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. |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 std::string& lbl, |
+ const Edge::LivenessKind& kind, |
+ const std::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() ? |
+ (static_cast<RawPtr*>(Parent())->HasReferenceType() ? |
+ "reference" : "raw") : |
+ Parent()->IsRefPtr() ? "ref" : |
+ Parent()->IsOwnPtr() ? "own" : |
+ (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" : |
+ "val"); |
+ json_->CloseObject(); |
+ } |
+ |
+ void DumpField(RecordInfo* src, FieldPoint* point, const std::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_; |
+ std::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())); |
+ } |
+} |
+ |
+DiagnosticsEngine::Level BlinkGCPluginConsumer::getErrorLevel() { |
+ return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error |
+ : DiagnosticsEngine::Warning; |
+} |
+ |
+std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) { |
+ const SourceManager& source_manager = instance_.getSourceManager(); |
+ PresumedLoc ploc = source_manager.getPresumedLoc(loc); |
+ if (ploc.isInvalid()) |
+ return ""; |
+ std::string loc_str; |
+ llvm::raw_string_ostream os(loc_str); |
+ os << ploc.getFilename() |
+ << ":" << ploc.getLine() |
+ << ":" << ploc.getColumn(); |
+ return os.str(); |
+} |
+ |
+bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) { |
+ return (!record || |
+ !InCheckedNamespace(record) || |
+ IsIgnoredClass(record) || |
+ InIgnoredDirectory(record)); |
+} |
+ |
+bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) { |
+ // Ignore any class prefixed by SameSizeAs. These are used in |
+ // Blink to verify class sizes and don't need checking. |
+ const std::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 BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) { |
+ std::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<std::string>::iterator it = options_.ignored_directories.begin(); |
+ for (; it != options_.ignored_directories.end(); ++it) |
+ if (filename.find(*it) != std::string::npos) |
+ return true; |
+ return false; |
+} |
+ |
+bool BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::GetFilename(SourceLocation loc, |
+ std::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 (!CheckFieldsVisitor::IsWarning(it->second)) |
+ 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 (CheckFieldsVisitor::IsRawPtrError(it->second)) { |
+ error = diag_raw_ptr_to_gc_managed_class_note_; |
+ } else if (CheckFieldsVisitor::IsReferencePtrError(it->second)) { |
+ error = diag_reference_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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::ReportMissingTraceDispatchMethod(RecordInfo* info) { |
+ ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_); |
+} |
+ |
+void BlinkGCPluginConsumer::ReportMissingFinalizeDispatchMethod( |
+ RecordInfo* info) { |
+ ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::ReportMissingTraceDispatch( |
+ const FunctionDecl* dispatch, |
+ RecordInfo* receiver) { |
+ ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); |
+} |
+ |
+void BlinkGCPluginConsumer::ReportMissingFinalizeDispatch( |
+ const FunctionDecl* dispatch, |
+ RecordInfo* receiver) { |
+ ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::NoteFieldRequiresTracing( |
+ RecordInfo* holder, |
+ FieldDecl* field) { |
+ NoteField(field, diag_field_requires_tracing_note_); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::NoteFieldContainsGCRoot(FieldPoint* point) { |
+ NoteField(point, diag_field_contains_gc_root_note_); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::NoteField(FieldPoint* point, unsigned note) { |
+ NoteField(point->field(), note); |
+} |
+ |
+void BlinkGCPluginConsumer::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 BlinkGCPluginConsumer::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; |
+} |