Esempio n. 1
0
        private static async Task <IEnumerable <CodeAction> > GetFixesAsync(Solution solution, CodeFixProvider codeFixProvider, Diagnostic diagnostic)
        {
            List <CodeAction> codeActions = new List <CodeAction>();

            await codeFixProvider.RegisterCodeFixesAsync(new CodeFixContext(solution.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => codeActions.Add(a), CancellationToken.None));

            return(codeActions);
        }
        private async Task <IEnumerable <Tuple <Diagnostic, CodeFixCollection> > > GetDiagnosticAndFixesAsync(
            IEnumerable <Diagnostic> diagnostics,
            DiagnosticAnalyzer provider,
            CodeFixProvider fixer,
            TestDiagnosticAnalyzerDriver testDriver,
            Document document,
            TextSpan span,
            FixAllScope?scope,
            string fixAllActionId)
        {
            Assert.NotEmpty(diagnostics);
            var result = new List <Tuple <Diagnostic, CodeFixCollection> >();

            if (scope == null)
            {
                // Simple code fix.
                foreach (var diagnostic in diagnostics)
                {
                    var fixes   = new List <CodeFix>();
                    var context = new CodeFixContext(document, diagnostic, (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), CancellationToken.None);

                    await fixer.RegisterCodeFixesAsync(context);

                    if (fixes.Any())
                    {
                        var codeFix = new CodeFixCollection(
                            fixer, diagnostic.Location.SourceSpan, fixes.ToImmutableArray(),
                            fixAllState: null, supportedScopes: ImmutableArray <FixAllScope> .Empty, firstDiagnostic: null);
                        result.Add(Tuple.Create(diagnostic, codeFix));
                    }
                }
            }
            else
            {
                // Fix all fix.
                var fixAllProvider = fixer.GetFixAllProvider();
                Assert.NotNull(fixAllProvider);

                var fixAllState   = GetFixAllState(fixAllProvider, diagnostics, provider, fixer, testDriver, document, scope.Value, fixAllActionId);
                var fixAllContext = fixAllState.CreateFixAllContext(new ProgressTracker(), CancellationToken.None);
                var fixAllFix     = await fixAllProvider.GetFixAsync(fixAllContext);

                if (fixAllFix != null)
                {
                    // Same fix applies to each diagnostic in scope.
                    foreach (var diagnostic in diagnostics)
                    {
                        var diagnosticSpan = diagnostic.Location.IsInSource ? diagnostic.Location.SourceSpan : default(TextSpan);
                        var codeFix        = new CodeFixCollection(
                            fixAllProvider, diagnosticSpan, ImmutableArray.Create(new CodeFix(document.Project, fixAllFix, diagnostic)),
                            fixAllState: null, supportedScopes: ImmutableArray <FixAllScope> .Empty, firstDiagnostic: null);
                        result.Add(Tuple.Create(diagnostic, codeFix));
                    }
                }
            }

            return(result);
        }
        /// <summary>
        /// General verifier for codefixes.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Then gets the string after the codefix is applied and compares it with the expected result.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        private static async Task VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics)
        {
            Document document = CreateDocument(oldSource, language);

            Diagnostic[]             analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
            IEnumerable <Diagnostic> compilerDiagnostics = GetCompilerDiagnostics(document);
            int attempts = analyzerDiagnostics.Length;

            for (int i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = await ApplyFix(document, actions.ElementAt((int)codeFixIndex));

                    break;
                }

                document = await ApplyFix(document, actions.ElementAt(0));

                analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });

                IEnumerable <Diagnostic> newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                //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 = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                    Assert.IsTrue(false,
                                  $"Fix introduced new compiler diagnostics:{string.Join(Environment.NewLine, newCompilerDiagnostics.Select(d => d.ToString()))}" +
                                  $"{Environment.NewLine}{Environment.NewLine}New document:{Environment.NewLine}{document.GetSyntaxRootAsync().Result.ToFullString()}{Environment.NewLine}");
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            string actual = GetStringFromDocument(document);

            Assert.AreEqual <string>(
                newSource?.Replace("\r", "", StringComparison.InvariantCulture),
                actual?.Replace("\r", "", StringComparison.InvariantCulture));
        }
