File: Analyzers\CustomMarshallerAttributeFixer.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj (Microsoft.Interop.LibraryImportGenerator)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;
using Microsoft.CodeAnalysis.Editing;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
 
namespace Microsoft.Interop.Analyzers
{
    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    public class CustomMarshallerAttributeFixer : CodeFixProvider
    {
        private const string AddMissingCustomTypeMarshallerMembersKey = nameof(AddMissingCustomTypeMarshallerMembersKey);
 
        private sealed class CustomFixAllProvider : FixAllProvider
        {
            public static FixAllProvider Instance { get; } = new CustomFixAllProvider();
 
            public override async Task<CodeAction?> GetFixAsync(FixAllContext fixAllContext)
            {
                ImmutableArray<Diagnostic> diagnostics = await GetAllDiagnosticsInScope(fixAllContext).ConfigureAwait(false);
                Dictionary<(INamedTypeSymbol marshallerType, ITypeSymbol managedType, bool isLinearCollectionMarshaller), HashSet<string>> uniqueMarshallersToFix = new();
                // Organize all the diagnostics by marshaller, managed type, and whether or not it's a collection marshaller
                foreach (Diagnostic diagnostic in diagnostics)
                {
                    Document doc = fixAllContext.Solution.GetDocument(diagnostic.Location.SourceTree);
                    SemanticModel model = await doc.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
 
                    var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, fixAllContext.CancellationToken);
                    ITypeSymbol? managedType = GetManagedTypeInAttributeSyntax(diagnostic.Location, entryPointTypeSymbol);
 
                    SyntaxNode root = await diagnostic.Location.SourceTree.GetRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
 
                    SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan);
                    var marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, fixAllContext.CancellationToken).Symbol;
                    var uniqueMarshallerFixKey = (marshallerType, managedType, ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol));
                    if (!uniqueMarshallersToFix.TryGetValue(uniqueMarshallerFixKey, out HashSet<string> membersToAdd))
                    {
                        uniqueMarshallersToFix[uniqueMarshallerFixKey] = membersToAdd = new HashSet<string>();
                    }
 
