Exemplo n.º 1
0
        private static async Task <(CodeAction, Document)> GetFixAsync(
            ImmutableArray <Diagnostic> diagnostics,
            DiagnosticDescriptor descriptor,
            CodeFixProvider fixer,
            Project project,
            CodeFixerOptions options,
            IFormatProvider formatProvider      = null,
            CancellationToken cancellationToken = default)
        {
            if (diagnostics.Length == 1)
            {
                return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
            }

            FixAllProvider fixAllProvider = fixer.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                if (options.DiagnosticIdsFixableOneByOne.Contains(descriptor.Id))
                {
                    return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
                }

                WriteLine($"  '{fixer.GetType().FullName}' does not have FixAllProvider", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(default);
Exemplo n.º 2
0
        internal IEnumerable <Tuple <Diagnostic, CodeFixCollection> > GetDiagnosticAndFixes(
            IEnumerable <Diagnostic> diagnostics,
            DiagnosticAnalyzer provider,
            CodeFixProvider fixer,
            TestDiagnosticAnalyzerDriver testDriver,
            Document document,
            TextSpan span,
            string annotation,
            string fixAllActionId)
        {
            foreach (var diagnostic in diagnostics)
            {
                if (annotation == null)
                {
                    var fixes   = new List <CodeFix>();
                    var context = new CodeFixContext(document, diagnostic, (a, d) => fixes.Add(new CodeFix(a, d)), CancellationToken.None);
                    fixer.RegisterCodeFixesAsync(context).Wait();
                    if (fixes.Any())
                    {
                        var codeFix = new CodeFixCollection(fixer, diagnostic.Location.SourceSpan, fixes);
                        yield return(Tuple.Create(diagnostic, codeFix));
                    }
                }
                else
                {
                    var fixAllProvider = fixer.GetFixAllProvider();
                    Assert.NotNull(fixAllProvider);
                    FixAllScope scope = GetFixAllScope(annotation);

                    Func <Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync =
                        (d, diagIds, c) =>
                    {
                        var root  = d.GetSyntaxRootAsync().Result;
                        var diags = testDriver.GetDocumentDiagnostics(provider, d, root.FullSpan);
                        diags = diags.Where(diag => diagIds.Contains(diag.Id));
                        return(Task.FromResult(diags));
                    };

                    Func <Project, bool, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync =
                        (p, includeAllDocumentDiagnostics, diagIds, c) =>
                    {
                        var diags = includeAllDocumentDiagnostics ?
                                    testDriver.GetAllDiagnostics(provider, p) :
                                    testDriver.GetProjectDiagnostics(provider, p);
                        diags = diags.Where(diag => diagIds.Contains(diag.Id));
                        return(Task.FromResult(diags));
                    };

                    var diagnosticIds            = ImmutableHashSet.Create(diagnostic.Id);
                    var fixAllDiagnosticProvider = new FixAllCodeActionContext.FixAllDiagnosticProvider(diagnosticIds, getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync);
                    var fixAllContext            = new FixAllContext(document, fixer, scope, fixAllActionId, diagnosticIds, fixAllDiagnosticProvider, CancellationToken.None);
                    var fixAllFix = fixAllProvider.GetFixAsync(fixAllContext).WaitAndGetResult(CancellationToken.None);
                    if (fixAllFix != null)
                    {
                        var codeFix = new CodeFixCollection(fixAllProvider, diagnostic.Location.SourceSpan, ImmutableArray.Create(new CodeFix(fixAllFix, diagnostic)));
                        yield return(Tuple.Create(diagnostic, codeFix));
                    }
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets an optional <see cref="FixAllProviderInfo"/> for the given code fix provider.
        /// </summary>
        private static FixAllProviderInfo?CreateWithCodeFixer(CodeFixProvider provider)
        {
            var fixAllProvider = provider.GetFixAllProvider();

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

            var diagnosticIds = fixAllProvider.GetSupportedFixAllDiagnosticIds(provider);

            if (diagnosticIds == null || diagnosticIds.IsEmpty())
            {
                return(null);
            }

            var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty();

            if (scopes.IsEmpty)
            {
                return(null);
            }

            return(new CodeFixerFixAllProviderInfo(fixAllProvider, diagnosticIds, scopes));
        }
Exemplo n.º 4
0
        internal static async Task <ImmutableList <CodeFixEquivalenceGroup> > CreateAsync(CodeFixProvider codeFixProvider, IEnumerable <Diagnostic> allDiagnostics, Solution solution)
        {
            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            var relevantDiagnostics = allDiagnostics.Where(diagnostic => codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)).ToImmutableArray();

            if (fixAllProvider == null)
            {
                return(ImmutableList.Create <CodeFixEquivalenceGroup>());
            }

            List <CodeAction> actions = new List <CodeAction>();

            foreach (var diagnostic in relevantDiagnostics)
            {
                actions.AddRange(await GetFixesAsync(solution, codeFixProvider, diagnostic).ConfigureAwait(false));
            }

            List <CodeFixEquivalenceGroup> groups = new List <CodeFixEquivalenceGroup>();

            foreach (var item in actions.GroupBy(x => x.EquivalenceKey))
            {
                groups.Add(new CodeFixEquivalenceGroup(item.Key, solution, fixAllProvider, codeFixProvider, relevantDiagnostics));
            }

            return(groups.ToImmutableList());
        }
Exemplo n.º 5
0
        private static void VerifyFixAllCodeFix(string path, string pathToExpected, DiagnosticAnalyzer diagnosticAnalyzer,
                                                CodeFixProvider codeFixProvider, string codeFixTitle)
        {
            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return;
            }

            using (var workspace = new AdhocWorkspace())
            {
                var file         = new FileInfo(path);
                var parseOptions = GetParseOptionsWithDifferentLanguageVersions(null, file.Extension);

                foreach (var parseOption in parseOptions)
                {
                    var document = CreateProject(file, GeneratedAssemblyName, workspace)
                                   .AddDocument(file, true)
                                   .Documents
                                   .Single(d => d.Name == file.Name);
                    RunFixAllProvider(diagnosticAnalyzer, codeFixProvider, codeFixTitle, fixAllProvider, document, parseOption, pathToExpected);
                }
            }
        }
Exemplo n.º 6
0
        private async Task <(ImmutableArray <Diagnostic>, ImmutableArray <CodeAction>, CodeAction actionToinvoke)> GetDiagnosticAndFixesAsync(
            IEnumerable <Diagnostic> diagnostics,
            DiagnosticAnalyzer provider,
            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, provider, 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);
            }
        }
Exemplo n.º 7
0
        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);
        }
