diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDiagnostic.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDiagnostic.cs
new file mode 100644
index 0000000000..46a6e06b8c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDiagnostic.cs
@@ -0,0 +1,81 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ internal class DefaultRazorDiagnostic : RazorDiagnostic
+ {
+ private readonly RazorDiagnosticDescriptor _descriptor;
+ private readonly object[] _args;
+
+ internal DefaultRazorDiagnostic(RazorDiagnosticDescriptor descriptor, SourceSpan span, object[] args)
+ {
+ _descriptor = descriptor;
+ Span = span;
+ _args = args;
+ }
+
+ public override string Id => _descriptor.Id;
+
+ public override RazorDiagnosticSeverity Severity => _descriptor.Severity;
+
+ public override SourceSpan Span { get; }
+
+ public override string GetMessage(IFormatProvider formatProvider)
+ {
+ var format = _descriptor.GetMessageFormat();
+ return string.Format(formatProvider, format, _args);
+ }
+
+ public override bool Equals(RazorDiagnostic obj)
+ {
+ var other = obj as DefaultRazorDiagnostic;
+ if (other == null)
+ {
+ return false;
+ }
+
+ if (!_descriptor.Equals(other._descriptor))
+ {
+ return false;
+ }
+
+ if (!Span.Equals(other.Span))
+ {
+ return false;
+ }
+
+ if (_args.Length != other._args.Length)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < _args.Length; i++)
+ {
+ if (!_args[i].Equals(other._args[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ var hash = new HashCodeCombiner();
+ hash.Add(_descriptor.GetHashCode());
+ hash.Add(Span.GetHashCode());
+
+ for (var i = 0; i < _args.Length; i++)
+ {
+ hash.Add(_args[i]);
+ }
+
+ return hash;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RazorError.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RazorError.cs
index a67bfab440..de7f83791f 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RazorError.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RazorError.cs
@@ -65,6 +65,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Message, StringComparer.Ordinal);
hashCodeCombiner.Add(Location);
+ hashCodeCombiner.Add(Length);
return hashCodeCombiner;
}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/LegacyRazorDiagnostic.cs b/src/Microsoft.AspNetCore.Razor.Evolution/LegacyRazorDiagnostic.cs
new file mode 100644
index 0000000000..0da278fbde
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/LegacyRazorDiagnostic.cs
@@ -0,0 +1,40 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ internal class LegacyRazorDiagnostic : RazorDiagnostic
+ {
+ private readonly RazorError _error;
+
+ public LegacyRazorDiagnostic(RazorError error)
+ {
+ _error = error;
+ }
+
+ public override string Id => "RZ9999";
+
+ public override RazorDiagnosticSeverity Severity => RazorDiagnosticSeverity.Error;
+
+ public override SourceSpan Span => new SourceSpan(_error.Location, _error.Length);
+
+ public override string GetMessage(IFormatProvider formatProvider)
+ {
+ return _error.Message;
+ }
+
+ public override bool Equals(RazorDiagnostic obj)
+ {
+ var other = obj as LegacyRazorDiagnostic;
+ return other == null ? false : _error.Equals(other._error);
+ }
+
+ public override int GetHashCode()
+ {
+ return _error.GetHashCode();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
index 45f3012e6f..9c4f6c57a9 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
@@ -10,6 +10,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.Razor.Evolution.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ ///
+ /// Value cannot be null or an empty string.
+ ///
+ internal static string ArgumentCannotBeNullOrEmpty
+ {
+ get { return GetString("ArgumentCannotBeNullOrEmpty"); }
+ }
+
+ ///
+ /// Value cannot be null or an empty string.
+ ///
+ internal static string FormatArgumentCannotBeNullOrEmpty()
+ {
+ return GetString("ArgumentCannotBeNullOrEmpty");
+ }
+
///
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
///
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnostic.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnostic.cs
new file mode 100644
index 0000000000..5724e134ee
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnostic.cs
@@ -0,0 +1,74 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public abstract class RazorDiagnostic : IEquatable, IFormattable
+ {
+ internal static readonly object[] EmptyArgs = new object[0];
+
+ public abstract string Id { get; }
+
+ public abstract RazorDiagnosticSeverity Severity { get; }
+
+ public abstract SourceSpan Span { get; }
+
+ public abstract string GetMessage(IFormatProvider formatProvider);
+
+ public string GetMessage() => GetMessage(null);
+
+ public abstract bool Equals(RazorDiagnostic other);
+
+ public override abstract int GetHashCode();
+
+ public static RazorDiagnostic Create(RazorDiagnosticDescriptor descriptor, SourceSpan span)
+ {
+ if (descriptor == null)
+ {
+ throw new ArgumentNullException(nameof(descriptor));
+ }
+
+ return new DefaultRazorDiagnostic(descriptor, span, EmptyArgs);
+ }
+
+ public static RazorDiagnostic Create(RazorDiagnosticDescriptor descriptor, SourceSpan span, params object[] args)
+ {
+ if (descriptor == null)
+ {
+ throw new ArgumentNullException(nameof(descriptor));
+ }
+
+ return new DefaultRazorDiagnostic(descriptor, span, args);
+ }
+
+ internal static RazorDiagnostic Create(RazorError error)
+ {
+ if (error == null)
+ {
+ throw new ArgumentNullException(nameof(error));
+ }
+
+ return new LegacyRazorDiagnostic(error);
+ }
+
+ public override string ToString()
+ {
+ return ((IFormattable)this).ToString(null, null);
+ }
+
+ public override bool Equals(object obj)
+ {
+ var other = obj as RazorDiagnostic;
+ return other == null ? false : Equals(other);
+ }
+
+ string IFormattable.ToString(string ignore, IFormatProvider formatProvider)
+ {
+ // Our indices are 0-based, but we we want to print them as 1-based.
+ return string.Format($"{Span.FilePath}({Span.LineIndex + 1},{Span.CharacterIndex + 1}): {Severity} {Id}: {GetMessage(formatProvider)}");
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs
new file mode 100644
index 0000000000..e29e48cea9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs
@@ -0,0 +1,60 @@
+// Copyright (c) .NET Foundation. 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.Diagnostics;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ [DebuggerDisplay("{Error} {Id}: {GetMessageFormat()}")]
+ public sealed class RazorDiagnosticDescriptor : IEquatable
+ {
+ private readonly Func _messageFormat;
+
+ public RazorDiagnosticDescriptor(
+ string id,
+ Func messageFormat,
+ RazorDiagnosticSeverity severity)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(id));
+ }
+
+ if (messageFormat == null)
+ {
+ throw new ArgumentNullException(nameof(messageFormat));
+ }
+
+ Id = id;
+ _messageFormat = messageFormat;
+ Severity = severity;
+ }
+
+ public string Id { get; }
+
+ public RazorDiagnosticSeverity Severity { get; }
+
+ public string GetMessageFormat() => _messageFormat();
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as RazorDiagnosticDescriptor);
+ }
+
+ public bool Equals(RazorDiagnosticDescriptor other)
+ {
+ if (other == null)
+ {
+ return false;
+ }
+
+ return string.Equals(Id, other.Id, StringComparison.Ordinal);
+ }
+
+ public override int GetHashCode()
+ {
+ return StringComparer.Ordinal.GetHashCode(Id);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticSeverity.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticSeverity.cs
new file mode 100644
index 0000000000..5201227cb4
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticSeverity.cs
@@ -0,0 +1,11 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public enum RazorDiagnosticSeverity
+ {
+ // Purposely using the same value as Roslyn here.
+ Error = 3,
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx b/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
index c41ba721b4..0b6711b754 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Value cannot be null or an empty string.
+
The '{0}' feature requires a '{1}' provided by the '{2}'.
diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj b/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
index 312cf2dd85..b5835d9bf5 100644
--- a/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
+++ b/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
@@ -1,7 +1,5 @@
-
-
Runtime components for rendering Razor pages and implementing tag helpers.
$(Summary)
@@ -16,18 +14,15 @@ Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper
true
aspnetcore;cshtml;razor;taghelper;taghelpers
-
-
+
-
-
-
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorDiagnosticTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorDiagnosticTest.cs
new file mode 100644
index 0000000000..9ba1460122
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorDiagnosticTest.cs
@@ -0,0 +1,217 @@
+// Copyright(c) .NET Foundation.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.Globalization;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public class DefaultRazorDiagnosticTest
+ {
+ [Fact]
+ public void DefaultRazorDiagnostic_Ctor()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "error", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ // Act
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+
+ // Assert
+ Assert.Equal("RZ0000", diagnostic.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, diagnostic.Severity);
+ Assert.Equal(span, diagnostic.Span);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_GetMessage()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "error", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+
+ // Act
+ var result = diagnostic.GetMessage();
+
+ // Assert
+ Assert.Equal("error", result);
+ }
+
+
+ [Fact]
+ public void DefaultRazorDiagnostic_GetMessage_WithArgs()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new[] { "error" });
+
+ // Act
+ var result = diagnostic.GetMessage();
+
+ // Assert
+ Assert.Equal("this is an error", result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_GetMessage_WithArgs_FormatProvider()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[] { 1.3m });
+
+ // Act
+ var result = diagnostic.GetMessage(CultureInfo.GetCultureInfo("fr-FR"));
+
+ // Assert
+ Assert.Equal("this is an 1,3", result);
+ }
+
+
+ [Fact]
+ public void DefaultRazorDiagnostic_ToString()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an error", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+
+ // Act
+ var result = diagnostic.ToString();
+
+ // Assert
+ Assert.Equal("test.cs(2,9): Error RZ0000: this is an error", result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_ToString_FormatProvider()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[] { 1.3m });
+
+ // Act
+ var result = ((IFormattable)diagnostic).ToString("ignored", CultureInfo.GetCultureInfo("fr-FR"));
+
+ // Assert
+ Assert.Equal("test.cs(2,9): Error RZ0000: this is an 1,3", result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_Equals()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_NotEquals_DifferentLocation()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span1 = new SourceSpan("test.cs", 15, 1, 8, 5);
+ var span2 = new SourceSpan("test.cs", 15, 1, 8, 3);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span1, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span2, new object[0]);
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_NotEquals_DifferentId()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor1, span, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor2, span, new object[0]);
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_HashCodesEqual()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_HashCodesNotEqual_DifferentLocation()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span1 = new SourceSpan("test.cs", 15, 1, 8, 5);
+ var span2 = new SourceSpan("test.cs", 15, 1, 8, 3);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span1, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span2, new object[0]);
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void DefaultRazorDiagnostic_HashCodesNotEqual_DifferentId()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "this is an {0}", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ var diagnostic1 = new DefaultRazorDiagnostic(descriptor1, span, new object[0]);
+ var diagnostic2 = new DefaultRazorDiagnostic(descriptor2, span, new object[0]);
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.False(result);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/LegacyRazorDiagnosticTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/LegacyRazorDiagnosticTest.cs
new file mode 100644
index 0000000000..7bf20e1d41
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/LegacyRazorDiagnosticTest.cs
@@ -0,0 +1,170 @@
+// Copyright(c) .NET Foundation.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.Globalization;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public class LegacyRazorDiagnosticTest
+ {
+ [Fact]
+ public void LegacyRazorDiagnostic_Ctor()
+ {
+ // Arrange
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+ var error = new RazorError("This is an error", new SourceLocation("test.cs", 15, 1, 8), 5);
+
+ // Act
+ var diagnostic = new LegacyRazorDiagnostic(error);
+
+ // Assert
+ Assert.Equal("RZ9999", diagnostic.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, diagnostic.Severity);
+ Assert.Equal(span, diagnostic.Span);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_GetMessage()
+ {
+ // Arrange
+ var error = new RazorError("This is an error", SourceLocation.Zero, 5);
+ var diagnostic = new LegacyRazorDiagnostic(error);
+
+ // Act
+ var result = diagnostic.GetMessage();
+
+ // Assert
+ Assert.Equal("This is an error", result);
+ }
+
+ // RazorError doesn't support format strings.
+ [Fact]
+ public void LegacyRazorDiagnostic_GetMessage_FormatProvider()
+ {
+ // Arrange
+ var error = new RazorError("This is an error", SourceLocation.Zero, 5);
+ var diagnostic = new LegacyRazorDiagnostic(error);
+
+ // Act
+ var result = diagnostic.GetMessage(CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.Equal("This is an error", result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_ToString()
+ {
+ // Arrange
+ var error = new RazorError("This is an error", SourceLocation.Zero, 5);
+ var diagnostic = new LegacyRazorDiagnostic(error);
+
+ // Act
+ var result = diagnostic.ToString();
+
+ // Assert
+ Assert.Equal("(1,1): Error RZ9999: This is an error", result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_ToString_FormatProvider()
+ {
+ // Arrange
+ var error = new RazorError("This is an error", SourceLocation.Zero, 5);
+ var diagnostic = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+
+ // Act
+ var result = ((IFormattable)diagnostic).ToString("ignored", CultureInfo.InvariantCulture);
+
+ // Assert
+ Assert.Equal("(1,1): Error RZ9999: This is an error", result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_Equals()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_NotEquals_DifferentLocation()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 1));
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_NotEquals_DifferentMessage()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is maybe an error", SourceLocation.Zero, 5));
+
+ // Act
+ var result = diagnostic1.Equals(diagnostic2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_HashCodesEqual()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_HashCodesNotEqual_DifferentLocation()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 2));
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void LegacyRazorDiagnostic_HashCodesNotEqual_DifferentMessage()
+ {
+ // Arrange
+ var diagnostic1 = new LegacyRazorDiagnostic(new RazorError("This is an error", SourceLocation.Zero, 5));
+ var diagnostic2 = new LegacyRazorDiagnostic(new RazorError("This is maybe an error", SourceLocation.Zero, 5));
+
+ // Act
+ var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
+
+ // Assert
+ Assert.False(result);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticDescriptorTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticDescriptorTest.cs
new file mode 100644
index 0000000000..7fa0f0ba7c
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticDescriptorTest.cs
@@ -0,0 +1,78 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public class RazorDiagnosticDescriptorTest
+ {
+ [Fact]
+ public void RazorDiagnosticDescriptor_Ctor()
+ {
+ // Arrange & Act
+ var descriptor = new RazorDiagnosticDescriptor("RZ0001", () => "Hello, World!", RazorDiagnosticSeverity.Error);
+
+ // Assert
+ Assert.Equal("RZ0001", descriptor.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, descriptor.Severity);
+ Assert.Equal("Hello, World!", descriptor.GetMessageFormat());
+ }
+
+ [Fact]
+ public void RazorDiagnosticDescriptor_Equals()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "a!", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0001", () => "b!", RazorDiagnosticSeverity.Error);
+
+ // Act
+ var result = descriptor1.Equals(descriptor2);
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void RazorDiagnosticDescriptor_NotEquals()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "a!", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "b!", RazorDiagnosticSeverity.Error);
+
+ // Act
+ var result = descriptor1.Equals(descriptor2);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void RazorDiagnosticDescriptor_HashCodesEqual()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "a!", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0001", () => "b!", RazorDiagnosticSeverity.Error);
+
+ // Act
+ var result = descriptor1.GetHashCode() == descriptor2.GetHashCode();
+
+ // Assert
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void RazorDiagnosticDescriptor_HashCodesNotEqual()
+ {
+ // Arrange
+ var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "a!", RazorDiagnosticSeverity.Error);
+ var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "b!", RazorDiagnosticSeverity.Error);
+
+ // Act
+ var result = descriptor1.GetHashCode() == descriptor2.GetHashCode();
+
+ // Assert
+ Assert.False(result);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticTest.cs
new file mode 100644
index 0000000000..fe7e1fc683
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorDiagnosticTest.cs
@@ -0,0 +1,62 @@
+// Copyright(c) .NET Foundation.All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public class RazorDiagnosticTest
+ {
+ [Fact]
+ public void Create_WithDescriptor_CreatesDefaultRazorDiagnostic()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0001", () => "a", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ // Act
+ var diagnostic = RazorDiagnostic.Create(descriptor, span);
+
+ // Assert
+ var defaultDiagnostic = Assert.IsType(diagnostic);
+ Assert.Equal("RZ0001", defaultDiagnostic.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, defaultDiagnostic.Severity);
+ Assert.Equal(span, diagnostic.Span);
+ }
+
+ [Fact]
+ public void Create_WithDescriptor_AndArgs_CreatesDefaultRazorDiagnostic()
+ {
+ // Arrange
+ var descriptor = new RazorDiagnosticDescriptor("RZ0001", () => "a", RazorDiagnosticSeverity.Error);
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+
+ // Act
+ var diagnostic = RazorDiagnostic.Create(descriptor, span, "Hello", "World");
+
+ // Assert
+ var defaultDiagnostic = Assert.IsType(diagnostic);
+ Assert.Equal("RZ0001", defaultDiagnostic.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, defaultDiagnostic.Severity);
+ Assert.Equal(span, diagnostic.Span);
+ }
+
+ [Fact]
+ public void Create_WithRazorError_CreatesLegacyRazorDiagnostic()
+ {
+ // Arrange
+ var span = new SourceSpan("test.cs", 15, 1, 8, 5);
+ var error = new RazorError("This is an error", new SourceLocation("test.cs", 15, 1, 8), 5);
+
+ // Act
+ var diagnostic = RazorDiagnostic.Create(error);
+
+ // Assert
+ var legacyDiagnostic = Assert.IsType(diagnostic);
+ Assert.Equal("RZ9999", legacyDiagnostic.Id);
+ Assert.Equal(RazorDiagnosticSeverity.Error, legacyDiagnostic.Severity);
+ Assert.Equal(span, diagnostic.Span);
+ }
+ }
+}