private async Task <bool> ContainsAnyFix(Document document, DiagnosticData diagnostic, bool considerSuppressionFixes, CancellationToken cancellationToken) { ImmutableArray <CodeFixProvider> workspaceFixers = ImmutableArray <CodeFixProvider> .Empty; List <CodeFixProvider> projectFixers = null; Lazy <ImmutableDictionary <DiagnosticId, ImmutableArray <CodeFixProvider> > > fixerMap; bool hasAnySharedFixer = _workspaceFixersMap.TryGetValue(document.Project.Language, out fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers); var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnostic.Id, out projectFixers); Lazy <ISuppressionFixProvider> lazySuppressionProvider = null; var hasSuppressionFixer = considerSuppressionFixes && _suppressionProvidersMap.TryGetValue(document.Project.Language, out lazySuppressionProvider) && lazySuppressionProvider.Value != null; if (!hasAnySharedFixer && !hasAnyProjectFixer && !hasSuppressionFixer) { return(false); } var allFixers = ImmutableArray <CodeFixProvider> .Empty; if (hasAnySharedFixer) { allFixers = workspaceFixers; } if (hasAnyProjectFixer) { allFixers = allFixers.AddRange(projectFixers); } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var dx = diagnostic.ToDiagnostic(tree); if (hasSuppressionFixer && lazySuppressionProvider.Value.CanBeSuppressed(dx)) { return(true); } var fixes = new List <CodeFix>(); var context = new CodeFixContext(document, dx, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (a, d) => { // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. lock (fixes) { fixes.Add(new CodeFix(a, d)); } }, verifyArguments: false, cancellationToken: cancellationToken); var extensionManager = document.Project.Solution.Workspace.Services.GetService <IExtensionManager>(); // we do have fixer. now let's see whether it actually can fix it foreach (var fixer in allFixers) { await extensionManager.PerformActionAsync(fixer, () => fixer.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask).ConfigureAwait(false); if (!fixes.Any()) { continue; } return(true); } return(false); }
private async Task <bool> ContainsAnyFix(Document document, DiagnosticData diagnostic, CancellationToken cancellationToken) { // TODO: We don't return true here if the only available fixes are suppressions. // This is to avoid the problem where lightbulb would show up for every green warning // squiggle in the editor thereby diluting the promise of the light bulb from // "I have a fix" to "I have some action". This is temporary until the editor team exposes // some mechanism (e.g. a faded out lightbulb) that would allow us to say "I have an action // but not a fix". ImmutableArray <CodeFixProvider> workspaceFixers = ImmutableArray <CodeFixProvider> .Empty; List <CodeFixProvider> projectFixers = null; Lazy <ImmutableDictionary <DiagnosticId, ImmutableArray <CodeFixProvider> > > fixerMap; bool hasAnySharedFixer = this.workspaceFixersMap.TryGetValue(document.Project.Language, out fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers); var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnostic.Id, out projectFixers); if (!hasAnySharedFixer && !hasAnyProjectFixer) { return(false); } var allFixers = ImmutableArray <CodeFixProvider> .Empty; if (hasAnySharedFixer) { allFixers = workspaceFixers; } if (hasAnyProjectFixer) { allFixers = allFixers.AddRange(projectFixers); } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var dx = diagnostic.ToDiagnostic(tree); var extensionManager = document.Project.Solution.Workspace.Services.GetService <IExtensionManager>(); var fixes = new List <CodeFix>(); var context = new CodeFixContext(document, dx, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (a, d) => { // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. lock (fixes) { fixes.Add(new CodeFix(a, d)); } }, verifyArguments: false, cancellationToken: cancellationToken); // we do have fixer. now let's see whether it actually can fix it foreach (var fixer in allFixers) { await extensionManager.PerformActionAsync(fixer, () => fixer.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask).ConfigureAwait(false); if (!fixes.Any()) { continue; } return(true); } return(false); }