Skip to content

Commit 9e04507

Browse files
authored
Explicitly forbid tests/suites/exit tests in extensions to InlineArray. (#1485)
This PR ensures we provide a reasonable diagnostic if `@Test`, `@Suite`, or `#expect(processExitsWith:)` is used within an extension to `InlineArray` that uses the sugared syntax `[n of T]`. We now emit a diagnostic like: > Attribute 'Suite' cannot be applied to a structure within a generic extension to type '[1 of T]' Without this change, compilation succeeds, but the test won't be runnable (this may change with #1408 though!) ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 7f9beab commit 9e04507

4 files changed

Lines changed: 34 additions & 7 deletions

File tree

Sources/TestingMacros/ConditionMacro.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,12 +632,20 @@ extension ExitTestConditionMacro {
632632
// Disallow exit tests in generic types and functions as they cannot be
633633
// correctly expanded due to the use of a nested type with static members.
634634
for lexicalContext in context.lexicalContext {
635-
if let lexicalContext = lexicalContext.asProtocol((any WithGenericParametersSyntax).self) {
636-
if let genericClause = lexicalContext.genericParameterClause {
635+
if let lexicalContext = lexicalContext.asProtocol((any DeclGroupSyntax).self) {
636+
if let genericClause = lexicalContext.asProtocol((any WithGenericParametersSyntax).self)?.genericParameterClause {
637637
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: genericClause, on: lexicalContext))
638638
} else if let whereClause = lexicalContext.genericWhereClause {
639639
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: whereClause, on: lexicalContext))
640-
} else if let functionDecl = lexicalContext.as(FunctionDeclSyntax.self) {
640+
} else if [.arrayType, .dictionaryType, .optionalType, .implicitlyUnwrappedOptionalType, .inlineArrayType].contains(lexicalContext.type.kind) {
641+
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: lexicalContext.type, on: lexicalContext))
642+
}
643+
} else if let functionDecl = lexicalContext.as(FunctionDeclSyntax.self) {
644+
if let genericClause = functionDecl.genericParameterClause {
645+
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: genericClause, on: functionDecl))
646+
} else if let whereClause = functionDecl.genericWhereClause {
647+
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: whereClause, on: functionDecl))
648+
} else {
641649
for parameter in functionDecl.signature.parameterClause.parameters {
642650
if parameter.type.isSome {
643651
diagnostics.append(.expressionMacroUnsupported(macro, inGenericContextBecauseOf: parameter, on: functionDecl))

Sources/TestingMacros/Support/DiagnosticMessage+Diagnosing.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,7 @@ func diagnoseIssuesWithLexicalContext(
189189
diagnostics.append(.genericDeclarationNotSupported(decl, whenUsing: attribute, becauseOf: genericClause, on: lexicalContext))
190190
} else if let whereClause = lexicalContext.genericWhereClause {
191191
diagnostics.append(.genericDeclarationNotSupported(decl, whenUsing: attribute, becauseOf: whereClause, on: lexicalContext))
192-
} else if [.arrayType, .dictionaryType, .optionalType, .implicitlyUnwrappedOptionalType].contains(lexicalContext.type.kind) {
193-
// These types are all syntactic sugar over generic types (Array<T>,
194-
// Dictionary<T>, and Optional<T>) and are just as unsupported. T! is
195-
// unsupported in this position, but it's still forbidden so don't even try!
192+
} else if [.arrayType, .dictionaryType, .optionalType, .implicitlyUnwrappedOptionalType, .inlineArrayType].contains(lexicalContext.type.kind) {
196193
diagnostics.append(.genericDeclarationNotSupported(decl, whenUsing: attribute, becauseOf: lexicalContext.type, on: lexicalContext))
197194
}
198195

Tests/TestingMacrosTests/ConditionMacroTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,26 @@ struct ConditionMacroTests {
438438

439439
@Test("#expect(processExitsWith:) diagnostics",
440440
arguments: [
441+
"struct S<T> { func f() { #expectExitTest(processExitsWith: x) {} } }":
442+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within generic structure 'S'",
443+
"extension S where T: U { func f() { #expectExitTest(processExitsWith: x) {} } }":
444+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
445+
"extension [T] { func f() { #expectExitTest(processExitsWith: x) {} } }":
446+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
447+
"extension [T:U] { func f() { #expectExitTest(processExitsWith: x) {} } }":
448+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
449+
"extension T? { func f() { #expectExitTest(processExitsWith: x) {} } }":
450+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
451+
"extension T! { func f() { #expectExitTest(processExitsWith: x) {} } }":
452+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
453+
"extension [1 of T] { func f() { #expectExitTest(processExitsWith: x) {} } }":
454+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within a generic declaration",
441455
"func f<T>() { #expectExitTest(processExitsWith: x) {} }":
442456
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within generic function 'f()'",
457+
"func f() where T: U { #expectExitTest(processExitsWith: x) {} }":
458+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within generic function 'f()'",
459+
"func f(_: some T) { #expectExitTest(processExitsWith: x) {} }":
460+
"Cannot call macro ''#expectExitTest(processExitsWith:_:)'' within generic function 'f(_:)'",
443461
]
444462
)
445463
func exitTestDiagnostics(input: String, expectedMessage: String) throws {

Tests/TestingMacrosTests/TestDeclarationMacroTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ struct TestDeclarationMacroTests {
159159
"Attribute 'Test' cannot be applied to a function within a generic extension to type 'T!'",
160160
"extension T! { @Suite struct S {} }":
161161
"Attribute 'Suite' cannot be applied to a structure within a generic extension to type 'T!'",
162+
"extension [1 of T] { @Test func f() {} }":
163+
"Attribute 'Test' cannot be applied to a function within a generic extension to type '[1 of T]'",
164+
"extension [1 of T] { @Suite struct S {} }":
165+
"Attribute 'Suite' cannot be applied to a structure within a generic extension to type '[1 of T]'",
162166
]
163167
)
164168
func apiMisuseErrors(input: String, expectedMessage: String) throws {

0 commit comments

Comments
 (0)