_ => throw new InvalidOperationException()
};
- IArgumentOperation instanceArg = operation.Arguments[instanceIndex];
+ IArgumentOperation instanceArg = GetArgumentForParameterAtIndex(operation.Arguments, instanceIndex);
if (instanceArg.Parameter.Type.SpecialType != SpecialType.System_Object)
{
return;
};
}
+ 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!;
}
else
{
- ITypeOfOperation? typeOfOperation = operation.Arguments[1].ChildOperations.FirstOrDefault() as ITypeOfOperation;
+ ITypeOfOperation? typeOfOperation = GetArgumentForParameterAtIndex(operation.Arguments, 1).ChildOperations.FirstOrDefault() as ITypeOfOperation;
type = typeOfOperation?.TypeOperand;
if (paramCount is 2)
return;
}
- ITypeOfOperation? typeOfOperation = operation.Arguments[1].ChildOperations.FirstOrDefault() as ITypeOfOperation;
+ ITypeOfOperation? typeOfOperation = GetArgumentForParameterAtIndex(operation.Arguments, 1).ChildOperations.FirstOrDefault() as ITypeOfOperation;
type = typeOfOperation?.TypeOperand;
if (paramCount is 3)
/// <summary>
/// These are regression tests for https://github.com/dotnet/runtime/issues/90909.
/// Ensure that we don't emit root interceptors to handle types/members that
- /// are inaccessible to the generated helpers. Tests for inaccessbile transitive members
+ /// are inaccessible to the generated helpers. Tests for inaccessible transitive members
/// are covered in the shared (reflection/src-gen) <see cref="ConfigurationBinderTests"/>,
/// e.g. <see cref="NonPublicModeGetStillIgnoresReadonly"/>.
/// </summary>
public async Task Configure_T_name_BinderOptions() =>
await VerifyAgainstBaselineUsingFile("Configure_T_name_BinderOptions.generated.txt", GetConfigureSource(@""""", section, _ => { }"), extType: ExtensionClassType.ServiceCollection);
+ [Theory]
+ [InlineData("OptionsConfigurationServiceCollectionExtensions.Configure<MyClass>(config: section, services: services);")]
+ [InlineData("""OptionsConfigurationServiceCollectionExtensions.Configure<MyClass>(name: "", config: section, services: services);""")]
+ [InlineData("OptionsConfigurationServiceCollectionExtensions.Configure<MyClass>(configureBinder: _ => { }, config: section, services: services);")]
+ [InlineData("""OptionsConfigurationServiceCollectionExtensions.Configure<MyClass>(configureBinder: _ => { }, config: section, name: "", services: services);""")]
+ [InlineData("""OptionsConfigurationServiceCollectionExtensions.Configure<MyClass>(name: "", services: services, configureBinder: _ => { }, config: section);""")]
+ public async Task Configure_T_NamedParameters_OutOfOrder(string row)
+ {
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.DependencyInjection;
+
+ public class Program
+ {
+ public static void Main()
+ {
+ ConfigurationBuilder configurationBuilder = new();
+ IConfiguration config = configurationBuilder.Build();
+ IConfigurationSection section = config.GetSection("MySection");
+ ServiceCollection services = new();
+
+ {{row}}
+ }
+
+ public class MyClass
+ {
+ public string MyString { get; set; }
+ public int MyInt { get; set; }
+ public List<int> MyList { get; set; }
+ public Dictionary<string, string> MyDictionary { get; set; }
+ }
+ }
+ """;
+
+ await VerifyThatSourceIsGenerated(source);
+ }
+
+ [Theory]
+ [InlineData("OptionsBuilderConfigurationExtensions.Bind(config: config, optionsBuilder: optionsBuilder);")]
+ [InlineData("OptionsBuilderConfigurationExtensions.Bind(configureBinder: _ => { }, config: config, optionsBuilder: optionsBuilder);")]
+ [InlineData("OptionsBuilderConfigurationExtensions.Bind(config: config, configureBinder: _ => { }, optionsBuilder: optionsBuilder);")]
+ public async Task Bind_T_NamedParameters_OutOfOrder(string row)
+ {
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Options;
+
+ public class Program
+ {
+ public static void Main()
+ {
+ ConfigurationBuilder configurationBuilder = new();
+ IConfiguration config = configurationBuilder.Build();
+ var services = new ServiceCollection();
+ OptionsBuilder<MyClass> optionsBuilder = new(services, "");
+
+ {{row}}
+ }
+
+ public class MyClass
+ {
+ public string MyString { get; set; }
+ public int MyInt { get; set; }
+ public List<int> MyList { get; set; }
+ public Dictionary<string, string> MyDictionary { get; set; }
+ }
+ }
+ """;
+
+ await VerifyThatSourceIsGenerated(source);
+ }
+
private string GetBindSource(string? configureActions = null) => $$"""
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
public async Task Bind() =>
await VerifyAgainstBaselineUsingFile("Bind.generated.txt", BindCallSampleCode, extType: ExtensionClassType.ConfigurationBinder);
+ [Theory]
+ [InlineData("ConfigurationBinder.Bind(instance: configObj, configuration: config);")]
+ [InlineData("""ConfigurationBinder.Bind(key: "", instance: configObj, configuration: config);""")]
+ [InlineData("""ConfigurationBinder.Bind(instance: configObj, key: "", configuration: config);""")]
+ [InlineData("ConfigurationBinder.Bind(configureOptions: _ => { }, configuration: config, instance: configObj);")]
+ [InlineData("ConfigurationBinder.Bind(configuration: config, configureOptions: _ => { }, instance: configObj);")]
+ public async Task Bind_NamedParameters_OutOfOrder(string row)
+ {
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Configuration;
+
+ public class Program
+ {
+ public static void Main()
+ {
+ ConfigurationBuilder configurationBuilder = new();
+ IConfigurationRoot config = configurationBuilder.Build();
+
+ MyClass configObj = new();
+ {{row}}
+ }
+
+ public class MyClass
+ {
+ public string MyString { get; set; }
+ public int MyInt { get; set; }
+ public List<int> MyList { get; set; }
+ public Dictionary<string, string> MyDictionary { get; set; }
+ }
+ }
+ """;
+
+ await VerifyThatSourceIsGenerated(source);
+ }
+
+ [Theory]
+ [InlineData("var obj = ConfigurationBinder.Get(type: typeof(MyClass), configuration: config);")]
+ [InlineData("var obj = ConfigurationBinder.Get<MyClass>(configureOptions: _ => { }, configuration: config);")]
+ [InlineData("var obj = ConfigurationBinder.Get(configureOptions: _ => { }, type: typeof(MyClass), configuration: config);")]
+ [InlineData("var obj = ConfigurationBinder.Get(type: typeof(MyClass), configureOptions: _ => { }, configuration: config);")]
+ public async Task Get_TypeOf_NamedParametersOutOfOrder(string row)
+ {
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Configuration;
+
+ public class Program
+ {
+ public static void Main()
+ {
+ ConfigurationBuilder configurationBuilder = new();
+ IConfigurationRoot config = configurationBuilder.Build();
+
+ MyClass configObj = new();
+ {{row}}
+ }
+
+ public class MyClass
+ {
+ public string MyString { get; set; }
+ public int MyInt { get; set; }
+ public List<int> MyList { get; set; }
+ public Dictionary<string, string> MyDictionary { get; set; }
+ }
+ }
+ """;
+
+ await VerifyThatSourceIsGenerated(source);
+ }
+
+ [Theory]
+ [InlineData("""var str = ConfigurationBinder.GetValue(key: "key", configuration: config, type: typeof(string));""")]
+ [InlineData("""var str = ConfigurationBinder.GetValue<string>(key: "key", configuration: config);""")]
+ [InlineData("""var str = ConfigurationBinder.GetValue<string>(key: "key", defaultValue: "default", configuration: config);""")]
+ [InlineData("""var str = ConfigurationBinder.GetValue<string>(configuration: config, key: "key", defaultValue: "default");""")]
+ [InlineData("""var str = ConfigurationBinder.GetValue(defaultValue: "default", key: "key", configuration: config, type: typeof(string));""")]
+ [InlineData("""var str = ConfigurationBinder.GetValue(defaultValue: "default", type: typeof(string), key: "key", configuration: config);""")]
+ public async Task GetValue_NamedParametersOutOfOrder(string row)
+ {
+ string source = $$"""
+ using System.Collections.Generic;
+ using Microsoft.Extensions.Configuration;
+
+ public class Program
+ {
+ public static void Main()
+ {
+ ConfigurationBuilder configurationBuilder = new();
+ IConfigurationRoot config = configurationBuilder.Build();
+ {{row}}
+ }
+
+ public class MyClass
+ {
+ public string MyString { get; set; }
+ public int MyInt { get; set; }
+ public List<int> MyList { get; set; }
+ public Dictionary<string, string> MyDictionary { get; set; }
+ }
+ }
+ """;
+
+ await VerifyThatSourceIsGenerated(source);
+ }
+
[Fact]
public async Task Bind_Instance()
{
ServiceCollection,
}
+ private static async Task VerifyThatSourceIsGenerated(string testSourceCode)
+ {
+ var (d, r) = await RunGenerator(testSourceCode);
+ Assert.Equal(1, r.Length);
+ Assert.Empty(d);
+ Assert.True(r[0].SourceText.Lines.Count > 10);
+ }
+
private static async Task VerifyAgainstBaselineUsingFile(
string filename,
string testSourceCode,
)
;
}
+
+ [Fact]
+ public void TestBindAndConfigureWithNamedParameters()
+ {
+ OptionsBuilder<FakeOptions>? optionsBuilder = CreateOptionsBuilder();
+ IServiceCollection services = new ServiceCollection();
+
+ OptionsBuilderConfigurationExtensions.Bind(config: s_emptyConfig, optionsBuilder: optionsBuilder);
+ OptionsBuilderConfigurationExtensions.Bind(configureBinder: _ => { }, config: s_emptyConfig, optionsBuilder: optionsBuilder);
+
+ OptionsBuilderConfigurationExtensions.BindConfiguration(configureBinder: _ => { }, configSectionPath: "path", optionsBuilder: optionsBuilder);
+
+ OptionsConfigurationServiceCollectionExtensions.Configure<FakeOptions>(config: s_emptyConfig, services: services);
+ OptionsConfigurationServiceCollectionExtensions.Configure<FakeOptions>(name: "", config: s_emptyConfig, services: services);
+ OptionsConfigurationServiceCollectionExtensions.Configure<FakeOptions>(configureBinder: _ => { }, config: s_emptyConfig, services: services);
+ OptionsConfigurationServiceCollectionExtensions.Configure<FakeOptions>(name: "", configureBinder: _ => { }, config: s_emptyConfig, services: services);
+ }
}
}