Fixes incorrect value copying when ParseArguments has the same field name+type as the ParseCommand (Issue #322)#495
Merged
natecook1000 merged 8 commits intoSep 22, 2022
Conversation
2 tasks
Member
natecook1000
left a comment
There was a problem hiding this comment.
Thanks so much for diagnosing and fixing this issue, @randomeizer! I have some small nits for you to fix, but otherwise this looks ready to go 👍🏻
Co-authored-by: Nate Cook <natecook@apple.com>
Co-authored-by: Nate Cook <natecook@apple.com>
…licate-fields-in-OptionGroup
Contributor
Author
|
Nits picked, ready to go 🙂 |
Member
|
@swift-ci Please test |
Member
|
@swift-ci Please test macos platform |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This fix addresses the core issue described in #322.
As noted in the issue, if you have a
ParsedCommand/AsyncParsedCommandwhich contains aParsedArgumentsimplementation, and theParsedCommandandParsedArgumentsboth have a field which has the same name and type, they will both update each other, even if one has a unique.customLong(...)name.For example, a simple case which will break:
This is then output with flags set:
The first and last combination are correct, the two in the middle are incorrect - only one or the other should be
truein those cases.Detailed Design
It turns out that the core issue is in
ArgumentDecoder, with some assistance fromInputKey. The rootArgumentDecoderis handed the flat list ofParsedValues, which contains a reference to thekeyas anInputKey. ThatInputKeyjust has the field name, with no link to any.customLongvalues. That same list ofParsedValuesis handed to any sub-decoders for@OptionGroup-annotatedParsedArgumentsimplementations. When the sub-decoder looks for its named field, itsInputKeymatches the parent struct's field, and they both end up sharing the same value.For example, if I have an instance of
MyCommandcalledcmd, the two fields have thisInputKey:cmd.myFlag:InputKey(rawValue: "myFlag")cmd.options.myFlag:InputKey(rawValue: "myFlag")The fix I've implemented is to augment
InputKeyto record any "parent" key relevant to the field during the parsing phase. For example, ourMyCommandinstance will now have the following:cmd.myFlag:InputKey(name: "myFlag", parent: .root)cmd.options.myFlag:InputKey(name: "myFlag", parent: .key(InputKey(name: "options", parent: .root)))Interestingly, the fix did not require any changes to
ArgumentDecoder, but it did require changes to anything that created anInputKey, along with passing in aparentInputKeywhere relevant, mostly when dealing withParsedArguments.Documentation Plan
I have added source-level documentation to the
InputKeyin particular, along with a couple of other locations for clarity. All the changes were to internal code, so no public API/documentation changes seem to be necessary. Happy to update them if I missed something though.Test Plan
I've added four new tests cases into
OptionGroupEndToEndTests, for lack of a better place to put it. I'm happy to move them elsewhere if there is a more appropriate location. Specifically, they are:testUniqueNamesForDuplicatedFlag_NoFlagstestUniqueNamesForDuplicatedFlag_RootOnlytestUniqueNamesForDuplicatedFlag_OptionOnlytestUniqueNamesForDuplicatedFlag_RootAndOptionThey cover the four example combinations from above. I'm currently not testing other types (
@Option/@Argument), can add those also if desired. Again, let me know where is best to put them.All existing tests are currently passing.
Source Impact
The key change was with
InputKey, as described above, and the related requirements of passing around the parentInputKeyat various points, particularly invalidatemethods. It doesn't change any public APIs.I also too the opportunity to move copy-pasted code that was dropping the leading "_" for
@propertyWrapperfields intoInputKeyto keep everything tidy.InputKeyended up being larger than theParsedValuescode, so I moved the whole struct into its own file:InputKey.swiftin the same folder.As noted above, it does not change the public API, so I don't believe any updates to the public API documentation are required.
Checklist