|
// 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;
}
}
|