Skip to content

Commit 2b7d982

Browse files
committed
fix #1589
1 parent 188a46e commit 2b7d982

File tree

8 files changed

+90
-20
lines changed

8 files changed

+90
-20
lines changed

src/System.CommandLine.Tests/CommandExtensionsTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.Collections.Generic;
45
using System.CommandLine.Builder;
56
using System.CommandLine.IO;
7+
using System.CommandLine.Parsing;
8+
using System.Threading.Tasks;
69
using FluentAssertions;
710
using Xunit;
11+
using System.CommandLine.Tests.Utility;
12+
using Xunit.Abstractions;
813

914
namespace System.CommandLine.Tests
1015
{
1116
public class CommandExtensionsTests
1217
{
18+
private ITestOutputHelper _output;
19+
20+
public CommandExtensionsTests(ITestOutputHelper output)
21+
{
22+
_output = output;
23+
}
24+
1325
[Fact]
1426
public void Command_Invoke_can_be_called_more_than_once_for_the_same_command()
1527
{
@@ -52,5 +64,55 @@ public void When_CommandLineBuilder_is_used_then_Command_Invoke_does_not_use_its
5264
.Should()
5365
.NotContain("hello!");
5466
}
67+
68+
[Fact] // https://github.com/dotnet/command-line-api/issues/1589
69+
public async Task Implicit_parsers_for_Parse_and_Invoke_do_not_affect_one_another()
70+
{
71+
RootCommand root = new();
72+
73+
root.Parse("");
74+
75+
var console = new TestConsole();
76+
77+
await root.InvokeAsync("-h", console);
78+
79+
_output.WriteLine(console.Out.ToString());
80+
81+
console.Should().ShowHelp();
82+
}
83+
84+
[Fact]
85+
public void Invoke_extension_method_reuses_implicit_parser_instance()
86+
{
87+
List<Parser> parsers = new();
88+
var command = new Command("x");
89+
90+
command.SetHandler((ParseResult result) => parsers.Add(result.Parser));
91+
92+
command.Invoke("");
93+
command.Invoke("");
94+
95+
var parser1 = parsers[0];
96+
var parser2 = parsers[1];
97+
98+
parser1.Should().BeSameAs(parser2);
99+
}
100+
101+
[Fact]
102+
public void Parse_and_Invoke_extension_methods_use_different_implicit_parsers()
103+
{
104+
var command = new Command("x");
105+
106+
Parser implicitParserForInvoking = null;
107+
Parser implicitParserForParsing = null;
108+
109+
command.SetHandler((ParseResult result) => implicitParserForInvoking = result.Parser);
110+
111+
command.Invoke("");
112+
113+
implicitParserForParsing = command.Parse("").Parser;
114+
115+
implicitParserForInvoking.Should().NotBeSameAs(implicitParserForParsing);
116+
}
55117
}
56118
}

src/System.CommandLine.Tests/SymbolTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void When_Name_is_explicitly_set_then_adding_aliases_does_not_change_it()
2020
}
2121

