Skip to content

Commit f94e4b9

Browse files
authored
Disfavor deprecated property wrapper inits (#522)
This change is to make sure that the non-optional generic parameter type is chosen for @argument and @option properties that provide a default value. Previously, an optional type might be chosen depending on how the `help` parameter was spelled, due to strange overload resolution. In particular, using the `.init` caused selection of the deprecated optional overload: // Unexpected: // Infers `Value` as `Optional<AbsolutePath>` @argument(help: .init("The path")) var path = AbsolutePath("/") // Expected: // Infers `Value` as `AbsolutePath` @argument(help: "The path") var path = AbsolutePath("/") This addresses the issue by marking the deprecated overloads as disfavored. rdar://102383455
1 parent 8f9fa6f commit f94e4b9

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

Sources/ArgumentParser/Parsable Properties/Argument.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public struct ArgumentArrayParsingStrategy: Hashable {
290290
}
291291

292292
// MARK: - @Argument T: ExpressibleByArgument Initializers
293-
extension Argument {
293+
extension Argument where Value: ExpressibleByArgument {
294294
/// Creates a property with a default value provided by standard Swift default
295295
/// value syntax.
296296
///
@@ -309,7 +309,7 @@ extension Argument {
309309
wrappedValue: Value,
310310
help: ArgumentHelp? = nil,
311311
completion: CompletionKind? = nil
312-
) where Value: ExpressibleByArgument {
312+
) {
313313
self.init(_parsedValue: .init { key in
314314
let arg = ArgumentDefinition(
315315
container: Bare<Value>.self,
@@ -338,7 +338,7 @@ extension Argument {
338338
public init(
339339
help: ArgumentHelp? = nil,
340340
completion: CompletionKind? = nil
341-
) where Value: ExpressibleByArgument {
341+
) {
342342
self.init(_parsedValue: .init { key in
343343
let arg = ArgumentDefinition(
344344
container: Bare<Value>.self,
@@ -459,6 +459,7 @@ extension Argument {
459459
@available(*, deprecated, message: """
460460
Optional @Arguments with default values should be declared as non-Optional.
461461
""")
462+
@_disfavoredOverload
462463
public init<T>(
463464
wrappedValue _wrappedValue: Optional<T>,
464465
help: ArgumentHelp? = nil,
@@ -540,6 +541,7 @@ extension Argument {
540541
@available(*, deprecated, message: """
541542
Optional @Arguments with default values should be declared as non-Optional.
542543
""")
544+
@_disfavoredOverload
543545
public init<T>(
544546
wrappedValue _wrappedValue: Optional<T>,
545547
help: ArgumentHelp? = nil,

Sources/ArgumentParser/Parsable Properties/Option.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public struct ArrayParsingStrategy: Hashable {
233233
}
234234

235235
// MARK: - @Option T: ExpressibleByArgument Initializers
236-
extension Option {
236+
extension Option where Value: ExpressibleByArgument {
237237
/// Creates a property with a default value provided by standard Swift default value syntax.
238238
///
239239
/// This method is called to initialize an `Option` with a default value such as:
@@ -254,7 +254,7 @@ extension Option {
254254
parsing parsingStrategy: SingleValueParsingStrategy = .next,
255255
help: ArgumentHelp? = nil,
256256
completion: CompletionKind? = nil
257-
) where Value: ExpressibleByArgument {
257+
) {
258258
self.init(_parsedValue: .init { key in
259259
let arg = ArgumentDefinition(
260260
container: Bare<Value>.self,
@@ -278,7 +278,7 @@ extension Option {
278278
parsing parsingStrategy: SingleValueParsingStrategy = .next,
279279
completion: CompletionKind?,
280280
help: ArgumentHelp?
281-
) where Value: ExpressibleByArgument {
281+
) {
282282
self.init(
283283
wrappedValue: wrappedValue,
284284
name: name,
@@ -304,7 +304,7 @@ extension Option {
304304
parsing parsingStrategy: SingleValueParsingStrategy = .next,
305305
help: ArgumentHelp? = nil,
306306
completion: CompletionKind? = nil
307-
) where Value: ExpressibleByArgument {
307+
) {
308308
self.init(_parsedValue: .init { key in
309309
let arg = ArgumentDefinition(
310310
container: Bare<Value>.self,
@@ -432,6 +432,7 @@ extension Option {
432432
@available(*, deprecated, message: """
433433
Optional @Options with default values should be declared as non-Optional.
434434
""")
435+
@_disfavoredOverload
435436
public init<T>(
436437
wrappedValue: Optional<T>,
437438
name: NameSpecification = .long,
@@ -532,6 +533,7 @@ extension Option {
532533
@available(*, deprecated, message: """
533534
Optional @Options with default values should be declared as non-Optional.
534535
""")
536+
@_disfavoredOverload
535537
public init<T>(
536538
wrappedValue: Optional<T>,
537539
name: NameSpecification = .long,

Tests/ArgumentParserEndToEndTests/DefaultsEndToEndTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,3 +793,38 @@ extension DefaultsEndToEndTests {
793793
}
794794
}
795795
}
796+
797+
// MARK: Overload selection
798+
799+
extension DefaultsEndToEndTests {
800+
private struct AbsolutePath: ExpressibleByArgument {
801+
init(_ value: String) {}
802+
init?(argument: String) {}
803+
}
804+
805+
private struct TwoPaths: ParsableCommand {
806+
@Argument(help: .init("The path"))
807+
var path1 = AbsolutePath("abc")
808+
809+
@Argument(help: "The path")
810+
var path2 = AbsolutePath("abc")
811+
812+
@Option(help: .init("The path"))
813+
var path3 = AbsolutePath("abc")
814+
815+
@Option(help: "The path")
816+
var path4 = AbsolutePath("abc")
817+
}
818+
819+
/// Tests that a non-optional `Value` type is inferred, regardless of how the
820+
/// initializer parameters are spelled. Previously, string literals and
821+
/// `.init` calls for the help parameter inferred different generic types.
822+
func testHelpInitInferredType() throws {
823+
AssertParse(TwoPaths.self, []) { cmd in
824+
XCTAssert(type(of: cmd.path1) == AbsolutePath.self)
825+
XCTAssert(type(of: cmd.path2) == AbsolutePath.self)
826+
XCTAssert(type(of: cmd.path3) == AbsolutePath.self)
827+
XCTAssert(type(of: cmd.path4) == AbsolutePath.self)
828+
}
829+
}
830+
}

0 commit comments

Comments
 (0)