Exemplo n.º 8
0
                static XElement CreateFixAllProviderElement(CodeFixProvider fixer)
                {
                    FixAllProvider fixAllProvider = fixer.GetFixAllProvider();

                    if (fixAllProvider != null)
                    {
                        return(new XElement("FixAllProvider", new XAttribute("Name", fixAllProvider.GetType().FullName)));
                    }

                    return(null);
                }
        private async static Task VerifyFixAllAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, bool allowNewCompilerDiagnostics, LanguageVersion languageVersionCSharp, Microsoft.CodeAnalysis.VisualBasic.LanguageVersion languageVersionVB, string equivalenceKey = null)
        {
            var document            = CreateDocument(oldSource, language, languageVersionCSharp, languageVersionVB);
            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true);

            var getDocumentDiagnosticsAsync = analyzer != null
                ? (Func <Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > >)(async(doc, ids, ct) =>
                                                                                                                     await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { doc }).ConfigureAwait(true))
                : (async(doc, ids, ct) =>
            {
                var compilerDiags = await GetCompilerDiagnosticsAsync(doc).ConfigureAwait(true);
                return(compilerDiags.Where(d => codeFixProvider.FixableDiagnosticIds.Contains(d.Id)));
            });
            Func <Project, bool, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = async(proj, b, ids, ct) =>
            {
                var theDocs = proj.Documents;
                var diags   = await Task.WhenAll(theDocs.Select(d => getDocumentDiagnosticsAsync?.Invoke(d, ids, ct))).ConfigureAwait(true);

                return(diags.SelectMany(d => d));
            };
            var fixAllProvider           = codeFixProvider.GetFixAllProvider();
            var fixAllDiagnosticProvider = new FixAllDiagnosticProvider(codeFixProvider.FixableDiagnosticIds.ToImmutableHashSet(), getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync);

            if (equivalenceKey == null)
            {
                equivalenceKey = codeFixProvider.GetType().Name;
            }
            var fixAllContext = new FixAllContext(document, codeFixProvider, FixAllScope.Document,
                                                  equivalenceKey,
                                                  codeFixProvider.FixableDiagnosticIds,
                                                  fixAllDiagnosticProvider,
                                                  CancellationToken.None);
            var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(true);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }
            document = await ApplyFixAsync(document, action).ConfigureAwait(true);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));

            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");
            }
            var actual = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

            Assert.Equal(newSource, actual);
        }
Exemplo n.º 10
0
        private async Task VerifyFixAllAsync(CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, bool allowNewCompilerDiagnostics)
        {
            var diagnosticIds       = codeFixProvider.FixableDiagnosticIds;
            var project             = CreateProject(oldSources, LanguageNames.CSharp);
            var compilerDiagnostics = (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d);
            Func <Document, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync = async doc =>
            {
                var compilerDiags = await GetCompilerDiagnosticsAsync(doc).ConfigureAwait(true);

                return(compilerDiags.Where(d => diagnosticIds.Contains(d.Id)));
            };
            Func <Project, bool, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = async(proj, b) =>
            {
                var theDocs = proj.Documents;
                var diags   = await Task.WhenAll(theDocs.Select(d => getDocumentDiagnosticsAsync(d))).ConfigureAwait(true);

                return(diags.SelectMany(d => d));
            };
            var fixAllProvider = codeFixProvider.GetFixAllProvider();
            var fixAllContext  = NewFixAllContext(null, project, codeFixProvider, FixAllScope.Solution,
                                                  null,//code action ids in codecracker are always null
                                                  diagnosticIds,
                                                  (doc, diagIds, cancelationToken) => getDocumentDiagnosticsAsync(doc),
                                                  (theProject, b, diagIds, cancelationToken) => getProjectDiagnosticsAsync(theProject, b),
                                                  CancellationToken.None);

            var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(true);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }

            project = await ApplyFixAsync(project, action).ConfigureAwait(true);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d));

            if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
            {
                Assert.True(false, $"Fix introduced new compiler diagnostics:\r\n{string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n");
            }

            var docs = project.Documents.ToArray();

            for (int i = 0; i < docs.Length; i++)
            {
                var document = docs[i];
                var actual   = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

                Assert.Equal(newSources[i], actual);
            }
        }
        private bool?IsBatchFixer(CodeFixProvider provider)
        {
            var fixAllProvider = provider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(null);
            }
            else
            {
                return(fixAllProvider.GetType() == _batchFixerType);
            }
        }
