Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,10 @@ private void EmitInitializeMethod(ObjectSpec type)
EmitStartBlock(returnExpression);
foreach (PropertySpec property in initOnlyProps)
{
string propertyName = property.Name;
_writer.WriteLine($@"{propertyName} = {propertyName},");
// Properties bound through a matching constructor parameter don't have a local of their
// own; their bound value lives in the local named after the parameter.
string valueExpr = property.MatchingCtorParam?.Name ?? property.Name;
_writer.WriteLine($@"{property.Name} = {valueExpr},");
}
EmitEndBlock(endBraceTrailingSource: ";");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,27 @@ public string Color
}
}

public class ClassWithMatchingParametersAndProperties_DifferentlyCasedCtorParam
{
private readonly string _color;

public ClassWithMatchingParametersAndProperties_DifferentlyCasedCtorParam(string color, int length)
{
_color = color;
this.ColorFromCtor = color;
this.Length = length;
}

public int Length { get; set; }

public string ColorFromCtor { get; }
public string Color
{
get => _color;
init => _color = "the color is " + value;
}
}

public sealed class TreeElement : Dictionary<string, TreeElement>;

public record TypeWithRecursionThroughCollections
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,24 @@ public void CanBindOnParametersAndProperties_PropertiesAreSetAfterTheConstructor
Assert.Equal("the color is Green", options.Color);
}

[Fact]
public void CanBindOnParametersAndProperties_DifferentlyCasedConstructorParameter()
{
var dic = new Dictionary<string, string>
{
{"Length", "42"},
{"Color", "Green"},
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();

var options = config.Get<ClassWithMatchingParametersAndProperties_DifferentlyCasedCtorParam>();
Assert.Equal(42, options.Length);
Assert.Equal("Green", options.ColorFromCtor);
Assert.Equal("the color is Green", options.Color);
}

/// <summary>
/// This test to ensure the binding of the constructor/property array is done once and not duplicating values in the array.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,15 @@ private static HashSet<Assembly> GetFilteredAssemblyRefs(IEnumerable<Type> exclu
return assemblies;
}

public static byte[] CreateAssemblyImage(Compilation compilation)
private static void AssertCanCreateAssemblyImage(Compilation compilation)
{
MemoryStream ms = new MemoryStream();
var emitResult = compilation.Emit(ms);
var emitResult = compilation.Emit(Stream.Null);
if (!emitResult.Success)
{
// Explicit failures to include in the test output.
string errorMessage = string.Join(Environment.NewLine, emitResult.Diagnostics.Select(d => d.ToString()));
throw new InvalidOperationException(errorMessage);
}
return ms.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,63 @@ public class Settings
Assert.NotNull(result.GeneratedSource);
Assert.Empty(result.Diagnostics);

// Ensure the generated code can be compiled.
// If there is any compilation error, exception will be thrown with the list of the errors in the exception message.
byte[] emittedAssemblyImage = CreateAssemblyImage(result.OutputCompilation);
Assert.NotNull(emittedAssemblyImage);
AssertCanCreateAssemblyImage(result.OutputCompilation);
}

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))]
// Required, settable property whose value flows through a constructor marked [SetsRequiredMembers]
// whose parameter name differs only in casing from the property.
[InlineData("""
[method: SetsRequiredMembers]
public class GreetSettings(string name)
{
public string Greeting { get; set; } = "Hello";
public required string Name { get; set; } = name;
}
""")]
// Same as above, but the constructor parameter name matches the property name exactly.
[InlineData("""
[method: SetsRequiredMembers]
public class GreetSettings(string Name)
{
public string Greeting { get; set; } = "Hello";
public required string Name { get; set; } = Name;
}
""")]
// Required property set via a constructor parameter, but the constructor does not set required
// members, so the property must still be assigned through the object initializer.
[InlineData("""
public class GreetSettings(string name)
{
public string Greeting { get; set; } = "Hello";
public required string Name { get; set; } = name;
}
""")]
public async Task RequiredPropertyWithMatchingConstructorParameter(string greetSettingsType)
{
string source = $$"""
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;

public class Program
{
public static void Main()
{
ConfigurationBuilder configurationBuilder = new();
IConfiguration config = configurationBuilder.Build();

GreetSettings settings = config.GetSection("Settings").Get<GreetSettings>()!;
}
}

{{greetSettingsType}}
""";

ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(source, assemblyReferences: GetAssemblyRefsWithAdditional(typeof(ConfigurationBuilder)));
Assert.NotNull(result.GeneratedSource);
Assert.Empty(result.Diagnostics);

AssertCanCreateAssemblyImage(result.OutputCompilation);
}

[Fact]
Expand Down Expand Up @@ -322,10 +375,7 @@ public static void Main()
Assert.NotNull(result.GeneratedSource);
Assert.Empty(result.Diagnostics);

// Ensure the generated code can be compiled.
// If there is any compilation error, exception will be thrown with the list of the errors in the exception message.
byte[] emittedAssemblyImage = CreateAssemblyImage(result.OutputCompilation);
Assert.NotNull(emittedAssemblyImage);
AssertCanCreateAssemblyImage(result.OutputCompilation);
}

/// <summary>
Expand Down
Loading