From 55b23f2a7a608d03f68806efcbb16026d67dcfe6 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 31 Jan 2024 21:13:45 +0900 Subject: [PATCH 01/12] macros: prepare generic arguments for replacement in macros --- .../MacroReplacement.swift | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 5f89e7ece25..41a50a741dc 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -58,7 +58,7 @@ public enum MacroDefinition { /// defining macro. These subtrees will need to be replaced with the text of /// the corresponding argument to the macro, which can be accomplished with /// `MacroDeclSyntax.expandDefinition`. - case expansion(MacroExpansionExprSyntax, replacements: [Replacement]) + case expansion(MacroExpansionExprSyntax, replacements: [Replacement], genericReplacements: [GenericArgumentReplacement]) // FIXME: do we need to evolve this without breaking? I assume so? } extension MacroDefinition { @@ -70,11 +70,21 @@ extension MacroDefinition { /// The index of the parameter in the defining macro. public let parameterIndex: Int } + + /// A replacement that occurs as part of an expanded macro definition. + public struct GenericArgumentReplacement { + /// A reference to a parameter as it occurs in the macro expansion expression. + public let reference: GenericArgumentSyntax + + /// The index of the parameter in the defining macro. + public let parameterIndex: Int + } } fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { let macro: MacroDeclSyntax var replacements: [MacroDefinition.Replacement] = [] + var genericReplacements: [MacroDefinition.GenericArgumentReplacement] = [] var diagnostics: [Diagnostic] = [] init(macro: MacroDeclSyntax) { @@ -156,6 +166,45 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { return .visitChildren } + override func visit(_ node: GenericArgumentClauseSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + override func visit(_ node: GenericArgumentListSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + override func visit(_ node: GenericArgumentSyntax) -> SyntaxVisitorContinueKind { + let baseName = "\(node)" // FIXME: where to get the name from properly + guard let genericParameterClause = macro.genericParameterClause else { + return .skipChildren + } + + let matchedParameter = genericParameterClause.parameters.enumerated().first { (index, parameter) in +// guard let parameterName = parameter.parameterName else { +// return false +// } + + return true; // FIXME: what do we need to match here + } + + guard let (parameterIndex, _) = matchedParameter else { + // We have a reference to something that isn't a parameter of the macro. +// diagnostics.append( +// Diagnostic( +// node: Syntax(baseName), // FIXME: error should be about type argument +// message: MacroExpanderError.nonParameterReference(baseName) +// ) +// ) + + return .visitChildren + } + + genericReplacements.append(.init(reference: node, parameterIndex: parameterIndex)) + + return .visitChildren + } + override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { if let expr = node.as(ExprSyntax.self) { // We have an expression that is not one of the allowed forms, so @@ -230,7 +279,7 @@ extension MacroDeclSyntax { throw DiagnosticsError(diagnostics: visitor.diagnostics) } - return .expansion(definition, replacements: visitor.replacements) + return .expansion(definition, replacements: visitor.replacements, genericReplacements: visitor.genericReplacements) } } From 9ed1837ac5aec03e09b130001edd6f2410830a2a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 2 Feb 2024 11:43:31 +0900 Subject: [PATCH 02/12] release notes for the breaking change --- Release Notes/511.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Release Notes/511.md b/Release Notes/511.md index 7f751f7f8ed..d538cd5b11f 100644 --- a/Release Notes/511.md +++ b/Release Notes/511.md @@ -67,6 +67,11 @@ ## API-Incompatible Changes +- `MacroDefinition` used for expanding macros: + - Description: The `MacroDefinition/expansion` enum case used to have two values (`(MacroExpansionExprSyntax, replacements: [Replacement])`), has now gained another value in order to support generic argument replacements in macro expansions: `(MacroExpansionExprSyntax, replacements: [Replacement], genericReplacements: [GenericArgumentReplacement])` + - Pull request: https://github.com/apple/swift-syntax/pull/2450 + - Migration steps: Code which exhaustively checked over the enum should be changed to `case .expansion(let node, let replacements, let genericReplacements):`. Creating the `.extension` gained a compatibility shim, retaining the previous syntax source compatible (`return .expansion(node, replacements: [])`). + - Effect specifiers: - Description: The `unexpectedAfterThrowsSpecifier` node of the various effect specifiers has been removed. - Pull request: https://github.com/apple/swift-syntax/pull/2219 From 65d757d1e47802b823b5c5169222d27ca8f90a4c Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 2 Feb 2024 13:00:19 +0900 Subject: [PATCH 03/12] add test and complete impl with error handling --- .../MacroReplacement.swift | 59 +++++++++++++------ .../MacroReplacementTests.swift | 55 ++++++++++++++++- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 41a50a741dc..0da611f856e 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -18,6 +18,7 @@ enum MacroExpanderError: DiagnosticMessage { case undefined case definitionNotMacroExpansion case nonParameterReference(TokenSyntax) + case nonTypeReference(TypeSyntax) case nonLiteralOrParameter(ExprSyntax) var message: String { @@ -31,6 +32,9 @@ enum MacroExpanderError: DiagnosticMessage { case .nonParameterReference(let name): return "reference to value '\(name.text)' that is not a macro parameter in expansion" + case .nonTypeReference(let name): + return "reference to type '\(name)' that is not a macro type parameter in expansion" + case .nonLiteralOrParameter: return "only literals and macro parameters are permitted in expansion" } @@ -58,7 +62,14 @@ public enum MacroDefinition { /// defining macro. These subtrees will need to be replaced with the text of /// the corresponding argument to the macro, which can be accomplished with /// `MacroDeclSyntax.expandDefinition`. - case expansion(MacroExpansionExprSyntax, replacements: [Replacement], genericReplacements: [GenericArgumentReplacement]) // FIXME: do we need to evolve this without breaking? I assume so? + case expansion(MacroExpansionExprSyntax, replacements: [Replacement], genericReplacements: [GenericArgumentReplacement]) +} + +extension MacroDefinition { + /// Best effort compatibility shim, the case has gained additional parameters. + public func expansion(_ node: MacroExpansionExprSyntax, replacements: [Replacement]) -> Self { + .expansion(node, replacements: replacements, genericReplacements: []) + } } extension MacroDefinition { @@ -175,27 +186,23 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { } override func visit(_ node: GenericArgumentSyntax) -> SyntaxVisitorContinueKind { - let baseName = "\(node)" // FIXME: where to get the name from properly + let baseName = node.argument guard let genericParameterClause = macro.genericParameterClause else { return .skipChildren } let matchedParameter = genericParameterClause.parameters.enumerated().first { (index, parameter) in -// guard let parameterName = parameter.parameterName else { -// return false -// } - - return true; // FIXME: what do we need to match here + return parameter.name.text == "\(baseName)" } guard let (parameterIndex, _) = matchedParameter else { // We have a reference to something that isn't a parameter of the macro. -// diagnostics.append( -// Diagnostic( -// node: Syntax(baseName), // FIXME: error should be about type argument -// message: MacroExpanderError.nonParameterReference(baseName) -// ) -// ) + diagnostics.append( + Diagnostic( + node: Syntax(baseName), + message: MacroExpanderError.nonTypeReference(baseName) + ) + ) return .visitChildren } @@ -287,10 +294,14 @@ extension MacroDeclSyntax { /// uses of macro parameters with their corresponding arguments. private final class MacroExpansionRewriter: SyntaxRewriter { let parameterReplacements: [DeclReferenceExprSyntax: Int] + let genericParameterReplacements: [DeclReferenceExprSyntax: Int] let arguments: [ExprSyntax] - init(parameterReplacements: [DeclReferenceExprSyntax: Int], arguments: [ExprSyntax]) { + init(parameterReplacements: [DeclReferenceExprSyntax: Int], + genericReplacements: [DeclReferenceExprSyntax: Int], + arguments: [ExprSyntax]) { self.parameterReplacements = parameterReplacements + self.genericParameterReplacements = genericReplacements self.arguments = arguments super.init(viewMode: .sourceAccurate) } @@ -311,7 +322,8 @@ extension MacroDeclSyntax { private func expand( argumentList: LabeledExprListSyntax?, definition: MacroExpansionExprSyntax, - replacements: [MacroDefinition.Replacement] + replacements: [MacroDefinition.Replacement], + genericReplacements: [MacroDefinition.GenericArgumentReplacement] ) -> ExprSyntax { // FIXME: Do real call-argument matching between the argument list and the // macro parameter list, porting over from the compiler. @@ -326,6 +338,11 @@ extension MacroDeclSyntax { (replacement.reference, replacement.parameterIndex) } ), + genericReplacements: Dictionary( + uniqueKeysWithValues: replacements.map { replacement in + (replacement.reference, replacement.parameterIndex) + } + ), arguments: arguments ).visit(definition) } @@ -336,12 +353,14 @@ extension MacroDeclSyntax { public func expand( _ node: some FreestandingMacroExpansionSyntax, definition: MacroExpansionExprSyntax, - replacements: [MacroDefinition.Replacement] + replacements: [MacroDefinition.Replacement], + genericReplacements: [MacroDefinition.GenericArgumentReplacement] ) -> ExprSyntax { return expand( argumentList: node.arguments, definition: definition, - replacements: replacements + replacements: replacements, + genericReplacements: genericReplacements ) } @@ -351,7 +370,8 @@ extension MacroDeclSyntax { public func expand( _ node: AttributeSyntax, definition: MacroExpansionExprSyntax, - replacements: [MacroDefinition.Replacement] + replacements: [MacroDefinition.Replacement], + genericReplacements: [MacroDefinition.GenericArgumentReplacement] ) -> ExprSyntax { // Dig out the argument list. let argumentList: LabeledExprListSyntax? @@ -364,7 +384,8 @@ extension MacroDeclSyntax { return expand( argumentList: argumentList, definition: definition, - replacements: replacements + replacements: replacements, + genericReplacements: genericReplacements ) } } diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index d6a4d6e45a7..e022ff5d3f9 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -25,7 +25,7 @@ final class MacroReplacementTests: XCTestCase { """ let definition = try macro.as(MacroDeclSyntax.self)!.checkDefinition() - guard case let .expansion(_, replacements) = definition else { + guard case let .expansion(_, replacements, _) = definition else { XCTFail("not an expansion definition") fatalError() } @@ -96,7 +96,7 @@ final class MacroReplacementTests: XCTestCase { let macroDecl = macro.as(MacroDeclSyntax.self)! let definition = try macroDecl.checkDefinition() - guard case let .expansion(expansion, replacements) = definition else { + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") fatalError() } @@ -104,7 +104,8 @@ final class MacroReplacementTests: XCTestCase { let expandedSyntax = macroDecl.expand( use.as(MacroExpansionExprSyntax.self)!, definition: expansion, - replacements: replacements + replacements: replacements, + genericReplacements: genericReplacements ) assertStringsEqualWithDiff( expandedSyntax.description, @@ -113,4 +114,52 @@ final class MacroReplacementTests: XCTestCase { """ ) } + + func testMacroGenericArgumentExpansion() throws { + let macro: DeclSyntax = + """ + macro gen(a: A, b: B) = #otherMacro(first: a, second: b) + """ + + let use: ExprSyntax = + """ + #gen(a: 5, b: "Hello") + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + fatalError() + } + + guard let replacementA = genericReplacements.first else { + XCTFail("Expected generic replacement for A") + fatalError() + } + guard let replacementB = genericReplacements.dropFirst().first else { + XCTFail("Expected generic replacement for A") + fatalError() + } + XCTAssertEqual(genericReplacements.count, 2) + + XCTAssertEqual(replacementA.parameterIndex, 0) + XCTAssertEqual("\(replacementA.reference.argument)", "A") + + XCTAssertEqual(replacementB.parameterIndex, 1) + XCTAssertEqual("\(replacementB.reference.argument)", "B") + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacements, + genericReplacements: genericReplacements + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #otherMacro(first: 5, second: "Hello") + """ + ) + } } From 250b411a4a4ab80dd17da14bbac3949a129ff49b Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 10:39:46 +0900 Subject: [PATCH 04/12] Apply suggestions from code review Co-authored-by: Alex Hoppen --- Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift | 5 +++-- .../MacroReplacementTests.swift | 5 +---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 0da611f856e..4ff9d330be1 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -67,6 +67,7 @@ public enum MacroDefinition { extension MacroDefinition { /// Best effort compatibility shim, the case has gained additional parameters. + @available(*, deprecated, message: "Use the expansion case with three associated values instead") public func expansion(_ node: MacroExpansionExprSyntax, replacements: [Replacement]) -> Self { .expansion(node, replacements: replacements, genericReplacements: []) } @@ -191,7 +192,7 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { return .skipChildren } - let matchedParameter = genericParameterClause.parameters.enumerated().first { (index, parameter) in + let parameterIndex = genericParameterClause.parameters.firstIndex { (index, parameter) in return parameter.name.text == "\(baseName)" } @@ -371,7 +372,7 @@ extension MacroDeclSyntax { _ node: AttributeSyntax, definition: MacroExpansionExprSyntax, replacements: [MacroDefinition.Replacement], - genericReplacements: [MacroDefinition.GenericArgumentReplacement] + genericReplacements: [MacroDefinition.GenericArgumentReplacement] = [] ) -> ExprSyntax { // Dig out the argument list. let argumentList: LabeledExprListSyntax? diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index e022ff5d3f9..d17c5f43dcc 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -133,10 +133,7 @@ final class MacroReplacementTests: XCTestCase { fatalError() } - guard let replacementA = genericReplacements.first else { - XCTFail("Expected generic replacement for A") - fatalError() - } + let replacementA = try XCTUnwrap(genericReplacements.first) guard let replacementB = genericReplacements.dropFirst().first else { XCTFail("Expected generic replacement for A") fatalError() From 7aca598531a5c9eeda8ecb9f23cbfbc152ec85bc Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 10:53:49 +0900 Subject: [PATCH 05/12] add a test for not visiting a parameters generic arg --- .../MacroReplacementTests.swift | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index d17c5f43dcc..9833152e576 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -130,13 +130,16 @@ final class MacroReplacementTests: XCTestCase { let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") - fatalError() + return } let replacementA = try XCTUnwrap(genericReplacements.first) - guard let replacementB = genericReplacements.dropFirst().first else { XCTFail("Expected generic replacement for A") - fatalError() + return + } + guard let replacementB = genericReplacements.dropFirst().first else { + XCTFail("Expected generic replacement for B") + return } XCTAssertEqual(genericReplacements.count, 2) @@ -159,4 +162,38 @@ final class MacroReplacementTests: XCTestCase { """ ) } + + func testMacroGenericArgumentExpansion_notVisitGenericParameterArguments() throws { + let macro: DeclSyntax = + """ + macro gen(a: Array) = #otherMacro(first: a) + """ + + let use: ExprSyntax = + """ + #gen(a: [1, 2, 3]) + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + return + } + + XCTAssertEqual(genericReplacements.count, 0) + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacements, + genericReplacements: genericReplacements + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #otherMacro(first: [1, 2, 3]) + """ + ) + } } From 24a39b6053e2968b17fa9bc4e80134e902a225bf Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 11:01:45 +0900 Subject: [PATCH 06/12] cleanup baseName and use concrete .text instead --- .../MacroReplacement.swift | 12 +++-- .../MacroReplacementTests.swift | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 4ff9d330be1..4b1b42c3d7e 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -18,7 +18,7 @@ enum MacroExpanderError: DiagnosticMessage { case undefined case definitionNotMacroExpansion case nonParameterReference(TokenSyntax) - case nonTypeReference(TypeSyntax) + case nonTypeReference(TokenSyntax) case nonLiteralOrParameter(ExprSyntax) var message: String { @@ -187,13 +187,17 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { } override func visit(_ node: GenericArgumentSyntax) -> SyntaxVisitorContinueKind { - let baseName = node.argument + guard let baseName = node.argument.as(IdentifierTypeSyntax.self)?.name else { + // Handle error + return .visitChildren + } + guard let genericParameterClause = macro.genericParameterClause else { return .skipChildren } - let parameterIndex = genericParameterClause.parameters.firstIndex { (index, parameter) in - return parameter.name.text == "\(baseName)" + let matchedParameter = genericParameterClause.parameters.enumerated().first { (index, parameter) in + return parameter.name.text == baseName.text } guard let (parameterIndex, _) = matchedParameter else { diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index 9833152e576..fb241ebb0fa 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -163,6 +163,54 @@ final class MacroReplacementTests: XCTestCase { ) } + func testMacroGenericArgumentExpansion_ignoreTrivia() throws { + let macro: DeclSyntax = + """ + macro gen(a: A, b: B) = #otherMacro(first: a, second: b) + """ + + let use: ExprSyntax = + """ + #gen(a: 5, b: "Hello") + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + return + } + + guard let replacementA = genericReplacements.first else { + XCTFail("Expected generic replacement for A") + return + } + guard let replacementB = genericReplacements.dropFirst().first else { + XCTFail("Expected generic replacement for B") + return + } + XCTAssertEqual(genericReplacements.count, 2) + + XCTAssertEqual(replacementA.parameterIndex, 0) + XCTAssertEqual("\(replacementA.reference.argument)", "A") + + XCTAssertEqual(replacementB.parameterIndex, 1) + XCTAssertEqual("\(replacementB.reference.argument)", "B") + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacements, + genericReplacements: genericReplacements + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #otherMacro(first: 5, second: "Hello") + """ + ) + } + func testMacroGenericArgumentExpansion_notVisitGenericParameterArguments() throws { let macro: DeclSyntax = """ From 20e47a7ba0c9e9560a29023d5b84e71b27497236 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 11:06:30 +0900 Subject: [PATCH 07/12] formatting --- .../MacroReplacement.swift | 20 ++++++++++--------- .../MacroReplacementTests.swift | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 4b1b42c3d7e..4d56d3abeee 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -202,12 +202,12 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { guard let (parameterIndex, _) = matchedParameter else { // We have a reference to something that isn't a parameter of the macro. - diagnostics.append( - Diagnostic( - node: Syntax(baseName), - message: MacroExpanderError.nonTypeReference(baseName) - ) - ) + diagnostics.append( + Diagnostic( + node: Syntax(baseName), + message: MacroExpanderError.nonTypeReference(baseName) + ) + ) return .visitChildren } @@ -302,9 +302,11 @@ private final class MacroExpansionRewriter: SyntaxRewriter { let genericParameterReplacements: [DeclReferenceExprSyntax: Int] let arguments: [ExprSyntax] - init(parameterReplacements: [DeclReferenceExprSyntax: Int], - genericReplacements: [DeclReferenceExprSyntax: Int], - arguments: [ExprSyntax]) { + init( + parameterReplacements: [DeclReferenceExprSyntax: Int], + genericReplacements: [DeclReferenceExprSyntax: Int], + arguments: [ExprSyntax] + ) { self.parameterReplacements = parameterReplacements self.genericParameterReplacements = genericReplacements self.arguments = arguments diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index fb241ebb0fa..3c984f67808 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -140,7 +140,7 @@ final class MacroReplacementTests: XCTestCase { guard let replacementB = genericReplacements.dropFirst().first else { XCTFail("Expected generic replacement for B") return - } + } XCTAssertEqual(genericReplacements.count, 2) XCTAssertEqual(replacementA.parameterIndex, 0) From bac0b5e1859a8621873102b5e851656643f87fc0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 12:37:14 +0900 Subject: [PATCH 08/12] re-implement replacement logic inside MacroReplacement.swift --- Release Notes/511.md | 5 ++ .../MacroReplacement.swift | 73 ++++++++++++++----- .../MacroReplacementTests.swift | 61 +++++++++++----- 3 files changed, 102 insertions(+), 37 deletions(-) diff --git a/Release Notes/511.md b/Release Notes/511.md index d538cd5b11f..7bf1ed9f395 100644 --- a/Release Notes/511.md +++ b/Release Notes/511.md @@ -35,6 +35,11 @@ - `String.isValidIdentifier(for:)` - Description: `SwiftParser` adds an extension on `String` to check if it can be used as an identifier in a given context. - Pull Request: https://github.com/apple/swift-syntax/pull/2434 + +- `MacroDeclSyntax.expand` + - the `expand(argumentList:definition:replacements:)` method gains a new parameter 'genericReplacements:' that is defaulted to an empty array. + - The method's signature is now `expand(argumentList:definition:replacements:genericReplacements:)` + - Pull Request: https://github.com/apple/swift-syntax/pull/2450 - `SyntaxProtocol.asMacroLexicalContext()` and `allMacroLexicalContexts(enclosingSyntax:)` - Description: Produce the lexical context for a given syntax node (if it has one), or the entire stack of lexical contexts enclosing a syntax node, for use in macro expansion. diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 4d56d3abeee..c7b83a304a0 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -299,17 +299,21 @@ extension MacroDeclSyntax { /// uses of macro parameters with their corresponding arguments. private final class MacroExpansionRewriter: SyntaxRewriter { let parameterReplacements: [DeclReferenceExprSyntax: Int] - let genericParameterReplacements: [DeclReferenceExprSyntax: Int] let arguments: [ExprSyntax] + // let genericParameterReplacements: [DeclReferenceExprSyntax: Int] + let genericParameterReplacements: [GenericArgumentSyntax: Int] + let genericArguments: [TypeSyntax] init( parameterReplacements: [DeclReferenceExprSyntax: Int], - genericReplacements: [DeclReferenceExprSyntax: Int], - arguments: [ExprSyntax] + arguments: [ExprSyntax], + genericReplacements: [GenericArgumentSyntax: Int], + genericArguments: [TypeSyntax] ) { self.parameterReplacements = parameterReplacements - self.genericParameterReplacements = genericReplacements self.arguments = arguments + self.genericParameterReplacements = genericReplacements + self.genericArguments = genericArguments super.init(viewMode: .sourceAccurate) } @@ -321,6 +325,25 @@ private final class MacroExpansionRewriter: SyntaxRewriter { // Swap in the argument for this parameter return arguments[parameterIndex].trimmed } + + override func visit(_ node: GenericArgumentSyntax) -> GenericArgumentSyntax { + guard let parameterIndex = genericParameterReplacements[node] else { + return super.visit(node) + } + + // Swap in the argument for type parameter + return GenericArgumentSyntax( + leadingTrivia: node.leadingTrivia, + node.unexpectedBeforeArgument, + argument: genericArguments[parameterIndex].trimmed, + node.unexpectedBetweenArgumentAndTrailingComma, + trailingComma: node.trailingComma, + node.unexpectedAfterTrailingComma + // TODO: seems we're getting spurious trailing " " here, + // skipping trailing trivia for now + // trailingTrivia: node.trailingTrivia + ) + } } extension MacroDeclSyntax { @@ -328,30 +351,42 @@ extension MacroDeclSyntax { /// argument list. private func expand( argumentList: LabeledExprListSyntax?, + genericArgumentList: GenericArgumentClauseSyntax?, definition: MacroExpansionExprSyntax, replacements: [MacroDefinition.Replacement], - genericReplacements: [MacroDefinition.GenericArgumentReplacement] + genericReplacements: [MacroDefinition.GenericArgumentReplacement] = [] ) -> ExprSyntax { // FIXME: Do real call-argument matching between the argument list and the // macro parameter list, porting over from the compiler. + let parameterReplacements = Dictionary( + uniqueKeysWithValues: replacements.map { replacement in + (replacement.reference, replacement.parameterIndex) + } + ) let arguments: [ExprSyntax] = argumentList?.map { element in element.expression } ?? [] - return MacroExpansionRewriter( - parameterReplacements: Dictionary( - uniqueKeysWithValues: replacements.map { replacement in - (replacement.reference, replacement.parameterIndex) - } - ), - genericReplacements: Dictionary( - uniqueKeysWithValues: replacements.map { replacement in - (replacement.reference, replacement.parameterIndex) - } - ), - arguments: arguments - ).visit(definition) + + let genericReplacements = Dictionary( + uniqueKeysWithValues: genericReplacements.map { replacement in + (replacement.reference, replacement.parameterIndex) + } + ) + let genericArguments: [TypeSyntax] = + genericArgumentList?.arguments.map { element in + element.argument + } ?? [] + + + let rewriter: MacroExpansionRewriter = MacroExpansionRewriter( + parameterReplacements: parameterReplacements, + arguments: arguments, + genericReplacements: genericReplacements, + genericArguments: genericArguments + ) + return rewriter.visit(definition) } /// Given a freestanding macro expansion syntax node that references this @@ -365,6 +400,7 @@ extension MacroDeclSyntax { ) -> ExprSyntax { return expand( argumentList: node.arguments, + genericArgumentList: node.genericArgumentClause, definition: definition, replacements: replacements, genericReplacements: genericReplacements @@ -390,6 +426,7 @@ extension MacroDeclSyntax { return expand( argumentList: argumentList, + genericArgumentList: .init(arguments: []), definition: definition, replacements: replacements, genericReplacements: genericReplacements diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index 3c984f67808..b43fbc3f33e 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -115,7 +115,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion() throws { + func testMacroGenericArgumentExpansion_base() throws { let macro: DeclSyntax = """ macro gen(a: A, b: B) = #otherMacro(first: a, second: b) @@ -132,15 +132,10 @@ final class MacroReplacementTests: XCTestCase { XCTFail("not a normal expansion") return } - + let replacementA = try XCTUnwrap(genericReplacements.first) - XCTFail("Expected generic replacement for A") - return - } - guard let replacementB = genericReplacements.dropFirst().first else { - XCTFail("Expected generic replacement for B") - return - } + let replacementB = try XCTUnwrap(genericReplacements.dropFirst().first) + XCTAssertEqual(genericReplacements.count, 2) XCTAssertEqual(replacementA.parameterIndex, 0) @@ -158,7 +153,7 @@ final class MacroReplacementTests: XCTestCase { assertStringsEqualWithDiff( expandedSyntax.description, """ - #otherMacro(first: 5, second: "Hello") + #otherMacro(first: 5, second: "Hello") """ ) } @@ -181,14 +176,8 @@ final class MacroReplacementTests: XCTestCase { return } - guard let replacementA = genericReplacements.first else { - XCTFail("Expected generic replacement for A") - return - } - guard let replacementB = genericReplacements.dropFirst().first else { - XCTFail("Expected generic replacement for B") - return - } + let replacementA = try XCTUnwrap(genericReplacements.first) + let replacementB = try XCTUnwrap(genericReplacements.dropFirst().first) XCTAssertEqual(genericReplacements.count, 2) XCTAssertEqual(replacementA.parameterIndex, 0) @@ -206,7 +195,7 @@ final class MacroReplacementTests: XCTestCase { assertStringsEqualWithDiff( expandedSyntax.description, """ - #otherMacro(first: 5, second: "Hello") + #otherMacro(first: 5, second: "Hello") """ ) } @@ -244,4 +233,38 @@ final class MacroReplacementTests: XCTestCase { """ ) } + + func testMacroGenericArgumentExpansion_replaceInner() throws { + let macro: DeclSyntax = + """ + macro gen(a: Array) = #otherMacro(first: a) + """ + + let use: ExprSyntax = + """ + #gen(a: [1, 2, 3]) + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + return + } + + XCTAssertEqual(genericReplacements.count, 0) + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacements, + genericReplacements: genericReplacements + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #otherMacro(first: [1, 2, 3]) + """ + ) + } } From ff225ed1b6df34e912db4b213513c57b6a75a394 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Feb 2024 13:40:15 +0900 Subject: [PATCH 09/12] improve generics tests; prevent out of bounds access formatting --- .../MacroReplacement.swift | 22 ++++++++++--------- .../MacroReplacementTests.swift | 10 ++++----- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index c7b83a304a0..be8d77c934a 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -331,6 +331,10 @@ private final class MacroExpansionRewriter: SyntaxRewriter { return super.visit(node) } + guard parameterIndex < genericArguments.count else { + return super.visit(node) + } + // Swap in the argument for type parameter return GenericArgumentSyntax( leadingTrivia: node.leadingTrivia, @@ -339,9 +343,9 @@ private final class MacroExpansionRewriter: SyntaxRewriter { node.unexpectedBetweenArgumentAndTrailingComma, trailingComma: node.trailingComma, node.unexpectedAfterTrailingComma - // TODO: seems we're getting spurious trailing " " here, - // skipping trailing trivia for now - // trailingTrivia: node.trailingTrivia + // TODO: seems we're getting spurious trailing " " here, + // skipping trailing trivia for now + // trailingTrivia: node.trailingTrivia ) } } @@ -359,27 +363,25 @@ extension MacroDeclSyntax { // FIXME: Do real call-argument matching between the argument list and the // macro parameter list, porting over from the compiler. let parameterReplacements = Dictionary( - uniqueKeysWithValues: replacements.map { replacement in - (replacement.reference, replacement.parameterIndex) - } - ) + uniqueKeysWithValues: replacements.map { replacement in + (replacement.reference, replacement.parameterIndex) + } + ) let arguments: [ExprSyntax] = argumentList?.map { element in element.expression } ?? [] - let genericReplacements = Dictionary( uniqueKeysWithValues: genericReplacements.map { replacement in (replacement.reference, replacement.parameterIndex) } ) let genericArguments: [TypeSyntax] = - genericArgumentList?.arguments.map { element in + genericArgumentList?.arguments.map { element in element.argument } ?? [] - let rewriter: MacroExpansionRewriter = MacroExpansionRewriter( parameterReplacements: parameterReplacements, arguments: arguments, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index b43fbc3f33e..0932acdb348 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -132,7 +132,7 @@ final class MacroReplacementTests: XCTestCase { XCTFail("not a normal expansion") return } - + let replacementA = try XCTUnwrap(genericReplacements.first) let replacementB = try XCTUnwrap(genericReplacements.dropFirst().first) @@ -237,12 +237,12 @@ final class MacroReplacementTests: XCTestCase { func testMacroGenericArgumentExpansion_replaceInner() throws { let macro: DeclSyntax = """ - macro gen(a: Array) = #otherMacro(first: a) + macro gen(a: Array) = #reduce(first: a) """ let use: ExprSyntax = """ - #gen(a: [1, 2, 3]) + #gen(a: [1, 2, 3]) """ let macroDecl = macro.as(MacroDeclSyntax.self)! @@ -252,7 +252,7 @@ final class MacroReplacementTests: XCTestCase { return } - XCTAssertEqual(genericReplacements.count, 0) + XCTAssertEqual(genericReplacements.count, 1) let expandedSyntax = macroDecl.expand( use.as(MacroExpansionExprSyntax.self)!, @@ -263,7 +263,7 @@ final class MacroReplacementTests: XCTestCase { assertStringsEqualWithDiff( expandedSyntax.description, """ - #otherMacro(first: [1, 2, 3]) + #reduce(first: [1, 2, 3]) """ ) } From 9c91502c04cf94409d0f938492e320a55062d252 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 6 Feb 2024 11:40:22 +0900 Subject: [PATCH 10/12] add default parameter value; add another test --- .../MacroReplacement.swift | 2 +- .../MacroReplacementTests.swift | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index be8d77c934a..c817f9228d5 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -398,7 +398,7 @@ extension MacroDeclSyntax { _ node: some FreestandingMacroExpansionSyntax, definition: MacroExpansionExprSyntax, replacements: [MacroDefinition.Replacement], - genericReplacements: [MacroDefinition.GenericArgumentReplacement] + genericReplacements: [MacroDefinition.GenericArgumentReplacement] = [] ) -> ExprSyntax { return expand( argumentList: node.arguments, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index 0932acdb348..f0caef9ceac 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -267,4 +267,38 @@ final class MacroReplacementTests: XCTestCase { """ ) } + + func testMacroGenericArgumentExpansion_array() throws { + let macro: DeclSyntax = + """ + macro gen(a: Array) = #other(first: a) + """ + + let use: ExprSyntax = + """ + #otheren(a: [1, 2, 3]) + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + return + } + + XCTAssertEqual(genericReplacements.count, 0) + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacements, + genericReplacements: genericReplacements + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #other(first: [1, 2, 3]) + """ + ) + } } From c0bce0d44e49515ff050014b4efa54a9ea6f8338 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 6 Feb 2024 12:37:56 +0900 Subject: [PATCH 11/12] don't crash when keys are not unique in macro expansion --- .../MacroReplacement.swift | 10 +++-- .../MacroReplacementTests.swift | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index c817f9228d5..995c742de26 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -363,9 +363,10 @@ extension MacroDeclSyntax { // FIXME: Do real call-argument matching between the argument list and the // macro parameter list, porting over from the compiler. let parameterReplacements = Dictionary( - uniqueKeysWithValues: replacements.map { replacement in + replacements.map { replacement in (replacement.reference, replacement.parameterIndex) - } + }, + uniquingKeysWith: { l, r in l } ) let arguments: [ExprSyntax] = argumentList?.map { element in @@ -373,9 +374,10 @@ extension MacroDeclSyntax { } ?? [] let genericReplacements = Dictionary( - uniqueKeysWithValues: genericReplacements.map { replacement in + genericReplacements.map { replacement in (replacement.reference, replacement.parameterIndex) - } + }, + uniquingKeysWith: { l, r in l } ) let genericArguments: [TypeSyntax] = genericArgumentList?.arguments.map { element in diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index f0caef9ceac..22bfcd585b2 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -301,4 +301,43 @@ final class MacroReplacementTests: XCTestCase { """ ) } + + func testMacroExpansion_dontCrashOnDuplicates() throws { + let macro: DeclSyntax = + """ + macro gen(a: Array) = #other(first: a) + """ + + let use: ExprSyntax = + """ + #otheren(a: [1, 2, 3]) + """ + + let macroDecl = macro.as(MacroDeclSyntax.self)! + let definition = try macroDecl.checkDefinition() + guard case let .expansion(expansion, replacements, genericReplacements) = definition else { + XCTFail("not a normal expansion") + return + } + + var replacementsWithDupes = replacements + replacementsWithDupes.append(contentsOf: replacements) + var genericReplacementsWithDupes = genericReplacements + genericReplacementsWithDupes.append(contentsOf: genericReplacements) + + XCTAssertEqual(genericReplacements.count, 0) + + let expandedSyntax = macroDecl.expand( + use.as(MacroExpansionExprSyntax.self)!, + definition: expansion, + replacements: replacementsWithDupes, + genericReplacements: genericReplacementsWithDupes + ) + assertStringsEqualWithDiff( + expandedSyntax.description, + """ + #other(first: [1, 2, 3]) + """ + ) + } } From 9d22af9543fb52f4c0614eead5f9c4e84000c8d0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 6 Feb 2024 15:03:06 +0900 Subject: [PATCH 12/12] Apply changes from review --- .../MacroReplacement.swift | 24 ++++--------- .../MacroReplacementTests.swift | 36 +++++++++---------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 995c742de26..1aa33c632e9 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -188,8 +188,7 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor { override func visit(_ node: GenericArgumentSyntax) -> SyntaxVisitorContinueKind { guard let baseName = node.argument.as(IdentifierTypeSyntax.self)?.name else { - // Handle error - return .visitChildren + return .skipChildren } guard let genericParameterClause = macro.genericParameterClause else { @@ -300,7 +299,6 @@ extension MacroDeclSyntax { private final class MacroExpansionRewriter: SyntaxRewriter { let parameterReplacements: [DeclReferenceExprSyntax: Int] let arguments: [ExprSyntax] - // let genericParameterReplacements: [DeclReferenceExprSyntax: Int] let genericParameterReplacements: [GenericArgumentSyntax: Int] let genericArguments: [TypeSyntax] @@ -336,17 +334,9 @@ private final class MacroExpansionRewriter: SyntaxRewriter { } // Swap in the argument for type parameter - return GenericArgumentSyntax( - leadingTrivia: node.leadingTrivia, - node.unexpectedBeforeArgument, - argument: genericArguments[parameterIndex].trimmed, - node.unexpectedBetweenArgumentAndTrailingComma, - trailingComma: node.trailingComma, - node.unexpectedAfterTrailingComma - // TODO: seems we're getting spurious trailing " " here, - // skipping trailing trivia for now - // trailingTrivia: node.trailingTrivia - ) + var node = node + node.argument = genericArguments[parameterIndex].trimmed + return node } } @@ -380,11 +370,9 @@ extension MacroDeclSyntax { uniquingKeysWith: { l, r in l } ) let genericArguments: [TypeSyntax] = - genericArgumentList?.arguments.map { element in - element.argument - } ?? [] + genericArgumentList?.arguments.map { $0.argument } ?? [] - let rewriter: MacroExpansionRewriter = MacroExpansionRewriter( + let rewriter = MacroExpansionRewriter( parameterReplacements: parameterReplacements, arguments: arguments, genericReplacements: genericReplacements, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift index 22bfcd585b2..a564a2c3284 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroReplacementTests.swift @@ -24,7 +24,7 @@ final class MacroReplacementTests: XCTestCase { macro expand1(a: Int, b: Int) = #otherMacro(first: b, second: ["a": a], third: [3.14159, 2.71828], fourth: 4) """ - let definition = try macro.as(MacroDeclSyntax.self)!.checkDefinition() + let definition = try macro.cast(MacroDeclSyntax.self).checkDefinition() guard case let .expansion(_, replacements, _) = definition else { XCTFail("not an expansion definition") fatalError() @@ -43,7 +43,7 @@ final class MacroReplacementTests: XCTestCase { let diags: [Diagnostic] do { - _ = try macro.as(MacroDeclSyntax.self)!.checkDefinition() + _ = try macro.cast(MacroDeclSyntax.self).checkDefinition() XCTFail("should have failed with an error") fatalError() } catch let diagError as DiagnosticsError { @@ -69,7 +69,7 @@ final class MacroReplacementTests: XCTestCase { let diags: [Diagnostic] do { - _ = try macro.as(MacroDeclSyntax.self)!.checkDefinition() + _ = try macro.cast(MacroDeclSyntax.self).checkDefinition() XCTFail("should have failed with an error") fatalError() } catch let diagError as DiagnosticsError { @@ -94,7 +94,7 @@ final class MacroReplacementTests: XCTestCase { #expand1(a: 5, b: 17) """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -115,7 +115,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion_base() throws { + func testMacroGenericArgumentExpansionBase() throws { let macro: DeclSyntax = """ macro gen(a: A, b: B) = #otherMacro(first: a, second: b) @@ -126,7 +126,7 @@ final class MacroReplacementTests: XCTestCase { #gen(a: 5, b: "Hello") """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -158,7 +158,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion_ignoreTrivia() throws { + func testMacroGenericArgumentExpansionIgnoreTrivia() throws { let macro: DeclSyntax = """ macro gen(a: A, b: B) = #otherMacro(first: a, second: b) @@ -169,7 +169,7 @@ final class MacroReplacementTests: XCTestCase { #gen(a: 5, b: "Hello") """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -200,7 +200,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion_notVisitGenericParameterArguments() throws { + func testMacroGenericArgumentExpansionNotVisitGenericParameterArguments() throws { let macro: DeclSyntax = """ macro gen(a: Array) = #otherMacro(first: a) @@ -211,7 +211,7 @@ final class MacroReplacementTests: XCTestCase { #gen(a: [1, 2, 3]) """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -234,7 +234,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion_replaceInner() throws { + func testMacroGenericArgumentExpansionReplaceInner() throws { let macro: DeclSyntax = """ macro gen(a: Array) = #reduce(first: a) @@ -245,7 +245,7 @@ final class MacroReplacementTests: XCTestCase { #gen(a: [1, 2, 3]) """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -268,7 +268,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroGenericArgumentExpansion_array() throws { + func testMacroGenericArgumentExpansionArray() throws { let macro: DeclSyntax = """ macro gen(a: Array) = #other(first: a) @@ -276,10 +276,10 @@ final class MacroReplacementTests: XCTestCase { let use: ExprSyntax = """ - #otheren(a: [1, 2, 3]) + #gen(a: [1, 2, 3]) """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion") @@ -302,7 +302,7 @@ final class MacroReplacementTests: XCTestCase { ) } - func testMacroExpansion_dontCrashOnDuplicates() throws { + func testMacroExpansionDontCrashOnDuplicates() throws { let macro: DeclSyntax = """ macro gen(a: Array) = #other(first: a) @@ -310,10 +310,10 @@ final class MacroReplacementTests: XCTestCase { let use: ExprSyntax = """ - #otheren(a: [1, 2, 3]) + #gen(a: [1, 2, 3]) """ - let macroDecl = macro.as(MacroDeclSyntax.self)! + let macroDecl = macro.cast(MacroDeclSyntax.self) let definition = try macroDecl.checkDefinition() guard case let .expansion(expansion, replacements, genericReplacements) = definition else { XCTFail("not a normal expansion")