Skip to content

Commit 1f2adb8

Browse files
authored
fix #2640 (#2646)
1 parent 0a63bad commit 1f2adb8

File tree

4 files changed

+43
-7
lines changed

4 files changed

+43
-7
lines changed

src/System.CommandLine.Tests/HelpOptionTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,38 @@ public void The_users_can_set_max_width()
255255
output.ToString().Should().Contain($" {secondPart}{Environment.NewLine}");
256256
}
257257

258+
[Fact] // https://github.com/dotnet/command-line-api/issues/2640
259+
public void DefaultValueFactory_does_not_throw_when_help_is_invoked()
260+
{
261+
var invocationConfiguration = new InvocationConfiguration
262+
{
263+
Output = new StringWriter(),
264+
Error = new StringWriter()
265+
};
266+
267+
Command subcommand = new("do")
268+
{
269+
new Option<DirectoryInfo>("-x")
270+
{
271+
DefaultValueFactory = result =>
272+
{
273+
result.AddError("Oops!");
274+
return null;
275+
}
276+
}
277+
};
278+
subcommand.SetAction(_ => { });
279+
RootCommand rootCommand = new()
280+
{
281+
subcommand
282+
};
283+
284+
rootCommand.Parse("do --help").Invoke(invocationConfiguration);
285+
286+
invocationConfiguration.Error.ToString().Should().Be("");
287+
}
288+
289+
258290
private sealed class CustomizedHelpAction : SynchronousCommandLineAction
259291
{
260292
internal const string CustomUsageText = "This is custom command usage example.";

src/System.CommandLine/Argument.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSour
112112
/// <returns>Returns the default value for the argument, if defined. Null otherwise.</returns>
113113
public object? GetDefaultValue()
114114
{
115-
return GetDefaultValue(new ArgumentResult(this, null!, null));
115+
var command = Parents.FlattenBreadthFirst(x => x.Parents)
116+
.OfType<Command>()
117+
.FirstOrDefault();
118+
119+
return GetDefaultValue(new ArgumentResult(this, new SymbolResultTree(command), null));
116120
}
117121

118122
internal abstract object? GetDefaultValue(ArgumentResult argumentResult);

src/System.CommandLine/Parsing/SymbolResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public abstract class SymbolResult
1515

1616
private protected SymbolResult(SymbolResultTree symbolResultTree, SymbolResult? parent)
1717
{
18-
SymbolResultTree = symbolResultTree;
18+
SymbolResultTree = symbolResultTree ?? throw new ArgumentNullException(nameof(symbolResultTree));
1919
Parent = parent;
2020
}
2121

src/System.CommandLine/Parsing/SymbolResultTree.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ namespace System.CommandLine.Parsing
77
{
88
internal sealed class SymbolResultTree : Dictionary<Symbol, SymbolResult>
99
{
10-
private readonly Command _rootCommand;
10+
private readonly Command? _rootCommand;
1111
internal List<ParseError>? Errors;
1212
internal List<Token>? UnmatchedTokens;
1313
private Dictionary<string, SymbolNode>? _symbolsByName;
1414

1515
internal SymbolResultTree(
16-
Command rootCommand,
17-
List<string>? tokenizeErrors)
16+
Command? rootCommand = null,
17+
List<string>? tokenizeErrors = null)
1818
{
1919
_rootCommand = rootCommand;
2020

@@ -78,13 +78,13 @@ internal void AddUnmatchedToken(Token token, CommandResult commandResult, Comman
7878

7979
public SymbolResult? GetResult(string name)
8080
{
81-
if (_symbolsByName is null)
81+
if (_symbolsByName is null && _rootCommand is not null)
8282
{
8383
_symbolsByName = new();
8484
PopulateSymbolsByName(_rootCommand);
8585
}
8686

87-
if (!_symbolsByName.TryGetValue(name, out SymbolNode? node))
87+
if (!_symbolsByName!.TryGetValue(name, out SymbolNode? node))
8888
{
8989
throw new ArgumentException($"No symbol result found with name \"{name}\".");
9090
}

0 commit comments

Comments
 (0)