Repository navigation
Config binder source-gen: .NET 7 app fails to compile with .NET 8 preview 4 package #86348
Description
Description
I was testing out the new config source generator. In .NET Preview 3, I can add the NuGet package to a .NET 7 app, and the app can build and use the source generated code as expected.
With the preview 4 version of the generator I get a compiler error:
error CS8030: Anonymous function converted to a void returning delegate cannot return a value
Reproduction Steps
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<TestOptions>(builder.Configuration);csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<!-- 👇 Note that this is required, otherwise it can't find Dictionary<> values-->
<!-- 👇 Should be fixed too 😉-->
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0-preview.4.*" />
</ItemGroup>
</Project>Building gives the following error:
CS8030: Anonymous function converted to a void returning delegate cannot return a value
The generated code looks like this (error location highlighted):
// <auto-generated/>
#nullable enable
internal static class GeneratedConfigurationBinder
{
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<T>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration)
{
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
if (typeof(T) == typeof(global::TestOptions))
{
return services.Configure<global::TestOptions>(obj =>
{
if (!global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.HasValueOrChildren(configuration))
{
// 👇 Anonymous function converted to a void returning delegate cannot return a value
return default;
}
global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj);
});
}
throw new global::System.NotSupportedException($"Unable to bind to type '{typeof(T)}': 'Generator parser did not detect the type as input'");
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using System;
using System.Globalization;
using Microsoft.Extensions.Configuration;
internal static class Helpers
{
public static void BindCore(IConfiguration configuration, ref TestOptions obj)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (configuration["String"] is string stringValue1)
{
obj.String = stringValue1;
}
}
public static bool HasValueOrChildren(IConfiguration configuration)
{
if ((configuration as IConfigurationSection)?.Value is not null)
{
return true;
}
return HasChildren(configuration);
}
public static bool HasChildren(IConfiguration configuration)
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
return true;
}
return false;
}
}
}Expected behavior
No build errors. With preview 3 there are no build errors, because it's not returning a value. This is the generated code using the preview 3 package:
// <auto-generated/>
#nullable enable
using System.Linq;
internal static class GeneratedConfigurationBinder
{
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<T>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration)
{
if (typeof(T) == typeof(TestOptions))
{
return services.Configure<TestOptions>(obj =>
{
BindCore(configuration, ref obj);
});
}
throw new global::System.NotSupportedException($"Unable to bind to type '{typeof(T)}': 'Generator parser did not detect the type as input'");
}
private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref TestOptions obj)
{
if (obj is null)
{
throw new global::System.ArgumentNullException(nameof(obj));
}
if (configuration["String"] is string stringValue1)
{
obj.String = stringValue1;
}
}
public static bool HasChildren(global::Microsoft.Extensions.Configuration.IConfiguration configuration)
{
foreach (global::Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
{
return true;
}
return false;
}
}
``
### Actual behaviorerror CS8030: Anonymous function converted to a void returning delegate cannot return a value
### Regression?
Yes, this worked in the .NET 8 preview 3 Microsoft.Extensions.Configuration.Binder package (tested with .NET 7)
### Known Workarounds
_No response_
### Configuration
.NET SDK:
Version: 7.0.202
Commit: 6c74320bc3
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19044
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\7.0.202\
Host:
Version: 7.0.5
Architecture: x64
Commit: 8042d61
Not tested on anything else yet
### Other information
_No response_
Activity
[-]Config binder source-gen: .7 app fails to compile with .NET 8 preview 4 package[/-][+]Config binder source-gen: .NET 7 app fails to compile with .NET 8 preview 4 package[/+]andrewlock commentedon May 16, 2023
FYI I've just tested by upgrading the app to .NET 8, using the .NET 8 preview 4, and I get the same error
tarekgh commentedon May 16, 2023
Thanks @andrewlock for the report. we'll look at that.
layomia commentedon May 17, 2023
Thanks for the report. The compilation issue with the
Configuremethod was tangentially fixed for preview 5 in #85843. The implementation will be bolstered and further tested as part of #83600.I'll take a look at the issue with implicit usings.
tarekgh commentedon May 17, 2023
@layomia I assume we have a test covering this case now. Right?
layomia commentedon May 17, 2023
Yes we have a regression test.
runtime/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/TestConfigureCallGen.generated.txt
Lines 6 to 27 in 8f2c610
Tornhoof commentedon May 17, 2023
In my experience with SrcGen tests, I highly recommend disabling Implicit usings in the test projects, you don't gain much and it produces errors which do not show up in the CI pipeline.
andrewlock commentedon Jun 13, 2023
FYI, I have just installed .NET preview 5, and the source generator still appears to be broken. For example:
I've highlighted error locations.
Program.cs
Generated Code:
layomia commentedon Jun 13, 2023
Thanks. I'm going to open a PR for #83600 shortly which will comprehensively fix these compilation issues.
layomia commentedon Jun 20, 2023
Fixed in #86348.