Esempio n. 4
0
        /// <summary>
        ///     General verifier for codefixes.
        ///     Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        ///     Then gets the string after the codefix is applied and compares it with the expected result.
        ///     Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to
        ///     true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">
        ///     A bool controlling whether or not the test will fail if the CodeFix
        ///     introduces other warnings after being applied
        /// </param>
        private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider,
                               string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics)
        {
            var document            = CreateDocument(oldSource, language);
            var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
            var compilerDiagnostics = GetCompilerDiagnostics(document);
            var attempts            = analyzerDiagnostics.Length;

            for (var i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a),
                                                 CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = ApplyFix(document, actions.ElementAt((int)codeFixIndex));
                    break;
                }

                document            = ApplyFix(document, actions.ElementAt(0));
                analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                //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 =
                        document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result,
                                                                 Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                    Assert.IsTrue(false,
                                  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())),
                                                document.GetSyntaxRootAsync().Result.ToFullString()));
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            var actual = GetStringFromDocument(document);

            Assert.AreEqual(newSource, actual);
        }
Esempio n. 5
0
        protected async Task VerifyFix(string oldSource, string newSource, int?codeFixIndex = null)
        {
            using (var workspace = new AdhocWorkspace())
            {
                var project     = AddProject(workspace.CurrentSolution, oldSource);
                var document    = project.Documents.Single();
                var compilation = await project.GetCompilationAsync();

                var compilerDiagnostics = compilation.GetDiagnostics();

                VerifyNoCompilerDiagnosticErrors(compilerDiagnostics);

                var analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics(
                    DiagnosticAnalyzer,
                    project.AnalyzerOptions);

                var previousAnalyzerDiagnostics = analyzerDiagnostics;
                var attempts = analyzerDiagnostics.Length;

                for (var i = 0; i < attempts; ++i)
                {
                    var actions = new List <CodeAction>();
                    var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                    await CodeFixProvider.RegisterCodeFixesAsync(context);

                    if (!actions.Any())
                    {
                        break;
                    }

                    document = await document.ApplyCodeAction(actions[codeFixIndex ?? 0]);

                    compilation = await document.Project.GetCompilationAsync();

                    compilerDiagnostics = compilation.GetDiagnostics();

                    VerifyNoCompilerDiagnosticErrors(compilerDiagnostics);

                    analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics(
                        DiagnosticAnalyzer,
                        project.AnalyzerOptions);

                    // check if there are analyzer diagnostics left after the code fix
                    var newAnalyzerDiagnostics = analyzerDiagnostics.Except(previousAnalyzerDiagnostics).ToList();
                    if (analyzerDiagnostics.Length == previousAnalyzerDiagnostics.Length && newAnalyzerDiagnostics.Any())
                    {
                        Execute.Assertion.Fail(
                            $"Fix didn't fix analyzer diagnostics: {newAnalyzerDiagnostics.ToDebugString()} New document:{Environment.NewLine}{await document.ToFullString()}");
                    }

                    previousAnalyzerDiagnostics = analyzerDiagnostics;
                }

                var actual = await document.ToFullString();

                actual.Should().Be(newSource);
            }
        }
