Skip to content

Commit b3ff6c1

Browse files
authored
Merge pull request #243 from owenv/more-validation
Implement additional argument validation steps and record multiple failures before exiting
2 parents 8bcfe23 + c21e733 commit b3ff6c1

File tree

3 files changed

+137
-5
lines changed

3 files changed

+137
-5
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public struct Driver {
2727
case unableToLoadOutputFileMap(String)
2828
case unableToDecodeFrontendTargetInfo
2929
case failedToRetrieveFrontendTargetInfo
30+
case missingProfilingData(String)
31+
case conditionalCompilationFlagHasRedundantPrefix(String)
32+
case conditionalCompilationFlagIsNotValidIdentifier(String)
3033
// Explicit Module Build Failures
3134
case malformedModuleDependency(String, String)
3235
case missingPCMArguments(String)
@@ -55,6 +58,12 @@ public struct Driver {
5558
return "could not decode frontend target info; compiler driver and frontend executables may be incompatible"
5659
case .failedToRetrieveFrontendTargetInfo:
5760
return "failed to retrieve frontend target info"
61+
case .missingProfilingData(let arg):
62+
return "no profdata file exists at '\(arg)'"
63+
case .conditionalCompilationFlagHasRedundantPrefix(let name):
64+
return "invalid argument '-D\(name)'; did you provide a redundant '-D' in your build settings?"
65+
case .conditionalCompilationFlagIsNotValidIdentifier(let name):
66+
return "conditional compilation flags must be valid Swift identifiers (rather than '\(name)')"
5867
// Explicit Module Build Failures
5968
case .malformedModuleDependency(let moduleName, let errorDescription):
6069
return "Malformed Module Dependency: \(moduleName), \(errorDescription)"
@@ -327,7 +336,13 @@ public struct Driver {
327336
self.numThreads = Self.determineNumThreads(&parsedOptions, compilerMode: compilerMode, diagnosticsEngine: diagnosticEngine)
328337
self.numParallelJobs = Self.determineNumParallelJobs(&parsedOptions, diagnosticsEngine: diagnosticEngine, env: env)
329338

330-
try Self.validateWarningControlArgs(&parsedOptions)
339+
Self.validateWarningControlArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
340+
Self.validateProfilingArgs(&parsedOptions,
341+
fileSystem: fileSystem,
342+
workingDirectory: workingDirectory,
343+
diagnosticEngine: diagnosticEngine)
344+
Self.validateCompilationConditionArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
345+
Self.validateFrameworkSearchPathArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
331346
Self.validateCoverageArgs(&parsedOptions, diagnosticsEngine: diagnosticEngine)
332347
try toolchain.validateArgs(&parsedOptions,
333348
targetTriple: self.frontendTargetInfo.target.triple,
@@ -1554,14 +1569,63 @@ extension Diagnostic.Message {
15541569
static var error_bridging_header_module_interface: Diagnostic.Message {
15551570
.error("using bridging headers with module interfaces is unsupported")
15561571
}
1572+
static func warning_cannot_assign_to_compilation_condition(name: String) -> Diagnostic.Message {
1573+
.warning("conditional compilation flags do not have values in Swift; they are either present or absent (rather than '\(name)')")
1574+
}
1575+
static func warning_framework_search_path_includes_extension(path: String) -> Diagnostic.Message {
1576+
.warning("framework search path ends in \".framework\"; add directory containing framework instead: \(path)")
1577+
}
15571578
}
15581579

15591580
// MARK: Miscellaneous Argument Validation
15601581
extension Driver {
1561-
static func validateWarningControlArgs(_ parsedOptions: inout ParsedOptions) throws {
1582+
static func validateWarningControlArgs(_ parsedOptions: inout ParsedOptions,
1583+
diagnosticEngine: DiagnosticsEngine) {
15621584
if parsedOptions.hasArgument(.suppressWarnings) &&
15631585
parsedOptions.hasFlag(positive: .warningsAsErrors, negative: .noWarningsAsErrors, default: false) {
1564-
throw Error.conflictingOptions(.warningsAsErrors, .suppressWarnings)
1586+
diagnosticEngine.emit(Error.conflictingOptions(.warningsAsErrors, .suppressWarnings))
1587+
}
1588+
}
1589+
1590+
static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
1591+
fileSystem: FileSystem,
1592+
workingDirectory: AbsolutePath?,
1593+
diagnosticEngine: DiagnosticsEngine) {
1594+
if parsedOptions.hasArgument(.profileGenerate) &&
1595+
parsedOptions.hasArgument(.profileUse) {
1596+
diagnosticEngine.emit(Error.conflictingOptions(.profileGenerate, .profileUse))
1597+
}
1598+
1599+
if let profileArgs = parsedOptions.getLastArgument(.profileUse)?.asMultiple,
1600+
let workingDirectory = workingDirectory ?? fileSystem.currentWorkingDirectory {
1601+
for profilingData in profileArgs {
1602+
if !fileSystem.exists(AbsolutePath(profilingData,
1603+
relativeTo: workingDirectory)) {
1604+
diagnosticEngine.emit(Error.missingProfilingData(profilingData))
1605+
}
1606+
}
1607+
}
1608+
}
1609+
1610+
static func validateCompilationConditionArgs(_ parsedOptions: inout ParsedOptions,
1611+
diagnosticEngine: DiagnosticsEngine) {
1612+
for arg in parsedOptions.arguments(for: .D).map(\.argument.asSingle) {
1613+
if arg.contains("=") {
1614+
diagnosticEngine.emit(.warning_cannot_assign_to_compilation_condition(name: arg))
1615+
} else if arg.hasPrefix("-D") {
1616+
diagnosticEngine.emit(Error.conditionalCompilationFlagHasRedundantPrefix(arg))
1617+
} else if !arg.sd_isSwiftIdentifier {
1618+
diagnosticEngine.emit(Error.conditionalCompilationFlagIsNotValidIdentifier(arg))
1619+
}
1620+
}
1621+
}
1622+
1623+
static func validateFrameworkSearchPathArgs(_ parsedOptions: inout ParsedOptions,
1624+
diagnosticEngine: DiagnosticsEngine) {
1625+
for arg in parsedOptions.arguments(for: .F, .Fsystem).map(\.argument.asSingle) {
1626+
if arg.hasSuffix(".framework") || arg.hasSuffix(".framework/") {
1627+
diagnosticEngine.emit(.warning_framework_search_path_includes_extension(path: arg))
1628+
}
15651629
}
15661630
}
15671631

Sources/swift-driver/main.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ do {
4747
var driver = try Driver(args: arguments,
4848
diagnosticsEngine: diagnosticsEngine,
4949
executor: executor)
50+
// FIXME: The following check should be at the end of Driver.init, but current
51+
// usage of the DiagnosticVerifier in tests makes this difficult.
52+
guard !driver.diagnosticEngine.hasErrors else { throw Diagnostics.fatalError }
53+
5054
let jobs = try driver.planBuild()
5155
try driver.run(jobs: jobs)
5256

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,70 @@ final class SwiftDriverTests: XCTestCase {
16641664
try assertNoDriverDiagnostics(args: "swiftc", "-c", "-target", "x86_64-apple-macosx10.14", "-link-objc-runtime", "foo.swift")
16651665
}
16661666

1667+
func testProfileArgValidation() throws {
1668+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-profile-generate", "-profile-use=profile.profdata"]) {
1669+
$1.expect(.error(Driver.Error.conflictingOptions(.profileGenerate, .profileUse)))
1670+
$1.expect(.error(Driver.Error.missingProfilingData("profile.profdata")))
1671+
}
1672+
1673+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-profile-use=profile.profdata"]) {
1674+
$1.expect(.error(Driver.Error.missingProfilingData("profile.profdata")))
1675+
}
1676+
1677+
try withTemporaryDirectory { path in
1678+
try localFileSystem.writeFileContents(path.appending(component: "profile.profdata"), bytes: .init())
1679+
try assertNoDriverDiagnostics(args: "swiftc", "-working-directory", path.pathString, "foo.swift", "-profile-use=profile.profdata")
1680+
}
1681+
1682+
try withTemporaryDirectory { path in
1683+
try localFileSystem.writeFileContents(path.appending(component: "profile.profdata"), bytes: .init())
1684+
try assertDriverDiagnostics(args: ["swiftc", "-working-directory", path.pathString, "foo.swift",
1685+
"-profile-use=profile.profdata,profile2.profdata"]) {
1686+
$1.expect(.error(Driver.Error.missingProfilingData(path.appending(component: "profile2.profdata").pathString)))
1687+
}
1688+
}
1689+
}
1690+
1691+
func testConditionalCompilationArgValidation() throws {
1692+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-DFOO=BAR"]) {
1693+
$1.expect(.warning("conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'FOO=BAR')"))
1694+
}
1695+
1696+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-D-DFOO"]) {
1697+
$1.expect(.error(Driver.Error.conditionalCompilationFlagHasRedundantPrefix("-DFOO")))
1698+
}
1699+
1700+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-Dnot-an-identifier"]) {
1701+
$1.expect(.error(Driver.Error.conditionalCompilationFlagIsNotValidIdentifier("not-an-identifier")))
1702+
}
1703+
1704+
try assertNoDriverDiagnostics(args: "swiftc", "foo.swift", "-DFOO")
1705+
}
1706+
1707+
func testFrameworkSearchPathArgValidation() throws {
1708+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-F/some/dir/xyz.framework"]) {
1709+
$1.expect(.warning("framework search path ends in \".framework\"; add directory containing framework instead: /some/dir/xyz.framework"))
1710+
}
1711+
1712+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-F/some/dir/xyz.framework/"]) {
1713+
$1.expect(.warning("framework search path ends in \".framework\"; add directory containing framework instead: /some/dir/xyz.framework/"))
1714+
}
1715+
1716+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-Fsystem", "/some/dir/xyz.framework"]) {
1717+
$1.expect(.warning("framework search path ends in \".framework\"; add directory containing framework instead: /some/dir/xyz.framework"))
1718+
}
1719+
1720+
try assertNoDriverDiagnostics(args: "swiftc", "foo.swift", "-Fsystem", "/some/dir/")
1721+
}
1722+
1723+
func testMultipleValidationFailures() throws {
1724+
try assertDiagnostics { engine, verifier in
1725+
verifier.expect(.error(Driver.Error.conditionalCompilationFlagIsNotValidIdentifier("not-an-identifier")))
1726+
verifier.expect(.warning("framework search path ends in \".framework\"; add directory containing framework instead: /some/dir/xyz.framework"))
1727+
_ = try Driver(args: ["swiftc", "foo.swift", "-Dnot-an-identifier", "-F/some/dir/xyz.framework"], diagnosticsEngine: engine)
1728+
}
1729+
}
1730+
16671731
// Test cases ported from Driver/macabi-environment.swift
16681732
func testDarwinSDKVersioning() throws {
16691733
try withTemporaryDirectory { tmpDir in
@@ -2310,8 +2374,8 @@ final class SwiftDriverTests: XCTestCase {
23102374
}
23112375

23122376
do {
2313-
XCTAssertThrowsError(try Driver(args: ["swift", "-no-warnings-as-errors", "-warnings-as-errors", "-suppress-warnings", "foo.swift"])) {
2314-
XCTAssertEqual($0 as? Driver.Error, Driver.Error.conflictingOptions(.warningsAsErrors, .suppressWarnings))
2377+
try assertDriverDiagnostics(args: ["swift", "-no-warnings-as-errors", "-warnings-as-errors", "-suppress-warnings", "foo.swift"]) {
2378+
$1.expect(.error(Driver.Error.conflictingOptions(.warningsAsErrors, .suppressWarnings)))
23152379
}
23162380
}
23172381

0 commit comments

Comments
 (0)