diff --git a/CommandLineTool/main.swift b/CommandLineTool/main.swift index ddb2a7f5..7fce7552 100644 --- a/CommandLineTool/main.swift +++ b/CommandLineTool/main.swift @@ -92,14 +92,15 @@ extension FileHandle: TextOutputStream { } } -func printErrors(_ errors: [FormatError]) { +func printWarnings(_ errors: [Error]) { var stderr = FileHandle.standardError for error in errors { - print("error: \(error)", to: &stderr) + print("warning: \(error)", to: &stderr) } } func processArguments(_ args: [String]) { + var errors = [Error]() do { // Get options let args = try preprocessArguments(args, commandLineArguments) @@ -149,15 +150,18 @@ func processArguments(_ args: [String]) { let time = timeEvent { (filesParsed, filesChecked, options, errors) = inferOptions(from: inferURL) } - printErrors(errors) + printWarnings(errors) + if filesParsed == 0 { + throw FormatError.parsing("failed to to infer options") + } print("options inferred from \(filesParsed)/\(filesChecked) files in \(time)") print("") print(commandLineArguments(for: options).map({ "--\($0) \($1)" }).joined(separator: " ")) print("") + return } else { throw FormatError.options("--inferoptions argument was not a valid path") } - return } // Get input path(s) @@ -174,7 +178,6 @@ func processArguments(_ args: [String]) { // Get cache path var cacheURL: URL? - var errors = [FormatError]() let defaultCacheFileName = "swiftformat.cache" let manager = FileManager.default func setDefaultCacheURL() { @@ -185,7 +188,7 @@ func processArguments(_ args: [String]) { try manager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true, attributes: nil) cacheURL = cacheDirectory.appendingPathComponent(defaultCacheFileName) } catch { - errors.append(FormatError.writing("failed to create cache directory at: \(cacheDirectory.path)")) + errors.append(FormatError.writing("failed to create cache directory at \(cacheDirectory.path)")) } } else { errors.append(FormatError.reading("failed to find cache directory at ~/Library/Caches")) @@ -194,7 +197,7 @@ func processArguments(_ args: [String]) { if let cache = args["cache"] { switch cache { case "": - throw FormatError.options("--cache option expects a value.") + throw FormatError.options("--cache option expects a value") case "ignore": break case "clear": @@ -203,13 +206,13 @@ func processArguments(_ args: [String]) { do { try manager.removeItem(at: cacheURL) } catch { - errors.append(FormatError.writing("failed to delete cache file at: \(cacheURL.path)")) + errors.append(FormatError.writing("failed to delete cache file at \(cacheURL.path)")) } } default: cacheURL = expandPath(cache) guard cacheURL != nil else { - throw FormatError.options("unsupported --cache value: \(cache).") + throw FormatError.options("unsupported --cache value `\(cache)`") } var isDirectory: ObjCBool = false if manager.fileExists(atPath: cacheURL!.path, isDirectory: &isDirectory) && isDirectory.boolValue { @@ -224,6 +227,7 @@ func processArguments(_ args: [String]) { if inputURLs.count == 0 { var input: String? var finished = false + var fatalError: Error? DispatchQueue.global(qos: .userInitiated).async { while let line = readLine(strippingNewline: false) { input = (input ?? "") + line @@ -236,16 +240,14 @@ func processArguments(_ args: [String]) { try output.write(to: outputURL, atomically: true, encoding: String.Encoding.utf8) print("swiftformat completed successfully") } catch { - throw FormatError.writing("failed to write file: \(outputURL.path)") + throw FormatError.writing("failed to write file \(outputURL.path)") } } else { // Write to stdout print(output) } - } catch let error as FormatError { - errors.append(error) } catch { - errors.append(FormatError.writing("unknown error: \(error)")) + fatalError = error } } finished = true @@ -255,8 +257,11 @@ func processArguments(_ args: [String]) { while start.timeIntervalSinceNow > -0.01 {} // If no input received by now, assume none is coming if input != nil { - while !finished && start.timeIntervalSinceNow > -30 {} - printErrors(errors) + while !finished && start.timeIntervalSinceNow > -30 { + if let fatalError = fatalError { + throw fatalError + } + } } else { showHelp() } @@ -277,15 +282,20 @@ func processArguments(_ args: [String]) { fileOptions: fileOptions, cacheURL: cacheURL ) - errors += _errors + errors += _errors as [Error] + } + printWarnings(errors) + if filesChecked == 0 { + let inputPaths = inputURLs.map({ $0.path }).joined(separator: ", ") + throw FormatError.options("no eligible files found at \(inputPaths)") } - printErrors(errors) print("swiftformat completed. \(filesWritten)/\(filesChecked) files updated in \(time)") - } catch let error as FormatError { - printErrors([error]) } catch { - printErrors([FormatError.options("unknown error: \(error)")]) + printWarnings(errors) + // Fatal error + var stderr = FileHandle.standardError + print("error: \(error)", to: &stderr) } } diff --git a/CommandLineTool/swiftformat b/CommandLineTool/swiftformat index d2954fd1..63265924 100755 Binary files a/CommandLineTool/swiftformat and b/CommandLineTool/swiftformat differ diff --git a/SwiftFormat/SwiftFormat.swift b/SwiftFormat/SwiftFormat.swift index c2f5bd11..82994d63 100644 --- a/SwiftFormat/SwiftFormat.swift +++ b/SwiftFormat/SwiftFormat.swift @@ -91,9 +91,9 @@ public func enumerateSwiftFiles(withInputURL inputURL: URL, guard let resourceValues = try? inputURL.resourceValues(forKeys: Set(keys)) else { if manager.fileExists(atPath: inputURL.path) { - onComplete { throw FormatError.reading("failed to read attributes for: \(inputURL.path)") } + onComplete { throw FormatError.reading("failed to read attributes for \(inputURL.path)") } } else { - onComplete { throw FormatError.reading("file not found: \(inputURL.path)") } + onComplete { throw FormatError.reading("file not found at \(inputURL.path)") } } return } @@ -104,7 +104,7 @@ public func enumerateSwiftFiles(withInputURL inputURL: URL, } else if resourceValues.isDirectory == true { guard let files = try? manager.contentsOfDirectory( at: inputURL, includingPropertiesForKeys: keys, options: .skipsHiddenFiles) else { - onComplete { throw FormatError.reading("failed to read contents of directory at: \(inputURL.path)") } + onComplete { throw FormatError.reading("failed to read contents of directory at \(inputURL.path)") } return } for url in files { @@ -200,14 +200,14 @@ func inferOptions(from inputURL: URL) -> (Int, Int, FormatOptions, [FormatError] guard let input = try? String(contentsOf: inputURL) else { return { filesChecked += 1 - errors.append(FormatError.reading("failed to read file: \(inputURL.path)")) + errors.append(FormatError.reading("failed to read file \(inputURL.path)")) } } let _tokens = tokenize(input) if let error = parsingError(for: _tokens), case .parsing(let string) = error { return { filesChecked += 1 - errors.append(FormatError.parsing("\(string) in file: \(inputURL.path)")) + errors.append(FormatError.parsing("\(string) in \(inputURL.path)")) } } return { @@ -239,7 +239,11 @@ func processInput(_ inputURLs: [URL], for inputURL in inputURLs { guard let resourceValues = try? inputURL.resourceValues( forKeys: Set([.isDirectoryKey, .isAliasFileKey, .isSymbolicLinkKey])) else { - errors.append(FormatError.reading("failed to read attributes for: \(inputURL.path)")) + if FileManager.default.fileExists(atPath: inputURL.path) { + errors.append(FormatError.reading("failed to read attributes for \(inputURL.path)")) + } else { + errors.append(FormatError.reading("file not found at \(inputURL.path)")) + } continue } if !fileOptions.followSymlinks && @@ -256,7 +260,7 @@ func processInput(_ inputURLs: [URL], guard let input = try? String(contentsOf: inputURL) else { return { filesChecked += 1 // TODO: should this count? - throw FormatError.reading("failed to read file: \(inputURL.path)") + throw FormatError.reading("failed to read file \(inputURL.path)") } } let cacheKey: String = { @@ -282,7 +286,7 @@ func processInput(_ inputURLs: [URL], } catch { return { filesChecked += 1 - throw FormatError.writing("failed to create directory at: \(outputURL.path), \(error)") + throw FormatError.writing("failed to create directory at \(outputURL.path), \(error)") } } } else if output == input { @@ -302,13 +306,13 @@ func processInput(_ inputURLs: [URL], } catch { return { filesChecked += 1 - throw FormatError.writing("failed to write file: \(outputURL.path), \(error)") + throw FormatError.writing("failed to write file \(outputURL.path), \(error)") } } } catch FormatError.parsing(let string) { return { filesChecked += 1 - throw FormatError.parsing("\(string) in file: \(inputURL.path)") + throw FormatError.parsing("\(string) in \(inputURL.path)") } } catch { return { @@ -318,15 +322,12 @@ func processInput(_ inputURLs: [URL], } } } - if filesChecked == 0 { - let inputPaths = inputURLs.map({ $0.path }).joined(separator: ", ") - errors.append(FormatError.options("no eligible files found at: \(inputPaths)")) - } else { + if filesChecked > 0 { // Save cache if let cache = cache, let cacheURL = cacheURL, let cacheDirectory = cacheDirectory { if !(cache as NSDictionary).write(to: cacheURL, atomically: true) { if FileManager.default.fileExists(atPath: cacheDirectory.path) { - errors.append(FormatError.writing("failed to write cache file at: \(cacheURL.path)")) + errors.append(FormatError.writing("failed to write cache file at \(cacheURL.path)")) } else { errors.append(FormatError.reading("specified cache file directory does not exist: \(cacheDirectory.path)")) }