File: src\Analyzers\CSharp\CodeFixes\GenerateParameterizedMember\CSharpGenerateConversionService.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateMethod;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateParameterizedMember;
 
[ExportLanguageService(typeof(IGenerateConversionService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed partial class CSharpGenerateConversionService() :
    AbstractGenerateConversionService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
{
    protected override bool IsImplicitConversionGeneration(SyntaxNode node)
    {
        return node is ExpressionSyntax &&
                (node.Parent is AssignmentExpressionSyntax || node.Parent is EqualsValueClauseSyntax) &&
                !(node is CastExpressionSyntax) &&
                !(node is MemberAccessExpressionSyntax);
    }
 
    protected override bool IsExplicitConversionGeneration(SyntaxNode node)
        => node is CastExpressionSyntax;
 
    protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
        => containingType.ContainingTypesOrSelfHasUnsafeKeyword();
 
    protected override AbstractInvocationInfo CreateInvocationMethodInfo(
        SemanticDocument document, AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
    {
        return new CSharpGenerateParameterizedMemberService<CSharpGenerateConversionService>.InvocationExpressionInfo(document, state);
    }
 
    protected override bool AreSpecialOptionsActive(SemanticModel semanticModel)
        => CSharpCommonGenerationServiceMethods.AreSpecialOptionsActive();
 
    protected override bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel)
        => CSharpCommonGenerationServiceMethods.IsValidSymbol();
 
    protected override bool TryInitializeImplicitConversionState(
       SemanticDocument document,
       SyntaxNode expression,
       ISet<TypeKind> classInterfaceModuleStructTypes,
       CancellationToken cancellationToken,
       out SyntaxToken identifierToken,
       out IMethodSymbol methodSymbol,
       out INamedTypeSymbol typeToGenerateIn)
    {
        if (TryGetConversionMethodAndTypeToGenerateIn(document, expression, classInterfaceModuleStructTypes, cancellationToken, out methodSymbol, out typeToGenerateIn))
        {
            identifierToken = SyntaxFactory.Token(
                default,
                SyntaxKind.ImplicitKeyword,
                WellKnownMemberNames.ImplicitConversionName,
                WellKnownMemberNames.ImplicitConversionName,
                default);
            return true;
        }
 
        identifierToken = default;
        methodSymbol = null;
        typeToGenerateIn = null;
        return false;
    }
 
    protected override bool TryInitializeExplicitConversionState(
        SemanticDocument document,
        SyntaxNode expression,
        ISet<TypeKind> classInterfaceModuleStructTypes,
        CancellationToken cancellationToken,
        out SyntaxToken identifierToken,
        out IMethodSymbol methodSymbol,
        out INamedTypeSymbol typeToGenerateIn)
    {
        if (TryGetConversionMethodAndTypeToGenerateIn(document, expression, classInterfaceModuleStructTypes, cancellationToken, out methodSymbol, out typeToGenerateIn))
        {
            identifierToken = SyntaxFactory.Token(
                default,
                SyntaxKind.ImplicitKeyword,
                WellKnownMemberNames.ExplicitConversionName,
                WellKnownMemberNames.ExplicitConversionName,
                default);
            return true;
        }
 
        identifierToken = default;
        methodSymbol = null;
        typeToGenerateIn = null;
        return false;
    }
 
    private static bool TryGetConversionMethodAndTypeToGenerateIn(
        SemanticDocument document,
        SyntaxNode expression,
        ISet<TypeKind> classInterfaceModuleStructTypes,
        CancellationToken cancellationToken,
        out IMethodSymbol methodSymbol,
        out INamedTypeSymbol typeToGenerateIn)
    {
        if (expression is CastExpressionSyntax castExpression)
        {
            return TryGetExplicitConversionMethodAndTypeToGenerateIn(
                document,
                castExpression,
                classInterfaceModuleStructTypes,
                cancellationToken,
                out methodSymbol,
                out typeToGenerateIn);
        }
 
        return TryGetImplicitConversionMethodAndTypeToGenerateIn(
                document,
                expression,
                classInterfaceModuleStructTypes,
            cancellationToken,
                out methodSymbol,
                out typeToGenerateIn);
    }
 
    private static bool TryGetExplicitConversionMethodAndTypeToGenerateIn(
        SemanticDocument document,
        CastExpressionSyntax castExpression,
        ISet<TypeKind> classInterfaceModuleStructTypes,
        CancellationToken cancellationToken,
        out IMethodSymbol methodSymbol,
        out INamedTypeSymbol typeToGenerateIn)
    {
        methodSymbol = null;
        typeToGenerateIn = document.SemanticModel.GetTypeInfo(castExpression.Type, cancellationToken).Type as INamedTypeSymbol;
        if (typeToGenerateIn == null
            || document.SemanticModel.GetTypeInfo(castExpression.Expression, cancellationToken).Type is not INamedTypeSymbol parameterSymbol
            || typeToGenerateIn.IsErrorType()
            || parameterSymbol.IsErrorType())
        {
            return false;
        }
 
        methodSymbol = GenerateMethodSymbol(typeToGenerateIn, parameterSymbol);
 
        if (!ValidateTypeToGenerateIn(
                typeToGenerateIn,
                true,
                classInterfaceModuleStructTypes))
        {
            typeToGenerateIn = parameterSymbol;
        }
 
        return true;
    }
 
    private static bool TryGetImplicitConversionMethodAndTypeToGenerateIn(
        SemanticDocument document,
        SyntaxNode expression,
        ISet<TypeKind> classInterfaceModuleStructTypes,
        CancellationToken cancellationToken,
        out IMethodSymbol methodSymbol,
        out INamedTypeSymbol typeToGenerateIn)
    {
        methodSymbol = null;
        typeToGenerateIn = document.SemanticModel.GetTypeInfo(expression, cancellationToken).ConvertedType as INamedTypeSymbol;
        if (typeToGenerateIn == null
            || document.SemanticModel.GetTypeInfo(expression, cancellationToken).Type is not INamedTypeSymbol parameterSymbol
            || typeToGenerateIn.IsErrorType()
            || parameterSymbol.IsErrorType())
        {
            return false;
        }
 
        methodSymbol = GenerateMethodSymbol(typeToGenerateIn, parameterSymbol);
 
        if (!ValidateTypeToGenerateIn(
                typeToGenerateIn,
                true,
                classInterfaceModuleStructTypes))
        {
            typeToGenerateIn = parameterSymbol;
        }
 
        return true;
    }
 
    private static IMethodSymbol GenerateMethodSymbol(
        INamedTypeSymbol typeToGenerateIn, INamedTypeSymbol parameterSymbol)
    {
        // Remove any generic parameters
        if (typeToGenerateIn.IsGenericType)
        {
            typeToGenerateIn = typeToGenerateIn.ConstructUnboundGenericType().ConstructedFrom;
        }
 
        return CodeGenerationSymbolFactory.CreateMethodSymbol(
            attributes: [],
            accessibility: default,
            modifiers: default,
            returnType: typeToGenerateIn,
            refKind: RefKind.None,
            explicitInterfaceImplementations: default,
            name: null,
            typeParameters: [],
            parameters: [CodeGenerationSymbolFactory.CreateParameterSymbol(parameterSymbol, "v")],
            methodKind: MethodKind.Conversion);
    }
 
    protected override string GetImplicitConversionDisplayText(AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
        => string.Format(CodeFixesResources.Generate_implicit_conversion_operator_in_0, state.TypeToGenerateIn.Name);
 
    protected override string GetExplicitConversionDisplayText(AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
        => string.Format(CodeFixesResources.Generate_explicit_conversion_operator_in_0, state.TypeToGenerateIn.Name);
}