diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs index 599697730..68ad1265b 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs @@ -234,7 +234,7 @@ private void VerifyDiagnosticResults(IEnumerable actualResults, Immu if (!expected.HasLocation) { - verifier.Equal(Location.None, actual.Location, $"Expected:\nA project diagnostic with No location\nActual:\n{FormatDiagnostics(analyzers, actual)}"); + verifier.Equal(Location.None, actual.Location, $"Expected:\r\nA project diagnostic with No location\r\nActual:\r\n{FormatDiagnostics(analyzers, actual)}"); } else { @@ -280,39 +280,31 @@ private void VerifyDiagnosticResults(IEnumerable actualResults, Immu /// The describing the expected location of the /// diagnostic. /// The verifier to use for test assertions. - private void VerifyDiagnosticLocation(ImmutableArray analyzers, Diagnostic diagnostic, Location actual, FileLinePositionSpan expected, IVerifier verifier) + private void VerifyDiagnosticLocation(ImmutableArray analyzers, Diagnostic diagnostic, Location actual, DiagnosticLocation expected, IVerifier verifier) { var actualSpan = actual.GetLineSpan(); - var assert = actualSpan.Path == expected.Path || (actualSpan.Path?.Contains("Test0.") == true && expected.Path.Contains("Test.")); - verifier.True(assert, $"Expected diagnostic to be in file \"{expected.Path}\" was actually in file \"{actualSpan.Path}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); + var assert = actualSpan.Path == expected.Span.Path || (actualSpan.Path?.Contains("Test0.") == true && expected.Span.Path.Contains("Test.")); + verifier.True(assert, $"Expected diagnostic to be in file \"{expected.Span.Path}\" was actually in file \"{actualSpan.Path}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); - VerifyLinePosition(analyzers, diagnostic, actualSpan.StartLinePosition, expected.StartLinePosition, "start", verifier); - if (expected.StartLinePosition < expected.EndLinePosition) + VerifyLinePosition(analyzers, diagnostic, actualSpan.StartLinePosition, expected.Span.StartLinePosition, "start", verifier); + if (!expected.Options.HasFlag(DiagnosticLocationOptions.IgnoreLength)) { - VerifyLinePosition(analyzers, diagnostic, actualSpan.EndLinePosition, expected.EndLinePosition, "end", verifier); + VerifyLinePosition(analyzers, diagnostic, actualSpan.EndLinePosition, expected.Span.EndLinePosition, "end", verifier); } } private void VerifyLinePosition(ImmutableArray analyzers, Diagnostic diagnostic, LinePosition actualLinePosition, LinePosition expectedLinePosition, string positionText, IVerifier verifier) { - // Only check the line position if it matters - if (expectedLinePosition.Line > 0) - { - verifier.Equal( - expectedLinePosition.Line, - actualLinePosition.Line, - $"Expected diagnostic to {positionText} on line \"{expectedLinePosition.Line + 1}\" was actually on line \"{actualLinePosition.Line + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); - } - - // Only check the column position if it matters - if (expectedLinePosition.Character > 0) - { - verifier.Equal( - expectedLinePosition.Character, - actualLinePosition.Character, - $"Expected diagnostic to {positionText} at column \"{expectedLinePosition.Character + 1}\" was actually at column \"{actualLinePosition.Character + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); - } + verifier.Equal( + expectedLinePosition.Line, + actualLinePosition.Line, + $"Expected diagnostic to {positionText} on line \"{expectedLinePosition.Line + 1}\" was actually on line \"{actualLinePosition.Line + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); + + verifier.Equal( + expectedLinePosition.Character, + actualLinePosition.Character, + $"Expected diagnostic to {positionText} at column \"{expectedLinePosition.Character + 1}\" was actually at column \"{actualLinePosition.Character + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzers, diagnostic)}\r\n"); } /// @@ -428,7 +420,7 @@ private static bool IsSuppressible(ImmutableArray analyzers, private static bool IsInSourceFile(DiagnosticResult result, (string filename, SourceText content)[] sources) { - return sources.Any(source => source.filename.Equals(result.Spans[0].Path)); + return sources.Any(source => source.filename.Equals(result.Spans[0].Span.Path)); } /// diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocation.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocation.cs new file mode 100644 index 000000000..35ca37420 --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocation.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Testing +{ + public readonly struct DiagnosticLocation + { + public DiagnosticLocation(FileLinePositionSpan span, DiagnosticLocationOptions options) + { + Span = span; + Options = options; + } + + public FileLinePositionSpan Span { get; } + + public DiagnosticLocationOptions Options { get; } + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocationOptions.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocationOptions.cs new file mode 100644 index 000000000..3bfa4e9fb --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticLocationOptions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Testing +{ + /// + /// Defines options for interpreting . + /// + public enum DiagnosticLocationOptions + { + /// + /// The diagnostic location is a simple . + /// + None = 0, + + /// + /// The diagnostic location is defined as a position instead of a span. The length of the actual diagnostic span + /// should be ignored when comparing results. + /// + IgnoreLength = 1, + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticResult.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticResult.cs index 910d0c7a8..e64ee2191 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticResult.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/DiagnosticResult.cs @@ -11,15 +11,15 @@ namespace Microsoft.CodeAnalysis.Testing /// /// Structure that stores information about a appearing in a source. /// - public struct DiagnosticResult + public readonly struct DiagnosticResult { public static readonly DiagnosticResult[] EmptyDiagnosticResults = { }; private static readonly object[] EmptyArguments = new object[0]; - private ImmutableArray _spans; - private bool _suppressMessage; - private string _message; + private readonly ImmutableArray _spans; + private readonly bool _suppressMessage; + private readonly string _message; public DiagnosticResult(string id, DiagnosticSeverity severity) : this() @@ -36,9 +36,27 @@ public DiagnosticResult(DiagnosticDescriptor descriptor) MessageFormat = descriptor.MessageFormat; } - public ImmutableArray Spans => _spans.IsDefault ? ImmutableArray.Empty : _spans; + private DiagnosticResult( + ImmutableArray spans, + bool suppressMessage, + string message, + DiagnosticSeverity severity, + string id, + LocalizableString messageFormat, + object[] messageArguments) + { + _spans = spans; + _suppressMessage = suppressMessage; + _message = message; + Severity = severity; + Id = id; + MessageFormat = messageFormat; + MessageArguments = messageArguments; + } - public DiagnosticSeverity Severity { get; private set; } + public ImmutableArray Spans => _spans.IsDefault ? ImmutableArray.Empty : _spans; + + public DiagnosticSeverity Severity { get; } public string Id { get; } @@ -65,9 +83,9 @@ public string Message } } - public LocalizableString MessageFormat { get; private set; } + public LocalizableString MessageFormat { get; } - public object[] MessageArguments { get; private set; } + public object[] MessageArguments { get; } public bool HasLocation => !Spans.IsEmpty; @@ -79,31 +97,62 @@ public static DiagnosticResult CompilerWarning(string identifier) public DiagnosticResult WithSeverity(DiagnosticSeverity severity) { - var result = this; - result.Severity = severity; - return result; + return new DiagnosticResult( + spans: _spans, + suppressMessage: _suppressMessage, + message: _message, + severity: severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } public DiagnosticResult WithArguments(params object[] arguments) { - var result = this; - result.MessageArguments = arguments; - return result; + return new DiagnosticResult( + spans: _spans, + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: arguments); } public DiagnosticResult WithMessage(string message) { - var result = this; - result._message = message; - result._suppressMessage = message is null; - return result; + return new DiagnosticResult( + spans: _spans, + suppressMessage: message is null, + message: message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } public DiagnosticResult WithMessageFormat(LocalizableString messageFormat) { - var result = this; - result.MessageFormat = messageFormat; - return result; + return new DiagnosticResult( + spans: _spans, + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: messageFormat, + messageArguments: MessageArguments); + } + + public DiagnosticResult WithNoLocation() + { + return new DiagnosticResult( + spans: ImmutableArray.Empty, + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } public DiagnosticResult WithLocation(int line, int column) @@ -116,16 +165,16 @@ public DiagnosticResult WithLocation(string path, int line, int column) => WithLocation(path, new LinePosition(line - 1, column - 1)); public DiagnosticResult WithLocation(string path, LinePosition location) - => AppendSpan(new FileLinePositionSpan(path, location, location)); + => AppendSpan(new FileLinePositionSpan(path, location, location), DiagnosticLocationOptions.IgnoreLength); public DiagnosticResult WithSpan(int startLine, int startColumn, int endLine, int endColumn) => WithSpan(path: string.Empty, startLine, startColumn, endLine, endColumn); public DiagnosticResult WithSpan(string path, int startLine, int startColumn, int endLine, int endColumn) - => AppendSpan(new FileLinePositionSpan(path, new LinePosition(startLine - 1, startColumn - 1), new LinePosition(endLine - 1, endColumn - 1))); + => AppendSpan(new FileLinePositionSpan(path, new LinePosition(startLine - 1, startColumn - 1), new LinePosition(endLine - 1, endColumn - 1)), DiagnosticLocationOptions.None); public DiagnosticResult WithSpan(FileLinePositionSpan span) - => AppendSpan(span); + => AppendSpan(span, DiagnosticLocationOptions.None); public DiagnosticResult WithDefaultPath(string path) { @@ -134,18 +183,23 @@ public DiagnosticResult WithDefaultPath(string path) return this; } - var result = this; var spans = Spans.ToBuilder(); for (var i = 0; i < spans.Count; i++) { - if (spans[i].Path == string.Empty) + if (spans[i].Span.Path == string.Empty) { - spans[i] = new FileLinePositionSpan(path, spans[i].Span); + spans[i] = new DiagnosticLocation(new FileLinePositionSpan(path, spans[i].Span.Span), spans[i].Options); } } - result._spans = spans.MoveToImmutable(); - return result; + return new DiagnosticResult( + spans: spans.MoveToImmutable(), + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } public DiagnosticResult WithLineOffset(int offset) @@ -159,21 +213,32 @@ public DiagnosticResult WithLineOffset(int offset) var spansBuilder = result.Spans.ToBuilder(); for (var i = 0; i < result.Spans.Length; i++) { - var newStartLinePosition = new LinePosition(result.Spans[i].StartLinePosition.Line + offset, result.Spans[i].StartLinePosition.Character); - var newEndLinePosition = new LinePosition(result.Spans[i].EndLinePosition.Line + offset, result.Spans[i].EndLinePosition.Character); + var newStartLinePosition = new LinePosition(result.Spans[i].Span.StartLinePosition.Line + offset, result.Spans[i].Span.StartLinePosition.Character); + var newEndLinePosition = new LinePosition(result.Spans[i].Span.EndLinePosition.Line + offset, result.Spans[i].Span.EndLinePosition.Character); - spansBuilder[i] = new FileLinePositionSpan(result.Spans[i].Path, newStartLinePosition, newEndLinePosition); + spansBuilder[i] = new DiagnosticLocation(new FileLinePositionSpan(result.Spans[i].Span.Path, newStartLinePosition, newEndLinePosition), result.Spans[i].Options); } - result._spans = spansBuilder.MoveToImmutable(); - return result; + return new DiagnosticResult( + spans: spansBuilder.MoveToImmutable(), + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } - private DiagnosticResult AppendSpan(FileLinePositionSpan span) + private DiagnosticResult AppendSpan(FileLinePositionSpan span, DiagnosticLocationOptions options) { - var result = this; - result._spans = Spans.Add(span); - return result; + return new DiagnosticResult( + spans: Spans.Add(new DiagnosticLocation(span, options)), + suppressMessage: _suppressMessage, + message: _message, + severity: Severity, + id: Id, + messageFormat: MessageFormat, + messageArguments: MessageArguments); } public override string ToString() @@ -182,17 +247,17 @@ public override string ToString() if (HasLocation) { var location = Spans[0]; - builder.Append(location.Path == string.Empty ? "?" : location.Path); + builder.Append(location.Span.Path == string.Empty ? "?" : location.Span.Path); builder.Append("("); - builder.Append(location.StartLinePosition.Line + 1); + builder.Append(location.Span.StartLinePosition.Line + 1); builder.Append(","); - builder.Append(location.StartLinePosition.Character + 1); - if (location.EndLinePosition != location.StartLinePosition) + builder.Append(location.Span.StartLinePosition.Character + 1); + if (!location.Options.HasFlag(DiagnosticLocationOptions.IgnoreLength)) { builder.Append(","); - builder.Append(location.EndLinePosition.Line + 1); + builder.Append(location.Span.EndLinePosition.Line + 1); builder.Append(","); - builder.Append(location.EndLinePosition.Character + 1); + builder.Append(location.Span.EndLinePosition.Character + 1); } builder.Append("): "); diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IEnumerableExtensions.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IEnumerableExtensions.cs index 428db2306..bc5bbdb3c 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IEnumerableExtensions.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/IEnumerableExtensions.cs @@ -11,11 +11,11 @@ internal static class IEnumerableExtensions public static DiagnosticResult[] ToOrderedArray(this IEnumerable diagnosticResults) { return diagnosticResults - .OrderBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Path, StringComparer.Ordinal) - .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Start.Line) - .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Start.Character) - .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.End.Line) - .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.End.Character) + .OrderBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Path, StringComparer.Ordinal) + .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Span.Start.Line) + .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Span.Start.Character) + .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Span.End.Line) + .ThenBy(diagnosticResult => diagnosticResult.Spans.FirstOrDefault().Span.Span.End.Character) .ThenBy(diagnosticResult => diagnosticResult.Id, StringComparer.Ordinal) .ToArray(); } diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt index 1aaabea85..2627e35e5 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -36,6 +36,13 @@ Microsoft.CodeAnalysis.Testing.DefaultVerifier.NotEmpty(string collectionName Microsoft.CodeAnalysis.Testing.DefaultVerifier.PushContext(string context) -> Microsoft.CodeAnalysis.Testing.IVerifier Microsoft.CodeAnalysis.Testing.DefaultVerifier.SequenceEqual(System.Collections.Generic.IEnumerable expected, System.Collections.Generic.IEnumerable actual, System.Collections.Generic.IEqualityComparer equalityComparer = null, string message = null) -> void Microsoft.CodeAnalysis.Testing.DefaultVerifier.True(bool assert, string message = null) -> void +Microsoft.CodeAnalysis.Testing.DiagnosticLocation +Microsoft.CodeAnalysis.Testing.DiagnosticLocation.DiagnosticLocation(Microsoft.CodeAnalysis.FileLinePositionSpan span, Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions options) -> void +Microsoft.CodeAnalysis.Testing.DiagnosticLocation.Options.get -> Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions +Microsoft.CodeAnalysis.Testing.DiagnosticLocation.Span.get -> Microsoft.CodeAnalysis.FileLinePositionSpan +Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions +Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions.IgnoreLength = 1 -> Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions +Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions.None = 0 -> Microsoft.CodeAnalysis.Testing.DiagnosticLocationOptions Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.DiagnosticResult(Microsoft.CodeAnalysis.DiagnosticDescriptor descriptor) -> void Microsoft.CodeAnalysis.Testing.DiagnosticResult.DiagnosticResult(string id, Microsoft.CodeAnalysis.DiagnosticSeverity severity) -> void @@ -45,7 +52,7 @@ Microsoft.CodeAnalysis.Testing.DiagnosticResult.Message.get -> string Microsoft.CodeAnalysis.Testing.DiagnosticResult.MessageArguments.get -> object[] Microsoft.CodeAnalysis.Testing.DiagnosticResult.MessageFormat.get -> Microsoft.CodeAnalysis.LocalizableString Microsoft.CodeAnalysis.Testing.DiagnosticResult.Severity.get -> Microsoft.CodeAnalysis.DiagnosticSeverity -Microsoft.CodeAnalysis.Testing.DiagnosticResult.Spans.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Testing.DiagnosticResult.Spans.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithArguments(params object[] arguments) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithDefaultPath(string path) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithLineOffset(int offset) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult @@ -55,6 +62,7 @@ Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithLocation(string path, Micros Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithLocation(string path, int line, int column) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithMessage(string message) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithMessageFormat(Microsoft.CodeAnalysis.LocalizableString messageFormat) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult +Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithNoLocation() -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithSeverity(Microsoft.CodeAnalysis.DiagnosticSeverity severity) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithSpan(Microsoft.CodeAnalysis.FileLinePositionSpan span) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult Microsoft.CodeAnalysis.Testing.DiagnosticResult.WithSpan(int startLine, int startColumn, int endLine, int endColumn) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs index a2ef7e0f9..b0e020223 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs @@ -54,7 +54,7 @@ public async Task TestCSharpMarkupCompilerError() { var testCode = @" class TestClass { - void TestMethod() { throw null {|CS1002:|}} + void TestMethod() { throw null {|CS1002:}|} } "; diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs index 5942dfa2d..2d8f537da 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs @@ -30,7 +30,27 @@ public async Task TestDiagnosticMatchesCorrectLocation() }.RunAsync(); } - [Fact(Skip = "https://github.com/dotnet/roslyn-sdk/issues/207")] + [Fact] + public async Task TestDiagnosticWithoutLocation() + { + await new CSharpAnalyzerTest + { + TestCode = @"class TestClass { }", + ExpectedDiagnostics = { new DiagnosticResult(ReportCompilationDiagnosticAnalyzer.Descriptor) }, + }.RunAsync(); + } + + [Fact] + public async Task TestDiagnosticExplicitWithoutLocation() + { + await new CSharpAnalyzerTest + { + TestCode = @"class TestClass { }", + ExpectedDiagnostics = { new DiagnosticResult(ReportCompilationDiagnosticAnalyzer.Descriptor).WithNoLocation() }, + }.RunAsync(); + } + + [Fact] [WorkItem(207, "https://github.com/dotnet/roslyn-sdk/issues/207")] public async Task TestDiagnosticDoesNotMatchIncorrectSpan() { @@ -73,6 +93,48 @@ public async Task TestDiagnosticDoesNotMatchIncorrectLocation() Assert.Equal(expected, exception.Message); } + [Fact] + public async Task TestDiagnosticDoesNotMatchMissingLocation() + { + var exception = await Assert.ThrowsAsync(async () => + { + await new CSharpAnalyzerTest + { + TestCode = @"class TestClass { }", + ExpectedDiagnostics = { new DiagnosticResult(HighlightBraceAnalyzer.Descriptor) }, + }.RunAsync(); + }); + + var expected = + "Expected:" + Environment.NewLine + + "A project diagnostic with No location" + Environment.NewLine + + "Actual:" + Environment.NewLine + + "// Test0.cs(1,17): warning Brace: message" + Environment.NewLine + + "GetCSharpResultAt(1, 17, HighlightBraceSpanAnalyzer.Brace)" + Environment.NewLine; + Assert.Equal(expected, exception.Message); + } + + [Fact] + public async Task TestDiagnosticDoesNotMatchNoLocation() + { + var exception = await Assert.ThrowsAsync(async () => + { + await new CSharpAnalyzerTest + { + TestCode = @"class TestClass { }", + ExpectedDiagnostics = { new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithNoLocation() }, + }.RunAsync(); + }); + + var expected = + "Expected:" + Environment.NewLine + + "A project diagnostic with No location" + Environment.NewLine + + "Actual:" + Environment.NewLine + + "// Test0.cs(1,17): warning Brace: message" + Environment.NewLine + + "GetCSharpResultAt(1, 17, HighlightBraceSpanAnalyzer.Brace)" + Environment.NewLine; + Assert.Equal(expected, exception.Message); + } + [Fact] public async Task TestZeroWidthDiagnosticMatchesCorrectSpan() { @@ -181,5 +243,26 @@ protected override Diagnostic CreateDiagnostic(SyntaxToken token) return Diagnostic.Create(Descriptor, token.GetLocation()); } } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + private class ReportCompilationDiagnosticAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor("Brace", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationAction(HandleCompilation); + } + + private void HandleCompilation(CompilationAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(Descriptor, location: null)); + } + } } }