// Copyright 2016 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 "DiagnosticsReporter.h"

using namespace clang;

namespace {

const char kClassMustLeftMostlyDeriveGC[] =
    "[blink-gc] Class %0 must derive from GarbageCollected 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 kFieldsImproperlyTraced[] =
    "[blink-gc] Class %0 has untraced or not traceable fields.";

const char kFieldRequiresTracingNote[] =
    "[blink-gc] Untraced field %0 declared here:";

const char kFieldShouldNotBeTracedNote[] =
    "[blink-gc] Untraceable 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 kUniquePtrToGCManagedClassNote[] =
    "[blink-gc] std::unique_ptr 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 kStackAllocatedDerivesGarbageCollected[] =
    "[blink-gc] Stack-allocated class %0 derives class %1"
    " which is garbage collected.";

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 kIteratorToGCManagedCollectionNote[] =
    "[blink-gc] Iterator field %0 to a GC managed collection declared here:";

const char kTraceMethodOfStackAllocatedParentNote[] =
    "[blink-gc] The stack allocated class %0 provides an unnecessary "
    "trace method:";

const char kUniquePtrUsedWithGC[] =
    "[blink-gc] Disallowed use of %0 found; %1 is a garbage-collected type. "
    "std::unique_ptr cannot hold garbage-collected objects.";

const char kOptionalUsedWithGC[] =
    "[blink-gc] Disallowed construction of %0 found; %1 is a garbage-collected "
    "type. optional cannot hold garbage-collected objects.";

const char kMissingMixinMarker[] =
    "[blink-gc] Garbage-collected class %0 derives mixin class %1. "
    "You must add USING_GARBAGE_COLLECTED_MIXIN(%2).";

const char kMissingMixinMarkerNote[] =
    "[blink-gc] Mixin base class derived here:";

} // namespace

DiagnosticBuilder DiagnosticsReporter::ReportDiagnostic(
    SourceLocation location,
    unsigned diag_id) {
  SourceManager& manager = instance_.getSourceManager();
  FullSourceLoc full_loc(location, manager);
  return diagnostic_.Report(full_loc, diag_id);
}

DiagnosticsReporter::DiagnosticsReporter(
    clang::CompilerInstance& instance)
    : instance_(instance),
      diagnostic_(instance.getDiagnostics())
{
  // 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_fields_improperly_traced_ =
      diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsImproperlyTraced);
  diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID(
      getErrorLevel(), 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_stack_allocated_derives_gc_ = diagnostic_.getCustomDiagID(
      getErrorLevel(), kStackAllocatedDerivesGarbageCollected);
  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_iterator_to_gc_managed_collection_note_ = diagnostic_.getCustomDiagID(
      getErrorLevel(), kIteratorToGCManagedCollectionNote);
  diag_trace_method_of_stack_allocated_parent_ = diagnostic_.getCustomDiagID(
      getErrorLevel(), kTraceMethodOfStackAllocatedParentNote);

  // Register note messages.
  diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID(
      DiagnosticsEngine::Note, kBaseRequiresTracingNote);
  diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
      DiagnosticsEngine::Note, kFieldRequiresTracingNote);
  diag_field_should_not_be_traced_note_ = diagnostic_.getCustomDiagID(
      DiagnosticsEngine::Note, kFieldShouldNotBeTracedNote);
  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_unique_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
      DiagnosticsEngine::Note, kUniquePtrToGCManagedClassNote);
  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);

  diag_unique_ptr_used_with_gc_ =
      diagnostic_.getCustomDiagID(getErrorLevel(), kUniquePtrUsedWithGC);
  diag_optional_used_with_gc_ =
      diagnostic_.getCustomDiagID(getErrorLevel(), kOptionalUsedWithGC);
  diag_missing_mixin_marker_ =
      diagnostic_.getCustomDiagID(getErrorLevel(), kMissingMixinMarker);
  diag_missing_mixin_marker_note_ = diagnostic_.getCustomDiagID(
      DiagnosticsEngine::Note, kMissingMixinMarkerNote);
}

bool DiagnosticsReporter::hasErrorOccurred() const
{
  return diagnostic_.hasErrorOccurred();
}

