< Summary

Information
Class: Hertzole.ScriptableValues.Generator.NoCallbackImplementationCodeFixProvider
Assembly: Hertzole.ScriptableValues.Generator
File(s): Generator/Hertzole.ScriptableValues.Generator/Code Fixers/NoCallbackImplementationCodeFixProvider.cs
Tag: 52_28197466029
Line coverage
0%
Covered lines: 0
Uncovered lines: 81
Coverable lines: 81
Total lines: 182
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 35
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
GetFixAllProvider()100%110%
RegisterCodeFixesAsync(...)0%220%
CreateChangedDocument()0%24240%
GetImplementationMethod(...)0%990%

File(s)

Generator/Hertzole.ScriptableValues.Generator/Code Fixers/NoCallbackImplementationCodeFixProvider.cs

#LineLine coverage
 1using System;
 2using System.Collections.Immutable;
 3using System.Composition;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Microsoft.CodeAnalysis;
 7using Microsoft.CodeAnalysis.CodeActions;
 8using Microsoft.CodeAnalysis.CodeFixes;
 9using Microsoft.CodeAnalysis.CSharp;
 10using Microsoft.CodeAnalysis.CSharp.Syntax;
 11using Microsoft.CodeAnalysis.Editing;
 12using Microsoft.CodeAnalysis.Text;
 13
 14namespace Hertzole.ScriptableValues.Generator;
 15
 16[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NoCallbackImplementationCodeFixProvider))]
 17[Shared]
 18public sealed class NoCallbackImplementationCodeFixProvider : CodeFixProvider
 19{
 020    private static readonly ImmutableArray<string> fixableDiagnosticIds = ImmutableArray.Create(DiagnosticIdentifiers.No
 21
 22    public override ImmutableArray<string> FixableDiagnosticIds
 23    {
 024        get { return fixableDiagnosticIds; }
 25    }
 26
 27    public override FixAllProvider GetFixAllProvider()
 28    {
 029        return WellKnownFixAllProviders.BatchFixer;
 30    }
 31
 32    public override Task RegisterCodeFixesAsync(CodeFixContext context)
 33    {
 034        Diagnostic diagnostic = context.Diagnostics[0];
 035        if (!diagnostic.Properties.TryGetValue("callbackName", out string? callbackName))
 36        {
 037            callbackName = string.Empty;
 38        }
 39
 040        context.RegisterCodeFix(CodeAction.Create(
 041            string.Format(Resources.HSV0001CodeFixTitle, callbackName),
 042            token => CreateChangedDocument(context, token),
 043            nameof(Resources.HSV0001CodeFixTitle)), diagnostic);
 44
 045        return Task.CompletedTask;
 46    }
 47
 48    private static async Task<Document> CreateChangedDocument(CodeFixContext context, CancellationToken cancellationToke
 49    {
 050        SyntaxNode? root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
 051        if (root == null)
 52        {
 053            return context.Document;
 54        }
 55
 56        // Find the declaration
 057        TextSpan diagnosticSpan = context.Diagnostics[0].Location.SourceSpan;
 058        SyntaxNode node = root.FindNode(diagnosticSpan);
 59
 060        if (node is not AttributeSyntax attribute)
 61        {
 062            return context.Document;
 63        }
 64
 65        // Get the member declaration
 066        MemberDeclarationSyntax? memberDeclaration = attribute.FirstAncestorOrSelf<MemberDeclarationSyntax>();
 067        if (memberDeclaration == null)
 68        {
 069            return context.Document;
 70        }
 71
 072        SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(fa
 073        if (semanticModel == null)
 74        {
 075            return context.Document;
 76        }
 77
 78        // Get the field/property wrapper
 079        if (!memberDeclaration.TryGetFieldOrPropertyWrapper(in semanticModel, out ImmutableArray<FieldOrPropertyWrapper>
 80        {
 081            return context.Document;
 82        }
 83
 084        Diagnostic diagnostic = context.Diagnostics[0];
 085        if (!diagnostic.Properties.TryGetValue("fieldName", out string? fieldName))
 86        {
 087            return context.Document;
 88        }
 89
 090        FieldOrPropertyWrapper? targetWrapper = null;
 091        for (int i = 0; i < wrappers.Length; i++)
 92        {
 093            FieldOrPropertyWrapper wrapper = wrappers.ItemRef(i);
 094            if (wrapper.Name == fieldName)
 95            {
 096                targetWrapper = wrapper;
 097                break;
 98            }
 99        }
 100
 0101        if (targetWrapper == null)
 102        {
 0103            return context.Document;
 104        }
 105
 0106        if (!ScriptableValueHelper.TryGetScriptableType(in semanticModel, (INamedTypeSymbol?) targetWrapper.Value.Type, 
 0107                out ITypeSymbol? genericType) ||
 0108            !diagnostic.Properties.TryGetValue("callbackName", out string? callbackName))
 109        {
 0110            return context.Document;
 111        }
 112
 0113        DocumentEditor editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken).ConfigureAwait(fal
 114
 0115        SyntaxNode? newMethod = GetImplementationMethod(in editor, in semanticModel, in callbackName!, in scriptableType
 116
 0117        editor.InsertAfter(memberDeclaration, newMethod);
 118
 0119        return editor.GetChangedDocument();
 0120    }
 121
 122    private static SyntaxNode GetImplementationMethod(in DocumentEditor editor,
 123        in SemanticModel semanticModel,
 124        in string callbackName,
 125        in ScriptableType scriptableType,
 126        in ITypeSymbol? genericType)
 127    {
 0128        SyntaxGenerator generator = editor.Generator;
 0129        using ArrayBuilder<SyntaxNode> parametersBuilder = new ArrayBuilder<SyntaxNode>();
 130
 0131        switch (scriptableType)
 132        {
 133            case ScriptableType.Value:
 0134                SyntaxNode genericTypeExpression = generator.TypeExpression(genericType!, true);
 135
 0136                parametersBuilder.Add(generator.ParameterDeclaration("oldValue", genericTypeExpression));
 0137                parametersBuilder.Add(generator.ParameterDeclaration("newValue", genericTypeExpression));
 0138                break;
 139            case ScriptableType.GenericEvent:
 140            case ScriptableType.Event:
 0141                SyntaxNode senderExpression = generator.TypeExpression(semanticModel.Compilation.GetSpecialType(SpecialT
 0142                SyntaxNode argsExpression = generator.TypeExpression(genericType ?? semanticModel.Compilation.GetTypeByM
 143
 0144                parametersBuilder.Add(generator.ParameterDeclaration("sender", senderExpression));
 0145                parametersBuilder.Add(generator.ParameterDeclaration("args", argsExpression));
 0146                break;
 147            case ScriptableType.Pool:
 0148                SyntaxNode poolActionExpression =
 0149                    generator.TypeExpression(semanticModel.Compilation.GetTypeByMetadataName("Hertzole.ScriptableValues.
 150
 0151                SyntaxNode poolItemExpression = generator.TypeExpression(genericType!, true);
 152
 0153                parametersBuilder.Add(generator.ParameterDeclaration("action", poolActionExpression));
 0154                parametersBuilder.Add(generator.ParameterDeclaration("item", poolItemExpression));
 0155                break;
 156            case ScriptableType.List:
 157            case ScriptableType.Dictionary:
 0158                SyntaxNode collectionArgsExpression = generator.TypeExpression(semanticModel.Compilation.GetCollectionCh
 159
 0160                parametersBuilder.Add(generator.ParameterDeclaration("args", collectionArgsExpression));
 0161                break;
 162            default:
 0163                throw new NotSupportedException("Unsupported scriptable type: " + scriptableType);
 164        }
 165
 0166        SyntaxNode newMethod = generator.MethodDeclaration(
 0167            callbackName,
 0168            parametersBuilder.ToImmutable(),
 0169            accessibility: Accessibility.Private,
 0170            modifiers: DeclarationModifiers.Partial);
 171
 0172        ThrowStatementSyntax throwStatement = SyntaxFactory.ThrowStatement(
 0173            SyntaxFactory.ObjectCreationExpression(
 0174                SyntaxFactory.ParseTypeName("System.NotImplementedException"),
 0175                SyntaxFactory.ArgumentList(),
 0176                null));
 177
 0178        newMethod = generator.WithStatements(newMethod, [throwStatement]);
 179
 0180        return newMethod;
 0181    }
 182}