Esempio n. 6
0
        private async Task <(ImmutableArray <Diagnostic>, ImmutableArray <CodeAction>, CodeAction actionToinvoke)> GetDiagnosticAndFixesAsync(
            IEnumerable <Diagnostic> diagnostics,
            CodeFixProvider fixer,
            TestDiagnosticAnalyzerDriver testDriver,
            Document document,
            TextSpan span,
            FixAllScope?scope,
            int index)
        {
            Assert.NotEmpty(diagnostics);

            var intersectingDiagnostics = diagnostics.Where(d => d.Location.SourceSpan.IntersectsWith(span))
                                          .ToImmutableArray();

            var fixes = new List <CodeFix>();

            foreach (var diagnostic in intersectingDiagnostics)
            {
                var context = new CodeFixContext(
                    document, diagnostic,
                    (a, d) => fixes.Add(new CodeFix(document.Project, a, d)),
                    CancellationToken.None);

                await fixer.RegisterCodeFixesAsync(context);
            }

            var actions = fixes.SelectAsArray(f => f.Action);

            actions = actions.SelectMany(a => a is TopLevelSuppressionCodeAction
                ? a.NestedCodeActions
                : ImmutableArray.Create(a)).ToImmutableArray();

            actions = MassageActions(actions);

            if (scope == null)
            {
                // Simple code fix.
                return(intersectingDiagnostics, actions, actions.Length == 0 ? null : actions[index]);
            }
            else
            {
                var equivalenceKey = actions[index].EquivalenceKey;

                // Fix all fix.
                var fixAllProvider = fixer.GetFixAllProvider();
                Assert.NotNull(fixAllProvider);

                var fixAllState = GetFixAllState(
                    fixAllProvider, diagnostics, fixer, testDriver, document,
                    scope.Value, equivalenceKey);
                var fixAllContext = fixAllState.CreateFixAllContext(new ProgressTracker(), CancellationToken.None);
                var fixAllFix     = await fixAllProvider.GetFixAsync(fixAllContext);

                // We have collapsed the fixes down to the single fix-all fix, so we just let our
                // caller know they should pull that entry out of the result.
                return(intersectingDiagnostics, ImmutableArray.Create(fixAllFix), fixAllFix);
            }
        }
Esempio n. 7
0
        private static IEnumerable <CodeAction> GetCodeActionsForDiagnostic(CodeFixProvider codeFixProvider, Document document,
                                                                            Diagnostic diagnostic)
        {
            var actions = new List <CodeAction>();
            var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), CancellationToken.None);

            codeFixProvider.RegisterCodeFixesAsync(context).Wait();
            return(actions);
        }
        internal static List <CodeAction> GetFixActions(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source)
        {
            var document            = CodeAnalysisHelper.CreateDocument(source, language);
            var analyzerDiagnostics = CodeAnalysisHelper.GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
            var actions             = new List <CodeAction>();
            var context             = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);

            codeFixProvider.RegisterCodeFixesAsync(context).Wait();
            return(actions);
        }
        /// <summary>
        /// General verifier for codefixes.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Then gets the string after the codefix is applied and compares it with the expected result.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSources">Code files, each in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSources">Code files, each in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple in the same location</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        /// <param name="hasEntrypoint"><c>true</c> to set the compiler in a mode as if it were compiling an exe (as opposed to a dll).</param>
        private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, int?codeFixIndex, bool allowNewCompilerDiagnostics, bool hasEntrypoint)
        {
            var project             = CreateProject(oldSources, language, hasEntrypoint);
            var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(ImmutableArray.Create(analyzer), project.Documents.ToArray(), hasEntrypoint);
            var compilerDiagnostics = project.Documents.SelectMany(doc => GetCompilerDiagnostics(doc));
            var attempts            = analyzerDiagnostics.Length;

            // We'll go through enough for each diagnostic to be caught once
            for (int i = 0; i < attempts; ++i)
            {
                var diagnostic = analyzerDiagnostics[0]; // just get the first one -- the list gets smaller with each loop.
                var document   = project.GetDocument(diagnostic.Location.SourceTree);
                var actions    = new List <CodeAction>();
                var context    = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();
                if (!actions.Any())
                {
                    continue;
                }

                document = ApplyFix(document, actions[codeFixIndex ?? 0]);
                project  = document.Project;

                this.Logger.WriteLine("Code after fix:\n{0}", document.GetSyntaxRootAsync().Result.ToFullString());

                analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(ImmutableArray.Create(analyzer), project.Documents.ToArray());
                var newCompilerDiagnostics = GetNewDiagnostics(
                    compilerDiagnostics,
                    project.Documents.SelectMany(doc => GetCompilerDiagnostics(doc)));

                // 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 = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                    Assert.True(
                        false,
                        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())),
                            document.GetSyntaxRootAsync().Result.ToFullString()));
                }
            }

            // After applying all of the code fixes, compare the resulting string to the inputted one
            int j = 0;

            foreach (var document in project.Documents)
            {
                var actual = GetStringFromDocument(document);
                Assert.Equal(newSources[j++], actual, ignoreLineEndingDifferences: true);
            }
        }
