File: src\Analyzers\CSharp\CodeFixes\GenerateParameterizedMember\CSharpGenerateMethodService.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.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateParameterizedMember;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateMethod;
 
[ExportLanguageService(typeof(IGenerateParameterizedMemberService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpGenerateMethodService() :
    AbstractGenerateMethodService<CSharpGenerateMethodService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
{
    protected override bool IsExplicitInterfaceGeneration(SyntaxNode node)
        => node is MethodDeclarationSyntax;
 
    protected override bool IsSimpleNameGeneration(SyntaxNode node)
        => node is SimpleNameSyntax;
 
    protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
        => containingType.ContainingTypesOrSelfHasUnsafeKeyword();
 
    protected override AbstractInvocationInfo CreateInvocationMethodInfo(SemanticDocument document, AbstractGenerateParameterizedMemberService<CSharpGenerateMethodService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
        => new CSharpGenerateParameterizedMemberService<CSharpGenerateMethodService>.InvocationExpressionInfo(document, state);
 
    protected override bool AreSpecialOptionsActive(SemanticModel semanticModel)
        => CSharpCommonGenerationServiceMethods.AreSpecialOptionsActive();
 
    protected override bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel)
        => CSharpCommonGenerationServiceMethods.IsValidSymbol();
 
    protected override bool TryInitializeExplicitInterfaceState(
        SemanticDocument document,
        SyntaxNode node,
        CancellationToken cancellationToken,
        out SyntaxToken identifierToken,
        out IMethodSymbol methodSymbol,
        out INamedTypeSymbol typeToGenerateIn)
    {
        var methodDeclaration = (MethodDeclarationSyntax)node;
        identifierToken = methodDeclaration.Identifier;
 
        if (methodDeclaration.ExplicitInterfaceSpecifier != null &&
            !methodDeclaration.ParameterList.OpenParenToken.IsMissing &&
            !methodDeclaration.ParameterList.CloseParenToken.IsMissing)
        {
            var semanticModel = document.SemanticModel;
            methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
            if (methodSymbol != null && !methodSymbol.ExplicitInterfaceImplementations.Any())
            {
                var semanticInfo = semanticModel.GetTypeInfo(methodDeclaration.ExplicitInterfaceSpecifier.Name, cancellationToken);
                typeToGenerateIn = semanticInfo.Type as INamedTypeSymbol;
                return typeToGenerateIn != null;
            }
        }
 
        identifierToken = default;
        methodSymbol = null;
        typeToGenerateIn = null;
        return false;
    }
 
    protected override bool TryInitializeSimpleNameState(
        SemanticDocument document,
        SimpleNameSyntax simpleName,
        CancellationToken cancellationToken,
        out SyntaxToken identifierToken,
        out ExpressionSyntax simpleNameOrMemberAccessExpression,
        out InvocationExpressionSyntax invocationExpressionOpt,
        out bool isInConditionalAccessExpression)
    {
        identifierToken = simpleName.Identifier;
 
        var memberAccess = simpleName?.Parent as MemberAccessExpressionSyntax;
        var conditionalMemberAccess = simpleName?.Parent?.Parent?.Parent as ConditionalAccessExpressionSyntax;
        var inConditionalMemberAccess = conditionalMemberAccess != null;
        if (memberAccess != null)
        {
            simpleNameOrMemberAccessExpression = memberAccess;
        }
        else if (inConditionalMemberAccess)
        {
            simpleNameOrMemberAccessExpression = conditionalMemberAccess;
        }
        else
        {
            simpleNameOrMemberAccessExpression = simpleName;
        }
 
        if (memberAccess == null || memberAccess.Name == simpleName)
        {
            if (simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression, out invocationExpressionOpt))
            {
                isInConditionalAccessExpression = inConditionalMemberAccess;
                return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing;
            }
            // We need to check that the tree is structured like so:
            // ConditionalAccessExpressionSyntax
            //    ->  InvocationExpressionSyntax
            //          ->   MemberBindingExpressionSyntax
            // and that the name at the end of this expression matches the simple name we were given
            else if ((((simpleNameOrMemberAccessExpression as ConditionalAccessExpressionSyntax)
                       ?.WhenNotNull as InvocationExpressionSyntax)
                            ?.Expression as MemberBindingExpressionSyntax)
                                ?.Name == simpleName)
            {
                invocationExpressionOpt = (InvocationExpressionSyntax)((ConditionalAccessExpressionSyntax)simpleNameOrMemberAccessExpression).WhenNotNull;
                isInConditionalAccessExpression = inConditionalMemberAccess;
                return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing;
            }
            else if (simpleName.IsKind(SyntaxKind.IdentifierName))
            {
                // If we don't have an invocation node, then see if we can infer a delegate in
                // this location. Check if this is a place where a delegate can go.  Only do this
                // for identifier names. for now.  It gets really funky if you have to deal with
                // a generic name here.
 
                // Can't assign into a method.
                if (!simpleNameOrMemberAccessExpression.IsLeftSideOfAnyAssignExpression())
                {
                    invocationExpressionOpt = null;
                    isInConditionalAccessExpression = inConditionalMemberAccess;
                    return true;
                }
            }
        }
 
        identifierToken = default;
        simpleNameOrMemberAccessExpression = null;
        invocationExpressionOpt = null;
        isInConditionalAccessExpression = false;
        return false;
    }
 
    protected override ITypeSymbol DetermineReturnTypeForSimpleNameOrMemberAccessExpression(
        ITypeInferenceService typeInferenceService,
        SemanticModel semanticModel,
        ExpressionSyntax expression,
        CancellationToken cancellationToken)
    {
        if (semanticModel.SyntaxTree.IsNameOfContext(expression.SpanStart, semanticModel, cancellationToken))
        {
            return typeInferenceService.InferType(semanticModel, expression, true, cancellationToken);
        }
 
        return null;
    }
}