Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Unified Diff: tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp

Issue 1385193002: Bisect clang Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: 246985 Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+}
« no previous file with comments | « tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.h ('k') | tools/clang/blink_gc_plugin/BlinkGCPluginOptions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698