2222
[Fact]
23-
public void Parse_extension_method_reuses_parser_instance()
23+
public void Parse_extension_method_reuses_implicit_parser_instance()
2424
{
2525
var symbol = CreateSymbol("x");
2626

src/System.CommandLine.Tests/Utility/AssertionExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public static AndConstraint<StringCollectionAssertions> BeEquivalentSequenceTo(
4949
public static AndConstraint<ConsoleAssertions> ShowHelp(this ConsoleAssertions assertions)
5050
{
5151
assertions.Subject.Out.ToString().Should().Contain("Usage:");
52-
assertions.Subject.Error.ToString().Should().BeEmpty();
5352

5453
return new AndConstraint<ConsoleAssertions>(assertions);
5554
}

src/System.CommandLine/ArgumentExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public static TArgument LegalFileNamesOnly<TArgument>(
209209
public static ParseResult Parse(
210210
this Argument argument,
211211
string commandLine) =>
212-
argument.GetOrCreateDefaultParser().Parse(commandLine);
212+
argument.GetOrCreateDefaultSimpleParser().Parse(commandLine);
213213

214214
/// <summary>
215215
/// Parses a command line string value using an argument.
@@ -220,6 +220,6 @@ public static ParseResult Parse(
220220
public static ParseResult Parse(
221221
this Argument argument,
222222
string[] args) =>
223-
argument.GetOrCreateDefaultParser().Parse(args);
223+
argument.GetOrCreateDefaultSimpleParser().Parse(args);
224224
}
225225
}

src/System.CommandLine/Command.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ public void AddGlobalOption(Option option)
127127
/// <inheritdoc />
128128
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
129129

130-
internal Parser? ImplicitParser { get; set; }
130+
internal Parser? ImplicitInvocationParser { get; set; }
131+
132+
internal Parser? ImplicitSimpleParser { get; set; }
131133

132134
private protected void AddSymbol(Symbol symbol)
133135
{

src/System.CommandLine/CommandExtensions.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,7 @@ public static Task<int> InvokeAsync(
7474

7575
private static InvocationPipeline GetDefaultInvocationPipeline(Command command, string[] args)
7676
{
77-
if (command.ImplicitParser is null)
78-
{
79-
command.ImplicitParser = new CommandLineBuilder(command)
80-
.UseDefaults()
81-
.Build();
82-
}
83-
84-
var parseResult = command.ImplicitParser.Parse(args);
77+
var parseResult = command.GetOrCreateDefaultInvocationParser().Parse(args);
8578

8679
return new InvocationPipeline(parseResult);
8780
}
@@ -95,7 +88,7 @@ private static InvocationPipeline GetDefaultInvocationPipeline(Command command,
9588
public static ParseResult Parse(
9689
this Command command,
9790
params string[] args) =>
98-
command.GetOrCreateDefaultParser().Parse(args);
91+
command.GetOrCreateDefaultSimpleParser().Parse(args);
9992

10093
/// <summary>
10194
/// Parses a command line string value using the specified command.
@@ -107,6 +100,6 @@ public static ParseResult Parse(
107100
public static ParseResult Parse(
108101
this Command command,
109102
string commandLine) =>
110-
command.GetOrCreateDefaultParser().Parse(commandLine);
103+
command.GetOrCreateDefaultSimpleParser().Parse(commandLine);
111104
}
112105
}

src/System.CommandLine/OptionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public static TOption LegalFileNamesOnly<TOption>(
171171
public static ParseResult Parse(
172172
this Option option,
173173
string commandLine) =>
174-
option.GetOrCreateDefaultParser().Parse(commandLine);
174+
option.GetOrCreateDefaultSimpleParser().Parse(commandLine);
175175

176176
/// <summary>
177177
/// Parses a command line string value using an option.
@@ -182,6 +182,6 @@ public static ParseResult Parse(
182182
public static ParseResult Parse(
183183
this Option option,
184184
string[] args) =>
185-
option.GetOrCreateDefaultParser().Parse(args);
185+
option.GetOrCreateDefaultSimpleParser().Parse(args);
186186
}
187187
}

src/System.CommandLine/SymbolExtensions.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Collections.Generic;
5+
using System.CommandLine.Builder;
56
using System.CommandLine.Parsing;
67
using System.Linq;
78

@@ -33,14 +34,27 @@ internal static IReadOnlyList<Argument> Arguments(this Symbol symbol)
3334
}
3435
}
3536

36-
internal static Parser GetOrCreateDefaultParser(this Symbol symbol)
37+
internal static Parser GetOrCreateDefaultSimpleParser(this Symbol symbol)
3738
{
3839
var root = GetOrCreateRootCommand(symbol);
3940

40-
if (root.ImplicitParser is not { } parser)
41+
if (root.ImplicitSimpleParser is not { } parser)
4142
{
4243
parser = new Parser(new CommandLineConfiguration(root));
43-
root.ImplicitParser = parser;
44+
root.ImplicitSimpleParser = parser;
45+
}
46+
47+
return parser;
48+
}
49+
50+
internal static Parser GetOrCreateDefaultInvocationParser(this Symbol symbol)
51+
{
52+
var root = GetOrCreateRootCommand(symbol);
53+
54+
if (root.ImplicitInvocationParser is not { } parser)
55+
{
56+
parser = new CommandLineBuilder(root).UseDefaults().Build();
57+
root.ImplicitInvocationParser = parser;
4458
}
4559

4660
return parser;

0 commit comments

Comments
 (0)