Skip to content

Revert source break in 1.8.0 parse methods#908

Merged
natecook1000 merged 5 commits into
mainfrom
fix-asyncparse-break
May 27, 2026
Merged

Revert source break in 1.8.0 parse methods#908
natecook1000 merged 5 commits into
mainfrom
fix-asyncparse-break

Conversation

@natecook1000

Copy link
Copy Markdown
Member

This removes the async overloads to parse and parseAsRoot that caused a source break in 1.8.0, and introduces new names that disambiguate the methods with async behavior. Instead of calling the existing methods with the await keyword, users can call await asyncParse or await asyncParseAsRoot.

This change also adds a validation step to the synchronous parsing path to check if an async completions function is called.

Users that have already updated their code to work around the source break will revert to the old methods with this change, which will yield a "No 'async' operations occur within 'await' expression" warning. They can remove the await keyword or update to call the async versions directly to fix the warning.

Resolves #907

This removes the `async` overloads to `parse` and `parseAsRoot`
that caused a source break in 1.8.0, and introduces new names
that disambiguate the methods with `async` behavior. Instead of
calling the existing methods with the `await` keyword, users can
call `await asyncParse` or `await asyncParseAsRoot`.

This change also adds a validation step to the synchronous parsing
path to check if an async completions function is called.

Users that have already updated their code to work around the
source break will revert to the old methods with this change,
which will yield a "No 'async' operations occur within 'await'
expression" warning. They can remove the `await` keyword or update
to call the async versions directly to fix the warning.
@rgoldberg rgoldberg self-requested a review May 26, 2026 21:08

@rgoldberg rgoldberg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using #if DEBUG to call the validator in the sync parse(_:) is a great idea.

I just suggest:

  1. Splitting the error text into 2 separate paragraphs to avoid "wall of text".
  2. Removing the validate overload, since it's unnecessary.

Comment thread Sources/ArgumentParser/Parsing/CommandParser.swift Outdated
Comment on lines +309 to +311
let error =
AsyncCompletionsValidator
.validate(self.rootCommand, parent: nil, forcedSyncParse: true)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let error =
AsyncCompletionsValidator
.validate(self.rootCommand, parent: nil, forcedSyncParse: true)
!self.rootCommand.invalidAsyncCompletions(
parent: nil,
propertyPath: .init(describing: self.rootCommand)
)
.isEmpty

The validate overload is unnecessary.

Comment on lines +38 to +47
{
validate(type, parent: parent, forcedSyncParse: false)
}

static func validate(
_ type: ParsableArguments.Type,
parent: InputKey?,
forcedSyncParse: Bool
)
-> ParsableArgumentsValidatorError?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
validate(type, parent: parent, forcedSyncParse: false)
}
static func validate(
_ type: ParsableArguments.Type,
parent: InputKey?,
forcedSyncParse: Bool
)
-> ParsableArgumentsValidatorError?

The validate overload is unnecessary.

Comment on lines +51 to +54
!(type is AsyncParsableCommand.Type) || forcedSyncParse
else {
return nil
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!(type is AsyncParsableCommand.Type) || forcedSyncParse
else {
return nil
}
!(type is AsyncParsableCommand.Type)
else { return nil }

The validate overload is unnecessary.

This & the other suggestion together just revert this file back to pre-PR version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's unnecessary. This validator now gets called two ways:

  1. The validator process calls it. In that case, we want to verify that a root type that only conforms to ParsableCommand doesn't have any async custom completion handlers declared, so we exit early if type conforms to AsyncParsableCommand.
  2. The synchronous parse method calls it. Here we are checking for an AsyncParsableCommand (which would have passed validation) that is being parsed synchronously. So we want to perform the "validation" step (aka find out if there are any async custom completion handlers) even though the type (correctly) conforms to AsyncParsableCommand.

So we need a way to say "do this no matter what" in the validator.

@rgoldberg rgoldberg May 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the suggestion in my earlier comment.

Calling invalidAsyncCompletions(…) directly achieves "do this no matter what", because it avoids the guard in validate(…).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, right on! That's what I ended up doing in my change, just didn't parse that you'd already shown it upthread. 👍🏻

@natecook1000 natecook1000 merged commit 043bca1 into main May 27, 2026
34 checks passed
@natecook1000 natecook1000 deleted the fix-asyncparse-break branch May 27, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1.8.0 breaks source for AsyncParsableCommands that manually invoke parse... method

2 participants