Esempio n. 10
0
            /// <summary>
            /// Create an instance of <see cref="TestDiagnosticProvider"/>.
            /// </summary>
            /// <returns>The <see cref="TestDiagnosticProvider"/>.</returns>
            internal static async Task <TestDiagnosticProvider> CreateAsync(Solution solution, CodeFixProvider fix, string?fixTitle, IReadOnlyList <Diagnostic> diagnostics)
            {
                var actions    = new List <CodeAction>();
                var diagnostic = diagnostics.First();
                var context    = new CodeFixContext(solution.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), CancellationToken.None);
                await fix.RegisterCodeFixesAsync(context).ConfigureAwait(false);

                var action = FindAction(actions, fixTitle);

                return(new TestDiagnosticProvider(diagnostics, solution.GetDocument(diagnostics.First().Location.SourceTree), action.EquivalenceKey));
            }
        /// <summary>
        /// General verifier for codefixes.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Then gets the string after the codefix is applied and compares it with the expected result.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        /// <param name="languageVersionCSharp">C# language version used for compiling the test project, required unless you inform the VB language version.</param>
        /// <param name="languageVersionVB">VB language version used for compiling the test project, required unless you inform the C# language version.</param>
        private async static Task VerifyFixAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics, LanguageVersion languageVersionCSharp, Microsoft.CodeAnalysis.VisualBasic.LanguageVersion languageVersionVB)
        {
            var supportedDiagnostics      = analyzer.SupportedDiagnostics.Select(d => d.Id);
            var codeFixFixableDiagnostics = codeFixProvider.FixableDiagnosticIds;

            Assert.True(codeFixFixableDiagnostics.Any(d => supportedDiagnostics.Contains(d)), "Code fix provider does not fix the diagnostic provided by the analyzer.");
            var document            = CreateDocument(oldSource, language, languageVersionCSharp, languageVersionVB);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(true);

            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true);

            var attempts = analyzerDiagnostics.Length;

            for (int i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(true);

                if (!actions.Any())
                {
                    break;
                }

                document = codeFixIndex != null
                    ? await ApplyFixAsync(document, actions.ElementAt((int)codeFixIndex)).ConfigureAwait(true)
                    : await ApplyFixAsync(document, actions.ElementAt(0)).ConfigureAwait(true);

                analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(true);

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));

                //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 = document.WithSyntaxRoot(Formatter.Format(await document.GetSyntaxRootAsync().ConfigureAwait(true), Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));

                    Assert.True(false, $"Fix introduced new compiler diagnostics:\r\n{string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n\r\nNew document:\r\n{(await document.GetSyntaxRootAsync().ConfigureAwait(true)).ToFullString()}\r\n");
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            var actual = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

            Assert.Equal(newSource, actual);
        }
        // Helpers/Fixing
        private static async Task GetCodeFixActionsAsync(CodeFixProvider fixer, Project project, Diagnostic diagnostic, List <CodeAction> actions, CancellationToken cancellationToken)
        {
            if (!fixer.FixableDiagnosticIds.Contains(diagnostic.Id))
            {
                throw new ArgumentException($"Diagnostic is not supported by CodeFixProvider: Diagnostic={diagnostic.Id}, CodeFixProvider={fixer.GetType().Name}");
            }

            var tree     = diagnostic.Location.SourceTree ?? throw new Exception("Syntax tree is null");
            var document = project.GetDocument(tree) ?? throw new Exception("Document is not found");
            var context  = new CodeFixContext(document, diagnostic, (action, _) => actions.Add(action), cancellationToken);
            await fixer.RegisterCodeFixesAsync(context).ConfigureAwait(false);
        }
