Skip to content

Commit 36f90b4

Browse files
committed
Emit return type for closures to help with type checking a bit
1 parent ab9713f commit 36f90b4

File tree

7 files changed

+133
-83
lines changed

7 files changed

+133
-83
lines changed

Sources/Testing/Expectations/Expectation+Macro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
_ optionalValue: consuming T?,
7070
_ comment: @autoclosure () -> Comment? = nil,
7171
sourceLocation: SourceLocation = #_sourceLocation
72-
) -> T = #externalMacro(module: "TestingMacros", type: "RequireMacro") where T: ~Copyable
72+
) -> T = #externalMacro(module: "TestingMacros", type: "UnwrapMacro") where T: ~Copyable
7373

7474
/// Unwrap an optional boolean value or, if it is `nil`, fail and throw an
7575
/// error.

Sources/TestingMacros/ConditionMacro.swift

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ public import SwiftSyntaxMacros
3939
public protocol ConditionMacro: ExpressionMacro, Sendable {
4040
/// Whether or not the macro's expansion may throw an error.
4141
static var isThrowing: Bool { get }
42+
43+
/// The return type of the expansion's closure, if it can be statically
44+
/// determined.
45+
///
46+
/// This property is ignored when a condition macro is closure-based.
47+
static var returnType: TypeSyntax? { get }
4248
}
4349

4450
// MARK: -
@@ -67,6 +73,15 @@ extension ConditionMacro {
6773
.disabled
6874
}
6975

76+
public static var returnType: TypeSyntax? {
77+
TypeSyntax(
78+
MemberTypeSyntax(
79+
baseType: IdentifierTypeSyntax(name: .identifier("Swift")),
80+
name: .identifier("Bool")
81+
)
82+
)
83+
}
84+
7085
/// Perform the expansion of this condition macro.
7186
///
7287
/// - Parameters:
@@ -179,6 +194,7 @@ extension ConditionMacro {
179194
for: macro,
180195
rootedAt: originalArgumentExpr,
181196
effectKeywordsToApply: effectKeywordsToApply,
197+
returnType: returnType,
182198
in: context
183199
)
184200
checkArguments.append(Argument(expression: closureExpr))
@@ -316,6 +332,25 @@ public struct RequireMacro: ConditionMacro {
316332
}
317333
}
318334

335+
/// A type describing the expansion of the `#require()` macro when it produces
336+
/// an optional value.
337+
public struct UnwrapMacro: ConditionMacro {
338+
public static var isThrowing: Bool {
339+
true
340+
}
341+
342+
public static var returnType: TypeSyntax? {
343+
TypeSyntax(
344+
MemberTypeSyntax(
345+
baseType: IdentifierTypeSyntax(name: .identifier("Swift")),
346+
name: .identifier("Optional")
347+
)
348+
)
349+
}
350+
}
351+
352+
// MARK: - Refined condition macros
353+
319354
/// A protocol that can be used to create a condition macro that refines the
320355
/// behavior of another previously-defined condition macro.
321356
public protocol RefinedConditionMacro: ConditionMacro {
@@ -326,6 +361,10 @@ extension RefinedConditionMacro {
326361
public static var isThrowing: Bool {
327362
Base.isThrowing
328363
}
364+
365+
public static var returnType: TypeSyntax? {
366+
Base.returnType
367+
}
329368
}
330369

331370
// MARK: - Diagnostics-emitting condition macros
@@ -335,7 +374,7 @@ extension RefinedConditionMacro {
335374
///
336375
/// This type is otherwise exactly equivalent to ``RequireMacro``.
337376
public struct AmbiguousRequireMacro: RefinedConditionMacro {
338-
public typealias Base = RequireMacro
377+
public typealias Base = UnwrapMacro
339378

340379
public static func expansion(
341380
of macro: some FreestandingMacroExpansionSyntax,
@@ -346,7 +385,7 @@ public struct AmbiguousRequireMacro: RefinedConditionMacro {
346385
}
347386

348387
// Perform the normal macro expansion for #require().
349-
return try RequireMacro.expansion(of: macro, in: context)
388+
return try Base.expansion(of: macro, in: context)
350389
}
351390

352391
/// Check for an ambiguous argument to the `#require()` macro and emit the
@@ -378,7 +417,7 @@ public struct AmbiguousRequireMacro: RefinedConditionMacro {
378417
///
379418
/// This type is otherwise exactly equivalent to ``RequireMacro``.
380419
public struct NonOptionalRequireMacro: RefinedConditionMacro {
381-
public typealias Base = RequireMacro
420+
public typealias Base = UnwrapMacro
382421

383422
public static func expansion(
384423
of macro: some FreestandingMacroExpansionSyntax,
@@ -389,7 +428,7 @@ public struct NonOptionalRequireMacro: RefinedConditionMacro {
389428
}
390429

391430
// Perform the normal macro expansion for #require().
392-
return try RequireMacro.expansion(of: macro, in: context)
431+
return try Base.expansion(of: macro, in: context)
393432
}
394433
}
395434

@@ -418,7 +457,7 @@ public struct RequireThrowsMacro: RefinedConditionMacro {
418457
}
419458

420459
// Perform the normal macro expansion for #require().
421-
return try RequireMacro.expansion(of: macro, in: context)
460+
return try Base.expansion(of: macro, in: context)
422461
}
423462
}
424463

@@ -438,7 +477,7 @@ public struct RequireThrowsNeverMacro: RefinedConditionMacro {
438477
}
439478

440479
// Perform the normal macro expansion for #require().
441-
return try RequireMacro.expansion(of: macro, in: context)
480+
return try Base.expansion(of: macro, in: context)
442481
}
443482
}
444483

