|
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateMethod;
internal abstract class CSharpGenerateParameterizedMemberService<TService> : AbstractGenerateParameterizedMemberService<TService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
where TService : AbstractGenerateParameterizedMemberService<TService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
{
internal sealed class InvocationExpressionInfo(SemanticDocument document, State state) : AbstractInvocationInfo(document, state)
{
private readonly InvocationExpressionSyntax _invocationExpression = state.InvocationExpressionOpt;
protected override ImmutableArray<ParameterName> DetermineParameterNames(CancellationToken cancellationToken)
{
return Document.SemanticModel.GenerateParameterNames(
_invocationExpression.ArgumentList, cancellationToken);
}
protected override RefKind DetermineRefKind(CancellationToken cancellationToken)
=> _invocationExpression.IsParentKind(SyntaxKind.RefExpression) ? RefKind.Ref : RefKind.None;
protected override ITypeSymbol DetermineReturnTypeWorker(CancellationToken cancellationToken)
{
// Defer to the type inferrer to figure out what the return type of this new method
// should be.
var typeInference = Document.Document.GetLanguageService<ITypeInferenceService>();
var inferredType = typeInference.InferType(
Document.SemanticModel, _invocationExpression, objectAsDefault: true,
name: State.IdentifierToken.ValueText, cancellationToken);
return inferredType;
}
protected override ImmutableArray<ITypeParameterSymbol> GetCapturedTypeParameters(CancellationToken cancellationToken)
{
var result = new List<ITypeParameterSymbol>();
var semanticModel = Document.SemanticModel;
foreach (var argument in _invocationExpression.ArgumentList.Arguments)
{
var type = argument.DetermineParameterType(semanticModel, cancellationToken);
type.GetReferencedTypeParameters(result);
}
return [.. result];
}
protected override ImmutableArray<ITypeParameterSymbol> GenerateTypeParameters(CancellationToken cancellationToken)
{
// Generate dummy type parameter names for a generic method. If the user is inside a
// generic method, and calls a generic method with type arguments from the outer
// method, then use those same names for the generated type parameters.
//
// TODO(cyrusn): If we do capture method type variables, then we should probably
// capture their constraints as well.
var genericName = (GenericNameSyntax)State.SimpleNameOpt;
var semanticModel = Document.SemanticModel;
if (genericName.TypeArgumentList.Arguments.Count == 1)
{
var typeParameter = GetUniqueTypeParameter(
genericName.TypeArgumentList.Arguments.First(),
s => !State.TypeToGenerateIn.GetAllTypeParameters().Any(static (t, s) => t.Name == s, s),
cancellationToken);
return [typeParameter];
}
else
{
var list = new FixedSizeArrayBuilder<ITypeParameterSymbol>(genericName.TypeArgumentList.Arguments.Count);
using var _ = PooledHashSet<string>.GetInstance(out var usedIdentifiers);
usedIdentifiers.Add("T");
foreach (var type in genericName.TypeArgumentList.Arguments)
{
var typeParameter = GetUniqueTypeParameter(
type,
s => !usedIdentifiers.Contains(s) && !State.TypeToGenerateIn.GetAllTypeParameters().Any(static (t, s) => t.Name == s, s),
cancellationToken);
usedIdentifiers.Add(typeParameter.Name);
list.Add(typeParameter);
}
return list.MoveToImmutable();
}
}
private ITypeParameterSymbol GetUniqueTypeParameter(
TypeSyntax type,
Func<string, bool> isUnique,
CancellationToken cancellationToken)
{
var methodTypeParameter = GetMethodTypeParameter(type, cancellationToken);
return methodTypeParameter ?? CodeGenerationSymbolFactory.CreateTypeParameterSymbol(NameGenerator.GenerateUniqueName("T", isUnique));
}
private ITypeParameterSymbol GetMethodTypeParameter(TypeSyntax type, CancellationToken cancellationToken)
{
if (type is IdentifierNameSyntax)
{
var info = Document.SemanticModel.GetTypeInfo(type, cancellationToken);
if (info.Type is ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method } typeParameter)
return typeParameter;
}
return null;
}
protected override ImmutableArray<RefKind> DetermineParameterModifiers(CancellationToken cancellationToken)
=> _invocationExpression.ArgumentList.Arguments.Select(a => a.GetRefKind()).ToImmutableArray();
protected override ImmutableArray<ITypeSymbol> DetermineParameterTypes(CancellationToken cancellationToken)
=> _invocationExpression.ArgumentList.Arguments.Select(a => DetermineParameterType(a, cancellationToken)).ToImmutableArray();
private ITypeSymbol DetermineParameterType(ArgumentSyntax argument, CancellationToken cancellationToken)
=> argument.DetermineParameterType(Document.SemanticModel, cancellationToken);
protected override ImmutableArray<bool> DetermineParameterOptionality(CancellationToken cancellationToken)
=> _invocationExpression.ArgumentList.Arguments.Select(a => false).ToImmutableArray();
protected override bool IsIdentifierName()
=> State.SimpleNameOpt.Kind() == SyntaxKind.IdentifierName;
protected override bool IsImplicitReferenceConversion(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType)
{
var conversion = compilation.ClassifyConversion(sourceType, targetType);
return conversion.IsImplicit && conversion.IsReference;
}
protected override ImmutableArray<ITypeSymbol> DetermineTypeArguments(CancellationToken cancellationToken)
{
if (State.SimpleNameOpt is not GenericNameSyntax genericName)
return [];
var result = new FixedSizeArrayBuilder<ITypeSymbol>(genericName.TypeArgumentList.Arguments.Count);
foreach (var typeArgument in genericName.TypeArgumentList.Arguments)
{
var typeInfo = Document.SemanticModel.GetTypeInfo(typeArgument, cancellationToken);
result.Add(typeInfo.Type);
}
return result.MoveToImmutable();
}
}
}
|