Esempio n. 13
0
        private ImmutableArray <CodeAction> GetCodeFixesForDiagnostic([NotNull] Diagnostic diagnostic, [NotNull] Document document,
                                                                      [NotNull] CodeFixProvider fixProvider)
        {
            ImmutableArray <CodeAction> .Builder builder = ImmutableArray.CreateBuilder <CodeAction>();
            Action <CodeAction, ImmutableArray <Diagnostic> > registerCodeFix = (codeAction, _) => builder.Add(codeAction);

            var fixContext = new CodeFixContext(document, diagnostic, registerCodeFix, CancellationToken.None);

            fixProvider.RegisterCodeFixesAsync(fixContext).Wait();

            return(builder.ToImmutable());
        }
        private static async Task <Document> GetSingleAnalyzerDocumentAsync(ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Document document, int maxNumberOfIterations, CancellationToken cancellationToken)
        {
            var previousDiagnostics = ImmutableArray.Create <Diagnostic>();

            bool done;

            do
            {
                var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false);

                if (analyzerDiagnostics.Length == 0)
                {
                    break;
                }

                if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics))
                {
                    break;
                }

                if (--maxNumberOfIterations < 0)
                {
                    Assert.True(false, "The upper limit for the number of code fix iterations was exceeded");
                }

                previousDiagnostics = analyzerDiagnostics;

                done = true;
                for (var i = 0; i < analyzerDiagnostics.Length; i++)
                {
                    var actions = new List <CodeAction>();
                    var context = new CodeFixContext(document, analyzerDiagnostics[i], (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)
                        {
                            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);
                            break;
                        }
                    }
                }
            }while (!done);

            return(document);
        }
Esempio n. 15
0
        private async Task VerifyFixAsync(string language, ImmutableArray <string> diagnosticIds, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics)
        {
            var document            = CreateDocument(oldSource, language);
            var compilerDiagnostics = (await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true)).ToList();
            var analyzerDiagnostics = compilerDiagnostics.Where(c => diagnosticIds.Contains(c.Id)).ToList();
            var attempts            = analyzerDiagnostics.Count();

            for (int i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(true);

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = await ApplyFixAsync(document, actions.ElementAt((int)codeFixIndex)).ConfigureAwait(true);

                    break;
                }

                document = await ApplyFixAsync(document, actions.ElementAt(0)).ConfigureAwait(true);

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));
                compilerDiagnostics = (await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true)).ToList();
                analyzerDiagnostics = compilerDiagnostics.Where(c => diagnosticIds.Contains(c.Id)).ToList();

                //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 = document.WithSyntaxRoot(Formatter.Format(await document.GetSyntaxRootAsync().ConfigureAwait(true), Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));

                    Assert.True(false, $"Fix introduced new compiler diagnostics:\r\n{string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n\r\nNew document:\r\n{(await document.GetSyntaxRootAsync().ConfigureAwait(true)).ToFullString()}\r\n");
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            var actual = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

            Assert.Equal(newSource, actual);
        }
        /// <summary>
        /// Apply codefixes and returns reslt.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="equivalenceKey">CodeAction.EquivalenceKey determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        internal static string GetFixResult(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string equivalenceKey, bool allowNewCompilerDiagnostics)
        {
            var document            = CodeAnalysisHelper.CreateDocument(oldSource, language);
            var analyzerDiagnostics = CodeAnalysisHelper.GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
            var compilerDiagnostics = CodeAnalysisHelper.GetCompilerDiagnostics(document);
            var attempts            = analyzerDiagnostics.Length;

            for (int i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();

                if (!actions.Any())
                {
                    break;
                }

                if (equivalenceKey != null)
                {
                    document = CodeAnalysisHelper.ApplyFix(document, actions.Single(n => n.EquivalenceKey == equivalenceKey));
                    break;
                }

                document            = CodeAnalysisHelper.ApplyFix(document, actions.ElementAt(0));
                analyzerDiagnostics = CodeAnalysisHelper.GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });

                var newCompilerDiagnostics = CodeAnalysisHelper.GetNewDiagnostics(compilerDiagnostics, CodeAnalysisHelper.GetCompilerDiagnostics(document));

                //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 = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = CodeAnalysisHelper.GetNewDiagnostics(compilerDiagnostics, CodeAnalysisHelper.GetCompilerDiagnostics(document));

                    Assert.IsTrue(false,
                                  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())),
                                                document.GetSyntaxRootAsync().Result.ToFullString()));
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            var newSource = CodeAnalysisHelper.GetStringFromDocument(document);

            return(newSource);
        }
