diff --git a/Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift b/Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift index 5c0cc8438..3b0349765 100644 --- a/Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift +++ b/Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift @@ -73,4 +73,3 @@ extension AsyncMainProtocol { } } } - diff --git a/Sources/ArgumentParser/Parsable Types/ParsableCommand.swift b/Sources/ArgumentParser/Parsable Types/ParsableCommand.swift index c9a4f68c7..c9ad4b47d 100644 --- a/Sources/ArgumentParser/Parsable Types/ParsableCommand.swift +++ b/Sources/ArgumentParser/Parsable Types/ParsableCommand.swift @@ -84,7 +84,7 @@ extension ParsableCommand { public static func helpMessage( for subcommand: ParsableCommand.Type, columns: Int? = nil - ) -> String { + ) -> String { helpMessage(for: subcommand, includeHidden: false, columns: columns) } @@ -124,6 +124,13 @@ extension ParsableCommand { /// - Parameter arguments: An array of arguments to use for parsing. If /// `arguments` is `nil`, this uses the program's command-line arguments. public static func main(_ arguments: [String]?) { + +#if DEBUG + if #available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *) { + checkAsyncHierarchy(self, root: "\(self)") + } +#endif + do { var command = try parseAsRoot(arguments) try command.run() @@ -164,4 +171,28 @@ extension ParsableCommand { internal static var defaultIncludesUnconditionalArguments: Bool { configuration.defaultSubcommand?.includesUnconditionalArguments == true } + +#if DEBUG + @available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *) + internal static func checkAsyncHierarchy(_ command: ParsableCommand.Type, root: String) { + for sub in command.configuration.subcommands { + checkAsyncHierarchy(sub, root: root) + + guard sub.configuration.subcommands.isEmpty else { continue } + guard sub is AsyncParsableCommand.Type else { continue } + + fatalError(""" + + -------------------------------------------------------------------- + Asynchronous subcommand of a synchronous root. + + The asynchronous command `\(sub)` is declared as a subcommand of the synchronous root command `\(root)`. + + With this configuration, your asynchronous `run()` method will not be called. To fix this issue, change `\(root)`'s `ParsableCommand` conformance to `AsyncParsableCommand`. + -------------------------------------------------------------------- + + """.wrapped(to: 70)) + } + } +#endif }