Skip to content

Commit 2377f59

Browse files
committed
Issue #283: report exceptions in property.SetValue as parsing errors
ToDo: add tests and nameinfo
1 parent 67f77e1 commit 2377f59

File tree

5 files changed

+74
-57
lines changed

5 files changed

+74
-57
lines changed

src/CommandLine/CommandLine.csproj

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,16 @@
169169
</Reference>
170170
</ItemGroup>
171171
</When>
172-
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3' Or $(TargetFrameworkVersion) == 'v4.6' Or $(TargetFrameworkVersion) == 'v4.6.1' Or $(TargetFrameworkVersion) == 'v4.6.2' Or $(TargetFrameworkVersion) == 'v4.6.3' Or $(TargetFrameworkVersion) == 'v4.7' Or $(TargetFrameworkVersion) == 'v4.7.1')" />
173-
<When Condition="($(TargetFrameworkIdentifier) == '.NETCore') Or ($(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v2.2' Or $(TargetFrameworkVersion) == 'v2.3' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.1' Or $(TargetFrameworkVersion) == 'v4.2' Or $(TargetFrameworkVersion) == 'v4.3' Or $(TargetFrameworkVersion) == 'v4.4' Or $(TargetFrameworkVersion) == 'v5.0' Or $(TargetFrameworkVersion) == 'v5.1' Or $(TargetFrameworkVersion) == 'v6.0' Or $(TargetFrameworkVersion) == 'v7.0' Or $(TargetFrameworkVersion) == 'v7.1' Or $(TargetFrameworkVersion) == 'v8.0')) Or ($(TargetFrameworkIdentifier) == 'MonoTouch') Or ($(TargetFrameworkIdentifier) == '.NETCoreApp' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkIdentifier) == '.NETStandard' And ($(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v1.2' Or $(TargetFrameworkVersion) == 'v1.3' Or $(TargetFrameworkVersion) == 'v1.4' Or $(TargetFrameworkVersion) == 'v1.5' Or $(TargetFrameworkVersion) == 'v1.6' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkProfile) == 'Profile7') Or ($(TargetFrameworkProfile) == 'Profile44') Or ($(TargetFrameworkIdentifier) == 'Xamarin.iOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.Mac') Or ($(TargetFrameworkIdentifier) == 'Xamarin.tvOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.watchOS')">
172+
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3' Or $(TargetFrameworkVersion) == 'v4.6' Or $(TargetFrameworkVersion) == 'v4.6.1' Or $(TargetFrameworkVersion) == 'v4.6.2' Or $(TargetFrameworkVersion) == 'v4.6.3' Or $(TargetFrameworkVersion) == 'v4.7' Or $(TargetFrameworkVersion) == 'v4.7.1')">
173+
<ItemGroup>
174+
<Reference Include="FSharp.Core">
175+
<HintPath>..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll</HintPath>
176+
<Private>True</Private>
177+
<Paket>True</Paket>
178+
</Reference>
179+
</ItemGroup>
180+
</When>
181+
<When Condition="($(TargetFrameworkIdentifier) == '.NETCore') Or ($(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v2.2' Or $(TargetFrameworkVersion) == 'v2.3' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.1' Or $(TargetFrameworkVersion) == 'v4.2' Or $(TargetFrameworkVersion) == 'v4.3' Or $(TargetFrameworkVersion) == 'v4.4' Or $(TargetFrameworkVersion) == 'v5.0' Or $(TargetFrameworkVersion) == 'v5.1' Or $(TargetFrameworkVersion) == 'v6.0' Or $(TargetFrameworkVersion) == 'v7.0' Or $(TargetFrameworkVersion) == 'v7.1' Or $(TargetFrameworkVersion) == 'v8.0' Or $(TargetFrameworkVersion) == 'v8.1')) Or ($(TargetFrameworkIdentifier) == 'MonoTouch') Or ($(TargetFrameworkIdentifier) == '.NETCoreApp' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v2.0' Or $(TargetFrameworkVersion) == 'v2.1')) Or ($(TargetFrameworkIdentifier) == '.NETStandard' And ($(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v1.2' Or $(TargetFrameworkVersion) == 'v1.3' Or $(TargetFrameworkVersion) == 'v1.4' Or $(TargetFrameworkVersion) == 'v1.5' Or $(TargetFrameworkVersion) == 'v1.6' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkProfile) == 'Profile7') Or ($(TargetFrameworkProfile) == 'Profile44') Or ($(TargetFrameworkIdentifier) == 'Xamarin.iOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.Mac') Or ($(TargetFrameworkIdentifier) == 'Xamarin.tvOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.watchOS')">
174182
<ItemGroup>
175183
<Reference Include="FSharp.Core">
176184
<HintPath>..\..\packages\FSharp.Core\lib\portable-net45+netcore45\FSharp.Core.dll</HintPath>

src/CommandLine/Core/InstanceBuilder.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,23 @@ public static ParserResult<T> Build<T>(
8080
var specPropsWithValue =
8181
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith());
8282

