Skip to content

Commit d4cdec2

Browse files
authored
Fix 1137 (for real this time) (#1336)
1 parent 6800b73 commit d4cdec2

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

src/System.CommandLine.Hosting/InvocationLifetime.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.CommandLine.Invocation;
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.CommandLine.Invocation;
25
using System.Threading;
36
using System.Threading.Tasks;
47

src/System.CommandLine.Tests/Binding/ModelBinderTests.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.CommandLine.Binding;
66
using System.CommandLine.Builder;
77
using System.CommandLine.Invocation;
8-
using System.CommandLine.IO;
98
using System.CommandLine.Parsing;
109
using System.IO;
1110
using FluentAssertions;
@@ -794,5 +793,43 @@ public class ComplexType
794793
{
795794
public decimal OptDecimal { get; set; }
796795
}
796+
797+
#if NETCOREAPP2_1_OR_GREATER
798+
[Theory]
799+
[InlineData("--class-with-span-ctor a51ca309-84fa-452f-96be-51e47702ffb4 --int-value 1234")]
800+
[InlineData("--class-with-span-ctor a51ca309-84fa-452f-96be-51e47702ffb4")]
801+
[InlineData("--int-value 1234")]
802+
public void When_only_available_constructor_is_span_then_null_is_passed(string commandLine)
803+
{
804+
var root = new RootCommand
805+
{
806+
new Option<ClassWithSpanConstructor>("--class-with-span-ctor"),
807+
new Option<int>("--int-value"),
808+
};
809+
810+
var handlerWasCalled = false;
811+
812+
root.Handler = CommandHandler.Create<ClassWithSpanConstructor, int>((spanCtor, intValue) =>
813+
{
814+
handlerWasCalled = true;
815+
});
816+
817+
root.Invoke(commandLine);
818+
819+
handlerWasCalled.Should().BeTrue();
820+
}
821+
822+
public class ClassWithSpanConstructor
823+
{
824+
private Guid value;
825+
826+
public ClassWithSpanConstructor(ReadOnlySpan<byte> guid)
827+
{
828+
value = new Guid(guid);
829+
}
830+
831+
public override string ToString() => value.ToString();
832+
}
833+
#endif
797834
}
798835
}

src/System.CommandLine/Binding/ModelBinder.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void BindMemberFromValue(PropertyInfo property, IValueDescriptor valueDes
6767

6868
private (bool success, object? newInstance, bool anyNonDefaults) CreateInstanceInternal(BindingContext bindingContext)
6969
{
70-
if (DisallowedBindingType())
70+
if (IsModelTypeUnbindable())
7171
{
7272
throw new InvalidOperationException($"The type {ModelDescriptor.ModelType} cannot be bound");
7373
}
@@ -93,11 +93,11 @@ public void BindMemberFromValue(PropertyInfo property, IValueDescriptor valueDes
9393
}
9494
}
9595

96-
private bool DisallowedBindingType()
96+
private bool IsModelTypeUnbindable()
9797
{
9898
var modelType = ModelDescriptor.ModelType;
99-
return modelType.IsConstructedGenericTypeOf(typeof(Span<>)) ||
100-
modelType.IsConstructedGenericTypeOf(typeof(ReadOnlySpan<>));
99+
return modelType.IsConstructedGenericTypeOf(typeof(Span<>)) ||
100+
modelType.IsConstructedGenericTypeOf(typeof(ReadOnlySpan<>));
101101
}
102102

103103
private bool ShortCutTheBinding()
@@ -258,6 +258,8 @@ internal static (BoundValue? boundValue, bool usedNonDefault) GetBoundValue(
258258
bool includeMissingValues,
259259
Type? parentType)
260260
{
261+
262+
261263
if (bindingContext.TryBindToScalarValue(
262264
valueDescriptor,
263265
valueSource,
@@ -276,7 +278,14 @@ internal static (BoundValue? boundValue, bool usedNonDefault) GetBoundValue(
276278
if (valueDescriptor.ValueType != parentType) // Recursive models aren't allowed
277279
{
278280
var binder = bindingContext.GetModelBinder(valueDescriptor);
281+
282+
if (binder.IsModelTypeUnbindable())
283+
{
284+
return (null, false);
285+
}
286+
279287
var (success, newInstance, usedNonDefaults) = binder.CreateInstanceInternal(bindingContext);
288+
280289
if (success)
281290
{
282291
return (new BoundValue(newInstance, valueDescriptor, valueSource), usedNonDefaults);
@@ -287,14 +296,13 @@ internal static (BoundValue? boundValue, bool usedNonDefault) GetBoundValue(
287296
{
288297
return (new BoundValue(parameterDescriptor.GetDefaultValue(), valueDescriptor, valueSource), false);
289298
}
290-
// Logic dropped here - misnamed and purpose unclear: ShouldPassNullToConstructor(constructorDescriptor.Parent, constructorDescriptor))
299+
291300
return (BoundValue.DefaultForType(valueDescriptor), false);
292301
}
293302

294303
return (null, false);
295304
}
296305

297-
298306
protected ConstructorDescriptor FindModelConstructorDescriptor(ConstructorInfo constructorInfo)
299307
{
300308
var constructorParameters = constructorInfo.GetParameters();

0 commit comments

Comments
 (0)