Skip to content

Commit

Permalink
Improve deprecation and disabling mechanisms
Browse files Browse the repository at this point in the history
  • Loading branch information
nicklockwood committed Aug 21, 2020
1 parent aa3d8dd commit 0d52b23
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 40 deletions.
4 changes: 2 additions & 2 deletions Sources/Arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,12 @@ func warningsForArguments(_ args: [String: String]) -> [String] {
var warnings = [String]()
for option in FormatOptions.Descriptor.all {
if args[option.argumentName] != nil, let message = option.deprecationMessage {
warnings.append(message)
warnings.append("--\(option.argumentName) option is deprecated. \(message)")
}
}
for name in Set(rulesArguments.flatMap { (try? args[$0].map(parseRules) ?? []) ?? [] }) {
if let message = FormatRules.byName[name]?.deprecationMessage {
warnings.append(message)
warnings.append("\(name) rule is deprecated. \(message)")
}
}
return warnings
Expand Down
6 changes: 6 additions & 0 deletions Sources/CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ func printRuleInfo(for name: String, as type: CLI.OutputType) throws {
print(name, as: type)
print("", as: type)
print(stripMarkdown(rule.help), as: type)
if let message = rule.deprecationMessage {
print("", as: type)
print("Note: \(rule.name) rule is deprecated. \(message)")
print("")
return
}
if !rule.options.isEmpty {
print("\nOptions:\n", as: type)
print(rule.options.compactMap {
Expand Down
46 changes: 28 additions & 18 deletions Sources/OptionsDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,17 @@ extension FormatOptions {
let propertyName: String // internal property; ok to change this
let displayName: String
let help: String
let deprecationMessage: String?
let toOptions: (String, inout FormatOptions) throws -> Void
let fromOptions: (FormatOptions) -> String
private(set) var type: ArgumentType

var deprecationMessage: String? {
return FormatOptions.Descriptor.deprecatedMessage[argumentName]
}

var isDeprecated: Bool {
return deprecationMessage != nil
}

var isRenamed: Bool {
return deprecationMessage != nil && FormatOptions.Descriptor.all.contains(where: {
return isDeprecated && FormatOptions.Descriptor.all.contains(where: {
$0.propertyName == propertyName && $0.argumentName != argumentName
})
}
Expand All @@ -85,6 +82,7 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, Bool>,
trueValues: [String],
falseValues: [String])
Expand All @@ -94,6 +92,7 @@ extension FormatOptions {
self.propertyName = propertyName
self.displayName = displayName
self.help = help
self.deprecationMessage = deprecationMessage
type = .binary(true: trueValues, false: falseValues)
toOptions = { value, options in
switch value.lowercased() {
Expand All @@ -114,6 +113,7 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, T>,
fromArgument: @escaping (String) -> T?,
toArgument: @escaping (T) -> String)
Expand All @@ -122,6 +122,7 @@ extension FormatOptions {
self.propertyName = propertyName
self.displayName = displayName
self.help = help
self.deprecationMessage = deprecationMessage
type = .text
toOptions = { key, options in
guard let value = fromArgument(key) else {
Expand All @@ -138,6 +139,7 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, String>,
options: DictionaryLiteral<String, String>)
{
Expand All @@ -147,6 +149,7 @@ extension FormatOptions {
propertyName: propertyName,
displayName: displayName,
help: help,
deprecationMessage: deprecationMessage,
keyPath: keyPath,
fromArgument: { map[$0.lowercased()] },
toArgument: { value in
Expand All @@ -166,13 +169,15 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, Int>)
{
self.init(
argumentName: argumentName,
propertyName: propertyName,
displayName: displayName,
help: help,
deprecationMessage: deprecationMessage,
keyPath: keyPath,
fromArgument: { Int($0).map { max(0, $0) } },
toArgument: { String($0) }
Expand All @@ -183,13 +188,15 @@ extension FormatOptions {
init<T: RawRepresentable>(argumentName: String,
propertyName: String,
displayName: String,
help: String = "",
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, T>) where T.RawValue == String
{
self.argumentName = argumentName
self.propertyName = propertyName
self.displayName = displayName
self.help = help
self.deprecationMessage = deprecationMessage
type = .text
toOptions = { value, options in
guard let value = T(rawValue: value) ?? T(rawValue: value.lowercased()) else {
Expand All @@ -205,7 +212,8 @@ extension FormatOptions {
init<T: RawRepresentable>(argumentName: String,
propertyName: String,
displayName: String,
help: String = "",
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, T>,
options: [String]) where T.RawValue == String
{
Expand All @@ -214,6 +222,7 @@ extension FormatOptions {
propertyName: propertyName,
displayName: displayName,
help: help,
deprecationMessage: deprecationMessage,
keyPath: keyPath
)
type = .enum(options)
Expand All @@ -223,13 +232,15 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, [String]>,
validate: @escaping (String) throws -> Void = { _ in })
{
self.argumentName = argumentName
self.propertyName = propertyName
self.displayName = displayName
self.help = help
self.deprecationMessage = deprecationMessage
type = .array
toOptions = { value, options in
let values = parseCommaDelimitedList(value)
Expand All @@ -250,13 +261,15 @@ extension FormatOptions {
propertyName: String,
displayName: String,
help: String,
deprecationMessage: String? = nil,
keyPath: WritableKeyPath<FormatOptions, Set<String>>,
validate: @escaping (String) throws -> Void = { _ in })
{
self.argumentName = argumentName
self.propertyName = propertyName
self.displayName = displayName
self.help = help
self.deprecationMessage = deprecationMessage
type = .set
toOptions = { value, options in
let values = parseCommaDelimitedList(value)
Expand Down Expand Up @@ -821,22 +834,12 @@ extension FormatOptions.Descriptor {

// MARK: - DEPRECATED

static let deprecatedMessage = [
empty.argumentName: "--empty option is deprecated. Use --voidtype instead.",
indentComments.argumentName: "--comments option is deprecated. Relative indent within multiline comments is now preserved by default.",
insertBlankLines.argumentName: "--insertlines option is deprecated. Use '--enable blankLinesBetweenScopes' or '--enable blankLinesAroundMark' or '--disable blankLinesBetweenScopes' or '--disable blankLinesAroundMark' instead.",
removeBlankLines.argumentName: "--removelines option is deprecated. Use '--enable blankLinesAtStartOfScope' or '--enable blankLinesAtEndOfScope' or '--disable blankLinesAtStartOfScope' or '--disable blankLinesAtEndOfScope' instead.",
hexLiterals.argumentName: "--hexliterals option is deprecated. Use --hexliteralcase instead.",
wrapElements.argumentName: "--wrapelements option is deprecated. Use --wrapcollections instead.",
experimentalRules.argumentName: "--experimentalRules option is deprecated. Use --enable to opt-in to rules individually.",
specifierOrder.argumentName: "--specifierorder option is deprecated. Use --modifierorder instead.",
]

static let empty = FormatOptions.Descriptor(
argumentName: "empty",
propertyName: "empty",
displayName: "Empty",
help: "deprecated",
deprecationMessage: "Use --voidtype instead.",
keyPath: \.useVoid,
trueValues: ["void"],
falseValues: ["tuple", "tuples"]
Expand All @@ -846,6 +849,7 @@ extension FormatOptions.Descriptor {
propertyName: "indentComments",
displayName: "Comments",
help: "Indenting of comment bodies: \"indent\" (default) or \"ignore\"",
deprecationMessage: "Relative indent within multiline comments is now preserved by default.",
keyPath: \.indentComments,
trueValues: ["indent", "indented"],
falseValues: ["ignore"]
Expand All @@ -855,6 +859,7 @@ extension FormatOptions.Descriptor {
propertyName: "insertBlankLines",
displayName: "Insert Lines",
help: "deprecated",
deprecationMessage: "Use '--enable blankLinesBetweenScopes' or '--enable blankLinesAroundMark' or '--disable blankLinesBetweenScopes' or '--disable blankLinesAroundMark' instead.",
keyPath: \.insertBlankLines,
trueValues: ["enabled", "true"],
falseValues: ["disabled", "false"]
Expand All @@ -864,6 +869,7 @@ extension FormatOptions.Descriptor {
propertyName: "removeBlankLines",
displayName: "Remove Lines",
help: "deprecated",
deprecationMessage: "Use '--enable blankLinesAtStartOfScope' or '--enable blankLinesAtEndOfScope' or '--disable blankLinesAtStartOfScope' or '--disable blankLinesAtEndOfScope' instead.",
keyPath: \.removeBlankLines,
trueValues: ["enabled", "true"],
falseValues: ["disabled", "false"]
Expand All @@ -873,6 +879,7 @@ extension FormatOptions.Descriptor {
propertyName: "uppercaseHex",
displayName: "hexliterals",
help: "deprecated",
deprecationMessage: "Use --hexliteralcase instead.",
keyPath: \.uppercaseHex,
trueValues: ["uppercase", "upper"],
falseValues: ["lowercase", "lower"]
Expand All @@ -882,6 +889,7 @@ extension FormatOptions.Descriptor {
propertyName: "wrapCollections",
displayName: "Wrap Elements",
help: "deprecated",
deprecationMessage: "Use --wrapcollections instead.",
keyPath: \.wrapCollections,
options: ["before-first", "after-first", "preserve", "disabled"]
)
Expand All @@ -890,6 +898,7 @@ extension FormatOptions.Descriptor {
propertyName: "experimentalRules",
displayName: "Experimental Rules",
help: "Experimental rules: \"enabled\" or \"disabled\" (default)",
deprecationMessage: "Use --enable to opt-in to rules individually.",
keyPath: \.experimentalRules,
trueValues: ["enabled", "true"],
falseValues: ["disabled", "false"]
Expand All @@ -899,6 +908,7 @@ extension FormatOptions.Descriptor {
propertyName: "modifierOrder",
displayName: "Specifier Order",
help: "deprecated",
deprecationMessage: "Use --modifierorder instead.",
keyPath: \FormatOptions.modifierOrder,
validate: {
guard _FormatRules.allModifiers.contains($0) else {
Expand Down
34 changes: 15 additions & 19 deletions Sources/Rules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@ public final class FormatRule: Equatable, Comparable {
fileprivate(set) var index = 0
let help: String
let runOnceOnly: Bool
let disabledByDefault: Bool
let orderAfter: [String]
let options: [String]
let sharedOptions: [String]

var deprecationMessage: String? {
return FormatRule.deprecatedMessage[name]
}
let deprecationMessage: String?

var isDeprecated: Bool {
return deprecationMessage != nil
}

fileprivate init(help: String,
deprecationMessage: String? = nil,
runOnceOnly: Bool = false,
disabledByDefault: Bool = false,
orderAfter: [String] = [],
options: [String] = [],
sharedOptions: [String] = [],
Expand All @@ -59,9 +59,11 @@ public final class FormatRule: Equatable, Comparable {
self.fn = fn
self.help = help
self.runOnceOnly = runOnceOnly
self.disabledByDefault = disabledByDefault || deprecationMessage != nil
self.orderAfter = orderAfter
self.options = options
self.sharedOptions = sharedOptions
self.deprecationMessage = deprecationMessage
}

public func apply(with formatter: Formatter) {
Expand All @@ -77,10 +79,6 @@ public final class FormatRule: Equatable, Comparable {
public static func < (lhs: FormatRule, rhs: FormatRule) -> Bool {
return lhs.index < rhs.index
}

static let deprecatedMessage = [
"specifiers": "specifiers rule is deprecated. Use modifierOrder instead.",
]
}

public let FormatRules = _FormatRules()
Expand Down Expand Up @@ -124,14 +122,8 @@ private func allRules(except rules: [String]) -> [FormatRule] {
}

private let _allRules = allRules(except: [])
private let _disabledByDefault = _allRules.filter { $0.disabledByDefault }.map { $0.name }
private let _defaultRules = allRules(except: _disabledByDefault)
private let _deprecatedRules = FormatRule.deprecatedMessage.keys
private let _disabledByDefault = _deprecatedRules + [
"isEmpty",
"multilineEnumCases",
"multilineSwitchCases",
"organizeDeclarations",
]

public extension _FormatRules {
/// A Dictionary of rules by name
Expand Down Expand Up @@ -1888,6 +1880,7 @@ public struct _FormatRules {
/// Deprecated
public let specifiers = FormatRule(
help: "Use consistent ordering for member modifiers.",
deprecationMessage: "Use modifierOrder instead.",
options: ["modifierorder"]
) { formatter in
_ = formatter.options.modifierOrder
Expand Down Expand Up @@ -3503,7 +3496,7 @@ public struct _FormatRules {
/// Formats enum cases declaration into one case per line
public let multilineEnumCases = FormatRule(
help: "Writes one enum case per line.",
options: [],
disabledByDefault: true,
sharedOptions: ["linebreaks"]
) { formatter in
formatter.forEach(.keyword("case")) { i, _ in
Expand Down Expand Up @@ -3531,7 +3524,7 @@ public struct _FormatRules {
/// Writes one switch case per line
public let multilineSwitchCases = FormatRule(
help: "Writes one switch case per line.",
options: [],
disabledByDefault: true,
sharedOptions: ["linebreaks", "tabwidth", "indent", "smarttabs"]
) { formatter in
formatter.forEach(.endOfScope("case")) { i, _ in
Expand Down Expand Up @@ -4066,7 +4059,8 @@ public struct _FormatRules {

/// Replace count == 0 with isEmpty
public let isEmpty = FormatRule(
help: "Prefer `isEmpty` over comparing `count` against zero."
help: "Prefer `isEmpty` over comparing `count` against zero.",
disabledByDefault: true
) { formatter in
formatter.forEach(.identifier("count")) { i, _ in
guard let dotIndex = formatter.index(of: .nonSpaceOrLinebreak, before: i, if: {
Expand Down Expand Up @@ -4900,7 +4894,9 @@ public struct _FormatRules {
public let organizeDeclarations = FormatRule(
help: "Organizes declarations within class, struct, and enum bodies.",
runOnceOnly: true,
options: ["categorymark", "beforemarks", "lifecycle", "structthreshold", "classthreshold", "enumthreshold"]
disabledByDefault: true,
options: ["categorymark", "beforemarks", "lifecycle", "structthreshold",
"classthreshold", "enumthreshold"]
) { formatter in
/// Categories of declarations within an individual type
enum Category: String, CaseIterable {
Expand Down
2 changes: 1 addition & 1 deletion Tests/MetadataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MetadataTests: XCTestCase {
for rule in FormatRules.all {
result += "\n\n## \(rule.name)\n\n\(rule.help)"
if let message = rule.deprecationMessage {
result += "\n\n*Note: \(message)*"
result += "\n\n*Note: \(rule.name) rule is deprecated. \(message)*"
continue
}
if !rule.options.isEmpty {
Expand Down

0 comments on commit 0d52b23

Please sign in to comment.