                    membersToAdd.UnionWith(diagnostic.Properties[MissingMemberNames.Key].Split(MissingMemberNames.Delimiter));
                }
 
                Dictionary<INamedTypeSymbol, INamedTypeSymbol> partiallyUpdatedSymbols = new(SymbolEqualityComparer.Default);
 
                SymbolEditor symbolEditor = SymbolEditor.Create(fixAllContext.Solution);
 
                // Apply each fix
                foreach (var marshallerInfo in uniqueMarshallersToFix)
                {
                    var (marshallerType, managedType, isLinearCollectionMarshaller) = marshallerInfo.Key;
                    HashSet<string> missingMembers = marshallerInfo.Value;
 
                    if (!partiallyUpdatedSymbols.TryGetValue(marshallerType, out INamedTypeSymbol newMarshallerType))
                    {
                        newMarshallerType = marshallerType;
                    }
 
                    newMarshallerType = (INamedTypeSymbol)await symbolEditor.EditOneDeclarationAsync(
                        marshallerType,
                        (editor, decl) => AddMissingMembers(
                            editor,
                            decl,
                            marshallerType,
                            managedType,
                            missingMembers,
                            isLinearCollectionMarshaller),
                        fixAllContext.CancellationToken).ConfigureAwait(false);
 
                    partiallyUpdatedSymbols[marshallerType] = newMarshallerType;
                }
 
                return CodeAction.Create(SR.AddMissingCustomTypeMarshallerMembers, ct => Task.FromResult(symbolEditor.ChangedSolution));
            }
 
            private static async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsInScope(FixAllContext context)
            {
                switch (context.Scope)
                {
                    case FixAllScope.Document:
                        return await context.GetDocumentDiagnosticsAsync(context.Document).ConfigureAwait(false);
                    case FixAllScope.Project:
                        return await context.GetAllDiagnosticsAsync(context.Project).ConfigureAwait(false);
                    case FixAllScope.Solution:
                        return ImmutableArray.CreateRange((await Task.WhenAll(context.Solution.Projects.Select(context.GetAllDiagnosticsAsync)).ConfigureAwait(false)).SelectMany(arr => arr));
                    default:
                        throw new UnreachableException();
                }
            }
        }
 
        public override FixAllProvider? GetFixAllProvider() => CustomFixAllProvider.Instance;
 
        public override ImmutableArray<string> FixableDiagnosticIds { get; } =
            ImmutableArray.Create(
                AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape);
 
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            Document doc = context.Document;
            SyntaxNode? root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
            if (root == null)
                return;
 
            SyntaxNode node = root.FindNode(context.Span);
            var (missingMemberNames, missingMembersDiagnostics) = GetRequiredShapeMissingMemberNames(context.Diagnostics);
 
            if (missingMembersDiagnostics.Count > 0)
            {
                context.RegisterCodeFix(
                    CodeAction.Create(
                        SR.AddMissingCustomTypeMarshallerMembers,
                        ct => AddMissingMembers(doc, node, missingMemberNames, ct),
                        AddMissingCustomTypeMarshallerMembersKey),
                    missingMembersDiagnostics);
            }
        }
 
        private static (HashSet<string> missingMembers, List<Diagnostic> fixedDiagnostics) GetRequiredShapeMissingMemberNames(IEnumerable<Diagnostic> diagnostics)
        {
            HashSet<string> missingMemberNames = new();
            List<Diagnostic> requiredShapeDiagnostics = new();
            foreach (var diagnostic in diagnostics)
            {
                if (diagnostic.Id == AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape)
                {
                    requiredShapeDiagnostics.Add(diagnostic);
                    if (diagnostic.Properties.TryGetValue(MissingMemberNames.Key, out string missingMembers))
                    {
                        missingMemberNames.UnionWith(missingMembers.Split(MissingMemberNames.Delimiter));
                    }
                }
            }
 
            return (missingMemberNames, requiredShapeDiagnostics);
        }
 
        private static void IgnoreArityMismatch(INamedTypeSymbol marshallerType, INamedTypeSymbol managedType)
        {
        }
 