Exemplo n.º 12
0
        private async Task VerifyFixAllAsync(string language, CodeFixProvider codeFixProvider, string oldSource, string newSource, bool allowNewCompilerDiagnostics)
        {
            var diagnosticIds       = codeFixProvider.FixableDiagnosticIds;
            var document            = CreateDocument(oldSource, language);
            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true);

            Func <Document, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync = async doc =>
            {
                var compilerDiags = await GetCompilerDiagnosticsAsync(doc).ConfigureAwait(true);

                return(compilerDiags.Where(d => diagnosticIds.Contains(d.Id)));
            };
            Func <Project, bool, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = async(proj, b) =>
            {
                var theDocs = proj.Documents;
                var diags   = await Task.WhenAll(theDocs.Select(d => getDocumentDiagnosticsAsync(d))).ConfigureAwait(true);

                return(diags.SelectMany(d => d));
            };
            var fixAllProvider = codeFixProvider.GetFixAllProvider();
            var fixAllContext  = NewFixAllContext(document, document.Project, codeFixProvider, FixAllScope.Document,
                                                  null,//code action ids in codecracker are always null
                                                  diagnosticIds,
                                                  (doc, diagIds, cancelationToken) => getDocumentDiagnosticsAsync(doc),
                                                  (theProject, b, diagIds, cancelationToken) => getProjectDiagnosticsAsync(theProject, b),
                                                  CancellationToken.None);

            var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(true);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }

            document = await ApplyFixAsync(document, action).ConfigureAwait(true);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document).ConfigureAwait(true));

            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");
            }

            var actual = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

            Assert.Equal(newSource, actual);
        }
Exemplo n.º 13
0
        public async Task <Solution> ApplyCodeFixesAsync(
            Solution solution,
            CodeAnalysisResult result,
            CodeFixProvider codeFix,
            string diagnosticId,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            var fixAllProvider = codeFix.GetFixAllProvider();

            if (fixAllProvider?.GetSupportedFixAllScopes()?.Contains(FixAllScope.Solution) != true)
            {
                logger.LogWarning(Resources.Unable_to_fix_0_Code_fix_1_doesnt_support_Fix_All_in_Solution, diagnosticId, codeFix.GetType().Name);
                return(solution);
            }

            var project = solution.Projects.FirstOrDefault();

            if (project == null)
            {
                throw new InvalidOperationException(string.Format(Resources.Solution_0_has__no_projects, solution));
            }

            var fixAllContext = new FixAllContext(
                project: project,
                codeFixProvider: codeFix,
                scope: FixAllScope.Solution,
                codeActionEquivalenceKey: null,
                diagnosticIds: codeFix.FixableDiagnosticIds,
                fixAllDiagnosticProvider: new DiagnosticProvider(result),
                cancellationToken: cancellationToken);

            try
            {
                var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                var operations = action != null
                    ? await action.GetOperationsAsync(cancellationToken).ConfigureAwait(false)
                    : ImmutableArray <CodeActionOperation> .Empty;

                var applyChangesOperation = operations.OfType <ApplyChangesOperation>().SingleOrDefault();
                return(applyChangesOperation?.ChangedSolution ?? solution);
            }
            catch (Exception ex)
            {
                logger.LogWarning(Resources.Failed_to_apply_code_fix_0_for_1_2, codeFix?.GetType().Name, diagnosticId, ex.Message);
                return(solution);
            }
        }
Exemplo n.º 14
0
        private void VerifyFixAll(
            Document document,
            DiagnosticAnalyzer analyzerOpt,
            CodeFixProvider codeFixProvider,
            string newSource,
            string newSourceFileName,
            IEnumerable <TestAdditionalDocument> additionalFiles,
            bool allowNewCompilerDiagnostics,
            TestValidationMode validationMode)
        {
            var fixableDiagnosticIds = codeFixProvider.FixableDiagnosticIds.ToSet();
            Func <IEnumerable <Diagnostic>, ImmutableArray <Diagnostic> > getFixableDiagnostics = diags =>
                                                                                                  diags.Where(d => fixableDiagnosticIds.Contains(d.Id)).ToImmutableArrayOrEmpty();

            var analyzerDiagnostics = GetSortedDiagnostics(analyzerOpt, new[] { document }, additionalFiles: additionalFiles, validationMode: validationMode);
            var compilerDiagnostics = document.GetSemanticModelAsync().Result.GetDiagnostics();
            var fixableDiagnostics  = getFixableDiagnostics(analyzerDiagnostics.Concat(compilerDiagnostics));

            var fixAllProvider     = codeFixProvider.GetFixAllProvider();
            var diagnosticProvider = new FixAllDiagnosticProvider(analyzerOpt, additionalFiles, validationMode, getFixableDiagnostics);
            var fixAllContext      = new FixAllContext(document, codeFixProvider, FixAllScope.Document, string.Empty, fixableDiagnostics.Select(d => d.Id), diagnosticProvider, CancellationToken.None);
            var codeAction         = fixAllProvider.GetFixAsync(fixAllContext).Result;

            document        = document.Apply(codeAction);
            additionalFiles = document.Project.AdditionalDocuments.Select(a => new TestAdditionalDocument(a));

            additionalFiles = document.Project.AdditionalDocuments.Select(a => new TestAdditionalDocument(a));

            analyzerDiagnostics = GetSortedDiagnostics(analyzerOpt, new[] { document }, additionalFiles: additionalFiles, validationMode: validationMode);

            var updatedCompilerDiagnostics = document.GetSemanticModelAsync().Result.GetDiagnostics();
            var newCompilerDiagnostics     = GetNewDiagnostics(compilerDiagnostics, updatedCompilerDiagnostics);

            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, document.GetSemanticModelAsync().Result.GetDiagnostics());

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

            var actualText = GetActualTextForNewDocument(document, newSourceFileName);

            Assert.Equal(newSource, actualText.ToString());
        }
        public static async Task <ImmutableArray <CodeFixEquivalenceGroup> > CreateAsync(CodeFixProvider codeFixProvider, ImmutableArray <Diagnostic> allDiagnostics, Project project, CancellationToken cancellationToken)
        {
            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(ImmutableArray.Create <CodeFixEquivalenceGroup>());
            }

            var groupLookup     = new Dictionary <string, Builder>();
            var equivalenceKeys = new HashSet <string>();

            foreach (var diagnostic in allDiagnostics)
            {
                if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
                {
                    continue;
                }

                var codeActions = await GetFixesAsync(project, codeFixProvider, diagnostic, cancellationToken).ConfigureAwait(false);

                equivalenceKeys.Clear();

                foreach (var action in codeActions)
                {
                    if (action.EquivalenceKey != null)
                    {
                        equivalenceKeys.Add(action.EquivalenceKey);
                    }
                }

                foreach (var key in equivalenceKeys)
                {
                    if (!groupLookup.TryGetValue(key, out var group))
                    {
                        groupLookup.Add(key, group = new Builder(key, project, fixAllProvider, codeFixProvider));
                    }

                    group.AddDiagnostic(diagnostic);
                }
            }

            return(groupLookup.Select(x => x.Value.ToEquivalenceGroup()).ToImmutableArray());
        }
