Copy the Option before applying property values in handleProperties - #433
Copy the Option before applying property values in handleProperties#433farkhalit wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Fixes property-based parsing so that option values from Properties are applied to a cloned Option rather than mutating the caller-owned Options, preventing value “write-through” across parses and threads.
Changes:
- Clone
Optioninstances in both property-processing paths before applyingprocessValue()/ adding toCommandLine. - Add a regression test ensuring reusing the same
Optionsacross parses doesn’t leak values into earlierCommandLines or intoOptions.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/test/java/org/apache/commons/cli/AbstractParserTestCase.java | Adds regression test covering Options reuse across property-based parses. |
| src/main/java/org/apache/commons/cli/Parser.java | Clones options in processProperties before setting values / adding to CommandLine. |
| src/main/java/org/apache/commons/cli/DefaultParser.java | Clones options in handleProperties before setting values / passing to handleOption. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // the Options belong to the caller and outlive this parse, so hold the value on a copy | ||
| final Option copy = (Option) opt.clone(); | ||
| if (copy.hasArg()) { | ||
| if (copy.isValuesEmpty()) { |
There was a problem hiding this comment.
Done here too, same restructure: the continue happens first so no clone is made for a skipped flag.
| // the Options belong to the caller and outlive this parse, so hold the value on a copy | ||
| final Option copy = (Option) opt.clone(); | ||
| if (copy.hasArg()) { | ||
| if (copy.isValuesEmpty()) { | ||
| copy.processValue(stripLeadingAndTrailingQuotesDefaultOff(value)); |
There was a problem hiding this comment.
Good point, fixed. The no-arg skip check now runs against opt before the clone, so a flag whose value isn't yes/true/1 never allocates a copy.
|
@farkhalit |
Signed-off-by: farkhalit rida <farkhlait@sproutxp.com>
|
Reviewed both Copilot notes, they're fair. Pushed a commit that moves the no-arg skip check ahead of the clone in both paths, so a flag whose property value isn't yes/true/1 no longer allocates a copy. Behavior is unchanged and the parser tests stay green. |
DefaultParser.handlePropertiescallsprocessValueon theOptioninstance owned by the caller'sOptionsrather than on a copy, so a value that arrives through the defaultsPropertiesis written into the sharedOptionand stays there after the parse. Reuse that sameOptionsfor a second parse and theisValuesEmptyguard silently serves the first parse's value instead of the second one's, the value remains readable throughoptions.getOption(...), and two threads parsing at once write the same values list.Parser.processPropertieshas the same write-through and also puts the sharedOptionon theCommandLine, so a later parse rewrites what an earlierCommandLinereturns.Both property paths now clone before storing the value, which is what the argv paths already do in
handleOptionandprocessArgs. The new test inAbstractParserTestCasefails forDefaultParser,BasicParser,GnuParserandPosixParserwithout the change.mvn; that'smvnon the command line by itself.