Description
The new refactor to change SetHandler
to SetAction
introduced a new undesirable behavior in that actions that return a Task<int>
will not have the result observed and returned from the invoked command.
static async Task<int> Main(string[] args)
{
RootCommand command = new();
command.SetAction(CommandAction);
int result = await command.InvokeAsync(string.Empty);
// This is 0 (unexpected) but expected to be 42.
return result;
}
static Task<int> CommandAction(InvocationContext context, CancellationToken token)
{
return Task.FromResult(42);
}
While I understand that pay-for-play was intended in #2095, the compiler is more than happy to allow Func<InvocationContext, CancellationToken, Task<int>>
callbacks to be used for the SetAction
method when it's parameter type is Func<InvocationContext, CancellationToken, Task>
. The underlying AnnonymousCliAction
doesn't check if the returned Task
is actually a Task<int>
and blindly returns 0
.
Because it's valid to pass a Func<InvocationContext, CancellationToken, Task<int>>
callback, it is non-obvious that the result will not actually be observed and the developer may have difficulty understanding why their program is not return non-zero exit codes for failure cases.
Suggested Fixes:
Either the AnnonymousCliAction.InvokeAsync
should check if the returned Task
is actually Task<int>
OR a new method should be added to Command
that accepts a Func<InvocationContext, CancellationToken, Task<int>>
callback.