Roslyn Vsix stopped working after moving the project - c#

I had a working Roslyn VSIX Project for analyzing resources that could be localizable. Everything was working fine until I moved the project to a new location.
The analyzer's still seem to run and trigger the Code fix, However, the Code Action is never Registered into visual studio, so there is no option for that fix for some reason.
My analyzer:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ConstDiagnosticAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "Viventium.Localization.Tools.ConstantToResource";
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.ConstAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.ConstAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.ConstAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private const string Category = "Naming";
private static ResourceLocalizationRule localizationRule = new ResourceLocalizationRule();
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeConstDeclaration, SyntaxKind.FieldDeclaration);
}
public static void AnalyzeConstDeclaration(SyntaxNodeAnalysisContext context)
{
var fieldDeclaration = (FieldDeclarationSyntax)context.Node;
if (false == IsValidConstDeclaration(context, fieldDeclaration))
{
return;
}
var firstVariable = fieldDeclaration.Declaration.Variables.FirstOrDefault();
var firstSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), firstSymbol.Name));
}
private static bool VariableIsInResx(SyntaxNodeAnalysisContext context, FieldDeclarationSyntax fieldDeclaration)
{
var solution = context.GetSolution();
var documentPropertyMap = PropertyMapCache.GetDocumentPropertyMap(solution);
return localizationRule.IsFieldDeclaredInResx(fieldDeclaration, documentPropertyMap);
}
private static bool IsValidConstDeclaration(SyntaxNodeAnalysisContext context, FieldDeclarationSyntax fieldDeclaration)
{
if (false == fieldDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return false;
}
if (fieldDeclaration.Declaration.Variables.Count > 1)
{
return false;
}
var declaredVariable = fieldDeclaration.Declaration.Variables.FirstOrDefault();
var initializer = declaredVariable.Initializer;
if (initializer == null)
{
return false;
}
var constantValue = context.SemanticModel.GetConstantValue(initializer.Value);
if (!constantValue.HasValue)
{
return false;
}
var variableTypeName = fieldDeclaration.Declaration.Type;
var variableType = context.SemanticModel.GetTypeInfo(variableTypeName).ConvertedType;
if (variableType.SpecialType != SpecialType.System_String)
{
return false;
}
return true;
}
}
The Fix Provider:
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ConstantToResourceCodeFixProvider)), Shared]
public class ConstantToResourceCodeFixProvider : CodeFixProvider
{
#region Consts
private const string title = "Convert Constant Use Resources";
#endregion
#region CodeFixProvider Overrides
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(ConstDiagnosticAnalyzer.DiagnosticId); }
}
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Find the type declaration identified by the diagnostic.
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf<FieldDeclarationSyntax>().First();
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeActionWithPreview.CreateStateful(
title: title,
createChangedDocument: (c, isPreview) => this.CheckConstForLocalization(context, declaration, isPreview, c),
equivalenceKey: title),
diagnostic);
}
#endregion
#region Analyzer Logic
private async Task<Document> CheckConstForLocalization(CodeFixContext context, FieldDeclarationSyntax field, bool isPreview, CancellationToken cancellationToken)
{
var documentPropertyMap = PropertyMapCache.GetDocumentPropertyMap(context.Document.Project.Solution);
var localizationRule = new ResourceLocalizationRule();
var updatedDocument = localizationRule.ConvertDocumentToUseResources(context.Document, documentPropertyMap, field, isPreview, cancellationToken);
return await updatedDocument;
}
#endregion
}
Can some one please give me a hint to of what is going on an how I can fix this?

After hours of debugging it turns out Diagnostic Analyzer's are not allowed to have periods in them...
public const string DiagnosticId = "Viventium_Localization_Tools_ConstantToResource";
The fix was quite simple I just changed the DiagnosticId to use underscores, and I've opened a bug with Roslyn

Related

What's the best way to check source code lines for exceeding line length with Roslyn Analyzer?

