diff --git a/Sources/ArgumentParser/Parsing/ArgumentSet.swift b/Sources/ArgumentParser/Parsing/ArgumentSet.swift index 4016630bb..2b80f5501 100644 --- a/Sources/ArgumentParser/Parsing/ArgumentSet.swift +++ b/Sources/ArgumentParser/Parsing/ArgumentSet.swift @@ -348,8 +348,8 @@ extension ArgumentSet { try parseValue(argument, parsed, origin, update, &result, &usedOrigins) } case .terminator: - // Mark the terminator as used: - result.set(ParsedValues.Element(key: .terminator, value: 0, inputOrigin: [origin])) + // Ignore the terminator, it might get picked up as a positional value later. + break } } diff --git a/Sources/ArgumentParser/Parsing/CommandParser.swift b/Sources/ArgumentParser/Parsing/CommandParser.swift index 4ccf02b7e..951b4d872 100644 --- a/Sources/ArgumentParser/Parsing/CommandParser.swift +++ b/Sources/ArgumentParser/Parsing/CommandParser.swift @@ -85,7 +85,7 @@ extension CommandParser { try checkForBuiltInFlags(split) // We should have used up all arguments at this point: - guard split.isEmpty else { + guard !split.containsNonTerminatorArguments else { // Check if one of the arguments is an unknown option for (index, element) in split.elements { if case .option(let argument) = element { diff --git a/Sources/ArgumentParser/Parsing/SplitArguments.swift b/Sources/ArgumentParser/Parsing/SplitArguments.swift index 9f99dadba..39d1496b4 100644 --- a/Sources/ArgumentParser/Parsing/SplitArguments.swift +++ b/Sources/ArgumentParser/Parsing/SplitArguments.swift @@ -196,10 +196,20 @@ extension SplitArguments.Element { } extension SplitArguments { + /// `true` if the arguments are empty. var isEmpty: Bool { elements.isEmpty } - + + /// `true` if the arguments are empty, or if the only remaining argument is the `--` terminator. + var containsNonTerminatorArguments: Bool { + if elements.isEmpty { return false } + if elements.count > 1 { return true } + + if case .terminator = elements[0].element { return false } + else { return true } + } + subscript(position: Index) -> Element? { return elements.first { $0.0 == position diff --git a/Tests/ArgumentParserEndToEndTests/OptionGroupEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/OptionGroupEndToEndTests.swift index 52b20be07..a740bcf5f 100644 --- a/Tests/ArgumentParserEndToEndTests/OptionGroupEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/OptionGroupEndToEndTests.swift @@ -16,7 +16,7 @@ import ArgumentParser final class OptionGroupEndToEndTests: XCTestCase { } -struct Inner: TestableParsableArguments { +fileprivate struct Inner: TestableParsableArguments { @Flag(name: [.short, .long]) var extraVerbiage: Bool @Option(default: 0) @@ -33,7 +33,7 @@ struct Inner: TestableParsableArguments { } } -struct Outer: TestableParsableArguments { +fileprivate struct Outer: TestableParsableArguments { @Flag() var verbose: Bool @Argument() @@ -53,7 +53,7 @@ struct Outer: TestableParsableArguments { } } -struct Command: TestableParsableCommand { +fileprivate struct Command: TestableParsableCommand { static let configuration = CommandConfiguration(commandName: "testCommand") @OptionGroup() diff --git a/Tests/ArgumentParserEndToEndTests/RepeatingEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/RepeatingEndToEndTests.swift index 867c2f341..b87f418a5 100644 --- a/Tests/ArgumentParserEndToEndTests/RepeatingEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/RepeatingEndToEndTests.swift @@ -129,6 +129,32 @@ extension RepeatingEndToEndTests { // MARK: - +fileprivate struct Outer: ParsableCommand { + static let configuration = CommandConfiguration(subcommands: [Inner.self]) +} + +fileprivate struct Inner: ParsableCommand { + @Flag() + var verbose: Bool + + @Argument(parsing: .unconditionalRemaining) + var files: [String] +} + +extension RepeatingEndToEndTests { + func testParsing_subcommandRemaining() { + AssertParseCommand( + Outer.self, Inner.self, + ["inner", "--verbose", "one", "two", "--", "three", "--other"]) + { inner in + XCTAssertTrue(inner.verbose) + XCTAssertEqual(inner.files, ["one", "two", "--", "three", "--other"]) + } + } +} + +// MARK: - + fileprivate struct Qux: ParsableArguments { @Option(parsing: .upToNextOption) var names: [String] @Flag() var verbose: Bool