Skip to content

Commit

Permalink
Emit return type for closures to help with type checking a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
grynspan committed Dec 16, 2024
1 parent ab9713f commit 36f90b4
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 83 deletions.
2 changes: 1 addition & 1 deletion Sources/Testing/Expectations/Expectation+Macro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
_ optionalValue: consuming T?,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) -> T = #externalMacro(module: "TestingMacros", type: "RequireMacro") where T: ~Copyable
) -> T = #externalMacro(module: "TestingMacros", type: "UnwrapMacro") where T: ~Copyable

/// Unwrap an optional boolean value or, if it is `nil`, fail and throw an
/// error.
Expand Down
51 changes: 45 additions & 6 deletions Sources/TestingMacros/ConditionMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ public import SwiftSyntaxMacros
public protocol ConditionMacro: ExpressionMacro, Sendable {
/// Whether or not the macro's expansion may throw an error.
static var isThrowing: Bool { get }

/// The return type of the expansion's closure, if it can be statically
/// determined.
///
/// This property is ignored when a condition macro is closure-based.
static var returnType: TypeSyntax? { get }
}

// MARK: -
Expand Down Expand Up @@ -67,6 +73,15 @@ extension ConditionMacro {
.disabled
}

public static var returnType: TypeSyntax? {
TypeSyntax(
MemberTypeSyntax(
baseType: IdentifierTypeSyntax(name: .identifier("Swift")),
name: .identifier("Bool")
)
)
}

/// Perform the expansion of this condition macro.
///
/// - Parameters:
Expand Down Expand Up @@ -179,6 +194,7 @@ extension ConditionMacro {
for: macro,
rootedAt: originalArgumentExpr,
effectKeywordsToApply: effectKeywordsToApply,
returnType: returnType,
in: context
)
checkArguments.append(Argument(expression: closureExpr))
Expand Down Expand Up @@ -316,6 +332,25 @@ public struct RequireMacro: ConditionMacro {
}
}

/// A type describing the expansion of the `#require()` macro when it produces
/// an optional value.
public struct UnwrapMacro: ConditionMacro {
public static var isThrowing: Bool {
true
}

public static var returnType: TypeSyntax? {
TypeSyntax(
MemberTypeSyntax(
baseType: IdentifierTypeSyntax(name: .identifier("Swift")),
name: .identifier("Optional")
)
)
}
}

// MARK: - Refined condition macros

/// A protocol that can be used to create a condition macro that refines the
/// behavior of another previously-defined condition macro.
public protocol RefinedConditionMacro: ConditionMacro {
Expand All @@ -326,6 +361,10 @@ extension RefinedConditionMacro {
public static var isThrowing: Bool {
Base.isThrowing
}

public static var returnType: TypeSyntax? {
Base.returnType
}
}

// MARK: - Diagnostics-emitting condition macros
Expand All @@ -335,7 +374,7 @@ extension RefinedConditionMacro {
///
/// This type is otherwise exactly equivalent to ``RequireMacro``.
public struct AmbiguousRequireMacro: RefinedConditionMacro {
public typealias Base = RequireMacro
public typealias Base = UnwrapMacro

public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
Expand All @@ -346,7 +385,7 @@ public struct AmbiguousRequireMacro: RefinedConditionMacro {
}

// Perform the normal macro expansion for #require().
return try RequireMacro.expansion(of: macro, in: context)
return try Base.expansion(of: macro, in: context)
}

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

public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
Expand All @@ -389,7 +428,7 @@ public struct NonOptionalRequireMacro: RefinedConditionMacro {
}

// Perform the normal macro expansion for #require().
return try RequireMacro.expansion(of: macro, in: context)
return try Base.expansion(of: macro, in: context)
}
}

Expand Down Expand Up @@ -418,7 +457,7 @@ public struct RequireThrowsMacro: RefinedConditionMacro {
}

// Perform the normal macro expansion for #require().
return try RequireMacro.expansion(of: macro, in: context)
return try Base.expansion(of: macro, in: context)
}
}

Expand All @@ -438,7 +477,7 @@ public struct RequireThrowsNeverMacro: RefinedConditionMacro {
}

// Perform the normal macro expansion for #require().
return try RequireMacro.expansion(of: macro, in: context)
return try Base.expansion(of: macro, in: context)
}
}

Expand Down
8 changes: 8 additions & 0 deletions Sources/TestingMacros/Support/ConditionArgumentParsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ extension ConditionMacro {
/// for the purposes of generating expression ID values.
/// - effectKeywordsToApply: The set of effect keywords in the expanded
/// expression or its lexical context that may apply to `node`.
/// - returnType: The return type of the expanded closure, if statically
/// known at macro expansion time.
/// - context: The macro context in which the expression is being parsed.
///
/// - Returns: A tuple containing the rewritten copy of `node`, a list of all
Expand All @@ -626,6 +628,7 @@ extension ConditionMacro {
for macro: some FreestandingMacroExpansionSyntax,
rootedAt effectiveRootNode: some SyntaxProtocol,
effectKeywordsToApply: Set<Keyword>,
returnType: (some TypeSyntaxProtocol)?,
in context: some MacroExpansionContext
) -> (ClosureExprSyntax, rewrittenNodes: Set<Syntax>) {
_diagnoseTrivialBooleanValue(from: ExprSyntax(node), for: macro, in: context)
Expand Down Expand Up @@ -713,6 +716,11 @@ extension ConditionMacro {
}
)
),
returnClause: returnType.map { returnType in
ReturnClauseSyntax(
type: returnType.with(\.leadingTrivia, .space)
).with(\.leadingTrivia, .space)
},
inKeyword: .keyword(.in)
.with(\.leadingTrivia, .space)
.with(\.trailingTrivia, .newline)
Expand Down
1 change: 1 addition & 0 deletions Sources/TestingMacros/TestingMacrosMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct TestingMacrosMain: CompilerPlugin {
TestDeclarationMacro.self,
ExpectMacro.self,
RequireMacro.self,
UnwrapMacro.self,
AmbiguousRequireMacro.self,
NonOptionalRequireMacro.self,
RequireThrowsMacro.self,
Expand Down
1 change: 1 addition & 0 deletions Tests/SubexpressionShowcase/SubexpressionShowcase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func subexpressionShowcase() async throws {
#expect(m(123 == 456))
#endif

try #require(x == x)
_ = try #require(.some(Int32.Magnitude(1)))

let n = 1 as Any
Expand Down
Loading

0 comments on commit 36f90b4

Please sign in to comment.