#pragma warning disable IDE0060
        private static async Task<Solution> AddMissingMembers(Document doc, SyntaxNode node, HashSet<string> missingMemberNames, CancellationToken ct)
        {
            var model = await doc.GetSemanticModelAsync(ct).ConfigureAwait(false);
 
            var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(node.SpanStart, ct);
 
            // TODO: Convert to use the IOperation tree once IAttributeOperation is available
            var managedTypeSymbolInAttribute = GetManagedTypeInAttributeSyntax(node.GetLocation(), entryPointTypeSymbol);
 
            bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol);
 
            // Explicitly ignore the generic arity mismatch diagnostics as we will only reach here if there are no mismatches.
            // The analyzer will not for missing members if the managed type cannot be resolved.
            ManualTypeMarshallingHelper.TryResolveManagedType(entryPointTypeSymbol, ManualTypeMarshallingHelper.ReplaceGenericPlaceholderInType(managedTypeSymbolInAttribute, entryPointTypeSymbol, model.Compilation), isLinearCollectionMarshaller, IgnoreArityMismatch, out ITypeSymbol managedType);
 
            SymbolEditor editor = SymbolEditor.Create(doc.Project.Solution);
 
            INamedTypeSymbol marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, ct).Symbol;
 
            await editor.EditOneDeclarationAsync(marshallerType, (editor, decl) => AddMissingMembers(editor, decl, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller), ct).ConfigureAwait(false);
 
            return editor.ChangedSolution;
        }
 
        // Get the managed type from the CustomMarshallerAttribute located at the provided location in source on the provided type.
        // As we only get fixable diagnostics for types that have valid non-null managed types in the CustomMarshallerAttribute applications,
        // we do not need to worry about the returned symbol being null.
        private static ITypeSymbol GetManagedTypeInAttributeSyntax(Location locationInAttribute, INamedTypeSymbol attributedTypeSymbol)
            => (ITypeSymbol)attributedTypeSymbol.GetAttributes().First(attr =>
                    attr.ApplicationSyntaxReference.SyntaxTree == locationInAttribute.SourceTree
                    && attr.ApplicationSyntaxReference.Span.Contains(locationInAttribute.SourceSpan)).ConstructorArguments[0].Value!;
 
        private static void AddMissingMembers(
            DocumentEditor editor,
            SyntaxNode declaringSyntax,
            INamedTypeSymbol marshallerType,
            ITypeSymbol managedType,
            HashSet<string> missingMemberNames,
            bool isLinearCollectionMarshaller)
        {
            if (marshallerType.IsStatic && marshallerType.IsReferenceType)
            {
                AddMissingMembersToStatelessMarshaller(editor, declaringSyntax, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller);
            }
            if (marshallerType.IsValueType)
            {
                AddMissingMembersToStatefulMarshaller(editor, declaringSyntax, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller);
            }
        }
 
        private static void AddMissingMembersToStatelessMarshaller(DocumentEditor editor, SyntaxNode declaringSyntax, INamedTypeSymbol marshallerType, ITypeSymbol managedType, HashSet<string> missingMemberNames, bool isLinearCollectionMarshaller)
        {
            SyntaxGenerator gen = editor.Generator;
            // Get the methods of the shape so we can use them to determine what types to use in signatures that are not obvious.
            var (_, methods) = StatelessMarshallerShapeHelper.GetShapeForType(marshallerType, managedType, isLinearCollectionMarshaller, editor.SemanticModel.Compilation);
            INamedTypeSymbol spanOfT = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.System_Span_Metadata)!;
            INamedTypeSymbol readOnlySpanOfT = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!;
            var (typeParameters, _) = marshallerType.GetAllTypeArgumentsIncludingInContainingTypes();
 
            // Use a lazy factory for the type syntaxes to avoid re-checking the various methods and reconstructing the syntax.
            Lazy<SyntaxNode> unmanagedTypeSyntax = new(CreateUnmanagedTypeSyntax, isThreadSafe: false);
            Lazy<ITypeSymbol> managedElementTypeSymbol = new(CreateManagedElementTypeSymbol, isThreadSafe: false);
 
            List<SyntaxNode> newMembers = new();
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateless.ConvertToUnmanaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateless.ConvertToUnmanaged,
                        parameters: new[] { gen.ParameterDeclaration("managed", gen.TypeExpression(managedType)) },
                        returnType: unmanagedTypeSyntax.Value,
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateless.ConvertToManaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateless.ConvertToManaged,
                        parameters: new[] { gen.ParameterDeclaration("unmanaged", unmanagedTypeSyntax.Value) },
                        returnType: gen.TypeExpression(managedType),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.BufferSize))
            {
                newMembers.Add(
                    gen.WithAccessorDeclarations(
                        gen.PropertyDeclaration(ShapeMemberNames.BufferSize,
                            gen.TypeExpression(editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_Int32)),
                            Accessibility.Public,
                            DeclarationModifiers.Static),
                        gen.GetAccessorDeclaration(statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) })));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForUnmanagedElements))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForUnmanagedElements,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("managed", gen.TypeExpression(managedType)),
                            gen.ParameterDeclaration("numElements", type: gen.TypeExpression(SpecialType.System_Int32), refKind: RefKind.Out),
                        },
                        returnType: unmanagedTypeSyntax.Value,
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForManagedElements))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.AllocateContainerForManagedElements,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("unmanaged", unmanagedTypeSyntax.Value),
                            gen.ParameterDeclaration("numElements", type: gen.TypeExpression(SpecialType.System_Int32)),
                        },
                        returnType: gen.TypeExpression(managedType),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesSource,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("managed", gen.TypeExpression(managedType))
                        },
                        returnType: gen.TypeExpression(readOnlySpanOfT.Construct(managedElementTypeSymbol.Value)),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesDestination))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesDestination,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("unmanaged", unmanagedTypeSyntax.Value),
                            gen.ParameterDeclaration("numElements", gen.TypeExpression(SpecialType.System_Int32))
                        },
                        returnType: gen.TypeExpression(spanOfT.Construct(typeParameters[typeParameters.Length - 1])),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesSource))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.GetUnmanagedValuesSource,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("unmanaged", unmanagedTypeSyntax.Value),
                            gen.ParameterDeclaration("numElements", gen.TypeExpression(SpecialType.System_Int32))
                        },
                        returnType: gen.TypeExpression(readOnlySpanOfT.Construct(typeParameters[typeParameters.Length - 1])),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesDestination))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateless.GetManagedValuesDestination,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("managed", gen.TypeExpression(managedType))
                        },
                        returnType: gen.TypeExpression(spanOfT.Construct(managedElementTypeSymbol.Value)),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.Static,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            editor.ReplaceNode(declaringSyntax, (declaringSyntax, gen) => gen.AddMembers(declaringSyntax, newMembers));
 
            SyntaxNode CreateUnmanagedTypeSyntax()
            {
                ITypeSymbol? unmanagedType = null;
                if (methods.ToUnmanaged is not null)
                {
                    unmanagedType = methods.ToUnmanaged.ReturnType;
                }
                else if (methods.ToUnmanagedWithBuffer is not null)
                {
                    unmanagedType = methods.ToUnmanagedWithBuffer.ReturnType;
                }
                else if (methods.ToManaged is not null)
                {
                    unmanagedType = methods.ToManaged.Parameters[0].Type;
                }
                else if (methods.ToManagedFinally is not null)
                {
                    unmanagedType = methods.ToManagedFinally.Parameters[0].Type;
                }
                else if (methods.UnmanagedValuesSource is not null)
                {
                    unmanagedType = methods.UnmanagedValuesSource.Parameters[0].Type;
                }
                else if (methods.UnmanagedValuesDestination is not null)
                {
                    unmanagedType = methods.UnmanagedValuesDestination.Parameters[0].Type;
                }
 
                if (unmanagedType is not null)
                {
                    return gen.TypeExpression(unmanagedType);
                }
                return gen.TypeExpression(editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_IntPtr));
            }
 
            ITypeSymbol CreateManagedElementTypeSymbol()
            {
                if (methods.ManagedValuesSource is not null)
                {
                    return ((INamedTypeSymbol)methods.ManagedValuesSource.ReturnType).TypeArguments[0];
                }
                if (methods.ManagedValuesDestination is not null)
                {
                    return ((INamedTypeSymbol)methods.ManagedValuesDestination.ReturnType).TypeArguments[0];
                }
 
                return editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_IntPtr);
            }
        }
 
        private static void AddMissingMembersToStatefulMarshaller(DocumentEditor editor, SyntaxNode declaringSyntax, INamedTypeSymbol marshallerType, ITypeSymbol managedType, HashSet<string> missingMemberNames, bool isLinearCollectionMarshaller)
        {
            SyntaxGenerator gen = editor.Generator;
            // Get the methods of the shape so we can use them to determine what types to use in signatures that are not obvious.
            var (_, methods) = StatefulMarshallerShapeHelper.GetShapeForType(marshallerType, managedType, isLinearCollectionMarshaller, editor.SemanticModel.Compilation);
            INamedTypeSymbol spanOfT = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.System_Span_Metadata)!;
            INamedTypeSymbol readOnlySpanOfT = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!;
            var (typeParameters, _) = marshallerType.GetAllTypeArgumentsIncludingInContainingTypes();
 
            // Use a lazy factory for the type syntaxes to avoid re-checking the various methods and reconstructing the syntax.
            Lazy<SyntaxNode> unmanagedTypeSyntax = new(CreateUnmanagedTypeSyntax, isThreadSafe: false);
            Lazy<ITypeSymbol> managedElementTypeSymbol = new(CreateManagedElementTypeSymbol, isThreadSafe: false);
 
            List<SyntaxNode> newMembers = new();
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateful.FromManaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateful.FromManaged,
                        parameters: new[] { gen.ParameterDeclaration("managed", gen.TypeExpression(managedType)) },
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateful.ToUnmanaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateful.ToUnmanaged,
                        returnType: unmanagedTypeSyntax.Value,
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateful.FromUnmanaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateful.FromUnmanaged,
                        parameters: new[] { gen.ParameterDeclaration("unmanaged", unmanagedTypeSyntax.Value) },
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.Value.Stateful.ToManaged))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateful.ToManaged,
                        returnType: gen.TypeExpression(managedType),
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.BufferSize))
            {
                newMembers.Add(
                    gen.WithAccessorDeclarations(
                        gen.PropertyDeclaration(ShapeMemberNames.BufferSize,
                            gen.TypeExpression(editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_Int32)),
                            Accessibility.Public,
                            DeclarationModifiers.Static),
                        gen.GetAccessorDeclaration(statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) })));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateful.GetManagedValuesSource))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateful.GetManagedValuesSource,
                        returnType: gen.TypeExpression(readOnlySpanOfT.Construct(managedElementTypeSymbol.Value)),
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateful.GetUnmanagedValuesDestination))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateful.GetUnmanagedValuesDestination,
                        returnType: gen.TypeExpression(spanOfT.Construct(typeParameters[typeParameters.Length - 1])),
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateful.GetUnmanagedValuesSource))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateful.GetUnmanagedValuesSource,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("numElements", gen.TypeExpression(SpecialType.System_Int32))
                        },
                        returnType: gen.TypeExpression(readOnlySpanOfT.Construct(typeParameters[typeParameters.Length - 1])),
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.LinearCollection.Stateful.GetManagedValuesDestination))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.LinearCollection.Stateful.GetManagedValuesDestination,
                        parameters: new[]
                        {
                            gen.ParameterDeclaration("numElements", gen.TypeExpression(SpecialType.System_Int32))
                        },
                        returnType: gen.TypeExpression(spanOfT.Construct(managedElementTypeSymbol.Value)),
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            if (missingMemberNames.Contains(ShapeMemberNames.Free))
            {
                newMembers.Add(
                    gen.MethodDeclaration(
                        ShapeMemberNames.Value.Stateful.Free,
                        accessibility: Accessibility.Public,
                        statements: new[] { DefaultMethodStatement(gen, editor.SemanticModel.Compilation) }));
            }
 
            editor.ReplaceNode(declaringSyntax, (declaringSyntax, gen) => gen.AddMembers(declaringSyntax, newMembers));
 
            SyntaxNode CreateUnmanagedTypeSyntax()
            {
                ITypeSymbol? unmanagedType = null;
                if (methods.ToUnmanaged is not null)
                {
                    unmanagedType = methods.ToUnmanaged.ReturnType;
                }
                else if (methods.FromUnmanaged is not null)
                {
                    unmanagedType = methods.FromUnmanaged.Parameters[0].Type;
                }
                else if (methods.UnmanagedValuesSource is not null)
                {
                    unmanagedType = methods.UnmanagedValuesSource.Parameters[0].Type;
                }
                else if (methods.UnmanagedValuesDestination is not null)
                {
                    unmanagedType = methods.UnmanagedValuesDestination.Parameters[0].Type;
                }
 
                if (unmanagedType is not null)
                {
                    return gen.TypeExpression(unmanagedType);
                }
                return gen.TypeExpression(editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_IntPtr));
            }
 
            ITypeSymbol CreateManagedElementTypeSymbol()
            {
                if (methods.ManagedValuesSource is not null)
                {
                    return ((INamedTypeSymbol)methods.ManagedValuesSource.ReturnType).TypeArguments[0];
                }
                if (methods.ManagedValuesDestination is not null)
                {
                    return ((INamedTypeSymbol)methods.ManagedValuesDestination.ReturnType).TypeArguments[0];
                }
 
                return editor.SemanticModel.Compilation.GetSpecialType(SpecialType.System_IntPtr);
            }
        }
 
        private static SyntaxNode DefaultMethodStatement(SyntaxGenerator generator, Compilation compilation)
        {
            return generator.ThrowStatement(generator.ObjectCreationExpression(
                generator.TypeExpression(
                    compilation.GetTypeByMetadataName("System.NotImplementedException"))));
        }
#pragma warning disable IDE0060
    }
}