Skip to content

Commit 5d38f55

Browse files
authored
Merge pull request swiftlang#162 from dylansturg/ignore_unparsable_files
Add `ignore-unparsable-files` to silence diagnostics for invalid syntax
2 parents 5e7ef93 + a829e96 commit 5d38f55

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

Sources/swift-format/CommandLineOptions.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ struct SwiftFormatCommand: ParsableCommand {
7171
help: "Recursively run on '.swift' files in any provided directories.")
7272
var recursive: Bool
7373

74+
/// Whether unparsable files, due to syntax errors or unrecognized syntax, should be ignored or
75+
/// treated as containing an error. When ignored, unparsable files are output verbatim in format
76+
/// mode and no diagnostics are raised in lint mode. When not ignored, unparsable files raise a
77+
/// diagnostic in both format and lint mode.
78+
@Flag(help: """
79+
Ignores unparsable files, disabling all diagnostics and formatting for files that contain \
80+
invalid syntax.
81+
""")
82+
var ignoreUnparsableFiles: Bool
83+
7484
/// The list of paths to Swift source files that should be formatted or linted.
7585
@Argument(help: "One or more input filenames")
7686
var paths: [String]

Sources/swift-format/Run.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ import TSCBasic
2424
/// - configuration: The `Configuration` that contains user-specific settings.
2525
/// - sourceFile: A file handle from which to read the source code to be linted.
2626
/// - assumingFilename: The filename of the source file, used in diagnostic output.
27+
/// - ignoreUnparsableFiles: Whether or not to ignore files that contain syntax errors.
2728
/// - debugOptions: The set containing any debug options that were supplied on the command line.
2829
/// - diagnosticEngine: A diagnostic collector that handles diagnostic messages.
2930
/// - Returns: Zero if there were no lint errors, otherwise a non-zero number.
3031
func lintMain(
3132
configuration: Configuration, sourceFile: FileHandle, assumingFilename: String?,
32-
debugOptions: DebugOptions, diagnosticEngine: DiagnosticEngine
33+
ignoreUnparsableFiles: Bool, debugOptions: DebugOptions, diagnosticEngine: DiagnosticEngine
3334
) {
3435
let linter = SwiftLinter(configuration: configuration, diagnosticEngine: diagnosticEngine)
3536
linter.debugOptions = debugOptions
@@ -49,6 +50,9 @@ func lintMain(
4950
Diagnostic.Message(.error, "Unable to lint \(path): file is not readable or does not exist."))
5051
return
5152
} catch SwiftFormatError.fileContainsInvalidSyntax(let position) {
53+
guard !ignoreUnparsableFiles else {
54+
return
55+
}
5256
let path = assumingFileURL.path
5357
let location = SourceLocationConverter(file: path, source: source).location(for: position)
5458
diagnosticEngine.diagnose(
@@ -69,12 +73,13 @@ func lintMain(
6973
/// - sourceFile: A file handle from which to read the source code to be linted.
7074
/// - assumingFilename: The filename of the source file, used in diagnostic output.
7175
/// - inPlace: Whether or not to overwrite the current file when formatting.
76+
/// - ignoreUnparsableFiles: Whether or not to ignore files that contain syntax errors.
7277
/// - debugOptions: The set containing any debug options that were supplied on the command line.
7378
/// - diagnosticEngine: A diagnostic collector that handles diagnostic messages.
7479
/// - Returns: Zero if there were no format errors, otherwise a non-zero number.
7580
func formatMain(
7681
configuration: Configuration, sourceFile: FileHandle, assumingFilename: String?, inPlace: Bool,
77-
debugOptions: DebugOptions, diagnosticEngine: DiagnosticEngine
82+
ignoreUnparsableFiles: Bool, debugOptions: DebugOptions, diagnosticEngine: DiagnosticEngine
7883
) {
7984
// Even though `diagnosticEngine` is defined, it's use is reserved for fatal messages. Pass nil
8085
// to the formatter to suppress other messages since they will be fixed or can't be automatically
@@ -111,6 +116,15 @@ func formatMain(
111116
.error, "Unable to format \(path): file is not readable or does not exist."))
112117
return
113118
} catch SwiftFormatError.fileContainsInvalidSyntax(let position) {
119+
guard !ignoreUnparsableFiles else {
120+
guard !inPlace else {
121+
// For in-place mode, nothing is expected to stdout and the file shouldn't be modified.
122+
return
123+
}
124+
stdoutStream.write(source)
125+
stdoutStream.flush()
126+
return
127+
}
114128
let path = assumingFileURL.path
115129
let location = SourceLocationConverter(file: path, source: source).location(for: position)
116130
diagnosticEngine.diagnose(

Sources/swift-format/main.swift

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import ArgumentParser
1314
import Foundation
1415
import SwiftFormat
1516
import SwiftFormatConfiguration
1617
import SwiftFormatCore
1718
import SwiftSyntax
1819
import TSCBasic
19-
import ArgumentParser
2020

2121
extension SwiftFormatCommand {
2222
func run() throws {
@@ -29,36 +29,44 @@ extension SwiftFormatCommand {
2929
formatMain(
3030
configuration: configuration, sourceFile: FileHandle.standardInput,
3131
assumingFilename: assumeFilename, inPlace: false,
32+
ignoreUnparsableFiles: ignoreUnparsableFiles,
3233
debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
3334
} else {
34-
try processSources(from: paths, configurationPath: configurationPath, diagnosticEngine: diagnosticEngine) {
35+
try processSources(
36+
from: paths, configurationPath: configurationPath, diagnosticEngine: diagnosticEngine
37+
) {
3538
(sourceFile, path, configuration) in
3639
formatMain(
3740
configuration: configuration, sourceFile: sourceFile, assumingFilename: path,
38-
inPlace: inPlace, debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
41+
inPlace: inPlace, ignoreUnparsableFiles: ignoreUnparsableFiles,
42+
debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
3943
}
4044
}
41-
45+
4246
case .lint:
4347
if paths.isEmpty {
4448
let configuration = try loadConfiguration(
4549
forSwiftFile: nil, configFilePath: configurationPath)
4650
lintMain(
47-
configuration: configuration, sourceFile: FileHandle.standardInput,
48-
assumingFilename: assumeFilename, debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
51+
configuration: configuration, sourceFile: FileHandle.standardInput,
52+
assumingFilename: assumeFilename, ignoreUnparsableFiles: ignoreUnparsableFiles,
53+
debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
4954
} else {
50-
try processSources(from: paths, configurationPath: configurationPath, diagnosticEngine: diagnosticEngine) {
55+
try processSources(
56+
from: paths, configurationPath: configurationPath, diagnosticEngine: diagnosticEngine
57+
) {
5158
(sourceFile, path, configuration) in
5259
lintMain(
5360
configuration: configuration, sourceFile: sourceFile, assumingFilename: path,
61+
ignoreUnparsableFiles: ignoreUnparsableFiles,
5462
debugOptions: debugOptions, diagnosticEngine: diagnosticEngine)
5563
}
5664
}
57-
65+
5866
case .dumpConfiguration:
5967
try dumpDefaultConfiguration()
6068
}
61-
69+
6270
// If any of the operations have generated diagnostics, exit with the
6371
// error status code.
6472
if !diagnosticEngine.diagnostics.isEmpty {
@@ -140,7 +148,8 @@ private func dumpDefaultConfiguration() throws {
140148
guard let jsonString = String(data: data, encoding: .utf8) else {
141149
// This should never happen, but let's make sure we fail more gracefully than crashing, just
142150
// in case.
143-
throw FormatError(message: "Could not dump the default configuration: the JSON was not valid UTF-8")
151+
throw FormatError(
152+
message: "Could not dump the default configuration: the JSON was not valid UTF-8")
144153
}
145154
print(jsonString)
146155
} catch {

0 commit comments

Comments
 (0)