From ac892b64da73c9ab5271ed1a98a349e87708d48c Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 8 Aug 2025 12:09:38 -0500 Subject: [PATCH 1/7] Emit a warning if a display name string is empty Fixes rdar://157603034 --- Sources/TestingMacros/CMakeLists.txt | 1 + .../StringLiteralExprSyntaxAdditions.swift | 30 +++++++++++++++++++ .../Support/AttributeDiscovery.swift | 8 +++++ .../Support/DiagnosticMessage.swift | 16 ++++++++++ .../TestDeclarationMacroTests.swift | 8 +++++ 5 files changed, 63 insertions(+) create mode 100644 Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift diff --git a/Sources/TestingMacros/CMakeLists.txt b/Sources/TestingMacros/CMakeLists.txt index effa782a9..64f825f5a 100644 --- a/Sources/TestingMacros/CMakeLists.txt +++ b/Sources/TestingMacros/CMakeLists.txt @@ -90,6 +90,7 @@ target_sources(TestingMacros PRIVATE Support/Additions/FunctionDeclSyntaxAdditions.swift Support/Additions/IntegerLiteralExprSyntaxAdditions.swift Support/Additions/MacroExpansionContextAdditions.swift + Support/Additions/StringLiteralExprSyntaxAdditions.swift Support/Additions/TokenSyntaxAdditions.swift Support/Additions/TriviaPieceAdditions.swift Support/Additions/TypeSyntaxProtocolAdditions.swift diff --git a/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift new file mode 100644 index 000000000..d11d7ad54 --- /dev/null +++ b/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift @@ -0,0 +1,30 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +import SwiftSyntax + +extension StringLiteralExprSyntax { + /// Whether or not this string literal expression is completely empty. + /// + /// The value of this property is `false` even if the literal contains one or + /// more interpolation segments whose evaluated expressions are an empty + /// string. The value is only `true` if this string literal contains only one + /// empty string segment. + var isEmptyLiteral: Bool { + guard segments.count == 1, + case let .stringSegment(stringSegment) = segments.first, + case let .stringSegment(text) = stringSegment.content.tokenKind + else { + return false + } + + return text.isEmpty + } +} diff --git a/Sources/TestingMacros/Support/AttributeDiscovery.swift b/Sources/TestingMacros/Support/AttributeDiscovery.swift index 3d95df294..afefd259e 100644 --- a/Sources/TestingMacros/Support/AttributeDiscovery.swift +++ b/Sources/TestingMacros/Support/AttributeDiscovery.swift @@ -149,6 +149,14 @@ struct AttributeInfo { } } + // If there was a display name but it's completely empty, emit a warning + // diagnostic since this can cause confusion isn't generally recommended. + // Note that this is only possible for string literal display names; the + // compiler enforces that raw identifiers must be non-empty. + if let displayName, displayName.isEmptyLiteral { + context.diagnose(.emptyDisplayName(displayName)) + } + // Remove leading "Self." expressions from the arguments of the attribute. // See _SelfRemover for more information. Rewriting a syntax tree discards // location information from the copy, so only invoke the rewriter if the diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index 80c3d9e1f..9aa4acdc2 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -643,6 +643,22 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { ) } + /// Create a diagnostic message stating that a string literal expression + /// passed as the display name to a `@Test` or `@Suite` attribute is empty + /// but should not be. + /// + /// - Parameters: + /// - displayNameExpr: The display name string literal expression. + /// + /// - Returns: A diagnostic message. + static func emptyDisplayName(_ displayNameExpr: StringLiteralExprSyntax) -> Self { + Self( + syntax: Syntax(displayNameExpr), + message: "Display name string should not be empty", + severity: .warning + ) + } + /// Create a diagnostic message stating that a declaration has two display /// names. /// diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index 47e2b5112..f36c7d3b3 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -304,6 +304,14 @@ struct TestDeclarationMacroTests { // .serialized on a non-parameterized test function "@Test(.serialized) func f() {}": "Trait '.serialized' has no effect when used with a non-parameterized test function", + + // empty display name string literal + #"@Test("") func f() {}"#: + "Display name string should not be empty", + ##"@Test(#""#) func f() {}"##: + "Display name string should not be empty", + #"@Suite("") struct S {}"#: + "Display name string should not be empty", ] ) func apiMisuseWarnings(input: String, expectedMessage: String) throws { From ba7214d5bd9d4fc6a06921a7edc765cd6f7d4c58 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 8 Aug 2025 22:04:04 -0500 Subject: [PATCH 2/7] Use representedLiteralValue instead --- Sources/TestingMacros/CMakeLists.txt | 1 - .../StringLiteralExprSyntaxAdditions.swift | 30 ------------------- .../Support/AttributeDiscovery.swift | 3 +- 3 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift diff --git a/Sources/TestingMacros/CMakeLists.txt b/Sources/TestingMacros/CMakeLists.txt index 64f825f5a..effa782a9 100644 --- a/Sources/TestingMacros/CMakeLists.txt +++ b/Sources/TestingMacros/CMakeLists.txt @@ -90,7 +90,6 @@ target_sources(TestingMacros PRIVATE Support/Additions/FunctionDeclSyntaxAdditions.swift Support/Additions/IntegerLiteralExprSyntaxAdditions.swift Support/Additions/MacroExpansionContextAdditions.swift - Support/Additions/StringLiteralExprSyntaxAdditions.swift Support/Additions/TokenSyntaxAdditions.swift Support/Additions/TriviaPieceAdditions.swift Support/Additions/TypeSyntaxProtocolAdditions.swift diff --git a/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift deleted file mode 100644 index d11d7ad54..000000000 --- a/Sources/TestingMacros/Support/Additions/StringLiteralExprSyntaxAdditions.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -import SwiftSyntax - -extension StringLiteralExprSyntax { - /// Whether or not this string literal expression is completely empty. - /// - /// The value of this property is `false` even if the literal contains one or - /// more interpolation segments whose evaluated expressions are an empty - /// string. The value is only `true` if this string literal contains only one - /// empty string segment. - var isEmptyLiteral: Bool { - guard segments.count == 1, - case let .stringSegment(stringSegment) = segments.first, - case let .stringSegment(text) = stringSegment.content.tokenKind - else { - return false - } - - return text.isEmpty - } -} diff --git a/Sources/TestingMacros/Support/AttributeDiscovery.swift b/Sources/TestingMacros/Support/AttributeDiscovery.swift index afefd259e..19a660a47 100644 --- a/Sources/TestingMacros/Support/AttributeDiscovery.swift +++ b/Sources/TestingMacros/Support/AttributeDiscovery.swift @@ -11,6 +11,7 @@ import SwiftSyntax import SwiftSyntaxBuilder import SwiftSyntaxMacros +import SwiftParser /// A syntax rewriter that removes leading `Self.` tokens from member access /// expressions in a syntax tree. @@ -153,7 +154,7 @@ struct AttributeInfo { // diagnostic since this can cause confusion isn't generally recommended. // Note that this is only possible for string literal display names; the // compiler enforces that raw identifiers must be non-empty. - if let displayName, displayName.isEmptyLiteral { + if let displayName, displayName.representedLiteralValue?.isEmpty == true { context.diagnose(.emptyDisplayName(displayName)) } From 91b29e096ce09f79e66e8620811802801a0dde55 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 12 Aug 2025 11:32:19 -0500 Subject: [PATCH 3/7] Add removal fix-it --- .../Support/AttributeDiscovery.swift | 4 ++-- .../TestingMacros/Support/DiagnosticMessage.swift | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Sources/TestingMacros/Support/AttributeDiscovery.swift b/Sources/TestingMacros/Support/AttributeDiscovery.swift index 19a660a47..cdbff7d54 100644 --- a/Sources/TestingMacros/Support/AttributeDiscovery.swift +++ b/Sources/TestingMacros/Support/AttributeDiscovery.swift @@ -154,8 +154,8 @@ struct AttributeInfo { // diagnostic since this can cause confusion isn't generally recommended. // Note that this is only possible for string literal display names; the // compiler enforces that raw identifiers must be non-empty. - if let displayName, displayName.representedLiteralValue?.isEmpty == true { - context.diagnose(.emptyDisplayName(displayName)) + if let displayName, let displayNameArgument, displayName.representedLiteralValue?.isEmpty == true { + context.diagnose(.emptyDisplayName(displayName, fromArgument: displayNameArgument)) } // Remove leading "Self." expressions from the arguments of the attribute. diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index 9aa4acdc2..564b19abb 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -649,13 +649,24 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { /// /// - Parameters: /// - displayNameExpr: The display name string literal expression. + /// - argumentContainingDisplayName: The argument node containing the node + /// `displayNameExpr`. /// /// - Returns: A diagnostic message. - static func emptyDisplayName(_ displayNameExpr: StringLiteralExprSyntax) -> Self { + static func emptyDisplayName( + _ displayNameExpr: StringLiteralExprSyntax, + fromArgument argumentContainingDisplayName: LabeledExprListSyntax.Element + ) -> Self { Self( syntax: Syntax(displayNameExpr), message: "Display name string should not be empty", - severity: .warning + severity: .warning, + fixIts: [ + FixIt( + message: MacroExpansionFixItMessage("Remove '\(displayNameExpr.representedLiteralValue!)'"), + changes: [.replace(oldNode: Syntax(argumentContainingDisplayName), newNode: Syntax("" as ExprSyntax))] + ), + ] ) } From 4b48d71467b27b87e2029dcc3f6a24c71639608b Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 12 Aug 2025 11:43:17 -0500 Subject: [PATCH 4/7] Improve diagnostic message for consistency --- .../TestingMacros/Support/AttributeDiscovery.swift | 6 ++++-- .../TestingMacros/Support/DiagnosticMessage.swift | 12 ++++++++---- .../TestDeclarationMacroTests.swift | 6 +++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Sources/TestingMacros/Support/AttributeDiscovery.swift b/Sources/TestingMacros/Support/AttributeDiscovery.swift index cdbff7d54..9d7f70916 100644 --- a/Sources/TestingMacros/Support/AttributeDiscovery.swift +++ b/Sources/TestingMacros/Support/AttributeDiscovery.swift @@ -154,8 +154,10 @@ struct AttributeInfo { // diagnostic since this can cause confusion isn't generally recommended. // Note that this is only possible for string literal display names; the // compiler enforces that raw identifiers must be non-empty. - if let displayName, let displayNameArgument, displayName.representedLiteralValue?.isEmpty == true { - context.diagnose(.emptyDisplayName(displayName, fromArgument: displayNameArgument)) + if let namedDecl = declaration.asProtocol((any NamedDeclSyntax).self), + let displayName, let displayNameArgument, + displayName.representedLiteralValue?.isEmpty == true { + context.diagnose(.declaration(namedDecl, hasEmptyDisplayName: displayName, fromArgument: displayNameArgument, using: attribute)) } // Remove leading "Self." expressions from the arguments of the attribute. diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index 564b19abb..64a4a57ca 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -648,18 +648,22 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { /// but should not be. /// /// - Parameters: + /// - decl: The declaration that has an empty display name. /// - displayNameExpr: The display name string literal expression. /// - argumentContainingDisplayName: The argument node containing the node /// `displayNameExpr`. + /// - attribute: The `@Test` or `@Suite` attribute. /// /// - Returns: A diagnostic message. - static func emptyDisplayName( - _ displayNameExpr: StringLiteralExprSyntax, - fromArgument argumentContainingDisplayName: LabeledExprListSyntax.Element + static func declaration( + _ decl: some NamedDeclSyntax, + hasEmptyDisplayName displayNameExpr: StringLiteralExprSyntax, + fromArgument argumentContainingDisplayName: LabeledExprListSyntax.Element, + using attribute: AttributeSyntax ) -> Self { Self( syntax: Syntax(displayNameExpr), - message: "Display name string should not be empty", + message: "Attribute \(_macroName(attribute)) specifies an empty display name for \(_kindString(for: decl))", severity: .warning, fixIts: [ FixIt( diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index f36c7d3b3..13fcd4f8d 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -307,11 +307,11 @@ struct TestDeclarationMacroTests { // empty display name string literal #"@Test("") func f() {}"#: - "Display name string should not be empty", + "Attribute 'Test' specifies an empty display name for function", ##"@Test(#""#) func f() {}"##: - "Display name string should not be empty", + "Attribute 'Test' specifies an empty display name for function", #"@Suite("") struct S {}"#: - "Display name string should not be empty", + "Attribute 'Suite' specifies an empty display name for structure", ] ) func apiMisuseWarnings(input: String, expectedMessage: String) throws { From 8b2cdf27b164f49331fbb76558e23bc387789848 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 12 Aug 2025 15:47:03 -0500 Subject: [PATCH 5/7] Improve phrasing --- Sources/TestingMacros/Support/DiagnosticMessage.swift | 4 ++-- Tests/TestingMacrosTests/TestDeclarationMacroTests.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index 64a4a57ca..eccdf5422 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -663,11 +663,11 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { ) -> Self { Self( syntax: Syntax(displayNameExpr), - message: "Attribute \(_macroName(attribute)) specifies an empty display name for \(_kindString(for: decl))", + message: "Attribute \(_macroName(attribute)) specifies an empty display name for this \(_kindString(for: decl))", severity: .warning, fixIts: [ FixIt( - message: MacroExpansionFixItMessage("Remove '\(displayNameExpr.representedLiteralValue!)'"), + message: MacroExpansionFixItMessage("Remove display name argument"), changes: [.replace(oldNode: Syntax(argumentContainingDisplayName), newNode: Syntax("" as ExprSyntax))] ), ] diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index 13fcd4f8d..5fe5a6fde 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -307,11 +307,11 @@ struct TestDeclarationMacroTests { // empty display name string literal #"@Test("") func f() {}"#: - "Attribute 'Test' specifies an empty display name for function", + "Attribute 'Test' specifies an empty display name for this function", ##"@Test(#""#) func f() {}"##: - "Attribute 'Test' specifies an empty display name for function", + "Attribute 'Test' specifies an empty display name for this function", #"@Suite("") struct S {}"#: - "Attribute 'Suite' specifies an empty display name for structure", + "Attribute 'Suite' specifies an empty display name for this structure", ] ) func apiMisuseWarnings(input: String, expectedMessage: String) throws { From cb45a57452976282767117e9781392da30f012ef Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 13 Aug 2025 16:21:53 -0500 Subject: [PATCH 6/7] Add fix-it which fills in the display name --- ...EditorPlaceholderExprSyntaxAdditions.swift | 31 +++++++++++++++++-- .../Support/DiagnosticMessage.swift | 4 +++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Sources/TestingMacros/Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift index 9a0d31ab3..360b5260e 100644 --- a/Sources/TestingMacros/Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift +++ b/Sources/TestingMacros/Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift @@ -9,6 +9,7 @@ // import SwiftSyntax +import SwiftSyntaxBuilder extension EditorPlaceholderExprSyntax { /// Initialize an instance of this type with the given placeholder string and @@ -39,7 +40,7 @@ extension EditorPlaceholderExprSyntax { // Manually concatenate the string to avoid it being interpreted as a // placeholder when editing this file. - self.init(placeholder: .identifier("<#\(placeholderContent)#" + ">")) + self.init(placeholder: .identifier(_editorPlaceholder(containing: placeholderContent))) } /// Initialize an instance of this type with the given type, using that as the @@ -62,6 +63,32 @@ extension TypeSyntax { /// /// - Returns: A new `TypeSyntax` instance representing a placeholder. static func placeholder(_ placeholder: String) -> Self { - return Self(IdentifierTypeSyntax(name: .identifier("<#\(placeholder)#" + ">"))) + Self(IdentifierTypeSyntax(name: .identifier(_editorPlaceholder(containing: placeholder)))) } } + +extension StringLiteralExprSyntax { + /// Construct a string literal expression syntax node containing an editor + /// placeholder string. + /// + /// - Parameters + /// - placeholder: The placeholder string, not including surrounding angle + /// brackets or pound characters. + init(placeholder: String) { + self.init(content: _editorPlaceholder(containing: placeholder)) + } +} + +/// Format a source editor placeholder string with the specified content. +/// +/// - Parameters: +/// - content: The placeholder string, not including surrounding angle +/// brackets or pound characters +/// +/// - Returns: A fully-formatted formatted editor placeholder string, including +/// necessary surrounding punctuation. +private func _editorPlaceholder(containing content: String) -> String { + // Manually concatenate the string to avoid it being interpreted as a + // placeholder when editing this file. + "<#\(content)#" + ">" +} diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index eccdf5422..212e561ce 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -670,6 +670,10 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { message: MacroExpansionFixItMessage("Remove display name argument"), changes: [.replace(oldNode: Syntax(argumentContainingDisplayName), newNode: Syntax("" as ExprSyntax))] ), + FixIt( + message: MacroExpansionFixItMessage("Add display name"), + changes: [.replace(oldNode: Syntax(argumentContainingDisplayName), newNode: Syntax(StringLiteralExprSyntax(placeholder: "display name")))] + ), ] ) } From 8ab1a4cd272a702f1a8092e7917af4c6f5b13c55 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 13 Aug 2025 16:25:47 -0500 Subject: [PATCH 7/7] Switch warning severity to error --- .../Support/AttributeDiscovery.swift | 8 ++++---- .../Support/DiagnosticMessage.swift | 2 +- .../TestDeclarationMacroTests.swift | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/TestingMacros/Support/AttributeDiscovery.swift b/Sources/TestingMacros/Support/AttributeDiscovery.swift index 9d7f70916..fdf4b7e93 100644 --- a/Sources/TestingMacros/Support/AttributeDiscovery.swift +++ b/Sources/TestingMacros/Support/AttributeDiscovery.swift @@ -150,10 +150,10 @@ struct AttributeInfo { } } - // If there was a display name but it's completely empty, emit a warning - // diagnostic since this can cause confusion isn't generally recommended. - // Note that this is only possible for string literal display names; the - // compiler enforces that raw identifiers must be non-empty. + // If there was a display name but it's completely empty, emit a diagnostic + // since this can cause confusion isn't generally recommended. Note that + // this is only possible for string literal display names; the compiler + // enforces that raw identifiers must be non-empty. if let namedDecl = declaration.asProtocol((any NamedDeclSyntax).self), let displayName, let displayNameArgument, displayName.representedLiteralValue?.isEmpty == true { diff --git a/Sources/TestingMacros/Support/DiagnosticMessage.swift b/Sources/TestingMacros/Support/DiagnosticMessage.swift index 212e561ce..7d7ee1f31 100644 --- a/Sources/TestingMacros/Support/DiagnosticMessage.swift +++ b/Sources/TestingMacros/Support/DiagnosticMessage.swift @@ -664,7 +664,7 @@ struct DiagnosticMessage: SwiftDiagnostics.DiagnosticMessage { Self( syntax: Syntax(displayNameExpr), message: "Attribute \(_macroName(attribute)) specifies an empty display name for this \(_kindString(for: decl))", - severity: .warning, + severity: .error, fixIts: [ FixIt( message: MacroExpansionFixItMessage("Remove display name argument"), diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index 5fe5a6fde..9ac9c6264 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -149,6 +149,14 @@ struct TestDeclarationMacroTests { "Attribute 'Test' cannot be applied to a function within structure 'S' because its conformance to 'Escapable' has been suppressed", "struct S: ~(Escapable) { @Test func f() {} }": "Attribute 'Test' cannot be applied to a function within structure 'S' because its conformance to 'Escapable' has been suppressed", + + // empty display name string literal + #"@Test("") func f() {}"#: + "Attribute 'Test' specifies an empty display name for this function", + ##"@Test(#""#) func f() {}"##: + "Attribute 'Test' specifies an empty display name for this function", + #"@Suite("") struct S {}"#: + "Attribute 'Suite' specifies an empty display name for this structure", ] ) func apiMisuseErrors(input: String, expectedMessage: String) throws { @@ -304,14 +312,6 @@ struct TestDeclarationMacroTests { // .serialized on a non-parameterized test function "@Test(.serialized) func f() {}": "Trait '.serialized' has no effect when used with a non-parameterized test function", - - // empty display name string literal - #"@Test("") func f() {}"#: - "Attribute 'Test' specifies an empty display name for this function", - ##"@Test(#""#) func f() {}"##: - "Attribute 'Test' specifies an empty display name for this function", - #"@Suite("") struct S {}"#: - "Attribute 'Suite' specifies an empty display name for this structure", ] ) func apiMisuseWarnings(input: String, expectedMessage: String) throws {