Exemplo n.º 16
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);
        }
Exemplo n.º 17
0
        private async Task VerifyFixAllAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, bool allowNewCompilerDiagnostics)
        {
            var project             = CreateProject(oldSources, LanguageNames.CSharp);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, project.Documents.ToArray()).ConfigureAwait(true);

            var compilerDiagnostics = (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d);

            var fixAllProvider = codeFixProvider.GetFixAllProvider();
            var fixAllContext  = NewFixAllContext(null, project, codeFixProvider, FixAllScope.Solution,
                                                  null,//code action ids in codecracker are always null
                                                  codeFixProvider.FixableDiagnosticIds,
                                                  (doc, diagnosticIds, cancelationToken) => Task.FromResult(analyzerDiagnostics.Where(d => d.Location.SourceTree.FilePath == doc.Name)),
                                                  (theProject, b, diagnosticIds, cancelationToken) => Task.FromResult((IEnumerable <Diagnostic>)analyzerDiagnostics), //todo: verify, probably wrong
                                                  CancellationToken.None);

            var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(true);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }

            project = await ApplyFixAsync(project, action).ConfigureAwait(true);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d));

            if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
            {
                Assert.True(false, $"Fix introduced new compiler diagnostics:\r\n{string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n");
            }

            var docs = project.Documents.ToArray();

            for (int i = 0; i < docs.Length; i++)
            {
                var document = docs[i];
                var actual   = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

                Assert.Equal(newSources[i], actual);
            }
        }
        /// <summary>
        /// Verifies that
        /// 1. <paramref name="diagnosticsAndSources"/> produces the expected diagnostics
        /// 2. The code fix fixes the code.
        /// </summary>
        /// <param name="analyzer">The analyzer to run on the code..</param>
        /// <param name="codeFix">The code fix to apply.</param>
        /// <param name="diagnosticsAndSources">The code and expected diagnostics.</param>
        /// <param name="fixedCode">The expected code produced by the code fix.</param>
        /// <param name="suppressedDiagnostics">The diagnostics to suppress when compiling.</param>
        /// <param name="metadataReferences">The meta data metadataReferences to add to the compilation.</param>
        /// <param name="fixTitle">The title of the fix to apply if more than one.</param>
        /// <param name="allowCompilationErrors">If compilation errors are accepted in the fixed code.</param>
        public static void FixAll(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, DiagnosticsAndSources diagnosticsAndSources, IReadOnlyList <string> fixedCode, IEnumerable <string> suppressedDiagnostics, IEnumerable <MetadataReference> metadataReferences, string fixTitle, AllowCompilationErrors allowCompilationErrors)
        {
            VerifyAnalyzerSupportsDiagnostics(analyzer, diagnosticsAndSources.ExpectedDiagnostics);
            VerifyCodeFixSupportsAnalyzer(analyzer, codeFix);
            var sln         = CodeFactory.CreateSolution(diagnosticsAndSources, analyzer, suppressedDiagnostics, metadataReferences);
            var diagnostics = Analyze.GetDiagnostics(analyzer, sln);

            VerifyDiagnostics(diagnosticsAndSources, diagnostics);
            FixAllOneByOne(analyzer, codeFix, sln, fixedCode, fixTitle, allowCompilationErrors);

            var fixAllProvider = codeFix.GetFixAllProvider();

            if (fixAllProvider != null)
            {
                foreach (var scope in fixAllProvider.GetSupportedFixAllScopes())
                {
                    FixAllByScope(analyzer, codeFix, sln, fixedCode, fixTitle, allowCompilationErrors, scope);
                }
            }
        }
