Reputation: 424
I have made a new "Analyzer with Code Fix (.NET Standard)" project, and updated the analyzer to check for what I want, and that works great. Now I have modified the CodeFixProvider, but it never shows up when debugging.
I have set breakpoints in the getter for FixableDiagnosticIds
, GetFixAllProvider()
, and RegisterCodeFixesAsync(CodeFixContext context)
, yet none of the breakpoints ever get called when I click the "light bulb" on a line that the analyzer, properly, tags.
Any ideas on how to figure out why it doesn't seem to be getting called?
In the default project created by "Analyzer with Code Fix (.NET Standard)", breakpoints in those 3 places are getting called correctly.
My analyzer code
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace InAnalyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class InAnalyzerAnalyzer : DiagnosticAnalyzer
{
public const string CanBeInvokedWithInDiagnosticId = "IN3001";
public const string DoNotUseInWithParameterDiagnosticId = "IN3002";
public const string UseInWithParameterDiagnosticId = "IN3003";
private const string CanBeInvokedWithInCategory = "Performance";
private const string DoNotUseInWithParameterCategory = "Performance";
private const string UseInWithParameterCategory = "Performance";
private static readonly LocalizableString CanBeInvokedWithInTitle = new LocalizableResourceString(nameof(Resources.CanBeInvokedWithInAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString DoNotUseInWithParameterTitle = new LocalizableResourceString( nameof(Resources.DoNotUseInWithParameterAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString UseInWithParameterTitle = new LocalizableResourceString( nameof(Resources.UseInWithParameterAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString CanBeInvokedWithInMessageFormat = new LocalizableResourceString(nameof(Resources.CanBeInvokedWithInAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString DoNotUseInWithParameterMessageFormat = new LocalizableResourceString(nameof(Resources.DoNotUseInWithParameterAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString UseInWithParameterMessageFormat = new LocalizableResourceString(nameof(Resources.UseInWithParameterAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString CanBeInvokedWithInDescription = new LocalizableResourceString(nameof(Resources.CanBeInvokedWithInAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString DoNotUseInWithParameterDescription = new LocalizableResourceString(nameof(Resources.DoNotUseInWithParameterAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString UseInWithParameterDescription = new LocalizableResourceString(nameof(Resources.UseInWithParameterAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private static readonly DiagnosticDescriptor CanBeInvokedWithInRule = new DiagnosticDescriptor(
CanBeInvokedWithInDiagnosticId,
CanBeInvokedWithInTitle,
CanBeInvokedWithInMessageFormat,
CanBeInvokedWithInCategory,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
CanBeInvokedWithInDescription);
private static readonly DiagnosticDescriptor DoNotUseInWithParameterRule = new DiagnosticDescriptor(
DoNotUseInWithParameterDiagnosticId,
DoNotUseInWithParameterTitle,
DoNotUseInWithParameterMessageFormat,
DoNotUseInWithParameterCategory,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
DoNotUseInWithParameterDescription);
private static readonly DiagnosticDescriptor UseInWithParameterRule = new DiagnosticDescriptor(
UseInWithParameterDiagnosticId,
UseInWithParameterTitle,
UseInWithParameterMessageFormat,
UseInWithParameterCategory,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
UseInWithParameterDescription);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(CanBeInvokedWithInRule, DoNotUseInWithParameterRule, UseInWithParameterRule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeInvocationExpressionSyntaxNode, SyntaxKind.InvocationExpression);
context.RegisterSyntaxNodeAction(AnalyzeMethodDeclarationSyntaxNode, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeInvocationExpressionSyntaxNode(SyntaxNodeAnalysisContext context)
{
var node = (InvocationExpressionSyntax) context.Node;
var symbol = context.SemanticModel.GetSymbolInfo(node).Symbol ??
context.SemanticModel.GetDeclaredSymbol(node);
if (symbol is IMethodSymbol methodSymbol)
{
var parametersSymbol = methodSymbol.Parameters;
var argumentSyntaxList = node?.ArgumentList.Arguments;
if (argumentSyntaxList != null)
{
var argumentSyntaxes = argumentSyntaxList.Value;
for (var index = 0; index < parametersSymbol.Length; index++)
{
var parameterSymbol = parametersSymbol[index];
if (parameterSymbol.RefKind == RefKind.In &&
parameterSymbol.Type.IsReadOnly &&
parameterSymbol.Type.IsValueType &&
index < argumentSyntaxes.Count)
{
var argumentSyntax = argumentSyntaxes[index];
if (argumentSyntax?.RefKindKeyword.IsKind(SyntaxKind.InKeyword) == false)
{
var diagnostic = Diagnostic.Create(
CanBeInvokedWithInRule,
argumentSyntax.Expression.GetLocation(),
parameterSymbol.Name,
parameterSymbol.Type);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
private static void AnalyzeMethodDeclarationSyntaxNode(SyntaxNodeAnalysisContext context)
{
var node = (MethodDeclarationSyntax) context.Node;
var parameterSyntaxList = node?.ParameterList.Parameters;
if (parameterSyntaxList != null)
{
var parameterSyntaxes = parameterSyntaxList.Value;
for (var index = 0; index < parameterSyntaxes.Count; index++)
{
var parameterSyntax = parameterSyntaxes[index];
if (parameterSyntax != null)
{
var symbol = context.SemanticModel.GetSymbolInfo(parameterSyntax.Type).Symbol;
if (symbol is ITypeSymbol typeSymbol)
{
if (typeSymbol.IsReadOnly &&
typeSymbol.IsValueType)
{
if (!parameterSyntax.Modifiers.Any(SyntaxKind.InKeyword))
{
var diagnostic = Diagnostic.Create(
UseInWithParameterRule,
parameterSyntax.Identifier.GetLocation(),
parameterSyntax.Identifier,
typeSymbol);
context.ReportDiagnostic(diagnostic);
}
}
else
{
foreach (var modifier in parameterSyntax.Modifiers)
{
if (modifier.Kind() == SyntaxKind.InKeyword)
{
var diagnostic = Diagnostic.Create(
DoNotUseInWithParameterRule,
modifier.GetLocation(),
parameterSyntax.Identifier,
typeSymbol);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
}
}
}
}
My CodeFixProvider
(Probably not complete and correct; I'd like to debug and get it working correctly, but I can't get it to even try to run):
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
namespace InAnalyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(InAnalyzerCodeFixProvider)), Shared]
public class InAnalyzerCodeFixProvider : CodeFixProvider
{
private const string AddInModifierTitle = "Add 'in' modifier";
private const string RemoveInModifierTitle = "Remove 'in' modifier";
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(
InAnalyzerAnalyzer.CanBeInvokedWithInDiagnosticId,
InAnalyzerAnalyzer.DoNotUseInWithParameterDiagnosticId,
InAnalyzerAnalyzer.UseInWithParameterDiagnosticId);
public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var token = root.FindToken(context.Span.Start);
if (!token.Span.IntersectsWith(context.Span))
{
return;
}
var generator = SyntaxGenerator.GetGenerator(context.Document);
var node = generator.GetDeclaration(token.Parent);
if (node == null)
{
return;
}
foreach (var diagnostic in context.Diagnostics)
{
switch (diagnostic.Id)
{
case InAnalyzerAnalyzer.CanBeInvokedWithInDiagnosticId:
case InAnalyzerAnalyzer.UseInWithParameterDiagnosticId:
context.RegisterCodeFix(
CodeAction.Create(
AddInModifierTitle,
c => AddInModifierAsync(context.Document, node, c),
AddInModifierTitle),
diagnostic);
break;
case InAnalyzerAnalyzer.DoNotUseInWithParameterDiagnosticId:
context.RegisterCodeFix(
CodeAction.Create(
RemoveInModifierTitle,
c => RemoveInModifierAsync(context.Document, node, c),
RemoveInModifierTitle),
diagnostic);
break;
}
}
}
private async Task<Document> AddInModifierAsync(
Document document,
SyntaxNode node,
CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
switch (node.Parent)
{
case ArgumentSyntax argumentSyntax:
editor.ReplaceNode(
argumentSyntax,
argumentSyntax.WithRefKindKeyword(SyntaxFactory.Token(SyntaxKind.InKeyword)));
break;
case ParameterSyntax parameterSyntax:
editor.ReplaceNode(
parameterSyntax,
parameterSyntax.AddModifiers(SyntaxFactory.Token(SyntaxKind.InKeyword)));
break;
}
return editor.GetChangedDocument();
}
private async Task<Document> RemoveInModifierAsync(
Document document,
SyntaxNode node,
CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
////switch (node.Parent)
////{
//// case ArgumentSyntax argumentSyntax:
//// editor.ReplaceNode(
//// argumentSyntax,
//// argumentSyntax.WithRefKindKeyword(SyntaxFactory.Token(SyntaxKind.InKeyword)));
//// break;
//// case ParameterSyntax parameterSyntax:
//// editor.ReplaceNode(
//// parameterSyntax,
//// parameterSyntax.AddModifiers(SyntaxFactory.Token(SyntaxKind.InKeyword)));
//// break;
////}
return editor.GetChangedDocument();
}
}
}
Upvotes: 3
Views: 1085
Reputation: 1080
For anyone else suffering this stuff, try this:
Upvotes: 0
Reputation: 424
After finding this, which pointed to the CreateExpInstance tool, I tried resetting the "Roslyn"-suffixed experimental instance. That didn't solve my issue, but I then deleted my "Roslyn"-suffixed experimental instance and tried debugging my VSIX again. This time, debugging my VSIX worked and showed my CodeFixProvider.
rd /s/q "%LOCALAPPDATA%\Microsoft\VisualStudio\16.0_0f71fe5bRoslyn"
Upvotes: 3