Beispiel #1
0
        /// <summary>
        /// Fix the solution by applying the code fix.
        /// </summary>
        /// <param name="solution">The solution with the diagnostic.</param>
        /// <param name="fix">The code fix.</param>
        /// <param name="diagnostics">The diagnostics.</param>
        /// <param name="fixTitle">The expected title of the fix. Must be provided if more than one code action is registered. If only one pass null.</param>
        /// <returns>The fixed solution or the same instance if no fix.</returns>
        public static Solution Apply(Solution solution, CodeFixProvider fix, IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, string?fixTitle = null)
        {
            var flatDiagnostics = diagnostics.SelectMany(x => x).ToArray();

            if (flatDiagnostics.Length == 1)
            {
                return(Apply(solution, fix, flatDiagnostics[0], fixTitle));
            }

            var trees = flatDiagnostics.Select(x => x.Location.SourceTree).Distinct().ToArray();

            if (trees.Length == 1)
            {
                var document = solution.Projects.SelectMany(x => x.Documents)
                               .Single(x => x.GetSyntaxTreeAsync().GetAwaiter().GetResult() == trees[0]);
                var provider   = TestDiagnosticProvider.CreateAsync(solution, fix, fixTitle, flatDiagnostics).GetAwaiter().GetResult();
                var context    = new FixAllContext(document, fix, FixAllScope.Document, provider.EquivalenceKey, flatDiagnostics.Select(x => x.Id), provider, CancellationToken.None);
                var action     = WellKnownFixAllProviders.BatchFixer.GetFixAsync(context).GetAwaiter().GetResult();
                var operations = action.GetOperationsAsync(CancellationToken.None).GetAwaiter().GetResult();
                if (operations.TrySingleOfType(out ApplyChangesOperation? operation))
                {
                    return(operation !.ChangedSolution);
                }
            }

            throw new InvalidOperationException($"Failed applying fix, bug in Gu.Roslyn.Asserts");
        }