Exemplo n.º 19
0
        /// <summary>
        /// Verifies that
        /// 1. <paramref name="diagnosticsAndSources"/> produces the expected diagnostics
        /// 2. The code fix fixes the code.
        /// </summary>
        /// <param name="analyzer">The analyzer to run on the code..</param>
        /// <param name="codeFix">The code fix to apply.</param>
        /// <param name="diagnosticsAndSources">The code and expected diagnostics</param>
        /// <param name="fixedCode">The expected code produced by the code fix.</param>
        /// <param name="compilationOptions">The <see cref="CSharpCompilationOptions"/> to use.</param>
        /// <param name="metadataReference">The meta data metadataReference to add to the compilation.</param>
        /// <param name="fixTitle">The title of the fix to apply if more than one.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        /// <param name="allowCompilationErrors">If compilation errors are accepted in the fixed code.</param>
        public static async Task FixAllAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, DiagnosticsAndSources diagnosticsAndSources, IReadOnlyList <string> fixedCode, CSharpCompilationOptions compilationOptions, IReadOnlyList <MetadataReference> metadataReference, string fixTitle, AllowCompilationErrors allowCompilationErrors)
        {
            var data = await CreateDiagnosticsMetadataAsync(
                analyzer,
                codeFix,
                diagnosticsAndSources,
                compilationOptions,
                metadataReference);

            await FixAllOneByOneAsync(analyzer, codeFix, fixedCode, fixTitle, allowCompilationErrors, data).ConfigureAwait(false);

            var fixAllProvider = codeFix.GetFixAllProvider();

            if (fixAllProvider != null)
            {
                foreach (var scope in fixAllProvider.GetSupportedFixAllScopes())
                {
                    await FixAllByScopeAsync(analyzer, codeFix, fixedCode, fixTitle, allowCompilationErrors, data, scope);
                }
            }
        }
Exemplo n.º 20
0
        private static void VerifyFixAllCodeFix(string path, string pathToExpected, DiagnosticAnalyzer diagnosticAnalyzer,
                                                CodeFixProvider codeFixProvider, string codeFixTitle)
        {
            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return;
            }

            using (var workspace = new AdhocWorkspace())
            {
                var document = GetDocument(path, GeneratedAssemblyName, workspace);

                List <Diagnostic> diagnostics;
                string            actualCode;
                CalculateDiagnosticsAndCode(diagnosticAnalyzer, document, out diagnostics, out actualCode);

                Assert.AreNotEqual(0, diagnostics.Count);

                var fixAllDiagnosticProvider = new FixAllDiagnosticProvider(
                    codeFixProvider.FixableDiagnosticIds.ToImmutableHashSet(),
                    (doc, ids, ct) => Task.FromResult(
                        GetDiagnostics(document.Project.GetCompilationAsync().Result, diagnosticAnalyzer)),
                    null);
                var fixAllContext = new FixAllContext(document, codeFixProvider, FixAllScope.Document,
                                                      codeFixTitle,
                                                      codeFixProvider.FixableDiagnosticIds,
                                                      fixAllDiagnosticProvider,
                                                      CancellationToken.None);
                var codeActionToExecute = fixAllProvider.GetFixAsync(fixAllContext).Result;

                Assert.IsNotNull(codeActionToExecute);

                document = ApplyCodeFix(document, codeActionToExecute);

                CalculateDiagnosticsAndCode(diagnosticAnalyzer, document, out diagnostics, out actualCode);
                Assert.AreEqual(File.ReadAllText(pathToExpected), actualCode);
            }
        }
Exemplo n.º 21
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)}");
        }
Exemplo n.º 22
0
        private async Task VerifyFixAllAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, bool allowNewCompilerDiagnostics)
        {
            var document            = CreateDocument(oldSource, LanguageNames.CSharp);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document });

            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document);

            var fixAllProvider = codeFixProvider.GetFixAllProvider();
            var fixAllContext  = NewFixAllContext(document, document.Project, codeFixProvider, FixAllScope.Document,
                                                  null,//code action ids in codecracker are always null
                                                  analyzerDiagnostics.Select(a => a.Id),
                                                  (project, doc, diagnosticIds, cancelationToken) => Task.FromResult <IEnumerable <Diagnostic> >(analyzerDiagnostics),
                                                  CancellationToken.None);

            var action = await fixAllProvider.GetFixAsync(fixAllContext);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }

            document = await ApplyFixAsync(document, action);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document));

            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(), Formatter.Annotation, document.Project.Solution.Workspace));
                newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document));
                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()).ToFullString()}\r\n");
            }

            var actual = await GetStringFromDocumentAsync(document);

            Assert.Equal(newSource, actual);
        }
Exemplo n.º 23
0
        public async Task <Solution> ApplyCodeFixesAsync(
            Solution solution,
            CodeAnalysisResult result,
            CodeFixProvider codeFix,
            string diagnosticId,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            var fixAllProvider = codeFix.GetFixAllProvider();

            if (fixAllProvider?.GetSupportedFixAllScopes()?.Contains(FixAllScope.Solution) != true)
            {
                logger.LogWarning(Resources.Unable_to_fix_0_Code_fix_1_doesnt_support_Fix_All_in_Solution, diagnosticId, codeFix.GetType().Name);
                return(solution);
            }

            var document = result.Diagnostics
                           .SelectMany(kvp => kvp.Value)
                           .Select(diagnostic => solution.GetDocument(diagnostic.Location.SourceTree))
                           .FirstOrDefault();

            if (document is null)
            {
                return(solution);
            }

            var fixAllContext = new FixAllContext(
                document: document,
                codeFixProvider: codeFix,
                scope: FixAllScope.Solution,
                codeActionEquivalenceKey: null,
                diagnosticIds: new[] { diagnosticId },
                fixAllDiagnosticProvider: new DiagnosticProvider(result),
                cancellationToken: cancellationToken);

            try
            {
                var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (action is null)
                {
                    logger.LogWarning(Resources.Unable_to_fix_0_Code_fix_1_didnt_return_a_Fix_All_action, diagnosticId, codeFix.GetType().Name);
                    return(solution);
                }

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

                var applyChangesOperation = operations.OfType <ApplyChangesOperation>().SingleOrDefault();
                if (action is null)
                {
                    logger.LogWarning(Resources.Unable_to_fix_0_Code_fix_1_returned_an_unexpected_operation, diagnosticId, codeFix.GetType().Name);
                    return(solution);
                }

                return(applyChangesOperation.ChangedSolution);
            }
            catch (Exception ex)
            {
                logger.LogWarning(Resources.Failed_to_apply_code_fix_0_for_1_2, codeFix?.GetType().Name, diagnosticId, ex.Message);
                return(solution);
            }
        }
