@@ -88,37 +88,104 @@ func diagnoseIssuesWithTags(in traitExprs: [ExprSyntax], addedTo attribute: Attr
88
88
}
89
89
}
90
90
91
- #if canImport(SwiftSyntax600 )
92
- /// Diagnose issues with the lexical context containing a declaration.
91
+ /// Diagnose issues with a synthesized suite (one without an `@Suite` attribute )
92
+ /// containing a declaration.
93
93
///
94
94
/// - Parameters:
95
+ /// - lexicalContext: The single lexical context to inspect.
95
96
/// - decl: The declaration to inspect.
96
- /// - testAttribute: The `@Test` attribute applied to `decl`.
97
- /// - context: The macro context in which the expression is being parsed.
97
+ /// - attribute: The `@Test` or `@Suite` attribute applied to `decl`.
98
98
///
99
99
/// - Returns: An array of zero or more diagnostic messages related to the
100
100
/// lexical context containing `decl`.
101
+ ///
102
+ /// This function is also used by ``SuiteDeclarationMacro`` for a number of its
103
+ /// own diagnostics. The implementation substitutes different diagnostic
104
+ /// messages when `suiteDecl` and `decl` are the same syntax node on the
105
+ /// assumption that a suite is self-diagnosing.
101
106
func diagnoseIssuesWithLexicalContext(
107
+ _ lexicalContext: some SyntaxProtocol ,
102
108
containing decl: some DeclSyntaxProtocol ,
103
- attribute: AttributeSyntax ,
104
- in context: some MacroExpansionContext
109
+ attribute: AttributeSyntax
105
110
) -> [ DiagnosticMessage ] {
106
111
var diagnostics = [ DiagnosticMessage] ( )
107
112
108
- for lexicalContext in context. lexicalContext {
109
- if !lexicalContext. isProtocol ( ( any DeclGroupSyntax ) . self) {
113
+ // Functions, closures, etc. are not supported as enclosing lexical contexts.
114
+ guard let lexicalContext = lexicalContext. asProtocol ( ( any DeclGroupSyntax ) . self) else {
115
+ if Syntax ( lexicalContext) == Syntax ( decl) {
116
+ diagnostics. append ( . attributeNotSupported( attribute, on: lexicalContext) )
117
+ } else {
110
118
diagnostics. append ( . containingNodeUnsupported( lexicalContext, whenUsing: attribute, on: decl) )
111
119
}
120
+ return diagnostics
121
+ }
122
+
123
+ // Generic suites are not supported.
124
+ if let genericClause = lexicalContext. asProtocol ( ( any WithGenericParametersSyntax ) . self) ? . genericParameterClause {
125
+ diagnostics. append ( . genericDeclarationNotSupported( decl, whenUsing: attribute, becauseOf: genericClause) )
126
+ } else if let whereClause = lexicalContext. genericWhereClause {
127
+ diagnostics. append ( . genericDeclarationNotSupported( decl, whenUsing: attribute, becauseOf: whereClause) )
128
+ }
112
129
113
- if let classDecl = lexicalContext. as ( ClassDeclSyntax . self) {
114
- if !classDecl. modifiers. lazy. map ( \. name. tokenKind) . contains ( . keyword( . final) ) {
130
+ // Suites that are classes must be final.
131
+ if let classDecl = lexicalContext. as ( ClassDeclSyntax . self) {
132
+ if !classDecl. modifiers. lazy. map ( \. name. tokenKind) . contains ( . keyword( . final) ) {
133
+ if Syntax ( classDecl) == Syntax ( decl) {
134
+ diagnostics. append ( . nonFinalClassNotSupported( classDecl, whenUsing: attribute) )
135
+ } else {
115
136
diagnostics. append ( . containingNodeUnsupported( classDecl, whenUsing: attribute, on: decl) )
116
137
}
117
- } else if let protocolDecl = lexicalContext. as ( ProtocolDeclSyntax . self) {
138
+ }
139
+ }
140
+
141
+ // Suites cannot be protocols (there's nowhere to put most of the
142
+ // declarations we generate.)
143
+ if let protocolDecl = lexicalContext. as ( ProtocolDeclSyntax . self) {
144
+ if Syntax ( protocolDecl) == Syntax ( decl) {
145
+ diagnostics. append ( . attributeNotSupported( attribute, on: protocolDecl) )
146
+ } else {
118
147
diagnostics. append ( . containingNodeUnsupported( protocolDecl, whenUsing: attribute, on: decl) )
119
148
}
120
149
}
121
150
151
+ // Check other attributes on the declaration. Note that it should be
152
+ // impossible to reach this point if the declaration can't have attributes.
153
+ if let attributedDecl = lexicalContext. asProtocol ( ( any WithAttributesSyntax ) . self) {
154
+ // Availability is not supported on suites (we need semantic availability
155
+ // to correctly understand the availability of a suite.)
156
+ let availabilityAttributes = attributedDecl. availabilityAttributes
157
+ if !availabilityAttributes. isEmpty {
158
+ // Diagnose all @available attributes.
159
+ for availabilityAttribute in availabilityAttributes {
160
+ diagnostics. append ( . availabilityAttributeNotSupported( availabilityAttribute, on: decl, whenUsing: attribute) )
161
+ }
162
+ } else if let noasyncAttribute = attributedDecl. noasyncAttribute {
163
+ // No @available attributes, but we do have an @_unavailableFromAsync
164
+ // attribute and we still need to diagnose that.
165
+ diagnostics. append ( . availabilityAttributeNotSupported( noasyncAttribute, on: decl, whenUsing: attribute) )
166
+ }
167
+ }
168
+
122
169
return diagnostics
123
170
}
171
+
172
+ #if canImport(SwiftSyntax600)
173
+ /// Diagnose issues with the lexical context containing a declaration.
174
+ ///
175
+ /// - Parameters:
176
+ /// - decl: The declaration to inspect.
177
+ /// - attribute: The `@Test` or `@Suite` attribute applied to `decl`.
178
+ /// - context: The macro context in which the expression is being parsed.
179
+ ///
180
+ /// - Returns: An array of zero or more diagnostic messages related to the
181
+ /// lexical context containing `decl`.
182
+ func diagnoseIssuesWithLexicalContext(
183
+ _ lexicalContext: [ Syntax ] ,
184
+ containing decl: some DeclSyntaxProtocol ,
185
+ attribute: AttributeSyntax
186
+ ) -> [ DiagnosticMessage ] {
187
+ lexicalContext. lazy
188
+ . map { diagnoseIssuesWithLexicalContext ( $0, containing: decl, attribute: attribute) }
189
+ . reduce ( into: [ ] , += )
190
+ }
124
191
#endif
0 commit comments