Beispiel #2
0
        private async Task TestFixAllAsync(int codepage, FixAllScope scope)
        {
            string[] testCode = new[] { "class Foo { }", "class Bar { }" };

            this.fileEncoding = Encoding.GetEncoding(codepage);

            // Create a project using the specified encoding
            Project project    = this.CreateProject(testCode);
            Project oldProject = project;

            Workspace workspace = project.Solution.Workspace;

            var codeFixer      = this.GetCSharpCodeFixProvider();
            var fixAllProvider = codeFixer.GetFixAllProvider();
            var diagnostics    = new List <Diagnostic>();
            var descriptor     = this.GetCSharpDiagnosticAnalyzers().First().SupportedDiagnostics.First();

            foreach (var document in project.Documents)
            {
                // Create a diagnostic for the document to fix
                var diagnostic = Diagnostic.Create(
                    descriptor,
                    Location.Create(await document.GetSyntaxTreeAsync().ConfigureAwait(false), TextSpan.FromBounds(0, 0)));
                diagnostics.Add(diagnostic);
            }

            FixAllContext fixAllContext = new FixAllContext(
                project.Documents.First(),
                codeFixer,
                scope,
                nameof(SA1412CodeFixProvider) + "." + this.fileEncoding.WebName,
                new[] { SA1412StoreFilesAsUtf8.DiagnosticId },
                TestDiagnosticProvider.Create(diagnostics.ToImmutableArray()),
                CancellationToken.None);

            CodeAction codeAction = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

            var operation = codeAction.GetOperationsAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()[0];

            operation.Apply(workspace, CancellationToken.None);

            // project should now have the "fixed document" in it.
            // Because of limitations in roslyn the fixed document should
            // have a different DocumentId then the broken document
            project = workspace.CurrentSolution.Projects.First();

            Assert.Equal(2, project.DocumentIds.Count);

            for (int i = 0; i < project.DocumentIds.Count; i++)
            {
                DocumentId documentId = project.DocumentIds[i];
                SourceText sourceText = await project.GetDocument(documentId).GetTextAsync().ConfigureAwait(false);

                Assert.Equal(testCode[i], sourceText.ToString());

                Assert.Equal(Encoding.UTF8, sourceText.Encoding);
                Assert.NotEqual(oldProject.DocumentIds[i], project.DocumentIds[i]);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Fix the solution by applying the code fix one fix at the time until it stops fixing the code.
        /// </summary>
        /// <returns>The fixed solution or the same instance if no fix.</returns>
        internal static async Task <Solution> ApplyAllFixableScopeByScopeAsync(Solution solution, DiagnosticAnalyzer analyzer, CodeFixProvider fix, FixAllScope scope, string?fixTitle = null, CancellationToken cancellationToken = default)
        {
            var fixable = await Analyze.GetFixableDiagnosticsAsync(solution, analyzer, fix).ConfigureAwait(false);

            var fixedSolution = solution;
            int count;

            do
            {
                count = fixable.Count;
                if (count == 0)
                {
                    return(fixedSolution);
                }

                var diagnosticProvider = await TestDiagnosticProvider.CreateAsync(fixedSolution, fix, fixTitle, fixable).ConfigureAwait(false);

                fixedSolution = await ApplyAsync(fix, scope, diagnosticProvider, cancellationToken).ConfigureAwait(false);

                fixable = await Analyze.GetFixableDiagnosticsAsync(fixedSolution, analyzer, fix).ConfigureAwait(false);
            }while (fixable.Count < count);
            return(fixedSolution);
        }
Beispiel #4
0
        private static async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken)
        {
            int expectedNumberOfIterations = numberOfIterations;

            if (numberOfIterations < 0)
            {
                numberOfIterations = -numberOfIterations;
            }

            var previousDiagnostics = ImmutableArray.Create <Diagnostic>();

            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(null);
            }

            bool done;

            do
            {
                var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false);

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

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

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

                Diagnostic firstDiagnostic = null;
                string     equivalenceKey  = null;
                foreach (var diagnostic in analyzerDiagnostics)
                {
                    if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
                    {
                        // do not pass unsupported diagnostics to a code fix provider
                        continue;
                    }

                    var actions = new List <CodeAction>();
                    var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), cancellationToken);
                    await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);

                    if (actions.Count > (codeFixIndex ?? 0))
                    {
                        firstDiagnostic = diagnostic;
                        equivalenceKey  = actions[codeFixIndex ?? 0].EquivalenceKey;
                        break;
                    }
                }

                if (firstDiagnostic == null)
                {
                    return(project);
                }

                previousDiagnostics = analyzerDiagnostics;

                done = true;

                FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics);

                IEnumerable <string> analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id);
                IEnumerable <string> compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal));
                IEnumerable <string> disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key);
                IEnumerable <string> relevantIds           = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct();
                FixAllContext        fixAllContext         = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken);

                CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (action == null)
                {
                    return(project);
                }

                var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false);

                if (fixedProject != project)
                {
                    done = false;

                    project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false);
                }
            }while (!done);

            if (expectedNumberOfIterations >= 0)
            {
                Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations");
            }

            return(project);
        }
        private async Task TestFixAllWithMultipleEncodingsAsync(FixAllScope scope)
        {
            string[] testCode = new[] { "class Foo { }", "class Bar { }" };

            this.fileEncoding = Encoding.Unicode;

            // Create a project using the specified encoding
            Project project = this.CreateProject(testCode);

            project = project.AddDocument("Test2.cs", SourceText.From("class FooBar { }", Encoding.UTF7)).Project;

            Project oldProject = project;

            Workspace workspace = project.Solution.Workspace;

            var codeFixer      = this.GetCSharpCodeFixProvider();
            var fixAllProvider = codeFixer.GetFixAllProvider();
            var diagnostics    = new List <Diagnostic>();
            var descriptor     = this.GetCSharpDiagnosticAnalyzers().First().SupportedDiagnostics.First();

            foreach (var document in project.Documents)
            {
                // Create a diagnostic for the document to fix
                var properties = ImmutableDictionary <string, string> .Empty.SetItem(SA1412StoreFilesAsUtf8.EncodingProperty, (await document.GetTextAsync().ConfigureAwait(false)).Encoding.WebName);

                var diagnostic = Diagnostic.Create(
                    descriptor,
                    Location.Create(await document.GetSyntaxTreeAsync().ConfigureAwait(false), TextSpan.FromBounds(0, 0)),
                    properties);
                diagnostics.Add(diagnostic);
            }

            FixAllContext fixAllContext = new FixAllContext(
                project.Documents.First(),
                codeFixer,
                scope,
                nameof(SA1412CodeFixProvider) + "." + this.fileEncoding.WebName,
                new[] { SA1412StoreFilesAsUtf8.DiagnosticId },
                TestDiagnosticProvider.Create(diagnostics.ToImmutableArray()),
                CancellationToken.None);

            CodeAction codeAction = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

            var operation = codeAction.GetOperationsAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()[0];

            operation.Apply(workspace, CancellationToken.None);

            // project should now have the "fixed document" in it.
            // Because of limitations in Roslyn the fixed document should
            // have a different DocumentId then the broken document
            project = workspace.CurrentSolution.Projects.First();

            Assert.Equal(3, project.DocumentIds.Count);

            // The order of the document ids is now different from the order before.
            for (int i = 1; i < 3; i++)
            {
                DocumentId documentId = project.DocumentIds[i];
                SourceText sourceText = await project.GetDocument(documentId).GetTextAsync().ConfigureAwait(false);

                Assert.Equal(testCode[i - 1], sourceText.ToString());

                Assert.Equal(Encoding.UTF8, sourceText.Encoding);
                Assert.NotEqual(oldProject.DocumentIds[i - 1], project.DocumentIds[i]);
            }

            // Check that Test2.cs was not changed
            DocumentId otherDocumentId = project.DocumentIds[0];
            var        otherDocument   = project.GetDocument(otherDocumentId);

            Assert.Equal("Test2.cs", otherDocument.Name);

            SourceText otherDocumentText = await otherDocument.GetTextAsync().ConfigureAwait(false);

            Assert.Equal("class FooBar { }", otherDocumentText.ToString());

            Assert.Equal(Encoding.UTF7, otherDocumentText.Encoding);
            Assert.Equal(oldProject.DocumentIds[2], project.DocumentIds[0]);
        }