DiagnosticsEngine::Level DiagnosticsReporter::getErrorLevel() const {
  return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error
                                           : DiagnosticsEngine::Warning;
}

void DiagnosticsReporter::ClassMustLeftMostlyDeriveGC(
    RecordInfo* info) {
  ReportDiagnostic(info->record()->getInnerLocStart(),
                   diag_class_must_left_mostly_derive_gc_)
      << info->record();
}

void DiagnosticsReporter::ClassRequiresTraceMethod(RecordInfo* info) {
  ReportDiagnostic(info->record()->getInnerLocStart(),
                   diag_class_requires_trace_method_)
      << info->record();

  for (auto& base : info->GetBases())
    if (base.second.NeedsTracing().IsNeeded())
      NoteBaseRequiresTracing(&base.second);

  for (auto& field : info->GetFields())
    if (!field.second.IsProperlyTraced())
      NoteFieldRequiresTracing(info, field.first);
}

void DiagnosticsReporter::BaseRequiresTracing(
    RecordInfo* derived,
    CXXMethodDecl* trace,
    CXXRecordDecl* base) {
  ReportDiagnostic(trace->getBeginLoc(), diag_base_requires_tracing_)
      << base << derived->record();
}

void DiagnosticsReporter::FieldsImproperlyTraced(
    RecordInfo* info,
    CXXMethodDecl* trace) {
  // Only mention untraceable in header diagnostic if they appear.
  unsigned diag = diag_fields_require_tracing_;
  for (auto& field : info->GetFields()) {
    if (field.second.IsInproperlyTraced()) {
      diag = diag_fields_improperly_traced_;
      break;
    }
  }
  ReportDiagnostic(trace->getBeginLoc(), diag) << info->record();
  for (auto& field : info->GetFields()) {
    if (!field.second.IsProperlyTraced())
      NoteFieldRequiresTracing(info, field.first);
    if (field.second.IsInproperlyTraced())
      NoteFieldShouldNotBeTraced(info, field.first);
  }
}

void DiagnosticsReporter::ClassContainsInvalidFields(
    RecordInfo* info,
    const CheckFieldsVisitor::Errors& errors) {
  ReportDiagnostic(info->record()->getBeginLoc(),
                   diag_class_contains_invalid_fields_)
      << info->record();

  for (auto& error : errors) {
    unsigned note;
    if (error.second == CheckFieldsVisitor::kRawPtrToGCManaged) {
      note = diag_raw_ptr_to_gc_managed_class_note_;
    } else if (error.second == CheckFieldsVisitor::kRefPtrToGCManaged) {
      note = diag_ref_ptr_to_gc_managed_class_note_;
    } else if (error.second == CheckFieldsVisitor::kReferencePtrToGCManaged) {
      note = diag_reference_ptr_to_gc_managed_class_note_;
    } else if (error.second == CheckFieldsVisitor::kUniquePtrToGCManaged) {
      note = diag_unique_ptr_to_gc_managed_class_note_;
    } else if (error.second == CheckFieldsVisitor::kMemberToGCUnmanaged) {
      note = diag_member_to_gc_unmanaged_class_note_;
    } else if (error.second == CheckFieldsVisitor::kMemberInUnmanaged) {
      note = diag_member_in_unmanaged_class_note_;
    } else if (error.second == CheckFieldsVisitor::kPtrFromHeapToStack) {
      note = diag_stack_allocated_field_note_;
    } else if (error.second == CheckFieldsVisitor::kGCDerivedPartObject) {
      note = diag_part_object_to_gc_derived_class_note_;
    } else if (error.second == CheckFieldsVisitor::kIteratorToGCManaged) {
      note = diag_iterator_to_gc_managed_collection_note_;
    } else {
      assert(false && "Unknown field error");
    }
    NoteField(error.first, note);
  }
}

void DiagnosticsReporter::ClassContainsGCRoots(
    RecordInfo* info,
    const CheckGCRootsVisitor::Errors& errors) {
  for (auto& error : errors) {
    FieldPoint* point = nullptr;
    for (FieldPoint* path : error) {
      if (!point) {
        point = path;
        ReportDiagnostic(info->record()->getBeginLoc(),
                         diag_class_contains_gc_root_)
            << info->record() << point->field();
        continue;
      }
      NotePartObjectContainsGCRoot(point);
      point = path;
    }
    NoteFieldContainsGCRoot(point);
  }
}