I'd like to enforce some hard limits to code line length.
What Roslyn's API should I use for it?
Registering action for every syntax node and checking node's location seems to be not the most efficient approach.
An easy approach might be to register for syntax trees, but then immediately just call GetText() on the syntax tree; from there you can look at the lines of the text directly and at least find "long" lines since you can directly get line spans and lengths that way. You'll still potentially need to filter out things though like long string literals or something.
Not perfect solution:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class LineMaxLengthAnalyzer : DiagnosticAnalyzer
{
public const int LineMaxLength = 150;
public const string DiagnosticId = nameof(LineMaxLengthAnalyzer);
private static readonly LocalizableString Title = new LocalizableResourceString(
nameof(Resources.LineMaxLengthAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(
nameof(Resources.LineMaxLengthAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(
nameof(Resources.LineMaxLengthAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private const string Category = "Readability";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
var nodeTypes = Enum.GetValues(typeof(SyntaxKind)).Cast<SyntaxKind>().ToArray();
context.RegisterSyntaxTreeAction(VerifyLines);
}
private void VerifyLines(SyntaxTreeAnalysisContext context)
{
var text = context.Tree.GetText();
text.Lines.Where(line => line.End - line.Start + 1 > LineMaxLength).ToList()
.ForEach(line =>
{
var location = Location.Create(context.Tree, line.Span);
var diagnostic = Diagnostic.Create(Rule, location, LineMaxLength);
context.ReportDiagnostic(diagnostic);
});
}
}

Roslyn Analyzer not clearing last diagnostic

I have created a Roslyn Analyzer that validates text files against a set of rules and have noticed that when I fix the last issue that diagnostic does not disappear. I have created a simple example to replicate the problem below.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class Analyzer1Analyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "TestCompilationAnalyzer";
private static readonly LocalizableString Title = "TestAnalyzer";
private static readonly LocalizableString MessageFormat = "This message should always appear {0}";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterAdditionalFileAction(AdditionalFileAction);
}
private void AdditionalFileAction(AdditionalFileAnalysisContext cxt)
{
var text = File.ReadAllText(cxt.AdditionalFile.Path);
if (text.Contains("help"))
{
cxt.ReportDiagnostic(Diagnostic.Create(Rule, Location.None, $"help"));
}
if (text.Contains("test"))
{
cxt.ReportDiagnostic(Diagnostic.Create(Rule, Location.None, $"test"));
}
}
}
if I add the words help and test to my additional file both diagnostics appear but if I remove both whatever was showing last won't disappear.
I found a workaround using code generators instead
[Generator]
public class SchemaTestAnalyzer : ISourceGenerator
{
public const string DiagnosticId = "TestCompilationAnalyzer";
private static readonly LocalizableString Title = "TestAnalyzer";
private static readonly LocalizableString MessageFormat = "This message should always appear {0}";
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true);
public void Execute(GeneratorExecutionContext context)
{
var additionalFile = context.AdditionalFiles.FirstOrDefault();
if (additionalFile != null)
{
var text = File.ReadAllText(additionalFile.Path);
if (text.Contains("help"))
{
context.ReportDiagnostic(Diagnostic.Create(Rule, Location.None, "help"));
}
if (text.Contains("test"))
{
context.ReportDiagnostic(Diagnostic.Create(Rule, Location.None, "test"));
}
}
}
public void Initialize(GeneratorInitializationContext context) { }
}
these work as expected and the last issue disappears when addressed and re-analyzed

I'm getting Http error 404 as a result on working on abp.io framework

I'm new at working on the abp.io framework, precisely Angular + Entity Framework Core.
I want to be able to create or display an objects list of a class that I've created.
For example, I've made a class called Address on the Domain layer.
Here is its AppService on the Application layer:
namespace ASKOM.RefPlusStudio.core.Services
{
[Authorize(corePermissions.Addresses.Default)]
public class AddressAppService : coreAppService, IAddressAppService
{
private readonly IAddressRepository _addressRepository;
private readonly AddressManager _addressManager;
public AddressAppService(IAddressRepository addressRepository, AddressManager addressManager)
{
_addressRepository = addressRepository;
_addressManager = addressManager;
}
[Authorize(corePermissions.Addresses.Create)]
public async Task<AddressDto> CreateAsync(CreateUpdateAddressDto input)
{
var address = await _addressManager.CreateAsync(
input.StreetNumber,
input.StreetName,
input.PostalCode,
input.City,
input.Country
);
await _addressRepository.InsertAsync(address);
return ObjectMapper.Map<Address, AddressDto>(address);
}
[Authorize(corePermissions.Addresses.Delete)]
public async Task DeleteAsync(Guid id)
{
await _addressRepository.DeleteAsync(id);
}
public async Task<AddressDto> GetAsync(Guid id)
{
var address = await _addressRepository.GetAsync(id);
return ObjectMapper.Map<Address, AddressDto>(address);
}
public async Task<PagedResultDto<AddressDto>> GetListAsync(GetAddressListDto input)
{
if (input.Sorting.IsNullOrWhiteSpace())
{
input.Sorting = nameof(Address.Country);
}
var addresses = await _addressRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter
);
var totalCount = await AsyncExecuter.CountAsync(
_addressRepository.WhereIf(
!input.Filter.IsNullOrWhiteSpace(),
address => address.Country.Contains(input.Filter)
)
);
return new PagedResultDto<AddressDto>(
totalCount,
ObjectMapper.Map<List<Address>, List<AddressDto>>(addresses)
);
}
[Authorize(corePermissions.Addresses.Edit)]
public async Task UpdateAsync(Guid id, CreateUpdateAddressDto input)
{
var address = await _addressRepository.GetAsync(id);
address.StreetNumber = input.StreetNumber;
address.StreetName = input.StreetName;
address.PostalCode = input.PostalCode;
address.City = input.City;
address.Country = input.Country;
await _addressRepository.UpdateAsync(address);
}
}
}
I gave them all the permissions needed.
Here is corePermissions.cs :
namespace ASKOM.RefPlusStudio.core.Permissions
{
public static class corePermissions
{
public const string GroupName = "core";
//Add your own permission names. Example:
//public const string MyPermission1 = GroupName + ".MyPermission1";
public static class Addresses
{
public const string Default = GroupName + ".Addresses";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
}
I wanted to see if it can display an Addresses list so I've made a static one on DataSeeder:
namespace ASKOM.RefPlusStudio.core
{
public class coreDataSeederContributor : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Address, Guid> _addressRepository;
private readonly IGuidGenerator _guidGenerator;
public coreDataSeederContributor(IRepository<Address, Guid> addressRepository, IGuidGenerator guidGenerator)
{
_addressRepository = addressRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _addressRepository.GetCountAsync() > 0)
{
return;
}
var address = new Address(
id: _guidGenerator.Create(),
streetNumber: 07,
streetName: "Med Salah Belhaj",
postalCode: 2080,
city: "Ariana",
country: "Tunisie"
);
//autoSave: true
await _addressRepository.InsertAsync(address);
}
}
}
Here is now the result on Swagger UI:
When I try to open the request URL, here is what it shows:
I'm sure that I may have forgotten something and that's why I get Access Denied on the Request URL, but I don't really know what it is because I'm new at this.
Could you please help me?
Thank you
There was a problem with the database. That's why it hasn't read the data I've provided in the DataSeedProvider.

How do I debug a CodeFixProvider not ever loading

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();
}
}
}
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"
For anyone else suffering this stuff, try this:
Find the Reset visual studio experimental instance short cut command by using the Start search menu
Run it
Clean your solution
Run it in RELEASE mode
Notice that your stuff now appears in the instance
Close it
Switch back to DEBUG model
Et voila. Le black magique just happened. Problem Solv-ed.