Esempio n. 17
0
        /// <summary>
        /// General verifier for codefixes.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Then gets the string after the codefix is applied and compares it with the expected result.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// /// <param name="diagnosticId">Diagnostic id to fix</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        private void VerifyFix(string language, string diagnosticId, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics)
        {
            var document            = CreateDocument(oldSource, language);
            var compilerDiagnostics = GetCompilerDiagnostics(document);

            if (compilerDiagnostics.Any(x => x.Id == diagnosticId) == false)
            {
                var reportedIssues = compilerDiagnostics.Select(x => x.Id).ToList();
                Assert.Fail($"There is no issue reported for {diagnosticId}. Reported issues: {string.Join(',', reportedIssues)}");
            }


            foreach (var diagnosticToFix in compilerDiagnostics.Where(x => x.Id == diagnosticId))
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, diagnosticToFix, (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = ApplyFix(document, actions.ElementAt((int)codeFixIndex));
                    break;
                }

                document = ApplyFix(document, actions.ElementAt(0));

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                //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 = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));

                    Assert.IsTrue(false,
                                  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())),
                                                document.GetSyntaxRootAsync().Result.ToFullString()));
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            var actual = GetStringFromDocument(document);

            Assert.AreEqual(newSource, actual);
        }
Esempio n. 18
0
        /// <summary>
        /// Get the code actions registered by <paramref name="fix"/> for <paramref name="solution"/>.
        /// </summary>
        /// <param name="solution">The solution with the diagnostic.</param>
        /// <param name="fix">The code fix.</param>
        /// <param name="diagnostic">The diagnostic.</param>
        /// <returns>The list of registered actions.</returns>
        internal static async Task <IReadOnlyList <CodeAction> > GetActionsAsync(Solution solution, CodeFixProvider fix, Diagnostic diagnostic)
        {
            var document = solution.GetDocument(diagnostic.Location.SourceTree);
            var actions  = new List <CodeAction>();
            var context  = new CodeFixContext(
                document,
                diagnostic,
                (a, d) => actions.Add(a),
                CancellationToken.None);
            await fix.RegisterCodeFixesAsync(context).ConfigureAwait(false);

            return(actions);
        }
Esempio n. 19
0
        /// <summary>
        /// Get the code actions registered by <paramref name="codeFix"/> for <paramref name="solution"/>.
        /// </summary>
        /// <param name="solution">The solution with the diagnostic.</param>
        /// <param name="codeFix">The code fix.</param>
        /// <param name="diagnostic">The diagnostic.</param>
        /// <returns>The list of registered actions.</returns>
        internal static IReadOnlyList <CodeAction> GetActions(Solution solution, CodeFixProvider codeFix, Diagnostic diagnostic)
        {
            var document = solution.GetDocument(diagnostic.Location.SourceTree);
            var actions  = new List <CodeAction>();
            var context  = new CodeFixContext(
                document,
                diagnostic,
                (a, d) => actions.Add(a),
                CancellationToken.None);

            codeFix.RegisterCodeFixesAsync(context).GetAwaiter().GetResult();
            return(actions);
        }
Esempio n. 20
0
    public async Task InvokeAsync()
    {
        var actions = ImmutableArray.CreateBuilder <CodeAction>();

        void registerCodeFix(CodeAction a, ImmutableArray <Diagnostic> d) => actions.Add(a);

        var context = new CodeFixContext(document, diagnostic, registerCodeFix, CancellationToken.None);

        await provider.RegisterCodeFixesAsync(context).ConfigureAwait(false);

        var codeAction = actions.Single();

        operations = await codeAction.GetOperationsAsync(CancellationToken.None).ConfigureAwait(false);
    }