Sources/TestingMacros/Support/ConditionArgumentParsing.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,8 @@ extension ConditionMacro {
614614
/// for the purposes of generating expression ID values.
615615
/// - effectKeywordsToApply: The set of effect keywords in the expanded
616616
/// expression or its lexical context that may apply to `node`.
617+
/// - returnType: The return type of the expanded closure, if statically
618+
/// known at macro expansion time.
617619
/// - context: The macro context in which the expression is being parsed.
618620
///
619621
/// - Returns: A tuple containing the rewritten copy of `node`, a list of all
@@ -626,6 +628,7 @@ extension ConditionMacro {
626628
for macro: some FreestandingMacroExpansionSyntax,
627629
rootedAt effectiveRootNode: some SyntaxProtocol,
628630
effectKeywordsToApply: Set<Keyword>,
631+
returnType: (some TypeSyntaxProtocol)?,
629632
in context: some MacroExpansionContext
630633
) -> (ClosureExprSyntax, rewrittenNodes: Set<Syntax>) {
631634
_diagnoseTrivialBooleanValue(from: ExprSyntax(node), for: macro, in: context)
@@ -713,6 +716,11 @@ extension ConditionMacro {
713716
}
714717
)
715718
),
719+
returnClause: returnType.map { returnType in
720+
ReturnClauseSyntax(
721+
type: returnType.with(\.leadingTrivia, .space)
722+
).with(\.leadingTrivia, .space)
723+
},
716724
inKeyword: .keyword(.in)
717725
.with(\.leadingTrivia, .space)
718726
.with(\.trailingTrivia, .newline)

Sources/TestingMacros/TestingMacrosMain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct TestingMacrosMain: CompilerPlugin {
2222
TestDeclarationMacro.self,
2323
ExpectMacro.self,
2424
RequireMacro.self,
25+
UnwrapMacro.self,
2526
AmbiguousRequireMacro.self,
2627
NonOptionalRequireMacro.self,
2728
RequireThrowsMacro.self,

Tests/SubexpressionShowcase/SubexpressionShowcase.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func subexpressionShowcase() async throws {
110110
#expect(m(123 == 456))
111111
#endif
112112

113+
try #require(x == x)
113114
_ = try #require(.some(Int32.Magnitude(1)))
114115

115116
let n = 1 as Any

0 commit comments

Comments
 (0)