void DiagnosticsReporter::FinalizerAccessesFinalizedFields(
    CXXMethodDecl* dtor,
    const CheckFinalizerVisitor::Errors& errors) {
  for (auto& error : errors) {
    bool as_eagerly_finalized = error.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(error.member->getBeginLoc(), diag_error)
        << dtor << error.field->field();
    NoteField(error.field, diag_note);
  }
}

void DiagnosticsReporter::ClassRequiresFinalization(RecordInfo* info) {
  ReportDiagnostic(info->record()->getInnerLocStart(),
                   diag_class_requires_finalization_)
      << info->record();
}

void DiagnosticsReporter::ClassDoesNotRequireFinalization(
    RecordInfo* info) {
  ReportDiagnostic(info->record()->getInnerLocStart(),
                   diag_class_does_not_require_finalization_)
      << info->record();
}

void DiagnosticsReporter::OverriddenNonVirtualTrace(
    RecordInfo* info,
    CXXMethodDecl* trace,
    CXXMethodDecl* overridden) {
  ReportDiagnostic(trace->getBeginLoc(), diag_overridden_non_virtual_trace_)
      << info->record() << overridden->getParent();
  NoteOverriddenNonVirtualTrace(overridden);
}

void DiagnosticsReporter::MissingTraceDispatchMethod(RecordInfo* info) {
  ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_);
}

void DiagnosticsReporter::MissingFinalizeDispatchMethod(
    RecordInfo* info) {
  ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_);
}

void DiagnosticsReporter::ReportMissingDispatchMethod(
    RecordInfo* info,
    unsigned error) {
  ReportDiagnostic(info->record()->getInnerLocStart(), error)
      << info->record();
}

void DiagnosticsReporter::VirtualAndManualDispatch(
    RecordInfo* info,
    CXXMethodDecl* dispatch) {
  ReportDiagnostic(info->record()->getInnerLocStart(),
                   diag_virtual_and_manual_dispatch_)
      << info->record();
  NoteManualDispatchMethod(dispatch);
}

void DiagnosticsReporter::MissingTraceDispatch(
    const FunctionDecl* dispatch,
    RecordInfo* receiver) {
  ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_);
}

void DiagnosticsReporter::MissingFinalizeDispatch(
    const FunctionDecl* dispatch,
    RecordInfo* receiver) {
  ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_);
}

void DiagnosticsReporter::ReportMissingDispatch(
    const FunctionDecl* dispatch,
    RecordInfo* receiver,
    unsigned error) {
  ReportDiagnostic(dispatch->getBeginLoc(), error) << receiver->record();
}

void DiagnosticsReporter::StackAllocatedDerivesGarbageCollected(
    RecordInfo* info,
    BasePoint* base) {
  ReportDiagnostic(base->spec().getBeginLoc(), diag_stack_allocated_derives_gc_)
      << info->record() << base->info()->record();
}

void DiagnosticsReporter::ClassOverridesNew(
    RecordInfo* info,
    CXXMethodDecl* newop) {
  ReportDiagnostic(newop->getBeginLoc(), diag_class_overrides_new_)
      << info->record();
}

void DiagnosticsReporter::ClassDeclaresPureVirtualTrace(
    RecordInfo* info,
    CXXMethodDecl* trace) {
  ReportDiagnostic(trace->getBeginLoc(),
                   diag_class_declares_pure_virtual_trace_)
      << info->record();
}

void DiagnosticsReporter::LeftMostBaseMustBePolymorphic(
    RecordInfo* derived,
    CXXRecordDecl* base) {
  ReportDiagnostic(base->getBeginLoc(),
                   diag_left_most_base_must_be_polymorphic_)
      << base << derived->record();
}

void DiagnosticsReporter::BaseClassMustDeclareVirtualTrace(
    RecordInfo* derived,
    CXXRecordDecl* base) {
  ReportDiagnostic(base->getBeginLoc(),
                   diag_base_class_must_declare_virtual_trace_)
      << base << derived->record();
}

