Skip to content

Commit 1555299

Browse files
authored
Merge pull request #1838 from rintaro/macros-macrorole-legacydeclaration
[Macros] Accept "freeStandingDeclaration" as 'MacroRole.declaration'
2 parents dbd3e78 + 7d864db commit 1555299

File tree

4 files changed

+205
-13
lines changed

4 files changed

+205
-13
lines changed

Examples/Sources/ExamplePlugin/ExamplePlugin.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import SwiftSyntaxMacros
55
struct ThePlugin: CompilerPlugin {
66
var providingMacros: [Macro.Type] = [
77
EchoExpressionMacro.self,
8+
FuncUniqueMacro.self,
89
MetadataMacro.self,
10+
PeerValueWithSuffixNameMacro.self,
11+
MemberDeprecatedMacro.self,
12+
EquatableConformanceMacro.self,
13+
DidSetPrintMacro.self,
14+
PrintAnyMacro.self,
915
]
1016
}

Examples/Sources/ExamplePlugin/Macros.swift

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ struct EchoExpressionMacro: ExpressionMacro {
1616
}
1717
}
1818

19+
/// Func With unique name.
20+
struct FuncUniqueMacro: DeclarationMacro {
21+
static func expansion(
22+
of node: some FreestandingMacroExpansionSyntax,
23+
in context: some MacroExpansionContext
24+
) throws -> [DeclSyntax] {
25+
let name = context.makeUniqueName("unique")
26+
return ["func \(name)() {}"]
27+
}
28+
}
29+
1930
/// Add a static property `__metadata__`.
2031
struct MetadataMacro: MemberMacro {
2132
static func expansion<
@@ -26,14 +37,81 @@ struct MetadataMacro: MemberMacro {
2637
providingMembersOf declaration: Declaration,
2738
in context: Context
2839
) throws -> [DeclSyntax] {
29-
guard let cls = declaration.as(ClassDeclSyntax.self) else {
40+
guard let type = declaration.asProtocol(IdentifiedDeclSyntax.self) else {
3041
return []
3142
}
32-
let className = cls.identifier.trimmedDescription
43+
let typeName = type.identifier.trimmedDescription
3344
return [
3445
"""
35-
static var __metadata__: [String: String] { ["name": "\(raw: className)"] }
46+
static var __metadata__: [String: String] { ["name": "\(raw: typeName)"] }
3647
"""
3748
]
3849
}
3950
}
51+
52+
/// Peer 'var' with the name suffixed with '_peer'.
53+
struct PeerValueWithSuffixNameMacro: PeerMacro {
54+
static func expansion(
55+
of node: AttributeSyntax,
56+
providingPeersOf declaration: some DeclSyntaxProtocol,
57+
in context: some MacroExpansionContext
58+
) throws -> [DeclSyntax] {
59+
guard let identified = declaration.asProtocol(IdentifiedDeclSyntax.self) else {
60+
return []
61+
}
62+
return ["var \(raw: identified.identifier.text)_peer: Int { 1 }"]
63+
}
64+
}
65+
66+
/// Add '@available(*, deprecated)' to members.
67+
struct MemberDeprecatedMacro: MemberAttributeMacro {
68+
static func expansion(
69+
of node: SwiftSyntax.AttributeSyntax,
70+
attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
71+
providingAttributesFor member: some SwiftSyntax.DeclSyntaxProtocol,
72+
in context: some SwiftSyntaxMacros.MacroExpansionContext
73+
) throws -> [SwiftSyntax.AttributeSyntax] {
74+
return ["@available(*, deprecated)"]
75+
}
76+
}
77+
78+
/// Add 'Equatable' conformance.
79+
struct EquatableConformanceMacro: ConformanceMacro {
80+
static func expansion(
81+
of node: AttributeSyntax,
82+
providingConformancesOf declaration: some DeclGroupSyntax,
83+
in context: some MacroExpansionContext
84+
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
85+
return [("Equatable", nil)]
86+
}
87+
}
88+
89+
/// Add 'didSet' printing the new value.
90+
struct DidSetPrintMacro: AccessorMacro {
91+
static func expansion(
92+
of node: AttributeSyntax,
93+
providingAccessorsOf declaration: some DeclSyntaxProtocol,
94+
in context: some MacroExpansionContext
95+
) throws -> [AccessorDeclSyntax] {
96+
guard
97+
let identifier = declaration.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier
98+
else {
99+
return []
100+
}
101+
102+
return ["didSet { print(\(identifier)) }"]
103+
}
104+
}
105+
106+
/// 'print(<arg>)'.
107+
struct PrintAnyMacro: CodeItemMacro {
108+
static func expansion(
109+
of node: some FreestandingMacroExpansionSyntax,
110+
in context: some MacroExpansionContext
111+
) throws -> [CodeBlockItemSyntax] {
112+
guard let expr = node.argumentList.first?.expression else {
113+
return []
114+
}
115+
return ["print(\(expr))"]
116+
}
117+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Old compiler might send '.declaration' as "freeStandingDeclaration".
14+
extension PluginMessage.MacroRole {
15+
init(from decoder: Decoder) throws {
16+
let stringValue = try decoder.singleValueContainer().decode(String.self)
17+
if let role = Self(rawValue: stringValue) {
18+
self = role
19+
return
20+
}
21+
// Accept "freeStandingDeclaration" as '.declaration'.
22+
if stringValue == "freeStandingDeclaration" {
23+
self = Self.declaration
24+
return
25+
}
26+
throw DecodingError.dataCorrupted(
27+
DecodingError.Context(
28+
codingPath: decoder.codingPath,
29+
debugDescription: "Invalid string value for MacroRole: \(stringValue)"
30+
)
31+
)
32+
}
33+
}

lit_tests/compiler_plugin_basic.swift

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,105 @@
22
//
33
// RUN: %empty-directory(%t)
44
//
5-
// RUN: %swift-frontend -typecheck -swift-version 5 \
6-
// RUN: -enable-experimental-feature Macros \
5+
// RUN: %swift-frontend -typecheck -verify -swift-version 5 \
6+
// RUN: -enable-experimental-feature CodeItemMacros \
77
// RUN: -dump-macro-expansions \
88
// RUN: -load-plugin-executable %examples_bin_path/ExamplePlugin#ExamplePlugin \
9-
// RUN -module-name MyApp \
9+
// RUN: -parse-as-library \
10+
// RUN: -module-name TestApp \
1011
// RUN: %s 2>&1 | tee %t/expansions-dump.txt
1112
//
1213
// RUN: %FileCheck %s < %t/expansions-dump.txt
1314

1415
@freestanding(expression)
1516
macro echo<T>(_: T) -> T = #externalMacro(module: "ExamplePlugin", type: "EchoExpressionMacro")
1617

18+
@freestanding(declaration)
19+
macro funcUnique() = #externalMacro(module: "ExamplePlugin", type: "FuncUniqueMacro")
20+
21+
@freestanding(codeItem)
22+
macro printAny(_: Any) = #externalMacro(module: "ExamplePlugin", type: "PrintAnyMacro")
23+
1724
@attached(member, names: named(__metadata__))
1825
macro Metadata() = #externalMacro(module: "ExamplePlugin", type: "MetadataMacro")
1926

27+
@attached(peer, names: suffixed(_peer))
28+
macro PeerWithSuffix() = #externalMacro(module: "ExamplePlugin", type: "PeerValueWithSuffixNameMacro")
29+
30+
@attached(memberAttribute)
31+
macro MemberDeprecated() = #externalMacro(module: "ExamplePlugin", type: "MemberDeprecatedMacro")
32+
33+
@attached(conformance)
34+
macro Equatable() = #externalMacro(module: "ExamplePlugin", type: "EquatableConformanceMacro")
35+
36+
@attached(accessor)
37+
macro DidSetPrint() = #externalMacro(module: "ExamplePlugin", type: "DidSetPrintMacro")
38+
2039
@Metadata
21-
class MyClass {
40+
@MemberDeprecated
41+
@Equatable
42+
@PeerWithSuffix
43+
struct MyStruct {
44+
@DidSetPrint
2245
var value: Int = #echo(12)
46+
// expected-error@-1 {{expansion of macro 'DidSetPrint()' did not produce a non-observing accessor}}
47+
48+
func _test() {
49+
#printAny("test")
50+
}
2351
}
2452

25-
// For '@Metadata'
26-
// CHECK: {{^}}static var __metadata__: [String: String] {
27-
// CHECK-NEXT: {{^}} ["name": "MyClass"]
28-
// CHECK-NEXT: {{^}}}
53+
#funcUnique
54+
55+
// CHECK: @__swiftmacro_7TestApp8MyStruct14PeerWithSuffixfMp_.swift
56+
// CHECK-NEXT: ------------------------------
57+
// CHECK-NEXT: var MyStruct_peer: Int {
58+
// CHECK-NEXT: 1
59+
// CHECK-NEXT: }
60+
// CHECK-NEXT: ------------------------------
61+
62+
// CHECK: @__swiftmacro_7TestApp8MyStruct9EquatablefMc_.swift
63+
// CHECK-NEXT: ------------------------------
64+
// CHECK-NEXT: extension MyStruct : Equatable {}
65+
// CHECK-NEXT: ------------------------------
66+
67+
// CHECK: @__swiftmacro_7TestApp8MyStruct8MetadatafMm_.swift
68+
// CHECK-NEXT: ------------------------------
69+
// CHECK-NEXT: static var __metadata__: [String: String] {
70+
// CHECK-NEXT: ["name": "MyStruct"]
71+
// CHECK-NEXT: }
72+
// CHECK-NEXT: ------------------------------
73+
74+
// CHECK: @__swiftmacro_7TestApp8MyStructV5value16MemberDeprecatedfMr_.swift
75+
// CHECK-NEXT: ------------------------------
76+
// CHECK-NEXT: @available(*, deprecated)
77+
// CHECK-NEXT: ------------------------------
78+
79+
// CHECK: @__swiftmacro_7TestApp8MyStructV5_test16MemberDeprecatedfMr0_.swift
80+
// CHECK-NEXT: ------------------------------
81+
// CHECK-NEXT: @available(*, deprecated)
82+
// CHECK-NEXT: ------------------------------
83+
84+
// CHECK: @__swiftmacro_7TestApp8MyStructV5value11DidSetPrintfMa_.swift
85+
// CHECK-NEXT: ------------------------------
86+
// CHECK-NEXT: {
87+
// CHECK-NEXT: didSet {
88+
// CHECK-NEXT: print(value)
89+
// CHECK-NEXT: }
90+
// CHECK-NEXT: }
91+
// CHECK-NEXT: ------------------------------
92+
93+
// CHECK: @__swiftmacro_7TestApp8MyStructV4echofMf_.swift
94+
// CHECK-NEXT: ------------------------------
95+
// CHECK-NEXT: /* echo */12
96+
// CHECK-NEXT: ------------------------------
97+
98+
// CHECK: @__swiftmacro_7TestApp33_B5E4CA48BE2C4AA1BE7F954C809E362ALl10funcUniquefMf_.swift
99+
// CHECK-NEXT: ------------------------------
100+
// CHECK-NEXT: func $s7TestApp33_B5E4CA48BE2C4AA1BE7F954C809E362ALl10funcUniquefMf_6uniquefMu_() {
101+
// CHECK-NEXT: }
29102

30-
// For '#echo(12)'
31-
// CHECK: /* echo */12
103+
// CHECK: @__swiftmacro_7TestApp8MyStructV5_testyyF8printAnyfMf0_.swift
104+
// CHECK-NEXT: ------------------------------
105+
// CHECK-NEXT: print("test")
106+
// CHECK-NEXT: ------------------------------

0 commit comments

Comments
 (0)