From 1da5f5f32dc379c43c7a63417a6e7dfbf820c3d4 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 24 Jul 2023 10:09:31 -0700 Subject: [PATCH 1/6] Add parser entry functions for more nodes --- .../Sources/SyntaxSupport/DeclNodes.swift | 1 + .../GenerateSwiftSyntax.swift | 2 +- .../swiftparser/ParserEntryFile.swift | 105 -------- .../ResultBuildersFile.swift | 5 + ...yStringInterpolationConformancesFile.swift | 7 + Sources/SwiftParser/CMakeLists.txt | 4 +- .../CollectionNodes+Parsable.swift | 129 ++++++++++ Sources/SwiftParser/Declarations.swift | 97 ++++--- Sources/SwiftParser/ParseSourceFile.swift | 100 ++++++++ ...Entry.swift => LayoutNodes+Parsable.swift} | 103 ++------ .../generated/ResultBuilders.swift | 240 ++++++++++++++++++ ...bleByStringInterpolationConformances.swift | 10 + 12 files changed, 562 insertions(+), 241 deletions(-) create mode 100644 Sources/SwiftParser/CollectionNodes+Parsable.swift create mode 100644 Sources/SwiftParser/ParseSourceFile.swift rename Sources/SwiftParser/generated/{Parser+Entry.swift => LayoutNodes+Parsable.swift} (77%) diff --git a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift index 0d2685bf0af..4707658f539 100644 --- a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift @@ -46,6 +46,7 @@ public let DECL_NODES: [Node] = [ kind: .accessorBlock, base: .syntax, nameForDiagnostics: nil, + parserFunction: "parseGetSet", traits: [ "Braced" ], diff --git a/CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift b/CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift index fba3de72d00..4f86ea041c4 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift @@ -82,7 +82,7 @@ struct GenerateSwiftSyntax: ParsableCommand { [ // SwiftParser GeneratedFileSpec(swiftParserGeneratedDir + ["IsLexerClassified.swift"], isLexerClassifiedFile), - GeneratedFileSpec(swiftParserGeneratedDir + ["Parser+Entry.swift"], parserEntryFile), + GeneratedFileSpec(swiftParserGeneratedDir + ["LayoutNodes+Parsable.swift"], parserEntryFile), GeneratedFileSpec(swiftParserGeneratedDir + ["Parser+TokenSpecSet.swift"], parserTokenSpecSetFile), GeneratedFileSpec(swiftParserGeneratedDir + ["TokenSpecStaticMembers.swift"], tokenSpecStaticMembersFile), diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserEntryFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserEntryFile.swift index d1f5f2f2036..c982516f477 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserEntryFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserEntryFile.swift @@ -18,84 +18,6 @@ import Utils let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { DeclSyntax("@_spi(RawSyntax) import SwiftSyntax") - try! ExtensionDeclSyntax("extension Parser") { - DeclSyntax( - """ - /// Parse the source code in the given string as Swift source file. See - /// `Parser.init` for more details. - public static func parse( - source: String - ) -> SourceFileSyntax { - var parser = Parser(source) - return SourceFileSyntax.parse(from: &parser) - } - """ - ) - - DeclSyntax( - """ - /// Parse the source code in the given buffer as Swift source file. See - /// `Parser.init` for more details. - public static func parse( - source: UnsafeBufferPointer, - maximumNestingLevel: Int? = nil - ) -> SourceFileSyntax { - var parser = Parser(source, maximumNestingLevel: maximumNestingLevel) - return SourceFileSyntax.parse(from: &parser) - } - """ - ) - - DeclSyntax( - """ - /// Parse the source code in the given string as Swift source file with support - /// for incremental parsing. - /// - /// When parsing a source file for the first time, invoke `parseIncrementally` - /// with `parseTransition: nil`. This returns the initial tree as well as - /// ``LookaheadRanges``. If an edit is made to the source file, an - /// ``IncrementalParseTransition`` can be constructed from the initial tree - /// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with - /// the post-edit source and that parse transition, the parser will re-use - /// nodes that haven’t changed. - /// - /// - Parameters: - /// - source: The source code to parse - /// - parseTransition: If a similar source file has already been parsed, the - /// ``IncrementalParseTransition`` that contains the previous tree as well - /// as the edits that were performed to it. - /// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe - /// how far the parser looked ahead while parsing a node, which is - /// necessary to construct an ``IncrementalParseTransition`` for a - /// subsequent incremental parse - public static func parseIncrementally( - source: String, - parseTransition: IncrementalParseTransition? - ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { - var parser = Parser(source, parseTransition: parseTransition) - return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) - } - """ - ) - - DeclSyntax( - """ - /// Parse the source code in the given buffer as Swift source file with support - /// for incremental parsing. - /// - /// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)`` - public static func parseIncrementally( - source: UnsafeBufferPointer, - maximumNestingLevel: Int? = nil, - parseTransition: IncrementalParseTransition? - ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { - var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition) - return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) - } - """ - ) - } - DeclSyntax( """ public protocol SyntaxParseable: SyntaxProtocol { @@ -128,33 +50,6 @@ let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { } try! ExtensionDeclSyntax("fileprivate extension Parser") { - DeclSyntax( - """ - mutating func parseRemainder(into: R) -> R { - guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else { - preconditionFailure("Only support parsing of non-collection layout nodes") - } - - let remainingTokens = self.consumeRemainingTokens() - if remainingTokens.isEmpty { - return into - } - - let existingUnexpected: [RawSyntax] - if let unexpectedNode = layout.children[layout.children.count - 1] { - precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self)) - existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements - } else { - existingUnexpected = [] - } - let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena) - - let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena) - return R.init(withUnexpected)! - } - """ - ) - DeclSyntax( """ mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax { diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift index 37646399e03..ce207805834 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift @@ -90,6 +90,11 @@ let resultBuildersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { DeclSyntax( """ /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 } } diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift index 5e0e77dfd79..381f43861d3 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift @@ -21,4 +21,11 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFileSyntax(le for node in SYNTAX_NODES where node.parserFunction != nil { DeclSyntax("extension \(node.kind.syntaxType): SyntaxExpressibleByStringInterpolation {}") } + + // `SyntaxParsable` conformance for collection nodes is hand-written. + // We also need to hand-write the corresponding `SyntaxExpressibleByStringInterpolation` conformances. + DeclSyntax("extension AccessorDeclListSyntax: SyntaxExpressibleByStringInterpolation {}") + DeclSyntax("extension AttributeListSyntax: SyntaxExpressibleByStringInterpolation {}") + DeclSyntax("extension CodeBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {}") + DeclSyntax("extension MemberBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {}") } diff --git a/Sources/SwiftParser/CMakeLists.txt b/Sources/SwiftParser/CMakeLists.txt index daa969ccbaf..dc5815c110a 100644 --- a/Sources/SwiftParser/CMakeLists.txt +++ b/Sources/SwiftParser/CMakeLists.txt @@ -10,6 +10,7 @@ add_swift_host_library(SwiftParser Attributes.swift Availability.swift CharacterInfo.swift + CollectionNodes+Parsable.swift Declarations.swift Directives.swift Expressions.swift @@ -21,6 +22,7 @@ add_swift_host_library(SwiftParser Nominals.swift Parameters.swift Parser.swift + ParseSourceFile.swift Patterns.swift TokenSpec.swift TokenSpecSet.swift @@ -37,7 +39,7 @@ add_swift_host_library(SwiftParser Types.swift generated/IsLexerClassified.swift - generated/Parser+Entry.swift + generated/LayoutNodes+Parsable.swift generated/Parser+TokenSpecSet.swift generated/TokenSpecStaticMembers.swift diff --git a/Sources/SwiftParser/CollectionNodes+Parsable.swift b/Sources/SwiftParser/CollectionNodes+Parsable.swift new file mode 100644 index 00000000000..2013cc15c5d --- /dev/null +++ b/Sources/SwiftParser/CollectionNodes+Parsable.swift @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@_spi(RawSyntax) import SwiftSyntax + +fileprivate extension SyntaxCollection { + static func parse( + from parser: inout Parser, + parse: (_ parser: inout Parser) -> some RawSyntaxNodeProtocol, + makeMissing: (_ remainingTokens: [RawSyntax], _ arena: SyntaxArena) -> some RawSyntaxNodeProtocol + ) -> Self { + // Keep the parser alive so that the arena in which `raw` is allocated + // doesn’t get deallocated before we have a chance to create a syntax node + // from it. We can’t use `parser.arena` as the parameter to + // `Syntax(raw:arena:)` because the node might have been re-used during an + // incremental parse and would then live in a different arena than + // `parser.arena`. + defer { + withExtendedLifetime(parser) { + } + } + let node = parse(&parser) + + if parser.at(.endOfFile) { + return Syntax(raw: node.raw, rawNodeArena: parser.arena).cast(Self.self) + } + + let layoutView = node.raw.layoutView! + + if layoutView.children.isEmpty { + let remainingTokens = parser.consumeRemainingTokens() + assert(!remainingTokens.isEmpty) + let missing = makeMissing(remainingTokens, parser.arena) + let raw = layoutView.insertingChild(missing.raw, at: node.raw.layoutView!.children.count, arena: parser.arena) + return Syntax(raw: raw, rawNodeArena: parser.arena).cast(Self.self) + } else { + // First unwrap: We know that children.last exists because children is not empty + // Second unwrap: This is a collection and collections never have optional children. Thus the last child can’t be nil. + let lastWithRemainder = parser.parseRemainder(into: layoutView.children.last!!) + let raw = layoutView.replacingChild(at: layoutView.children.count - 1, with: lastWithRemainder, arena: parser.arena) + return Syntax(raw: raw, rawNodeArena: parser.arena).cast(Self.self) + } + } +} + +extension AccessorDeclListSyntax: SyntaxParseable { + public static func parse(from parser: inout Parser) -> Self { + return parse(from: &parser) { parser in + return parser.parseAccessorList() ?? RawAccessorDeclListSyntax(elements: [], arena: parser.arena) + } makeMissing: { remainingTokens, arena in + return RawAccessorDeclSyntax( + attributes: nil, + modifier: nil, + accessorSpecifier: RawTokenSyntax(missing: .keyword, text: "get", arena: arena), + parameters: nil, + effectSpecifiers: nil, + initEffects: nil, + body: nil, + arena: arena + ) + } + } +} + +extension AttributeListSyntax: SyntaxParseable { + public static func parse(from parser: inout Parser) -> Self { + return parse(from: &parser) { parser in + let node = parser.parseAttributeList() ?? RawAttributeListSyntax(elements: [], arena: parser.arena) + return RawSyntax(node) + } makeMissing: { remainingTokens, arena in + return RawAttributeSyntax( + atSign: RawTokenSyntax(missing: .atSign, arena: arena), + attributeName: RawTypeSyntax(RawMissingTypeSyntax(arena: arena)), + leftParen: nil, + arguments: nil, + rightParen: nil, + arena: arena + ) + } + } +} + +extension CodeBlockItemListSyntax: SyntaxParseable { + public static func parse(from parser: inout Parser) -> Self { + return parse(from: &parser) { parser in + let node = parser.parseCodeBlockItemList(until: { _ in false }) + return RawSyntax(node) + } makeMissing: { remainingTokens, arena in + let missingExpr = RawMissingExprSyntax(arena: arena) + return RawCodeBlockItemSyntax(item: .expr(RawExprSyntax(missingExpr)), semicolon: nil, arena: arena) + } + } +} + +extension MemberBlockItemListSyntax: SyntaxParseable { + public static func parse(from parser: inout Parser) -> Self { + return parse(from: &parser) { parser in + return RawSyntax(parser.parseMemberDeclList()) + } makeMissing: { remainingTokens, arena in + let missingDecl = RawMissingDeclSyntax( + attributes: nil, + modifiers: nil, + placeholder: RawTokenSyntax(missing: .identifier, text: "<#declaration#>", arena: arena), + RawUnexpectedNodesSyntax(remainingTokens, arena: arena), + arena: arena + ) + return RawMemberBlockItemSyntax(decl: RawDeclSyntax(missingDecl), semicolon: nil, arena: arena) + } + } +} + +//==========================================================================// +// IMPORTANT: When adding a new conformance, also add a corrsponding // +// conformance to SyntaxExpressibleByStringInterpolation. // +//==========================================================================// + +//==========================================================================// +// IMPORTANT: If you are tempted to add a new conformances here please // +// insert in in alphabetical order above // +//==========================================================================// diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index a4fb39a85fd..6bcbe02488d 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -712,12 +712,8 @@ extension Parser { return result } - /// `introducer` is the `struct`, `class`, ... keyword that is the cause that the member decl block is being parsed. - /// If the left brace is missing, its indentation will be used to judge whether a following `}` was - /// indented to close this code block or a surrounding context. See `expectRightBrace`. - mutating func parseMemberBlock(introducer: RawTokenSyntax? = nil) -> RawMemberBlockSyntax { + mutating func parseMemberDeclList() -> RawMemberBlockItemListSyntax { var elements = [RawMemberBlockItemSyntax]() - let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) do { var loopProgress = LoopProgressCondition() while !self.at(.endOfFile, .rightBrace) && self.hasProgressed(&loopProgress) { @@ -739,13 +735,16 @@ extension Parser { elements.append(newElement) } } + return RawMemberBlockItemListSyntax(elements: elements, arena: self.arena) + } + + /// `introducer` is the `struct`, `class`, ... keyword that is the cause that the member decl block is being parsed. + /// If the left brace is missing, its indentation will be used to judge whether a following `}` was + /// indented to close this code block or a surrounding context. See `expectRightBrace`. + mutating func parseMemberBlock(introducer: RawTokenSyntax? = nil) -> RawMemberBlockSyntax { + let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) + let members = parseMemberDeclList() let (unexpectedBeforeRBrace, rbrace) = self.expectRightBrace(leftBrace: lbrace, introducer: introducer) - let members: RawMemberBlockItemListSyntax - if elements.isEmpty && (lbrace.isMissing || rbrace.isMissing) { - members = RawMemberBlockItemListSyntax(elements: [], arena: self.arena) - } else { - members = RawMemberBlockItemListSyntax(elements: elements, arena: self.arena) - } return RawMemberBlockSyntax( unexpectedBeforeLBrace, @@ -1354,7 +1353,7 @@ extension Parser { } /// Parse an accessor once we know we have an introducer - private mutating func parseAccessorDecl( + mutating func parseAccessorDecl( introducer: AccessorIntroducer ) -> RawAccessorDeclSyntax { // 'set' and 'willSet' can have an optional name. This isn't valid in a @@ -1392,6 +1391,26 @@ extension Parser { ) } + mutating func parseAccessorList() -> RawAccessorDeclListSyntax? { + // Collect all explicit accessors to a list. + var elements = [RawAccessorDeclSyntax]() + do { + var loopProgress = LoopProgressCondition() + while !self.at(.endOfFile, .rightBrace) && self.hasProgressed(&loopProgress) { + guard let introducer = self.parseAccessorIntroducer() else { + break + } + + elements.append(parseAccessorDecl(introducer: introducer)) + } + } + if elements.isEmpty { + return nil + } else { + return RawAccessorDeclListSyntax(elements: elements, arena: self.arena) + } + } + /// Parse the body of a variable declaration. This can include explicit /// getters, setters, and observers, or the body of a computed property. mutating func parseGetSet() -> RawSubscriptDeclSyntax.Accessors { @@ -1404,45 +1423,25 @@ extension Parser { } else { (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) } - // Collect all explicit accessors to a list. - var elements = [RawAccessorDeclSyntax]() - do { - var loopProgress = LoopProgressCondition() - while !self.at(.endOfFile, .rightBrace) && self.hasProgressed(&loopProgress) { - guard let introducer = self.parseAccessorIntroducer() else { - // There can only be an implicit getter if no other accessors were - // seen before this one. - guard elements.isEmpty else { - let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) - return .accessors( - RawAccessorBlockSyntax( - unexpectedBeforeLBrace, - leftBrace: lbrace, - accessors: RawAccessorDeclListSyntax(elements: elements, arena: self.arena), - unexpectedBeforeRBrace, - rightBrace: rbrace, - arena: self.arena - ) - ) - } - let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) }) + let accessorList = parseAccessorList() - let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) - return .getter( - RawCodeBlockSyntax( - unexpectedBeforeLBrace, - leftBrace: lbrace, - statements: body, - unexpectedBeforeRBrace, - rightBrace: rbrace, - arena: self.arena - ) - ) - } + // There can only be an implicit getter if no other accessors were + // seen before this one. + guard let accessorList else { + let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) }) - elements.append(parseAccessorDecl(introducer: introducer)) - } + let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) + return .getter( + RawCodeBlockSyntax( + unexpectedBeforeLBrace, + leftBrace: lbrace, + statements: body, + unexpectedBeforeRBrace, + rightBrace: rbrace, + arena: self.arena + ) + ) } let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) @@ -1450,7 +1449,7 @@ extension Parser { RawAccessorBlockSyntax( unexpectedBeforeLBrace, leftBrace: lbrace, - accessors: RawAccessorDeclListSyntax(elements: elements, arena: self.arena), + accessors: accessorList, unexpectedBeforeRBrace, rightBrace: rbrace, arena: self.arena diff --git a/Sources/SwiftParser/ParseSourceFile.swift b/Sources/SwiftParser/ParseSourceFile.swift new file mode 100644 index 00000000000..58bc41d191a --- /dev/null +++ b/Sources/SwiftParser/ParseSourceFile.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 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 the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@_spi(RawSyntax) import SwiftSyntax + +extension Parser { + /// Parse the source code in the given string as Swift source file. See + /// `Parser.init` for more details. + public static func parse( + source: String + ) -> SourceFileSyntax { + var parser = Parser(source) + return SourceFileSyntax.parse(from: &parser) + } + + /// Parse the source code in the given buffer as Swift source file. See + /// `Parser.init` for more details. + public static func parse( + source: UnsafeBufferPointer, + maximumNestingLevel: Int? = nil + ) -> SourceFileSyntax { + var parser = Parser(source, maximumNestingLevel: maximumNestingLevel) + return SourceFileSyntax.parse(from: &parser) + } + + /// Parse the source code in the given string as Swift source file with support + /// for incremental parsing. + /// + /// When parsing a source file for the first time, invoke `parseIncrementally` + /// with `parseTransition: nil`. This returns the initial tree as well as + /// ``LookaheadRanges``. If an edit is made to the source file, an + /// ``IncrementalParseTransition`` can be constructed from the initial tree + /// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with + /// the post-edit source and that parse transition, the parser will re-use + /// nodes that haven’t changed. + /// + /// - Parameters: + /// - source: The source code to parse + /// - parseTransition: If a similar source file has already been parsed, the + /// ``IncrementalParseTransition`` that contains the previous tree as well + /// as the edits that were performed to it. + /// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe + /// how far the parser looked ahead while parsing a node, which is + /// necessary to construct an ``IncrementalParseTransition`` for a + /// subsequent incremental parse + public static func parseIncrementally( + source: String, + parseTransition: IncrementalParseTransition? + ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { + var parser = Parser(source, parseTransition: parseTransition) + return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) + } + + /// Parse the source code in the given buffer as Swift source file with support + /// for incremental parsing. + /// + /// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)`` + public static func parseIncrementally( + source: UnsafeBufferPointer, + maximumNestingLevel: Int? = nil, + parseTransition: IncrementalParseTransition? + ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { + var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition) + return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) + } +} + +extension Parser { + mutating func parseRemainder(into: R) -> R { + guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else { + preconditionFailure("Only support parsing of non-collection layout nodes") + } + + let remainingTokens = self.consumeRemainingTokens() + if remainingTokens.isEmpty { + return into + } + + let existingUnexpected: [RawSyntax] + if let unexpectedNode = layout.children[layout.children.count - 1] { + precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self)) + existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements + } else { + existingUnexpected = [] + } + let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena) + + let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena) + return R.init(withUnexpected)! + } +} diff --git a/Sources/SwiftParser/generated/Parser+Entry.swift b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift similarity index 77% rename from Sources/SwiftParser/generated/Parser+Entry.swift rename to Sources/SwiftParser/generated/LayoutNodes+Parsable.swift index 6a0c5e9d4e1..1dd864c345a 100644 --- a/Sources/SwiftParser/generated/Parser+Entry.swift +++ b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift @@ -14,72 +14,28 @@ @_spi(RawSyntax) import SwiftSyntax -extension Parser { - /// Parse the source code in the given string as Swift source file. See - /// `Parser.init` for more details. - public static func parse( - source: String - ) -> SourceFileSyntax { - var parser = Parser(source) - return SourceFileSyntax.parse(from: &parser) - } - - /// Parse the source code in the given buffer as Swift source file. See - /// `Parser.init` for more details. - public static func parse( - source: UnsafeBufferPointer, - maximumNestingLevel: Int? = nil - ) -> SourceFileSyntax { - var parser = Parser(source, maximumNestingLevel: maximumNestingLevel) - return SourceFileSyntax.parse(from: &parser) - } - - /// Parse the source code in the given string as Swift source file with support - /// for incremental parsing. - /// - /// When parsing a source file for the first time, invoke `parseIncrementally` - /// with `parseTransition: nil`. This returns the initial tree as well as - /// ``LookaheadRanges``. If an edit is made to the source file, an - /// ``IncrementalParseTransition`` can be constructed from the initial tree - /// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with - /// the post-edit source and that parse transition, the parser will re-use - /// nodes that haven’t changed. - /// - /// - Parameters: - /// - source: The source code to parse - /// - parseTransition: If a similar source file has already been parsed, the - /// ``IncrementalParseTransition`` that contains the previous tree as well - /// as the edits that were performed to it. - /// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe - /// how far the parser looked ahead while parsing a node, which is - /// necessary to construct an ``IncrementalParseTransition`` for a - /// subsequent incremental parse - public static func parseIncrementally( - source: String, - parseTransition: IncrementalParseTransition? - ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { - var parser = Parser(source, parseTransition: parseTransition) - return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) - } - - /// Parse the source code in the given buffer as Swift source file with support - /// for incremental parsing. - /// - /// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)`` - public static func parseIncrementally( - source: UnsafeBufferPointer, - maximumNestingLevel: Int? = nil, - parseTransition: IncrementalParseTransition? - ) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) { - var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition) - return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges) - } -} - public protocol SyntaxParseable: SyntaxProtocol { static func parse(from parser: inout Parser) -> Self } +extension AccessorBlockSyntax: SyntaxParseable { + public static func parse(from parser: inout Parser) -> Self { + // Keep the parser alive so that the arena in which `raw` is allocated + // doesn’t get deallocated before we have a chance to create a syntax node + // from it. We can’t use `parser.arena` as the parameter to + // `Syntax(raw:arena:)` because the node might have been re-used during an + // incremental parse and would then live in a different arena than + // `parser.arena`. + defer { + withExtendedLifetime(parser) { + } + } + let node = parser.parseGetSet() + let raw = RawSyntax(parser.parseRemainder(into: node)) + return Syntax(raw: raw, rawNodeArena: raw.arena).cast(Self.self) + } +} + extension AccessorDeclSyntax: SyntaxParseable { public static func parse(from parser: inout Parser) -> Self { // Keep the parser alive so that the arena in which `raw` is allocated @@ -369,29 +325,6 @@ extension TypeSyntax: SyntaxParseable { } fileprivate extension Parser { - mutating func parseRemainder(into: R) -> R { - guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else { - preconditionFailure("Only support parsing of non-collection layout nodes") - } - - let remainingTokens = self.consumeRemainingTokens() - if remainingTokens.isEmpty { - return into - } - - let existingUnexpected: [RawSyntax] - if let unexpectedNode = layout.children[layout.children.count - 1] { - precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self)) - existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements - } else { - existingUnexpected = [] - } - let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena) - - let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena) - return R.init(withUnexpected)! - } - mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax { guard let node = self.parseCodeBlockItem(isAtTopLevel: false, allowInitDecl: true) else { // The missing item is not necessary to be a declaration, diff --git a/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift b/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift index 428a689c51b..ecc0799df7a 100644 --- a/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift +++ b/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift @@ -43,6 +43,11 @@ public struct AccessorDeclListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -123,6 +128,11 @@ public struct ArrayElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -218,6 +228,11 @@ public struct AttributeListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -298,6 +313,11 @@ public struct AvailabilityArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -381,6 +401,11 @@ public struct CatchClauseListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -461,6 +486,11 @@ public struct CatchItemListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -544,6 +574,11 @@ public struct ClosureCaptureListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -627,6 +662,11 @@ public struct ClosureParameterListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -710,6 +750,11 @@ public struct ClosureShorthandParameterListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -793,6 +838,11 @@ public struct CodeBlockItemListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -873,6 +923,11 @@ public struct CompositionTypeElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -953,6 +1008,11 @@ public struct ConditionElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1036,6 +1096,11 @@ public struct DeclModifierListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1116,6 +1181,11 @@ public struct DeclNameArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1196,6 +1266,11 @@ public struct DesignatedTypeListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1276,6 +1351,11 @@ public struct DictionaryElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1359,6 +1439,11 @@ public struct DifferentiabilityArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1442,6 +1527,11 @@ public struct DocumentationAttributeArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1525,6 +1615,11 @@ public struct EffectsAttributeArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1605,6 +1700,11 @@ public struct EnumCaseElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1688,6 +1788,11 @@ public struct EnumCaseParameterListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1771,6 +1876,11 @@ public struct ExprListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1851,6 +1961,11 @@ public struct FunctionParameterListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -1934,6 +2049,11 @@ public struct GenericArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2017,6 +2137,11 @@ public struct GenericParameterListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2100,6 +2225,11 @@ public struct GenericRequirementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2183,6 +2313,11 @@ public struct IfConfigClauseListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2263,6 +2398,11 @@ public struct ImportPathComponentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2343,6 +2483,11 @@ public struct InheritedTypeListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2426,6 +2571,11 @@ public struct KeyPathComponentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2506,6 +2656,11 @@ public struct LabeledExprListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2589,6 +2744,11 @@ public struct MemberBlockItemListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2669,6 +2829,11 @@ public struct MultipleTrailingClosureElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2749,6 +2914,11 @@ public struct ObjCSelectorPieceListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2829,6 +2999,11 @@ public struct PatternBindingListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -2912,6 +3087,11 @@ public struct PlatformVersionItemListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3013,6 +3193,11 @@ public struct PrecedenceGroupAttributeListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3093,6 +3278,11 @@ public struct PrecedenceGroupNameListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3176,6 +3366,11 @@ public struct PrimaryAssociatedTypeListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3283,6 +3478,11 @@ public struct SpecializeAttributeArgumentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3375,6 +3575,11 @@ public struct StringLiteralSegmentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3455,6 +3660,11 @@ public struct SwitchCaseItemListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3550,6 +3760,11 @@ public struct SwitchCaseListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3630,6 +3845,11 @@ public struct TuplePatternElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3713,6 +3933,11 @@ public struct TupleTypeElementListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3796,6 +4021,11 @@ public struct UnexpectedNodesBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3876,6 +4106,11 @@ public struct VersionComponentListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 @@ -3956,6 +4191,11 @@ public struct YieldedExpressionListBuilder { } /// Add all the elements of `expression` to this result builder, effectively flattening them. + /// + /// - Note: This overload is disfavored to resolve an ambiguity when both the final result and + /// the elements are expressible by string interpolation. In that case we favor creating a + /// single element from the string literal. + @_disfavoredOverload public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component { return expression.map { $0 diff --git a/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift b/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift index 4f98497a06e..3c5386c1cf6 100644 --- a/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift +++ b/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift @@ -14,6 +14,8 @@ import SwiftSyntax +extension AccessorBlockSyntax: SyntaxExpressibleByStringInterpolation {} + extension AccessorDeclSyntax: SyntaxExpressibleByStringInterpolation {} extension AttributeSyntax: SyntaxExpressibleByStringInterpolation {} @@ -45,3 +47,11 @@ extension StmtSyntax: SyntaxExpressibleByStringInterpolation {} extension SwitchCaseSyntax: SyntaxExpressibleByStringInterpolation {} extension TypeSyntax: SyntaxExpressibleByStringInterpolation {} + +extension AccessorDeclListSyntax: SyntaxExpressibleByStringInterpolation {} + +extension AttributeListSyntax: SyntaxExpressibleByStringInterpolation {} + +extension CodeBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {} + +extension MemberBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {} From 4007f33b5170084b5086b518b86ce324cfaf45e9 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 24 Jul 2023 10:11:23 -0700 Subject: [PATCH 2/6] Conform all SyntaxChildChoices to SyntaxHashable --- .../swiftsyntax/SyntaxCollectionsFile.swift | 2 +- .../swiftsyntax/SyntaxNodesFile.swift | 2 +- .../CollectionNodes+Parsable.swift | 1 - .../generated/SyntaxCollections.swift | 10 +++---- .../syntaxNodes/SyntaxDeclNodes.swift | 2 +- .../syntaxNodes/SyntaxExprNodes.swift | 4 +-- .../generated/syntaxNodes/SyntaxNodes.swift | 26 +++++++++---------- .../syntaxNodes/SyntaxStmtNodes.swift | 2 +- 8 files changed, 24 insertions(+), 25 deletions(-) diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxCollectionsFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxCollectionsFile.swift index 47bb3d51b7b..0aaed0e392d 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxCollectionsFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxCollectionsFile.swift @@ -41,7 +41,7 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { } else { try EnumDeclSyntax( """ - public enum Element: SyntaxChildChoices + public enum Element: SyntaxChildChoices, SyntaxHashable """ ) { for choiceName in node.elementChoices { diff --git a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift index 3edb61dcf9b..de2db9b5db4 100644 --- a/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -241,7 +241,7 @@ private func generateSyntaxChildChoices(for child: Child) throws -> EnumDeclSynt return nil } - return try! EnumDeclSyntax("public enum \(raw: child.name): SyntaxChildChoices") { + return try! EnumDeclSyntax("public enum \(raw: child.name): SyntaxChildChoices, SyntaxHashable") { for choice in choices { DeclSyntax("case `\(choice.varOrCaseName)`(\(raw: choice.syntaxNodeKind.syntaxType))") } diff --git a/Sources/SwiftParser/CollectionNodes+Parsable.swift b/Sources/SwiftParser/CollectionNodes+Parsable.swift index 2013cc15c5d..daf31ce0035 100644 --- a/Sources/SwiftParser/CollectionNodes+Parsable.swift +++ b/Sources/SwiftParser/CollectionNodes+Parsable.swift @@ -63,7 +63,6 @@ extension AccessorDeclListSyntax: SyntaxParseable { accessorSpecifier: RawTokenSyntax(missing: .keyword, text: "get", arena: arena), parameters: nil, effectSpecifiers: nil, - initEffects: nil, body: nil, arena: arena ) diff --git a/Sources/SwiftSyntax/generated/SyntaxCollections.swift b/Sources/SwiftSyntax/generated/SyntaxCollections.swift index 2e64894cf5a..e1b52a7423b 100644 --- a/Sources/SwiftSyntax/generated/SyntaxCollections.swift +++ b/Sources/SwiftSyntax/generated/SyntaxCollections.swift @@ -90,7 +90,7 @@ public struct ArrayElementListSyntax: SyntaxCollection, SyntaxHashable { /// - ``TypeAliasDeclSyntax``.``TypeAliasDeclSyntax/attributes`` /// - ``VariableDeclSyntax``.``VariableDeclSyntax/attributes`` public struct AttributeListSyntax: SyntaxCollection, SyntaxHashable { - public enum Element: SyntaxChildChoices { + public enum Element: SyntaxChildChoices, SyntaxHashable { case `attribute`(AttributeSyntax) case `ifConfigDecl`(IfConfigDeclSyntax) @@ -930,7 +930,7 @@ public struct PlatformVersionItemListSyntax: SyntaxCollection, SyntaxHashable { /// /// - ``PrecedenceGroupDeclSyntax``.``PrecedenceGroupDeclSyntax/groupAttributes`` public struct PrecedenceGroupAttributeListSyntax: SyntaxCollection, SyntaxHashable { - public enum Element: SyntaxChildChoices { + public enum Element: SyntaxChildChoices, SyntaxHashable { case `precedenceGroupRelation`(PrecedenceGroupRelationSyntax) case `precedenceGroupAssignment`(PrecedenceGroupAssignmentSyntax) case `precedenceGroupAssociativity`(PrecedenceGroupAssociativitySyntax) @@ -1052,7 +1052,7 @@ public struct PrimaryAssociatedTypeListSyntax: SyntaxCollection, SyntaxHashable /// /// - ``AttributeSyntax``.``AttributeSyntax/arguments`` public struct SpecializeAttributeArgumentListSyntax: SyntaxCollection, SyntaxHashable { - public enum Element: SyntaxChildChoices { + public enum Element: SyntaxChildChoices, SyntaxHashable { case `labeledSpecializeArgument`(LabeledSpecializeArgumentSyntax) case `specializeAvailabilityArgument`(SpecializeAvailabilityArgumentSyntax) case `specializeTargetFunctionArgument`(SpecializeTargetFunctionArgumentSyntax) @@ -1141,7 +1141,7 @@ public struct SpecializeAttributeArgumentListSyntax: SyntaxCollection, SyntaxHas /// /// - ``StringLiteralExprSyntax``.``StringLiteralExprSyntax/segments`` public struct StringLiteralSegmentListSyntax: SyntaxCollection, SyntaxHashable { - public enum Element: SyntaxChildChoices { + public enum Element: SyntaxChildChoices, SyntaxHashable { case `stringSegment`(StringSegmentSyntax) case `expressionSegment`(ExpressionSegmentSyntax) @@ -1228,7 +1228,7 @@ public struct SwitchCaseItemListSyntax: SyntaxCollection, SyntaxHashable { /// - ``IfConfigClauseSyntax``.``IfConfigClauseSyntax/elements`` /// - ``SwitchExprSyntax``.``SwitchExprSyntax/cases`` public struct SwitchCaseListSyntax: SyntaxCollection, SyntaxHashable { - public enum Element: SyntaxChildChoices { + public enum Element: SyntaxChildChoices, SyntaxHashable { case `switchCase`(SwitchCaseSyntax) case `ifConfigDecl`(IfConfigDeclSyntax) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift index 38add3e67e0..0d5be44edca 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift @@ -6290,7 +6290,7 @@ public struct StructDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? /// - `accessors`: (``AccessorBlockSyntax`` | ``CodeBlockSyntax``)? public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { - public enum Accessors: SyntaxChildChoices { + public enum Accessors: SyntaxChildChoices, SyntaxHashable { case `accessors`(AccessorBlockSyntax) case `getter`(CodeBlockSyntax) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxExprNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxExprNodes.swift index e9a5ad90e5e..7df2705e376 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxExprNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxExprNodes.swift @@ -1819,7 +1819,7 @@ public struct CopyExprSyntax: ExprSyntaxProtocol, SyntaxHashable { /// - `content`: (`':'` | ``DictionaryElementListSyntax``) /// - `rightSquare`: `']'` public struct DictionaryExprSyntax: ExprSyntaxProtocol, SyntaxHashable { - public enum Content: SyntaxChildChoices { + public enum Content: SyntaxChildChoices, SyntaxHashable { case `colon`(TokenSyntax) case `elements`(DictionaryElementListSyntax) @@ -2909,7 +2909,7 @@ public struct IdentifierExprSyntax: ExprSyntaxProtocol, SyntaxHashable { /// /// - ``IfExprSyntax``.``IfExprSyntax/elseBody`` public struct IfExprSyntax: ExprSyntaxProtocol, SyntaxHashable { - public enum ElseBody: SyntaxChildChoices { + public enum ElseBody: SyntaxChildChoices, SyntaxHashable { case `ifExpr`(IfExprSyntax) case `codeBlock`(CodeBlockSyntax) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift index 9626d992b25..284296b1772 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift @@ -612,7 +612,7 @@ public struct ArrayElementSyntax: SyntaxProtocol, SyntaxHashable { /// - ``AttributeListSyntax`` /// - ``SwitchCaseSyntax``.``SwitchCaseSyntax/attribute`` public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable { - public enum Arguments: SyntaxChildChoices { + public enum Arguments: SyntaxChildChoices, SyntaxHashable { case `argumentList`(LabeledExprListSyntax) case `token`(TokenSyntax) case `string`(StringLiteralExprSyntax) @@ -1089,7 +1089,7 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``AvailabilityArgumentListSyntax`` public struct AvailabilityArgumentSyntax: SyntaxProtocol, SyntaxHashable { - public enum Argument: SyntaxChildChoices { + public enum Argument: SyntaxChildChoices, SyntaxHashable { case `token`(TokenSyntax) case `availabilityVersionRestriction`(PlatformVersionSyntax) case `availabilityLabeledArgument`(AvailabilityLabeledArgumentSyntax) @@ -1479,7 +1479,7 @@ public struct AvailabilityConditionSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``AvailabilityArgumentSyntax``.``AvailabilityArgumentSyntax/argument`` public struct AvailabilityLabeledArgumentSyntax: SyntaxProtocol, SyntaxHashable { - public enum Value: SyntaxChildChoices { + public enum Value: SyntaxChildChoices, SyntaxHashable { case `string`(StringLiteralExprSyntax) case `version`(VersionTupleSyntax) @@ -3419,7 +3419,7 @@ public struct ClosureShorthandParameterSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``ClosureExprSyntax``.``ClosureExprSyntax/signature`` public struct ClosureSignatureSyntax: SyntaxProtocol, SyntaxHashable { - public enum ParameterClause: SyntaxChildChoices { + public enum ParameterClause: SyntaxChildChoices, SyntaxHashable { case `simpleInput`(ClosureShorthandParameterListSyntax) case `parameterClause`(ClosureParameterClauseSyntax) @@ -3717,7 +3717,7 @@ public struct ClosureSignatureSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``CodeBlockItemListSyntax`` public struct CodeBlockItemSyntax: SyntaxProtocol, SyntaxHashable { - public enum Item: SyntaxChildChoices { + public enum Item: SyntaxChildChoices, SyntaxHashable { case `decl`(DeclSyntax) case `stmt`(StmtSyntax) case `expr`(ExprSyntax) @@ -4217,7 +4217,7 @@ public struct CompositionTypeElementSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``ConditionElementListSyntax`` public struct ConditionElementSyntax: SyntaxProtocol, SyntaxHashable { - public enum Condition: SyntaxChildChoices { + public enum Condition: SyntaxChildChoices, SyntaxHashable { case `expression`(ExprSyntax) case `availability`(AvailabilityConditionSyntax) case `matchingPattern`(MatchingPatternConditionSyntax) @@ -6630,7 +6630,7 @@ public struct DifferentiabilityArgumentsSyntax: SyntaxProtocol, SyntaxHashable { /// - ``DerivativeAttributeArgumentsSyntax``.``DerivativeAttributeArgumentsSyntax/arguments`` /// - ``DifferentiableAttributeArgumentsSyntax``.``DifferentiableAttributeArgumentsSyntax/arguments`` public struct DifferentiabilityWithRespectToArgumentSyntax: SyntaxProtocol, SyntaxHashable { - public enum Arguments: SyntaxChildChoices { + public enum Arguments: SyntaxChildChoices, SyntaxHashable { case `argument`(DifferentiabilityArgumentSyntax) case `argumentList`(DifferentiabilityArgumentsSyntax) @@ -7045,7 +7045,7 @@ public struct DifferentiableAttributeArgumentsSyntax: SyntaxProtocol, SyntaxHash /// /// - ``DocumentationAttributeArgumentListSyntax`` public struct DocumentationAttributeArgumentSyntax: SyntaxProtocol, SyntaxHashable { - public enum Value: SyntaxChildChoices { + public enum Value: SyntaxChildChoices, SyntaxHashable { case `token`(TokenSyntax) case `string`(StringLiteralExprSyntax) @@ -10085,7 +10085,7 @@ public struct GenericParameterSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``GenericRequirementListSyntax`` public struct GenericRequirementSyntax: SyntaxProtocol, SyntaxHashable { - public enum Requirement: SyntaxChildChoices { + public enum Requirement: SyntaxChildChoices, SyntaxHashable { case `sameTypeRequirement`(SameTypeRequirementSyntax) case `conformanceRequirement`(ConformanceRequirementSyntax) case `layoutRequirement`(LayoutRequirementSyntax) @@ -10435,7 +10435,7 @@ public struct GenericWhereClauseSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``IfConfigClauseListSyntax`` public struct IfConfigClauseSyntax: SyntaxProtocol, SyntaxHashable { - public enum Elements: SyntaxChildChoices { + public enum Elements: SyntaxChildChoices, SyntaxHashable { case `statements`(CodeBlockItemListSyntax) case `switchCases`(SwitchCaseListSyntax) case `decls`(MemberBlockItemListSyntax) @@ -11398,7 +11398,7 @@ public struct InitializerClauseSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``KeyPathComponentListSyntax`` public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable { - public enum Component: SyntaxChildChoices { + public enum Component: SyntaxChildChoices, SyntaxHashable { case `property`(KeyPathPropertyComponentSyntax) case `subscript`(KeyPathSubscriptComponentSyntax) case `optional`(KeyPathOptionalComponentSyntax) @@ -14274,7 +14274,7 @@ public struct OriginallyDefinedInAttributeArgumentsSyntax: SyntaxProtocol, Synta /// /// - ``PatternBindingListSyntax`` public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { - public enum Accessors: SyntaxChildChoices { + public enum Accessors: SyntaxChildChoices, SyntaxHashable { case `accessors`(AccessorBlockSyntax) case `getter`(CodeBlockSyntax) @@ -17402,7 +17402,7 @@ public struct SwitchCaseLabelSyntax: SyntaxProtocol, SyntaxHashable { /// /// - ``SwitchCaseListSyntax`` public struct SwitchCaseSyntax: SyntaxProtocol, SyntaxHashable { - public enum Label: SyntaxChildChoices { + public enum Label: SyntaxChildChoices, SyntaxHashable { case `default`(SwitchDefaultLabelSyntax) case `case`(SwitchCaseLabelSyntax) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxStmtNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxStmtNodes.swift index 219a54e728a..98113c96427 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxStmtNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxStmtNodes.swift @@ -2215,7 +2215,7 @@ public struct WhileStmtSyntax: StmtSyntaxProtocol, SyntaxHashable { /// - `yieldKeyword`: `'yield'` /// - `yieldedExpressions`: (``YieldedExpressionsClauseSyntax`` | ``ExprSyntax``) public struct YieldStmtSyntax: StmtSyntaxProtocol, SyntaxHashable { - public enum YieldedExpressions: SyntaxChildChoices { + public enum YieldedExpressions: SyntaxChildChoices, SyntaxHashable { case `multiple`(YieldedExpressionsClauseSyntax) case `single`(ExprSyntax) From b14b6bac8354acd28952db3150b398c753b7c730 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 24 Jul 2023 14:33:14 -0700 Subject: [PATCH 3/6] Use AccessorBlockSyntax to represent getter --- .../Sources/SyntaxSupport/DeclNodes.swift | 37 ++--- Sources/SwiftParser/Declarations.swift | 45 ++--- .../SwiftSyntaxCompatibility.swift | 10 -- .../generated/ChildNameForKeyPath.swift | 24 +-- .../RenamedChildrenCompatibility.swift | 52 +++--- .../generated/SyntaxCollections.swift | 1 + .../generated/raw/RawSyntaxNodes.swift | 136 ++++++--------- .../generated/raw/RawSyntaxValidation.swift | 9 +- .../syntaxNodes/SyntaxDeclNodes.swift | 76 ++------- .../generated/syntaxNodes/SyntaxNodes.swift | 156 ++++++++---------- .../SyntaxNodeWithBody.swift | 2 +- .../MacroExpansion.swift | 2 +- .../MacroSystem.swift | 12 +- .../BasicFormatTests.swift | 10 +- Tests/SwiftParserTest/DeclarationTests.swift | 12 +- .../ExtensionDeclTests.swift | 4 +- .../MacroSystemTests.swift | 12 +- 17 files changed, 236 insertions(+), 364 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift index 4707658f539..2411a09deb4 100644 --- a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift @@ -57,7 +57,16 @@ public let DECL_NODES: [Node] = [ ), Child( name: "Accessors", - kind: .collection(kind: .accessorDeclList, collectionElementName: "Accessor") + kind: .nodeChoices(choices: [ + Child( + name: "Accessors", + kind: .collection(kind: .accessorDeclList, collectionElementName: "Accessor") + ), + Child( + name: "Getter", + kind: .node(kind: .codeBlockItemList) + ), + ]) ), Child( name: "RightBrace", @@ -1630,18 +1639,9 @@ public let DECL_NODES: [Node] = [ isOptional: true ), Child( - name: "Accessors", + name: "AccessorBlock", deprecatedName: "Accessor", - kind: .nodeChoices(choices: [ - Child( - name: "Accessors", - kind: .node(kind: .accessorBlock) - ), - Child( - name: "Getter", - kind: .node(kind: .codeBlock) - ), - ]), + kind: .node(kind: .accessorBlock), isOptional: true ), Child( @@ -2191,18 +2191,9 @@ public let DECL_NODES: [Node] = [ isOptional: true ), Child( - name: "Accessors", + name: "AccessorBlock", deprecatedName: "Accessor", - kind: .nodeChoices(choices: [ - Child( - name: "Accessors", - kind: .node(kind: .accessorBlock) - ), - Child( - name: "Getter", - kind: .node(kind: .codeBlock) - ), - ]), + kind: .node(kind: .accessorBlock), isOptional: true ), ] diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 6bcbe02488d..28e58eddd42 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1149,7 +1149,7 @@ extension Parser { } // Parse getter and setter. - let accessor: RawSubscriptDeclSyntax.Accessors? + let accessor: RawAccessorBlockSyntax? if self.at(.leftBrace) || self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil { accessor = self.parseGetSet() } else { @@ -1166,7 +1166,7 @@ extension Parser { parameterClause: parameterClause, returnClause: returnClause, genericWhereClause: genericWhereClause, - accessors: accessor, + accessorBlock: accessor, arena: self.arena ) } @@ -1263,14 +1263,9 @@ extension Parser { initializer = nil } - let accessors: RawPatternBindingSyntax.Accessors? + let accessors: RawAccessorBlockSyntax? if self.at(.leftBrace) || (inMemberDeclList && self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil && !self.at(.keyword(.`init`))) { - switch self.parseGetSet() { - case .accessors(let parsedAccessors): - accessors = .accessors(parsedAccessors) - case .getter(let getter): - accessors = .getter(getter) - } + accessors = self.parseGetSet() } else { accessors = nil } @@ -1281,7 +1276,7 @@ extension Parser { pattern: pattern, typeAnnotation: typeAnnotation, initializer: initializer, - accessors: accessors, + accessorBlock: accessors, trailingComma: keepGoing, arena: self.arena ) @@ -1413,7 +1408,7 @@ extension Parser { /// Parse the body of a variable declaration. This can include explicit /// getters, setters, and observers, or the body of a computed property. - mutating func parseGetSet() -> RawSubscriptDeclSyntax.Accessors { + mutating func parseGetSet() -> RawAccessorBlockSyntax { // Parse getter and setter. let unexpectedBeforeLBrace: RawUnexpectedNodesSyntax? let lbrace: RawTokenSyntax @@ -1432,28 +1427,24 @@ extension Parser { let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) }) let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) - return .getter( - RawCodeBlockSyntax( - unexpectedBeforeLBrace, - leftBrace: lbrace, - statements: body, - unexpectedBeforeRBrace, - rightBrace: rbrace, - arena: self.arena - ) - ) - } - - let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) - return .accessors( - RawAccessorBlockSyntax( + return RawAccessorBlockSyntax( unexpectedBeforeLBrace, leftBrace: lbrace, - accessors: accessorList, + accessors: .getter(body), unexpectedBeforeRBrace, rightBrace: rbrace, arena: self.arena ) + } + + let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) + return RawAccessorBlockSyntax( + unexpectedBeforeLBrace, + leftBrace: lbrace, + accessors: .accessors(accessorList), + unexpectedBeforeRBrace, + rightBrace: rbrace, + arena: self.arena ) } } diff --git a/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift b/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift index c5422acc72f..cf97bba9502 100644 --- a/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift +++ b/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift @@ -77,16 +77,6 @@ extension IdentifiedDeclSyntax where Self: NamedDeclSyntax { } } -extension PatternBindingSyntax { - @available(*, deprecated, renamed: "Accessors") - public typealias Accessor = Accessors -} - -extension SubscriptDeclSyntax { - @available(*, deprecated, renamed: "Accessors") - public typealias Accessor = Accessors -} - public extension SyntaxProtocol { @available(*, deprecated, message: "Use detached computed property instead.") func detach() -> Self { diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 41bd2043b68..83348e1abbc 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -2443,12 +2443,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenTypeAnnotationAndInitializer" case \PatternBindingSyntax.initializer: return "initializer" - case \PatternBindingSyntax.unexpectedBetweenInitializerAndAccessors: - return "unexpectedBetweenInitializerAndAccessors" - case \PatternBindingSyntax.accessors: - return "accessors" - case \PatternBindingSyntax.unexpectedBetweenAccessorsAndTrailingComma: - return "unexpectedBetweenAccessorsAndTrailingComma" + case \PatternBindingSyntax.unexpectedBetweenInitializerAndAccessorBlock: + return "unexpectedBetweenInitializerAndAccessorBlock" + case \PatternBindingSyntax.accessorBlock: + return "accessorBlock" + case \PatternBindingSyntax.unexpectedBetweenAccessorBlockAndTrailingComma: + return "unexpectedBetweenAccessorBlockAndTrailingComma" case \PatternBindingSyntax.trailingComma: return "trailingComma" case \PatternBindingSyntax.unexpectedAfterTrailingComma: @@ -2967,12 +2967,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenReturnClauseAndGenericWhereClause" case \SubscriptDeclSyntax.genericWhereClause: return "genericWhereClause" - case \SubscriptDeclSyntax.unexpectedBetweenGenericWhereClauseAndAccessors: - return "unexpectedBetweenGenericWhereClauseAndAccessors" - case \SubscriptDeclSyntax.accessors: - return "accessors" - case \SubscriptDeclSyntax.unexpectedAfterAccessors: - return "unexpectedAfterAccessors" + case \SubscriptDeclSyntax.unexpectedBetweenGenericWhereClauseAndAccessorBlock: + return "unexpectedBetweenGenericWhereClauseAndAccessorBlock" + case \SubscriptDeclSyntax.accessorBlock: + return "accessorBlock" + case \SubscriptDeclSyntax.unexpectedAfterAccessorBlock: + return "unexpectedAfterAccessorBlock" case \SuperExprSyntax.unexpectedBeforeSuperKeyword: return "unexpectedBeforeSuperKeyword" case \SuperExprSyntax.superKeyword: diff --git a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift index f4e445fa6d9..d76d95350bd 100644 --- a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift +++ b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift @@ -5650,37 +5650,37 @@ extension PackExpansionTypeSyntax { } extension PatternBindingSyntax { - @available(*, deprecated, renamed: "unexpectedBetweenInitializerAndAccessors") + @available(*, deprecated, renamed: "unexpectedBetweenInitializerAndAccessorBlock") public var unexpectedBetweenInitializerAndAccessor: UnexpectedNodesSyntax? { get { - return unexpectedBetweenInitializerAndAccessors + return unexpectedBetweenInitializerAndAccessorBlock } set { - unexpectedBetweenInitializerAndAccessors = newValue + unexpectedBetweenInitializerAndAccessorBlock = newValue } } - @available(*, deprecated, renamed: "accessors") - public var accessor: Accessors? { + @available(*, deprecated, renamed: "accessorBlock") + public var accessor: AccessorBlockSyntax? { get { - return accessors + return accessorBlock } set { - accessors = newValue + accessorBlock = newValue } } - @available(*, deprecated, renamed: "unexpectedBetweenAccessorsAndTrailingComma") + @available(*, deprecated, renamed: "unexpectedBetweenAccessorBlockAndTrailingComma") public var unexpectedBetweenAccessorAndTrailingComma: UnexpectedNodesSyntax? { get { - return unexpectedBetweenAccessorsAndTrailingComma + return unexpectedBetweenAccessorBlockAndTrailingComma } set { - unexpectedBetweenAccessorsAndTrailingComma = newValue + unexpectedBetweenAccessorBlockAndTrailingComma = newValue } } - @available(*, deprecated, message: "Use an initializer with accessors argument(s).") + @available(*, deprecated, message: "Use an initializer with accessorBlock argument(s).") @_disfavoredOverload public init( leadingTrivia: Trivia? = nil, @@ -5691,7 +5691,7 @@ extension PatternBindingSyntax { _ unexpectedBetweenTypeAnnotationAndInitializer: UnexpectedNodesSyntax? = nil, initializer: InitializerClauseSyntax? = nil, _ unexpectedBetweenInitializerAndAccessor: UnexpectedNodesSyntax? = nil, - accessor: Accessors? = nil, + accessor: AccessorBlockSyntax? = nil, _ unexpectedBetweenAccessorAndTrailingComma: UnexpectedNodesSyntax? = nil, trailingComma: TokenSyntax? = nil, _ unexpectedAfterTrailingComma: UnexpectedNodesSyntax? = nil, @@ -5707,7 +5707,7 @@ extension PatternBindingSyntax { unexpectedBetweenTypeAnnotationAndInitializer, initializer: initializer, unexpectedBetweenInitializerAndAccessor, - accessors: accessor, + accessorBlock: accessor, unexpectedBetweenAccessorAndTrailingComma, trailingComma: trailingComma, unexpectedAfterTrailingComma, @@ -7547,37 +7547,37 @@ extension SubscriptDeclSyntax { } } - @available(*, deprecated, renamed: "unexpectedBetweenGenericWhereClauseAndAccessors") + @available(*, deprecated, renamed: "unexpectedBetweenGenericWhereClauseAndAccessorBlock") public var unexpectedBetweenGenericWhereClauseAndAccessor: UnexpectedNodesSyntax? { get { - return unexpectedBetweenGenericWhereClauseAndAccessors + return unexpectedBetweenGenericWhereClauseAndAccessorBlock } set { - unexpectedBetweenGenericWhereClauseAndAccessors = newValue + unexpectedBetweenGenericWhereClauseAndAccessorBlock = newValue } } - @available(*, deprecated, renamed: "accessors") - public var accessor: Accessors? { + @available(*, deprecated, renamed: "accessorBlock") + public var accessor: AccessorBlockSyntax? { get { - return accessors + return accessorBlock } set { - accessors = newValue + accessorBlock = newValue } } - @available(*, deprecated, renamed: "unexpectedAfterAccessors") + @available(*, deprecated, renamed: "unexpectedAfterAccessorBlock") public var unexpectedAfterAccessor: UnexpectedNodesSyntax? { get { - return unexpectedAfterAccessors + return unexpectedAfterAccessorBlock } set { - unexpectedAfterAccessors = newValue + unexpectedAfterAccessorBlock = newValue } } - @available(*, deprecated, message: "Use an initializer with parameterClause, returnClause, accessors argument(s).") + @available(*, deprecated, message: "Use an initializer with parameterClause, returnClause, accessorBlock argument(s).") @_disfavoredOverload public init( leadingTrivia: Trivia? = nil, @@ -7596,7 +7596,7 @@ extension SubscriptDeclSyntax { _ unexpectedBetweenResultAndGenericWhereClause: UnexpectedNodesSyntax? = nil, genericWhereClause: GenericWhereClauseSyntax? = nil, _ unexpectedBetweenGenericWhereClauseAndAccessor: UnexpectedNodesSyntax? = nil, - accessor: Accessors? = nil, + accessor: AccessorBlockSyntax? = nil, _ unexpectedAfterAccessor: UnexpectedNodesSyntax? = nil, trailingTrivia: Trivia? = nil @@ -7618,7 +7618,7 @@ extension SubscriptDeclSyntax { unexpectedBetweenResultAndGenericWhereClause, genericWhereClause: genericWhereClause, unexpectedBetweenGenericWhereClauseAndAccessor, - accessors: accessor, + accessorBlock: accessor, unexpectedAfterAccessor, trailingTrivia: trailingTrivia ) diff --git a/Sources/SwiftSyntax/generated/SyntaxCollections.swift b/Sources/SwiftSyntax/generated/SyntaxCollections.swift index e1b52a7423b..dc52046e843 100644 --- a/Sources/SwiftSyntax/generated/SyntaxCollections.swift +++ b/Sources/SwiftSyntax/generated/SyntaxCollections.swift @@ -286,6 +286,7 @@ public struct ClosureShorthandParameterListSyntax: SyntaxCollection, SyntaxHasha /// /// ### Contained in /// +/// - ``AccessorBlockSyntax``.``AccessorBlockSyntax/accessors`` /// - ``ClosureExprSyntax``.``ClosureExprSyntax/statements`` /// - ``CodeBlockSyntax``.``CodeBlockSyntax/statements`` /// - ``IfConfigClauseSyntax``.``IfConfigClauseSyntax/elements`` diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodes.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodes.swift index 1834fc481c0..7a6598843a4 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodes.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodes.swift @@ -29,6 +29,36 @@ public protocol RawTypeSyntaxNodeProtocol: RawSyntaxNodeProtocol {} @_spi(RawSyntax) public struct RawAccessorBlockSyntax: RawSyntaxNodeProtocol { + public enum Accessors: RawSyntaxNodeProtocol { + case `accessors`(RawAccessorDeclListSyntax) + case `getter`(RawCodeBlockItemListSyntax) + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return RawAccessorDeclListSyntax.isKindOf(raw) || RawCodeBlockItemListSyntax.isKindOf(raw) + } + + public var raw: RawSyntax { + switch self { + case .accessors(let node): + return node.raw + case .getter(let node): + return node.raw + } + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + if let node = RawAccessorDeclListSyntax(other) { + self = .accessors(node) + return + } + if let node = RawCodeBlockItemListSyntax(other) { + self = .getter(node) + return + } + return nil + } + } + @_spi(RawSyntax) public var layoutView: RawSyntaxLayoutView { return raw.layoutView! @@ -60,7 +90,7 @@ public struct RawAccessorBlockSyntax: RawSyntaxNodeProtocol { _ unexpectedBeforeLeftBrace: RawUnexpectedNodesSyntax? = nil, leftBrace: RawTokenSyntax, _ unexpectedBetweenLeftBraceAndAccessors: RawUnexpectedNodesSyntax? = nil, - accessors: RawAccessorDeclListSyntax, + accessors: Accessors, _ unexpectedBetweenAccessorsAndRightBrace: RawUnexpectedNodesSyntax? = nil, rightBrace: RawTokenSyntax, _ unexpectedAfterRightBrace: RawUnexpectedNodesSyntax? = nil, @@ -92,8 +122,8 @@ public struct RawAccessorBlockSyntax: RawSyntaxNodeProtocol { layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) } - public var accessors: RawAccessorDeclListSyntax { - layoutView.children[3].map(RawAccessorDeclListSyntax.init(raw:))! + public var accessors: RawSyntax { + layoutView.children[3]! } public var unexpectedBetweenAccessorsAndRightBrace: RawUnexpectedNodesSyntax? { @@ -16068,36 +16098,6 @@ public struct RawPatternBindingListSyntax: RawSyntaxNodeProtocol { @_spi(RawSyntax) public struct RawPatternBindingSyntax: RawSyntaxNodeProtocol { - public enum Accessors: RawSyntaxNodeProtocol { - case `accessors`(RawAccessorBlockSyntax) - case `getter`(RawCodeBlockSyntax) - - public static func isKindOf(_ raw: RawSyntax) -> Bool { - return RawAccessorBlockSyntax.isKindOf(raw) || RawCodeBlockSyntax.isKindOf(raw) - } - - public var raw: RawSyntax { - switch self { - case .accessors(let node): - return node.raw - case .getter(let node): - return node.raw - } - } - - public init?(_ other: some RawSyntaxNodeProtocol) { - if let node = RawAccessorBlockSyntax(other) { - self = .accessors(node) - return - } - if let node = RawCodeBlockSyntax(other) { - self = .getter(node) - return - } - return nil - } - } - @_spi(RawSyntax) public var layoutView: RawSyntaxLayoutView { return raw.layoutView! @@ -16132,9 +16132,9 @@ public struct RawPatternBindingSyntax: RawSyntaxNodeProtocol { typeAnnotation: RawTypeAnnotationSyntax?, _ unexpectedBetweenTypeAnnotationAndInitializer: RawUnexpectedNodesSyntax? = nil, initializer: RawInitializerClauseSyntax?, - _ unexpectedBetweenInitializerAndAccessors: RawUnexpectedNodesSyntax? = nil, - accessors: Accessors?, - _ unexpectedBetweenAccessorsAndTrailingComma: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenInitializerAndAccessorBlock: RawUnexpectedNodesSyntax? = nil, + accessorBlock: RawAccessorBlockSyntax?, + _ unexpectedBetweenAccessorBlockAndTrailingComma: RawUnexpectedNodesSyntax? = nil, trailingComma: RawTokenSyntax?, _ unexpectedAfterTrailingComma: RawUnexpectedNodesSyntax? = nil, arena: __shared SyntaxArena @@ -16148,9 +16148,9 @@ public struct RawPatternBindingSyntax: RawSyntaxNodeProtocol { layout[3] = typeAnnotation?.raw layout[4] = unexpectedBetweenTypeAnnotationAndInitializer?.raw layout[5] = initializer?.raw - layout[6] = unexpectedBetweenInitializerAndAccessors?.raw - layout[7] = accessors?.raw - layout[8] = unexpectedBetweenAccessorsAndTrailingComma?.raw + layout[6] = unexpectedBetweenInitializerAndAccessorBlock?.raw + layout[7] = accessorBlock?.raw + layout[8] = unexpectedBetweenAccessorBlockAndTrailingComma?.raw layout[9] = trailingComma?.raw layout[10] = unexpectedAfterTrailingComma?.raw } @@ -16181,15 +16181,15 @@ public struct RawPatternBindingSyntax: RawSyntaxNodeProtocol { layoutView.children[5].map(RawInitializerClauseSyntax.init(raw:)) } - public var unexpectedBetweenInitializerAndAccessors: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenInitializerAndAccessorBlock: RawUnexpectedNodesSyntax? { layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) } - public var accessors: RawSyntax? { - layoutView.children[7] + public var accessorBlock: RawAccessorBlockSyntax? { + layoutView.children[7].map(RawAccessorBlockSyntax.init(raw:)) } - public var unexpectedBetweenAccessorsAndTrailingComma: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenAccessorBlockAndTrailingComma: RawUnexpectedNodesSyntax? { layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) } @@ -19391,36 +19391,6 @@ public struct RawSubscriptCallExprSyntax: RawExprSyntaxNodeProtocol { @_spi(RawSyntax) public struct RawSubscriptDeclSyntax: RawDeclSyntaxNodeProtocol { - public enum Accessors: RawSyntaxNodeProtocol { - case `accessors`(RawAccessorBlockSyntax) - case `getter`(RawCodeBlockSyntax) - - public static func isKindOf(_ raw: RawSyntax) -> Bool { - return RawAccessorBlockSyntax.isKindOf(raw) || RawCodeBlockSyntax.isKindOf(raw) - } - - public var raw: RawSyntax { - switch self { - case .accessors(let node): - return node.raw - case .getter(let node): - return node.raw - } - } - - public init?(_ other: some RawSyntaxNodeProtocol) { - if let node = RawAccessorBlockSyntax(other) { - self = .accessors(node) - return - } - if let node = RawCodeBlockSyntax(other) { - self = .getter(node) - return - } - return nil - } - } - @_spi(RawSyntax) public var layoutView: RawSyntaxLayoutView { return raw.layoutView! @@ -19463,9 +19433,9 @@ public struct RawSubscriptDeclSyntax: RawDeclSyntaxNodeProtocol { returnClause: RawReturnClauseSyntax, _ unexpectedBetweenReturnClauseAndGenericWhereClause: RawUnexpectedNodesSyntax? = nil, genericWhereClause: RawGenericWhereClauseSyntax?, - _ unexpectedBetweenGenericWhereClauseAndAccessors: RawUnexpectedNodesSyntax? = nil, - accessors: Accessors?, - _ unexpectedAfterAccessors: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenGenericWhereClauseAndAccessorBlock: RawUnexpectedNodesSyntax? = nil, + accessorBlock: RawAccessorBlockSyntax?, + _ unexpectedAfterAccessorBlock: RawUnexpectedNodesSyntax? = nil, arena: __shared SyntaxArena ) { let raw = RawSyntax.makeLayout( @@ -19485,9 +19455,9 @@ public struct RawSubscriptDeclSyntax: RawDeclSyntaxNodeProtocol { layout[11] = returnClause.raw layout[12] = unexpectedBetweenReturnClauseAndGenericWhereClause?.raw layout[13] = genericWhereClause?.raw - layout[14] = unexpectedBetweenGenericWhereClauseAndAccessors?.raw - layout[15] = accessors?.raw - layout[16] = unexpectedAfterAccessors?.raw + layout[14] = unexpectedBetweenGenericWhereClauseAndAccessorBlock?.raw + layout[15] = accessorBlock?.raw + layout[16] = unexpectedAfterAccessorBlock?.raw } self.init(unchecked: raw) } @@ -19548,15 +19518,15 @@ public struct RawSubscriptDeclSyntax: RawDeclSyntaxNodeProtocol { layoutView.children[13].map(RawGenericWhereClauseSyntax.init(raw:)) } - public var unexpectedBetweenGenericWhereClauseAndAccessors: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenGenericWhereClauseAndAccessorBlock: RawUnexpectedNodesSyntax? { layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:)) } - public var accessors: RawSyntax? { - layoutView.children[15] + public var accessorBlock: RawAccessorBlockSyntax? { + layoutView.children[15].map(RawAccessorBlockSyntax.init(raw:)) } - public var unexpectedAfterAccessors: RawUnexpectedNodesSyntax? { + public var unexpectedAfterAccessorBlock: RawUnexpectedNodesSyntax? { layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:)) } } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 618df4e69c4..1a0f8d35444 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -210,7 +210,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.leftBrace)])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 3, verify(layout[3], as: RawAccessorDeclListSyntax.self)) + assertAnyHasNoError(kind, 3, [ + verify(layout[3], as: RawSyntax.self)]) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.rightBrace)])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) @@ -2013,8 +2014,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 5, verify(layout[5], as: RawInitializerClauseSyntax?.self)) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) - assertAnyHasNoError(kind, 7, [ - verify(layout[7], as: RawSyntax?.self)]) + assertNoError(kind, 7, verify(layout[7], as: RawAccessorBlockSyntax?.self)) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.comma)])) assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) @@ -2380,8 +2380,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 12, verify(layout[12], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 13, verify(layout[13], as: RawGenericWhereClauseSyntax?.self)) assertNoError(kind, 14, verify(layout[14], as: RawUnexpectedNodesSyntax?.self)) - assertAnyHasNoError(kind, 15, [ - verify(layout[15], as: RawSyntax?.self)]) + assertNoError(kind, 15, verify(layout[15], as: RawAccessorBlockSyntax?.self)) assertNoError(kind, 16, verify(layout[16], as: RawUnexpectedNodesSyntax?.self)) case .superExpr: assert(layout.count == 3) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift index 0d5be44edca..9c0288f7b43 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxDeclNodes.swift @@ -6288,50 +6288,8 @@ public struct StructDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { /// - `parameterClause`: ``FunctionParameterClauseSyntax`` /// - `returnClause`: ``ReturnClauseSyntax`` /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? -/// - `accessors`: (``AccessorBlockSyntax`` | ``CodeBlockSyntax``)? +/// - `accessorBlock`: ``AccessorBlockSyntax``? public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { - public enum Accessors: SyntaxChildChoices, SyntaxHashable { - case `accessors`(AccessorBlockSyntax) - case `getter`(CodeBlockSyntax) - - public var _syntaxNode: Syntax { - switch self { - case .accessors(let node): - return node._syntaxNode - case .getter(let node): - return node._syntaxNode - } - } - - init(_ data: SyntaxData) { - self.init(Syntax(data))! - } - - public init(_ node: AccessorBlockSyntax) { - self = .accessors(node) - } - - public init(_ node: CodeBlockSyntax) { - self = .getter(node) - } - - public init?(_ node: some SyntaxProtocol) { - if let node = node.as(AccessorBlockSyntax.self) { - self = .accessors(node) - return - } - if let node = node.as(CodeBlockSyntax.self) { - self = .getter(node) - return - } - return nil - } - - public static var structure: SyntaxNodeStructure { - return .choices([.node(AccessorBlockSyntax.self), .node(CodeBlockSyntax.self)]) - } - } - public let _syntaxNode: Syntax public init?(_ node: some SyntaxProtocol) { @@ -6370,9 +6328,9 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { returnClause: ReturnClauseSyntax, _ unexpectedBetweenReturnClauseAndGenericWhereClause: UnexpectedNodesSyntax? = nil, genericWhereClause: GenericWhereClauseSyntax? = nil, - _ unexpectedBetweenGenericWhereClauseAndAccessors: UnexpectedNodesSyntax? = nil, - accessors: Accessors? = nil, - _ unexpectedAfterAccessors: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenGenericWhereClauseAndAccessorBlock: UnexpectedNodesSyntax? = nil, + accessorBlock: AccessorBlockSyntax? = nil, + _ unexpectedAfterAccessorBlock: UnexpectedNodesSyntax? = nil, trailingTrivia: Trivia? = nil ) { @@ -6393,9 +6351,9 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { returnClause, unexpectedBetweenReturnClauseAndGenericWhereClause, genericWhereClause, - unexpectedBetweenGenericWhereClauseAndAccessors, - accessors, - unexpectedAfterAccessors + unexpectedBetweenGenericWhereClauseAndAccessorBlock, + accessorBlock, + unexpectedAfterAccessorBlock ))) { (arena, _) in let layout: [RawSyntax?] = [ unexpectedBeforeAttributes?.raw, @@ -6412,9 +6370,9 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { returnClause.raw, unexpectedBetweenReturnClauseAndGenericWhereClause?.raw, genericWhereClause?.raw, - unexpectedBetweenGenericWhereClauseAndAccessors?.raw, - accessors?.raw, - unexpectedAfterAccessors?.raw + unexpectedBetweenGenericWhereClauseAndAccessorBlock?.raw, + accessorBlock?.raw, + unexpectedAfterAccessorBlock?.raw ] let raw = RawSyntax.makeLayout( kind: SyntaxKind.subscriptDecl, @@ -6605,7 +6563,7 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { } } - public var unexpectedBetweenGenericWhereClauseAndAccessors: UnexpectedNodesSyntax? { + public var unexpectedBetweenGenericWhereClauseAndAccessorBlock: UnexpectedNodesSyntax? { get { return data.child(at: 14, parent: Syntax(self)).map(UnexpectedNodesSyntax.init) } @@ -6614,16 +6572,16 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { } } - public var accessors: Accessors? { + public var accessorBlock: AccessorBlockSyntax? { get { - return data.child(at: 15, parent: Syntax(self)).map(Accessors.init) + return data.child(at: 15, parent: Syntax(self)).map(AccessorBlockSyntax.init) } set(value) { self = SubscriptDeclSyntax(data.replacingChild(at: 15, with: value?.data, arena: SyntaxArena())) } } - public var unexpectedAfterAccessors: UnexpectedNodesSyntax? { + public var unexpectedAfterAccessorBlock: UnexpectedNodesSyntax? { get { return data.child(at: 16, parent: Syntax(self)).map(UnexpectedNodesSyntax.init) } @@ -6648,9 +6606,9 @@ public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable { \Self.returnClause, \Self.unexpectedBetweenReturnClauseAndGenericWhereClause, \Self.genericWhereClause, - \Self.unexpectedBetweenGenericWhereClauseAndAccessors, - \Self.accessors, - \Self.unexpectedAfterAccessors + \Self.unexpectedBetweenGenericWhereClauseAndAccessorBlock, + \Self.accessorBlock, + \Self.unexpectedAfterAccessorBlock ]) } } diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift index 284296b1772..b376925f33e 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodes.swift @@ -17,14 +17,56 @@ /// ### Children /// /// - `leftBrace`: `'{'` -/// - `accessors`: ``AccessorDeclListSyntax`` +/// - `accessors`: (``AccessorDeclListSyntax`` | ``CodeBlockItemListSyntax``) /// - `rightBrace`: `'}'` /// /// ### Contained in /// -/// - ``PatternBindingSyntax``.``PatternBindingSyntax/accessors`` -/// - ``SubscriptDeclSyntax``.``SubscriptDeclSyntax/accessors`` +/// - ``PatternBindingSyntax``.``PatternBindingSyntax/accessorBlock`` +/// - ``SubscriptDeclSyntax``.``SubscriptDeclSyntax/accessorBlock`` public struct AccessorBlockSyntax: SyntaxProtocol, SyntaxHashable { + public enum Accessors: SyntaxChildChoices, SyntaxHashable { + case `accessors`(AccessorDeclListSyntax) + case `getter`(CodeBlockItemListSyntax) + + public var _syntaxNode: Syntax { + switch self { + case .accessors(let node): + return node._syntaxNode + case .getter(let node): + return node._syntaxNode + } + } + + init(_ data: SyntaxData) { + self.init(Syntax(data))! + } + + public init(_ node: AccessorDeclListSyntax) { + self = .accessors(node) + } + + public init(_ node: CodeBlockItemListSyntax) { + self = .getter(node) + } + + public init?(_ node: some SyntaxProtocol) { + if let node = node.as(AccessorDeclListSyntax.self) { + self = .accessors(node) + return + } + if let node = node.as(CodeBlockItemListSyntax.self) { + self = .getter(node) + return + } + return nil + } + + public static var structure: SyntaxNodeStructure { + return .choices([.node(AccessorDeclListSyntax.self), .node(CodeBlockItemListSyntax.self)]) + } + } + public let _syntaxNode: Syntax public init?(_ node: some SyntaxProtocol) { @@ -50,7 +92,7 @@ public struct AccessorBlockSyntax: SyntaxProtocol, SyntaxHashable { _ unexpectedBeforeLeftBrace: UnexpectedNodesSyntax? = nil, leftBrace: TokenSyntax = .leftBraceToken(), _ unexpectedBetweenLeftBraceAndAccessors: UnexpectedNodesSyntax? = nil, - accessors: AccessorDeclListSyntax, + accessors: Accessors, _ unexpectedBetweenAccessorsAndRightBrace: UnexpectedNodesSyntax? = nil, rightBrace: TokenSyntax = .rightBraceToken(), _ unexpectedAfterRightBrace: UnexpectedNodesSyntax? = nil, @@ -117,39 +159,15 @@ public struct AccessorBlockSyntax: SyntaxProtocol, SyntaxHashable { } } - public var accessors: AccessorDeclListSyntax { + public var accessors: Accessors { get { - return AccessorDeclListSyntax(data.child(at: 3, parent: Syntax(self))!) + return Accessors(data.child(at: 3, parent: Syntax(self))!) } set(value) { self = AccessorBlockSyntax(data.replacingChild(at: 3, with: value.data, arena: SyntaxArena())) } } - /// Adds the provided `element` to the node's `accessors` - /// collection. - /// - param element: The new `Accessor` to add to the node's - /// `accessors` collection. - /// - returns: A copy of the receiver with the provided `Accessor` - /// appended to its `accessors` collection. - public func addAccessor(_ element: AccessorDeclSyntax) -> AccessorBlockSyntax { - var collection: RawSyntax - let arena = SyntaxArena() - if let col = raw.layoutView!.children[3] { - collection = col.layoutView!.appending(element.raw, arena: arena) - } else { - collection = RawSyntax.makeLayout(kind: SyntaxKind.accessorDeclList, - from: [element.raw], arena: arena) - } - let newData = data.replacingChild( - at: 3, - with: collection, - rawNodeArena: arena, - allocationArena: arena - ) - return AccessorBlockSyntax(newData) - } - public var unexpectedBetweenAccessorsAndRightBrace: UnexpectedNodesSyntax? { get { return data.child(at: 4, parent: Syntax(self)).map(UnexpectedNodesSyntax.init) @@ -3910,9 +3928,7 @@ public struct CodeBlockItemSyntax: SyntaxProtocol, SyntaxHashable { /// - ``IfExprSyntax``.``IfExprSyntax/body`` /// - ``IfExprSyntax``.``IfExprSyntax/elseBody`` /// - ``InitializerDeclSyntax``.``InitializerDeclSyntax/body`` -/// - ``PatternBindingSyntax``.``PatternBindingSyntax/accessors`` /// - ``RepeatStmtSyntax``.``RepeatStmtSyntax/body`` -/// - ``SubscriptDeclSyntax``.``SubscriptDeclSyntax/accessors`` /// - ``WhileStmtSyntax``.``WhileStmtSyntax/body`` public struct CodeBlockSyntax: SyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax @@ -14267,55 +14283,13 @@ public struct OriginallyDefinedInAttributeArgumentsSyntax: SyntaxProtocol, Synta /// - `pattern`: ``PatternSyntax`` /// - `typeAnnotation`: ``TypeAnnotationSyntax``? /// - `initializer`: ``InitializerClauseSyntax``? -/// - `accessors`: (``AccessorBlockSyntax`` | ``CodeBlockSyntax``)? +/// - `accessorBlock`: ``AccessorBlockSyntax``? /// - `trailingComma`: `','`? /// /// ### Contained in /// /// - ``PatternBindingListSyntax`` public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { - public enum Accessors: SyntaxChildChoices, SyntaxHashable { - case `accessors`(AccessorBlockSyntax) - case `getter`(CodeBlockSyntax) - - public var _syntaxNode: Syntax { - switch self { - case .accessors(let node): - return node._syntaxNode - case .getter(let node): - return node._syntaxNode - } - } - - init(_ data: SyntaxData) { - self.init(Syntax(data))! - } - - public init(_ node: AccessorBlockSyntax) { - self = .accessors(node) - } - - public init(_ node: CodeBlockSyntax) { - self = .getter(node) - } - - public init?(_ node: some SyntaxProtocol) { - if let node = node.as(AccessorBlockSyntax.self) { - self = .accessors(node) - return - } - if let node = node.as(CodeBlockSyntax.self) { - self = .getter(node) - return - } - return nil - } - - public static var structure: SyntaxNodeStructure { - return .choices([.node(AccessorBlockSyntax.self), .node(CodeBlockSyntax.self)]) - } - } - public let _syntaxNode: Syntax public init?(_ node: some SyntaxProtocol) { @@ -14344,9 +14318,9 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { typeAnnotation: TypeAnnotationSyntax? = nil, _ unexpectedBetweenTypeAnnotationAndInitializer: UnexpectedNodesSyntax? = nil, initializer: InitializerClauseSyntax? = nil, - _ unexpectedBetweenInitializerAndAccessors: UnexpectedNodesSyntax? = nil, - accessors: Accessors? = nil, - _ unexpectedBetweenAccessorsAndTrailingComma: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenInitializerAndAccessorBlock: UnexpectedNodesSyntax? = nil, + accessorBlock: AccessorBlockSyntax? = nil, + _ unexpectedBetweenAccessorBlockAndTrailingComma: UnexpectedNodesSyntax? = nil, trailingComma: TokenSyntax? = nil, _ unexpectedAfterTrailingComma: UnexpectedNodesSyntax? = nil, trailingTrivia: Trivia? = nil @@ -14361,9 +14335,9 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { typeAnnotation, unexpectedBetweenTypeAnnotationAndInitializer, initializer, - unexpectedBetweenInitializerAndAccessors, - accessors, - unexpectedBetweenAccessorsAndTrailingComma, + unexpectedBetweenInitializerAndAccessorBlock, + accessorBlock, + unexpectedBetweenAccessorBlockAndTrailingComma, trailingComma, unexpectedAfterTrailingComma ))) { (arena, _) in @@ -14374,9 +14348,9 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { typeAnnotation?.raw, unexpectedBetweenTypeAnnotationAndInitializer?.raw, initializer?.raw, - unexpectedBetweenInitializerAndAccessors?.raw, - accessors?.raw, - unexpectedBetweenAccessorsAndTrailingComma?.raw, + unexpectedBetweenInitializerAndAccessorBlock?.raw, + accessorBlock?.raw, + unexpectedBetweenAccessorBlockAndTrailingComma?.raw, trailingComma?.raw, unexpectedAfterTrailingComma?.raw ] @@ -14447,7 +14421,7 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { } } - public var unexpectedBetweenInitializerAndAccessors: UnexpectedNodesSyntax? { + public var unexpectedBetweenInitializerAndAccessorBlock: UnexpectedNodesSyntax? { get { return data.child(at: 6, parent: Syntax(self)).map(UnexpectedNodesSyntax.init) } @@ -14456,16 +14430,16 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { } } - public var accessors: Accessors? { + public var accessorBlock: AccessorBlockSyntax? { get { - return data.child(at: 7, parent: Syntax(self)).map(Accessors.init) + return data.child(at: 7, parent: Syntax(self)).map(AccessorBlockSyntax.init) } set(value) { self = PatternBindingSyntax(data.replacingChild(at: 7, with: value?.data, arena: SyntaxArena())) } } - public var unexpectedBetweenAccessorsAndTrailingComma: UnexpectedNodesSyntax? { + public var unexpectedBetweenAccessorBlockAndTrailingComma: UnexpectedNodesSyntax? { get { return data.child(at: 8, parent: Syntax(self)).map(UnexpectedNodesSyntax.init) } @@ -14500,9 +14474,9 @@ public struct PatternBindingSyntax: SyntaxProtocol, SyntaxHashable { \Self.typeAnnotation, \Self.unexpectedBetweenTypeAnnotationAndInitializer, \Self.initializer, - \Self.unexpectedBetweenInitializerAndAccessors, - \Self.accessors, - \Self.unexpectedBetweenAccessorsAndTrailingComma, + \Self.unexpectedBetweenInitializerAndAccessorBlock, + \Self.accessorBlock, + \Self.unexpectedBetweenAccessorBlockAndTrailingComma, \Self.trailingComma, \Self.unexpectedAfterTrailingComma ]) diff --git a/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift b/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift index f0f4ed09746..bde93b3a5ac 100644 --- a/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift +++ b/Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift @@ -355,7 +355,7 @@ public extension VariableDeclSyntax { self = castedDecl precondition(self.bindings.count == 1) var binding: PatternBindingSyntax? = self.bindings.last - binding?.accessors = try .getter(CodeBlockSyntax(statements: accessor())) + binding?.accessorBlock = AccessorBlockSyntax(accessors: .getter(try accessor())) bindings = PatternBindingListSyntax([binding].compactMap { $0 }) } } diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index c824aca4af7..2e542264dd8 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -354,7 +354,7 @@ public func collapse( if role == .accessor, let varDecl = declarationNode.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, - binding.accessors == nil + binding.accessorBlock == nil { let indentation = String(repeating: " ", count: 4) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift index 41c3a23fef3..e8eb82494a9 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift @@ -343,13 +343,11 @@ class MacroApplication: SyntaxRewriter { visitedVarDecl.bindings.with( \.[visitedVarDecl.bindings.startIndex], binding.with( - \.accessors, - .accessors( - .init( - leftBrace: .leftBraceToken(leadingTrivia: .space), - accessors: .init(accessors), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) + \.accessorBlock, + AccessorBlockSyntax( + leftBrace: .leftBraceToken(leadingTrivia: .space), + accessors: .accessors(.init(accessors)), + rightBrace: .rightBraceToken(leadingTrivia: .newline) ) ) ) diff --git a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift index 8dd03faa837..98aefff19dc 100644 --- a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift +++ b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift @@ -285,12 +285,10 @@ final class BasicFormatTest: XCTestCase { colon: .colonToken(trailingTrivia: .space), type: TypeSyntax(IdentifierTypeSyntax(name: .identifier("Int"))) ), - accessors: PatternBindingSyntax.Accessors( - AccessorBlockSyntax( - leftBrace: .leftBraceToken(leadingTrivia: .space), - accessors: AccessorDeclListSyntax([]), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) + accessorBlock: AccessorBlockSyntax( + leftBrace: .leftBraceToken(leadingTrivia: .space), + accessors: .accessors(AccessorListSyntax([])), + rightBrace: .rightBraceToken(leadingTrivia: .newline) ) ) ]) diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index fcd0ba8e4a3..97ccaa089cd 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -1011,11 +1011,13 @@ final class DeclarationTests: XCTestCase { """, substructure: Syntax( AccessorBlockSyntax( - accessors: AccessorDeclListSyntax([ - AccessorDeclSyntax( - accessorSpecifier: .keyword(.get) - ) - ]), + accessors: .accessors( + AccessorListSyntax([ + AccessorDeclSyntax( + accessorSpecifier: .keyword(.get) + ) + ]) + ), UnexpectedNodesSyntax([ TokenSyntax.identifier("bogus"), TokenSyntax.keyword(.rethrows), TokenSyntax.identifier("set"), diff --git a/Tests/SwiftSyntaxBuilderTest/ExtensionDeclTests.swift b/Tests/SwiftSyntaxBuilderTest/ExtensionDeclTests.swift index 1f4da834a67..db2741c3d39 100644 --- a/Tests/SwiftSyntaxBuilderTest/ExtensionDeclTests.swift +++ b/Tests/SwiftSyntaxBuilderTest/ExtensionDeclTests.swift @@ -18,7 +18,7 @@ final class ExtensionDeclTests: XCTestCase { func testExtensionDecl() { let keywords = ["associatedtype", "class"].map { keyword -> VariableDeclSyntax in // We need to use `CodeBlock` here to ensure there is braces around. - let body = CodeBlockSyntax { + let body = CodeBlockItemListSyntax { FunctionCallExprSyntax(callee: ExprSyntax("TokenSyntax.\(raw: keyword)Keyword")) } @@ -29,7 +29,7 @@ final class ExtensionDeclTests: XCTestCase { PatternBindingSyntax( pattern: PatternSyntax("`\(raw: keyword)`"), typeAnnotation: TypeAnnotationSyntax(type: TypeSyntax("TokenSyntax")), - accessors: .getter(body) + accessorBlock: AccessorBlockSyntax(accessors: .getter(body)) ) } diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift index afeb137cf1f..8ca520daa12 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift @@ -252,7 +252,7 @@ extension PropertyWrapper: AccessorMacro { guard let varDecl = declaration.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, - binding.accessors == nil + binding.accessorBlock == nil else { return [] } @@ -287,7 +287,7 @@ extension PropertyWrapper: PeerMacro { let binding = varDecl.bindings.first, let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, let type = binding.typeAnnotation?.type, - binding.accessors == nil + binding.accessorBlock == nil else { return [] } @@ -497,11 +497,11 @@ public struct WrapStoredProperties: MemberAttributeMacro { } let binding = property.bindings.first! - switch binding.accessors { + switch binding.accessorBlock?.accessors { case .none: break - case .accessors(let node): - for accessor in node.accessors { + case .accessors(let accessorList): + for accessor in accessorList { switch accessor.accessorSpecifier.tokenKind { case .keyword(.get), .keyword(.set): return [] @@ -579,7 +579,7 @@ extension CustomTypeWrapperMacro: AccessorMacro { guard let property = declaration.as(VariableDeclSyntax.self), let binding = property.bindings.first, let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, - binding.accessors == nil + binding.accessorBlock == nil else { return [] } From a050bdccc3be16447d2b799a975bd96e82f44670 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 24 Jul 2023 18:08:04 -0700 Subject: [PATCH 4/6] Re-work MacroSystem on top of the same parsing functions that the compiler invokes This should make the MacroSystem behave more similarly to the expansions performed by the compiler. --- Package.swift | 2 +- Sources/SwiftSyntaxBuilder/Indenter.swift | 5 +- .../BasicMacroExpansionContext.swift | 65 +- .../SwiftSyntaxMacroExpansion/CMakeLists.txt | 2 +- .../IndentationUtils.swift | 95 ++ .../MacroExpansion.swift | 153 +- .../MacroSystem.swift | 1236 +++++++++++------ .../Syntax+MacroEvaluation.swift | 113 -- .../Assertions.swift | 19 +- .../BasicFormatTests.swift | 2 +- Tests/SwiftParserTest/DeclarationTests.swift | 2 +- .../MacroSystemTests.swift | 686 ++++++--- 12 files changed, 1611 insertions(+), 769 deletions(-) create mode 100644 Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift delete mode 100644 Sources/SwiftSyntaxMacroExpansion/Syntax+MacroEvaluation.swift diff --git a/Package.swift b/Package.swift index 589c6ca6a7e..15eb17c0785 100644 --- a/Package.swift +++ b/Package.swift @@ -182,7 +182,7 @@ let package = Package( .target( name: "SwiftSyntaxMacroExpansion", - dependencies: ["SwiftSyntax", "SwiftSyntaxMacros", "SwiftDiagnostics"], + dependencies: ["SwiftSyntax", "SwiftSyntaxBuilder", "SwiftSyntaxMacros", "SwiftDiagnostics"], exclude: ["CMakeLists.txt"] ), diff --git a/Sources/SwiftSyntaxBuilder/Indenter.swift b/Sources/SwiftSyntaxBuilder/Indenter.swift index 39aeca548d6..da8316871f2 100644 --- a/Sources/SwiftSyntaxBuilder/Indenter.swift +++ b/Sources/SwiftSyntaxBuilder/Indenter.swift @@ -25,7 +25,8 @@ extension Trivia { } } -class Indenter: SyntaxRewriter { +/// Adds a given amount of indentation after every newline in a syntax tree. +public class Indenter: SyntaxRewriter { let indentation: Trivia init(indentation: Trivia) { @@ -33,7 +34,7 @@ class Indenter: SyntaxRewriter { super.init(viewMode: .sourceAccurate) } - /// Adds `indentation` after all newlines in the syntax tree. + /// Add `indentation` after all newlines in the syntax tree. public static func indent( _ node: SyntaxType, indentation: Trivia diff --git a/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift b/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift index 4815fc8eef3..fa21a40703f 100644 --- a/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift +++ b/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift @@ -48,11 +48,13 @@ public class BasicMacroExpansionContext { /// information about that source file. private var sourceFiles: [SourceFileSyntax: KnownSourceFile] = [:] - /// Mapping from intentionally-disconnected syntax node roots to the - /// absolute offsets that have within a given source file, which is used - /// to establish the link between a node that been intentionally disconnected - /// from a source file to hide information from the macro implementation. - private var disconnectedNodes: [Syntax: (SourceFileSyntax, Int)] = [:] + /// Mapping from intentionally-disconnected syntax nodes to the corresponding + /// nodes in the original source file. + /// + /// This is used to establish the link between a node that been intentionally + /// disconnected from a source file to hide information from the macro + /// implementation. + private var detachedNodes: [Syntax: Syntax] = [:] /// The macro expansion discriminator, which is used to form unique names /// when requested. @@ -69,24 +71,10 @@ public class BasicMacroExpansionContext { } extension BasicMacroExpansionContext { - /// Note that the given node that was at the given position in the provided - /// source file has been disconnected and is now a new root. - private func addDisconnected( - _ node: some SyntaxProtocol, - at offset: AbsolutePosition, - in sourceFile: SourceFileSyntax - ) { - disconnectedNodes[Syntax(node)] = (sourceFile, offset.utf8Offset) - } - /// Detach the given node, and record where it came from. public func detach(_ node: Node) -> Node { let detached = node.detached - - if let rootSourceFile = node.root.as(SourceFileSyntax.self) { - addDisconnected(detached, at: node.position, in: rootSourceFile) - } - + detachedNodes[Syntax(detached)] = Syntax(node) return detached } } @@ -136,6 +124,27 @@ extension BasicMacroExpansionContext: MacroExpansionContext { diagnostics.append(diagnostic) } + /// Translates a position from a detached node to the corresponding location + /// in the original source file. + /// + /// - Parameters: + /// - position: The position to translate + /// - node: The node at which the position is anchored. This node is used to + /// find the offset in the original source file + /// - fileName: The file name that should be used in the `SourceLocation` + /// - Returns: The location in the original source file + public func location( + for position: AbsolutePosition, + anchoredAt node: Syntax, + fileName: String + ) -> SourceLocation { + guard let nodeInOriginalTree = detachedNodes[node.root] else { + return SourceLocationConverter(file: fileName, tree: node.root).location(for: position) + } + let adjustedPosition = position + SourceLength(utf8Length: nodeInOriginalTree.position.utf8Offset) + return SourceLocationConverter(file: fileName, tree: nodeInOriginalTree.root).location(for: adjustedPosition) + } + public func location( of node: some SyntaxProtocol, at position: PositionInSyntaxNode, @@ -143,21 +152,21 @@ extension BasicMacroExpansionContext: MacroExpansionContext { ) -> AbstractSourceLocation? { // Dig out the root source file and figure out how we need to adjust the // offset of the given syntax node to adjust for it. - let rootSourceFile: SourceFileSyntax - let offsetAdjustment: Int + let rootSourceFile: SourceFileSyntax? + let offsetAdjustment: SourceLength if let directRootSourceFile = node.root.as(SourceFileSyntax.self) { // The syntax node came from the source file itself. rootSourceFile = directRootSourceFile - offsetAdjustment = 0 - } else if let (adjustedSourceFile, offset) = disconnectedNodes[Syntax(node)] { + offsetAdjustment = .zero + } else if let nodeInOriginalTree = detachedNodes[Syntax(node)] { // The syntax node came from a disconnected root, so adjust for that. - rootSourceFile = adjustedSourceFile - offsetAdjustment = offset + rootSourceFile = nodeInOriginalTree.root.as(SourceFileSyntax.self) + offsetAdjustment = SourceLength(utf8Length: nodeInOriginalTree.position.utf8Offset) } else { return nil } - guard let knownRoot = sourceFiles[rootSourceFile] else { + guard let rootSourceFile, let knownRoot = sourceFiles[rootSourceFile] else { return nil } @@ -189,6 +198,6 @@ extension BasicMacroExpansionContext: MacroExpansionContext { // Do the location lookup. let converter = SourceLocationConverter(file: fileName, tree: rootSourceFile) - return AbstractSourceLocation(converter.location(for: rawPosition.advanced(by: offsetAdjustment))) + return AbstractSourceLocation(converter.location(for: rawPosition + offsetAdjustment)) } } diff --git a/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt b/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt index cb84c48bfdf..1ed29c312ac 100644 --- a/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt +++ b/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt @@ -1,10 +1,10 @@ add_swift_host_library(SwiftSyntaxMacroExpansion BasicMacroExpansionContext.swift FunctionParameterUtils.swift + IndentationUtils.swift MacroExpansion.swift MacroReplacement.swift MacroSystem.swift - Syntax+MacroEvaluation.swift ) target_link_libraries(SwiftSyntaxMacroExpansion PUBLIC diff --git a/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift new file mode 100644 index 00000000000..4011fbe04a9 --- /dev/null +++ b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +// MARK: SyntaxProtocol.indentationOfFirstLine + +extension SyntaxProtocol { + /// The indentation of the first line in this token. + var indentationOfFirstLine: Trivia { + guard let firstToken = self.firstToken(viewMode: .sourceAccurate) else { + return Trivia() + } + return firstToken.indentationOfLine + } +} + +// MARK: String.indented + +extension String { + /// Indents every new line in this string literal by `indentation`. + /// + /// - Note: The first line in the string gets indented as well. + func indented(by indentation: Trivia) -> String { + if isEmpty || indentation.isEmpty { + return self + } + + var indented = "" + var remaining = self[...] + while let nextNewline = remaining.firstIndex(where: { $0.isNewline }) { + if nextNewline != remaining.startIndex { + // Don’t add indentation if the line is empty. + indentation.write(to: &indented) + } + indented += remaining[...nextNewline] + remaining = remaining[remaining.index(after: nextNewline)...] + } + + if !remaining.isEmpty { + indentation.write(to: &indented) + indented += remaining + } + + return indented + } +} + +// MARK: SyntaxProtocol.stripp + +fileprivate class IndentationStripper: SyntaxRewriter { + override func visit(_ token: TokenSyntax) -> TokenSyntax { + if token.leadingTrivia.contains(where: \.isNewline) || token.trailingTrivia.contains(where: \.isNewline) { + return + token + .with(\.leadingTrivia, token.leadingTrivia.removingIndentation) + .with(\.trailingTrivia, token.trailingTrivia.removingIndentation) + } else { + return token + } + } +} + +extension Trivia { + /// Remove all indentation from the trivia. + var removingIndentation: Trivia { + var resultPieces: [TriviaPiece] = [] + var isAfterNewline = false + for piece in pieces { + if piece.isSpaceOrTab && isAfterNewline { + // Don’t add whitespace after a newline + continue + } + isAfterNewline = piece.isNewline + resultPieces.append(piece) + } + return Trivia(pieces: resultPieces) + } +} + +extension SyntaxProtocol { + /// This syntax node with all indentation removed. + var withIndentationRemoved: Self { + return IndentationStripper().rewrite(self).cast(Self.self) + } +} diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index 2e542264dd8..de60336d7df 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import SwiftSyntax +import SwiftBasicFormat @_spi(MacroExpansion) import SwiftSyntaxMacros public enum MacroRole { @@ -80,7 +81,9 @@ private enum MacroExpansionError: Error, CustomStringConvertible { /// - definition: a type conforms to one of freestanding `Macro` protocol. /// - macroRole: indicates which `Macro` protocol expansion should be performed /// - node: macro expansion syntax node (e.g. `#macroName(argument)`). -/// - in: context of the expansion. +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level /// - Returns: expanded source text. Upon failure (i.e. `definition.expansion()` /// throws) returns `nil`, and the diagnostics representing the `Error` are /// guaranteed to be added to context. @@ -88,7 +91,8 @@ public func expandFreestandingMacro( definition: Macro.Type, macroRole: MacroRole, node: FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext + in context: some MacroExpansionContext, + indentationWidth: Trivia = .spaces(4) ) -> String? { do { let expandedSyntax: Syntax @@ -100,8 +104,10 @@ public func expandFreestandingMacro( var rewritten = try declMacroDef.expansion(of: node, in: context) // Copy attributes and modifiers to the generated decls. if let expansionDecl = node.as(MacroExpansionDeclSyntax.self) { - let attributes = declMacroDef.propagateFreestandingMacroAttributes ? expansionDecl.attributes : nil - let modifiers = declMacroDef.propagateFreestandingMacroModifiers ? expansionDecl.modifiers : nil + // Strip any indentation from the attributes and modifiers that we are + // inheriting. The expanded macro should start at the leftmost column. + let attributes = declMacroDef.propagateFreestandingMacroAttributes ? expansionDecl.attributes?.withIndentationRemoved : nil + let modifiers = declMacroDef.propagateFreestandingMacroModifiers ? expansionDecl.modifiers?.withIndentationRemoved : nil rewritten = rewritten.map { $0.applying(attributes: attributes, modifiers: modifiers) } @@ -122,7 +128,7 @@ public func expandFreestandingMacro( (.codeItem, _): throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) } - return expandedSyntax.formattedExpansion(definition.formatMode) + return expandedSyntax.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) } catch { context.addDiagnostics(from: error, node: node) return nil @@ -171,7 +177,9 @@ public func expandFreestandingMacro( /// - declarationNode: target declaration syntax node to apply the expansion. /// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent /// context node of `declarationNode`. -/// - in: context of the expansion. +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level /// - Returns: A list of expanded source text. Upon failure (i.e. /// `definition.expansion()` throws) returns `nil`, and the diagnostics /// representing the `Error` are guaranteed to be added to context. @@ -183,7 +191,8 @@ public func expandAttachedMacroWithoutCollapsing parentDeclNode: DeclSyntax?, extendedType: TypeSyntax?, conformanceList: InheritedTypeListSyntax?, - in context: Context + in context: Context, + indentationWidth: Trivia = .spaces(4) ) -> [String]? { do { switch (definition, macroRole) { @@ -194,7 +203,7 @@ public func expandAttachedMacroWithoutCollapsing in: context ) return accessors.map { - $0.formattedExpansion(definition.formatMode) + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) } case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute): @@ -214,7 +223,7 @@ public func expandAttachedMacroWithoutCollapsing // Form a buffer containing an attribute list to return to the caller. return attributes.map { - $0.formattedExpansion(definition.formatMode) + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) } case (let attachedMacro as MemberMacro.Type, .member): @@ -231,7 +240,9 @@ public func expandAttachedMacroWithoutCollapsing ) // Form a buffer of member declarations to return to the caller. - return members.map { $0.formattedExpansion(definition.formatMode) } + return members.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } case (let attachedMacro as PeerMacro.Type, .peer): let peers = try attachedMacro.expansion( @@ -242,7 +253,7 @@ public func expandAttachedMacroWithoutCollapsing // Form a buffer of peer declarations to return to the caller. return peers.map { - $0.formattedExpansion(definition.formatMode) + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) } case (let attachedMacro as ExtensionMacro.Type, .extension): @@ -273,7 +284,7 @@ public func expandAttachedMacroWithoutCollapsing // Form a buffer of peer declarations to return to the caller. return extensions.map { - $0.formattedExpansion(definition.formatMode) + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) } default: @@ -294,7 +305,9 @@ public func expandAttachedMacroWithoutCollapsing /// - declarationNode: target declaration syntax node to apply the expansion. /// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent /// context node of `declarationNode`. -/// - in: context of the expansion. +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level /// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()` /// throws) returns `nil`, and the diagnostics representing the `Error` are /// guaranteed to be added to context. @@ -306,7 +319,8 @@ public func expandAttachedMacro( parentDeclNode: DeclSyntax?, extendedType: TypeSyntax?, conformanceList: InheritedTypeListSyntax?, - in context: Context + in context: Context, + indentationWidth: Trivia = .spaces(4) ) -> String? { let expandedSources = expandAttachedMacroWithoutCollapsing( definition: definition, @@ -316,21 +330,22 @@ public func expandAttachedMacro( parentDeclNode: parentDeclNode, extendedType: extendedType, conformanceList: conformanceList, - in: context + in: context, + indentationWidth: indentationWidth ) return expandedSources.map { - collapse(expansions: $0, for: macroRole, attachedTo: declarationNode) + collapse(expansions: $0, for: macroRole, attachedTo: declarationNode, indentationWidth: indentationWidth) } } fileprivate extension SyntaxProtocol { /// Perform a format if required and then trim any leading/trailing /// whitespace. - func formattedExpansion(_ mode: FormatMode) -> String { + func formattedExpansion(_ mode: FormatMode, indentationWidth: Trivia) -> String { let formatted: Syntax switch mode { case .auto: - formatted = self.formatted() + formatted = self.formatted(using: BasicFormat(indentationWidth: indentationWidth)) case .disabled: formatted = Syntax(self) } @@ -338,55 +353,83 @@ fileprivate extension SyntaxProtocol { } } +fileprivate extension DeclSyntax { + /// Returns this node with `attributes` and `modifiers` prepended to the + /// node’s attributes and modifiers, respectively. + /// + /// If the node can’t have attributes or modifiers, `attributes` or `modifiers` + /// are ignored and not applied. + func applying( + attributes: AttributeListSyntax?, + modifiers: DeclModifierListSyntax? + ) -> DeclSyntax { + func _combine(_ left: C, _ right: C?) -> C? { + guard let right = right else { return left } + var elems: [C.Element] = [] + elems += left + elems += right + return C(elems) + } + var node = self + if let attributes = attributes, + let withAttrs = node.asProtocol(WithAttributesSyntax.self) + { + node = withAttrs.with( + \.attributes, + _combine(attributes, withAttrs.attributes) + ).cast(DeclSyntax.self) + } + if let modifiers = modifiers, + let withModifiers = node.asProtocol(WithModifiersSyntax.self) + { + node = withModifiers.with( + \.modifiers, + _combine(modifiers, withModifiers.modifiers) + ).cast(DeclSyntax.self) + } + return node + } +} + /// Join `expansions` public func collapse( expansions: [String], for role: MacroRole, - attachedTo declarationNode: Node + attachedTo declarationNode: Node, + indentationWidth: Trivia = .spaces(4) ) -> String { if expansions.isEmpty { return "" } var expansions = expansions - var separator: String = "\n\n" - - if role == .accessor, - let varDecl = declarationNode.as(VariableDeclSyntax.self), - let binding = varDecl.bindings.first, - binding.accessorBlock == nil - { - let indentation = String(repeating: " ", count: 4) - - expansions = expansions.map({ indent($0, with: indentation) }) - expansions[0] = "{\n" + expansions[0] - expansions[expansions.count - 1] += "\n}" - } else if role == .memberAttribute { + var separator = "\n" + + switch role { + case .accessor: + let onDeclarationWithoutAccessor: Bool + if let varDecl = declarationNode.as(VariableDeclSyntax.self), + let binding = varDecl.bindings.first, + binding.accessorBlock == nil + { + onDeclarationWithoutAccessor = true + } else if let subscriptDecl = declarationNode.as(SubscriptDeclSyntax.self), + subscriptDecl.accessorBlock == nil + { + onDeclarationWithoutAccessor = true + } else { + onDeclarationWithoutAccessor = false + } + if onDeclarationWithoutAccessor { + expansions = expansions.map({ $0.indented(by: indentationWidth) }) + expansions[0] = "{\n" + expansions[0] + expansions[expansions.count - 1] += "\n}" + } + case .memberAttribute: separator = " " + default: + break } return expansions.joined(separator: separator) } - -fileprivate func indent(_ source: String, with indentation: String) -> String { - if source.isEmpty || indentation.isEmpty { - return source - } - - var indented = "" - var remaining = source[...] - while let nextNewline = remaining.firstIndex(where: { $0.isNewline }) { - if nextNewline != remaining.startIndex { - indented += indentation - } - indented += remaining[...nextNewline] - remaining = remaining[remaining.index(after: nextNewline)...] - } - - if !remaining.isEmpty { - indented += indentation - indented += remaining - } - - return indented -} diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift index e8eb82494a9..2a2495cbfc5 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift @@ -12,28 +12,337 @@ import SwiftDiagnostics import SwiftSyntax +import SwiftSyntaxBuilder +@_spi(MacroExpansion) import SwiftParser @_spi(MacroExpansion) import SwiftSyntaxMacros -/// Describes the kinds of errors that can occur within a macro system. -enum MacroSystemError: Error { - /// Indicates that a macro with the given name has already been defined. - case alreadyDefined(new: Macro.Type, existing: Macro.Type) +// MARK: - Public entry function - /// Indicates that an unknown macro was encountered during expansion. - case unknownMacro(name: String, node: Syntax) +extension SyntaxProtocol { + /// Expand all uses of the given set of macros within this syntax node. + public func expand( + macros: [String: Macro.Type], + in context: some MacroExpansionContext, + indentationWidth: Trivia = .spaces(4) + ) -> Syntax { + // Build the macro system. + var system = MacroSystem() + for (macroName, macroType) in macros { + try! system.add(macroType, name: macroName) + } - /// Indicates that a macro evaluated as an expression by the given node - /// is not an expression macro. - case requiresExpressionMacro(macro: Macro.Type, node: Syntax) + let applier = MacroApplication( + macroSystem: system, + context: context, + indentationWidth: indentationWidth + ) - /// Indicates that a macro evaluated as a code item by the given node - /// is not suitable for code items. - case requiresCodeItemMacro(macro: Macro.Type, node: Syntax) + return applier.rewrite(self) + } +} - /// Indicates that a macro produced diagnostics during evaluation. The - /// diagnostics might not specifically include errors, but will be reported - /// nonetheless. - case evaluationDiagnostics(node: Syntax, diagnostics: [Diagnostic]) +// MARK: - Expand macros + +/// Expand the given freestanding macro and parse the resulting text into a +/// syntax tree. +private func expandFreestandingMemberDeclList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> MemberBlockItemListSyntax? { + guard + let expanded = try expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = + expanded + .indented(by: node.indentationOfFirstLine) + .wrappingInNonCommentTrivia(from: node) + return "\(raw: indentedSource)" +} + +private func expandFreestandingCodeItemList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> CodeBlockItemListSyntax? { + guard + let expanded = try expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // The macro expansion just provides an expansion for the content. + // We need to make sure that we aren’t dropping the trivia before and after + // the expansion. + let indentedSource = + expanded + .indented(by: node.indentationOfFirstLine) + .wrappingInNonCommentTrivia(from: node) + return "\(raw: indentedSource)" +} + +private func expandFreestandingExpr( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> ExprSyntax? { + guard + let expanded = expandFreestandingMacro( + definition: definition, + macroRole: .expression, + node: node.detach(in: context), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = + expanded + .indented(by: node.indentationOfFirstLine) + .wrappingInNonCommentTrivia(from: node) + return "\(raw: indentedSource)" +} + +private func expandMemberMacro( + definition: MemberMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> MemberBlockItemListSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .member, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate new members from exisiting members by two newlines + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\(raw: indentedSource)" +} + +private func expandMemberAttributeMacro( + definition: MemberAttributeMacro.Type, + attributeNode: AttributeSyntax, + attachedTo declaration: DeclSyntax, + providingAttributeFor member: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> AttributeListSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .memberAttribute, + attributeNode: attributeNode.detach(in: context), + declarationNode: member.detach(in: context), + parentDeclNode: declaration.detach(in: context), + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // The added attributes should be on their own line, so prepend them with a newline. + let indentedSource = "\n" + expanded.indented(by: member.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +private func expandPeerMacroMember( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> MemberBlockItemListSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +private func expandPeerMacroCodeItem( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> CodeBlockItemListSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +/// Expand an accessor macro of a declaration that does not have existing +/// accessors. +/// +/// See comment in `expandAccessors` for an explanation why we need this. +private func expandAccessorMacroWithoutExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> AccessorBlockSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // `expandAttachedMacro` adds the `{` and `}` to wrap the accessor block. + // Since the entire block is indented, we start it on a new line. + let indentedSource = "\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +/// Expand an accessor macro of a declaration that already has an accessor. +/// +/// See comment in `expandAccessors` for an explanation why we need this. +private func expandAccessorMacroWithExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> AccessorDeclListSyntax? { + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the accessor from any existing accessors by two spaces + let indentedSource = "\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\(raw: indentedSource)" +} + +private func expandExtensionMacro( + definition: ExtensionMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) throws -> CodeBlockItemListSyntax? { + let extendedType: TypeSyntax + if let identified = attachedTo.asProtocol(NamedDeclSyntax.self) { + extendedType = "\(identified.name.trimmed)" + } else if let ext = attachedTo.as(ExtensionDeclSyntax.self) { + extendedType = "\(ext.extendedType.trimmed)" + } else { + return nil + } + + guard + let expanded = expandAttachedMacro( + definition: definition, + macroRole: .extension, + attributeNode: attributeNode.detach(in: context), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: extendedType.detach(in: context), + conformanceList: [], + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the extension from other declarations in the source file by two newlines. + let indentedSource = "\n\n" + expanded + return "\(raw: indentedSource)" +} + +// MARK: - MacroSystem + +/// Describes the kinds of errors that can occur within a macro system. +enum MacroSystemError: Error { + /// Indicates that a macro with the given name has already been defined. + case alreadyDefined(new: Macro.Type, existing: Macro.Type) } /// A system of known macros that can be expanded syntactically @@ -60,70 +369,133 @@ struct MacroSystem { } } +// MARK: - MacroApplication + +/// Removes attributes from a syntax tree while maintaining their surrounding trivia. +private class AttributeRemover: SyntaxRewriter { + var attributesToRemove: [AttributeSyntax] + + var triviaToAttachToNextToken: Trivia = Trivia() + + init(attributesToRemove: [AttributeSyntax]) { + self.attributesToRemove = attributesToRemove + } + + override func visit(_ node: AttributeListSyntax) -> AttributeListSyntax { + var filteredAttributes: [AttributeListSyntax.Element] = [] + for case .attribute(let attribute) in node { + if attributesToRemove.contains(attribute) { + var leadingTrivia = node.leadingTrivia + if let lastNewline = leadingTrivia.pieces.lastIndex(where: { $0.isNewline }), + leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace), + node.trailingTrivia.isEmpty, + node.nextToken(viewMode: .sourceAccurate)?.leadingTrivia.first?.isNewline ?? false + { + // If the attribute is on its own line based on the following conditions, + // remove the newline from it so we don’t end up with an empty line + // - Trailing trivia ends with a newline followed by arbitrary number of spaces or tabs + // - There is no trailing trivia and the next token starts on a new line + leadingTrivia = Trivia(pieces: leadingTrivia.pieces[.. TokenSyntax { + if !triviaToAttachToNextToken.isEmpty { + defer { triviaToAttachToNextToken = Trivia() } + return token.with(\.leadingTrivia, triviaToAttachToNextToken + token.leadingTrivia) + } else { + return token + } + } +} + +private let diagnosticDomain: String = "SwiftSyntaxMacroExpansion" + +private enum MacroApplicationError: DiagnosticMessage, Error { + case accessorMacroOnVariableWithMultipleBindings + case malformedAccessor + + var diagnosticID: MessageID { + return MessageID(domain: diagnosticDomain, id: "\(self)") + } + + var severity: DiagnosticSeverity { return .error } + + var message: String { + switch self { + case .accessorMacroOnVariableWithMultipleBindings: + return """ + swift-syntax applies macros syntactically but there is no way to represent a variable \ + declaration with multiple bindings that have accessors syntactically. \ + While the compiler allows this expansion, swift-syntax cannot represent it and thus \ + disallows it. + """ + case .malformedAccessor: + return """ + Macro returned a mal-formed accessor. Accessors should start with an introducer like 'get' or 'set'. + """ + } + } +} + /// Syntax rewriter that evaluates any macros encountered along the way. -class MacroApplication: SyntaxRewriter { +private class MacroApplication: SyntaxRewriter { let macroSystem: MacroSystem var context: Context - var skipNodes: Set = [] + var indentationWidth: Trivia + /// Nodes that we have already handled in `visitAny` and that should be visited + /// in the node-specific handling function. + var skipVisitAnyHandling: Set = [] - /// A stack of member attribute macos to expand when iterating over a ``MemberDeclListSyntax``. - var memberAttributeMacros: [([(AttributeSyntax, MemberAttributeMacro.Type)], DeclSyntax)] = [] + /// Store expanded extension while visiting member decls. This should be + /// added to top-level 'CodeBlockItemList'. + var extensions: [CodeBlockItemSyntax] = [] init( macroSystem: MacroSystem, - context: Context + context: Context, + indentationWidth: Trivia ) { self.macroSystem = macroSystem self.context = context + self.indentationWidth = indentationWidth super.init(viewMode: .sourceAccurate) } override func visitAny(_ node: Syntax) -> Syntax? { - if skipNodes.contains(node) { + if skipVisitAnyHandling.contains(node) { return nil } - if node.evaluatedMacroName != nil { - return node.evaluateMacro( - with: macroSystem, - context: context - ) + // Expand 'MacroExpansionExpr'. + // Note that 'MacroExpansionExpr'/'MacroExpansionExprDecl' at code item + // position are handled by 'visit(_:CodeBlockItemListSyntax)'. Here's only + // expression inside other syntax node. + if let expanded = expandExpr(node: node) { + return Syntax(visit(expanded)) } if let declSyntax = node.as(DeclSyntax.self), let attributedNode = node.asProtocol(WithAttributesSyntax.self), - let attributes = attributedNode.attributes + !(attributedNode.attributes?.isEmpty ?? true) { - // Visit the node. - skipNodes.insert(node) - let visitedNode = self.visit(declSyntax).asProtocol(WithAttributesSyntax.self)! - skipNodes.remove(node) - - // Remove any attached attributes. - let newAttributes = attributes.filter { - guard case let .attribute(attribute) = $0 else { - return true - } - - guard let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, - let macro = macroSystem.macros[attributeName] - else { - return true - } + // Visit the node, disabling the `visitAny` handling. + skipVisitAnyHandling.insert(node) + let visitedNode = self.visit(declSyntax) + skipVisitAnyHandling.remove(node) - return - !(macro is PeerMacro.Type - || macro is MemberMacro.Type - || macro is AccessorMacro.Type - || macro is MemberAttributeMacro.Type - || macro is ExtensionMacro.Type) - } - - if newAttributes.isEmpty { - return Syntax(fromProtocol: visitedNode.with(\.attributes, nil)) - } + let attributesToRemove = self.macroAttributes(attachedTo: visitedNode).map(\.attributeNode) - return Syntax(fromProtocol: visitedNode.with(\.attributes, AttributeListSyntax(newAttributes))) + return AttributeRemover(attributesToRemove: attributesToRemove).rewrite(visitedNode) } return nil @@ -131,238 +503,124 @@ class MacroApplication: SyntaxRewriter { override func visit(_ node: CodeBlockItemListSyntax) -> CodeBlockItemListSyntax { var newItems: [CodeBlockItemSyntax] = [] - for item in node { - if let expansion = item.item.asProtocol(FreestandingMacroExpansionSyntax.self), - let macro = macroSystem.macros[expansion.macro.text] - { - do { - if let macro = macro as? CodeItemMacro.Type { - let expandedItemList = try macro.expansion( - of: expansion, - in: context - ) - newItems.append(contentsOf: expandedItemList) - } else if let macro = macro as? DeclarationMacro.Type { - var expandedItemList = try macro.expansion( - of: expansion, - in: context - ) - if let declExpansion = expansion.as(MacroExpansionDeclSyntax.self) { - let attributes = macro.propagateFreestandingMacroAttributes ? declExpansion.attributes : nil - let modifiers = macro.propagateFreestandingMacroModifiers ? declExpansion.modifiers : nil - expandedItemList = expandedItemList.map { - $0.applying(attributes: attributes, modifiers: modifiers) - } - } - newItems.append( - contentsOf: expandedItemList.map { - CodeBlockItemSyntax(item: .decl($0)) - } - ) - } else if let macro = macro as? ExpressionMacro.Type { - let expandedExpr = try macro.expansion( - of: expansion, - in: context - ) - newItems.append(CodeBlockItemSyntax(item: .init(expandedExpr))) - } - } catch { - context.addDiagnostics(from: error, node: node) + func addResult(_ node: CodeBlockItemSyntax) { + // Expand freestanding macro. + if let expanded = expandCodeBlockItem(node: node) { + for item in expanded { + addResult(item) } - - continue + return } - // Recurse on the child node. - let newItem = visit(item.item) - newItems.append(item.with(\.item, newItem)) - - // Expand any peer declarations or conformances triggered by macros used - // as attributes. - if case let .decl(decl) = item.item { - let peers = expandPeers(of: decl) - newItems.append( - contentsOf: peers.map { - newDecl in CodeBlockItemSyntax(item: .decl(newDecl)) - } - ) + // Recurse on the child node + newItems.append(visit(node)) - if let declGroup = decl.asProtocol(DeclGroupSyntax.self) { - newItems.append( - contentsOf: expandExtensions(of: declGroup).map { - newDecl in CodeBlockItemSyntax(item: .decl(newDecl)) - } - ) + // Expand any peer macro on this item. + if case .decl(let decl) = node.item { + for peer in expandCodeBlockPeers(of: decl) { + addResult(peer) } + extensions += expandExtensions(of: decl) } } + for item in node { + addResult(item) + } + + // If this is the top-level code block item list, add the expanded extensions + // at its end. + if node.parent?.is(SourceFileSyntax.self) ?? false { + newItems += extensions + } + return CodeBlockItemListSyntax(newItems) } override func visit(_ node: MemberBlockItemListSyntax) -> MemberBlockItemListSyntax { + let parentDeclGroup = node + .parent? + .as(MemberBlockSyntax.self)? + .parent? + .as(DeclSyntax.self) var newItems: [MemberBlockItemSyntax] = [] - for item in node { - // Expand declaration macros, which produce zero or more declarations. - if let declExpansion = item.decl.as(MacroExpansionDeclSyntax.self), - let macro = macroSystem.macros[declExpansion.macroName.text], - let freestandingMacro = macro as? DeclarationMacro.Type - { - do { - var expandedList = try freestandingMacro.expansion( - of: declExpansion, - in: context - ) - let attributes = freestandingMacro.propagateFreestandingMacroAttributes ? declExpansion.attributes : nil - let modifiers = freestandingMacro.propagateFreestandingMacroModifiers ? declExpansion.modifiers : nil - expandedList = expandedList.map { - $0.applying(attributes: attributes, modifiers: modifiers) - } - newItems.append( - contentsOf: expandedList.map { decl in - return MemberBlockItemSyntax(decl: decl) - } - ) - } catch { - context.addDiagnostics(from: error, node: declExpansion) + func addResult(_ node: MemberBlockItemSyntax) { + // Expand freestanding macro. + if let expanded = expandMemberDecl(node: node) { + for item in expanded { + addResult(item) } + return + } - continue + // Recurse on the child node. + newItems.append(visit(node)) + + // Expand any peer macro on this member. + for peer in expandMemberDeclPeers(of: node.decl) { + addResult(peer) } + extensions += expandExtensions(of: node.decl) + } + for var item in node { // Expand member attribute members attached to the declaration context. - let attributedMember: MemberBlockItemListSyntax.Element - if let (macroAttributes, decl) = memberAttributeMacros.last { - attributedMember = expandAttributes( - for: macroAttributes, - attachedTo: decl, - annotating: item + // Note that MemberAttribute macros are _not_ applied to generated members + if let parentDeclGroup, let decl = item.decl.asProtocol(WithAttributesSyntax.self) { + var newAttributes = expandAttributesFromMemberAttributeMacros( + of: item.decl, + parentDecl: parentDeclGroup ) - } else { - attributedMember = item + .map { visit($0) } + if !newAttributes.isEmpty { + if let existingAttrs = decl.attributes { + newAttributes.insert(contentsOf: existingAttrs, at: 0) + } + item.decl = decl.with(\.attributes, AttributeListSyntax(newAttributes)).cast(DeclSyntax.self) + } } // Recurse on the child node. - let newDecl = visit(attributedMember.decl) - newItems.append(attributedMember.with(\.decl, newDecl)) - - // Expand any peer declarations triggered by macros used as attributes. - let peers = expandPeers(of: item.decl) - newItems.append( - contentsOf: peers.map { - newDecl in MemberBlockItemSyntax(decl: newDecl) - } - ) + addResult(item) } - return .init(newItems) - } - - func visit( - declGroup: DeclType - ) -> DeclSyntax { - memberAttributeMacros.append( - ( - getMacroAttributes(attachedTo: DeclSyntax(declGroup), ofType: MemberAttributeMacro.Type.self), - DeclSyntax(declGroup) - ) - ) - defer { memberAttributeMacros.removeLast() } - - // Expand any attached member macros. - let expandedDeclGroup = expandMembers(of: declGroup) - - // Recurse into member decls. - let newMembers = visit(expandedDeclGroup.memberBlock) - - return DeclSyntax(expandedDeclGroup.with(\.memberBlock, newMembers)) - } - - override func visit(_ node: ActorDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: StructDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: EnumDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: ClassDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } + // Expand any member macros of parent. + if let parentDeclGroup { + for member in expandMembers(of: parentDeclGroup) { + addResult(member) + } + } - override func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) + return .init(newItems) } - // Properties override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - let visitedNode = super.visit(node) - guard let visitedVarDecl = visitedNode.as(VariableDeclSyntax.self) else { - return visitedNode - } - - guard let binding = visitedVarDecl.bindings.first, - visitedVarDecl.bindings.count == 1 - else { + var node = super.visit(node).cast(VariableDeclSyntax.self) + guard node.bindings.count == 1, let binding = node.bindings.first else { + context.addDiagnostics(from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings, node: node) return DeclSyntax(node) } - - var accessors: [AccessorDeclSyntax] = [] - - let accessorMacroAttributes = getMacroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self) - for (accessorAttr, accessorMacro) in accessorMacroAttributes { - do { - let newAccessors = try accessorMacro.expansion( - of: accessorAttr, - providingAccessorsOf: visitedNode, - in: context - ) - - accessors.append(contentsOf: newAccessors) - } catch { - // FIXME: record the error - } - } - - if accessors.isEmpty { - return visitedNode - } - - return DeclSyntax( - visitedVarDecl.with( - \.bindings, - visitedVarDecl.bindings.with( - \.[visitedVarDecl.bindings.startIndex], - binding.with( - \.accessorBlock, - AccessorBlockSyntax( - leftBrace: .leftBraceToken(leadingTrivia: .space), - accessors: .accessors(.init(accessors)), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) - ) - ) - ) - ) + node.bindings[node.bindings.startIndex].accessorBlock = expandAccessors(of: node, existingAccessors: binding.accessorBlock) + return DeclSyntax(node) } - // Subscripts + override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax { + var node = super.visit(node).cast(SubscriptDeclSyntax.self) + node.accessorBlock = expandAccessors(of: node, existingAccessors: node.accessorBlock) + return DeclSyntax(node) + } } +// MARK: Attached macro expansions. + extension MacroApplication { - private func getMacroAttributes( - attachedTo decl: DeclSyntax, - ofType: MacroType.Type - ) -> [(AttributeSyntax, MacroType)] { + /// Get pairs of a macro attribute and the macro definition attached to `decl`. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax + ) -> [(attributeNode: AttributeSyntax, definition: Macro.Type)] { guard let attributedNode = decl.asProtocol(WithAttributesSyntax.self), let attributes = attributedNode.attributes else { @@ -371,193 +629,379 @@ extension MacroApplication { return attributes.compactMap { guard case let .attribute(attribute) = $0, - let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, - let macro = macroSystem.macros[attributeName], - let macroType = macro as? MacroType + let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, + let macro = macroSystem.lookup(attributeName) else { return nil } - return (attribute, macroType) + return (attribute, macro) } } - // If any of the custom attributes associated with the given declaration - // refer to "peer" declaration macros, expand them and return the resulting - // set of peer declarations. - private func expandPeers(of decl: DeclSyntax) -> [DeclSyntax] { - var peers: [DeclSyntax] = [] - let macroAttributes = getMacroAttributes(attachedTo: decl, ofType: PeerMacro.Type.self) - for (attribute, peerMacro) in macroAttributes { + /// Get pairs of a macro attribute and the macro definition attached to `decl` + /// matching `ofType` macro type. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type + ) -> [(attributeNode: AttributeSyntax, definition: MacroType)] { + return macroAttributes(attachedTo: decl) + .compactMap { (attributeNode: AttributeSyntax, definition: Macro.Type) in + if let macroType = definition as? MacroType { + return (attributeNode, macroType) + } else { + return nil + } + } + } + + /// Call `expandMacro` for every macro of type `ofType` attached to `decl` and + /// return the list of all expaned nodes. + private func expandMacros< + ExpandedNode: SyntaxProtocol, + ExpanedNodeCollection: Sequence, + MacroType + >( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type, + expandMacro: (_ attributeNode: AttributeSyntax, _ definition: MacroType) throws -> ExpanedNodeCollection? + ) -> [ExpandedNode] { + var result: [ExpandedNode] = [] + + for peerMacro in macroAttributes(attachedTo: decl, ofType: ofType) { do { - let newPeers = try peerMacro.expansion(of: attribute, providingPeersOf: decl, in: context) - peers.append(contentsOf: newPeers) + if let expanded = try expandMacro(peerMacro.attributeNode, peerMacro.definition) { + result += expanded + } } catch { - context.addDiagnostics(from: error, node: attribute) + context.addDiagnostics(from: error, node: peerMacro.attributeNode) } } - - return peers + return result } - // If any of the custom attributes associated with the given declaration - // refer to conformance macros, expand them and return the resulting - // set of extension declarations. - private func expandExtensions(of decl: DeclGroupSyntax) -> [DeclSyntax] { - let extendedType: TypeSyntax - if let named = decl.asProtocol(NamedDeclSyntax.self) { - extendedType = "\(named.name.trimmed)" - } else if let ext = decl.as(ExtensionDeclSyntax.self) { - extendedType = "\(ext.extendedType.trimmed)" - } else { - return [] + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandCodeBlockPeers` returns them as `CodeBlockItemSyntax`. The + /// overload is chosen based on the context in which the peers are expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandMemberDeclPeers(of decl: DeclSyntax) -> [MemberBlockItemSyntax] { + return expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition in + return try expandPeerMacroMember( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: context, + indentationWidth: indentationWidth + ) } + } - var extensions: [DeclSyntax] = [] + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandMemberDeclPeers` returns them as `MemberDeclListItemSyntax`. + /// The overload is chosen based on the context in which the peers are + /// expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandCodeBlockPeers(of decl: DeclSyntax) -> [CodeBlockItemSyntax] { + return expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition in + return try expandPeerMacroCodeItem( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: context, + indentationWidth: indentationWidth + ) + } + } - let extensionMacroAttrs = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ExtensionMacro.Type.self) - for (attribute, extensionMacro) in extensionMacroAttrs { - do { - // FIXME: We need a way for unit tests of extension macros to - // specify protocols already stated in source (e.g. as arguments - // to `assertMacroExpansion`). - let newExtensions = try extensionMacro.expansion( - of: attribute, - attachedTo: decl, - providingExtensionsOf: extendedType, - conformingTo: [], - in: context - ) + /// Expand all 'extension' macros attached to `decl`. + /// + /// - Returns: The macro-synthesized extensions + private func expandExtensions(of decl: DeclSyntax) -> [CodeBlockItemSyntax] { + return expandMacros(attachedTo: decl, ofType: ExtensionMacro.Type.self) { attributeNode, definition in + return try expandExtensionMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: context, + indentationWidth: indentationWidth + ) + } + } - extensions.append(contentsOf: newExtensions.map(DeclSyntax.init)) - } catch { - context.addDiagnostics(from: error, node: attribute) - } + /// Expand all 'member' macros attached to `decl`. + private func expandMembers(of decl: DeclSyntax) -> [MemberBlockItemSyntax] { + return expandMacros(attachedTo: decl, ofType: MemberMacro.Type.self) { attributeNode, definition in + return try expandMemberMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: context, + indentationWidth: indentationWidth + ) } + } - return extensions + /// Return the attributes on `decl` that are synthesized from member attribute + /// macros on `parentDecl`. + /// + /// - Note: This only returns synthesized attributes from member attribute + /// macros, not the attributes that are attached to `decl` in the source code. + private func expandAttributesFromMemberAttributeMacros( + of decl: DeclSyntax, + parentDecl: DeclSyntax + ) -> [AttributeListSyntax.Element] { + return expandMacros(attachedTo: parentDecl, ofType: MemberAttributeMacro.Type.self) { attributeNode, definition in + return try expandMemberAttributeMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: parentDecl, + providingAttributeFor: decl, + in: context, + indentationWidth: indentationWidth + ) + } } - /// Expands any attached custom attributes that refer to member declaration macros, - /// and returns result of adding those members to the given declaration. - private func expandMembers( - of decl: Decl - ) -> Decl { - var newMembers: [DeclSyntax] = [] - let macroAttributes = getMacroAttributes(attachedTo: DeclSyntax(decl), ofType: MemberMacro.Type.self) - for (attribute, memberMacro) in macroAttributes { + /// Expand all 'accessor' macros attached to `storage` and return the `storage` + /// node. + /// + /// - Returns: The storage node with all macro-synthesized accessors applied. + private func expandAccessors(of storage: some DeclSyntaxProtocol, existingAccessors: AccessorBlockSyntax?) -> AccessorBlockSyntax? { + let accessorMacros = macroAttributes(attachedTo: DeclSyntax(storage), ofType: AccessorMacro.Type.self) + + var newAccessorsBlock = existingAccessors + + for macro in accessorMacros { do { - try newMembers.append( - contentsOf: memberMacro.expansion( - of: attribute, - providingMembersOf: decl, - in: context + // Accessor macros get expanded differently depending on whether the + // variable already had an accessor. If not, '{' and '}' to wrap the + // accessor block are added. If an accessor block already exists, only + // the new accessors are returned. + // We need to parse the result from the macro invocation differently + // based on these cases. + if existingAccessors != nil { + if let newAccessors = try expandAccessorMacroWithExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: context, + indentationWidth: indentationWidth + ) { + // If existingAccessors is not `nil`, then we also set + // `newAccessorBlock` above to a a non-nil value, so + // `newAccessorsBlock` also isn’t `nil`. + newAccessorsBlock = newAccessorsBlock!.addingAccessors( + from: newAccessors, + indentationWidth: self.indentationWidth + ) + } + } else { + let newAccessors = try expandAccessorMacroWithoutExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: context, + indentationWidth: indentationWidth ) - ) + if newAccessorsBlock == nil { + newAccessorsBlock = newAccessors + } else if let newAccessors = newAccessors { + guard case .accessors(let accessorList) = newAccessors.accessors else { + throw MacroApplicationError.malformedAccessor + } + newAccessorsBlock = newAccessorsBlock!.addingAccessors( + from: accessorList, + indentationWidth: self.indentationWidth + ) + } + } } catch { - context.addDiagnostics(from: error, node: attribute) + context.addDiagnostics(from: error, node: macro.attributeNode) } } - - // FIXME: Is there a better way to add N members to a decl? - return decl.with( - \.memberBlock, - newMembers.reduce(decl.memberBlock) { partialMembers, newMember in - partialMembers.addMember(.init(decl: newMember)) - } - ) + return newAccessorsBlock } +} - private func expandAttributes( - for macroAttributes: [(AttributeSyntax, MemberAttributeMacro.Type)], - attachedTo decl: DeclSyntax, - annotating member: MemberBlockItemListSyntax.Element - ) -> MemberBlockItemListSyntax.Element { - guard let attributedDecl = member.decl.asProtocol(WithAttributesSyntax.self) else { - return member +// MARK: Freestanding macro expansion + +extension MacroApplication { + private func expandFreestandingMacro( + _ node: (any FreestandingMacroExpansionSyntax)?, + expandMacro: (_ macro: Macro.Type, _ node: any FreestandingMacroExpansionSyntax) throws -> ExpandedMacroType? + ) -> ExpandedMacroType? { + guard let node, + let macro = macroSystem.lookup(node.macro.text) + else { + return nil } + do { + return try expandMacro(macro, node) + } catch { + context.addDiagnostics(from: error, node: node) + return nil + } + } - var attributes: [AttributeSyntax] = [] - for (attribute, attributeMacro) in macroAttributes { - do { - let typedDecl = decl.asProtocol(DeclGroupSyntax.self)! - attributes.append( - contentsOf: try attributeMacro.expansion( - of: attribute, - attachedTo: typedDecl, - providingAttributesFor: member.decl, - in: context - ) - ) - } catch { - context.addDiagnostics(from: error, node: attribute) - } + /// Expand a freestanding macro expansion syntax in a code block item position. + /// + /// For example + /// ```swift + /// function test() { + /// #foo + /// } + /// ``` + func expandCodeBlockItem(node: CodeBlockItemSyntax) -> CodeBlockItemListSyntax? { + return expandFreestandingMacro(node.item.asProtocol(FreestandingMacroExpansionSyntax.self)) { macro, node in + return try expandFreestandingCodeItemList( + definition: macro, + node: node, + in: context, + indentationWidth: indentationWidth + ) } + } - let newAttributes = attributes.reduce(attributedDecl.attributes ?? .init([])) { - AttributeListSyntax($0 + [AttributeListSyntax.Element($1)]) + /// Expand a freestanding macro expansion syntax in a member decl position. + /// + /// For exmple + /// ```swift + /// struct S { + /// #foo + /// } + /// ``` + func expandMemberDecl(node: MemberBlockItemSyntax) -> MemberBlockItemListSyntax? { + return expandFreestandingMacro(node.decl.as(MacroExpansionDeclSyntax.self)) { macro, node in + return try expandFreestandingMemberDeclList( + definition: macro, + node: node, + in: context, + indentationWidth: indentationWidth + ) } + } - let newDecl = attributedDecl.with(\.attributes, newAttributes).as(DeclSyntax.self)! - return member.with(\.decl, newDecl) + /// Expand a freestanding macro expansion in a expression position inside + /// other products. + /// + /// For example + /// ```swift + /// let a = #foo + /// ``` + func expandExpr(node: Syntax) -> ExprSyntax? { + return expandFreestandingMacro(node.as(MacroExpansionExprSyntax.self)) { macro, node in + return try expandFreestandingExpr( + definition: macro, + node: node, + in: context, + indentationWidth: indentationWidth + ) + } } } -extension DeclSyntax { - /// Returns this node with `attributes` and `modifiers` prepended to the - /// node’s attributes and modifiers, respectively. If the node doesn’t contain - /// attributes or modifiers, `attributes` or `modifiers` are ignored and not - /// applied. - func applying( - attributes: AttributeListSyntax?, - modifiers: DeclModifierListSyntax? - ) -> DeclSyntax { - func _combine(_ left: C, _ right: C?) -> C? { - guard let right = right else { return left } - var elems: [C.Element] = [] - elems.append(contentsOf: left) - elems.append(contentsOf: right) - return C(elems) - } - var node = self - if let attributes = attributes, - let withAttrs = node.asProtocol(WithAttributesSyntax.self) - { - node = withAttrs.with( - \.attributes, - _combine(attributes, withAttrs.attributes) - ).cast(DeclSyntax.self) - } - if let modifiers = modifiers, - let withModifiers = node.asProtocol(WithModifiersSyntax.self) - { - node = withModifiers.with( - \.modifiers, - _combine(modifiers, withModifiers.modifiers) - ).cast(DeclSyntax.self) +// MARK: Syntax utilities + +private extension AccessorBlockSyntax { + /// Create a new `AccessorBlockSyntax` by adding the given accessors. + /// + /// If this `AccessorBlockSyntax` contains a single code block as a getter, + /// that code block will be converted to an explicit 'get' accessor. This + /// causes the body to be indented by `indentationWidth`. + func addingAccessors( + from newAccessors: AccessorDeclListSyntax, + indentationWidth: Trivia + ) -> AccessorBlockSyntax { + var result = self + switch self.accessors { + case .accessors(let accessors): + result.accessors = .accessors(AccessorDeclListSyntax(accessors + Array(newAccessors))) + case .getter(let getter): + if newAccessors.isEmpty { + return self + } + // Convert the existing getter into an explicit accessor (with 'get') so + // we can add more accessors. + let baseIndentation = getter.indentationOfFirstLine + let getterAsAccessor = AccessorDeclSyntax( + accessorSpecifier: .keyword(.get, leadingTrivia: .newline + baseIndentation, trailingTrivia: .space), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: Indenter.indent(getter, indentation: indentationWidth), + rightBrace: .rightBraceToken(leadingTrivia: .newline + baseIndentation) + ) + ) + result.accessors = .accessors(AccessorDeclListSyntax([getterAsAccessor] + Array(newAccessors))) } - return node + return result } } -extension SyntaxProtocol { - /// Expand all uses of the given set of macros within this syntax - /// node. - public func expand( - macros: [String: Macro.Type], - in context: some MacroExpansionContext - ) -> Syntax { - // Build the macro system. - var system = MacroSystem() - for (macroName, macroType) in macros { - try! system.add(macroType, name: macroName) +private extension String { + /// Add any non-comment trivia from `node` before/after this string. + /// + /// We need to do this because the macro is responsible for copying trivia + /// from the freestanding macro to the generated declaration. + /// + /// Essentially, what we want to keep any empty newlines in front of the + /// freestanding macro that separate it from the previous declarations. + func wrappingInNonCommentTrivia(from node: some SyntaxProtocol) -> String { + return node.leadingTrivia.removingComments.removingIndentation.description + + self + + node.leadingTrivia.removingComments.removingIndentation.description + } +} + +private extension SyntaxProtocol { + /// Detach the current node and inform the macro expansion context, + /// if it needs to know. + func detach(in context: MacroExpansionContext) -> Self { + if let basicContext = context as? BasicMacroExpansionContext { + return basicContext.detach(self) } - let applier = MacroApplication( - macroSystem: system, - context: context - ) + return self.detached + } +} - return applier.rewrite(self) +private extension Trivia { + /// Drop all comments from the trivia. + /// + /// If a comment is the only entry on a line, drop the entire line instead of + /// leaving an empty line. + var removingComments: Trivia { + var result: [TriviaPiece] = [] + + var lineContainedComment = false + var lineContainedNonWhitespaceNonComment = false + for piece in self.pieces { + switch piece { + case .spaces, .tabs: + result.append(piece) + case .backslashes, .formfeeds, .pounds, .shebang, .unexpectedText, .verticalTabs: + lineContainedNonWhitespaceNonComment = true + result.append(piece) + case .blockComment, .docBlockComment, .docLineComment, .lineComment: + lineContainedComment = true + case .carriageReturns, .carriageReturnLineFeeds, .newlines: + if lineContainedComment && !lineContainedNonWhitespaceNonComment { + continue + } else { + result.append(piece) + } + lineContainedComment = false + lineContainedNonWhitespaceNonComment = false + } + } + + return Trivia(pieces: result) } } diff --git a/Sources/SwiftSyntaxMacroExpansion/Syntax+MacroEvaluation.swift b/Sources/SwiftSyntaxMacroExpansion/Syntax+MacroEvaluation.swift deleted file mode 100644 index c3c4d91405e..00000000000 --- a/Sources/SwiftSyntaxMacroExpansion/Syntax+MacroEvaluation.swift +++ /dev/null @@ -1,113 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacros - -extension SyntaxProtocol { - /// Detach the current node and inform the macro expansion context, - /// if it needs to know. - fileprivate func detach(in context: MacroExpansionContext) -> Self { - if let basicContext = context as? BasicMacroExpansionContext { - return basicContext.detach(self) - } - - return self.detached - } -} - -extension MacroExpansionExprSyntax { - /// Evaluate the given macro for this syntax node, producing the expanded - /// result and (possibly) some diagnostics. - func evaluateMacro( - _ macro: Macro.Type, - in context: some MacroExpansionContext - ) -> ExprSyntax { - guard let exprMacro = macro as? ExpressionMacro.Type else { - return ExprSyntax(self) - } - - // Handle the rewrite. - do { - return try exprMacro.expansion(of: detach(in: context), in: context) - } catch { - context.addDiagnostics(from: error, node: self) - return ExprSyntax(self) - } - } -} - -extension MacroExpansionDeclSyntax { - /// Evaluate the given macro for this syntax node, producing the expanded - /// result and (possibly) some diagnostics. - func evaluateMacro( - _ macro: Macro.Type, - in context: some MacroExpansionContext - ) -> Syntax { - // TODO: declaration/statement macros - - return Syntax(self) - } -} - -extension Syntax { - /// Determine the name of the macro that is evaluated by this syntax node, - /// if indeed it is a macro evaluation. For example, "#stringify(x)" has the - /// name "stringify". - var evaluatedMacroName: String? { - switch self.as(SyntaxEnum.self) { - case .macroExpansionDecl(let expansion): - return expansion.macroName.text - - case .macroExpansionExpr(let expansion): - return expansion.macroName.text - - default: - return nil - } - } - - /// Evaluate the given macro and return the resulting syntax tree along with - /// any errors along the way. - /// - /// This operation only makes sense when `evaluatedMacroName` produces a - /// non-nil value, indicating that this syntax node is a macro evaluation of - /// some kind. - func evaluateMacro( - with macroSystem: MacroSystem, - context: some MacroExpansionContext - ) -> Syntax { - // If this isn't a macro evaluation node, do nothing. - guard let macroName = evaluatedMacroName else { - return self - } - - // Look for a macro with the given name. Otherwise, fail. - guard let macro = macroSystem.macros[macroName] else { - return self - } - - switch self.as(SyntaxEnum.self) { - case .macroExpansionDecl(let expansion): - return expansion.evaluateMacro(macro, in: context) - - case .macroExpansionExpr(let expansion): - return Syntax( - expansion.evaluateMacro(macro, in: context) - ) - - default: - fatalError("switch is out-of-sync with evaluatedMacroName") - } - } -} diff --git a/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift b/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift index d8a7581aac3..0d138d55a87 100644 --- a/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift +++ b/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift @@ -62,11 +62,11 @@ public struct NoteSpec { func assertNote( _ note: Note, - in tree: some SyntaxProtocol, + in expansionContext: BasicMacroExpansionContext, expected spec: NoteSpec ) { assertStringsEqualWithDiff(note.message, spec.message, "message of note does not match", file: spec.originatorFile, line: spec.originatorLine) - let location = note.location(converter: SourceLocationConverter(file: "", tree: tree)) + let location = expansionContext.location(for: note.position, anchoredAt: note.node, fileName: "") XCTAssertEqual(location.line, spec.line, "line of note does not match", file: spec.originatorFile, line: spec.originatorLine) XCTAssertEqual(location.column, spec.column, "column of note does not match", file: spec.originatorFile, line: spec.originatorLine) } @@ -181,14 +181,14 @@ public struct DiagnosticSpec { func assertDiagnostic( _ diag: Diagnostic, - in tree: some SyntaxProtocol, + in expansionContext: BasicMacroExpansionContext, expected spec: DiagnosticSpec ) { if let id = spec.id { XCTAssertEqual(diag.diagnosticID, id, "diagnostic ID does not match", file: spec.originatorFile, line: spec.originatorLine) } assertStringsEqualWithDiff(diag.message, spec.message, "message does not match", file: spec.originatorFile, line: spec.originatorLine) - let location = diag.location(converter: SourceLocationConverter(file: "", tree: tree)) + let location = expansionContext.location(for: diag.position, anchoredAt: diag.node, fileName: "") XCTAssertEqual(location.line, spec.line, "line does not match", file: spec.originatorFile, line: spec.originatorLine) XCTAssertEqual(location.column, spec.column, "column does not match", file: spec.originatorFile, line: spec.originatorLine) @@ -223,7 +223,7 @@ func assertDiagnostic( ) } else { for (note, expectedNote) in zip(diag.notes, spec.notes) { - assertNote(note, in: tree, expected: expectedNote) + assertNote(note, in: expansionContext, expected: expectedNote) } } if diag.fixIts.count != spec.fixIts.count { @@ -275,7 +275,7 @@ public func assertMacroExpansion( sourceFiles: [origSourceFile: .init(moduleName: testModuleName, fullFilePath: testFileName)] ) - let expandedSourceFile = origSourceFile.expand(macros: macros, in: context) + let expandedSourceFile = origSourceFile.expand(macros: macros, in: context, indentationWidth: indentationWidth) let diags = ParseDiagnosticsGenerator.diagnostics(for: expandedSourceFile) if !diags.isEmpty { XCTFail( @@ -291,13 +291,12 @@ public func assertMacroExpansion( ) } - let formattedSourceFile = expandedSourceFile.formatted(using: BasicFormat(indentationWidth: indentationWidth)) assertStringsEqualWithDiff( - formattedSourceFile.description.trimmingCharacters(in: .newlines), + expandedSourceFile.description.trimmingCharacters(in: .newlines), expectedExpandedSource.trimmingCharacters(in: .newlines), additionalInfo: """ Actual expanded source: - \(formattedSourceFile) + \(expandedSourceFile) """, file: file, line: line @@ -314,7 +313,7 @@ public func assertMacroExpansion( ) } else { for (actualDiag, expectedDiag) in zip(context.diagnostics, diagnostics) { - assertDiagnostic(actualDiag, in: origSourceFile, expected: expectedDiag) + assertDiagnostic(actualDiag, in: context, expected: expectedDiag) } } } diff --git a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift index 98aefff19dc..3dc06e08ec2 100644 --- a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift +++ b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift @@ -287,7 +287,7 @@ final class BasicFormatTest: XCTestCase { ), accessorBlock: AccessorBlockSyntax( leftBrace: .leftBraceToken(leadingTrivia: .space), - accessors: .accessors(AccessorListSyntax([])), + accessors: .accessors(AccessorDeclListSyntax([])), rightBrace: .rightBraceToken(leadingTrivia: .newline) ) ) diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 97ccaa089cd..3d38690b02c 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -1012,7 +1012,7 @@ final class DeclarationTests: XCTestCase { substructure: Syntax( AccessorBlockSyntax( accessors: .accessors( - AccessorListSyntax([ + AccessorDeclListSyntax([ AccessorDeclSyntax( accessorSpecifier: .keyword(.get) ) diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift index 8ca520daa12..0d9b0046e27 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift @@ -33,12 +33,9 @@ enum CustomError: Error, CustomStringConvertible { // MARK: Example macros public struct StringifyMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> ExprSyntax { guard let argument = macro.argumentList.first?.expression else { throw CustomError.message("missing argument") @@ -65,12 +62,9 @@ private func replaceFirstLabel( } public struct ColorLiteralMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, @@ -82,12 +76,9 @@ public struct ColorLiteralMacro: ExpressionMacro { } public struct FileLiteralMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, @@ -99,12 +90,9 @@ public struct FileLiteralMacro: ExpressionMacro { } public struct ImageLiteralMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, @@ -116,12 +104,9 @@ public struct ImageLiteralMacro: ExpressionMacro { } public struct ColumnMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> ExprSyntax { guard let sourceLoc: AbstractSourceLocation = context.location(of: macro) else { @@ -132,12 +117,9 @@ public struct ColumnMacro: ExpressionMacro { } public struct FileIDMacro: ExpressionMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> ExprSyntax { guard let sourceLoc: AbstractSourceLocation = context.location(of: macro) else { @@ -150,12 +132,9 @@ public struct FileIDMacro: ExpressionMacro { /// Macro whose only purpose is to ensure that we cannot see "out" of the /// macro expansion syntax node we were given. struct CheckContextIndependenceMacro: ExpressionMacro { - static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of macro: Node, - in context: Context + static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) -> ExprSyntax { // Should not have a parent. @@ -179,12 +158,9 @@ extension SimpleDiagnosticMessage: FixItMessage { } public struct ErrorMacro: DeclarationMacro { - public static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of node: Node, - in context: Context + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { guard let firstElement = node.argumentList.first, let stringLiteral = firstElement.expression @@ -211,12 +187,9 @@ public struct ErrorMacro: DeclarationMacro { } struct DefineBitwidthNumberedStructsMacro: DeclarationMacro { - static func expansion< - Node: FreestandingMacroExpansionSyntax, - Context: MacroExpansionContext - >( - of node: Node, - in context: Context + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { guard let firstElement = node.argumentList.first, let stringLiteral = firstElement.expression @@ -238,16 +211,29 @@ struct DefineBitwidthNumberedStructsMacro: DeclarationMacro { } } +public struct ConstantOneGetter: AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + return [ + """ + get { + return 1 + } + """ + ] + } +} + public struct PropertyWrapper {} extension PropertyWrapper: AccessorMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( + public static func expansion( of node: AttributeSyntax, - providingAccessorsOf declaration: Declaration, - in context: Context + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [AccessorDeclSyntax] { guard let varDecl = declaration.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, @@ -259,29 +245,24 @@ extension PropertyWrapper: AccessorMacro { return [ """ - - get { - _\(identifier).wrappedValue - } + get { + _\(identifier).wrappedValue + } """, """ - - set { - _\(identifier).wrappedValue = newValue - } + set { + _\(identifier).wrappedValue = newValue + } """, ] } } extension PropertyWrapper: PeerMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( + public static func expansion( of node: AttributeSyntax, - providingPeersOf declaration: Declaration, - in context: Context + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [SwiftSyntax.DeclSyntax] { guard let varDecl = declaration.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, @@ -315,13 +296,10 @@ extension PropertyWrapper: PeerMacro { } public struct AddCompletionHandler: PeerMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( + public static func expansion( of node: AttributeSyntax, - providingPeersOf declaration: Declaration, - in context: Context + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // Only on functions at the moment. We could handle initializers as well // with a bit of work. @@ -397,8 +375,7 @@ public struct AddCompletionHandler: PeerMacro { guard case let .attribute(attribute) = $0 else { return true } - - return attribute != node + return attribute.attributeName.as(IdentifierTypeSyntax.self)?.name == "addCompletionHandler" } ?? [] ) @@ -436,13 +413,10 @@ public struct AddCompletionHandler: PeerMacro { } public struct AddBackingStorage: MemberMacro { - public static func expansion< - Declaration: DeclGroupSyntax, - Context: MacroExpansionContext - >( + public static func expansion( of node: AttributeSyntax, - providingMembersOf decl: Declaration, - in context: Context + providingMembersOf decl: some DeclGroupSyntax, + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { @@ -454,15 +428,11 @@ public struct AddBackingStorage: MemberMacro { } public struct WrapAllProperties: MemberAttributeMacro { - public static func expansion< - Declaration: DeclGroupSyntax, - MemberDeclaration: DeclSyntaxProtocol, - Context: MacroExpansionContext - >( + public static func expansion( of node: AttributeSyntax, - attachedTo decl: Declaration, - providingAttributesFor member: MemberDeclaration, - in context: Context + attachedTo decl: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [AttributeSyntax] { guard member.is(VariableDeclSyntax.self) else { return [] @@ -480,15 +450,11 @@ public struct WrapAllProperties: MemberAttributeMacro { } public struct WrapStoredProperties: MemberAttributeMacro { - public static func expansion< - Declaration: DeclGroupSyntax, - MemberDeclaration: DeclSyntaxProtocol, - Context: MacroExpansionContext - >( + public static func expansion( of node: AttributeSyntax, - attachedTo decl: Declaration, - providingAttributesFor member: MemberDeclaration, - in context: Context + attachedTo decl: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [AttributeSyntax] { guard let property = member.as(VariableDeclSyntax.self), property.bindings.count == 1 @@ -500,8 +466,8 @@ public struct WrapStoredProperties: MemberAttributeMacro { switch binding.accessorBlock?.accessors { case .none: break - case .accessors(let accessorList): - for accessor in accessorList { + case .accessors(let node): + for accessor in node { switch accessor.accessorSpecifier.tokenKind { case .keyword(.get), .keyword(.set): return [] @@ -536,25 +502,16 @@ extension CustomTypeWrapperMacro: MemberMacro { providingMembersOf declaration: Declaration, in context: Context ) throws -> [DeclSyntax] { - return [ - """ - - var _storage: Wrapper - """ - ] + return ["var _storage: Wrapper"] } } extension CustomTypeWrapperMacro: MemberAttributeMacro { - static func expansion< - Declaration: DeclGroupSyntax, - MemberDeclaration: DeclSyntaxProtocol, - Context: MacroExpansionContext - >( + static func expansion( of node: AttributeSyntax, - attachedTo declaration: Declaration, - providingAttributesFor member: MemberDeclaration, - in context: Context + attachedTo declaration: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [AttributeSyntax] { return [ AttributeSyntax( @@ -568,13 +525,10 @@ extension CustomTypeWrapperMacro: MemberAttributeMacro { } extension CustomTypeWrapperMacro: AccessorMacro { - static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( + static func expansion( of node: AttributeSyntax, - providingAccessorsOf declaration: Declaration, - in context: Context + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) throws -> [AccessorDeclSyntax] { guard let property = declaration.as(VariableDeclSyntax.self), let binding = property.bindings.first, @@ -588,16 +542,14 @@ extension CustomTypeWrapperMacro: AccessorMacro { return [ """ - - get { - _storage[wrappedKeyPath: \\.\(identifier)] - } + get { + _storage[wrappedKeyPath: \\.\(identifier)] + } """, """ - - set { - _storage[wrappedKeyPath: \\.\(identifier)] = newValue - } + set { + _storage[wrappedKeyPath: \\.\(identifier)] = newValue + } """, ] } @@ -739,6 +691,7 @@ public let testMacros: [String: Macro.Type] = [ "checkContext": CheckContextIndependenceMacro.self, "colorLiteral": ColorLiteralMacro.self, "column": ColumnMacro.self, + "constantOne": ConstantOneGetter.self, "fileID": FileIDMacro.self, "imageLiteral": ImageLiteralMacro.self, "stringify": StringifyMacro.self, @@ -773,6 +726,21 @@ final class MacroSystemTests: XCTestCase { ) } + func testCommentsOnExpressionMacro() { + assertMacroExpansion( + """ + let b = + /*leading */ #stringify(x + y) /*trailing*/ + """, + expandedSource: """ + let b = + (x + y, "x + y") + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + func testStringifyExpression() { assertMacroExpansion( """ @@ -846,8 +814,8 @@ final class MacroSystemTests: XCTestCase { """, expandedSource: """ struct X { - func f() { - } + func f() { } + #myError(bad) func g() { } } @@ -889,7 +857,8 @@ final class MacroSystemTests: XCTestCase { var x: Int """, expandedSource: """ - var x: Int { + var x: Int + { get { _x.wrappedValue } @@ -897,6 +866,7 @@ final class MacroSystemTests: XCTestCase { _x.wrappedValue = newValue } } + private var _x: MyWrapperType """, macros: testMacros, @@ -904,6 +874,230 @@ final class MacroSystemTests: XCTestCase { ) } + func testAccessorOnVariableDeclWithExistingGetter() { + assertMacroExpansion( + """ + @constantOne + var x: Int { + return 42 + } + """, + expandedSource: """ + var x: Int { + get { + return 42 + } + get { + return 1 + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + + assertMacroExpansion( + """ + struct Foo { + @constantOne + var x: Int { + return 42 + } + } + """, + expandedSource: """ + struct Foo { + var x: Int { + get { + return 42 + } + get { + return 1 + } + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + + assertMacroExpansion( + """ + @constantOne + var x: Int { + get { + return 42 + } + } + """, + expandedSource: """ + var x: Int { + get { + return 42 + } + get { + return 1 + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testAccessorOnSubscript() { + // Adding an accessor to a subscript without an accessor isn't supported by + // the compiler (it complains that the subscript should have a body) but we + // can stil make the most reasonable syntactic expansion. + assertMacroExpansion( + """ + struct Foo { + @constantOne + subscript() -> Int + } + """, + expandedSource: """ + struct Foo { + subscript() -> Int + { + get { + return 1 + } + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testAccessorOnSubscriptDeclWithExistingGetter() { + assertMacroExpansion( + """ + struct Foo { + @constantOne + subscript() -> Int { + return 42 + } + } + """, + expandedSource: """ + struct Foo { + subscript() -> Int { + get { + return 42 + } + get { + return 1 + } + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + + assertMacroExpansion( + """ + struct Foo { + @constantOne + subscript() -> Int { + return 42 + } + } + """, + expandedSource: """ + struct Foo { + subscript() -> Int { + get { + return 42 + } + get { + return 1 + } + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + + assertMacroExpansion( + """ + struct Foo { + @constantOne + subscript() -> Int { + get { + return 42 + } + } + } + """, + expandedSource: """ + struct Foo { + subscript() -> Int { + get { + return 42 + } + get { + return 1 + } + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testAccessorOnVariableDeclWithMultipleBindings() { + assertMacroExpansion( + """ + @wrapProperty("MyWrapperType") + var x: Int, y: Int + """, + expandedSource: """ + var x: Int, y: Int + + private var _x: MyWrapperType + """, + diagnostics: [ + DiagnosticSpec( + message: + "swift-syntax applies macros syntactically but there is no way to represent a variable declaration with multiple bindings that have accessors syntactically. While the compiler allows this expansion, swift-syntax cannot represent it and thus disallows it.", + line: 1, + column: 1, + severity: .error + ) + ], + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testMultipleAccessorMacros() { + assertMacroExpansion( + """ + @constantOne + @constantOne + var x: Int + """, + expandedSource: """ + var x: Int + { + get { + return 1 + } + get { + return 1 + } + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + func testAddCompletionHandler() { assertMacroExpansion( """ @@ -911,8 +1105,7 @@ final class MacroSystemTests: XCTestCase { func f(a: Int, for b: String, _ value: Double) async -> String { } """, expandedSource: """ - func f(a: Int, for b: String, _ value: Double) async -> String { - } + func f(a: Int, for b: String, _ value: Double) async -> String { } func f(a: Int, for b: String, _ value: Double, completionHandler: (String) -> Void) { Task { @@ -936,6 +1129,30 @@ final class MacroSystemTests: XCTestCase { expandedSource: """ struct S { var value: Int + + var _storage: Storage + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testCommentAroundeAttachedMacro() { + assertMacroExpansion( + """ + /// Some doc comment + @addBackingStorage /* trailing */ + struct S { + var value: Int + } + """, + expandedSource: """ + /// Some doc comment + /* trailing */ + struct S { + var value: Int + var _storage: Storage } """, @@ -967,20 +1184,14 @@ final class MacroSystemTests: XCTestCase { @Wrapper var y: Int @Wrapper - var description: String { - "" - } + var description: String { "" } @Wrapper var computed: Int { - get { - 0 - } - set { - } + get { 0 } + set {} } - func test() { - } + func test() {} } """, macros: testMacros, @@ -1011,20 +1222,14 @@ final class MacroSystemTests: XCTestCase { @Wrapper var y: Int - var description: String { - "" - } + var description: String { "" } var computed: Int { - get { - 0 - } - set { - } + get { 0 } + set {} } - func test() { - } + func test() {} } """, macros: testMacros, @@ -1044,7 +1249,8 @@ final class MacroSystemTests: XCTestCase { expandedSource: """ struct Point { - var x: Int { + var x: Int + { get { _storage[wrappedKeyPath: \\.x] } @@ -1052,7 +1258,8 @@ final class MacroSystemTests: XCTestCase { _storage[wrappedKeyPath: \\.x] = newValue } } - var y: Int { + var y: Int + { get { _storage[wrappedKeyPath: \\.y] } @@ -1060,6 +1267,7 @@ final class MacroSystemTests: XCTestCase { _storage[wrappedKeyPath: \\.y] = newValue } } + var _storage: Wrapper } """, @@ -1115,6 +1323,28 @@ final class MacroSystemTests: XCTestCase { } func testDeclsFromStringLiterals() { + assertMacroExpansion( + #""" + #decls( + """ + static func foo() { + print("value") } + """, + "struct Inner {\n\n}" + ) + """#, + expandedSource: #""" + static func foo() { + print("value") + } + struct Inner { + + } + """#, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) + assertMacroExpansion( #""" struct S { @@ -1185,7 +1415,94 @@ final class MacroSystemTests: XCTestCase { macros: ["decls": DeclsFromStringsMacroNoAttrs.self], indentationWidth: indentationWidth ) + } + + func testIndentationOfMultipleModifiers() { + assertMacroExpansion( + """ + struct Foo { + public + static #decls("func foo() {}") + } + """, + expandedSource: """ + struct Foo { + public + static func foo() { + } + } + """, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) + } + + func testCommentsOnFreestandingDeclsExpansions() { + assertMacroExpansion( + """ + // some comment + #decls( + "func foo() {}", + "func bar() {}" + ) /* trailing comment */ + """, + expandedSource: """ + func foo() { + } + func bar() { + } + """, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) + } + + func testCommentsOnFreestandingDeclsExpansionsInMemberDeclList() { + assertMacroExpansion( + """ + struct Foo { + // some comment + #decls( + "func foo() {}", + "func bar() {}" + ) /* trailing comment */ + } + """, + expandedSource: """ + struct Foo { + func foo() { + } + func bar() { + } + } + """, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) + } + func testFreestandingDeclThatIncludesDocComment() { + assertMacroExpansion( + #""" + struct Foo { + #decls( + """ + /// Some doc comment + func foo() {} + """ + ) + } + """#, + expandedSource: """ + struct Foo { + /// Some doc comment + func foo() { + } + } + """, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) } func testConformanceExpansion() { @@ -1199,6 +1516,7 @@ final class MacroSystemTests: XCTestCase { struct MyType { } + extension MyType: Sendable { } """, @@ -1216,6 +1534,7 @@ final class MacroSystemTests: XCTestCase { extension A.B { } + extension A.B: Sendable { } """, @@ -1235,6 +1554,30 @@ final class MacroSystemTests: XCTestCase { struct MyType { } + + extension MyType: Sendable { + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + + func testNestedExtensionExpansion() { + assertMacroExpansion( + """ + struct Wrapper { + @AddSendableExtension + struct MyType { + } + } + """, + expandedSource: """ + struct Wrapper { + struct MyType { + } + } + extension MyType: Sendable { } """, @@ -1242,4 +1585,25 @@ final class MacroSystemTests: XCTestCase { indentationWidth: indentationWidth ) } + + func testAttributeWithComment() { + assertMacroExpansion( + """ + @wrapAllProperties struct S { + // Var value + var value = 1 + } + """, + expandedSource: """ + struct S { + @Wrapper + // Var value + var value = 1 + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } + } From a079352cf9216a1350ad9d68400f49947d9d29fd Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 26 Jul 2023 13:20:45 -0700 Subject: [PATCH 5/6] Pass `nil` as the default `indentationWidth` --- Sources/SwiftBasicFormat/BasicFormat.swift | 7 +++++-- .../MacroExpansion.swift | 15 +++++++++------ .../SwiftSyntaxMacroExpansion/MacroSystem.swift | 9 ++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftBasicFormat/BasicFormat.swift b/Sources/SwiftBasicFormat/BasicFormat.swift index 9a8092cd171..f2815a2cb80 100644 --- a/Sources/SwiftBasicFormat/BasicFormat.swift +++ b/Sources/SwiftBasicFormat/BasicFormat.swift @@ -49,11 +49,14 @@ open class BasicFormat: SyntaxRewriter { private var previousToken: TokenSyntax? = nil public init( - indentationWidth: Trivia = .spaces(4), + indentationWidth: Trivia? = nil, initialIndentation: Trivia = [], viewMode: SyntaxTreeViewMode = .sourceAccurate ) { - self.indentationWidth = indentationWidth + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from the + // source file to format in case it is already partially formatted. + self.indentationWidth = indentationWidth ?? .spaces(4) self.indentationStack = [(indentation: initialIndentation, isUserDefined: false)] super.init(viewMode: viewMode) } diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index de60336d7df..82f95c54792 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -92,7 +92,7 @@ public func expandFreestandingMacro( macroRole: MacroRole, node: FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext, - indentationWidth: Trivia = .spaces(4) + indentationWidth: Trivia? = nil ) -> String? { do { let expandedSyntax: Syntax @@ -192,7 +192,7 @@ public func expandAttachedMacroWithoutCollapsing extendedType: TypeSyntax?, conformanceList: InheritedTypeListSyntax?, in context: Context, - indentationWidth: Trivia = .spaces(4) + indentationWidth: Trivia? = nil ) -> [String]? { do { switch (definition, macroRole) { @@ -320,7 +320,7 @@ public func expandAttachedMacro( extendedType: TypeSyntax?, conformanceList: InheritedTypeListSyntax?, in context: Context, - indentationWidth: Trivia = .spaces(4) + indentationWidth: Trivia? = nil ) -> String? { let expandedSources = expandAttachedMacroWithoutCollapsing( definition: definition, @@ -341,7 +341,7 @@ public func expandAttachedMacro( fileprivate extension SyntaxProtocol { /// Perform a format if required and then trim any leading/trailing /// whitespace. - func formattedExpansion(_ mode: FormatMode, indentationWidth: Trivia) -> String { + func formattedExpansion(_ mode: FormatMode, indentationWidth: Trivia?) -> String { let formatted: Syntax switch mode { case .auto: @@ -396,7 +396,7 @@ public func collapse( expansions: [String], for role: MacroRole, attachedTo declarationNode: Node, - indentationWidth: Trivia = .spaces(4) + indentationWidth: Trivia? = nil ) -> String { if expansions.isEmpty { return "" @@ -421,7 +421,10 @@ public func collapse( onDeclarationWithoutAccessor = false } if onDeclarationWithoutAccessor { - expansions = expansions.map({ $0.indented(by: indentationWidth) }) + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from + // the expansions to collapse. + expansions = expansions.map({ $0.indented(by: indentationWidth ?? .spaces(4)) }) expansions[0] = "{\n" + expansions[0] expansions[expansions.count - 1] += "\n}" } diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift index 2a2495cbfc5..5ec6ebda1da 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift @@ -23,7 +23,7 @@ extension SyntaxProtocol { public func expand( macros: [String: Macro.Type], in context: some MacroExpansionContext, - indentationWidth: Trivia = .spaces(4) + indentationWidth: Trivia? = nil ) -> Syntax { // Build the macro system. var system = MacroSystem() @@ -463,11 +463,14 @@ private class MacroApplication: SyntaxRewriter { init( macroSystem: MacroSystem, context: Context, - indentationWidth: Trivia + indentationWidth: Trivia? ) { self.macroSystem = macroSystem self.context = context - self.indentationWidth = indentationWidth + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from the + // source file in which we expand the macros. + self.indentationWidth = indentationWidth ?? .spaces(4) super.init(viewMode: .sourceAccurate) } From 3477df60c43b2c33e110a10dbd8336394d8f8583 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 26 Jul 2023 13:13:01 -0700 Subject: [PATCH 6/6] Address review comments --- .../Sources/SyntaxSupport/DeclNodes.swift | 2 +- .../CollectionNodes+Parsable.swift | 3 +- Sources/SwiftParser/Declarations.swift | 6 +- .../generated/LayoutNodes+Parsable.swift | 2 +- .../IndentationUtils.swift | 2 +- .../MacroExpansion.swift | 3 +- .../MacroSystem.swift | 28 ++++---- .../MacroSystemTests.swift | 68 ++++++++++++++----- 8 files changed, 76 insertions(+), 38 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift index 2411a09deb4..fa058212ddc 100644 --- a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift @@ -46,7 +46,7 @@ public let DECL_NODES: [Node] = [ kind: .accessorBlock, base: .syntax, nameForDiagnostics: nil, - parserFunction: "parseGetSet", + parserFunction: "parseAccessorBlock", traits: [ "Braced" ], diff --git a/Sources/SwiftParser/CollectionNodes+Parsable.swift b/Sources/SwiftParser/CollectionNodes+Parsable.swift index daf31ce0035..a3b75e47f4d 100644 --- a/Sources/SwiftParser/CollectionNodes+Parsable.swift +++ b/Sources/SwiftParser/CollectionNodes+Parsable.swift @@ -119,7 +119,8 @@ extension MemberBlockItemListSyntax: SyntaxParseable { //==========================================================================// // IMPORTANT: When adding a new conformance, also add a corrsponding // -// conformance to SyntaxExpressibleByStringInterpolation. // +// conformance to SyntaxExpressibleByStringInterpolation in // +// SyntaxExpressibleByStringInterpolationConformances.swift // //==========================================================================// //==========================================================================// diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 28e58eddd42..ae03b3506a3 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1151,7 +1151,7 @@ extension Parser { // Parse getter and setter. let accessor: RawAccessorBlockSyntax? if self.at(.leftBrace) || self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil { - accessor = self.parseGetSet() + accessor = self.parseAccessorBlock() } else { accessor = nil } @@ -1265,7 +1265,7 @@ extension Parser { let accessors: RawAccessorBlockSyntax? if self.at(.leftBrace) || (inMemberDeclList && self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil && !self.at(.keyword(.`init`))) { - accessors = self.parseGetSet() + accessors = self.parseAccessorBlock() } else { accessors = nil } @@ -1408,7 +1408,7 @@ extension Parser { /// Parse the body of a variable declaration. This can include explicit /// getters, setters, and observers, or the body of a computed property. - mutating func parseGetSet() -> RawAccessorBlockSyntax { + mutating func parseAccessorBlock() -> RawAccessorBlockSyntax { // Parse getter and setter. let unexpectedBeforeLBrace: RawUnexpectedNodesSyntax? let lbrace: RawTokenSyntax diff --git a/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift index 1dd864c345a..22dd88571c9 100644 --- a/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift +++ b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift @@ -30,7 +30,7 @@ extension AccessorBlockSyntax: SyntaxParseable { withExtendedLifetime(parser) { } } - let node = parser.parseGetSet() + let node = parser.parseAccessorBlock() let raw = RawSyntax(parser.parseRemainder(into: node)) return Syntax(raw: raw, rawNodeArena: raw.arena).cast(Self.self) } diff --git a/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift index 4011fbe04a9..ca8fe6efe11 100644 --- a/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift +++ b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift @@ -27,7 +27,7 @@ extension SyntaxProtocol { // MARK: String.indented extension String { - /// Indents every new line in this string literal by `indentation`. + /// Indents every new line in this string by `indentation`. /// /// - Note: The first line in the string gets indented as well. func indented(by indentation: Trivia) -> String { diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index 82f95c54792..75b60e6ec8d 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -403,7 +403,7 @@ public func collapse( } var expansions = expansions - var separator = "\n" + var separator = "\n\n" switch role { case .accessor: @@ -427,6 +427,7 @@ public func collapse( expansions = expansions.map({ $0.indented(by: indentationWidth ?? .spaces(4)) }) expansions[0] = "{\n" + expansions[0] expansions[expansions.count - 1] += "\n}" + separator = "\n" } case .memberAttribute: separator = " " diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift index 5ec6ebda1da..25a84649661 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift @@ -263,9 +263,11 @@ private func expandAccessorMacroWithoutExistingAccessors( return nil } - // `expandAttachedMacro` adds the `{` and `}` to wrap the accessor block. - // Since the entire block is indented, we start it on a new line. - let indentedSource = "\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + // `expandAttachedMacro` adds the `{` and `}` to wrap the accessor block and + // then indents it. + // Remove any indentaiton from the first line using `drop(while:)` and then + // prepend a space to separate it from the variable declaration + let indentedSource = " " + expanded.indented(by: attachedTo.indentationOfFirstLine).drop(while: { $0.isWhitespace }) return "\(raw: indentedSource)" } @@ -434,14 +436,14 @@ private enum MacroApplicationError: DiagnosticMessage, Error { switch self { case .accessorMacroOnVariableWithMultipleBindings: return """ - swift-syntax applies macros syntactically but there is no way to represent a variable \ + swift-syntax applies macros syntactically and there is no way to represent a variable \ declaration with multiple bindings that have accessors syntactically. \ While the compiler allows this expansion, swift-syntax cannot represent it and thus \ disallows it. """ case .malformedAccessor: return """ - Macro returned a mal-formed accessor. Accessors should start with an introducer like 'get' or 'set'. + Macro returned a malformed accessor. Accessors should start with an introducer like 'get' or 'set'. """ } } @@ -452,8 +454,8 @@ private class MacroApplication: SyntaxRewriter { let macroSystem: MacroSystem var context: Context var indentationWidth: Trivia - /// Nodes that we have already handled in `visitAny` and that should be visited - /// in the node-specific handling function. + /// Nodes that we are currently handling in `visitAny` and that should be + /// visited using the node-specific handling function. var skipVisitAnyHandling: Set = [] /// Store expanded extension while visiting member decls. This should be @@ -481,8 +483,8 @@ private class MacroApplication: SyntaxRewriter { // Expand 'MacroExpansionExpr'. // Note that 'MacroExpansionExpr'/'MacroExpansionExprDecl' at code item - // position are handled by 'visit(_:CodeBlockItemListSyntax)'. Here's only - // expression inside other syntax node. + // position are handled by 'visit(_:CodeBlockItemListSyntax)'. + // Only expression expansions inside other syntax nodes is handled here. if let expanded = expandExpr(node: node) { return Syntax(visit(expanded)) } @@ -632,7 +634,7 @@ extension MacroApplication { return attributes.compactMap { guard case let .attribute(attribute) = $0, - let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, + let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, let macro = macroSystem.lookup(attributeName) else { return nil @@ -673,13 +675,13 @@ extension MacroApplication { ) -> [ExpandedNode] { var result: [ExpandedNode] = [] - for peerMacro in macroAttributes(attachedTo: decl, ofType: ofType) { + for macroAttribute in macroAttributes(attachedTo: decl, ofType: ofType) { do { - if let expanded = try expandMacro(peerMacro.attributeNode, peerMacro.definition) { + if let expanded = try expandMacro(macroAttribute.attributeNode, macroAttribute.definition) { result += expanded } } catch { - context.addDiagnostics(from: error, node: peerMacro.attributeNode) + context.addDiagnostics(from: error, node: macroAttribute.attributeNode) } } return result diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift index 0d9b0046e27..6aa02845a9e 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift @@ -608,13 +608,10 @@ public struct UnwrapMacro: CodeItemMacro { } } -public struct DeclsFromStringsMacro: DeclarationMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { +public struct DeclsFromStringsMacro: DeclarationMacro, MemberMacro { + private static func decls(from arguments: LabeledExprListSyntax) -> [DeclSyntax] { var strings: [String] = [] - for arg in node.argumentList { + for arg in arguments { guard let value = arg.expression.as(StringLiteralExprSyntax.self)?.representedLiteralValue else { @@ -627,6 +624,24 @@ public struct DeclsFromStringsMacro: DeclarationMacro { "\(raw: $0)" } } + + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + return decls(from: node.argumentList) + } + + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + guard case .argumentList(let arguments) = node.arguments else { + return [] + } + return decls(from: arguments) + } } public struct SendableConformanceMacro: ConformanceMacro { @@ -857,8 +872,7 @@ final class MacroSystemTests: XCTestCase { var x: Int """, expandedSource: """ - var x: Int - { + var x: Int { get { _x.wrappedValue } @@ -958,8 +972,7 @@ final class MacroSystemTests: XCTestCase { """, expandedSource: """ struct Foo { - subscript() -> Int - { + subscript() -> Int { get { return 1 } @@ -1064,7 +1077,7 @@ final class MacroSystemTests: XCTestCase { diagnostics: [ DiagnosticSpec( message: - "swift-syntax applies macros syntactically but there is no way to represent a variable declaration with multiple bindings that have accessors syntactically. While the compiler allows this expansion, swift-syntax cannot represent it and thus disallows it.", + "swift-syntax applies macros syntactically and there is no way to represent a variable declaration with multiple bindings that have accessors syntactically. While the compiler allows this expansion, swift-syntax cannot represent it and thus disallows it.", line: 1, column: 1, severity: .error @@ -1083,8 +1096,7 @@ final class MacroSystemTests: XCTestCase { var x: Int """, expandedSource: """ - var x: Int - { + var x: Int { get { return 1 } @@ -1249,8 +1261,7 @@ final class MacroSystemTests: XCTestCase { expandedSource: """ struct Point { - var x: Int - { + var x: Int { get { _storage[wrappedKeyPath: \\.x] } @@ -1258,8 +1269,7 @@ final class MacroSystemTests: XCTestCase { _storage[wrappedKeyPath: \\.x] = newValue } } - var y: Int - { + var y: Int { get { _storage[wrappedKeyPath: \\.y] } @@ -1417,6 +1427,30 @@ final class MacroSystemTests: XCTestCase { ) } + func testMemberDeclsFromStringLiterals() { + assertMacroExpansion( + """ + @decls("func foo() {}", "func bar() {}" + struct Foo { + var member: Int + } + """, + expandedSource: """ + struct Foo { + var member: Int + + func foo() { + } + + func bar() { + } + } + """, + macros: ["decls": DeclsFromStringsMacro.self], + indentationWidth: indentationWidth + ) + } + func testIndentationOfMultipleModifiers() { assertMacroExpansion( """