Skip to content

Commit a9b9644

Browse files
authored
Stop removing underscores from CodingKey names in InputKey (#548)
When a property wrapper is applied to a property, the property's storage is given a name with a prefixed underscore. That is, for a property named `x`, the actual storage is named `_x`. That prefixed storage is what is visible through reflection, so when building an ArgumentSet from a command type's Mirror, we need to remove the leading underscore. This is done when creating an InputKey for each property. However, InputKeys are also created from CodingKeys during decoding of a ParsableCommand. These CodingKeys _do not_ have the leading underscore that is visible, so any underscores that appear are actually from the declaration of the property with an underscored name. Removing leading underscores from CodingKey names results in a mismatch when trying to find the decoded value. This change simplifies the InputKey type to use an array path instead of an indirect enum and removes the leading underscore dropping when creating an InputKey from a CodingKey. rdar://104928743
1 parent 478c2df commit a9b9644

21 files changed

+135
-165
lines changed

Sources/ArgumentParser/Completions/BashCompletionsGenerator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ struct BashCompletionsGenerator {
132132
///
133133
/// These consist of completions that are defined as `.list` or `.custom`.
134134
fileprivate static func generateArgumentCompletions(_ commands: [ParsableCommand.Type]) -> [String] {
135-
ArgumentSet(commands.last!, visibility: .default, parent: .root)
135+
ArgumentSet(commands.last!, visibility: .default, parent: nil)
136136
.compactMap { arg -> String? in
137137
guard arg.isPositional else { return nil }
138138

@@ -159,7 +159,7 @@ struct BashCompletionsGenerator {
159159

160160
/// Returns the case-matching statements for supplying completions after an option or flag.
161161
fileprivate static func generateOptionHandlers(_ commands: [ParsableCommand.Type]) -> String {
162-
ArgumentSet(commands.last!, visibility: .default, parent: .root)
162+
ArgumentSet(commands.last!, visibility: .default, parent: nil)
163163
.compactMap { arg -> String? in
164164
let words = arg.bashCompletionWords()
165165
if words.isEmpty { return nil }

Sources/ArgumentParser/Parsable Properties/Flag.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ extension Flag where Value: EnumerableFlag {
396396
// flag, the default value to show to the user is the `--value-name`
397397
// flag that a user would provide on the command line, not a Swift value.
398398
let defaultValueFlag = initial.flatMap { value -> String? in
399-
let defaultKey = InputKey(name: String(describing: value), parent: .key(key))
399+
let defaultKey = InputKey(name: String(describing: value), parent: key)
400400
let defaultNames = Value.name(for: value).makeNames(defaultKey)
401401
return defaultNames.first?.synopsisString
402402
}
@@ -405,7 +405,7 @@ extension Flag where Value: EnumerableFlag {
405405
let hasCustomCaseHelp = caseHelps.contains(where: { $0 != nil })
406406

407407
let args = Value.allCases.enumerated().map { (i, value) -> ArgumentDefinition in
408-
let caseKey = InputKey(name: String(describing: value), parent: .key(key))
408+
let caseKey = InputKey(name: String(describing: value), parent: key)
409409
let name = Value.name(for: value)
410410

411411
let helpForCase = caseHelps[i] ?? help
@@ -519,7 +519,7 @@ extension Flag {
519519
let hasCustomCaseHelp = caseHelps.contains(where: { $0 != nil })
520520

521521
let args = Element.allCases.enumerated().map { (i, value) -> ArgumentDefinition in
522-
let caseKey = InputKey(name: String(describing: value), parent: .key(parentKey))
522+
let caseKey = InputKey(name: String(describing: value), parent: parentKey)
523523
let name = Element.name(for: value)
524524
let helpForCase = hasCustomCaseHelp ? (caseHelps[i] ?? help) : help
525525

@@ -552,7 +552,7 @@ extension Flag {
552552
let hasCustomCaseHelp = caseHelps.contains(where: { $0 != nil })
553553

554554
let args = Element.allCases.enumerated().map { (i, value) -> ArgumentDefinition in
555-
let caseKey = InputKey(name: String(describing: value), parent: .key(parentKey))
555+
let caseKey = InputKey(name: String(describing: value), parent: parentKey)
556556
let name = Element.name(for: value)
557557
let helpForCase = hasCustomCaseHelp ? (caseHelps[i] ?? help) : help
558558
let help = ArgumentDefinition.Help(

Sources/ArgumentParser/Parsable Properties/NameSpecification.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ extension FlagInversion {
167167
case .short, .customShort:
168168
return includingShort ? element.name(for: key) : nil
169169
case .long:
170-
let modifiedKey = key.with(newName: key.name.addingIntercappedPrefix(prefix))
170+
let modifiedKey = InputKey(name: key.name.addingIntercappedPrefix(prefix), parent: key)
171171
return element.name(for: modifiedKey)
172172
case .customLong(let name, let withSingleDash):
173173
let modifiedName = name.addingPrefixWithAutodetectedStyle(prefix)

Sources/ArgumentParser/Parsable Properties/OptionGroup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public struct OptionGroup<Value: ParsableArguments>: Decodable, ParsedWrapper {
7878
visibility: ArgumentVisibility = .default
7979
) {
8080
self.init(_parsedValue: .init { parentKey in
81-
var args = ArgumentSet(Value.self, visibility: .private, parent: .key(parentKey))
81+
var args = ArgumentSet(Value.self, visibility: .private, parent: parentKey)
8282
args.content.withEach {
8383
$0.help.parentTitle = title
8484
}

Sources/ArgumentParser/Parsable Types/ParsableArguments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ extension ArgumentSetProvider {
248248
}
249249

250250
extension ArgumentSet {
251-
init(_ type: ParsableArguments.Type, visibility: ArgumentVisibility, parent: InputKey.Parent) {
251+
init(_ type: ParsableArguments.Type, visibility: ArgumentVisibility, parent: InputKey?) {
252252
#if DEBUG
253253
do {
254254
try type._validate(parent: parent)

Sources/ArgumentParser/Parsable Types/ParsableArgumentsValidation.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//===----------------------------------------------------------------------===//
1111

1212
fileprivate protocol ParsableArgumentsValidator {
13-
static func validate(_ type: ParsableArguments.Type, parent: InputKey.Parent) -> ParsableArgumentsValidatorError?
13+
static func validate(_ type: ParsableArguments.Type, parent: InputKey?) -> ParsableArgumentsValidatorError?
1414
}
1515

1616
enum ValidatorErrorKind {
@@ -37,7 +37,7 @@ struct ParsableArgumentsValidationError: Error, CustomStringConvertible {
3737
}
3838

3939
extension ParsableArguments {
40-
static func _validate(parent: InputKey.Parent) throws {
40+
static func _validate(parent: InputKey?) throws {
4141
let validators: [ParsableArgumentsValidator.Type] = [
4242
PositionalArgumentsValidator.self,
4343
ParsableArgumentsCodingKeyValidator.self,
@@ -80,7 +80,7 @@ struct PositionalArgumentsValidator: ParsableArgumentsValidator {
8080
var kind: ValidatorErrorKind { .failure }
8181
}
8282

83-
static func validate(_ type: ParsableArguments.Type, parent: InputKey.Parent) -> ParsableArgumentsValidatorError? {
83+
static func validate(_ type: ParsableArguments.Type, parent: InputKey?) -> ParsableArgumentsValidatorError? {
8484
let sets: [ArgumentSet] = Mirror(reflecting: type.init())
8585
.children
8686
.compactMap { child in
@@ -190,7 +190,7 @@ struct ParsableArgumentsCodingKeyValidator: ParsableArgumentsValidator {
190190
}
191191
}
192192

193-
static func validate(_ type: ParsableArguments.Type, parent: InputKey.Parent) -> ParsableArgumentsValidatorError? {
193+
static func validate(_ type: ParsableArguments.Type, parent: InputKey?) -> ParsableArgumentsValidatorError? {
194194
let argumentKeys: [InputKey] = Mirror(reflecting: type.init())
195195
.children
196196
.compactMap { child in
@@ -235,7 +235,7 @@ struct ParsableArgumentsUniqueNamesValidator: ParsableArgumentsValidator {
235235
var kind: ValidatorErrorKind { .failure }
236236
}
237237

238-
static func validate(_ type: ParsableArguments.Type, parent: InputKey.Parent) -> ParsableArgumentsValidatorError? {
238+
static func validate(_ type: ParsableArguments.Type, parent: InputKey?) -> ParsableArgumentsValidatorError? {
239239
let argSets: [ArgumentSet] = Mirror(reflecting: type.init())
240240
.children
241241
.compactMap { child in
@@ -283,7 +283,7 @@ struct NonsenseFlagsValidator: ParsableArgumentsValidator {
283283
var kind: ValidatorErrorKind { .warning }
284284
}
285285

286-
static func validate(_ type: ParsableArguments.Type, parent: InputKey.Parent) -> ParsableArgumentsValidatorError? {
286+
static func validate(_ type: ParsableArguments.Type, parent: InputKey?) -> ParsableArgumentsValidatorError? {
287287
let argSets: [ArgumentSet] = Mirror(reflecting: type.init())
288288
.children
289289
.compactMap { child in

Sources/ArgumentParser/Parsable Types/ParsableCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ extension ParsableCommand {
166166
/// `true` if this command contains any array arguments that are declared
167167
/// with `.unconditionalRemaining`.
168168
internal static var includesUnconditionalArguments: Bool {
169-
ArgumentSet(self, visibility: .private, parent: .root).contains(where: {
169+
ArgumentSet(self, visibility: .private, parent: nil).contains(where: {
170170
$0.isRepeatingPositional && $0.parsingStrategy == .allRemainingInput
171171
})
172172
}

Sources/ArgumentParser/Parsing/ArgumentDefinition.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ extension ArgumentDefinition {
217217
///
218218
/// This initializer is used for any property defined on a `ParsableArguments`
219219
/// type that isn't decorated with one of ArgumentParser's property wrappers.
220-
init(unparsedKey: String, default defaultValue: Any?, parent: InputKey.Parent) {
220+
init(unparsedKey: String, default defaultValue: Any?, parent: InputKey?) {
221221
self.init(
222222
container: Bare<Any>.self,
223223
key: InputKey(name: unparsedKey, parent: parent),

Sources/ArgumentParser/Parsing/ArgumentSet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ extension ArgumentSet {
438438
func firstPositional(
439439
named name: String
440440
) -> ArgumentDefinition? {
441-
let key = InputKey(name: name, parent: .root)
441+
let key = InputKey(name: name, parent: nil)
442442
return first(where: { $0.help.keys.contains(key) })
443443
}
444444

Sources/ArgumentParser/Parsing/CommandParser.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ extension CommandParser {
140140
/// possible.
141141
fileprivate mutating func parseCurrent(_ split: inout SplitArguments) throws -> ParsableCommand {
142142
// Build the argument set (i.e. information on how to parse):
143-
let commandArguments = ArgumentSet(currentNode.element, visibility: .private, parent: .root)
143+
let commandArguments = ArgumentSet(currentNode.element, visibility: .private, parent: nil)
144144

145145
// Parse the arguments, ignoring anything unexpected
146146
let values = try commandArguments.lenientParse(
@@ -325,7 +325,7 @@ extension CommandParser {
325325
let completionValues = Array(args)
326326

327327
// Generate the argument set and parse the argument to find in the set
328-
let argset = ArgumentSet(current.element, visibility: .private, parent: .root)
328+
let argset = ArgumentSet(current.element, visibility: .private, parent: nil)
329329
let parsedArgument = try! parseIndividualArg(argToMatch, at: 0).first!
330330

331331
// Look up the specified argument and retrieve its custom completion function

0 commit comments

Comments
 (0)