File: RuntimeSource\Configuration.Binder\Parser\OptionsBuilderConfigurationExtensions.cs
Web Access
Project: src\src\Tools\ConfigurationSchemaGenerator\ConfigurationSchemaGenerator.csproj (ConfigurationSchemaGenerator)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
 
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
    public sealed partial class ConfigurationBindingGenerator
    {
        internal sealed partial class Parser
        {
            private void ParseInvocation_OptionsBuilderExt(BinderInvocation invocation)
            {
                IMethodSymbol targetMethod = invocation.Operation.TargetMethod;
                ImmutableArray<IParameterSymbol> @params = targetMethod.Parameters;
 
                if (!targetMethod.IsGenericMethod ||
                    @params.Length < 2 ||
                    @params[0].Type is not INamedTypeSymbol { IsGenericType: true } genericType ||
                    !SymbolEqualityComparer.Default.Equals(_typeSymbols.OptionsBuilderOfT_Unbound, genericType.ConstructUnboundGenericType()))
                {
                    return;
                }
 
                ITypeSymbol? typeSymbol = targetMethod.TypeArguments[0].WithNullableAnnotation(NullableAnnotation.None);
                // This would violate generic type constraint; any such invocation could not have been included in the initial parser.
                Debug.Assert(typeSymbol?.IsValueType is not true);
 
                if (targetMethod.Name is "Bind")
                {
                    ParseBindInvocation_OptionsBuilderExt(invocation, typeSymbol);
                }
                else if (targetMethod.Name is "BindConfiguration")
                {
                    ParseBindConfigurationInvocation(invocation, typeSymbol);
                }
            }
 
            private void ParseBindInvocation_OptionsBuilderExt(BinderInvocation invocation, ITypeSymbol? type)
            {
                IInvocationOperation operation = invocation.Operation!;
                IMethodSymbol targetMethod = operation.TargetMethod;
                ImmutableArray<IParameterSymbol> @params = targetMethod.Parameters;
                int paramCount = @params.Length;
 
                Debug.Assert(paramCount >= 2);
 
                if (!SymbolEqualityComparer.Default.Equals(_typeSymbols.IConfiguration, @params[1].Type))
                {
                    return;
                }
 
                MethodsToGen overload = paramCount switch
                {
                    2 => MethodsToGen.OptionsBuilderExt_Bind_T,
                    3 when SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type) =>
                        MethodsToGen.OptionsBuilderExt_Bind_T_BinderOptions,
                    _ => MethodsToGen.None
                };
 
                if (overload is not MethodsToGen.None)
                {
                    EnqueueTargetTypeForRootInvocation(type, overload, invocation);
                }
            }
 
            private void ParseBindConfigurationInvocation(BinderInvocation invocation, ITypeSymbol? type)
            {
                IMethodSymbol targetMethod = invocation.Operation.TargetMethod;
                ImmutableArray<IParameterSymbol> @params = targetMethod.Parameters;
 
                int paramCount = @params.Length;
                Debug.Assert(paramCount >= 2);
 
                if (paramCount is 3 &&
                    @params[1].Type.SpecialType is SpecialType.System_String &&
                    SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type))
                {
                    EnqueueTargetTypeForRootInvocation(type, MethodsToGen.OptionsBuilderExt_BindConfiguration_T_path_BinderOptions, invocation);
                }
            }
 
            private void RegisterInterceptor_OptionsBuilderExt(TypeParseInfo typeParseInfo, TypeSpec typeSpec)
            {
                MethodsToGen overload = typeParseInfo.BindingOverload;
                Debug.Assert((MethodsToGen.OptionsBuilderExt_Any & overload) is not 0);
 
                if (typeSpec is not ComplexTypeSpec complexTypeSpec)
                {
                    return;
                }
 
                if ((MethodsToGen.OptionsBuilderExt_Bind & overload) is not 0)
                {
                    if (!TryRegisterTypeForOverloadGen_ServiceCollectionExt(MethodsToGen.ServiceCollectionExt_Configure_T_name_BinderOptions, complexTypeSpec))
                    {
                        return;
                    }
                }
                else if (!_helperInfoBuilder!.TryRegisterTypeForBindCoreMainGen(complexTypeSpec))
                {
                    return;
                }
 
                _interceptorInfoBuilder.RegisterInterceptor(typeParseInfo.BindingOverload, typeParseInfo.BinderInvocation.Operation);
 
                // Emitting refs to IOptionsChangeTokenSource, ConfigurationChangeTokenSource.
                _helperInfoBuilder!.RegisterNamespace("Microsoft.Extensions.Options");
 
                // Emitting refs to OptionsBuilder<T>.
                _helperInfoBuilder!.RegisterNamespace("Microsoft.Extensions.DependencyInjection");
            }
        }
    }
}