Skip to content

Commit 10432f1

Browse files
fix: Correctly handle dash count for built-in arguments
1 parent 53ef250 commit 10432f1

File tree

6 files changed

+130
-17
lines changed

6 files changed

+130
-17
lines changed

Diff for: src/CommandLine/Core/InstanceBuilder.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public static ParserResult<T> Build<T>(
2222
CultureInfo parsingCulture,
2323
bool autoHelp,
2424
bool autoVersion,
25+
OptionsParseMode optionsParseMode,
2526
IEnumerable<ErrorType> nonFatalErrors)
2627
{
2728
return Build(
@@ -34,6 +35,7 @@ public static ParserResult<T> Build<T>(
3435
autoHelp,
3536
autoVersion,
3637
false,
38+
optionsParseMode,
3739
nonFatalErrors);
3840
}
3941

@@ -47,6 +49,7 @@ public static ParserResult<T> Build<T>(
4749
bool autoHelp,
4850
bool autoVersion,
4951
bool allowMultiInstance,
52+
OptionsParseMode optionsParseMode,
5053
IEnumerable<ErrorType> nonFatalErrors) {
5154
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));
5255

@@ -137,7 +140,7 @@ public static ParserResult<T> Build<T>(
137140

138141
var preprocessorErrors = (
139142
argumentsList.Any()
140-
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion))
143+
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion, optionsParseMode))
141144
: Enumerable.Empty<Error>()
142145
).Memoize();
143146

Diff for: src/CommandLine/Core/InstanceChooser.cs

+14-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static ParserResult<object> Choose(
2121
CultureInfo parsingCulture,
2222
bool autoHelp,
2323
bool autoVersion,
24+
OptionsParseMode optionsParseMode,
2425
IEnumerable<ErrorType> nonFatalErrors)
2526
{
2627
return Choose(
@@ -33,6 +34,7 @@ public static ParserResult<object> Choose(
3334
autoHelp,
3435
autoVersion,
3536
false,
37+
optionsParseMode,
3638
nonFatalErrors);
3739
}
3840

@@ -46,6 +48,7 @@ public static ParserResult<object> Choose(
4648
bool autoHelp,
4749
bool autoVersion,
4850
bool allowMultiInstance,
51+
OptionsParseMode optionsParseMode,
4952
IEnumerable<ErrorType> nonFatalErrors)
5053
{
5154
var verbs = Verb.SelectFromTypes(types);
@@ -62,22 +65,23 @@ ParserResult<object> choose()
6265
var firstArg = arguments.First();
6366

6467
bool preprocCompare(string command) =>
65-
nameComparer.Equals(command, firstArg) ||
66-
nameComparer.Equals(string.Concat("--", command), firstArg);
68+
nameComparer.Equals(command, firstArg)
69+
|| optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals(string.Concat("--", command), firstArg)
70+
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals(string.Concat("-", command), firstArg);
6771

6872
return (autoHelp && preprocCompare("help"))
6973
? MakeNotParsed(types,
7074
MakeHelpVerbRequestedError(verbs,
7175
arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer))
7276
: (autoVersion && preprocCompare("version"))
7377
? MakeNotParsed(types, new VersionRequestedError())
74-
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, nonFatalErrors);
78+
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, optionsParseMode, nonFatalErrors);
7579
}
7680

7781
return arguments.Any()
7882
? choose()
7983
: (defaultVerbCount == 1
80-
? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors)
84+
? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors)
8185
: MakeNotParsed(types, new NoVerbSelectedError()));
8286
}
8387

