Skip to content

Commit 6436c51

Browse files
authored
Merge pull request #2809 from swiftlang/jed/if-defined
Add support for parsing and emitting diagnostics for #if defined(...)
2 parents fc03feb + ec78b9a commit 6436c51

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

Sources/SwiftIfConfig/IfConfigDiagnostic.swift

+22-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible {
3939
case expectedModuleName(syntax: ExprSyntax)
4040
case badInfixOperator(syntax: ExprSyntax)
4141
case badPrefixOperator(syntax: ExprSyntax)
42+
case unexpectedDefined(syntax: ExprSyntax, argument: String)
4243

4344
var description: String {
4445
switch self {
@@ -102,6 +103,10 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible {
102103

103104
case .badPrefixOperator:
104105
return "expected unary '!' expression"
106+
107+
case .unexpectedDefined:
108+
return
109+
"compilation conditions in Swift are always boolean and do not need to be checked for existence with 'defined()'"
105110
}
106111
}
107112

@@ -126,7 +131,8 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible {
126131
.macabiIsMacCatalyst(syntax: let syntax),
127132
.expectedModuleName(syntax: let syntax),
128133
.badInfixOperator(syntax: let syntax),
129-
.badPrefixOperator(syntax: let syntax):
134+
.badPrefixOperator(syntax: let syntax),
135+
.unexpectedDefined(syntax: let syntax, argument: _):
130136
return Syntax(syntax)
131137

132138
case .unsupportedVersionOperator(name: _, operator: let op):
@@ -207,6 +213,21 @@ extension IfConfigDiagnostic: DiagnosticMessage {
207213
)
208214
}
209215

216+
// For the targetEnvironment(macabi) -> macCatalyst rename we have a Fix-It.
217+
if case .unexpectedDefined(syntax: let syntax, argument: let argument) = self {
218+
return Diagnostic(
219+
node: syntax,
220+
message: self,
221+
fixIt: .replace(
222+
message: SimpleFixItMessage(
223+
message: "remove 'defined()'"
224+
),
225+
oldNode: syntax,
226+
newNode: TokenSyntax.identifier(argument)
227+
)
228+
)
229+
}
230+
210231
return Diagnostic(node: syntax, message: self)
211232
}
212233
}

Sources/SwiftIfConfig/IfConfigEvaluation.swift

+13
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,19 @@ func evaluateIfConfig(
313313
role: "pointer authentication scheme"
314314
)
315315

316+
case .defined:
317+
guard let argExpr = call.arguments.singleUnlabeledExpression,
318+
let arg = argExpr.simpleIdentifierExpr?.name
319+
else {
320+
return recordError(.unknownExpression(condition))
321+
}
322+
extraDiagnostics.append(
323+
IfConfigDiagnostic.unexpectedDefined(syntax: condition, argument: arg).asDiagnostic
324+
)
325+
return checkConfiguration(at: condition) {
326+
(active: try configuration.isCustomConditionSet(name: arg), syntaxErrorsAllowed: false)
327+
}
328+
316329
case ._endian:
317330
// Ensure that we have a single argument that is a simple identifier.
318331
guard let argExpr = call.arguments.singleUnlabeledExpression,

Sources/SwiftIfConfig/IfConfigFunctions.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ enum IfConfigFunctions: String {
5757
/// via `_ptrauth(<name>)`.
5858
case _ptrauth
5959

60+
/// An unsupported function used by C preprocessor macros (e.g. `#if defined(FOO)`)
61+
case defined
62+
6063
/// Whether uses of this function consitutes a check that guards new syntax.
6164
/// When such a check fails, the compiler should not diagnose syntax errors
6265
/// within the covered block.
@@ -66,7 +69,7 @@ enum IfConfigFunctions: String {
6669
return true
6770

6871
case .hasAttribute, .hasFeature, .canImport, .os, .arch, .targetEnvironment,
69-
._hasAtomicBitWidth, ._endian, ._pointerBitWidth, ._runtime, ._ptrauth:
72+
._hasAtomicBitWidth, ._endian, ._pointerBitWidth, ._runtime, ._ptrauth, .defined:
7073
return false
7174
}
7275
}

Tests/SwiftIfConfigTest/EvaluateTests.swift

+63
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,69 @@ public class EvaluateTests: XCTestCase {
457457
.inactive
458458
)
459459
}
460+
461+
func testDefined() throws {
462+
let message =
463+
"compilation conditions in Swift are always boolean and do not need to be checked for existence with 'defined()'"
464+
465+
assertIfConfig(
466+
"defined(FOO)",
467+
.active,
468+
configuration: TestingBuildConfiguration(customConditions: ["FOO"]),
469+
diagnostics: [
470+
DiagnosticSpec(
471+
message: message,
472+
line: 1,
473+
column: 1,
474+
severity: .error,
475+
fixIts: [
476+
FixItSpec(message: "remove 'defined()'")
477+
]
478+
)
479+
]
480+
)
481+
482+
assertIfConfig(
483+
"defined(FOO)",
484+
.inactive,
485+
diagnostics: [
486+
DiagnosticSpec(
487+
message: message,
488+
line: 1,
489+
column: 1,
490+
severity: .error,
491+
fixIts: [
492+
FixItSpec(message: "remove 'defined()'")
493+
]
494+
)
495+
]
496+
)
497+
498+
assertIfConfig(
499+
"defined(FOO) || BAR || defined(BAZ)",
500+
.inactive,
501+
diagnostics: [
502+
DiagnosticSpec(
503+
message: message,
504+
line: 1,
505+
column: 1,
506+
severity: .error,
507+
fixIts: [
508+
FixItSpec(message: "remove 'defined()'")
509+
]
510+
),
511+
DiagnosticSpec(
512+
message: message,
513+
line: 1,
514+
column: 24,
515+
severity: .error,
516+
fixIts: [
517+
FixItSpec(message: "remove 'defined()'")
518+
]
519+
),
520+
]
521+
)
522+
}
460523
}
461524

462525
/// Assert the results of evaluating the condition within an `#if` against the

0 commit comments

Comments
 (0)