Exemplo n.º 24
0
        private async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, ImmutableArray <CodeFixProvider> codeFixProviders, int?codeFixIndex, string codeFixEquivalenceKey, Project project, int numberOfIterations, IVerifier verifier, CancellationToken cancellationToken)
        {
            var expectedNumberOfIterations = numberOfIterations;

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

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

            bool done;

            do
            {
                var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, CompilerDiagnostics, cancellationToken).ConfigureAwait(false);

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

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

                verifier.False(--numberOfIterations < -1, "The upper limit for the number of fix all iterations was exceeded");

                Diagnostic      firstDiagnostic          = null;
                CodeFixProvider effectiveCodeFixProvider = null;
                string          equivalenceKey           = null;
                foreach (var diagnostic in analyzerDiagnostics)
                {
                    var actions = new List <(CodeAction, CodeFixProvider)>();

                    foreach (var codeFixProvider in codeFixProviders)
                    {
                        if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
                        {
                            // do not pass unsupported diagnostics to a code fix provider
                            continue;
                        }

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

                    var actionToApply = TryGetCodeActionToApply(actions.Select(a => a.Item1).ToList(), codeFixIndex, codeFixEquivalenceKey, verifier);
                    if (actionToApply != null)
                    {
                        firstDiagnostic          = diagnostic;
                        effectiveCodeFixProvider = actions.SingleOrDefault(a => a.Item1 == actionToApply).Item2;
                        equivalenceKey           = actionToApply.EquivalenceKey;
                        break;
                    }
                }

                var fixAllProvider = effectiveCodeFixProvider?.GetFixAllProvider();
                if (firstDiagnostic == null || fixAllProvider == null)
                {
                    numberOfIterations++;
                    break;
                }

                previousDiagnostics = analyzerDiagnostics;

                done = true;

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

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

                var 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, verifier, cancellationToken).ConfigureAwait(false);
                }
            }while (!done);

            if (expectedNumberOfIterations >= 0)
            {
                verifier.Equal(expectedNumberOfIterations, expectedNumberOfIterations - numberOfIterations, $"Expected '{expectedNumberOfIterations}' iterations but found '{expectedNumberOfIterations - numberOfIterations}' iterations.");
            }
            else
            {
                verifier.True(numberOfIterations >= 0, "The upper limit for the number of code fix iterations was exceeded");
            }

            return(project);
        }
        private async static Task VerifyFixAllAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, bool allowNewCompilerDiagnostics, LanguageVersion languageVersionCSharp, Microsoft.CodeAnalysis.VisualBasic.LanguageVersion languageVersionVB, string equivalenceKey = null)
        {
            var project             = CreateProject(oldSources, language, languageVersionCSharp, languageVersionVB);
            var compilerDiagnostics = (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d);
            var fixAllProvider      = codeFixProvider.GetFixAllProvider();

            if (equivalenceKey == null)
            {
                equivalenceKey = codeFixProvider.GetType().Name;
            }
            FixAllContext fixAllContext;

            if (analyzer != null)
            {
                var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, project.Documents.ToArray()).ConfigureAwait(true);

                Func <Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync = (doc, ids, ct) =>
                                                                                                                                               Task.FromResult(analyzerDiagnostics.Where(d => d.Location.SourceTree.FilePath == doc.Name));
                Func <Project, bool, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = (proj, b, ids, ct) =>
                                                                                                                                                   Task.FromResult((IEnumerable <Diagnostic>)analyzerDiagnostics); //todo: verify, probably wrong
                var fixAllDiagnosticProvider = new FixAllDiagnosticProvider(codeFixProvider.FixableDiagnosticIds.ToImmutableHashSet(), getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync);
                fixAllContext = new FixAllContext(project.Documents.First(), codeFixProvider, FixAllScope.Solution,
                                                  equivalenceKey,
                                                  codeFixProvider.FixableDiagnosticIds,
                                                  fixAllDiagnosticProvider,
                                                  CancellationToken.None);
            }
            else
            {
                Func <Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync = async(doc, ids, ct) =>
                {
                    var compilerDiags = await GetCompilerDiagnosticsAsync(doc).ConfigureAwait(true);

                    return(compilerDiags.Where(d => codeFixProvider.FixableDiagnosticIds.Contains(d.Id)));
                };
                Func <Project, bool, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = async(proj, b, ids, ct) =>
                {
                    var theDocs = proj.Documents;
                    var diags   = await Task.WhenAll(theDocs.Select(d => getDocumentDiagnosticsAsync(d, ids, ct))).ConfigureAwait(true);

                    return(diags.SelectMany(d => d));
                };
                var fixAllDiagnosticProvider = new FixAllDiagnosticProvider(codeFixProvider.FixableDiagnosticIds.ToImmutableHashSet(), getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync);
                fixAllContext = new FixAllContext(project.Documents.First(), codeFixProvider, FixAllScope.Solution,
                                                  equivalenceKey,
                                                  codeFixProvider.FixableDiagnosticIds,
                                                  fixAllDiagnosticProvider,
                                                  CancellationToken.None);
            }
            var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(true);

            if (action == null)
            {
                throw new Exception("No action supplied for the code fix.");
            }
            project = await ApplyFixAsync(project, action).ConfigureAwait(true);

            //check if applying the code fix introduced any new compiler diagnostics
            var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, (await Task.WhenAll(project.Documents.Select(d => GetCompilerDiagnosticsAsync(d))).ConfigureAwait(true)).SelectMany(d => d));

            if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
            {
                Assert.True(false, $"Fix introduced new compiler diagnostics:\r\n{string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n");
            }
            var docs = project.Documents.ToArray();

            for (int i = 0; i < docs.Length; i++)
            {
                var document = docs[i];
                var actual   = await GetStringFromDocumentAsync(document).ConfigureAwait(true);

                newSources[i].Should().Be(actual);
            }
        }