@@ -91,6 +95,7 @@ private static ParserResult<object> MatchDefaultVerb(
9195
CultureInfo parsingCulture,
9296
bool autoHelp,
9397
bool autoVersion,
98+
OptionsParseMode optionsParseMode,
9499
IEnumerable<ErrorType> nonFatalErrors)
95100
{
96101
return !(defaultVerb is null)
@@ -103,6 +108,7 @@ private static ParserResult<object> MatchDefaultVerb(
103108
parsingCulture,
104109
autoHelp,
105110
autoVersion,
111+
optionsParseMode,
106112
nonFatalErrors)
107113
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));
108114
}
@@ -118,6 +124,7 @@ private static ParserResult<object> MatchVerb(
118124
bool autoHelp,
119125
bool autoVersion,
120126
bool allowMultiInstance,
127+
OptionsParseMode optionsParseMode,
121128
IEnumerable<ErrorType> nonFatalErrors)
122129
{
123130
string firstArg = arguments.First();
@@ -129,7 +136,7 @@ private static ParserResult<object> MatchVerb(
129136

130137
if (verbUsed == default)
131138
{
132-
return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors);
139+
return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors);
133140
}
134141
return InstanceBuilder.Build(
135142
Maybe.Just<Func<object>>(
@@ -141,7 +148,8 @@ private static ParserResult<object> MatchVerb(
141148
parsingCulture,
142149
autoHelp,
143150
autoVersion,
144-
allowMultiInstance,
151+
allowMultiInstance,
152+
optionsParseMode,
145153
nonFatalErrors);
146154
}
147155

Diff for: src/CommandLine/Core/PreprocessorGuards.cs

+9-8
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,31 @@ namespace CommandLine.Core
99
static class PreprocessorGuards
1010
{
1111
public static IEnumerable<Func<IEnumerable<string>, IEnumerable<Error>>>
12-
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion)
12+
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion, OptionsParseMode optionsParseMode)
1313
{
1414
var list = new List<Func<IEnumerable<string>, IEnumerable<Error>>>();
1515
if (autoHelp)
16-
list.Add(HelpCommand(nameComparer));
16+
list.Add(HelpCommand(nameComparer, optionsParseMode));
1717
if (autoVersion)
18-
list.Add(VersionCommand(nameComparer));
18+
list.Add(VersionCommand(nameComparer, optionsParseMode));
1919
return list;
2020
}
21-
22-
public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer)
21+
public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode)
2322
{
2423
return
2524
arguments =>
26-
nameComparer.Equals("--help", arguments.First())
25+
optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--help", arguments.First())
26+
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-help", arguments.First())
2727
? new Error[] { new HelpRequestedError() }
2828
: Enumerable.Empty<Error>();
2929
}
3030

31-
public static Func<IEnumerable<string>, IEnumerable<Error>> VersionCommand(StringComparer nameComparer)
31+
public static Func<IEnumerable<string>, IEnumerable<Error>> VersionCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode)
3232
{
3333
return
3434
arguments =>
35-
nameComparer.Equals("--version", arguments.First())
35+
optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--version", arguments.First())
36+
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-version", arguments.First())
3637
? new Error[] { new VersionRequestedError() }
3738
: Enumerable.Empty<Error>();
3839
}

Diff for: src/CommandLine/Parser.cs

+3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
102102
settings.AutoHelp,
103103
settings.AutoVersion,
104104
settings.AllowMultiInstance,
105+
settings.OptionsParseMode,
105106
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
106107
settings);
107108
}
@@ -133,6 +134,7 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
133134
settings.AutoHelp,
134135
settings.AutoVersion,
135136
settings.AllowMultiInstance,
137+
settings.OptionsParseMode,
136138
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
137139
settings);
138140
}
@@ -166,6 +168,7 @@ public ParserResult<object> ParseArguments(IEnumerable<string> args, params Type
166168
settings.AutoHelp,
167169
settings.AutoVersion,
168170
settings.AllowMultiInstance,
171+
settings.OptionsParseMode,
169172
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
170173
settings);
171174
}

Diff for: tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs

+42-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace CommandLine.Tests.Unit.Core
1919
{
2020
public class InstanceBuilderTests
2121
{
22-
private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false)
22+
private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false, OptionsParseMode optionsParseMode = OptionsParseMode.Default)
2323
where T : new()
2424
{
2525
return InstanceBuilder.Build(
@@ -32,6 +32,7 @@ private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp
3232
autoHelp,
3333
autoVersion,
3434
multiInstance,
35+
optionsParseMode,
3536
Enumerable.Empty<ErrorType>());
3637
}
3738

@@ -47,6 +48,7 @@ private static ParserResult<T> InvokeBuildEnumValuesCaseIgnore<T>(string[] argum
4748
CultureInfo.InvariantCulture,
4849
true,
4950
true,
51+
OptionsParseMode.Default,
5052
Enumerable.Empty<ErrorType>());
5153
}
5254

@@ -61,6 +63,7 @@ private static ParserResult<T> InvokeBuildImmutable<T>(string[] arguments)
6163
CultureInfo.InvariantCulture,
6264
true,
6365
true,
66+
OptionsParseMode.Default,
6467
Enumerable.Empty<ErrorType>());
6568
}
6669

@@ -421,6 +424,7 @@ public void Double_dash_force_subsequent_arguments_as_values()
421424
CultureInfo.InvariantCulture,
422425
true,
423426
true,
427+
OptionsParseMode.Default,
424428
Enumerable.Empty<ErrorType>());
425429

426430
// Verify outcome
@@ -1263,6 +1267,43 @@ public void Parse_int_sequence_with_multi_instance()
12631267