83-
Func<T> buildMutable = () =>
83+
var setPropertyErrors = new List<Error>();
84+
85+
Func <T> buildMutable = () =>
8486
{
8587
var mutable = factory.MapValueOrDefault(f => f(), Activator.CreateInstance<T>());
86-
mutable =
87-
mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail())
88-
.SetProperties(
89-
specPropsWithValue,
90-
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
91-
sp => sp.Specification.DefaultValue.FromJustOrFail())
92-
.SetProperties(
93-
specPropsWithValue,
94-
sp =>
95-
sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence
96-
&& sp.Specification.DefaultValue.MatchNothing(),
97-
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray());
98-
return mutable;
88+
setPropertyErrors.AddRange(mutable.SetProperties(specPropsWithValue, sp => sp.Value.IsJust(), sp => sp.Value.FromJustOrFail()));
89+
setPropertyErrors.AddRange(mutable.SetProperties(
90+
specPropsWithValue,
91+
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
92+
sp => sp.Specification.DefaultValue.FromJustOrFail()));
93+
setPropertyErrors.AddRange(mutable.SetProperties(
94+
specPropsWithValue,
95+
sp =>
96+
sp.Value.IsNothing() && sp.Specification.TargetType == TargetType.Sequence
97+
&& sp.Specification.DefaultValue.MatchNothing(),
98+
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray()));
99+
return mutable;
99100
};
100101

101102
Func<T> buildImmutable = () =>
@@ -121,6 +122,7 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
121122
.Concat(optionSpecPropsResult.SuccessfulMessages())
122123
.Concat(valueSpecPropsResult.SuccessfulMessages())
123124
.Concat(validationErrors)
125+
.Concat(setPropertyErrors)
124126
.Memorize();
125127

126128
var warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e;

src/CommandLine/Core/ReflectionExtensions.cs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -80,51 +80,30 @@ public static TargetType ToTargetType(this Type type)
8080
: TargetType.Scalar;
8181
}
8282

83-
public static T SetProperties<T>(
83+
public static IEnumerable<Error> SetProperties<T>(
8484
this T instance,
8585
IEnumerable<SpecificationProperty> specProps,
8686
Func<SpecificationProperty, bool> predicate,
8787
Func<SpecificationProperty, object> selector)
8888
{
89-
return specProps.Where(predicate).Aggregate(
90-
instance,
91-
(current, specProp) =>
92-
{
93-
specProp.Property.SetValue(current, selector(specProp));
94-
return instance;
95-
});
96-
}
97-
98-
private static T SetValue<T>(this PropertyInfo property, T instance, object value)
99-
{
100-
Action<Exception> fail = inner => {
101-
throw new InvalidOperationException("Cannot set value to target instance.", inner);
102-
};
103-
104-
try
105-
{
106-
property.SetValue(instance, value, null);
107-
}
108-
#if !PLATFORM_DOTNET
109-
catch (TargetException e)
110-
{
111-
fail(e);
112-
}
113-
#endif
114-
catch (TargetParameterCountException e)
115-
{
116-
fail(e);
117-
}
118-
catch (MethodAccessException e)
119-
{
120-
fail(e);
121-
}
122-
catch (TargetInvocationException e)
123-
{
124-
fail(e);
125-
}
126-
127-
return instance;
89+
return specProps.Where(predicate).SelectMany(specProp => specProp.Property.SetValue(instance, selector(specProp)));
90+
}
91+
92+
private static IEnumerable<Error> SetValue<T>(this PropertyInfo property, T instance, object value)
93+
{
94+
try
95+
{
96+
property.SetValue(instance, value, null);
97+
return Enumerable.Empty<Error>();
98+
}
99+
catch (TargetInvocationException e)
100+
{
101+
return new[] { new SetValueExceptionError(e.InnerException) };
102+
}
103+
catch (Exception e)
104+
{
105+
return new[] { new SetValueExceptionError(e) };
106+
}
128107
}
129108

130109
public static object CreateEmptyArray(this Type type)

src/CommandLine/Error.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ public enum ErrorType
6060
/// <summary>
6161
/// Value of <see cref="CommandLine.VersionRequestedError"/> type.
6262
/// </summary>
63-
VersionRequestedError
63+
VersionRequestedError,
64+
/// <summary>
65+
/// Value of <see cref="CommandLine.SetValueExceptionError"/> type.
66+
/// </summary>
67+
SetValueExceptionError
6468
}
6569

6670
/// <summary>
@@ -471,4 +475,26 @@ internal VersionRequestedError()
471475
{
472476
}
473477
}
478+
479+
/// <summary>
480+
/// Models as error generated when exception is thrown at Property.SetValue
481+
/// </summary>
482+
public sealed class SetValueExceptionError : Error
483+
{
484+
private readonly Exception exception;
485+
486+
internal SetValueExceptionError(Exception exception)
487+
: base(ErrorType.SetValueExceptionError)
488+
{
489+
this.exception = exception;
490+
}
491+
492+
/// <summary>
493+
/// The expection thrown from Property.SetValue
494+
/// </summary>
495+
public Exception Exception
496+
{
497+
get { return exception; }
498+
}
499+
}
474500
}

src/CommandLine/Text/SentenceBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ public override Func<Error, string> FormatError
137137
case ErrorType.RepeatedOptionError:
138138
return "Option '".JoinTo(((RepeatedOptionError)error).NameInfo.NameText,
139139
"' is defined multiple times.");
140+
case ErrorType.SetValueExceptionError:
141+
return "Error setting option value: ".JoinTo(((SetValueExceptionError)error).Exception.Message);
140142
}
141143
throw new InvalidOperationException();
142144
};

0 commit comments

Comments
 (0)