From 2f9ac7b8234b7fa38d6f95203119bbbe0e1fd73e Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 7 Jul 2025 12:24:24 -0400 Subject: [PATCH] Disallow the `@Test` attribute on operator declarations. (#1205) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR prevents `@Test` from being applied to an operator declaration such as: ```swift @Test(arguments: ...) static func +(lhs: A, rhs: B) { ... } ``` Now, the following error will be emitted by the compiler: > 🛑 Attribute 'Test' cannot be applied to an operator Previously, applying `@Test` to an operator produced undefined/unstable effects. Resolves #1204. ### 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. --- .../Additions/FunctionDeclSyntaxAdditions.swift | 10 ++++++++++ Sources/TestingMacros/Support/DiagnosticMessage.swift | 6 +++++- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- .../TestingMacrosTests/TestDeclarationMacroTests.swift | 2 ++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Sources/TestingMacros/Support/Additions/FunctionDeclSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/FunctionDeclSyntaxAdditions.swift index 9b9378283..9b55bc157 100644 --- a/Sources/TestingMacros/Support/Additions/FunctionDeclSyntaxAdditions.swift +++ b/Sources/TestingMacros/Support/Additions/FunctionDeclSyntaxAdditions.swift @@ -34,6 +34,16 @@ extension FunctionDeclSyntax { .contains(.keyword(.nonisolated)) } + /// Whether or not this function declares an operator. + var isOperator: Bool { + switch name.tokenKind { + case .binaryOperator, .prefixOperator, .postfixOperator: + true + default: + false + } + } + /// The name of this function including parentheses, parameter labels, and /// colons. var completeName: DeclReferenceExprSyntax { diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index b7103bcc6..dd35b118d 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -93,7 +93,11 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { let result: (value: String, article: String) switch node.kind { case .functionDecl: - result = ("function", "a") + if node.cast(FunctionDeclSyntax.self).isOperator { + result = ("operator", "an") + } else { + result = ("function", "a") + } case .classDecl: result = ("class", "a") case .structDecl: diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 50ac690d2..ef156edd6 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -61,7 +61,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { } // The @Test attribute is only supported on function declarations. - guard let function = declaration.as(FunctionDeclSyntax.self) else { + guard let function = declaration.as(FunctionDeclSyntax.self), !function.isOperator else { diagnostics.append(.attributeNotSupported(testAttribute, on: declaration)) return false } diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index 6c04eb9eb..c53783b6a 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -67,6 +67,8 @@ struct TestDeclarationMacroTests { "Attribute 'Test' cannot be applied to a structure", "@Test enum E {}": "Attribute 'Test' cannot be applied to an enumeration", + "@Test func +() {}": + "Attribute 'Test' cannot be applied to an operator", // Availability "@available(*, unavailable) @Suite struct S {}":