Esempio n. 21
0
        public async Task <string> GetEquivalenceKeyAsync(CodeFixProvider codeFixProvider, Document document, CancellationToken cancellationToken)
        {
            if (codeFixProvider == null)
            {
                throw new System.ArgumentException();
            }

            this.codeActions.Clear();
            await codeFixProvider
            .RegisterCodeFixesAsync(new CodeFixContext(document, this.diagnostics[0], (a, _) => codeActions.Add(a), cancellationToken))
            .ConfigureAwait(false);

            return(codeActions.Count > 0 ? codeActions[0].EquivalenceKey ! : string.Empty);
        }
        /// <summary>
        /// General verifier for a diagnostics that should not have fix registred.
        /// Creates a Document from the source string, then gets diagnostics on it and verify if it has no fix registred.
        /// It will fail the test if it has any fix registred to it
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="source">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="languageVersionCSharp">C# language version used for compiling the test project, required unless you inform the VB language version.</param>
        /// <param name="languageVersionVB">VB language version used for compiling the test project, required unless you inform the C# language version.</param>
        private async static Task VerifyHasNoFixAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source, LanguageVersion languageVersionCSharp, Microsoft.CodeAnalysis.VisualBasic.LanguageVersion languageVersionVB)
        {
            var document            = CreateDocument(source, language, languageVersionCSharp, languageVersionVB);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(true);

            foreach (var analyzerDiagnostic in analyzerDiagnostics)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostic, (a, d) => actions.Add(a), CancellationToken.None);
                await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(true);

                Assert.False(actions.Any(), $"Should not have a code fix registered for diagnostic '{analyzerDiagnostic.Id}'");
            }
        }
        void VerifyNoFixes(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source)
        {
            var document            = CreateDocument(source, language);
            var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document, });

            foreach (var analyzerDiagnostic in analyzerDiagnostics)
            {
                var context = new CodeFixContext(document, analyzerDiagnostic, (a, d) =>
                {
                    Assert.Fail($"Expected no code actions, got: {a}");
                }, default(CancellationToken));
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();
            }
        }
Esempio n. 24
0
    public static async Task <Project> ApplyCodeFix(this Project project, Diagnostic diagnostic, CodeFixProvider fix)
    {
        var document = project.Solution.GetDocument(diagnostic.Location.SourceTree);
        var actions  = new List <CodeAction>();
        var context  = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), CancellationToken.None);

        await fix.RegisterCodeFixesAsync(context);

        var operations = await actions.First().GetOperationsAsync(CancellationToken.None);

        var changeSolution = operations.OfType <ApplyChangesOperation>().First().ChangedSolution;
        var newProject     = changeSolution.Projects.First();

        return(newProject);
    }
Esempio n. 25
0
        private void VerifyNoFixOffered(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source, bool hasEntrypoint)
        {
            var document            = CreateDocument(source, language, hasEntrypoint);
            var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(ImmutableArray.Create(analyzer), new[] { document });
            var compilerDiagnostics = GetCompilerDiagnostics(document);
            var attempts            = analyzerDiagnostics.Length;

            for (int i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();
                Assert.Empty(actions);
            }
        }
        /// <summary>
        /// General verifier for a diagnostics that verifies how many code actions a code fix registered.
        /// Creates a Document from the source string, then gets diagnostics on it and verify if it has x number of code actions registred.
        /// It will fail the test if it has a different number of code actions registred to it.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="source">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="languageVersionCSharp">C# language version used for compiling the test project, required unless you inform the VB language version.</param>
        /// <param name="languageVersionVB">VB language version used for compiling the test project, required unless you inform the C# language version.</param>
        /// <param name="numberOfCodeActions">The expected number of code actions provided by the code fix.</param>
        private async static Task VerifyNumberOfCodeActions(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source, int numberOfCodeActions, LanguageVersion languageVersionCSharp, Microsoft.CodeAnalysis.VisualBasic.LanguageVersion languageVersionVB)
        {
            var document            = CreateDocument(source, language, languageVersionCSharp, languageVersionVB);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(true);

            foreach (var analyzerDiagnostic in analyzerDiagnostics)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostic, (a, d) => actions.Add(a), CancellationToken.None);
                await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(true);

                var numberOfCodeActionsFound = actions.Count();
                Assert.True(numberOfCodeActions == numberOfCodeActionsFound, $"Should have {numberOfCodeActions} code actions registered for diagnostic '{analyzerDiagnostic.Id}' but got {numberOfCodeActionsFound}.");
            }
        }
