Warning, /frameworks/syntax-highlighting/autotests/input/test.swift is written in an unsupported language. File is not indexed.
0001 // 0002 // Arguments.swift 0003 // SwiftFormat 0004 // 0005 // Created by Nick Lockwood on 07/08/2018. 0006 // Copyright © 2018 Nick Lockwood. 0007 // 0008 // Distributed under the permissive MIT license 0009 // Get the latest version from here: 0010 // 0011 // https://github.com/nicklockwood/SwiftFormat 0012 // 0013 // Permission is hereby granted, free of charge, to any person obtaining a copy 0014 // of this software and associated documentation files (the "Software"), to deal 0015 // in the Software without restriction, including without limitation the rights 0016 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0017 // copies of the Software, and to permit persons to whom the Software is 0018 // furnished to do so, subject to the following conditions: 0019 // 0020 // The above copyright notice and this permission notice shall be included in all 0021 // copies or substantial portions of the Software. 0022 // 0023 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0024 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0025 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0026 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0027 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 0028 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 0029 // SOFTWARE. 0030 // 0031 0032 import Foundation 0033 0034 extension Options { 0035 static let maxArgumentNameLength = 16 0036 0037 init(_ args: [String: String], in directory: String) throws { 0038 fileOptions = try fileOptionsFor(args, in: directory) 0039 formatOptions = try formatOptionsFor(args) 0040 let lint = args.keys.contains("lint") 0041 self.lint = lint 0042 rules = try rulesFor(args, lint: lint) 0043 } 0044 0045 mutating func addArguments(_ args: [String: String], in directory: String) throws { 0046 let oldArguments = argumentsFor(self) 0047 let newArguments = try mergeArguments(args, into: oldArguments) 0048 var newOptions = try Options(newArguments, in: directory) 0049 if let fileInfo = formatOptions?.fileInfo { 0050 newOptions.formatOptions?.fileInfo = fileInfo 0051 } 0052 self = newOptions 0053 } 0054 } 0055 0056 // Parse a space-delimited string into an array of command-line arguments 0057 // Replicates the behavior implemented by the console when parsing input 0058 func parseArguments(_ argumentString: String, ignoreComments: Bool = true) -> [String] { 0059 var arguments = [""] // Arguments always begin with script path 0060 var characters = String.UnicodeScalarView.SubSequence(argumentString.unicodeScalars) 0061 var string = "" 0062 var escaped = false 0063 var quoted = false 0064 loop: while let char = characters.popFirst() { 0065 switch char { 0066 case "#" where !ignoreComments && !escaped && !quoted: 0067 break loop // comment 0068 case "\\" where !escaped: 0069 escaped = true 0070 case "\"" where !escaped && !quoted: 0071 quoted = true 0072 case "\"" where !escaped && quoted: 0073 quoted = false 0074 fallthrough 0075 case " " where !escaped && !quoted: 0076 if !string.isEmpty { 0077 arguments.append(string) 0078 } 0079 string.removeAll() 0080 case "\"" where escaped: 0081 escaped = false 0082 string.append("\"") 0083 case _ where escaped && quoted: 0084 string.append("\\") 0085 fallthrough 0086 default: 0087 escaped = false 0088 string.append(Character(char)) 0089 } 0090 } 0091 if !string.isEmpty { 0092 arguments.append(string) 0093 } 0094 return arguments 0095 } 0096 0097 // Parse a flat array of command-line arguments into a dictionary of flags and values 0098 func preprocessArguments(_ args: [String], _ names: [String]) throws -> [String: String] { 0099 var anonymousArgs = 0 0100 var namedArgs: [String: String] = [:] 0101 var name = "" 0102 for arg in args { 0103 if arg.hasPrefix("--") { 0104 // Long argument names 0105 let key = String(arg.unicodeScalars.dropFirst(2)) 0106 guard names.contains(key) else { 0107 guard let match = bestMatches(for: key, in: names).first else { 0108 throw FormatError.options("Unknown option --\(key)") 0109 } 0110 throw FormatError.options("Unknown option --\(key). Did you mean --\(match)?") 0111 } 0112 name = key 0113 namedArgs[name] = namedArgs[name] ?? "" 0114 continue 0115 } else if arg.hasPrefix("-") { 0116 // Short argument names 0117 let flag = String(arg.unicodeScalars.dropFirst()) 0118 guard let match = names.first(where: { $0.hasPrefix(flag) }) else { 0119 throw FormatError.options("Unknown flag -\(flag)") 0120 } 0121 name = match 0122 namedArgs[name] = namedArgs[name] ?? "" 0123 continue 0124 } 0125 if name == "" { 0126 // Argument is anonymous 0127 name = String(anonymousArgs) 0128 anonymousArgs += 1 0129 } 0130 var arg = arg 0131 let hasTrailingComma = arg.hasSuffix(",") && arg != "," 0132 if hasTrailingComma { 0133 arg = String(arg.dropLast()) 0134 } 0135 if let existing = namedArgs[name], !existing.isEmpty, 0136 // TODO: find a more general way to represent merge-able options 0137 ["exclude", "unexclude", "disable", "enable", "lintonly", "rules"].contains(name) || 0138 Descriptors.all.contains(where: { 0139 $0.argumentName == name && $0.isSetType 0140 }) 0141 { 0142 namedArgs[name] = existing + "," + arg 0143 } else { 0144 namedArgs[name] = arg 0145 } 0146 if !hasTrailingComma { 0147 name = "" 0148 } 0149 } 0150 return namedArgs 0151 } 0152 0153 // Find best match for a given string in a list of options 0154 func bestMatches(for query: String, in options: [String]) -> [String] { 0155 let lowercaseQuery = query.lowercased() 0156 // Sort matches by Levenshtein edit distance 0157 return options 0158 .compactMap { option -> (String, distance: Int, commonPrefix: Int)? in 0159 let lowercaseOption = option.lowercased() 0160 let distance = editDistance(lowercaseOption, lowercaseQuery) 0161 let commonPrefix = lowercaseOption.commonPrefix(with: lowercaseQuery) 0162 if commonPrefix.isEmpty, distance > lowercaseQuery.count / 2 { 0163 return nil 0164 } 0165 return (option, distance, commonPrefix.count) 0166 } 0167 .sorted { 0168 if $0.distance == $1.distance { 0169 return $0.commonPrefix > $1.commonPrefix 0170 } 0171 return $0.distance < $1.distance 0172 } 0173 .map { $0.0 } 0174 } 0175 0176 /// The Damerau-Levenshtein edit-distance between two strings 0177 func editDistance(_ lhs: String, _ rhs: String) -> Int { 0178 let lhs = Array(lhs) 0179 let rhs = Array(rhs) 0180 var dist = [[Int]]() 0181 for i in 0 ... lhs.count { 0182 dist.append([i]) 0183 } 0184 for j in 1 ... rhs.count { 0185 dist[0].append(j) 0186 } 0187 for i in 1 ... lhs.count { 0188 for j in 1 ... rhs.count { 0189 if lhs[i - 1] == rhs[j - 1] { 0190 dist[i].append(dist[i - 1][j - 1]) 0191 } else { 0192 dist[i].append(min(dist[i - 1][j] + 1, 0193 dist[i][j - 1] + 1, 0194 dist[i - 1][j - 1] + 1)) 0195 } 0196 if i > 1, j > 1, lhs[i - 1] == rhs[j - 2], lhs[i - 2] == rhs[j - 1] { 0197 dist[i][j] = min(dist[i][j], dist[i - 2][j - 2] + 1) 0198 } 0199 } 0200 } 0201 return dist[lhs.count][rhs.count] 0202 } 0203 0204 // Parse a comma-delimited list of items 0205 func parseCommaDelimitedList(_ string: String) -> [String] { 0206 return string.components(separatedBy: ",").compactMap { 0207 let item = $0.trimmingCharacters(in: .whitespacesAndNewlines) 0208 return item.isEmpty ? nil : item 0209 } 0210 } 0211 0212 // Parse a comma-delimited string into an array of rules 0213 let allRules = Set(FormatRules.byName.keys) 0214 func parseRules(_ rules: String) throws -> [String] { 0215 return try parseCommaDelimitedList(rules).map { proposedName in 0216 if let name = allRules.first(where: { 0217 $0.lowercased() == proposedName.lowercased() 0218 }) { 0219 return name 0220 } 0221 if Descriptors.all.contains(where: { 0222 $0.argumentName == proposedName 0223 }) { 0224 for rule in FormatRules.all where rule.options.contains(proposedName) { 0225 throw FormatError.options( 0226 "'\(proposedName)' is not a formatting rule. Did you mean '\(rule.name)'?" 0227 ) 0228 } 0229 throw FormatError.options("'\(proposedName)' is not a formatting rule") 0230 } 0231 guard let match = bestMatches(for: proposedName, in: Array(allRules)).first else { 0232 throw FormatError.options("Unknown rule '\(proposedName)'") 0233 } 0234 throw FormatError.options("Unknown rule '\(proposedName)'. Did you mean '\(match)'?") 0235 } 0236 } 0237 0238 // Parse single file path, disallowing globs or commas 0239 func parsePath(_ path: String, for argument: String, in directory: String) throws -> URL { 0240 let expandedPath = expandPath(path, in: directory) 0241 if !FileManager.default.fileExists(atPath: expandedPath.path) { 0242 if path.contains(",") { 0243 throw FormatError.options("\(argument) argument does not support multiple paths") 0244 } 0245 if pathContainsGlobSyntax(path) { 0246 throw FormatError.options("\(argument) path cannot contain wildcards") 0247 } 0248 } 0249 return expandedPath 0250 } 0251 0252 // Parse one or more comma-delimited file paths, expanding globs as required 0253 func parsePaths(_ paths: String, in directory: String) throws -> [URL] { 0254 return try matchGlobs(expandGlobs(paths, in: directory), in: directory) 0255 } 0256 0257 // Merge two dictionaries of arguments 0258 func mergeArguments(_ args: [String: String], into config: [String: String]) throws -> [String: String] { 0259 var input = config 0260 var output = args 0261 // Merge excluded urls 0262 if let exclude = output["exclude"].map(parseCommaDelimitedList), 0263 var excluded = input["exclude"].map({ Set(parseCommaDelimitedList($0)) }) 0264 { 0265 excluded.formUnion(exclude) 0266 output["exclude"] = Array(excluded).sorted().joined(separator: ",") 0267 } 0268 // Merge unexcluded urls 0269 if let unexclude = output["unexclude"].map(parseCommaDelimitedList), 0270 var unexcluded = input["unexclude"].map({ Set(parseCommaDelimitedList($0)) }) 0271 { 0272 unexcluded.formUnion(unexclude) 0273 output["unexclude"] = Array(unexcluded).sorted().joined(separator: ",") 0274 } 0275 // Merge rules 0276 if let rules = try output["rules"].map(parseRules) { 0277 if rules.isEmpty { 0278 output["rules"] = nil 0279 } else { 0280 input["rules"] = nil 0281 input["enable"] = nil 0282 input["disable"] = nil 0283 input["lintonly"] = nil 0284 } 0285 } else { 0286 if let _disable = try output["disable"].map(parseRules) { 0287 if let rules = try input["rules"].map(parseRules) { 0288 input["rules"] = Set(rules).subtracting(_disable).sorted().joined(separator: ",") 0289 } 0290 if let enable = try input["enable"].map(parseRules) { 0291 input["enable"] = Set(enable).subtracting(_disable).sorted().joined(separator: ",") 0292 } 0293 if let lintonly = try input["lintonly"].map(parseRules) { 0294 input["lintonly"] = Set(lintonly).subtracting(_disable).sorted().joined(separator: ",") 0295 } 0296 if let disable = try input["disable"].map(parseRules) { 0297 input["disable"] = Set(disable).union(_disable).sorted().joined(separator: ",") 0298 output["disable"] = nil 0299 } 0300 } 0301 if let _enable = try output["enable"].map(parseRules) { 0302 if let enable = try input["enable"].map(parseRules) { 0303 input["enable"] = Set(enable).union(_enable).sorted().joined(separator: ",") 0304 output["enable"] = nil 0305 } 0306 if let lintonly = try input["lintonly"].map(parseRules) { 0307 input["lintonly"] = Set(lintonly).subtracting(_enable).sorted().joined(separator: ",") 0308 } 0309 if let disable = try input["disable"].map(parseRules) { 0310 input["disable"] = Set(disable).subtracting(_enable).sorted().joined(separator: ",") 0311 } 0312 } 0313 if let _lintonly = try output["lintonly"].map(parseRules) { 0314 if let lintonly = try input["lintonly"].map(parseRules) { 0315 input["lintonly"] = Set(lintonly).union(_lintonly).sorted().joined(separator: ",") 0316 output["lintonly"] = nil 0317 } 0318 } 0319 } 0320 // Merge other arguments 0321 for (key, inValue) in input { 0322 guard let outValue = output[key] else { 0323 output[key] = inValue 0324 continue 0325 } 0326 if Descriptors.all.contains(where: { $0.argumentName == key && $0.isSetType }) { 0327 let inOptions = parseCommaDelimitedList(inValue) 0328 let outOptions = parseCommaDelimitedList(outValue) 0329 output[key] = Set(inOptions).union(outOptions).sorted().joined(separator: ",") 0330 } 0331 } 0332 return output 0333 } 0334 0335 // Parse a configuration file into a dictionary of arguments 0336 public func parseConfigFile(_ data: Data) throws -> [String: String] { 0337 guard let input = String(data: data, encoding: .utf8) else { 0338 throw FormatError.reading("Unable to read data for configuration file") 0339 } 0340 let lines = try cumulate(successiveLines: input.components(separatedBy: .newlines)) 0341 let arguments = try lines.flatMap { line -> [String] in 0342 // TODO: parseArguments isn't a perfect fit here - should we use a different approach? 0343 let line = line.replacingOccurrences(of: "\\n", with: "\n") 0344 let parts = parseArguments(line, ignoreComments: false).dropFirst().map { 0345 $0.replacingOccurrences(of: "\n", with: "\\n") 0346 } 0347 guard let key = parts.first else { 0348 return [] 0349 } 0350 if !key.hasPrefix("-") { 0351 throw FormatError.options("Unknown option '\(key)' in configuration file") 0352 } 0353 return [key, parts.dropFirst().joined(separator: " ")] 0354 } 0355 do { 0356 return try preprocessArguments(arguments, optionsArguments) 0357 } catch let FormatError.options(message) { 0358 throw FormatError.options("\(message) in configuration file") 0359 } 0360 } 0361 0362 private func cumulate(successiveLines: [String]) throws -> [String] { 0363 var cumulatedLines = [String]() 0364 var iterator = successiveLines.makeIterator() 0365 while let currentLine = iterator.next() { 0366 var cumulatedLine = effectiveContent(of: currentLine) 0367 while cumulatedLine.hasSuffix("\\") { 0368 guard let nextLine = iterator.next() else { 0369 throw FormatError.reading("Configuration file ends with an illegal line continuation character '\'") 0370 } 0371 if !nextLine.trimmingCharacters(in: .whitespaces).starts(with: "#") { 0372 cumulatedLine = cumulatedLine.dropLast() + effectiveContent(of: nextLine) 0373 } 0374 } 0375 cumulatedLines.append(String(cumulatedLine)) 0376 } 0377 return cumulatedLines 0378 } 0379 0380 private func effectiveContent(of line: String) -> String { 0381 return line 0382 .prefix { $0 != "#" } 0383 .trimmingCharacters(in: .whitespaces) 0384 } 0385 0386 // Serialize a set of options into either an arguments string or a file 0387 func serialize(options: Options, 0388 swiftVersion: Version = .undefined, 0389 excludingDefaults: Bool = false, 0390 separator: String = "\n") -> String 0391 { 0392 var arguments = [[String: String]]() 0393 if let fileOptions = options.fileOptions { 0394 arguments.append(argumentsFor( 0395 Options(fileOptions: fileOptions), 0396 excludingDefaults: excludingDefaults 0397 )) 0398 } 0399 if let formatOptions = options.formatOptions { 0400 arguments.append(argumentsFor( 0401 Options(formatOptions: formatOptions), 0402 excludingDefaults: excludingDefaults 0403 )) 0404 } else if swiftVersion != .undefined { 0405 let descriptor = Descriptors.swiftVersion 0406 arguments.append([descriptor.argumentName: swiftVersion.rawValue]) 0407 } 0408 if let rules = options.rules { 0409 arguments.append(argumentsFor( 0410 Options(rules: rules), 0411 excludingDefaults: excludingDefaults 0412 )) 0413 } 0414 return arguments 0415 .map { serialize(arguments: $0, separator: separator) } 0416 .filter { !$0.isEmpty } 0417 .joined(separator: separator) 0418 } 0419 0420 // Serialize arguments 0421 func serialize(arguments: [String: String], 0422 separator: String = "\n") -> String 0423 { 0424 return arguments.map { 0425 var value = $1 0426 if value.contains(" ") { 0427 value = "\"\(value.replacingOccurrences(of: "\"", with: "\\\""))\"" 0428 } 0429 return "--\($0) \(value)" 0430 }.sorted().joined(separator: separator) 0431 } 0432 0433 // Get command line arguments from options 0434 func argumentsFor(_ options: Options, excludingDefaults: Bool = false) -> [String: String] { 0435 var args = [String: String]() 0436 if let fileOptions = options.fileOptions { 0437 var arguments = Set(fileArguments) 0438 do { 0439 if !excludingDefaults || fileOptions.followSymlinks != FileOptions.default.followSymlinks { 0440 args["symlinks"] = fileOptions.followSymlinks ? "follow" : "ignore" 0441 } 0442 arguments.remove("symlinks") 0443 } 0444 do { 0445 if !fileOptions.excludedGlobs.isEmpty { 0446 // TODO: find a better alternative to stringifying url 0447 args["exclude"] = fileOptions.excludedGlobs.map { $0.description }.sorted().joined(separator: ",") 0448 } 0449 arguments.remove("exclude") 0450 } 0451 do { 0452 if !fileOptions.unexcludedGlobs.isEmpty { 0453 // TODO: find a better alternative to stringifying url 0454 args["unexclude"] = fileOptions.unexcludedGlobs.map { $0.description }.sorted().joined(separator: ",") 0455 } 0456 arguments.remove("unexclude") 0457 } 0458 do { 0459 if !excludingDefaults || fileOptions.minVersion != FileOptions.default.minVersion { 0460 args["minversion"] = fileOptions.minVersion.description 0461 } 0462 arguments.remove("minversion") 0463 } 0464 assert(arguments.isEmpty) 0465 } 0466 if let formatOptions = options.formatOptions { 0467 for descriptor in Descriptors.all where !descriptor.isRenamed { 0468 let value = descriptor.fromOptions(formatOptions) 0469 guard value != descriptor.fromOptions(.default) || 0470 (!excludingDefaults && !descriptor.isDeprecated) 0471 else { 0472 continue 0473 } 0474 // Special case for swiftVersion 0475 // TODO: find a better solution for this 0476 if descriptor.argumentName == Descriptors.swiftVersion.argumentName, 0477 value == Version.undefined.rawValue 0478 { 0479 continue 0480 } 0481 args[descriptor.argumentName] = value 0482 } 0483 // Special case for wrapParameters 0484 let argumentName = Descriptors.wrapParameters.argumentName 0485 if args[argumentName] == WrapMode.default.rawValue { 0486 args[argumentName] = args[Descriptors.wrapArguments.argumentName] 0487 } 0488 } 0489 if options.lint { 0490 args["lint"] = "" 0491 } 0492 if let rules = options.rules { 0493 let defaultRules = allRules.subtracting(FormatRules.disabledByDefault) 0494 0495 let enabled = rules.subtracting(defaultRules) 0496 if !enabled.isEmpty { 0497 args["enable"] = enabled.sorted().joined(separator: ",") 0498 } 0499 0500 let disabled = defaultRules.subtracting(rules) 0501 if !disabled.isEmpty { 0502 args["disable"] = disabled.sorted().joined(separator: ",") 0503 } 0504 } 0505 return args 0506 } 0507 0508 private func processOption(_ key: String, 0509 in args: [String: String], 0510 from: inout Set<String>, 0511 handler: (String) throws -> Void) throws 0512 { 0513 precondition(optionsArguments.contains(key), "\(key) not in optionsArguments") 0514 var arguments = from 0515 arguments.remove(key) 0516 from = arguments 0517 guard let value = args[key] else { 0518 return 0519 } 0520 do { 0521 try handler(value) 0522 } catch { 0523 guard !value.isEmpty else { 0524 throw FormatError.options("--\(key) option expects a value") 0525 } 0526 if case var FormatError.options(string) = error, !string.isEmpty { 0527 if !string.contains(key) { 0528 string += " in --\(key)" 0529 } 0530 throw FormatError.options(string) 0531 } 0532 throw FormatError.options("Unsupported --\(key) value '\(value)'") 0533 } 0534 } 0535 0536 // Parse rule names from arguments 0537 public func rulesFor(_ args: [String: String], lint: Bool) throws -> Set<String> { 0538 var rules = allRules 0539 rules = try args["rules"].map { 0540 try Set(parseRules($0)) 0541 } ?? rules.subtracting(FormatRules.disabledByDefault) 0542 try args["enable"].map { 0543 try rules.formUnion(parseRules($0)) 0544 } 0545 try args["disable"].map { 0546 try rules.subtract(parseRules($0)) 0547 } 0548 try args["lintonly"].map { rulesString in 0549 if lint { 0550 try rules.formUnion(parseRules(rulesString)) 0551 } else { 0552 try rules.subtract(parseRules(rulesString)) 0553 } 0554 } 0555 return rules 0556 } 0557 0558 // Parse FileOptions from arguments 0559 func fileOptionsFor(_ args: [String: String], in directory: String) throws -> FileOptions? { 0560 var options = FileOptions() 0561 var arguments = Set(fileArguments) 0562 0563 var containsFileOption = false 0564 try processOption("symlinks", in: args, from: &arguments) { 0565 containsFileOption = true 0566 switch $0.lowercased() { 0567 case "follow": 0568 options.followSymlinks = true 0569 case "ignore": 0570 options.followSymlinks = false 0571 default: 0572 throw FormatError.options("") 0573 } 0574 } 0575 try processOption("exclude", in: args, from: &arguments) { 0576 containsFileOption = true 0577 options.excludedGlobs += expandGlobs($0, in: directory) 0578 } 0579 try processOption("unexclude", in: args, from: &arguments) { 0580 containsFileOption = true 0581 options.unexcludedGlobs += expandGlobs($0, in: directory) 0582 } 0583 try processOption("minversion", in: args, from: &arguments) { 0584 containsFileOption = true 0585 guard let minVersion = Version(rawValue: $0) else { 0586 throw FormatError.options("Unsupported --minversion value '\($0)'") 0587 } 0588 guard minVersion <= Version(stringLiteral: swiftFormatVersion) else { 0589 throw FormatError.options("Project specifies SwiftFormat --minversion of \(minVersion)") 0590 } 0591 options.minVersion = minVersion 0592 } 0593 assert(arguments.isEmpty, "\(arguments.joined(separator: ","))") 0594 return containsFileOption ? options : nil 0595 } 0596 0597 // Parse FormatOptions from arguments 0598 // Returns nil if the arguments dictionary does not contain any formatting arguments 0599 public func formatOptionsFor(_ args: [String: String]) throws -> FormatOptions? { 0600 var options = FormatOptions.default 0601 var arguments = Set(formattingArguments) 0602 0603 var containsFormatOption = false 0604 for option in Descriptors.all { 0605 try processOption(option.argumentName, in: args, from: &arguments) { 0606 containsFormatOption = true 0607 try option.toOptions($0, &options) 0608 } 0609 } 0610 assert(arguments.isEmpty, "\(arguments.joined(separator: ","))") 0611 return containsFormatOption ? options : nil 0612 } 0613 0614 // Get deprecation warnings from a set of arguments 0615 func warningsForArguments(_ args: [String: String]) -> [String] { 0616 var warnings = [String]() 0617 for option in Descriptors.all { 0618 if args[option.argumentName] != nil, let message = option.deprecationMessage { 0619 warnings.append("--\(option.argumentName) option is deprecated. \(message)") 0620 } 0621 } 0622 for name in Set(rulesArguments.flatMap { (try? args[$0].map(parseRules) ?? []) ?? [] }) { 0623 if let message = FormatRules.byName[name]?.deprecationMessage { 0624 warnings.append("\(name) rule is deprecated. \(message)") 0625 } 0626 } 0627 if let rules = try? rulesFor(args, lint: true) { 0628 for arg in args.keys where formattingArguments.contains(arg) { 0629 if !rules.contains(where: { 0630 guard let rule = FormatRules.byName[$0] else { 0631 return false 0632 } 0633 return rule.options.contains(arg) || rule.sharedOptions.contains(arg) 0634 }) { 0635 let expected = FormatRules.all.first(where: { 0636 $0.options.contains(arg) 0637 })?.name ?? "associated" 0638 warnings.append("--\(arg) option has no effect when \(expected) rule is disabled") 0639 } 0640 } 0641 } 0642 return warnings 0643 } 0644 0645 let fileArguments = [ 0646 "symlinks", 0647 "exclude", 0648 "unexclude", 0649 "minversion", 0650 ] 0651 0652 let rulesArguments = [ 0653 "disable", 0654 "enable", 0655 "lintonly", 0656 "rules", 0657 ] 0658 0659 let formattingArguments = Descriptors.formatting.map { $0.argumentName } 0660 let internalArguments = Descriptors.internal.map { $0.argumentName } 0661 let optionsArguments = fileArguments + rulesArguments + formattingArguments + internalArguments 0662 0663 let commandLineArguments = [ 0664 // Input options 0665 "filelist", 0666 "stdinpath", 0667 "config", 0668 "inferoptions", 0669 "linerange", 0670 "output", 0671 "cache", 0672 "dryrun", 0673 "lint", 0674 "lenient", 0675 "verbose", 0676 "quiet", 0677 "report", 0678 // Misc 0679 "help", 0680 "version", 0681 "options", 0682 "ruleinfo", 0683 ] + optionsArguments 0684 0685 let deprecatedArguments = Descriptors.all.compactMap { 0686 $0.isDeprecated ? $0.argumentName : nil 0687 }