diff --git a/NuGet.config b/NuGet.config
index 35f5827..26b8cc5 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,6 +1,7 @@
+
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/ApiDesign/DeclarePublicAPIAnalyzerTests.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/ApiDesign/DeclarePublicAPIAnalyzerTests.cs
index d5d95f0..976da44 100644
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/ApiDesign/DeclarePublicAPIAnalyzerTests.cs
+++ b/PublicApiAnalyzer/PublicApiAnalyzer.Test/ApiDesign/DeclarePublicAPIAnalyzerTests.cs
@@ -4,26 +4,20 @@
namespace PublicApiAnalyzer.Test.ApiDesign
{
using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CodeActions;
- using Microsoft.CodeAnalysis.CodeFixes;
- using Microsoft.CodeAnalysis.Diagnostics;
- using Microsoft.CodeAnalysis.Text;
+ using Microsoft.CodeAnalysis.CSharp.Testing;
+ using Microsoft.CodeAnalysis.Testing;
+ using Microsoft.CodeAnalysis.Testing.Verifiers;
using PublicApiAnalyzer.ApiDesign;
- using TestHelper;
using Xunit;
using Path = System.IO.Path;
- public class DeclarePublicAPIAnalyzerTests : CodeFixVerifier
+ public class DeclarePublicAPIAnalyzerTests
{
private string shippedText;
- private string shippedFilePath;
+ private string shippedFilePath = DeclarePublicAPIAnalyzer.ShippedFileName;
private string unshippedText;
- private string unshippedFilePath;
+ private string unshippedFilePath = DeclarePublicAPIAnalyzer.UnshippedFileName;
[Fact]
public async Task SimpleMissingTypeAsync()
@@ -37,14 +31,10 @@ private C() { }
this.shippedText = string.Empty;
this.unshippedText = string.Empty;
-
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
-
string fixedApi = "C" + Environment.NewLine;
- var updatedApi = await this.GetUpdatedApiAsync(source, 0, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedApi, expected).ConfigureAwait(false);
}
[Fact]
@@ -62,56 +52,40 @@ public void Method() { }
this.shippedText = string.Empty;
this.unshippedText = string.Empty;
+ var fixedApi = @"C
+C.ArrowExpressionProperty.get -> int
+C.C() -> void
+C.Field -> int
+C.Method() -> void
+C.Property.get -> int
+C.Property.set -> void
+";
DiagnosticResult[] expected =
{
// Test0.cs(2,14): error RS0016: Symbol 'C' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("C").WithLocation(2, 14),
// Test0.cs(2,14): warning RS0016: Symbol 'implicit constructor for C' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("implicit constructor for C").WithLocation(2, 14),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("implicit constructor for C").WithLocation(2, 14),
// Test0.cs(4,16): error RS0016: Symbol 'Field' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Field").WithLocation(4, 16),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Field").WithLocation(4, 16),
// Test0.cs(5,27): error RS0016: Symbol 'Property.get' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.get").WithLocation(5, 27),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.get").WithLocation(5, 27),
// Test0.cs(5,32): error RS0016: Symbol 'Property.set' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.set").WithLocation(5, 32),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Property.set").WithLocation(5, 32),
// Test0.cs(6,17): error RS0016: Symbol 'Method' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method").WithLocation(6, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method").WithLocation(6, 17),
// Test0.cs(7,43): error RS0016: Symbol 'ArrowExpressionProperty.get' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("ArrowExpressionProperty.get").WithLocation(7, 43),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("ArrowExpressionProperty.get").WithLocation(7, 43),
};
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
-
- string fixedApi = "C" + Environment.NewLine;
- var updatedApi = await this.GetUpdatedApiAsync(source, 0, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
-
- fixedApi = "C.C() -> void" + Environment.NewLine;
- updatedApi = await this.GetUpdatedApiAsync(source, 1, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
-
- fixedApi = "C.Field -> int" + Environment.NewLine;
- updatedApi = await this.GetUpdatedApiAsync(source, 2, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
-
- fixedApi = "C.Property.get -> int" + Environment.NewLine;
- updatedApi = await this.GetUpdatedApiAsync(source, 3, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
-
- fixedApi = "C.Property.set -> void" + Environment.NewLine;
- updatedApi = await this.GetUpdatedApiAsync(source, 4, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
-
- fixedApi = "C.Method() -> void" + Environment.NewLine;
- updatedApi = await this.GetUpdatedApiAsync(source, 5, CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(fixedApi, updatedApi.ToString());
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedApi, expected).ConfigureAwait(false);
}
[Fact]
@@ -130,9 +104,9 @@ private C() { }
this.unshippedText = string.Empty;
// PublicAPI.Shipped.txt(3,1): warning RS0017: Symbol 'C -> void()' is part of the declared API, but is either not public or could not be found
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C -> void()").WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 3, 1);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C -> void()").WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 3, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -149,7 +123,7 @@ public class C
C.C() -> void";
this.unshippedText = string.Empty;
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -166,7 +140,7 @@ public class C
this.unshippedText = @"
C.C() -> void";
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -184,9 +158,9 @@ public class C
// Test0.cs(2,14): warning RS0016: Symbol 'implicit constructor for C' is not part of the declared API.
string arg = string.Format(RoslynDiagnosticsResources.PublicImplicitConstructorErrorMessageName, "C");
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 14);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 14);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -204,9 +178,9 @@ public struct C
// Test0.cs(2,15): warning RS0016: Symbol 'implicit constructor for C' is not part of the declared API.
string arg = string.Format(RoslynDiagnosticsResources.PublicImplicitConstructorErrorMessageName, "C");
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 15);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 15);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -228,9 +202,9 @@ public C(int value)
// Test0.cs(2,15): warning RS0016: Symbol 'implicit constructor for C' is not part of the declared API.
string arg = string.Format(RoslynDiagnosticsResources.PublicImplicitConstructorErrorMessageName, "C");
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 15);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments(arg).WithLocation(2, 15);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -249,9 +223,9 @@ private C() { }
this.unshippedText = string.Empty;
// PublicAPI.Shipped.txt(3,1): warning RS0017: Symbol 'C.C() -> void' is part of the declared API, but is either not public or could not be found
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C.C() -> void").WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 3, 1);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C.C() -> void").WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 3, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -276,7 +250,7 @@ public void Method() { }
";
this.unshippedText = string.Empty;
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -302,7 +276,7 @@ public void Method() { }
C.Method() -> void
";
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -327,7 +301,7 @@ public enum E
E.V3 = 3 -> E
";
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -354,7 +328,7 @@ public class C
{DeclarePublicAPIAnalyzer.RemovedApiPrefix}C.Method() -> void
";
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -378,10 +352,10 @@ public class C
this.unshippedText = string.Empty;
- // error RS0024: The contents of the public API files are invalid: The shipped API file can't have removed members
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.PublicApiFilesInvalid).WithArguments(DeclarePublicAPIAnalyzer.InvalidReasonShippedCantHaveRemoved);
+ // error RS0024: The contents of the public API files are invalid: The shipped API file can't have removed members
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.PublicApiFilesInvalid).WithArguments(DeclarePublicAPIAnalyzer.InvalidReasonShippedCantHaveRemoved);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -406,12 +380,12 @@ public class C
this.unshippedText = string.Empty;
// Warning RS0025: The symbol 'C.Property.get -> int' appears more than once in the public API files.
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
.WithArguments("C.Property.get -> int")
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 6, 1)
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 4, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -436,12 +410,12 @@ public class C
C.Property.get -> int";
// Warning RS0025: The symbol 'C.Property.get -> int' appears more than once in the public API files.
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.DuplicateSymbolInApiFiles)
.WithArguments("C.Property.get -> int")
.WithLocation(DeclarePublicAPIAnalyzer.UnshippedFileName, 2, 1)
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 4, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -468,11 +442,11 @@ private void Method() { }
this.unshippedText = string.Empty;
// PublicAPI.Shipped.txt(7,1): warning RS0017: Symbol 'C.Method() -> void' is part of the declared API, but is either not public or could not be found
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule)
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule)
.WithArguments("C.Method() -> void")
.WithLocation(DeclarePublicAPIAnalyzer.ShippedFileName, 7, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -503,9 +477,9 @@ private void Method() { }
this.unshippedFilePath = Path.Combine(tempPath, DeclarePublicAPIAnalyzer.UnshippedFileName);
// <%TEMP_PATH%>\PublicAPI.Shipped.txt(7,1): warning RS0017: Symbol 'C.Method() -> void' is part of the declared API, but is either not public or could not be found
- var expected = this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C.Method() -> void").WithLocation(this.shippedFilePath, 7, 1);
+ var expected = new DiagnosticResult(DeclarePublicAPIAnalyzer.RemoveDeletedApiRule).WithArguments("C.Method() -> void").WithLocation(this.shippedFilePath, 7, 1);
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -526,7 +500,7 @@ public async Task TypeForwardsAreProcessed1Async()
";
this.unshippedText = string.Empty;
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -554,7 +528,7 @@ abstract System.StringComparer.GetHashCode(string obj) -> int (forwarded, contai
";
this.unshippedText = $@"";
- await this.VerifyCSharpDiagnosticAsync(source, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source).ConfigureAwait(false);
}
[Fact]
@@ -616,28 +590,28 @@ public void Method5(string p1 = null) { }
DiagnosticResult[] expected =
{
// Test0.cs(5,17): warning RS0016: Symbol 'Method1' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method1").WithLocation(5, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method1").WithLocation(5, 17),
// Test0.cs(8,17): warning RS0016: Symbol 'Method1' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method1").WithLocation(8, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method1").WithLocation(8, 17),
// Test0.cs(20,17): warning RS0026: Symbol 'Method4' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method4", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(20, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method4", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(20, 17),
// Test0.cs(25,17): warning RS0016: Symbol 'Method5' is not part of the declared API.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method5").WithLocation(25, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.DeclareNewApiRule).WithArguments("Method5").WithLocation(25, 17),
// Test0.cs(25,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(25, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(25, 17),
// Test0.cs(26,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(26, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(26, 17),
// Test0.cs(27,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(27, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri).WithLocation(27, 17),
};
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -706,19 +680,19 @@ public void Method6(string p1) { } // unshipped
DiagnosticResult[] expected =
{
// Test0.cs(21,17): warning RS0027: Symbol 'Method4' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method4", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(21, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method4", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(21, 17),
// Test0.cs(26,17): warning RS0027: Symbol 'Method5' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(26, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method5", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(26, 17),
// Test0.cs(31,17): warning RS0027: Symbol 'Method6' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method6", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(31, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method6", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(31, 17),
// Test0.cs(32,17): warning RS0027: Symbol 'Method6' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/documentation/RS0026.md' for details.
- this.CSharpDiagnostic(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method6", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(32, 17),
+ new DiagnosticResult(DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters).WithArguments("Method6", DeclarePublicAPIAnalyzer.OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri).WithLocation(32, 17),
};
- await this.VerifyCSharpDiagnosticAsync(source, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyAnalyzerAsync(source, expected).ConfigureAwait(false);
}
[Fact]
@@ -731,7 +705,7 @@ public class C
public int Property { get; set; }
public void Method() { }
public int ArrowExpressionProperty => 0;
- public int NewField; // Newly added field, not in current public API.
+ public int {|RS0016:NewField|}; // Newly added field, not in current public API.
}
";
@@ -752,7 +726,7 @@ public void Method() { }
C.Property.get -> int
C.Property.set -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
@@ -766,7 +740,7 @@ public class C
public int Property { get; set; }
public void Method() { }
public int ArrowExpressionProperty => 0;
- public int NewField;
+ public int {|RS0016:NewField|};
}
";
this.shippedText = string.Empty;
@@ -775,7 +749,7 @@ public void Method() { }
C.C() -> void
C.Field -> int
C.Method() -> void
-C.ObsoleteField -> int
+{|RS0017:C.ObsoleteField -> int|}
C.Property.get -> int
C.Property.set -> void";
var fixedUnshippedText = @"C
@@ -787,14 +761,14 @@ public void Method() { }
C.Property.get -> int
C.Property.set -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestSimpleMissingTypeFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
}
@@ -805,19 +779,19 @@ private C() { }
var fixedUnshippedText = @"C
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestMultipleMissingTypeAndMemberFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public int Field;
+ public int {|RS0016:Field|};
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = string.Empty;
@@ -828,7 +802,7 @@ public class C2 { }
C2.C2() -> void
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
@@ -838,17 +812,17 @@ public async Task TestChangingMethodSignatureForAnUnshippedMethodFixAsync()
public class C
{
private C() { }
- public void Method(int p1){ }
+ public void {|RS0016:Method|}(int p1){ }
}
";
this.shippedText = @"C";
// previously method had no params, so the fix should remove the previous overload.
- this.unshippedText = @"C.Method() -> void";
+ this.unshippedText = @"{|RS0017:C.Method() -> void|}";
var fixedUnshippedText = @"C.Method(int p1) -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
@@ -860,7 +834,7 @@ public class C
private C() { }
public void Method(int p1){ }
public void Method(int p1, int p2){ }
- public void Method(char p1){ }
+ public void {|RS0016:Method|}(char p1){ }
}
";
@@ -869,10 +843,10 @@ public void Method(char p1){ }
C.Method(int p1, int p2) -> void";
// previously method had no params, so the fix should remove the previous overload.
- this.unshippedText = @"C.Method() -> void";
+ this.unshippedText = @"{|RS0017:C.Method() -> void|}";
var fixedUnshippedText = @"C.Method(char p1) -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
@@ -882,7 +856,7 @@ public async Task TestAddingNewPublicOverloadFixAsync()
public class C
{
private C() { }
- public void Method(){ }
+ public void {|RS0016:Method|}(){ }
internal void Method(int p1){ }
internal void Method(int p1, int p2){ }
public void Method(char p1){ }
@@ -896,23 +870,23 @@ public void Method(char p1){ }
C.Method() -> void
C.Method(char p1) -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestMissingTypeAndMemberAndNestedMembersFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public int Field;
+ public int {|RS0016:Field|};
public class CC
{
- public int Field;
+ public int {|RS0016:Field|};
}
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = @"C.CC
@@ -925,23 +899,23 @@ public class C2 { }
C2.C2() -> void
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestMissingNestedGenericMembersAndStaleMembersFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public CC Field;
+ public CC {|RS0016:Field|};
private C3.C4 Field2;
private C3.C4 Method(C3.C4 p1) { throw new System.NotImplementedException(); }
public class CC
{
- public int Field;
- public CC Field2;
+ public int {|RS0016:Field|};
+ public CC {|RS0016:Field2|};
}
public class C3
@@ -949,7 +923,7 @@ public class C3
public class C4 { }
}
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = string.Empty;
@@ -959,8 +933,8 @@ public class C2 { }
C.C3.C4.C4() -> void
C.CC
C.CC.CC() -> void
-C.Field2 -> C.C3.C4
-C.Method(C.C3.C4 p1) -> C.C3.C4
+{|RS0017:C.Field2 -> C.C3.C4|}
+{|RS0017:C.Method(C.C3.C4 p1) -> C.C3.C4|}
";
var fixedUnshippedText = @"C
C.C3
@@ -976,23 +950,23 @@ public class C2 { }
C2.C2() -> void
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestWithExistingUnshippedNestedMembersFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public int Field;
+ public int {|RS0016:Field|};
public class CC
{
public int Field;
}
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = string.Empty;
@@ -1007,7 +981,7 @@ public class C2 { }
C2
C2.C2() -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
@@ -1017,7 +991,7 @@ public async Task TestWithExistingUnshippedNestedGenericMembersFixAsync()
public class C
{
private C() { }
- public class CC
+ public class {|RS0016:CC|}
{
public int Field;
}
@@ -1042,23 +1016,23 @@ private CC() { }
C.CC
C.CC.Field -> int";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestWithExistingShippedNestedMembersFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public int Field;
+ public int {|RS0016:Field|};
public class CC
{
public int Field;
}
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = @"C.CC
@@ -1071,46 +1045,46 @@ public class C2 { }
C2.C2() -> void
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestOnlyRemoveStaleSiblingEntriesFixAsync()
{
var source = @"
-public class C
+public class {|RS0016:C|}
{
private C() { }
- public int Field;
+ public int {|RS0016:Field|};
public class CC
{
private int Field; // This has a stale public API entry, but this shouldn't be removed unless we attempt to add a public API entry for a sibling.
}
}
-public class C2 { }
+public class {|RS0016:{|RS0016:C2|}|} { }
";
this.shippedText = string.Empty;
this.unshippedText = @"
C.CC
C.CC.CC() -> void
-C.CC.Field -> int";
+{|RS0017:C.CC.Field -> int|}";
var fixedUnshippedText = @"C
C.CC
C.CC.CC() -> void
-C.CC.Field -> int
+{|RS0017:C.CC.Field -> int|}
C.Field -> int
C2
C2.C2() -> void";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, numberOfFixAllIterations: 2, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Fact]
public async Task TestAddTrailingNewlineByDefaultAsync()
{
var source = @"
-public class C
+public class {|RS0016:{|RS0016:C|}|}
{
}
";
@@ -1121,7 +1095,7 @@ public class C
C.C() -> void
";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
[Theory]
@@ -1134,7 +1108,7 @@ public async Task TestPreserveTrailingNewlineAsync(string originalEndOfFile, str
public class C
{
public int Property { get; }
- public int NewField; // Newly added field, not in current public API.
+ public int {|RS0016:NewField|}; // Newly added field, not in current public API.
}
";
@@ -1147,70 +1121,59 @@ public class C
C.NewField -> int
C.Property.get -> int{expectedEndOfFile}";
- await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpUnshippedFileFixAsync(source, fixedUnshippedText).ConfigureAwait(false);
}
- protected override IEnumerable GetCSharpDiagnosticAnalyzers()
+ private async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
{
- yield return new DeclarePublicAPIAnalyzer();
- }
-
- protected override CodeFixProvider GetCSharpCodeFixProvider()
- {
- return new DeclarePublicAPIFix();
- }
-
- protected override string GetShippedPublicApi()
- {
- return this.shippedText;
- }
+ var test = new CSharpCodeFixTest
+ {
+ TestCode = source,
+ };
- protected override string GetShippedPublicApiFilePath()
- {
- return this.shippedFilePath;
- }
+ if (this.unshippedText != null)
+ {
+ test.TestState.AdditionalFiles.Add((this.unshippedFilePath, this.unshippedText));
+ }
- protected override string GetUnshippedPublicApi()
- {
- return this.unshippedText;
- }
+ if (this.shippedText != null)
+ {
+ test.TestState.AdditionalFiles.Add((this.shippedFilePath, this.shippedText));
+ }
- protected override string GetUnshippedPublicApiFilePath()
- {
- return this.unshippedFilePath;
+ test.Exclusions &= ~AnalysisExclusions.GeneratedCode;
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync().ConfigureAwait(false);
}
- private async Task VerifyCSharpUnshippedFileFixAsync(string source, string fixedUnshippedText, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default)
+ private async Task VerifyCSharpUnshippedFileFixAsync(string source, string fixedUnshippedText, params DiagnosticResult[] expected)
{
- VerifyCodeFixAsync verifyAsync =
- async (Project project, bool fixAll, CancellationToken ct) =>
+ var test = new CSharpCodeFixTest
+ {
+ TestState =
{
- var unshippedFile = project.AdditionalDocuments.Single(document => document.Name == DeclarePublicAPIAnalyzer.UnshippedFileName);
- Assert.Equal(fixedUnshippedText, (await unshippedFile.GetTextAsync(ct).ConfigureAwait(false)).ToString());
- };
-
- await this.VerifyCSharpFixAsync(source, verifyAsync, numberOfFixAllIterations: numberOfFixAllIterations, cancellationToken: CancellationToken.None).ConfigureAwait(false);
- }
+ Sources = { source },
+ AdditionalFiles =
+ {
+ (this.shippedFilePath, this.shippedText ?? string.Empty),
+ (this.unshippedFilePath, this.unshippedText ?? string.Empty),
+ },
+ },
+ FixedState =
+ {
+ Sources = { source },
+ AdditionalFiles =
+ {
+ (this.shippedFilePath, this.shippedText ?? string.Empty),
+ (this.unshippedFilePath, fixedUnshippedText),
+ },
+ InheritanceMode = StateInheritanceMode.Explicit,
+ },
+ };
- private async Task GetUpdatedApiAsync(string source, int diagnosticIndex, CancellationToken cancellationToken)
- {
- var fixes = await this.GetOfferedCSharpFixesAsync(source, diagnosticIndex, cancellationToken).ConfigureAwait(false);
- Assert.Equal(1, fixes.Item2.Length);
-
- var operations = await fixes.Item2[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false);
- Assert.Equal(1, operations.Length);
- ApplyChangesOperation operation = operations[0] as ApplyChangesOperation;
- Assert.NotNull(operation);
-
- var oldSolution = fixes.Item1;
- var newSolution = operation.ChangedSolution;
- var solutionChanges = newSolution.GetChanges(oldSolution);
- var projectChanges = solutionChanges.GetProjectChanges().Single();
- var changedDocumentId = projectChanges.GetChangedAdditionalDocuments().Single();
- var newDocument = projectChanges.NewProject.GetAdditionalDocument(changedDocumentId);
- var newText = await newDocument.GetTextAsync(CancellationToken.None).ConfigureAwait(false);
-
- return newText;
+ test.Exclusions &= ~AnalysisExclusions.GeneratedCode;
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync().ConfigureAwait(false);
}
}
}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/CodeFixVerifier.Helper.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/CodeFixVerifier.Helper.cs
deleted file mode 100644
index 7fa66d3..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/CodeFixVerifier.Helper.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CodeActions;
- using Microsoft.CodeAnalysis.Formatting;
- using Microsoft.CodeAnalysis.Simplification;
- using Microsoft.CodeAnalysis.Text;
-
- ///
- /// Diagnostic Producer class with extra methods dealing with applying code fixes.
- /// All methods are static
- ///
- public abstract partial class CodeFixVerifier : DiagnosticVerifier
- {
- ///
- /// Apply the inputted to the inputted document.
- /// Meant to be used to apply code fixes.
- ///
- /// The to apply the fix on
- /// A that will be applied to the
- /// .
- /// The that the task will observe.
- /// A with the changes from the .
- private static async Task ApplyFixAsync(Project project, CodeAction codeAction, CancellationToken cancellationToken)
- {
- var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false);
- var solution = operations.OfType().Single().ChangedSolution;
- return solution.GetProject(project.Id);
- }
-
- ///
- /// Compare two collections of s, and return a list of any new diagnostics that appear
- /// only in the second collection.
- ///
- /// Considers to be the same if they have the same s.
- /// In the case of multiple diagnostics with the same in a row, this method may not
- /// necessarily return the new one.
- ///
- ///
- /// The s that existed in the code before the code fix was
- /// applied.
- /// The s that exist in the code after the code fix was
- /// applied.
- /// A list of s that only surfaced in the code after the code fix was
- /// applied.
- private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics)
- {
- var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
- var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
-
- int oldIndex = 0;
- int newIndex = 0;
-
- while (newIndex < newArray.Length)
- {
- if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id)
- {
- ++oldIndex;
- ++newIndex;
- }
- else
- {
- yield return newArray[newIndex++];
- }
- }
- }
-
- ///
- /// Get the existing compiler diagnostics on the input document.
- ///
- /// The to run the compiler diagnostic analyzers on.
- /// The that the task will observe.
- /// The compiler diagnostics that were found in the code.
- private static async Task> GetCompilerDiagnosticsAsync(Project project, CancellationToken cancellationToken)
- {
- var allDiagnostics = ImmutableArray.Create();
-
- foreach (var document in project.Documents)
- {
- var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
- allDiagnostics = allDiagnostics.AddRange(semanticModel.GetDiagnostics(cancellationToken: cancellationToken));
- }
-
- return allDiagnostics;
- }
-
- ///
- /// Given a document, turn it into a string based on the syntax root.
- ///
- /// The to be converted to a string.
- /// The that the task will observe.
- /// A string containing the syntax of the after formatting.
- private static async Task GetStringFromDocumentAsync(Document document, CancellationToken cancellationToken)
- {
- var simplifiedDoc = await Simplifier.ReduceAsync(document, Simplifier.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
- var formatted = await Formatter.FormatAsync(simplifiedDoc, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
- var sourceText = await formatted.GetTextAsync(cancellationToken).ConfigureAwait(false);
- return sourceText.ToString();
- }
-
- ///
- /// Implements a workaround for issue #936, force re-parsing to get the same sort of syntax tree as the original document.
- ///
- /// The project to update.
- /// The .
- /// The updated .
- private static async Task RecreateProjectDocumentsAsync(Project project, CancellationToken cancellationToken)
- {
- foreach (var documentId in project.DocumentIds)
- {
- var document = project.GetDocument(documentId);
- document = await RecreateDocumentAsync(document, cancellationToken).ConfigureAwait(false);
- project = document.Project;
- }
-
- return project;
- }
-
- private static async Task RecreateDocumentAsync(Document document, CancellationToken cancellationToken)
- {
- var newText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
- newText = newText.WithChanges(new TextChange(new TextSpan(0, 0), " "));
- newText = newText.WithChanges(new TextChange(new TextSpan(0, 1), string.Empty));
- return document.WithText(newText);
- }
-
- ///
- /// Formats the whitespace in all documents of the specified .
- ///
- /// The project to update.
- /// The .
- /// The updated .
- private static async Task ReformatProjectDocumentsAsync(Project project, CancellationToken cancellationToken)
- {
- foreach (var documentId in project.DocumentIds)
- {
- var document = project.GetDocument(documentId);
- document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
- project = document.Project;
- }
-
- return project;
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResult.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResult.cs
deleted file mode 100644
index d0fa59c..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResult.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System;
- using Microsoft.CodeAnalysis;
-
- ///
- /// Structure that stores information about a appearing in a source.
- ///
- public struct DiagnosticResult
- {
- private static readonly object[] EmptyArguments = new object[0];
-
- private DiagnosticResultLocation[] locations;
- private string message;
-
- public DiagnosticResult(DiagnosticDescriptor descriptor)
- : this()
- {
- this.Id = descriptor.Id;
- this.Severity = descriptor.DefaultSeverity;
- this.MessageFormat = descriptor.MessageFormat;
- }
-
- public DiagnosticResultLocation[] Locations
- {
- get
- {
- if (this.locations == null)
- {
- this.locations = new DiagnosticResultLocation[] { };
- }
-
- return this.locations;
- }
-
- set
- {
- this.locations = value;
- }
- }
-
- public DiagnosticSeverity Severity
- {
- get; set;
- }
-
- public string Id
- {
- get; set;
- }
-
- public string Message
- {
- get
- {
- if (this.message != null)
- {
- return this.message;
- }
-
- if (this.MessageFormat != null)
- {
- return string.Format(this.MessageFormat.ToString(), this.MessageArguments ?? EmptyArguments);
- }
-
- return null;
- }
-
- set
- {
- this.message = value;
- }
- }
-
- public LocalizableString MessageFormat
- {
- get;
- set;
- }
-
- public object[] MessageArguments
- {
- get;
- set;
- }
-
- public string Path
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Path : string.Empty;
- }
- }
-
- public int Line
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Line : -1;
- }
- }
-
- public int Column
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Column : -1;
- }
- }
-
- public DiagnosticResult WithArguments(params object[] arguments)
- {
- DiagnosticResult result = this;
- result.MessageArguments = arguments;
- return result;
- }
-
- public DiagnosticResult WithMessageFormat(LocalizableString messageFormat)
- {
- DiagnosticResult result = this;
- result.MessageFormat = messageFormat;
- return result;
- }
-
- public DiagnosticResult WithLocation(int line, int column)
- {
- return this.WithLocation("Test0.cs", line, column);
- }
-
- public DiagnosticResult WithLocation(string path, int line, int column)
- {
- DiagnosticResult result = this;
- Array.Resize(ref result.locations, (result.locations?.Length ?? 0) + 1);
- result.locations[result.locations.Length - 1] = new DiagnosticResultLocation(path, line, column);
- return result;
- }
-
- public DiagnosticResult WithLineOffset(int offset)
- {
- DiagnosticResult result = this;
- Array.Resize(ref result.locations, result.locations?.Length ?? 0);
- for (int i = 0; i < result.locations.Length; i++)
- {
- result.locations[i].Line += offset;
- }
-
- return result;
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResultLocation.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResultLocation.cs
deleted file mode 100644
index a3cce50..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResultLocation.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System;
-
- ///
- /// Location where the diagnostic appears, as determined by path, line number, and column number.
- ///
- public struct DiagnosticResultLocation
- {
- public string Path;
- public int Line;
- public int Column;
-
- public DiagnosticResultLocation(string path, int line, int column)
- {
- if (line < 0 && column < 0)
- {
- throw new ArgumentOutOfRangeException("At least one of line and column must be > 0");
- }
-
- if (line < -1 || column < -1)
- {
- throw new ArgumentOutOfRangeException("Both line and column must be >= -1");
- }
-
- this.Path = path;
- this.Line = line;
- this.Column = column;
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs
deleted file mode 100644
index 1889506..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
- using Microsoft.CodeAnalysis.Diagnostics;
- using Microsoft.CodeAnalysis.Text;
- using PublicApiAnalyzer;
- using PublicApiAnalyzer.ApiDesign;
- using PublicApiAnalyzer.Test.Helpers;
-
- ///
- /// Class for turning strings into documents and getting the diagnostics on them.
- /// All methods are static.
- ///
- public abstract partial class DiagnosticVerifier
- {
- private static readonly string DefaultFilePathPrefix = "Test";
- private static readonly string CSharpDefaultFileExt = "cs";
- private static readonly string VisualBasicDefaultExt = "vb";
- private static readonly string CSharpDefaultFilePath = DefaultFilePathPrefix + 0 + "." + CSharpDefaultFileExt;
- private static readonly string VisualBasicDefaultFilePath = DefaultFilePathPrefix + 0 + "." + VisualBasicDefaultExt;
- private static readonly string TestProjectName = "TestProject";
-
- ///
- /// Given an analyzer and a collection of documents to apply it to, run the analyzer and gather an array of
- /// diagnostics found. The returned diagnostics are then ordered by location in the source documents.
- ///
- /// The analyzer to run on the documents.
- /// The s that the analyzer will be run on.
- /// The that the task will observe.
- /// A collection of s that surfaced in the source code, sorted by
- /// .
- protected static async Task> GetSortedDiagnosticsFromDocumentsAsync(ImmutableArray analyzers, Document[] documents, CancellationToken cancellationToken)
- {
- var projects = new HashSet();
- foreach (var document in documents)
- {
- projects.Add(document.Project);
- }
-
- var diagnostics = ImmutableArray.CreateBuilder();
- foreach (var project in projects)
- {
- var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
- var compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, cancellationToken);
- var compilerDiagnostics = compilation.GetDiagnostics(cancellationToken);
- var compilerErrors = compilerDiagnostics.Where(i => i.Severity == DiagnosticSeverity.Error);
- var diags = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false);
- var allDiagnostics = await compilationWithAnalyzers.GetAllDiagnosticsAsync().ConfigureAwait(false);
- var failureDiagnostics = allDiagnostics.Where(diagnostic => diagnostic.Id == "AD0001");
- foreach (var diag in diags.Concat(compilerErrors).Concat(failureDiagnostics))
- {
- if (diag.Location == Location.None || !diag.Location.IsInSource)
- {
- diagnostics.Add(diag);
- }
- else
- {
- for (int i = 0; i < documents.Length; i++)
- {
- var document = documents[i];
- var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
- if (tree == diag.Location.SourceTree)
- {
- diagnostics.Add(diag);
- break;
- }
- }
- }
- }
- }
-
- var results = SortDistinctDiagnostics(diagnostics);
- return results.ToImmutableArray();
- }
-
- ///
- /// Create a from a string through creating a project that contains it.
- ///
- /// Classes in the form of a string.
- /// The language the source classes are in. Values may be taken from the
- /// class.
- /// The file name for the document, or to generate a default
- /// filename according to the specified .
- /// A created from the source string.
- protected Document CreateDocument(string source, string language = LanguageNames.CSharp, string fileName = null)
- {
- string[] filenames = null;
- if (fileName != null)
- {
- filenames = new[] { fileName };
- }
-
- return this.CreateProject(new[] { source }, language, filenames).Documents.Single();
- }
-
- ///
- /// Creates a solution that will be used as parent for the sources that need to be checked.
- ///
- /// The project identifier to use.
- /// The language for which the solution is being created.
- /// The created solution.
- protected virtual Solution CreateSolution(ProjectId projectId, string language)
- {
- var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true);
-
- Solution solution = new AdhocWorkspace()
- .CurrentSolution
- .AddProject(projectId, TestProjectName, TestProjectName, language)
- .WithProjectCompilationOptions(projectId, compilationOptions)
- .AddMetadataReference(projectId, MetadataReferences.CorlibReference)
- .AddMetadataReference(projectId, MetadataReferences.SystemReference)
- .AddMetadataReference(projectId, MetadataReferences.SystemCoreReference)
- .AddMetadataReference(projectId, MetadataReferences.CSharpSymbolsReference)
- .AddMetadataReference(projectId, MetadataReferences.CodeAnalysisReference);
-
- var publicApi = this.GetUnshippedPublicApi();
- if (publicApi != null)
- {
- var documentId = DocumentId.CreateNewId(projectId);
- solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.UnshippedFileName, publicApi, filePath: this.GetUnshippedPublicApiFilePath());
- }
-
- publicApi = this.GetShippedPublicApi();
- if (publicApi != null)
- {
- var documentId = DocumentId.CreateNewId(projectId);
- solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.ShippedFileName, publicApi, filePath: this.GetShippedPublicApiFilePath());
- }
-
- ParseOptions parseOptions = solution.GetProject(projectId).ParseOptions;
- return solution.WithProjectParseOptions(projectId, parseOptions.WithDocumentationMode(DocumentationMode.Diagnose));
- }
-
- ///
- /// Gets the diagnostics that will be suppressed.
- ///
- /// A collection of diagnostic identifiers.
- protected virtual IEnumerable GetDisabledDiagnostics()
- {
- return Enumerable.Empty();
- }
-
- ///
- /// Gets the content of the settings file to use.
- ///
- /// The contents of the settings file to use.
- protected virtual string GetUnshippedPublicApi()
- {
- return null;
- }
-
- protected virtual string GetUnshippedPublicApiFilePath()
- {
- return null;
- }
-
- ///
- /// Gets the content of the settings file to use.
- ///
- /// The contents of the settings file to use.
- protected virtual string GetShippedPublicApi()
- {
- return null;
- }
-
- protected virtual string GetShippedPublicApiFilePath()
- {
- return null;
- }
-
- protected DiagnosticResult CSharpDiagnostic(string diagnosticId = null)
- {
- var analyzers = this.GetCSharpDiagnosticAnalyzers();
- var supportedDiagnostics = analyzers.SelectMany(analyzer => analyzer.SupportedDiagnostics);
- if (diagnosticId == null)
- {
- return this.CSharpDiagnostic(supportedDiagnostics.Single());
- }
- else
- {
- return this.CSharpDiagnostic(supportedDiagnostics.Single(i => i.Id == diagnosticId));
- }
- }
-
- protected DiagnosticResult CSharpDiagnostic(DiagnosticDescriptor descriptor)
- {
- return new DiagnosticResult(descriptor);
- }
-
- ///
- /// Create a project using the input strings as sources.
- ///
- ///
- /// This method first creates a by calling , and then
- /// applies compilation options to the project by calling .
- ///
- /// Classes in the form of strings.
- /// The language the source classes are in. Values may be taken from the
- /// class.
- /// The filenames or null if the default filename should be used
- /// A created out of the s created from the source
- /// strings.
- protected Project CreateProject(string[] sources, string language = LanguageNames.CSharp, string[] filenames = null)
- {
- Project project = this.CreateProjectImpl(sources, language, filenames);
- return this.ApplyCompilationOptions(project);
- }
-
- ///
- /// Create a project using the input strings as sources.
- ///
- /// Classes in the form of strings.
- /// The language the source classes are in. Values may be taken from the
- /// class.
- /// The filenames or null if the default filename should be used
- /// A created out of the s created from the source
- /// strings.
- protected virtual Project CreateProjectImpl(string[] sources, string language, string[] filenames)
- {
- string fileNamePrefix = DefaultFilePathPrefix;
- string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
-
- var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
- var solution = this.CreateSolution(projectId, language);
-
- int count = 0;
- for (int i = 0; i < sources.Length; i++)
- {
- string source = sources[i];
- var newFileName = filenames?[i] ?? fileNamePrefix + count + "." + fileExt;
- var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
- solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
- count++;
- }
-
- return solution.GetProject(projectId);
- }
-
- ///
- /// Applies compilation options to a project.
- ///
- ///
- /// The default implementation configures the project by enabling all supported diagnostics of analyzers
- /// included in as well as AD0001. After configuring these
- /// diagnostics, any diagnostic IDs indicated in are explictly supressed
- /// using .
- ///
- /// The project.
- /// The modified project.
- protected virtual Project ApplyCompilationOptions(Project project)
- {
- var analyzers = this.GetCSharpDiagnosticAnalyzers();
-
- var supportedDiagnosticsSpecificOptions = new Dictionary();
- foreach (var analyzer in analyzers)
- {
- foreach (var diagnostic in analyzer.SupportedDiagnostics)
- {
- // make sure the analyzers we are testing are enabled
- supportedDiagnosticsSpecificOptions[diagnostic.Id] = ReportDiagnostic.Default;
- }
- }
-
- // Report exceptions during the analysis process as errors
- supportedDiagnosticsSpecificOptions.Add("AD0001", ReportDiagnostic.Error);
-
- foreach (var id in this.GetDisabledDiagnostics())
- {
- supportedDiagnosticsSpecificOptions[id] = ReportDiagnostic.Suppress;
- }
-
- // update the project compilation options
- var modifiedSpecificDiagnosticOptions = supportedDiagnosticsSpecificOptions.ToImmutableDictionary().SetItems(project.CompilationOptions.SpecificDiagnosticOptions);
- var modifiedCompilationOptions = project.CompilationOptions.WithSpecificDiagnosticOptions(modifiedSpecificDiagnosticOptions);
-
- Solution solution = project.Solution.WithProjectCompilationOptions(project.Id, modifiedCompilationOptions);
- return solution.GetProject(project.Id);
- }
-
- ///
- /// Sort s by location in source document.
- ///
- /// A collection of s to be sorted.
- /// A collection containing the input , sorted by
- /// and .
- private static Diagnostic[] SortDistinctDiagnostics(IEnumerable diagnostics)
- {
- return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ThenBy(d => d.Id).ToArray();
- }
-
- ///
- /// Given classes in the form of strings, their language, and an to apply to
- /// it, return the s found in the string after converting it to a
- /// .
- ///
- /// Classes in the form of strings.
- /// The language the source classes are in. Values may be taken from the
- /// class.
- /// The analyzers to be run on the sources.
- /// The that the task will observe.
- /// The filenames or null if the default filename should be used
- /// A collection of s that surfaced in the source code, sorted by
- /// .
- private Task> GetSortedDiagnosticsAsync(string[] sources, string language, ImmutableArray analyzers, CancellationToken cancellationToken, string[] filenames)
- {
- return GetSortedDiagnosticsFromDocumentsAsync(analyzers, this.GetDocuments(sources, language, filenames), cancellationToken);
- }
-
- ///
- /// Given an array of strings as sources and a language, turn them into a and return the
- /// documents and spans of it.
- ///
- /// Classes in the form of strings.
- /// The language the source classes are in. Values may be taken from the
- /// class.
- /// The filenames or null if the default filename should be used
- /// A collection of s representing the sources.
- private Document[] GetDocuments(string[] sources, string language, string[] filenames)
- {
- if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic)
- {
- throw new ArgumentException("Unsupported Language");
- }
-
- var project = this.CreateProject(sources, language, filenames);
- var documents = project.Documents.ToArray();
-
- if (sources.Length != documents.Length)
- {
- throw new SystemException("Amount of sources did not match amount of Documents created");
- }
-
- return documents;
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/MetadataReferences.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/MetadataReferences.cs
deleted file mode 100644
index 10ea839..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/MetadataReferences.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace PublicApiAnalyzer.Test.Helpers
-{
- using System.Collections.Immutable;
- using System.Linq;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
-
- ///
- /// Metadata references used to create test projects.
- ///
- internal static class MetadataReferences
- {
- internal static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location).WithAliases(ImmutableArray.Create("global", "corlib"));
- internal static readonly MetadataReference SystemReference = MetadataReference.CreateFromFile(typeof(System.Diagnostics.Debug).Assembly.Location).WithAliases(ImmutableArray.Create("global", "system"));
- internal static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
- internal static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location);
- internal static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location);
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestDiagnosticProvider.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestDiagnosticProvider.cs
deleted file mode 100644
index c7adb14..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestDiagnosticProvider.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace PublicApiAnalyzer.Test.Helpers
-{
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CodeFixes;
-
- internal sealed class TestDiagnosticProvider : FixAllContext.DiagnosticProvider
- {
- private ImmutableArray diagnostics;
-
- private TestDiagnosticProvider(ImmutableArray diagnostics)
- {
- this.diagnostics = diagnostics;
- }
-
- public override Task> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken)
- {
- return Task.FromResult>(this.diagnostics);
- }
-
- public override Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken)
- {
- return Task.FromResult(this.diagnostics.Where(i => i.Location.GetLineSpan().Path == document.Name));
- }
-
- public override Task> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken)
- {
- return Task.FromResult(this.diagnostics.Where(i => !i.Location.IsInSource));
- }
-
- internal static TestDiagnosticProvider Create(ImmutableArray diagnostics)
- {
- return new TestDiagnosticProvider(diagnostics);
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestXmlReferenceResolver.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestXmlReferenceResolver.cs
deleted file mode 100644
index db0a3d7..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestXmlReferenceResolver.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace PublicApiAnalyzer.Test.Helpers
-{
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.CompilerServices;
- using System.Text;
- using Microsoft.CodeAnalysis;
-
- internal class TestXmlReferenceResolver : XmlReferenceResolver
- {
- public Dictionary XmlReferences { get; } =
- new Dictionary();
-
- public override bool Equals(object other)
- {
- return ReferenceEquals(this, other);
- }
-
- public override int GetHashCode()
- {
- return RuntimeHelpers.GetHashCode(this);
- }
-
- public override Stream OpenRead(string resolvedPath)
- {
- string content;
- if (!this.XmlReferences.TryGetValue(resolvedPath, out content))
- {
- return null;
- }
-
- return new MemoryStream(Encoding.UTF8.GetBytes(content));
- }
-
- public override string ResolveReference(string path, string baseFilePath)
- {
- return path;
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj b/PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj
index 1967ac6..14095f5 100644
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj
+++ b/PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj
@@ -3,6 +3,8 @@
net452
+ true
+ true
@@ -16,6 +18,7 @@
+
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/CodeFixVerifier.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/CodeFixVerifier.cs
deleted file mode 100644
index f89ebe4..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/CodeFixVerifier.cs
+++ /dev/null
@@ -1,626 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CodeActions;
- using Microsoft.CodeAnalysis.CodeFixes;
- using Microsoft.CodeAnalysis.Diagnostics;
- using Microsoft.CodeAnalysis.Formatting;
- using PublicApiAnalyzer.Test.Helpers;
- using Xunit;
-
- ///
- /// Superclass of all unit tests made for diagnostics with code fixes.
- /// Contains methods used to verify correctness of code fixes.
- ///
- public abstract partial class CodeFixVerifier : DiagnosticVerifier
- {
- private const int DefaultNumberOfIncrementalIterations = -1000;
- private const int DefaultIndentationSize = 4;
- private const bool DefaultUseTabs = false;
-
- public CodeFixVerifier()
- {
- this.IndentationSize = DefaultIndentationSize;
- this.UseTabs = DefaultUseTabs;
- }
-
- protected delegate Task VerifyCodeFixAsync(Project project, bool fixAll, CancellationToken cancellationToken);
-
- ///
- /// Gets or sets the value of the to apply to the test
- /// workspace.
- ///
- ///
- /// The value of the to apply to the test workspace.
- ///
- public int IndentationSize
- {
- get;
- protected set;
- }
-
- ///
- /// Gets or sets a value indicating whether the option is applied to the
- /// test workspace.
- ///
- ///
- /// The value of the to apply to the test workspace.
- ///
- public bool UseTabs
- {
- get;
- protected set;
- }
-
- ///
- /// Returns the code fix being tested (C#) - to be implemented in non-abstract class.
- ///
- /// The to be used for C# code.
- protected abstract CodeFixProvider GetCSharpCodeFixProvider();
-
- ///
- /// Called to test a C# code fix when applied on the input source as a string.
- ///
- /// A class in the form of a string before the code fix was applied to it.
- /// A class in the form of a string after the code fix was applied to it.
- /// A class in the form of a string after the batch fixer was applied to it.
- /// The name of the file in the project before the code fix was applied.
- /// The name of the file in the project after the code fix was applied.
- /// Index determining which code fix to apply if there are multiple.
- /// A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.
- /// The number of iterations the incremental fixer will be called.
- /// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
- /// value.
- /// The number of iterations the Fix All fixer will be called. If this
- /// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.
- /// The that the task will observe.
- /// A representing the asynchronous operation.
- protected Task VerifyCSharpFixAsync(string oldSource, string newSource, string batchNewSource = null, string oldFileName = null, string newFileName = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
- {
- var batchNewSources = batchNewSource == null ? null : new[] { batchNewSource };
- var oldFileNames = oldFileName == null ? null : new[] { oldFileName };
- var newFileNames = newFileName == null ? null : new[] { newFileName };
- return this.VerifyCSharpFixAsync(new[] { oldSource }, new[] { newSource }, batchNewSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, numberOfFixAllIterations, cancellationToken);
- }
-
- ///
- /// Called to test a C# code fix when applied on the input source as a string.
- ///
- /// A class in the form of a string before the code fix was applied to it.
- /// A validation function to verify the results of the code fix.
- /// The name of the file in the project before the code fix was applied.
- /// Index determining which code fix to apply if there are multiple.
- /// A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.
- /// The number of iterations the incremental fixer will be called.
- /// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
- /// value.
- /// The number of iterations the Fix All fixer will be called. If this
- /// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.
- /// The that the task will observe.
- /// A representing the asynchronous operation.
- protected Task VerifyCSharpFixAsync(string oldSource, VerifyCodeFixAsync verifyFixedProjectAsync, string oldFileName = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
- {
- var oldFileNames = oldFileName == null ? null : new[] { oldFileName };
- return this.VerifyCSharpFixAsync(new[] { oldSource }, verifyFixedProjectAsync, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, numberOfFixAllIterations, cancellationToken);
- }
-
- ///
- /// Called to test a C# code fix when applied on the input source as a string.
- ///
- /// An array of sources in the form of strings before the code fix was applied to them.
- /// An array of sources in the form of strings after the code fix was applied to them.
- /// An array of sources in the form of a strings after the batch fixer was applied to them.
- /// An array of file names in the project before the code fix was applied.
- /// An array of file names in the project after the code fix was applied.
- /// Index determining which code fix to apply if there are multiple.
- /// A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.
- /// The number of iterations the incremental fixer will be called.
- /// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
- /// value.
- /// The number of iterations the Fix All fixer will be called. If this
- /// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.
- /// The that the task will observe.
- /// A representing the asynchronous operation.
- protected async Task VerifyCSharpFixAsync(string[] oldSources, string[] newSources, string[] batchNewSources = null, string[] oldFileNames = null, string[] newFileNames = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
- {
- var t1 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, newSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, FixEachAnalyzerDiagnosticAsync, cancellationToken).ConfigureAwait(false);
-
- var fixAllProvider = this.GetCSharpCodeFixProvider().GetFixAllProvider();
- Assert.NotEqual(WellKnownFixAllProviders.BatchFixer, fixAllProvider);
-
- if (fixAllProvider == null)
- {
- await t1;
- }
- else
- {
- if (Debugger.IsAttached)
- {
- await t1;
- }
-
- var t2 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t2;
- }
-
- var t3 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInProjectAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t3;
- }
-
- var t4 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInSolutionAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t4;
- }
-
- if (!Debugger.IsAttached)
- {
- // Allow the operations to run in parallel
- await t1;
- await t2;
- await t3;
- await t4;
- }
- }
- }
-
- ///
- /// Called to test a C# code fix when applied on the input source as a string.
- ///
- /// An array of sources in the form of strings before the code fix was applied to them.
- /// A validation function to verify the results of the code fix.
- /// An array of file names in the project before the code fix was applied.
- /// Index determining which code fix to apply if there are multiple.
- /// A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.
- /// The number of iterations the incremental fixer will be called.
- /// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
- /// value.
- /// The number of iterations the Fix All fixer will be called. If this
- /// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.
- /// The that the task will observe.
- /// A representing the asynchronous operation.
- protected async Task VerifyCSharpFixAsync(string[] oldSources, VerifyCodeFixAsync verifyFixedProjectAsync, string[] oldFileNames = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
- {
-#pragma warning disable SA1101 // Prefix local calls with this
- Func verifyAsync = (project, ct) => verifyFixedProjectAsync(project, false, ct);
-#pragma warning restore SA1101 // Prefix local calls with this
-
- var t1 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, FixEachAnalyzerDiagnosticAsync, verifyAsync, cancellationToken).ConfigureAwait(false);
-
- var fixAllProvider = this.GetCSharpCodeFixProvider().GetFixAllProvider();
- Assert.NotEqual(WellKnownFixAllProviders.BatchFixer, fixAllProvider);
-
- if (fixAllProvider == null)
- {
- await t1;
- }
- else
- {
- if (Debugger.IsAttached)
- {
- await t1;
- }
-
-#pragma warning disable SA1101 // Prefix local calls with this
- Func verifyFixAllAsync = (project, ct) => verifyFixedProjectAsync(project, true, ct);
-#pragma warning restore SA1101 // Prefix local calls with this
-
- var t2 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t2;
- }
-
- var t3 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInProjectAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t3;
- }
-
- var t4 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInSolutionAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
- if (Debugger.IsAttached)
- {
- await t4;
- }
-
- if (!Debugger.IsAttached)
- {
- // Allow the operations to run in parallel
- await t1;
- await t2;
- await t3;
- await t4;
- }
- }
- }
-
- ///
- /// Called to test a C# fix all provider when applied on the input source as a string.
- ///
- /// A class in the form of a string before the code fix was applied to it.
- /// A class in the form of a string after the code fix was applied to it.
- /// Index determining which code fix to apply if there are multiple.
- /// A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.
- /// The number of iterations the fixer will be called. If this value is less
- /// than 0, the negated value is treated as an upper limit as opposed to an exact value.
- /// The that the task will observe.
- /// A representing the asynchronous operation.
- protected async Task VerifyCSharpFixAllFixAsync(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
- {
- await this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), new[] { oldSource }, new[] { newSource }, null, null, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- /// Gets all offered code fixes for the specified diagnostic within the given source.
- ///
- /// A valid C# source file in the form of a string.
- /// Index determining which diagnostic to use for determining the offered code fixes. Uses the first diagnostic if null.
- /// The that the task will observe.
- /// The collection of offered code actions. This collection may be empty.
- protected async Task>> GetOfferedCSharpFixesAsync(string source, int? diagnosticIndex = null, CancellationToken cancellationToken = default(CancellationToken))
- {
- return await this.GetOfferedFixesInternalAsync(LanguageNames.CSharp, source, diagnosticIndex, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), cancellationToken).ConfigureAwait(false);
- }
-
- ///
- protected override Solution CreateSolution(ProjectId projectId, string language)
- {
- Solution solution = base.CreateSolution(projectId, language);
- solution.Workspace.Options =
- solution.Workspace.Options
- .WithChangedOption(FormattingOptions.IndentationSize, language, this.IndentationSize)
- .WithChangedOption(FormattingOptions.UseTabs, language, this.UseTabs);
- return solution;
- }
-
- private static async Task FixEachAnalyzerDiagnosticAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
- {
- int expectedNumberOfIterations = numberOfIterations;
- if (numberOfIterations < 0)
- {
- numberOfIterations = -numberOfIterations;
- }
-
- var previousDiagnostics = ImmutableArray.Create();
-
- bool done;
- do
- {
- var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false);
- if (analyzerDiagnostics.Length == 0)
- {
- break;
- }
-
- if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics))
- {
- break;
- }
-
- if (--numberOfIterations < 0)
- {
- Assert.True(false, "The upper limit for the number of code fix iterations was exceeded");
- }
-
- previousDiagnostics = analyzerDiagnostics;
-
- done = true;
- foreach (var diagnostic in analyzerDiagnostics)
- {
- if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
- {
- // do not pass unsupported diagnostics to a code fix provider
- continue;
- }
-
- var actions = new List();
- var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), cancellationToken);
- await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);
-
- if (actions.Count > 0)
- {
- var fixedProject = await ApplyFixAsync(project, actions.ElementAt(codeFixIndex.GetValueOrDefault(0)), cancellationToken).ConfigureAwait(false);
- if (fixedProject != project)
- {
- done = false;
-
- project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false);
- break;
- }
- }
- }
- }
- while (!done);
-
- if (expectedNumberOfIterations >= 0)
- {
- Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations");
- }
-
- return project;
- }
-
- private static Task FixAllAnalyzerDiagnosticsInDocumentAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
- {
- return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Document, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
- }
-
- private static Task FixAllAnalyzerDiagnosticsInProjectAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
- {
- return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Project, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
- }
-
- private static Task FixAllAnalyzerDiagnosticsInSolutionAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
- {
- return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Solution, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
- }
-
- private static async Task FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
- {
- int expectedNumberOfIterations = numberOfIterations;
- if (numberOfIterations < 0)
- {
- numberOfIterations = -numberOfIterations;
- }
-
- var previousDiagnostics = ImmutableArray.Create();
-
- var fixAllProvider = codeFixProvider.GetFixAllProvider();
-
- if (fixAllProvider == null)
- {
- return null;
- }
-
- bool done;
- do
- {
- var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false);
- if (analyzerDiagnostics.Length == 0)
- {
- break;
- }
-
- if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics))
- {
- break;
- }
-
- if (--numberOfIterations < 0)
- {
- Assert.True(false, "The upper limit for the number of fix all iterations was exceeded");
- }
-
- Diagnostic firstDiagnostic = null;
- string equivalenceKey = null;
- foreach (var diagnostic in analyzerDiagnostics)
- {
- if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
- {
- // do not pass unsupported diagnostics to a code fix provider
- continue;
- }
-
- var actions = new List();
- var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), cancellationToken);
- await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);
- if (actions.Count > (codeFixIndex ?? 0))
- {
- firstDiagnostic = diagnostic;
- equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey;
- break;
- }
- }
-
- if (firstDiagnostic == null)
- {
- return project;
- }
-
- previousDiagnostics = analyzerDiagnostics;
-
- done = true;
-
- FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics);
-
- IEnumerable analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id);
- IEnumerable compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal));
- IEnumerable disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key);
- IEnumerable relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct();
- FixAllContext fixAllContext = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken);
-
- CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);
- if (action == null)
- {
- return project;
- }
-
- var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false);
- if (fixedProject != project)
- {
- done = false;
-
- project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false);
- }
- }
- while (!done);
-
- if (expectedNumberOfIterations >= 0)
- {
- Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations");
- }
-
- return project;
- }
-
- private static bool AreDiagnosticsDifferent(ImmutableArray analyzerDiagnostics, ImmutableArray previousDiagnostics)
- {
- if (analyzerDiagnostics.Length != previousDiagnostics.Length)
- {
- return true;
- }
-
- for (var i = 0; i < analyzerDiagnostics.Length; i++)
- {
- if ((analyzerDiagnostics[i].Id != previousDiagnostics[i].Id)
- || (analyzerDiagnostics[i].Location.SourceSpan != previousDiagnostics[i].Location.SourceSpan))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private async Task ApplyFixInternalAsync(
- string language,
- ImmutableArray analyzers,
- CodeFixProvider codeFixProvider,
- string[] oldSources,
- string[] oldFileNames,
- int? codeFixIndex,
- bool allowNewCompilerDiagnostics,
- int numberOfIterations,
- Func, CodeFixProvider, int?, Project, int, CancellationToken, Task> getFixedProject,
- CancellationToken cancellationToken)
- {
- if (oldFileNames != null)
- {
- // Make sure the test case is consistent regarding the number of sources and file names before the code fix
- Assert.Equal($"{oldSources.Length} old file names", $"{oldFileNames.Length} old file names");
- }
-
- var project = this.CreateProject(oldSources, language, oldFileNames);
- var compilerDiagnostics = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false);
-
- project = await getFixedProject(analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken).ConfigureAwait(false);
-
- var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false));
-
- // Check if applying the code fix introduced any new compiler diagnostics
- if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
- {
- // Format and get the compiler diagnostics again so that the locations make sense in the output
- project = await ReformatProjectDocumentsAsync(project, cancellationToken).ConfigureAwait(false);
- newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false));
-
- var message = new StringBuilder();
- message.Append("Fix introduced new compiler diagnostics:\r\n");
- newCompilerDiagnostics.Aggregate(message, (sb, d) => sb.Append(d.ToString()).Append("\r\n"));
- foreach (var document in project.Documents)
- {
- message.Append("\r\n").Append(document.Name).Append(":\r\n");
- message.Append((await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).ToFullString());
- message.Append("\r\n");
- }
-
- Assert.True(false, message.ToString());
- }
-
- return project;
- }
-
- private async Task VerifyFixInternalAsync(
- string language,
- ImmutableArray analyzers,
- CodeFixProvider codeFixProvider,
- string[] oldSources,
- string[] newSources,
- string[] oldFileNames,
- string[] newFileNames,
- int? codeFixIndex,
- bool allowNewCompilerDiagnostics,
- int numberOfIterations,
- Func, CodeFixProvider, int?, Project, int, CancellationToken, Task> getFixedProject,
- CancellationToken cancellationToken)
- {
- if (oldFileNames != null)
- {
- // Make sure the test case is consistent regarding the number of sources and file names before the code fix
- Assert.Equal($"{oldSources.Length} old file names", $"{oldFileNames.Length} old file names");
- }
-
- if (newFileNames != null)
- {
- // Make sure the test case is consistent regarding the number of sources and file names after the code fix
- Assert.Equal($"{newSources.Length} new file names", $"{newFileNames.Length} new file names");
- }
-
- // After applying all of the code fixes, compare the resulting string to the inputed one
- Func verifyFixedProjectAsync =
- async (project, ct) =>
- {
- var updatedDocuments = project.Documents.ToArray();
-
- Assert.Equal($"{newSources.Length} documents", $"{updatedDocuments.Length} documents");
-
- for (int i = 0; i < updatedDocuments.Length; i++)
- {
- var actual = await GetStringFromDocumentAsync(updatedDocuments[i], cancellationToken).ConfigureAwait(false);
- Assert.Equal(newSources[i], actual);
-
- if (newFileNames != null)
- {
- Assert.Equal(newFileNames[i], updatedDocuments[i].Name);
- }
- }
- };
-
- await this.VerifyFixInternalAsync(language, analyzers, codeFixProvider, oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, getFixedProject, verifyFixedProjectAsync, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task VerifyFixInternalAsync(
- string language,
- ImmutableArray analyzers,
- CodeFixProvider codeFixProvider,
- string[] oldSources,
- string[] oldFileNames,
- int? codeFixIndex,
- bool allowNewCompilerDiagnostics,
- int numberOfIterations,
- Func, CodeFixProvider, int?, Project, int, CancellationToken, Task> getFixedProject,
- Func verifyFixedProjectAsync,
- CancellationToken cancellationToken)
- {
- var project = await this.ApplyFixInternalAsync(language, analyzers, codeFixProvider, oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, getFixedProject, cancellationToken).ConfigureAwait(false);
-
- await verifyFixedProjectAsync(project, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task>> GetOfferedFixesInternalAsync(string language, string source, int? diagnosticIndex, ImmutableArray analyzers, CodeFixProvider codeFixProvider, CancellationToken cancellationToken)
- {
- var document = this.CreateDocument(source, language);
- var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false);
-
- var index = diagnosticIndex.HasValue ? diagnosticIndex.Value : 0;
-
- Assert.True(index < analyzerDiagnostics.Count());
-
- var actions = new List();
-
- // do not pass unsupported diagnostics to a code fix provider
- if (codeFixProvider.FixableDiagnosticIds.Contains(analyzerDiagnostics[index].Id))
- {
- var context = new CodeFixContext(document, analyzerDiagnostics[index], (a, d) => actions.Add(a), cancellationToken);
- await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);
- }
-
- return Tuple.Create(document.Project.Solution, actions.ToImmutableArray());
- }
- }
-}
diff --git a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/DiagnosticVerifier.cs b/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/DiagnosticVerifier.cs
deleted file mode 100644
index eadfaf0..0000000
--- a/PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/DiagnosticVerifier.cs
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
-
-namespace TestHelper
-{
- using System;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.Diagnostics;
- using Xunit;
-
- ///
- /// Superclass of all unit tests for s.
- ///
- public abstract partial class DiagnosticVerifier
- {
- protected static DiagnosticResult[] EmptyDiagnosticResults { get; } = { };
-
- ///
- /// Verifies that the analyzer will properly handle an empty source.
- ///
- /// A representing the asynchronous unit test.
- [Fact]
- public async Task TestEmptySourceAsync()
- {
- var testCode = string.Empty;
- await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
- }
-
- ///
- /// Verifies that each diagnostics contains a in the expected
- /// format.
- ///
- [Fact]
- public void TestHelpLink()
- {
- foreach (var diagnosticAnalyzer in this.GetCSharpDiagnosticAnalyzers())
- {
- foreach (var diagnostic in diagnosticAnalyzer.SupportedDiagnostics)
- {
- if (diagnostic.DefaultSeverity == DiagnosticSeverity.Hidden && diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable))
- {
- // This diagnostic will never appear in the UI.
- continue;
- }
-
- string expected = $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{diagnostic.Id}.md";
- Assert.Equal(expected, diagnostic.HelpLinkUri);
- }
- }
- }
-
- ///
- /// Gets the C# analyzers being tested
- ///
- ///
- /// New instances of all the C# analyzers being tested.
- ///
- protected abstract IEnumerable GetCSharpDiagnosticAnalyzers();
-
- ///
- /// Called to test a C# when applied on the single input source as a string.
- ///
- /// Input a for the expected .
- ///
- ///
- /// A class in the form of a string to run the analyzer on.
- /// A s describing the that should
- /// be reported by the analyzer for the specified source.
- /// The that the task will observe.
- /// The filename or null if the default filename should be used
- /// A representing the asynchronous operation.
- protected Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken, string filename = null)
- {
- return this.VerifyCSharpDiagnosticAsync(source, new[] { expected }, cancellationToken, filename);
- }
-
- ///
- /// Called to test a C# when applied on the single input source as a string.
- ///
- /// Input a for each expected.
- ///
- ///
- /// A class in the form of a string to run the analyzer on.
- /// A collection of s describing the
- /// s that should be reported by the analyzer for the specified source.
- /// The that the task will observe.
- /// The filename or null if the default filename should be used
- /// A representing the asynchronous operation.
- protected Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken, string filename = null)
- {
- return this.VerifyDiagnosticsAsync(new[] { source }, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), expected, cancellationToken, filename != null ? new[] { filename } : null);
- }
-
- ///
- /// Called to test a C# when applied on the input strings as sources.
- ///
- /// Input a for each expected.
- ///
- ///
- /// A collection of strings to create source documents from to run the analyzers
- /// on.
- /// A collection of s describing the
- /// s that should be reported by the analyzer for the specified sources.
- /// The that the task will observe.
- /// The filenames or null if the default filename should be used
- /// A representing the asynchronous operation.
- protected Task VerifyCSharpDiagnosticAsync(string[] sources, DiagnosticResult[] expected, CancellationToken cancellationToken, string[] filenames = null)
- {
- return this.VerifyDiagnosticsAsync(sources, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), expected, cancellationToken, filenames);
- }
-
- ///
- /// Checks each of the actual s found and compares them with the corresponding
- /// in the array of expected results. s are considered
- /// equal only if the , ,
- /// , and of the
- /// match the actual .
- ///
- /// The s found by the compiler after running the analyzer
- /// on the source code.
- /// The analyzers that have been run on the sources.
- /// A collection of s describing the expected
- /// diagnostics for the sources.
- private static void VerifyDiagnosticResults(IEnumerable actualResults, ImmutableArray analyzers, DiagnosticResult[] expectedResults)
- {
- int expectedCount = expectedResults.Length;
- int actualCount = actualResults.Count();
-
- if (expectedCount != actualCount)
- {
- string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzers, actualResults.ToArray()) : " NONE.";
-
- Assert.True(
- false,
- string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput));
- }
-
- for (int i = 0; i < expectedResults.Length; i++)
- {
- var actual = actualResults.ElementAt(i);
- var expected = expectedResults[i];
-
- if (expected.Line == -1 && expected.Column == -1)
- {
- if (actual.Location != Location.None)
- {
- string message =
- string.Format(
- "Expected:\nA project diagnostic with No location\nActual:\n{0}",
- FormatDiagnostics(analyzers, actual));
- Assert.True(false, message);
- }
- }
- else
- {
- VerifyDiagnosticLocation(analyzers, actual, actual.Location, expected.Locations.First());
- var additionalLocations = actual.AdditionalLocations.ToArray();
-
- if (additionalLocations.Length != expected.Locations.Length - 1)
- {
- string message =
- string.Format(
- "Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n",
- expected.Locations.Length - 1,
- additionalLocations.Length,
- FormatDiagnostics(analyzers, actual));
- Assert.True(false, message);
- }
-
- for (int j = 0; j < additionalLocations.Length; ++j)
- {
- VerifyDiagnosticLocation(analyzers, actual, additionalLocations[j], expected.Locations[j + 1]);
- }
- }
-
- if (actual.Id != expected.Id)
- {
- string message =
- string.Format(
- "Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Id,
- actual.Id,
- FormatDiagnostics(analyzers, actual));
- Assert.True(false, message);
- }
-
- if (actual.Severity != expected.Severity)
- {
- string message =
- string.Format(
- "Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Severity,
- actual.Severity,
- FormatDiagnostics(analyzers, actual));
- Assert.True(false, message);
- }
-
- if (actual.GetMessage() != expected.Message)
- {
- string message =
- string.Format(
- "Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Message,
- actual.GetMessage(),
- FormatDiagnostics(analyzers, actual));
- Assert.True(false, message);
- }
- }
- }
-
- ///
- /// Helper method to that checks the location of a
- /// and compares it with the location described by a
- /// .
- ///
- /// The analyzer that have been run on the sources.
- /// The diagnostic that was found in the code.
- /// The location of the diagnostic found in the code.
- /// The describing the expected location of the
- /// diagnostic.
- private static void VerifyDiagnosticLocation(ImmutableArray analyzers, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
- {
- var actualSpan = actual.GetLineSpan();
-
- string message =
- string.Format(
- "Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Path,
- actualSpan.Path,
- FormatDiagnostics(analyzers, diagnostic));
- Assert.True(
- actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")),
- message);
-
- var actualLinePosition = actualSpan.StartLinePosition;
-
- // Only check line position if it matters
- if (expected.Line > 0)
- {
- if (actualLinePosition.Line + 1 != expected.Line)
- {
- string message2 =
- string.Format(
- "Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Line,
- actualLinePosition.Line + 1,
- FormatDiagnostics(analyzers, diagnostic));
- Assert.True(false, message2);
- }
- }
-
- // Only check column position if it matters
- if (expected.Column > 0)
- {
- if (actualLinePosition.Character + 1 != expected.Column)
- {
- string message2 =
- string.Format(
- "Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Column,
- actualLinePosition.Character + 1,
- FormatDiagnostics(analyzers, diagnostic));
- Assert.True(false, message2);
- }
- }
- }
-
- ///
- /// Helper method to format a into an easily readable string.
- ///
- /// The analyzers that this verifier tests.
- /// A collection of s to be formatted.
- /// The formatted as a string.
- private static string FormatDiagnostics(ImmutableArray analyzers, params Diagnostic[] diagnostics)
- {
- var builder = new StringBuilder();
- for (int i = 0; i < diagnostics.Length; ++i)
- {
- var diagnosticsId = diagnostics[i].Id;
-
- builder.Append("// ").AppendLine(diagnostics[i].ToString());
-
- var applicableAnalyzer = analyzers.FirstOrDefault(a => a.SupportedDiagnostics.Any(dd => dd.Id == diagnosticsId));
- if (applicableAnalyzer != null)
- {
- var analyzerType = applicableAnalyzer.GetType();
-
- var location = diagnostics[i].Location;
- if (location == Location.None)
- {
- builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, diagnosticsId);
- }
- else if (!location.IsInSource)
- {
- builder.AppendFormat("GetMetadataResult({0})", diagnostics[i]);
- }
- else
- {
- string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt";
- var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
-
- builder.AppendFormat(
- "{0}({1}, {2}, {3}.{4})",
- resultMethodName,
- linePosition.Line + 1,
- linePosition.Character + 1,
- analyzerType.Name,
- diagnosticsId);
- }
-
- if (i != diagnostics.Length - 1)
- {
- builder.Append(',');
- }
-
- builder.AppendLine();
- }
- }
-
- return builder.ToString();
- }
-
- ///
- /// General method that gets a collection of actual s found in the source after the
- /// analyzer is run, then verifies each of them.
- ///
- /// An array of strings to create source documents from to run the analyzers on.
- /// The language of the classes represented by the source strings.
- /// The analyzers to be run on the source code.
- /// A collection of s that should appear after the analyzer
- /// is run on the sources.
- /// The that the task will observe.
- /// The filenames or null if the default filename should be used
- /// A representing the asynchronous operation.
- private async Task VerifyDiagnosticsAsync(string[] sources, string language, ImmutableArray analyzers, DiagnosticResult[] expected, CancellationToken cancellationToken, string[] filenames)
- {
- VerifyDiagnosticResults(await this.GetSortedDiagnosticsAsync(sources, language, analyzers, cancellationToken, filenames).ConfigureAwait(false), analyzers, expected);
- }
- }
-}