Exemplo n.º 26
0
        public static async Task <CodeAction> GetFixAsync(
            ImmutableArray <Diagnostic> diagnostics,
            DiagnosticDescriptor descriptor,
            CodeFixProvider fixer,
            Project project,
            CodeFixerOptions options,
            IFormatProvider formatProvider      = null,
            CancellationToken cancellationToken = default)
        {
            if (diagnostics.Length == 1)
            {
                return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
            }

            FixAllProvider fixAllProvider = fixer.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                if (options.DiagnosticIdsFixableOneByOne.Contains(descriptor.Id))
                {
                    return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
                }

                WriteLine($"  '{fixer.GetType().FullName}' does not have FixAllProvider", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            if (!fixAllProvider.GetSupportedFixAllDiagnosticIds(fixer).Any(f => f == descriptor.Id))
            {
                WriteLine($"  '{fixAllProvider.GetType().FullName}' does not support diagnostic '{descriptor.Id}'", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            if (!fixAllProvider.GetSupportedFixAllScopes().Any(f => f == FixAllScope.Project))
            {
                WriteLine($"  '{fixAllProvider.GetType().FullName}' does not support scope '{FixAllScope.Project}'", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            var multipleFixesInfos = new HashSet <MultipleFixesInfo>();

            CodeAction action = null;

            options.DiagnosticFixMap.TryGetValue(descriptor.Id, out string equivalenceKey);

            foreach (Diagnostic diagnostic in diagnostics)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (!diagnostic.Location.IsInSource)
                {
                    continue;
                }

                Document document = project.GetDocument(diagnostic.Location.SourceTree);

                if (document == null)
                {
                    continue;
                }

                CodeAction fixCandidate = await GetFixAsync(diagnostic, fixer, document, multipleFixesInfos, options, cancellationToken).ConfigureAwait(false);

                if (fixCandidate == null)
                {
                    continue;
                }

                if (equivalenceKey != null &&
                    equivalenceKey != fixCandidate.EquivalenceKey)
                {
                    break;
                }

                action = fixCandidate;

                var fixAllContext = new FixAllContext(
                    document,
                    fixer,
                    FixAllScope.Project,
                    action.EquivalenceKey,
                    new string[] { descriptor.Id },
                    new FixAllDiagnosticProvider(diagnostics),
                    cancellationToken);

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

                if (fix != null)
                {
                    WriteLine($"  CodeFixProvider: '{fixer.GetType().FullName}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);

                    if (!string.IsNullOrEmpty(action.EquivalenceKey))
                    {
                        WriteLine($"  EquivalenceKey:  '{action.EquivalenceKey}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);
                    }

                    WriteLine($"  FixAllProvider:  '{fixAllProvider.GetType().FullName}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);

                    return(fix);
                }

                WriteLine($"  Fixer '{fixer.GetType().FullName}' registered no action for diagnostic '{descriptor.Id}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);
                WriteDiagnostics(diagnostics, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: formatProvider, indentation: "    ", maxCount: 10, verbosity: Verbosity.Diagnostic);
            }

            return(null);
        }
Exemplo n.º 27
0
        private static async Task <CodeAction> GetFixAsync(
            string diagnosticId,
            Project project,
            ImmutableArray <Diagnostic> diagnostics,
            CodeFixProvider fixer,
            CancellationToken cancellationToken)
        {
            FixAllProvider fixAll = fixer.GetFixAllProvider();

            if (!fixAll.GetSupportedFixAllDiagnosticIds(fixer).Any(f => f == diagnosticId))
            {
                return(null);
            }

            if (!fixAll.GetSupportedFixAllScopes().Any(f => f == FixAllScope.Project))
            {
                return(null);
            }

            foreach (Diagnostic diagnostic in diagnostics)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (!diagnostic.Location.IsInSource)
                {
                    continue;
                }

                Document document = project.GetDocument(diagnostic.Location.SourceTree);

                Debug.Assert(document != null, "");

                if (document == null)
                {
                    continue;
                }

                CodeAction action = null;

                var context = new CodeFixContext(
                    document,
                    diagnostic,
                    (a, _) =>
                {
                    if (action == null)
                    {
                        action = a;
                    }
                    else if (!string.Equals(a.EquivalenceKey, action.EquivalenceKey, StringComparison.Ordinal))
                    {
#if DEBUG
                        WriteLine($"'{fixer.GetType().Name}' registered multiple actions for diagnostic '{diagnosticId}'", ConsoleColor.DarkYellow);
                        WriteLine($"  {action.EquivalenceKey}", ConsoleColor.DarkYellow);
                        WriteLine($"  {a.EquivalenceKey}", ConsoleColor.DarkYellow);
#endif
                        action = null;
                    }
                },
                    cancellationToken);

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

                if (action == null)
                {
                    continue;
                }

                var fixAllContext = new FixAllContext(
                    document,
                    fixer,
                    FixAllScope.Project,
                    action.EquivalenceKey,
                    new string[] { diagnosticId },
                    new FixAllDiagnosticProvider(diagnostics),
                    cancellationToken);

                CodeAction fixAllAction = await fixAll.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (fixAllAction == null && diagnosticId.StartsWith("RCS"))
                {
                    WriteLine($"'{fixer.GetType().FullName}' registered no action for diagnostics:", ConsoleColor.Magenta);
                    Write(diagnostics, 10, ConsoleColor.Magenta);
                }

                return(fixAllAction);
            }

            return(null);
        }
        internal static async Task <ImmutableArray <CodeFixEquivalenceGroup> > CreateAsync(CodeFixProvider codeFixProvider, ImmutableDictionary <ProjectId, ImmutableArray <Diagnostic> > allDiagnostics, Solution solution, CancellationToken cancellationToken)
        {
            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(ImmutableArray.Create <CodeFixEquivalenceGroup>());
            }

            Dictionary <ProjectId, Dictionary <string, List <Diagnostic> > > relevantDocumentDiagnostics =
                new Dictionary <ProjectId, Dictionary <string, List <Diagnostic> > >();
            Dictionary <ProjectId, List <Diagnostic> > relevantProjectDiagnostics =
                new Dictionary <ProjectId, List <Diagnostic> >();

            foreach (var projectDiagnostics in allDiagnostics)
            {
                foreach (var diagnostic in projectDiagnostics.Value)
                {
                    if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
                    {
                        continue;
                    }

                    if (diagnostic.Location.IsInSource)
                    {
                        string sourcePath = diagnostic.Location.GetLineSpan().Path;

                        Dictionary <string, List <Diagnostic> > projectDocumentDiagnostics;
                        if (!relevantDocumentDiagnostics.TryGetValue(projectDiagnostics.Key, out projectDocumentDiagnostics))
                        {
                            projectDocumentDiagnostics = new Dictionary <string, List <Diagnostic> >();
                            relevantDocumentDiagnostics.Add(projectDiagnostics.Key, projectDocumentDiagnostics);
                        }

                        List <Diagnostic> diagnosticsInFile;
                        if (!projectDocumentDiagnostics.TryGetValue(sourcePath, out diagnosticsInFile))
                        {
                            diagnosticsInFile = new List <Diagnostic>();
                            projectDocumentDiagnostics.Add(sourcePath, diagnosticsInFile);
                        }

                        diagnosticsInFile.Add(diagnostic);
                    }
                    else
                    {
                        List <Diagnostic> diagnosticsInProject;
                        if (!relevantProjectDiagnostics.TryGetValue(projectDiagnostics.Key, out diagnosticsInProject))
                        {
                            diagnosticsInProject = new List <Diagnostic>();
                            relevantProjectDiagnostics.Add(projectDiagnostics.Key, diagnosticsInProject);
                        }

                        diagnosticsInProject.Add(diagnostic);
                    }
                }
            }

            ImmutableDictionary <ProjectId, ImmutableDictionary <string, ImmutableArray <Diagnostic> > > documentDiagnosticsToFix =
                relevantDocumentDiagnostics.ToImmutableDictionary(i => i.Key, i => i.Value.ToImmutableDictionary(j => j.Key, j => j.Value.ToImmutableArray(), StringComparer.OrdinalIgnoreCase));
            ImmutableDictionary <ProjectId, ImmutableArray <Diagnostic> > projectDiagnosticsToFix =
                relevantProjectDiagnostics.ToImmutableDictionary(i => i.Key, i => i.Value.ToImmutableArray());

            HashSet <string> equivalenceKeys = new HashSet <string>();

            foreach (var diagnostic in relevantDocumentDiagnostics.Values.SelectMany(i => i.Values).SelectMany(i => i).Concat(relevantProjectDiagnostics.Values.SelectMany(i => i)))
            {
                foreach (var codeAction in await GetFixesAsync(solution, codeFixProvider, diagnostic, cancellationToken).ConfigureAwait(false))
                {
                    equivalenceKeys.Add(codeAction.EquivalenceKey);
                }
            }

            List <CodeFixEquivalenceGroup> groups = new List <CodeFixEquivalenceGroup>();

            foreach (var equivalenceKey in equivalenceKeys)
            {
                groups.Add(new CodeFixEquivalenceGroup(equivalenceKey, solution, fixAllProvider, codeFixProvider, documentDiagnosticsToFix, projectDiagnosticsToFix));
            }

            return(groups.ToImmutableArray());
        }
Exemplo n.º 29
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);
        }
        internal async Task <(ImmutableArray <Diagnostic>, ImmutableArray <CodeAction>, CodeAction actionToInvoke)> GetDiagnosticAndFixesAsync(
            IEnumerable <Diagnostic> diagnostics,
            CodeFixProvider fixer,
            TestDiagnosticAnalyzerDriver testDriver,
            Document document,
            TextSpan span,
            string annotation,
            int index)
        {
            if (diagnostics.IsEmpty())
            {
                return(ImmutableArray <Diagnostic> .Empty, ImmutableArray <CodeAction> .Empty, null);
            }

            var scope = GetFixAllScope(annotation);

            if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType &&
                document.GetLanguageService <IFixAllSpanMappingService>() is IFixAllSpanMappingService spanMappingService)
            {
                var documentsAndSpansToFix = await spanMappingService.GetFixAllSpansAsync(
                    document, span, scope.Value, CancellationToken.None).ConfigureAwait(false);

                if (documentsAndSpansToFix.IsEmpty)
                {
                    return(ImmutableArray <Diagnostic> .Empty, ImmutableArray <CodeAction> .Empty, null);
                }
            }

            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.Location.SourceSpan,
                    ImmutableArray.Create(diagnostic),
                    (a, d) => fixes.Add(new CodeFix(document.Project, a, d)),
                    testDriver.FallbackOptions,
                    isBlocking: false,
                    CancellationToken.None);

                await fixer.RegisterCodeFixesAsync(context);
            }

            VerifyCodeActionsRegisteredByProvider(fixer, fixes);

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

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

            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, testDriver.FallbackOptions);
            var fixAllContext = new FixAllContext(fixAllState, 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);
        }