12641268
((Parsed<Options_With_Sequence>)result).Value.IntSequence.Should().BeEquivalentTo(expected);
12651269
}
1270+
1271+
[Theory]
1272+
[InlineData("-help", OptionsParseMode.SingleDashOnly)]
1273+
[InlineData("-help", OptionsParseMode.SingleOrDoubleDash)]
1274+
[InlineData("--help", OptionsParseMode.Default)]
1275+
[InlineData("--help", OptionsParseMode.SingleOrDoubleDash)]
1276+
public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode)
1277+
{
1278+
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);
1279+
1280+
result.Errors.Single().Should().BeOfType<HelpRequestedError>();
1281+
}
1282+
1283+
[Theory]
1284+
[InlineData("-version", OptionsParseMode.SingleDashOnly)]
1285+
[InlineData("-version", OptionsParseMode.SingleOrDoubleDash)]
1286+
[InlineData("--version", OptionsParseMode.Default)]
1287+
[InlineData("--version", OptionsParseMode.SingleOrDoubleDash)]
1288+
public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode)
1289+
{
1290+
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);
1291+
1292+
result.Errors.Single().Should().BeOfType<VersionRequestedError>();
1293+
}
1294+
1295+
[Theory]
1296+
[InlineData("-help", OptionsParseMode.Default)]
1297+
[InlineData("--help", OptionsParseMode.SingleDashOnly)]
1298+
[InlineData("-version", OptionsParseMode.Default)]
1299+
[InlineData("--version", OptionsParseMode.SingleDashOnly)]
1300+
public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode)
1301+
{
1302+
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);
1303+
1304+
result.Errors.Should().NotBeOfType<HelpVerbRequestedError>()
1305+
.And.Should().NotBeOfType<VersionRequestedError>();
1306+
}
12661307

12671308
#region custom types
12681309

Diff for: tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs

+58-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public class InstanceChooserTests
1616
private static ParserResult<object> InvokeChoose(
1717
IEnumerable<Type> types,
1818
IEnumerable<string> arguments,
19-
bool multiInstance = false)
19+
bool multiInstance = false,
20+
OptionsParseMode optionsParseMode = OptionsParseMode.Default)
2021
{
2122
return InstanceChooser.Choose(
2223
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
@@ -28,6 +29,7 @@ private static ParserResult<object> InvokeChoose(
2829
true,
2930
true,
3031
multiInstance,
32+
optionsParseMode,
3133
Enumerable.Empty<ErrorType>());
3234
}
3335

@@ -183,5 +185,60 @@ public void Parse_sequence_verb_with_multi_instance_returns_verb_instance()
183185
Assert.IsType<SequenceOptions>(((Parsed<object>)result).Value);
184186
expected.Should().BeEquivalentTo(((Parsed<object>)result).Value);
185187
}
188+
189+
[Theory]
190+
[InlineData("help", OptionsParseMode.Default)]
191+
[InlineData("help", OptionsParseMode.SingleDashOnly)]
192+
[InlineData("help", OptionsParseMode.SingleOrDoubleDash)]
193+
[InlineData("-help", OptionsParseMode.SingleDashOnly)]
194+
[InlineData("-help", OptionsParseMode.SingleOrDoubleDash)]
195+
[InlineData("--help", OptionsParseMode.Default)]
196+
[InlineData("--help", OptionsParseMode.SingleOrDoubleDash)]
197+
public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode)
198+
{
199+
var result = InvokeChoose(
200+
Type.EmptyTypes,
201+
new[] { argument },
202+
true,
203+
optionsParseMode);
204+
205+
result.Errors.Single().Should().BeOfType<HelpVerbRequestedError>();
206+
}
207+
208+
[Theory]
209+
[InlineData("version", OptionsParseMode.Default)]
210+
[InlineData("version", OptionsParseMode.SingleDashOnly)]
211+
[InlineData("version", OptionsParseMode.SingleOrDoubleDash)]
212+
[InlineData("-version", OptionsParseMode.SingleDashOnly)]
213+
[InlineData("-version", OptionsParseMode.SingleOrDoubleDash)]
214+
[InlineData("--version", OptionsParseMode.Default)]
215+
[InlineData("--version", OptionsParseMode.SingleOrDoubleDash)]
216+
public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode)
217+
{
218+
var result = InvokeChoose(
219+
Type.EmptyTypes,
220+
new[] { argument },
221+
true,
222+
optionsParseMode);
223+
224+
result.Errors.Single().Should().BeOfType<VersionRequestedError>();
225+
}
226+
227+
[Theory]
228+
[InlineData("-help", OptionsParseMode.Default)]
229+
[InlineData("--help", OptionsParseMode.SingleDashOnly)]
230+
[InlineData("-version", OptionsParseMode.Default)]
231+
[InlineData("--version", OptionsParseMode.SingleDashOnly)]
232+
public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode)
233+
{
234+
var result = InvokeChoose(
235+
Type.EmptyTypes,
236+
new[] { argument },
237+
true,
238+
optionsParseMode);
239+
240+
result.Errors.Single().Should().NotBeOfType<HelpVerbRequestedError>()
241+
.And.Should().NotBeOfType<VersionRequestedError>();
242+
}
186243
}
187244
}

0 commit comments

Comments
 (0)