Esempio n. 27
0
        private async Task <FixedResult> FixCodeSingleDiagnostics(CancellationToken cancellationToken)
        {
            var codeActions = new List <CodeAction>();
            await codeFixProvider.RegisterCodeFixesAsync(new CodeFixContext(document, this.diagnostics[0], (a, _) => codeActions.Add(a), cancellationToken)).ConfigureAwait(false);

            if (codeActions.Count == 0)
            {
                return(FixedResult.Empty);
            }

            var oldSolution     = document.Project.Solution;
            var changedDocument = await ConvertCodeActionToChangedDocument(codeActions[0], cancellationToken).ConfigureAwait(false);

            return(GetFixedChanges(oldSolution, changedDocument.Project.Solution));
        }
        private async Task <ImmutableArray <CodeAction> > GetOfferedFixesInternalAsync(string language, string source, int?diagnosticIndex, ImmutableArray <DiagnosticAnalyzer> 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 <CodeAction>();
            var context = new CodeFixContext(document, analyzerDiagnostics[index], (a, d) => actions.Add(a), cancellationToken);
            await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);

            return(actions.ToImmutableArray());
        }
Esempio n. 29
0
        private async Task <ImmutableArray <CodeAction> > GetCodeFixesAsync(Document document, TextSpan span, DiagnosticDescriptor descriptor)
        {
            ImmutableArray <CodeAction> .Builder builder = ImmutableArray.CreateBuilder <CodeAction>();

            SyntaxTree tree = await document.GetSyntaxTreeAsync(CancellationToken.None).ConfigureAwait(false);

            var diagnostic = Diagnostic.Create(descriptor, Location.Create(tree, span));
            var context    = new CodeFixContext(document, diagnostic, (a, _) => builder.Add(a), CancellationToken.None);

            CodeFixProvider provider = this.CreateProvider();

            provider.RegisterCodeFixesAsync(context).Wait();

            return(builder.ToImmutable());
        }
Esempio n. 30
0
        /// <summary>
        /// General verifier for codefixes.
        /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
        /// Then gets the string after the codefix is applied and compares it with the expected result.
        /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
        /// </summary>
        /// <param name="language">The language the source code is in</param>
        /// <param name="analyzer">The analyzer to be applied to the source code</param>
        /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param>
        /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param>
        /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param>
        /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param>
        /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param>
        private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics)
        {
            var document            = CreateDocument(oldSource, language);
            var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
            var compilerDiagnostics = GetCompilerDiagnostics(document);
            var attempts            = analyzerDiagnostics.Length;

            for (var i = 0; i < attempts; ++i)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
                codeFixProvider.RegisterCodeFixesAsync(context).Wait();

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = ApplyFix(document, actions.ElementAt((int)codeFixIndex));
                    break;
                }

                document            = ApplyFix(document, actions.ElementAt(0));
                analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));


                //check if applying the code fix introduced any new compiler diagnostics
                if (!allowNewCompilerDiagnostics)
                {
                    newCompilerDiagnostics.ShouldBeEmpty(() => GetErrorMessage(document, compilerDiagnostics));
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }

            //after applying all of the code fixes, compare the resulting string to the inputted one
            var actual = GetStringFromDocument(document);

            actual.ShouldBe(newSource);
        }