Skip to content

Commit 58b4f0b

Browse files
authored
Fix #496 - Cryptic error message with immutable option class (#555)
1 parent 2063ffc commit 58b4f0b

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

src/CommandLine/Core/InstanceBuilder.cs

+18-2
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,9 @@ private static T BuildImmutable<T>(Type typeInfo, Maybe<Func<T>> factory, IEnume
169169
{
170170
throw new InvalidOperationException($"Type {typeInfo.FullName} appears to be immutable, but no constructor found to accept values.");
171171
}
172-
173-
var values =
172+
try
173+
{
174+
var values =
174175
(from prms in ctor.GetParameters()
175176
join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower() into spv
176177
from sp in spv.DefaultIfEmpty()
@@ -185,6 +186,21 @@ from sp in spv.DefaultIfEmpty()
185186
var immutable = (T)ctor.Invoke(values);
186187

187188
return immutable;
189+
}
190+
catch (Exception)
191+
{
192+
var ctorArgs = specPropsWithValue
193+
.Select(x => x.Property.Name.ToLowerInvariant()).ToArray();
194+
throw GetException(ctorArgs);
195+
}
196+
Exception GetException(string[] s)
197+
{
198+
var ctorSyntax = s != null ? " Constructor Parameters can be ordered as: " + $"'({string.Join(", ", s)})'" : string.Empty;
199+
var msg =
200+
$"Type {typeInfo.FullName} appears to be Immutable with invalid constructor. Check that constructor arguments have the same name and order of their underlying Type. {ctorSyntax}";
201+
InvalidOperationException invalidOperationException = new InvalidOperationException(msg);
202+
return invalidOperationException;
203+
}
188204
}
189205

190206
}

tests/CommandLine.Tests/Fakes/Immutable_Simple_Options.cs

+28
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,32 @@ public Immutable_Simple_Options(string stringValue, IEnumerable<int> intSequence
3131
[Value(0)]
3232
public long LongValue { get { return longValue; } }
3333
}
34+
35+
public class Immutable_Simple_Options_Invalid_Ctor_Args
36+
{
37+
private readonly string stringValue;
38+
private readonly IEnumerable<int> intSequence;
39+
private readonly bool boolValue;
40+
private readonly long longValue;
41+
42+
public Immutable_Simple_Options_Invalid_Ctor_Args(string stringValue1, IEnumerable<int> intSequence2, bool boolValue, long longValue)
43+
{
44+
this.stringValue = stringValue;
45+
this.intSequence = intSequence;
46+
this.boolValue = boolValue;
47+
this.longValue = longValue;
48+
}
49+
50+
[Option(HelpText = "Define a string value here.")]
51+
public string StringValue { get { return stringValue; } }
52+
53+
[Option('i', Min = 3, Max = 4, HelpText = "Define a int sequence here.")]
54+
public IEnumerable<int> IntSequence { get { return intSequence; } }
55+
56+
[Option('x', HelpText = "Define a boolean or switch value here.")]
57+
public bool BoolValue { get { return boolValue; } }
58+
59+
[Value(0)]
60+
public long LongValue { get { return longValue; } }
61+
}
3462
}

tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,22 @@ public void Parse_to_immutable_instance(string[] arguments, Immutable_Simple_Opt
901901
expected.Should().BeEquivalentTo(((Parsed<Immutable_Simple_Options>)result).Value);
902902
}
903903

904+
[Theory]
905+
[MemberData(nameof(ImmutableInstanceData))]
906+
public void Parse_to_immutable_instance_with_Invalid_Ctor_Args(string[] arguments, Immutable_Simple_Options _)
907+
{
908+
// Fixture setup in attributes
909+
910+
// Exercize system
911+
Action act = () => InvokeBuildImmutable<Immutable_Simple_Options_Invalid_Ctor_Args>(
912+
arguments);
913+
914+
// Verify outcome
915+
var expectedMsg =
916+
"Type CommandLine.Tests.Fakes.Immutable_Simple_Options_Invalid_Ctor_Args appears to be Immutable with invalid constructor. Check that constructor arguments have the same name and order of their underlying Type. Constructor Parameters can be ordered as: '(stringvalue, intsequence, boolvalue, longvalue)'";
917+
act.Should().Throw<InvalidOperationException>().WithMessage(expectedMsg);
918+
}
919+
904920
[Fact]
905921
public void Parse_to_type_with_single_string_ctor_builds_up_correct_instance()
906922
{

0 commit comments

Comments
 (0)