roslyn: Light bulb does not invoke code fix provider for diagnostics created in compilation end action
Repros in Visual Studio 3.9.0 (and 3.8.6) both as a NuGet package and a VSIX. Diagnostics are reported on all declared types.
If I report diagnostics from the compilation end action, they show up properly, but my code fix is never invoked when I bring up the light bulb. If I report the same diagnostics from the symbol action, they show up the same, and my code fix is invoked when I bring up the lightbulb. Am I abusing the API, or is there a Roslyn bug?
Repro steps:
- Paste the code below into an analyzer project and either use Ctrl+F5 on a VSIX project to deploy the analyzer, or create a NuGet package and reference it in a simple throwaway project.
- Wait for EXAMPLE diagnostics to appear on all types in the project you open.
- Press Ctrl+. to invoke the light bulb on one of the EXAMPLE diagnostics. Nothing happens.
RegisterCodeFixesAsync
was not called. - Recompile the analyzer, but cut and paste the two lines that report a diagnostic into the RegisterSymbolAction callback as indicated by the code comment.
- Redeploy the VSIX or NuGet package and repeat the above steps by invoking the light bulb when the diagnostics appear.
- You should instantly see that Debugger.Launch() was called this time.
RegisterCodeFixesAsync
was called.
Minimal repro is below. If you want to know why I’m using this pattern, the motivating source is at https://gist.github.com/jnm2/3b45cd503e28fc3341e9e2453c148f5f.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
public sealed class DiagnosticDescriptors
{
public static DiagnosticDescriptor ExampleDiagnostic { get; } = new(
id: "EXAMPLE",
title: "Example diagnostic",
messageFormat: "Example diagnostic",
"CodeQuality",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
}
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ClassModifierAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
DiagnosticDescriptors.ExampleDiagnostic);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(OnCompilationStart);
}
private static void OnCompilationStart(CompilationStartAnalysisContext context)
{
var suggestionCandidates = new List<INamedTypeSymbol>();
context.RegisterSymbolAction(context =>
{
var symbol = (INamedTypeSymbol)context.Symbol;
lock (suggestionCandidates) suggestionCandidates.Add(symbol);
}, SymbolKind.NamedType);
context.RegisterCompilationEndAction(context =>
{
foreach (var symbol in suggestionCandidates)
{
// Move these two lines into the symbol callback, and the light bulb starts invoking the code fix provider defined below
var syntax = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(context.CancellationToken);
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ExampleDiagnostic, syntax?.GetLocation()));
}
});
}
}
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
public sealed class ClassModifierCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(
DiagnosticDescriptors.ExampleDiagnostic.Id);
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
System.Diagnostics.Debugger.Launch();
}
}
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 18 (18 by maintainers)
@mavasani could we have the engine instead be lazy? essentially dismiss compilation end diagnostics whenever the solution snapshot they come from is not the current one? That would mean the lightbulb item would go away as soonn as things are not up-to-date, but a user could still apply codefixes from the ide. As it stands today the only way someone could apply a codefix that does compilation-end-analysis is to use
dotnet-format
.