// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using Roslyn.Utilities;
using System.Threading;
namespace Microsoft.CodeAnalysis
{
///
/// A DiagnosticInfo object has information about a diagnostic, but without any attached location information.
///
///
/// More specialized diagnostics with additional information (e.g., ambiguity errors) can derive from this class to
/// provide access to additional information about the error, such as what symbols were involved in the ambiguity.
///
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal class DiagnosticInfo : IFormattable, IObjectWritable
{
private readonly CommonMessageProvider _messageProvider;
private readonly int _errorCode;
private readonly DiagnosticSeverity _defaultSeverity;
private readonly DiagnosticSeverity _effectiveSeverity;
private readonly object[] _arguments;
private static ImmutableDictionary s_errorCodeToDescriptorMap = ImmutableDictionary.Empty;
// Mark compiler errors as non-configurable to ensure they can never be suppressed or filtered.
private static readonly ImmutableArray s_compilerErrorCustomTags = ImmutableArray.Create(WellKnownDiagnosticTags.Compiler, WellKnownDiagnosticTags.Telemetry, WellKnownDiagnosticTags.NotConfigurable);
private static readonly ImmutableArray s_compilerNonErrorCustomTags = ImmutableArray.Create(WellKnownDiagnosticTags.Compiler, WellKnownDiagnosticTags.Telemetry);
static DiagnosticInfo()
{
ObjectBinder.RegisterTypeReader(typeof(DiagnosticInfo), r => new DiagnosticInfo(r));
}
// Only the compiler creates instances.
internal DiagnosticInfo(CommonMessageProvider messageProvider, int errorCode)
{
_messageProvider = messageProvider;
_errorCode = errorCode;
_defaultSeverity = messageProvider.GetSeverity(errorCode);
_effectiveSeverity = _defaultSeverity;
}
// Only the compiler creates instances.
internal DiagnosticInfo(CommonMessageProvider messageProvider, int errorCode, params object[] arguments)
: this(messageProvider, errorCode)
{
AssertMessageSerializable(arguments);
_arguments = arguments;
}
private DiagnosticInfo(DiagnosticInfo original, DiagnosticSeverity overriddenSeverity)
{
_messageProvider = original.MessageProvider;
_errorCode = original._errorCode;
_defaultSeverity = original.DefaultSeverity;
_arguments = original._arguments;
_effectiveSeverity = overriddenSeverity;
}
internal static DiagnosticDescriptor GetDescriptor(int errorCode, CommonMessageProvider messageProvider)
{
var defaultSeverity = messageProvider.GetSeverity(errorCode);
return GetOrCreateDescriptor(errorCode, defaultSeverity, messageProvider);
}
private static DiagnosticDescriptor GetOrCreateDescriptor(int errorCode, DiagnosticSeverity defaultSeverity, CommonMessageProvider messageProvider)
{
return ImmutableInterlocked.GetOrAdd(ref s_errorCodeToDescriptorMap, errorCode, code => CreateDescriptor(code, defaultSeverity, messageProvider));
}
private static DiagnosticDescriptor CreateDescriptor(int errorCode, DiagnosticSeverity defaultSeverity, CommonMessageProvider messageProvider)
{
var id = messageProvider.GetIdForErrorCode(errorCode);
var title = messageProvider.GetTitle(errorCode);
var description = messageProvider.GetDescription(errorCode);
var messageFormat = messageProvider.GetMessageFormat(errorCode);
var helpLink = messageProvider.GetHelpLink(errorCode);
var category = messageProvider.GetCategory(errorCode);
var customTags = GetCustomTags(defaultSeverity);
return new DiagnosticDescriptor(id, title, messageFormat, category, defaultSeverity,
isEnabledByDefault: true, description: description, helpLinkUri: helpLink, customTags: customTags);
}
[Conditional("DEBUG")]
internal static void AssertMessageSerializable(object[] args)
{
foreach (var arg in args)
{
Debug.Assert(arg != null);
if (arg is IFormattable)
{
continue;
}
var type = arg.GetType();
if (type == typeof(string) || type == typeof(AssemblyIdentity))
{
continue;
}
var info = type.GetTypeInfo();
if (info.IsPrimitive)
{
continue;
}
throw ExceptionUtilities.UnexpectedValue(type);
}
}
// Only the compiler creates instances.
internal DiagnosticInfo(CommonMessageProvider messageProvider, bool isWarningAsError, int errorCode, params object[] arguments)
: this(messageProvider, errorCode, arguments)
{
Debug.Assert(!isWarningAsError || _defaultSeverity == DiagnosticSeverity.Warning);
if (isWarningAsError)
{
_effectiveSeverity = DiagnosticSeverity.Error;
}
}
// Create a copy of this instance with a explicit overridden severity
internal DiagnosticInfo GetInstanceWithSeverity(DiagnosticSeverity severity)
{
return new DiagnosticInfo(this, severity);
}
#region Serialization
bool IObjectWritable.ShouldReuseInSerialization => false;
void IObjectWritable.WriteTo(ObjectWriter writer)
{
this.WriteTo(writer);
}
protected virtual void WriteTo(ObjectWriter writer)
{
writer.WriteValue(_messageProvider);
writer.WriteUInt32((uint)_errorCode);
writer.WriteInt32((int)_effectiveSeverity);
writer.WriteInt32((int)_defaultSeverity);
int count = _arguments?.Length ?? 0;
writer.WriteUInt32((uint)count);
if (count > 0)
{
foreach (var arg in _arguments)
{
writer.WriteString(arg.ToString());
}
}
}
protected DiagnosticInfo(ObjectReader reader)
{
_messageProvider = (CommonMessageProvider)reader.ReadValue();
_errorCode = (int)reader.ReadUInt32();
_effectiveSeverity = (DiagnosticSeverity)reader.ReadInt32();
_defaultSeverity = (DiagnosticSeverity)reader.ReadInt32();
var count = (int)reader.ReadUInt32();
if (count == 0)
{
_arguments = Array.Empty