diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs
index 5c749ede2..7fa66d379 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CodeFixVerifier.Helper.cs
@@ -12,6 +12,7 @@ namespace TestHelper
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.
@@ -23,16 +24,16 @@ 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
+ /// 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(Document document, CodeAction codeAction, CancellationToken cancellationToken)
+ /// 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.GetDocument(document.Id);
+ return solution.GetProject(project.Id);
}
///
@@ -75,13 +76,20 @@ private static IEnumerable GetNewDiagnostics(IEnumerable
///
/// Get the existing compiler diagnostics on the input document.
///
- /// The to run the compiler diagnostic analyzers on.
+ /// 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(Document document, CancellationToken cancellationToken)
+ private static async Task> GetCompilerDiagnosticsAsync(Project project, CancellationToken cancellationToken)
{
- var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
- return semanticModel.GetDiagnostics(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;
}
///
@@ -97,5 +105,49 @@ private static async Task GetStringFromDocumentAsync(Document document,
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/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/FileMayOnlyContainTestBase.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/FileMayOnlyContainTestBase.cs
index 280aa1d21..80330e301 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/FileMayOnlyContainTestBase.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/FileMayOnlyContainTestBase.cs
@@ -3,6 +3,7 @@
namespace StyleCop.Analyzers.Test.MaintainabilityRules
{
+ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TestHelper;
@@ -20,7 +21,10 @@ public async Task TestOneElementAsync()
var testCode = @"%1 Foo
{
}";
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
+ testCode = testCode.Replace("%1", this.Keyword);
+
+ await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}
[Fact]
@@ -32,18 +36,29 @@ public async Task TestTwoElementsAsync()
%1 Bar
{
}";
- var fixedCode = @"%1 Foo
+
+ var fixedCode = new[]
+ {
+ @"%1 Foo
{
}
-";
+",
+ @"%1 Bar
+{
+}"
+ };
+
+ testCode = testCode.Replace("%1", this.Keyword);
+ fixedCode = fixedCode.Select(c => c.Replace("%1", this.Keyword)).ToArray();
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2);
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
@@ -59,22 +74,37 @@ public async Task TestThreeElementsAsync()
%1 FooBar
{
}";
- var fixedCode = @"%1 Foo
+
+ var fixedCode = new[]
+ {
+ @"%1 Foo
+{
+}
+",
+ @"%1 Bar
{
}
-";
+",
+ @"%1 FooBar
+{
+}"
+ };
+
+ testCode = testCode.Replace("%1", this.Keyword);
+ fixedCode = fixedCode.Select(code => code.Replace("%1", this.Keyword)).ToArray();
DiagnosticResult[] expected =
- {
- this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2),
- this.CSharpDiagnostic().WithLocation(7, this.Keyword.Length + 2)
- };
+ {
+ this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2),
+ this.CSharpDiagnostic().WithLocation(7, this.Keyword.Length + 2)
+ };
+
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
@@ -89,18 +119,31 @@ public async Task TestRemoveWarningSuppressionAsync()
#pragma warning disable SomeWarning
#pragma warning restore SomeWarning
}";
- var fixedCode = @"%1 Foo
+
+ var fixedCode = new[]
+ {
+ @"%1 Foo
{
}
-";
+",
+ @"%1 Bar
+{
+#pragma warning disable SomeWarning
+#pragma warning restore SomeWarning
+}"
+ };
+
+ testCode = testCode.Replace("%1", this.Keyword);
+ fixedCode = fixedCode.Select(code => code.Replace("%1", this.Keyword)).ToArray();
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2);
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
@@ -116,18 +159,30 @@ public async Task TestPreserveWarningSuppressionAsync()
}";
// See https://github.com/dotnet/roslyn/issues/3999
- var fixedCode = @"%1 Foo
+ var fixedCode = new[]
+ {
+ @"%1 Foo
{
}
-";
+",
+ @"%1 Bar
+{
+#pragma warning disable SomeWarning
+}"
+ };
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2);
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
+ foreach (var code in fixedCode)
+ {
+ await this.VerifyCSharpDiagnosticAsync(code.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode.Replace("%1", this.Keyword) }, fixedCode.Select(c => c.Replace("%1", this.Keyword)).ToArray(), cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
@@ -142,18 +197,31 @@ public async Task TestRemovePreprocessorDirectivesAsync()
#if true
#endif
}";
- var fixedCode = @"%1 Foo
+
+ var fixedCode = new[]
+ {
+ @"%1 Foo
{
}
-";
+",
+ @"%1 Bar
+{
+#if true
+#endif
+}"
+ };
+
+ testCode = testCode.Replace("%1", this.Keyword);
+ fixedCode = fixedCode.Select(code => code.Replace("%1", this.Keyword)).ToArray();
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, this.Keyword.Length + 2);
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
@@ -170,21 +238,34 @@ public async Task TestPreservePreprocessorDirectivesAsync()
}";
// See https://github.com/dotnet/roslyn/issues/3999
- var fixedCode = @"%1 Foo
+ var fixedCode = new[]
+ {
+ @"%1 Foo
{
#if true
}
#endif
-";
+",
+ @"
+#if true
+%1 Bar
+{
+#endif
+}"
+ };
+
+ testCode = testCode.Replace("%1", this.Keyword);
+ fixedCode = fixedCode.Select(code => code.Replace("%1", this.Keyword)).ToArray();
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(5, this.Keyword.Length + 2);
- await this.VerifyCSharpDiagnosticAsync(testCode.Replace("%1", this.Keyword), expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(fixedCode.Replace("%1", this.Keyword), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+
if (this.SupportsCodeFix)
{
- await this.VerifyCSharpFixAsync(testCode.Replace("%1", this.Keyword), fixedCode.Replace("%1", this.Keyword), cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1402UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1402UnitTests.cs
index 639d976af..5b9b6bcaa 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1402UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1402UnitTests.cs
@@ -48,16 +48,24 @@ public partial class Bar
{
}";
- var fixedCode = @"public partial class Foo
+
+ var fixedCode = new[]
+ {
+ @"public partial class Foo
{
}
-";
+",
+ @"public partial class Bar
+{
+
+}"
+ };
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, 22);
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
[Fact]
@@ -70,15 +78,22 @@ public class Test0
{
}";
- var fixedCode = @"public class Test0
+ var fixedCode = new[]
+ {
+ @"public class Test0
{
-}";
+}",
+ @"public class Foo
+{
+}
+"
+ };
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(1, 14);
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(new[] { testCode }, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
[Fact]
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/Verifiers/CodeFixVerifier.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Verifiers/CodeFixVerifier.cs
index 71ff0e455..942b459a4 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/Verifiers/CodeFixVerifier.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Verifiers/CodeFixVerifier.cs
@@ -8,6 +8,7 @@ namespace TestHelper
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
+ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
@@ -24,6 +25,8 @@ namespace TestHelper
///
public abstract partial class CodeFixVerifier : DiagnosticVerifier
{
+ private const int DefaultNumberOfIncrementalIterations = -1000;
+
///
/// Returns the code fix being tested (C#) - to be implemented in non-abstract class.
///
@@ -45,9 +48,29 @@ public abstract partial class CodeFixVerifier : DiagnosticVerifier
/// 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 oldSource, string newSource, string batchNewSource = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = -int.MaxValue, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
+ protected Task VerifyCSharpFixAsync(string oldSource, string newSource, string batchNewSource = 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(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, GetSingleAnalyzerDocumentAsync, cancellationToken).ConfigureAwait(false);
+ return this.VerifyCSharpFixAsync(new[] { oldSource }, new[] { newSource }, batchNewSource == null ? null : new[] { batchNewSource }, 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.
+ /// 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, 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, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, FixEachAnalyzerDiagnosticAsync, cancellationToken).ConfigureAwait(false);
var fixAllProvider = this.GetCSharpCodeFixProvider().GetFixAllProvider();
Assert.NotEqual(WellKnownFixAllProviders.BatchFixer, fixAllProvider);
@@ -63,19 +86,19 @@ public abstract partial class CodeFixVerifier : DiagnosticVerifier
await t1;
}
- var t2 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSource, batchNewSource ?? newSource, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, GetFixAllAnalyzerDocumentAsync, cancellationToken).ConfigureAwait(false);
+ var t2 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, cancellationToken).ConfigureAwait(false);
if (Debugger.IsAttached)
{
await t2;
}
- var t3 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSource, batchNewSource ?? newSource, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, GetFixAllAnalyzerProjectAsync, cancellationToken).ConfigureAwait(false);
+ var t3 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInProjectAsync, cancellationToken).ConfigureAwait(false);
if (Debugger.IsAttached)
{
await t3;
}
- var t4 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSource, batchNewSource ?? newSource, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, GetFixAllAnalyzerSolutionAsync, cancellationToken).ConfigureAwait(false);
+ var t4 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, batchNewSources ?? newSources, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInSolutionAsync, cancellationToken).ConfigureAwait(false);
if (Debugger.IsAttached)
{
await t4;
@@ -105,7 +128,7 @@ public abstract partial class CodeFixVerifier : DiagnosticVerifier
/// 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(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, GetFixAllAnalyzerDocumentAsync, cancellationToken).ConfigureAwait(false);
+ await this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), new[] { oldSource }, new[] { newSource }, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, cancellationToken).ConfigureAwait(false);
}
///
@@ -120,7 +143,7 @@ public abstract partial class CodeFixVerifier : DiagnosticVerifier
return await this.GetOfferedFixesInternalAsync(LanguageNames.CSharp, source, diagnosticIndex, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), cancellationToken).ConfigureAwait(false);
}
- private static async Task GetSingleAnalyzerDocumentAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken cancellationToken)
+ private static async Task FixEachAnalyzerDiagnosticAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
{
int expectedNumberOfIterations = numberOfIterations;
if (numberOfIterations < 0)
@@ -133,7 +156,7 @@ private static async Task GetSingleAnalyzerDocumentAsync(ImmutableArra
bool done;
do
{
- var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false);
+ var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false);
if (analyzerDiagnostics.Length == 0)
{
break;
@@ -152,28 +175,26 @@ private static async Task GetSingleAnalyzerDocumentAsync(ImmutableArra
previousDiagnostics = analyzerDiagnostics;
done = true;
- for (var i = 0; i < analyzerDiagnostics.Length; i++)
+ foreach (var diagnostic in analyzerDiagnostics)
{
- if (!codeFixProvider.FixableDiagnosticIds.Contains(analyzerDiagnostics[i].Id))
+ 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(document, analyzerDiagnostics[i], (a, d) => actions.Add(a), cancellationToken);
+ 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 fixedDocument = await ApplyFixAsync(document, actions.ElementAt(codeFixIndex.GetValueOrDefault(0)), cancellationToken).ConfigureAwait(false);
- if (fixedDocument != document)
+ var fixedProject = await ApplyFixAsync(project, actions.ElementAt(codeFixIndex.GetValueOrDefault(0)), cancellationToken).ConfigureAwait(false);
+ if (fixedProject != project)
{
done = false;
- var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
- // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document.
- document = document.WithText(newText);
+ project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false);
break;
}
}
@@ -186,25 +207,25 @@ private static async Task GetSingleAnalyzerDocumentAsync(ImmutableArra
Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations");
}
- return document;
+ return project;
}
- private static Task GetFixAllAnalyzerDocumentAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken cancellationToken)
+ private static Task FixAllAnalyzerDiagnosticsInDocumentAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
{
- return GetFixAllAnalyzerAsync(FixAllScope.Document, analyzers, codeFixProvider, codeFixIndex, document, numberOfIterations, cancellationToken);
+ return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Document, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
}
- private static Task GetFixAllAnalyzerProjectAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken cancellationToken)
+ private static Task FixAllAnalyzerDiagnosticsInProjectAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
{
- return GetFixAllAnalyzerAsync(FixAllScope.Project, analyzers, codeFixProvider, codeFixIndex, document, numberOfIterations, cancellationToken);
+ return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Project, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
}
- private static Task GetFixAllAnalyzerSolutionAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken cancellationToken)
+ private static Task FixAllAnalyzerDiagnosticsInSolutionAsync(ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
{
- return GetFixAllAnalyzerAsync(FixAllScope.Solution, analyzers, codeFixProvider, codeFixIndex, document, numberOfIterations, cancellationToken);
+ return FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope.Solution, analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken);
}
- private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken 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)
@@ -224,7 +245,7 @@ private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, Im
bool done;
do
{
- var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false);
+ var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false);
if (analyzerDiagnostics.Length == 0)
{
break;
@@ -240,6 +261,7 @@ private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, Im
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)
{
@@ -250,15 +272,21 @@ private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, Im
}
var actions = new List();
- var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken);
+ 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;
@@ -267,24 +295,22 @@ private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, Im
IEnumerable analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id);
IEnumerable compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal));
- IEnumerable disabledDiagnosticIds = document.Project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key);
+ 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(document, codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken);
+ 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 document;
+ return project;
}
- var fixedDocument = await ApplyFixAsync(document, action, cancellationToken).ConfigureAwait(false);
- if (fixedDocument != document)
+ var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false);
+ if (fixedProject != project)
{
done = false;
- var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
- // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document.
- document = document.WithText(newText);
+ project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false);
}
}
while (!done);
@@ -294,7 +320,7 @@ private static async Task GetFixAllAnalyzerAsync(FixAllScope scope, Im
Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations");
}
- return document;
+ return project;
}
private static bool AreDiagnosticsDifferent(ImmutableArray analyzerDiagnostics, ImmutableArray previousDiagnostics)
@@ -320,39 +346,51 @@ private async Task VerifyFixInternalAsync(
string language,
ImmutableArray analyzers,
CodeFixProvider codeFixProvider,
- string oldSource,
- string newSource,
+ string[] oldSources,
+ string[] newSources,
int? codeFixIndex,
bool allowNewCompilerDiagnostics,
int numberOfIterations,
- Func, CodeFixProvider, int?, Document, int, CancellationToken, Task> getFixedDocument,
+ Func, CodeFixProvider, int?, Project, int, CancellationToken, Task> getFixedProject,
CancellationToken cancellationToken)
{
- var document = this.CreateDocument(oldSource, language);
- var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document, cancellationToken).ConfigureAwait(false);
+ var project = this.CreateProject(oldSources, language);
+ var compilerDiagnostics = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false);
- document = await getFixedDocument(analyzers, codeFixProvider, codeFixIndex, document, numberOfIterations, cancellationToken).ConfigureAwait(false);
+ project = await getFixedProject(analyzers, codeFixProvider, codeFixIndex, project, numberOfIterations, cancellationToken).ConfigureAwait(false);
- var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document, cancellationToken).ConfigureAwait(false));
+ var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false));
- // check if applying the code fix introduced any new compiler diagnostics
+ // 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
- document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
- newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document, cancellationToken).ConfigureAwait(false));
-
- string message =
- string.Format(
- "Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n",
- string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())),
- (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).ToFullString());
- Assert.True(false, message);
+ 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());
}
- // after applying all of the code fixes, compare the resulting string to the inputted one
- var actual = await GetStringFromDocumentAsync(document, cancellationToken).ConfigureAwait(false);
- Assert.Equal(newSource, actual);
+ // After applying all of the code fixes, compare the resulting string to the inputted one
+ 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);
+ }
}
private async Task> GetOfferedFixesInternalAsync(string language, string source, int? diagnosticIndex, ImmutableArray analyzers, CodeFixProvider codeFixProvider, CancellationToken cancellationToken)