| 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..441138a724b25ef6ba0070d8a4d84efc58aaa288
|
| --- /dev/null
|
| +++ b/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp
|
| @@ -0,0 +1,1275 @@
|
| +// 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;
|
| +}
|
| +
|
| +DiagnosticBuilder BlinkGCPluginConsumer::ReportDiagnostic(
|
| + SourceLocation location,
|
| + unsigned diag_id) {
|
| + SourceManager& manager = instance_.getSourceManager();
|
| + FullSourceLoc full_loc(location, manager);
|
| + return diagnostic_.Report(full_loc, diag_id);
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassMustLeftMostlyDeriveGC(
|
| + RecordInfo* info) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + diag_class_must_left_mostly_derive_gc_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassRequiresTraceMethod(RecordInfo* info) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + 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) {
|
| + ReportDiagnostic(trace->getLocStart(), diag_base_requires_tracing_)
|
| + << base << derived->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportFieldsRequireTracing(
|
| + RecordInfo* info,
|
| + CXXMethodDecl* trace) {
|
| + ReportDiagnostic(trace->getLocStart(), 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) {
|
| + 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;
|
| + }
|
| + ReportDiagnostic(info->record()->getLocStart(),
|
| + 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) {
|
| + for (CheckGCRootsVisitor::Errors::iterator it = errors->begin();
|
| + it != errors->end();
|
| + ++it) {
|
| + CheckGCRootsVisitor::RootPath::iterator path = it->begin();
|
| + FieldPoint* point = *path;
|
| + ReportDiagnostic(info->record()->getLocStart(),
|
| + 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) {
|
| + 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_;
|
| + ReportDiagnostic(it->member->getLocStart(), diag_error)
|
| + << dtor << it->field->field();
|
| + NoteField(it->field, diag_note);
|
| + }
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassRequiresFinalization(RecordInfo* info) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + diag_class_requires_finalization_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassDoesNotRequireFinalization(
|
| + RecordInfo* info) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + diag_class_does_not_require_finalization_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassMustDeclareGCMixinTraceMethod(
|
| + RecordInfo* info) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + diag_class_must_declare_gc_mixin_trace_method_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportOverriddenNonVirtualTrace(
|
| + RecordInfo* info,
|
| + CXXMethodDecl* trace,
|
| + CXXMethodDecl* overridden) {
|
| + ReportDiagnostic(trace->getLocStart(), 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) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(), error)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportVirtualAndManualDispatch(
|
| + RecordInfo* info,
|
| + CXXMethodDecl* dispatch) {
|
| + ReportDiagnostic(info->record()->getInnerLocStart(),
|
| + 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) {
|
| + ReportDiagnostic(dispatch->getLocStart(), error) << receiver->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportDerivesNonStackAllocated(
|
| + RecordInfo* info,
|
| + BasePoint* base) {
|
| + ReportDiagnostic(base->spec().getLocStart(),
|
| + diag_derives_non_stack_allocated_)
|
| + << info->record() << base->info()->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassOverridesNew(
|
| + RecordInfo* info,
|
| + CXXMethodDecl* newop) {
|
| + ReportDiagnostic(newop->getLocStart(), diag_class_overrides_new_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportClassDeclaresPureVirtualTrace(
|
| + RecordInfo* info,
|
| + CXXMethodDecl* trace) {
|
| + ReportDiagnostic(trace->getLocStart(),
|
| + diag_class_declares_pure_virtual_trace_)
|
| + << info->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportLeftMostBaseMustBePolymorphic(
|
| + RecordInfo* derived,
|
| + CXXRecordDecl* base) {
|
| + ReportDiagnostic(base->getLocStart(),
|
| + diag_left_most_base_must_be_polymorphic_)
|
| + << base << derived->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::ReportBaseClassMustDeclareVirtualTrace(
|
| + RecordInfo* derived,
|
| + CXXRecordDecl* base) {
|
| + ReportDiagnostic(base->getLocStart(),
|
| + diag_base_class_must_declare_virtual_trace_)
|
| + << base << derived->record();
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
|
| + ReportDiagnostic(dispatch->getLocStart(),
|
| + diag_manual_dispatch_method_note_)
|
| + << dispatch;
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::NoteBaseRequiresTracing(BasePoint* base) {
|
| + ReportDiagnostic(base->spec().getLocStart(),
|
| + 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();
|
| + ReportDiagnostic(field->getLocStart(),
|
| + 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) {
|
| + ReportDiagnostic(dtor->getLocStart(), diag_user_declared_destructor_note_);
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) {
|
| + ReportDiagnostic(dtor->getLocStart(), diag_user_declared_finalizer_note_);
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::NoteBaseRequiresFinalization(BasePoint* base) {
|
| + ReportDiagnostic(base->spec().getLocStart(),
|
| + 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) {
|
| + ReportDiagnostic(field->getLocStart(), note) << field;
|
| +}
|
| +
|
| +void BlinkGCPluginConsumer::NoteOverriddenNonVirtualTrace(
|
| + CXXMethodDecl* overridden) {
|
| + ReportDiagnostic(overridden->getLocStart(),
|
| + diag_overridden_non_virtual_trace_note_)
|
| + << overridden;
|
| +}
|
|
|