Beispiel #6
0
        /// <summary>
        /// Fix the solution by applying the code fix.
        /// </summary>
        /// <returns>The fixed solution or the same instance if no fix.</returns>
        internal static async Task <Solution> ApplyAsync(CodeFixProvider fix, FixAllScope scope, TestDiagnosticProvider diagnosticProvider, CancellationToken cancellationToken)
        {
            var context = new FixAllContext(
                diagnosticProvider.Document,
                fix,
                scope,
                diagnosticProvider.EquivalenceKey,
                fix.FixableDiagnosticIds,
                diagnosticProvider,
                cancellationToken);
            var action = await fix.GetFixAllProvider().GetFixAsync(context).ConfigureAwait(false);

            var operations = await action.GetOperationsAsync(cancellationToken)
                             .ConfigureAwait(false);

            if (operations.TrySingleOfType(out ApplyChangesOperation? operation))
            {
                return(operation !.ChangedSolution);
            }

            throw new InvalidOperationException($"Expected one operation, was {string.Join(", ", operations)}");
        }
        private static async Task <Document> GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Document document, int maxNumberOfIterations, CancellationToken cancellationToken)
        {
            var previousDiagnostics = ImmutableArray.Create <Diagnostic>();

            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(null);
            }

            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 fix all iterations was exceeded");
                }

                string equivalenceKey = null;
                foreach (var diagnostic in analyzerDiagnostics)
                {
                    var actions = new List <CodeAction>();
                    var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken);
                    await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);

                    if (actions.Count > (codeFixIndex ?? 0))
                    {
                        equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey;
                        break;
                    }
                }

                previousDiagnostics = analyzerDiagnostics;

                done = true;

                FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics);

                FixAllContext fixAllContext = new FixAllContext(document, codeFixProvider, scope, equivalenceKey, codeFixProvider.FixableDiagnosticIds, fixAllDiagnosticProvider, cancellationToken);

                CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (action == null)
                {
                    return(document);
                }

                var fixedDocument = await ApplyFixAsync(document, action, 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);
                }
            }while (!done);

            return(document);
        }
Beispiel #8
0
        /// <summary>
        /// Fix the solution by applying the code fix.
        /// </summary>
        /// <returns>The fixed solution or the same instance if no fix.</returns>
        internal static async Task <Solution> ApplyAsync(CodeFixProvider codeFix, FixAllScope scope, TestDiagnosticProvider diagnosticProvider, CancellationToken cancellationToken)
        {
            var context = new FixAllContext(
                diagnosticProvider.Document,
                codeFix,
                scope,
                diagnosticProvider.EquivalenceKey,
                codeFix.FixableDiagnosticIds,
                diagnosticProvider,
                cancellationToken);
            var action = await codeFix.GetFixAllProvider().GetFixAsync(context).ConfigureAwait(false);

            var operations = await action.GetOperationsAsync(cancellationToken)
                             .ConfigureAwait(false);

            return(operations.OfType <ApplyChangesOperation>()
                   .Single()
                   .ChangedSolution);
        }