Skip to content

Config binder source-gen: .NET 7 app fails to compile with .NET 8 preview 4 package #86348

Closed
@andrewlock

Description

@andrewlock

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 behavior

error 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_

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions