| File: Parser\ConfigurationBinder.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.Configuration.Binder\gen\Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj (Microsoft.Extensions.Configuration.Binder.SourceGeneration) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis; using System.Diagnostics; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { public sealed partial class ConfigurationBindingGenerator { internal sealed partial class Parser { private void ParseInvocation_ConfigurationBinder(BinderInvocation invocation) { switch (invocation.Operation.TargetMethod.Name) { case "Bind": { ParseBindInvocation_ConfigurationBinder(invocation); } break; case "Get": { ParseGetInvocation(invocation); } break; case "GetValue": { ParseGetValueInvocation(invocation); } break; } } private void ParseBindInvocation_ConfigurationBinder(BinderInvocation invocation) { IInvocationOperation operation = invocation.Operation!; ImmutableArray<IParameterSymbol> @params = operation.TargetMethod.Parameters; int paramCount = @params.Length; if (!SymbolEqualityComparer.Default.Equals(_typeSymbols.IConfiguration, @params[0].Type)) { return; } MethodsToGen overload = MethodsToGen.None; if (paramCount is 2) { overload = MethodsToGen.ConfigBinder_Bind_instance; } else if (paramCount is 3) { if (@params[1].Type.SpecialType is SpecialType.System_String) { overload = MethodsToGen.ConfigBinder_Bind_key_instance; } else if (SymbolEqualityComparer.Default.Equals(@params[2].Type, _typeSymbols.ActionOfBinderOptions)) { overload = MethodsToGen.ConfigBinder_Bind_instance_BinderOptions; } } if (overload is MethodsToGen.None) { return; } int instanceIndex = overload switch { MethodsToGen.ConfigBinder_Bind_instance => 1, MethodsToGen.ConfigBinder_Bind_instance_BinderOptions => 1, MethodsToGen.ConfigBinder_Bind_key_instance => 2, _ => throw new InvalidOperationException() }; IArgumentOperation instanceArg = GetArgumentForParameterAtIndex(operation.Arguments, instanceIndex); if (instanceArg.Parameter?.Type.SpecialType is not SpecialType.System_Object) { return; } ITypeSymbol? type = ResolveType(instanceArg.Value)?.WithNullableAnnotation(NullableAnnotation.None); if (!IsValidRootConfigType(type)) { RecordDiagnostic(DiagnosticDescriptors.CouldNotDetermineTypeInfo, invocation.Location); return; } if (type.IsValueType) { RecordDiagnostic(DiagnosticDescriptors.ValueTypesInvalidForBind, invocation.Location, messageArgs: new object[] { type }); return; } EnqueueTargetTypeForRootInvocation(type, overload, invocation); static ITypeSymbol? ResolveType(IOperation conversionOperation) => conversionOperation switch { IConversionOperation c => ResolveType(c.Operand), IInstanceReferenceOperation i => i.Type, ILocalReferenceOperation l => l.Local.Type, IFieldReferenceOperation f => f.Field.Type, IPropertyReferenceOperation o => o.Type, IMethodReferenceOperation m when m.Method.MethodKind == MethodKind.Constructor => m.Method.ContainingType, IMethodReferenceOperation m => m.Method.ReturnType, IAnonymousFunctionOperation f => f.Symbol.ReturnType, IParameterReferenceOperation p => p.Parameter.Type, IObjectCreationOperation o => o.Type, _ => null }; } private static IArgumentOperation GetArgumentForParameterAtIndex(ImmutableArray<IArgumentOperation> arguments, int parameterIndex) { foreach (var argument in arguments) { if (argument.Parameter?.Ordinal == parameterIndex) { return argument; } } throw new InvalidOperationException(); } private void ParseGetInvocation(BinderInvocation invocation) { IInvocationOperation operation = invocation.Operation!; IMethodSymbol targetMethod = operation.TargetMethod; ImmutableArray<IParameterSymbol> @params = targetMethod.Parameters; int paramCount = @params.Length; if (!SymbolEqualityComparer.Default.Equals(_typeSymbols.IConfiguration, @params[0].Type)) { return; } MethodsToGen overload = MethodsToGen.None; ITypeSymbol? type; if (targetMethod.IsGenericMethod) { if (paramCount > 2) { return; } type = targetMethod.TypeArguments[0].WithNullableAnnotation(NullableAnnotation.None); if (paramCount is 1) { overload = MethodsToGen.ConfigBinder_Get_T; } else if (paramCount is 2 && SymbolEqualityComparer.Default.Equals(@params[1].Type, _typeSymbols.ActionOfBinderOptions)) { overload = MethodsToGen.ConfigBinder_Get_T_BinderOptions; } } else if (paramCount > 3) { return; } else { ITypeOfOperation? typeOfOperation = GetArgumentForParameterAtIndex(operation.Arguments, 1).ChildOperations.FirstOrDefault() as ITypeOfOperation; type = typeOfOperation?.TypeOperand; if (paramCount is 2) { overload = MethodsToGen.ConfigBinder_Get_TypeOf; } else if (paramCount is 3 && SymbolEqualityComparer.Default.Equals(@params[2].Type, _typeSymbols.ActionOfBinderOptions)) { overload = MethodsToGen.ConfigBinder_Get_TypeOf_BinderOptions; } } EnqueueTargetTypeForRootInvocation(type, overload, invocation); } private void ParseGetValueInvocation(BinderInvocation invocation) { IInvocationOperation operation = invocation.Operation!; IMethodSymbol targetMethod = operation.TargetMethod; ImmutableArray<IParameterSymbol> @params = targetMethod.Parameters; int paramCount = @params.Length; MethodsToGen overload = MethodsToGen.None; ITypeSymbol? type; if (targetMethod.IsGenericMethod) { if (paramCount > 3 || @params[1].Type.SpecialType is not SpecialType.System_String) { return; } type = targetMethod.TypeArguments[0].WithNullableAnnotation(NullableAnnotation.None); if (paramCount is 2) { overload = MethodsToGen.ConfigBinder_GetValue_T_key; } else if (paramCount is 3 && SymbolEqualityComparer.Default.Equals(@params[2].Type, type)) { overload = MethodsToGen.ConfigBinder_GetValue_T_key_defaultValue; } } else if (paramCount > 4) { return; } else { if (@params[2].Type.SpecialType is not SpecialType.System_String) { return; } ITypeOfOperation? typeOfOperation = GetArgumentForParameterAtIndex(operation.Arguments, 1).ChildOperations.FirstOrDefault() as ITypeOfOperation; type = typeOfOperation?.TypeOperand; if (paramCount is 3) { overload = MethodsToGen.ConfigBinder_GetValue_TypeOf_key; } else if (paramCount is 4 && @params[3].Type.SpecialType is SpecialType.System_Object) { overload = MethodsToGen.ConfigBinder_GetValue_TypeOf_key_defaultValue; } } if (!IsValidRootConfigType(type)) { RecordDiagnostic(DiagnosticDescriptors.CouldNotDetermineTypeInfo, invocation.Location); return; } ITypeSymbol effectiveType = IsNullable(type, out ITypeSymbol? underlyingType) ? underlyingType : type; if (IsParsableFromString(effectiveType, out _)) { EnqueueTargetTypeForRootInvocation(type, overload, invocation); } } private void RegisterInterceptor_ConfigurationBinder(TypeParseInfo typeParseInfo, TypeSpec typeSpec) { MethodsToGen overload = typeParseInfo.BindingOverload; IInvocationOperation invocationOperation = typeParseInfo.BinderInvocation!.Operation; Debug.Assert((MethodsToGen.ConfigBinder_Any & overload) is not 0); if ((MethodsToGen.ConfigBinder_Bind & overload) is not 0) { if (typeSpec is ComplexTypeSpec complexTypeSpec && _helperInfoBuilder!.TryRegisterTransitiveTypesForMethodGen(complexTypeSpec.TypeRef)) { _interceptorInfoBuilder.RegisterInterceptor_ConfigBinder_Bind(overload, complexTypeSpec, invocationOperation); } } else { Debug.Assert((MethodsToGen.ConfigBinder_Get & overload) is not 0 || (MethodsToGen.ConfigBinder_GetValue & overload) is not 0); bool registered = (MethodsToGen.ConfigBinder_Get & overload) is not 0 ? _helperInfoBuilder!.TryRegisterTypeForGetGen(typeSpec) : _helperInfoBuilder!.TryRegisterTypeForGetValueGen(typeSpec); if (registered) { _interceptorInfoBuilder.RegisterInterceptor(overload, invocationOperation); } } } } } }