Skip to content

Commit d0a2288

Browse files
committed
Throw an error if types conforming to MemberMacro implement no expansion method
When adding a new type that conforms to `MemberMacro`, no `expansion` function is required to be implemented. It’s necessary to do it this way because: - `expansion(of:providingMembersOf:in:)` needs to exist as a protocol requirement to keep Swift 5.9 macros that implement this method compiling - `expansion(of:providingMembersOf:in:)` needs to be defaulted so that macros implementing `expansion(of:providingMembersOf:conformingTo:in:)` don’t need to also provide an implementation for the legacy version that doesn’t have `conformingTo:` - `expansion(of:providingMembersOf:conformingTo:in:)` obviously needs to exist since it’s the new dedicated entry point - `expansion(of:providingMembersOf:conformingTo:in:)` needs to have a default implementation that calls `expansion(of:providingMembersOf:in:)` so that 5.9 macros continue to work - We can’t mark `expansion(of:providingMembersOf:in:)` as deprecated because it’s called by the default implementation of `expansion(of:providingMembersOf:conformingTo:in:)` and we want to keep swift-syntax building without warnings. At the moment, we provide default implementations for both `expansion` functions that call each other, which causes an infinite recursion, that makes it non-obvious to see what’s going wrong. With this change, the default implementation of the legacy `expansion(of:providingMembersOf:in:)` method throws an error saying that you need to implement either of the two expansion methods. That way you get the following behavior: - If you don’t implement either expansion function, the error gets thrown - If a macro implements the legacy `expansion(of:providingMembersOf:in:)` function, then it overrides the throwing version and the macro works. - If a macro implements the new `expansion(of:providingMembersOf:conformingTo:in:)` method, then the compiler calls into that method directly and the legacy `expansion(of:providingMembersOf:in:)` never gets invoked by the compiler. The only possible issue I can think of is if a library is calling `expansion(of:providingMembersOf:in:)` but the macro only implements `expansion(of:providingMembersOf:conformingTo:in:)`. In this case an error would get thrown where it currently isn’t. But since I don’t see any reason why anyone would be calling `expansion(of:providingMembersOf:in:)` directly, I think this is fine.
1 parent d295e6a commit d0a2288

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

Sources/SwiftSyntaxMacros/MacroProtocols/MemberMacro.swift

+14-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ public protocol MemberMacro: AttachedMacro {
2323
///
2424
/// - Returns: the set of member declarations introduced by this macro, which
2525
/// are nested inside the `attachedTo` declaration.
26-
@available(*, deprecated, message: "Use expansion(of:providingMembersOf:conformingTo:in:")
26+
///
27+
/// - Warning: This is the legacy `expansion` function of `MemberMacro` that is provided for backwards-compatiblity.
28+
/// Use ``expansion(of:providingMembersOf:conformingTo:in:)-1sxoe`` instead.
2729
static func expansion(
2830
of node: AttributeSyntax,
2931
providingMembersOf declaration: some DeclGroupSyntax,
@@ -54,14 +56,24 @@ public protocol MemberMacro: AttachedMacro {
5456
) throws -> [DeclSyntax]
5557
}
5658

59+
private struct UnimplementedExpansionMethodError: Error, CustomStringConvertible {
60+
var description: String {
61+
"""
62+
Types conforming to `MemberMacro` must implement either \
63+
expansion(of:providingMembersOf:in:) or \
64+
expansion(of:providingMembersOf:conformingTo:in:)
65+
"""
66+
}
67+
}
68+
5769
public extension MemberMacro {
5870
/// Default implementation supplies no conformances.
5971
static func expansion(
6072
of node: AttributeSyntax,
6173
providingMembersOf declaration: some DeclGroupSyntax,
6274
in context: some MacroExpansionContext
6375
) throws -> [DeclSyntax] {
64-
return try expansion(of: node, providingMembersOf: declaration, conformingTo: [], in: context)
76+
throw UnimplementedExpansionMethodError()
6577
}
6678

6779
/// Default implementation that ignores the unhandled conformances.

0 commit comments

Comments
 (0)