| File: Symbols\Synthesized\SynthesizedCollectionBuilderProjectedMethodSymbol.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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. using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols; /// <summary> /// See https://github.com/dotnet/csharplang/blob/90f1d8b0e9ba8a140f73aef376833969cce8bf9e/proposals/collection-expression-arguments.md?plain=1#L225 /// For collection builders: For each create method for the target type, we define a projection method with an identical /// signature to the create method but without the last parameter. This is the signature of the method a `with(...)` /// element will be matched against when using a collection builder type for a collection expression. /// </summary> /// <remarks> /// The 'Create' methods found are guaranteed by the spec to match generic arity with the collection type being created. /// So they will have signatures like: /// <code><![CDATA[ /// CollectionType<T1, T2> Create<T1, T2>(Parameter1, .. ParameterN, ReadOnlySpan<ElementType<T1, T2>> elements) /// ]]></code> /// Then spec then requires: For a collection expression with a target type <c><![CDATA[C<S0, S1, …>]]></c> where the /// type declaration <![CDATA[C<T0, T1, …>]]> has an associated builder method <![CDATA[B.M<U0, U1, …>()]]>, the generic /// type arguments from the target type are applied in order — and from outermost containing type to innermost — to the /// builder method. /// <para/> Because of this, the collection builder method will actually be the constructed method, not the original /// definition. And from this constructed method, we will then generate: <![CDATA[CollectionType<X, Y> /// Create(Parameter1, .. ParameterN)]]>. In other words, the projected method will have the same return type, no type /// parameters/arguments, and all but the last constructed parameter from the original method. /// </remarks> internal sealed class SynthesizedCollectionBuilderProjectedMethodSymbol( MethodSymbol originalCollectionBuilderMethod) : WrappedMethodSymbol { private readonly MethodSymbol _originalCollectionBuilderMethod = originalCollectionBuilderMethod; private ImmutableArray<ParameterSymbol> _lazyParameters; public override MethodSymbol UnderlyingMethod => _originalCollectionBuilderMethod; public override ImmutableArray<CSharpAttributeData> GetAttributes() => this.UnderlyingMethod.GetAttributes(); public override Symbol ContainingSymbol => this.UnderlyingMethod.ContainingSymbol; public override bool IsAsync => this.UnderlyingMethod.IsAsync; public override ImmutableArray<CustomModifier> RefCustomModifiers => this.UnderlyingMethod.RefCustomModifiers; public override TypeWithAnnotations ReturnTypeWithAnnotations => this.UnderlyingMethod.ReturnTypeWithAnnotations; internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); /// <summary> /// The projection method itself is intentionally not obsolete. We don't want to report obsoletion errors when /// using it in some speculative binding for overload resolution. Instead, we will then report the error on the /// original <see cref="UnderlyingMethod"/> this points at directly in <see /// cref="Binder.CheckCollectionBuilderMethod"/>. /// </summary> internal override ObsoleteAttributeData? ObsoleteAttributeData => null; /// <summary> /// Similarly to <see cref="ObsoleteAttributeData"/>, we do not want to report unmanaged callers only on this /// method. Instead, we will then report the error on the original <see cref="UnderlyingMethod"/> this points at /// directly in <see cref="Binder.CheckCollectionBuilderMethod"/>. /// </summary> internal override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null; /// <summary> /// Similarly to <see cref="ObsoleteAttributeData"/>, we report caller-unsafe errors on the <see cref="UnderlyingMethod"/> instead. /// </summary> internal override CallerUnsafeMode GetCallerUnsafeMode(ConsList<FieldSymbol> fieldsBeingBound) => CallerUnsafeMode.None; // Note: it is very intentional that we return empty arrays for Type arguments/parameters. Consider a // hypothetical signature like: // // Dict<TKey, TValue> Create<TKey, TValue>(IEqualityComparer<TKey> comparer, ReadOnlySpan<KeyValuePair<TKey, TValue>> elements) // // Where the target type is `Dict<int, string>`. The conversion process will already have instantiated this method // with the appropriate `int, string` type arguments. What we want to then expose is a signature like: // // Dict<int, string> Create(IEqualityComparer<int> comparer) // // i.e. we want to remove the type parameters and the final parameter that takes the elements. That way there is // no more inference done, or any confusion about needing type arguments when resolving a `with(...)` element // against this signature. public override int Arity => 0; public override bool IsGenericMethod => false; public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => []; public override ImmutableArray<TypeParameterSymbol> TypeParameters => []; internal override int ParameterCount => base.ParameterCount - 1; public override ImmutableArray<ParameterSymbol> Parameters { get { if (_lazyParameters.IsDefault) { // Grab all but the last parameter from the underlying method. var parameters = this.UnderlyingMethod.Parameters; var parameterCount = parameters.Length - 1; var builder = ArrayBuilder<ParameterSymbol>.GetInstance(parameterCount); for (int i = 0; i < parameterCount; i++) builder.Add(new SynthesizedCollectionBuilderProjectedParameterSymbol(this, parameters[i])); ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, builder.ToImmutableAndFree()); } return _lazyParameters; } } internal override int TryGetOverloadResolutionPriority() => this.UnderlyingMethod.TryGetOverloadResolutionPriority(); public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => throw ExceptionUtilities.Unreachable(); public override Symbol AssociatedSymbol => throw ExceptionUtilities.Unreachable(); internal override bool HasSpecialNameAttribute => throw ExceptionUtilities.Unreachable(); internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => throw ExceptionUtilities.Unreachable(); internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) => throw ExceptionUtilities.Unreachable(); internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes) => throw ExceptionUtilities.Unreachable(); private sealed class SynthesizedCollectionBuilderProjectedParameterSymbol( SynthesizedCollectionBuilderProjectedMethodSymbol methodSymbol, ParameterSymbol originalParameter) : WrappedParameterSymbol(originalParameter) { private readonly SynthesizedCollectionBuilderProjectedMethodSymbol _methodSymbol = methodSymbol; public override Symbol ContainingSymbol => _methodSymbol; internal override bool IsCallerLineNumber => this.UnderlyingParameter.IsCallerLineNumber; internal override bool IsCallerFilePath => this.UnderlyingParameter.IsCallerFilePath; internal override bool IsCallerMemberName => this.UnderlyingParameter.IsCallerMemberName; internal override int CallerArgumentExpressionParameterIndex => this.UnderlyingParameter.CallerArgumentExpressionParameterIndex; internal override bool HasEnumeratorCancellationAttribute => throw ExceptionUtilities.Unreachable(); internal override ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes => this.UnderlyingParameter.InterpolatedStringHandlerArgumentIndexes; internal override bool HasInterpolatedStringHandlerArgumentError => this.UnderlyingParameter.HasInterpolatedStringHandlerArgumentError; internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes) => throw ExceptionUtilities.Unreachable(); } }