From 94d5994c11f8c1e2bd4105b01ffb8d922e409830 Mon Sep 17 00:00:00 2001 From: Jon Sequeira Date: Wed, 7 Jun 2023 15:25:40 -0700 Subject: [PATCH] fix #2210 --- .../Binding/TypeConversionTests.cs | 25 +++++++++++++++++++ src/System.CommandLine/ParseResult.cs | 7 +++--- .../Parsing/ParseOperation.cs | 3 ++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs index b06f2ce33a..59579a3915 100644 --- a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs +++ b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Threading; using Xunit; namespace System.CommandLine.Tests.Binding @@ -195,6 +196,30 @@ public void Bool_parses_as_true_when_the_option_has_been_applied(string commandL .BeTrue(); } + [Fact] // https://github.com/dotnet/command-line-api/issues/2210 + public void Nullable_bool_with_unparseable_argument_does_not_throw() + { + CliRootCommand rootCommand = new(); + CliOption option = new("--test"); + rootCommand.Options.Add(option); + var result = rootCommand.Parse("--test ouch"); + + result.Invoking(r => r.GetValue(option)) + .Should().NotThrow(); + } + + [Fact] // https://github.com/dotnet/command-line-api/issues/2210 + public void Bool_with_unparseable_argument_does_not_throw() + { + CliRootCommand rootCommand = new(); + CliOption option = new("--test"); + rootCommand.Options.Add(option); + var result = rootCommand.Parse("--test ouch"); + + result.Invoking(r => r.GetValue(option)) + .Should().NotThrow(); + } + [Theory] [InlineData("the-command -x")] [InlineData("the-command -x true")] diff --git a/src/System.CommandLine/ParseResult.cs b/src/System.CommandLine/ParseResult.cs index 4a1bd0fcc9..84d88bccaa 100644 --- a/src/System.CommandLine/ParseResult.cs +++ b/src/System.CommandLine/ParseResult.cs @@ -135,6 +135,7 @@ CommandLineText is null public T? GetValue(string name) { var command = CommandResult.Command; + if (_namedResults is null) { // A null value means that given name exists, but was not parsed @@ -180,12 +181,12 @@ void Populate(Dictionary cache, IList s } } - static T? Convert(ArgumentConversionResult validatedResult) + static T Convert(ArgumentConversionResult validatedResult) { var convertedResult = validatedResult.ConvertIfNeeded(typeof(T)); - if (validatedResult.Result == ArgumentConversionResultType.Successful - && convertedResult.Result == ArgumentConversionResultType.NoArgument) + if (validatedResult is { Result: ArgumentConversionResultType.Successful } + && convertedResult is { Result: ArgumentConversionResultType.NoArgument }) { // invalid cast has been detected, InvalidCastException will be thrown return (T)validatedResult.Value!; diff --git a/src/System.CommandLine/Parsing/ParseOperation.cs b/src/System.CommandLine/Parsing/ParseOperation.cs index 934d442f57..b4a4895fd6 100644 --- a/src/System.CommandLine/Parsing/ParseOperation.cs +++ b/src/System.CommandLine/Parsing/ParseOperation.cs @@ -246,9 +246,10 @@ private void ParseOptionArguments(OptionResult optionResult) break; } } - else if (argument.ValueType == typeof(bool) && + else if ((argument.ValueType == typeof(bool) || argument.ValueType == typeof(bool?)) && !bool.TryParse(CurrentToken.Value, out _)) { + // Don't greedily consume the following token for bool. The presence of the option token (i.e. a flag) is sufficient. break; }