C# CodeFixes: Minimum Requirement that codefix is displayed

I recently started with CodeAnalyzers and CodeFixes.
Of course the template coming with the sdk () works. But when I move on, the codefix does not show up anymore on debugging.
When walking through the diagnostic is created as it is supposed to be.
BUT: The codefix (Strg+.) isnt shown anymore. I took care of Diagnostic-Id, equivalence-key, ... but cannot figure out, why I dont get my codefix shown anymore. So what are the minimum "requirements" for a codefix to be shown for a diagnostic?
Here is the code of the analyzer:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class VirtualMemberAnalyzer : DiagnosticAnalyzer
{
public const string PublicVirtualMethod_DiagnosticId = "PublicVirtualMethod";
private const string Category = "Naming";
private static readonly DiagnosticDescriptor PublicMethodVirtualRule = new DiagnosticDescriptor(
PublicVirtualMethod_DiagnosticId,
"public methode not virtual",
"every public methode must be virtual",
Category,
DiagnosticSeverity.Error,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(PublicMethodVirtualRule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
}
private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
foreach (var methodSymbol in namedTypeSymbol.GetMembers().OfType<IMethodSymbol>())
{
if (methodSymbol.MethodKind == MethodKind.Ordinary && !methodSymbol.IsStatic && !methodSymbol.IsVirtual && methodSymbol.DeclaredAccessibility == Accessibility.Public)
{
var diagnostic = Diagnostic.Create(
PublicMethodVirtualRule,
methodSymbol.Locations[0],
methodSymbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
}
And of the Codefix-Provider:
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(VirtualMemberAnalyzersCodeFixProvider)), Shared]
public class VirtualMemberAnalyzersCodeFixProvider : CodeFixProvider {
private const string title_property = "Make property virtual";
private const string title_method = "Make method virtual";
public sealed override ImmutableArray<string> FixableDiagnosticIds {
get {
return ImmutableArray.Create(VirtualMemberAnalyzer.PublicVirtualMethod_DiagnosticId);
}
}
public sealed override FixAllProvider GetFixAllProvider() {
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
var root = await context.Document.GetSyntaxRootAsync();
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var methodDeclarations = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>().FirstOrDefault();
if (methodDeclarations != null) {
CodeAction codeAction = CodeAction.Create(title_method, c => MakeVirtual(context.Document, methodDeclarations, c), equivalenceKey: title_method);
context.RegisterCodeFix(codeAction, diagnostic);
}
}
private async Task<Document> MakeVirtual(Document document, MethodDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
{
SyntaxTokenList memberDeclarationModifiers = memberDeclaration.Modifiers;
memberDeclarationModifiers.Add(SyntaxFactory.Token(SyntaxKind.VirtualKeyword));
MethodDeclarationSyntax methodDeclarationSyntax = memberDeclaration.WithModifiers(memberDeclarationModifiers);
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = oldRoot.ReplaceNode(memberDeclaration, methodDeclarationSyntax);
return document.WithSyntaxRoot(newRoot);
}
}
The memberDeclarationModifiers.Add(SyntaxFactory.Token(SyntaxKind.VirtualKeyword)); returns a new SyntaxTokenList. You're not using the new created list. Remember that SyntaxTrees are Immutable in Roslyn . Every change in the tree will create a new tree. Here is your codefix for your roslyn codefix :)
private async Task<Document> MakeVirtual(Document document, MethodDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
{
var methodDeclarationSyntax =
memberDeclaration.WithModifiers(
memberDeclaration.Modifiers.Add( SyntaxFactory.Token( SyntaxKind.VirtualKeyword ) ) );
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait( false );
var newRoot = oldRoot.ReplaceNode(memberDeclaration, methodDeclarationSyntax);
return document.WithSyntaxRoot(newRoot);
}

Categories

Resources