|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// A diagnostic (such as a compiler error or a warning), along with the location where it occurred.
/// </summary>
public abstract partial class Diagnostic
{
internal sealed class SimpleDiagnostic : Diagnostic
{
private readonly DiagnosticDescriptor _descriptor;
private readonly DiagnosticSeverity _severity;
private readonly int _warningLevel;
private readonly Location _location;
private readonly IReadOnlyList<Location> _additionalLocations;
private readonly object?[] _messageArgs;
private readonly ImmutableDictionary<string, string?> _properties;
private readonly bool _isSuppressed;
private SimpleDiagnostic(
DiagnosticDescriptor descriptor,
DiagnosticSeverity severity,
int warningLevel,
Location location,
IEnumerable<Location>? additionalLocations,
object?[]? messageArgs,
ImmutableDictionary<string, string?>? properties,
bool isSuppressed)
{
if ((warningLevel == 0 && severity != DiagnosticSeverity.Error) ||
(warningLevel != 0 && severity == DiagnosticSeverity.Error))
{
throw new ArgumentException($"{nameof(warningLevel)} ({warningLevel}) and {nameof(severity)} ({severity}) are not compatible.", nameof(warningLevel));
}
_descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
_severity = severity;
_warningLevel = warningLevel;
_location = location ?? Location.None;
_additionalLocations = additionalLocations?.ToImmutableArray() ?? SpecializedCollections.EmptyReadOnlyList<Location>();
_messageArgs = messageArgs ?? Array.Empty<object?>();
_properties = properties ?? ImmutableDictionary<string, string?>.Empty;
_isSuppressed = isSuppressed;
}
internal static SimpleDiagnostic Create(
DiagnosticDescriptor descriptor,
DiagnosticSeverity severity,
int warningLevel,
Location location,
IEnumerable<Location>? additionalLocations,
object?[]? messageArgs,
ImmutableDictionary<string, string?>? properties,
bool isSuppressed = false)
{
return new SimpleDiagnostic(descriptor, severity, warningLevel, location, additionalLocations, messageArgs, properties, isSuppressed);
}
internal static SimpleDiagnostic Create(string id, LocalizableString title, string category, LocalizableString message, LocalizableString description, string helpLink,
DiagnosticSeverity severity, DiagnosticSeverity defaultSeverity,
bool isEnabledByDefault, int warningLevel, Location location,
IEnumerable<Location>? additionalLocations, IEnumerable<string>? customTags,
ImmutableDictionary<string, string?>? properties, bool isSuppressed = false)
{
var descriptor = new DiagnosticDescriptor(id, title, message,
category, defaultSeverity, isEnabledByDefault, description, helpLink, customTags.ToImmutableArrayOrEmpty());
return new SimpleDiagnostic(descriptor, severity, warningLevel, location, additionalLocations, messageArgs: null, properties: properties, isSuppressed: isSuppressed);
}
public override DiagnosticDescriptor Descriptor
{
get { return _descriptor; }
}
public override string Id
{
get { return _descriptor.Id; }
}
public override string GetMessage(IFormatProvider? formatProvider = null)
{
if (_messageArgs.Length == 0)
{
return _descriptor.MessageFormat.ToString(formatProvider);
}
var localizedMessageFormat = _descriptor.MessageFormat.ToString(formatProvider);
try
{
return string.Format(formatProvider, localizedMessageFormat, _messageArgs);
}
catch (Exception)
{
// Analyzer reported diagnostic with invalid format arguments, so just return the unformatted message.
return localizedMessageFormat;
}
}
internal override IReadOnlyList<object?> Arguments
{
get { return _messageArgs; }
}
public override DiagnosticSeverity Severity
{
get { return _severity; }
}
public override bool IsSuppressed
{
get { return _isSuppressed; }
}
public override int WarningLevel
{
get { return _warningLevel; }
}
public override Location Location
{
get { return _location; }
}
public override IReadOnlyList<Location> AdditionalLocations
{
get { return _additionalLocations; }
}
public override ImmutableDictionary<string, string?> Properties
{
get { return _properties; }
}
public override bool Equals(Diagnostic? obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
var other = obj as SimpleDiagnostic;
if (other == null)
{
return false;
}
if (AnalyzerExecutor.IsAnalyzerExceptionDiagnostic(this))
{
// We have custom Equals logic for diagnostics generated for analyzer exceptions.
return AnalyzerExecutor.AreEquivalentAnalyzerExceptionDiagnostics(this, other);
}
return _descriptor.Equals(other._descriptor)
&& _messageArgs.SequenceEqual(other._messageArgs, (a, b) => a == b)
&& _location == other._location
&& _severity == other._severity
&& _warningLevel == other._warningLevel;
}
public override int GetHashCode()
{
return Hash.Combine(_descriptor,
Hash.CombineValues(_messageArgs,
Hash.Combine(_warningLevel,
Hash.Combine(_location, (int)_severity))));
}
internal override Diagnostic WithLocation(Location location)
{
if (location is null)
{
throw new ArgumentNullException(nameof(location));
}
if (location != _location)
{
return new SimpleDiagnostic(_descriptor, _severity, _warningLevel, location, _additionalLocations, _messageArgs, _properties, _isSuppressed);
}
return this;
}
internal override Diagnostic WithSeverity(DiagnosticSeverity severity)
{
if (this.Severity != severity)
{
var warningLevel = GetDefaultWarningLevel(severity);
return new SimpleDiagnostic(_descriptor, severity, warningLevel, _location, _additionalLocations, _messageArgs, _properties, _isSuppressed);
}
return this;
}
internal override Diagnostic WithIsSuppressed(bool isSuppressed)
{
if (this.IsSuppressed != isSuppressed)
{
return new SimpleDiagnostic(_descriptor, _severity, _warningLevel, _location, _additionalLocations, _messageArgs, _properties, isSuppressed);
}
return this;
}
}
}
}
|