diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index d3ade404f..f261afe20 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -4,6 +4,7 @@
## Fixed issues
- [#230] Enhancement: Support embedded newlines in usage help sections like header or descriptions. Thanks to [ddimtirov](https://github.com/ddimtirov).
+- [#233] Bugfix: Parser bug: first argument following clustered options is treated as a positional parameter. Thanks to [mgrossmann](https://github.com/mgrossmann).
# Picocli 2.0.2
diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java
index 60fa05b18..538dca926 100644
--- a/src/main/java/picocli/CommandLine.java
+++ b/src/main/java/picocli/CommandLine.java
@@ -2157,16 +2157,15 @@ private void processClusteredShortOptions(Collection required,
if (arity.min > 0 && !empty(cluster)) {
if (tracer.isDebug()) {tracer.debug("Trying to process '%s' as option parameter%n", cluster);}
}
- args.push(cluster); // interpret remainder as option parameter (CAUTION: may be empty string!)
// arity may be >= 1, or
// arity <= 0 && !cluster.startsWith(separator)
// e.g., boolean @Option("-v", arity=0, varargs=true); arg "-rvTRUE", remainder cluster="TRUE"
- if (!args.isEmpty() && args.peek().length() == 0 && !paramAttachedToOption) {
- args.pop(); // throw out empty string we get at the end of a group of clustered short options
+ if (!empty(cluster)) {
+ args.push(cluster); // interpret remainder as option parameter
}
int consumed = applyOption(field, Option.class, arity, paramAttachedToOption, args, initialized, argDescription);
// only return if cluster (and maybe more) was consumed, otherwise continue do-while loop
- if (consumed > 0 || args.isEmpty()) {
+ if (empty(cluster) || consumed > 0 || args.isEmpty()) {
return;
}
cluster = args.pop();
diff --git a/src/test/java/picocli/CommandLineTest.java b/src/test/java/picocli/CommandLineTest.java
index 756e4e585..ebf49a44c 100644
--- a/src/test/java/picocli/CommandLineTest.java
+++ b/src/test/java/picocli/CommandLineTest.java
@@ -955,6 +955,9 @@ public void testCompactFieldsAnyOrder() {
compact = CommandLine.populateCommand(new CompactFields(), "-r -v -oout p1 p2".split(" "));
verifyCompact(compact, true, true, "out", fileArray("p1", "p2"));
+ compact = CommandLine.populateCommand(new CompactFields(), "-rv -o out p1 p2".split(" ")); //#233
+ verifyCompact(compact, true, true, "out", fileArray("p1", "p2"));
+
compact = CommandLine.populateCommand(new CompactFields(), "-oout -r -v p1 p2".split(" "));
verifyCompact(compact, true, true, "out", fileArray("p1", "p2"));
@@ -1012,7 +1015,7 @@ public void testOptionsMixedWithParameters() {
@Test
public void testShortOptionsWithSeparatorButNoValueAssignsEmptyStringEvenIfNotLast() {
CompactFields compact = CommandLine.populateCommand(new CompactFields(), "-ro= -v".split(" "));
- verifyCompact(compact, true, true, "", null);
+ verifyCompact(compact, false, true, "-v", null);
}
@Test
public void testShortOptionsWithColonSeparatorButNoValueAssignsEmptyStringEvenIfNotLast() {
@@ -1020,11 +1023,33 @@ public void testShortOptionsWithColonSeparatorButNoValueAssignsEmptyStringEvenIf
CommandLine cmd = new CommandLine(compact);
cmd.setSeparator(":");
cmd.parse("-ro: -v".split(" "));
+ verifyCompact(compact, false, true, "-v", null);
+ }
+ @Test
+ public void testShortOptionsWithSeparatorButNoValueFailsIfValueRequired() {
+ try {
+ CommandLine.populateCommand(new CompactFields(), "-rvo=".split(" "));
+ fail("Expected exception");
+ } catch (ParameterException ex) {
+ assertEquals("Missing required parameter for option '-o' ()", ex.getMessage());
+ }
+ }
+ @Test
+ public void testShortOptionsWithSeparatorButNoValueAssignsQuotedEmptyStringEvenIfNotLast() {
+ CompactFields compact = CommandLine.populateCommand(new CompactFields(), "-ro=\"\" -v".split(" "));
+ verifyCompact(compact, true, true, "", null);
+ }
+ @Test
+ public void testShortOptionsWithColonSeparatorButNoValueAssignsQuotedEmptyStringEvenIfNotLast() {
+ CompactFields compact = new CompactFields();
+ CommandLine cmd = new CommandLine(compact);
+ cmd.setSeparator(":");
+ cmd.parse("-ro:\"\" -v".split(" "));
verifyCompact(compact, true, true, "", null);
}
@Test
- public void testShortOptionsWithSeparatorButNoValueAssignsEmptyStringIfLast() {
- CompactFields compact = CommandLine.populateCommand(new CompactFields(), "-rvo=".split(" "));
+ public void testShortOptionsWithSeparatorButNoValueAssignsEmptyQuotedStringIfLast() {
+ CompactFields compact = CommandLine.populateCommand(new CompactFields(), "-rvo=\"\"".split(" "));
verifyCompact(compact, true, true, "", null);
}