void DiagnosticsReporter::TraceMethodForStackAllocatedClass(
    RecordInfo* info,
    CXXMethodDecl* trace) {
  ReportDiagnostic(trace->getBeginLoc(),
                   diag_trace_method_of_stack_allocated_parent_)
      << info->record();
}

void DiagnosticsReporter::NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
  ReportDiagnostic(dispatch->getBeginLoc(), diag_manual_dispatch_method_note_)
      << dispatch;
}

void DiagnosticsReporter::NoteBaseRequiresTracing(BasePoint* base) {
  ReportDiagnostic(base->spec().getBeginLoc(), diag_base_requires_tracing_note_)
      << base->info()->record();
}

void DiagnosticsReporter::NoteFieldRequiresTracing(
    RecordInfo* holder,
    FieldDecl* field) {
  NoteField(field, diag_field_requires_tracing_note_);
}

void DiagnosticsReporter::NoteFieldShouldNotBeTraced(
    RecordInfo* holder,
    FieldDecl* field) {
  NoteField(field, diag_field_should_not_be_traced_note_);
}

void DiagnosticsReporter::NotePartObjectContainsGCRoot(FieldPoint* point) {
  FieldDecl* field = point->field();
  ReportDiagnostic(field->getBeginLoc(),
                   diag_part_object_contains_gc_root_note_)
      << field << field->getParent();
}

void DiagnosticsReporter::NoteFieldContainsGCRoot(FieldPoint* point) {
  NoteField(point, diag_field_contains_gc_root_note_);
}

void DiagnosticsReporter::NoteUserDeclaredDestructor(CXXMethodDecl* dtor) {
  ReportDiagnostic(dtor->getBeginLoc(), diag_user_declared_destructor_note_);
}

void DiagnosticsReporter::NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) {
  ReportDiagnostic(dtor->getBeginLoc(), diag_user_declared_finalizer_note_);
}

void DiagnosticsReporter::NoteBaseRequiresFinalization(BasePoint* base) {
  ReportDiagnostic(base->spec().getBeginLoc(),
                   diag_base_requires_finalization_note_)
      << base->info()->record();
}

void DiagnosticsReporter::NoteFieldRequiresFinalization(FieldPoint* point) {
  NoteField(point, diag_field_requires_finalization_note_);
}

void DiagnosticsReporter::NoteField(FieldPoint* point, unsigned note) {
  NoteField(point->field(), note);
}

void DiagnosticsReporter::NoteField(FieldDecl* field, unsigned note) {
  ReportDiagnostic(field->getBeginLoc(), note) << field;
}

void DiagnosticsReporter::NoteOverriddenNonVirtualTrace(
    CXXMethodDecl* overridden) {
  ReportDiagnostic(overridden->getBeginLoc(),
                   diag_overridden_non_virtual_trace_note_)
      << overridden;
}

void DiagnosticsReporter::UniquePtrUsedWithGC(
    const clang::Expr* expr,
    const clang::FunctionDecl* bad_function,
    const clang::CXXRecordDecl* gc_type) {
  ReportDiagnostic(expr->getBeginLoc(), diag_unique_ptr_used_with_gc_)
      << bad_function << gc_type << expr->getSourceRange();
}

void DiagnosticsReporter::OptionalUsedWithGC(
    const clang::Expr* expr,
    const clang::CXXRecordDecl* optional,
    const clang::CXXRecordDecl* gc_type) {
  ReportDiagnostic(expr->getBeginLoc(), diag_optional_used_with_gc_)
      << optional << gc_type << expr->getSourceRange();
}

void DiagnosticsReporter::MissingMixinMarker(
    const clang::CXXRecordDecl* bad_class,
    const clang::CXXRecordDecl* mixin_class,
    const clang::CXXBaseSpecifier* first_base) {
  ReportDiagnostic(first_base->getBaseTypeLoc(), diag_missing_mixin_marker_)
      << bad_class << mixin_class << bad_class->getName()
      << first_base->getSourceRange();
}

void DiagnosticsReporter::MissingMixinMarkerNote(
    const clang::CXXBaseSpecifier* base) {
  ReportDiagnostic(base->getBaseTypeLoc(), diag_missing_mixin_marker_note_)
      << base->getSourceRange();
}
