diff --git a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift index 28e4baf5045..ca49a20150c 100644 --- a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift @@ -139,6 +139,11 @@ public let ATTRIBUTE_NODES: [Node] = [ name: "documentationArguments", kind: .node(kind: .documentationAttributeArgumentList) ), + Child( + name: "abiArguments", + kind: .node(kind: .abiAttributeArguments), + experimentalFeature: .abiAttribute + ), ]), documentation: """ The arguments of the attribute. @@ -267,6 +272,30 @@ public let ATTRIBUTE_NODES: [Node] = [ ] ), + Node( + kind: .abiAttributeArguments, + base: .syntax, + experimentalFeature: .abiAttribute, + nameForDiagnostics: "ABI-providing declaration", + documentation: "The arguments of the '@abi' attribute", + children: [ + Child( + name: "provider", + kind: .nodeChoices(choices: [ + Child(name: "associatedType", kind: .node(kind: .associatedTypeDecl)), + Child(name: "deinitializer", kind: .node(kind: .deinitializerDecl)), + Child(name: "enumCase", kind: .node(kind: .enumCaseDecl)), + Child(name: "function", kind: .node(kind: .functionDecl)), + Child(name: "initializer", kind: .node(kind: .initializerDecl)), + Child(name: "missing", kind: .node(kind: .missingDecl)), + Child(name: "subscript", kind: .node(kind: .subscriptDecl)), + Child(name: "typeAlias", kind: .node(kind: .typeAliasDecl)), + Child(name: "variable", kind: .node(kind: .variableDecl)), + ]) + ) + ] + ), + Node( kind: .conventionAttributeArguments, base: .syntax, diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index b08b8b59105..6da75f84254 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -20,6 +20,7 @@ public enum ExperimentalFeature: String, CaseIterable { case trailingComma case coroutineAccessors case valueGenerics + case abiAttribute /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -38,6 +39,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "CoroutineAccessors" case .valueGenerics: return "ValueGenerics" + case .abiAttribute: + return "ABIAttribute" } } @@ -58,6 +61,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "coroutine accessors" case .valueGenerics: return "value generics" + case .abiAttribute: + return "@abi attribute" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift index dacec331673..370e73b5fac 100644 --- a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift @@ -131,6 +131,7 @@ public enum Keyword: CaseIterable { case _underlyingVersion case _UnknownLayout case _version + case abi case accesses case actor case addressWithNativeOwner @@ -405,6 +406,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("_UnknownLayout") case ._version: return KeywordSpec("_version") + case .abi: + return KeywordSpec("abi", experimentalFeature: .abiAttribute) case .accesses: return KeywordSpec("accesses") case .actor: diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index aca5c52aa86..1d9e97b0e29 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -22,6 +22,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case _canImportExpr case _canImportVersionInfo + case abiAttributeArguments case accessorBlock case accessorDecl case accessorDeclList @@ -340,6 +341,15 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon return .identifier(rawValue) } + public var uppercasedFirstWordRawValue: String { + switch self { + case .abiAttributeArguments: + "ABIAttributeArguments" + default: + rawValue.withFirstCharacterUppercased + } + } + public var syntaxType: TypeSyntax { switch self { case .syntax: @@ -347,7 +357,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case .syntaxCollection: return "SyntaxCollection" default: - return "\(raw: rawValue.withFirstCharacterUppercased)Syntax" + return "\(raw: uppercasedFirstWordRawValue)Syntax" } } diff --git a/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift b/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift index 954114c87bc..a9b68bf05ce 100644 --- a/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift +++ b/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift @@ -151,7 +151,7 @@ public struct SyntaxBuildableType: Hashable { public var resultBuilderType: TypeSyntax { switch kind { case .node(kind: let kind): - return TypeSyntax("\(raw: kind.rawValue.withFirstCharacterUppercased)Builder") + return TypeSyntax("\(raw: kind.uppercasedFirstWordRawValue)Builder") case .token: preconditionFailure("Tokens cannot be constructed using result builders") } diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxEnumFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxEnumFile.swift index b1758f26295..5afd357d4dc 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxEnumFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxEnumFile.swift @@ -60,7 +60,7 @@ let syntaxEnumFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { for base in SYNTAX_NODES where base.kind.isBase { let baseKind = base.kind - let baseName = baseKind.rawValue.withFirstCharacterUppercased + let baseName = baseKind.uppercasedFirstWordRawValue let enumType: TypeSyntax = "\(raw: baseName)SyntaxEnum" try! EnumDeclSyntax( diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index ff3fd07596d..2771b9a9e47 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #if swift(>=6) -@_spi(RawSyntax) internal import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) internal import SwiftSyntax #else -@_spi(RawSyntax) import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax #endif extension Parser { @@ -58,6 +58,7 @@ extension Parser { case _typeEraser case _unavailableFromAsync case `rethrows` + case abi case attached case available case backDeployed @@ -95,6 +96,7 @@ extension Parser { case TokenSpec(._typeEraser): self = ._typeEraser case TokenSpec(._unavailableFromAsync): self = ._unavailableFromAsync case TokenSpec(.`rethrows`): self = .rethrows + case TokenSpec(.abi) where experimentalFeatures.contains(.abiAttribute): self = .abi case TokenSpec(.attached): self = .attached case TokenSpec(.available): self = .available case TokenSpec(.backDeployed): self = .backDeployed @@ -136,6 +138,7 @@ extension Parser { case ._typeEraser: return .keyword(._typeEraser) case ._unavailableFromAsync: return .keyword(._unavailableFromAsync) case .`rethrows`: return .keyword(.rethrows) + case .abi: return .keyword(.abi) case .attached: return .keyword(.attached) case .available: return .keyword(.available) case .backDeployed: return .keyword(.backDeployed) @@ -176,9 +179,16 @@ extension Parser { case noArgument } + /// Parse the argument of an attribute, if it has one. + /// + /// - Parameters: + /// - argumentMode: Indicates whether the attribute must, may, or may not have an argument. + /// - parseArguments: Called to parse the argument list. If there is an opening parenthesis, it will have already been consumed. + /// - parseMissingArguments: If provided, called instead of `parseArgument` when an argument list was required but no opening parenthesis was present. mutating func parseAttribute( argumentMode: AttributeArgumentMode, - parseArguments: (inout Parser) -> RawAttributeSyntax.Arguments + parseArguments: (inout Parser) -> RawAttributeSyntax.Arguments, + parseMissingArguments: ((inout Parser) -> RawAttributeSyntax.Arguments)? = nil ) -> RawAttributeListSyntax.Element { var (unexpectedBeforeAtSign, atSign) = self.expect(.atSign) if atSign.trailingTriviaByteLength > 0 || self.currentToken.leadingTriviaByteLength > 0 { @@ -213,7 +223,12 @@ extension Parser { ) leftParen = leftParen.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena) } - let argument = parseArguments(&self) + let argument: RawAttributeSyntax.Arguments + if let parseMissingArguments, leftParen.presence == .missing { + argument = parseMissingArguments(&self) + } else { + argument = parseArguments(&self) + } let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) return .attribute( RawAttributeSyntax( @@ -255,6 +270,12 @@ extension Parser { } switch peek(isAtAnyIn: DeclarationAttributeWithSpecialSyntax.self) { + case .abi: + return parseAttribute(argumentMode: .required) { parser in + return .abiArguments(parser.parseABIAttributeArguments()) + } parseMissingArguments: { parser in + return .abiArguments(parser.parseABIAttributeArguments(missingLParen: true)) + } case .available, ._spi_available: return parseAttribute(argumentMode: .required) { parser in return .availability(parser.parseAvailabilityArgumentSpecList()) @@ -918,6 +939,52 @@ extension Parser { } } +extension Parser { + /// Parse the arguments inside an `@abi(...)` attribute. + /// + /// - Parameter missingLParen: `true` if the opening paren for the argument list was missing. + mutating func parseABIAttributeArguments(missingLParen: Bool = false) -> RawABIAttributeArgumentsSyntax { + func makeMissingProviderArguments(unexpectedBefore: [RawSyntax]) -> RawABIAttributeArgumentsSyntax { + return RawABIAttributeArgumentsSyntax( + provider: .missing( + RawMissingDeclSyntax( + unexpectedBefore.isEmpty ? nil : RawUnexpectedNodesSyntax(elements: unexpectedBefore, arena: self.arena), + attributes: self.emptyCollection(RawAttributeListSyntax.self), + modifiers: self.emptyCollection(RawDeclModifierListSyntax.self), + placeholder: self.missingToken(.identifier, text: "<#declaration#>"), + arena: arena + ) + ), + arena: self.arena + ) + } + + // Consider the three kinds of mistakes we might see here: + // + // 1. The user forgot the argument: `@abi(<>) var x: Int` + // 2. The user forgot the left paren: `@abi<> var x_abi: Int) var x: Int` + // 3. The user forgot the whole argument list: `@abi<> var x: Int` + // + // It's difficult to write code that recovers from both #2 and #3. The problem is that in both cases, what comes + // next looks like a declaration, so a simple lookahead cannot distinguish between them--you'd have to parse all + // the way to the closing paren. (And what if *that's* also missing?) + // + // In lieu of that, we judge that recovering gracefully from #3 is more important than #2 and therefore do not even + // attempt to parse the argument unless we've seen a left paren. + guard !missingLParen && !self.at(.rightParen) else { + return makeMissingProviderArguments(unexpectedBefore: []) + } + + let decl = parseDeclaration(in: .argumentList) + + guard let provider = RawABIAttributeArgumentsSyntax.Provider(decl) else { + return makeMissingProviderArguments(unexpectedBefore: [decl.raw]) + } + + return RawABIAttributeArgumentsSyntax(provider: provider, arena: self.arena) + } +} + extension Parser { mutating func parseBackDeployedAttributeArguments() -> RawBackDeployedAttributeArgumentsSyntax { let (unexpectedBeforeLabel, label) = self.expect(.keyword(.before)) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 8b68c484eca..1f7184fdce5 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -168,11 +168,61 @@ extension Parser { } } + /// Describes the context around a declaration in order to modify how it is parsed. + enum DeclarationParseContext { + /// The declaration is in top-level code or a function body; that is, it may be mixed with statements and + /// expressions. + case topLevelOrCodeBlock + + /// The declaration is in a member list. + case memberDeclList + + /// The declaration is in an argument list (for instance, of an `@abi` attribute). + case argumentList + + /// Should the parser assume that any syntax it encounters here *must* be declaration syntax? This allows more + /// aggressive recovery which might misinterpret statement or expression syntax as malformed declaration syntax. + var requiresDecl: Bool { + switch self { + case .topLevelOrCodeBlock: + return false + case .memberDeclList, .argumentList: + return true + } + } + + /// If an introducer is not found at the expected location, what token should terminate our search for one? + var recoveryPrecedence: TokenPrecedence? { + switch self { + case .topLevelOrCodeBlock: + // Scan as far as we want. + return nil + case .memberDeclList: + // Don't scan past the enclosing brace. + return .closingBrace + case .argumentList: + // Don't scan past the closing parenthesis. + return .weakBracketed(closingDelimiter: .rightParen) + } + } + + /// Is `#if` allowed in this context? If not, we parse it into unexpected syntax on whatever declaration is nested + /// inside it. + var allowsIfConfigDecl: Bool { + switch self { + case .topLevelOrCodeBlock, .memberDeclList: + return true + case .argumentList: + return false + } + } + } + /// Parse a declaration. /// - /// If `inMemberDeclList` is `true`, we know that the next item must be a - /// declaration and thus start with a keyword. This allows further recovery. - mutating func parseDeclaration(inMemberDeclList: Bool = false) -> RawDeclSyntax { + /// - Parameter context: Describes the code around the declaration being parsed. This affects how the parser tries + /// to recover from malformed syntax in the declaration. + mutating func parseDeclaration(in context: DeclarationParseContext = .topLevelOrCodeBlock) -> RawDeclSyntax { // If we are at a `#if` of attributes, the `#if` directive should be // parsed when we're parsing the attributes. if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { @@ -200,6 +250,20 @@ extension Parser { } syntax: { parser, elements in return .decls(RawMemberBlockItemListSyntax(elements: elements, arena: parser.arena)) } + if !context.allowsIfConfigDecl { + // Convert the IfConfig to unexpected syntax around the first decl inside it, if any. + return directive.makeUnexpectedKeepingFirstNode(of: RawDeclSyntax.self, arena: self.arena) { node in + return !node.is(RawIfConfigDeclSyntax.self) + } makeMissing: { + return RawDeclSyntax( + RawMissingDeclSyntax( + attributes: self.emptyCollection(RawAttributeListSyntax.self), + modifiers: self.emptyCollection(RawDeclModifierListSyntax.self), + arena: self.arena + ) + ) + } + } return RawDeclSyntax(directive) } @@ -221,8 +285,10 @@ extension Parser { // to parse. // If we are inside a memberDecl list, we don't want to eat closing braces (which most likely close the outer context) // while recovering to the declaration start. - let recoveryPrecedence = inMemberDeclList ? TokenPrecedence.closingBrace : nil - recoveryResult = self.canRecoverTo(anyIn: DeclarationKeyword.self, overrideRecoveryPrecedence: recoveryPrecedence) + recoveryResult = self.canRecoverTo( + anyIn: DeclarationKeyword.self, + overrideRecoveryPrecedence: context.recoveryPrecedence + ) } switch recoveryResult { @@ -273,17 +339,17 @@ extension Parser { case (.lhs(.pound), let handle)?: return RawDeclSyntax(self.parseMacroExpansionDeclaration(attrs, handle)) case (.rhs, let handle)?: - return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, inMemberDeclList: inMemberDeclList)) + return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, in: context)) case nil: break } - if inMemberDeclList { + if context.requiresDecl { let isProbablyVarDecl = self.at(.identifier, .wildcard) && self.peek(isAt: .colon, .equal, .comma) let isProbablyTupleDecl = self.at(.leftParen) && self.peek(isAt: .identifier, .wildcard) if isProbablyVarDecl || isProbablyTupleDecl { - return RawDeclSyntax(self.parseBindingDeclaration(attrs, .missing(.keyword(.var)))) + return RawDeclSyntax(self.parseBindingDeclaration(attrs, .missing(.keyword(.var)), in: context)) } if self.currentToken.isEditorPlaceholder { @@ -787,7 +853,7 @@ extension Parser { if self.at(.poundSourceLocation) { decl = RawDeclSyntax(self.parsePoundSourceLocationDirective()) } else { - decl = self.parseDeclaration(inMemberDeclList: true) + decl = self.parseDeclaration(in: .memberDeclList) } let semi = self.consume(if: .semicolon) @@ -1309,7 +1375,7 @@ extension Parser { mutating func parseBindingDeclaration( _ attrs: DeclAttributes, _ handle: RecoveryConsumptionHandle, - inMemberDeclList: Bool = false + in context: DeclarationParseContext ) -> RawVariableDeclSyntax { let (unexpectedBeforeIntroducer, introducer) = self.eat(handle) let hasTryBeforeIntroducer = unexpectedBeforeIntroducer?.containsToken(where: { TokenSpec(.try) ~= $0 }) ?? false @@ -1399,7 +1465,7 @@ extension Parser { if (self.at(.leftBrace) && (initializer == nil || !self.currentToken.isAtStartOfLine || self.withLookahead({ $0.atStartOfGetSetAccessor() }))) - || (inMemberDeclList && self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil + || (context.requiresDecl && self.at(anyIn: AccessorDeclSyntax.AccessorSpecifierOptions.self) != nil && !self.at(.keyword(.`init`))) { accessors = self.parseAccessorBlock() diff --git a/Sources/SwiftParser/SyntaxUtils.swift b/Sources/SwiftParser/SyntaxUtils.swift index 01bf1e7b141..992b648a2b3 100644 --- a/Sources/SwiftParser/SyntaxUtils.swift +++ b/Sources/SwiftParser/SyntaxUtils.swift @@ -105,6 +105,169 @@ extension RawUnexpectedNodesSyntax { } } +// MARK: - Converting nodes to unexpected nodes + +extension RawSyntaxNodeProtocol { + /// Select one node somewhere inside `self` and return a version of it with all of the other tokens in `self` + /// converted to unexpected nodes. + /// + /// This is the same as ``RawSyntaxNodeProtocol/makeUnexpectedKeepingNodes(of:arena:where:makeMissing:)`` except that + /// it always keeps, and always returns, exactly one node (the first one that would be encountered in a depth-first + /// search, or the missing node). The other nodes that would have been kept are converted to unexpected tokens as + /// usual. See that method for details about the behavior. + /// + /// - Parameters: + /// - keptType: The type of node that should be kept and returned. + /// - arena: The syntax arena to allocate new or copied nodes within. + /// - predicate: Should return `true` for the node that should be kept. Allows more selectivity than `keptType` + /// alone. + /// - makeMissing: If there are no children which satisfy `keptType` and `predicate`, this closure will be called + /// to create a substitute node for the unexpected syntax to be attached to. It's typically used to return a "missing syntax" + /// node, though that's not a technical requirement. + /// + /// - Returns: The kept node (or the missing node) with the other tokens in `self` attached to its leading and + /// trailing unexpected node children. + func makeUnexpectedKeepingFirstNode( + of keptType: KeptNode.Type, + arena: SyntaxArena, + where predicate: (KeptNode) -> Bool, + makeMissing: () -> KeptNode + ) -> KeptNode { + var alreadyFoundFirst = false + func compositePredicate(_ node: KeptNode) -> Bool { + if alreadyFoundFirst || !predicate(node) { + return false + } + alreadyFoundFirst = true + return true + } + + return makeUnexpectedKeepingNodes( + of: keptType, + arena: arena, + where: compositePredicate, + makeMissing: makeMissing + ).first! + } + + /// Select a number of nodes inside `self` and return versions of them with all of the other tokens in`self` + /// attached to them as unexpected nodes. + /// + /// For instance, if you had a `RawIfConfigDeclSyntax` like this: + /// + /// ```swift + /// #if FOO + /// func x() {} + /// func y() {} + /// #elseif BAR + /// func a() {} + /// func b() {} + /// #endif + /// ``` + /// + /// Then a call like this: + /// + /// ```swift + /// let functions = directive.makeUnexpectedKeepingNodes( + /// of: RawFunctionDeclSyntax.self, + /// arena: parser.arena, + /// where: { node in true }, + /// makeMissing: { RawFunctionDeclSyntax(...) } + /// ) + /// ``` + /// + /// Would return an array of four `RawFunctionDeclSyntax` nodes with the tokens for `#if FOO`, `#elseif BAR`, and + /// `#endif` added to the nodes for `x()`, `a()`, and `b()` respectively. + /// + /// Specifically, this method performs a depth-first recursive search of the children of `self`, collecting nodes of + /// type `keptType` for which `predicate` returns `true`. These nodes are then modified to add all of the tokens + /// within `self`, but outside of the kept nodes, to their leading or trailing `RawUnexpectedNodesSyntax` child. If + /// no kept nodes are found, the `makeMissing` closure is used to create a "missing syntax" node the tokens can be + /// attached to. + /// + /// Tokens are usually added to the leading unexpected node child of the next kept node, except for tokens after the + /// last kept node, which are added to the trailing unexpected node child of the last kept node. + /// + /// Token and collection nodes cannot be kept, as they cannot have unexpected syntax attached to them; however, + /// parents of such nodes can be kept. + /// + /// - Parameters: + /// - keptType: The type of node that should be kept and returned in the array. + /// - arena: The syntax arena to allocate new or copied nodes within. + /// - predicate: Should return `true` for nodes that should be kept. Allows more selectivity than `keptType` alone. + /// - makeMissing: If there are no children which satisfy `keptType` and `predicate`, this closure will be called + /// to create a substitute node for the unexpected syntax to be attached to. It's typically used to return a "missing syntax" + /// node, though that's not a technical requirement. + /// + /// - Returns: The kept nodes (or the missing node) with the other tokens in `self` attached to them at appropriate + /// unexpected node children. Note that there is always at least one node in the array. + func makeUnexpectedKeepingNodes( + of keptType: KeptNode.Type, + arena: SyntaxArena, + where predicate: (KeptNode) -> Bool, + makeMissing: () -> KeptNode + ) -> [KeptNode] { + var keptNodes: [KeptNode] = [] + var accumulatedTokens: [RawTokenSyntax] = [] + + func attachAccumulatedTokensToLastKeptNode(appending: Bool) { + if accumulatedTokens.isEmpty { + // Nothing to add? Nothing to do. + return + } + + let lastKeptNodeIndex = keptNodes.endIndex - 1 + let lastKeptNode = keptNodes[lastKeptNodeIndex].raw.layoutView! + + let childIndex = appending ? lastKeptNode.children.endIndex - 1 : 0 + + let newUnexpected: RawUnexpectedNodesSyntax + if let oldUnexpected = lastKeptNode.children[childIndex]?.cast(RawUnexpectedNodesSyntax.self) { + if appending { + newUnexpected = RawUnexpectedNodesSyntax(combining: oldUnexpected, accumulatedTokens, arena: arena)! + } else { + newUnexpected = RawUnexpectedNodesSyntax(combining: accumulatedTokens, oldUnexpected, arena: arena)! + } + } else { + newUnexpected = RawUnexpectedNodesSyntax(accumulatedTokens, arena: arena)! + } + + keptNodes[lastKeptNodeIndex] = lastKeptNode.replacingChild( + at: childIndex, + with: newUnexpected.raw, + arena: arena + ).cast(KeptNode.self) + + accumulatedTokens.removeAll() + } + + func walk(_ raw: RawSyntax) { + if let token = RawTokenSyntax(raw) { + if !token.isMissing { + accumulatedTokens.append(token) + } + } else if !raw.kind.isSyntaxCollection, let node = raw.as(KeptNode.self), predicate(node) { + keptNodes.append(node) + attachAccumulatedTokensToLastKeptNode(appending: false) + } else { + for case let child? in raw.layoutView!.children { + walk(child) + } + } + } + + walk(self.raw) + + if keptNodes.isEmpty { + keptNodes.append(makeMissing()) + } + + attachAccumulatedTokensToLastKeptNode(appending: true) + + return keptNodes + } +} + // MARK: - Misc extension SyntaxText { diff --git a/Sources/SwiftParser/TokenPrecedence.swift b/Sources/SwiftParser/TokenPrecedence.swift index efe8f63d67c..3c7c92011de 100644 --- a/Sources/SwiftParser/TokenPrecedence.swift +++ b/Sources/SwiftParser/TokenPrecedence.swift @@ -283,6 +283,7 @@ enum TokenPrecedence: Comparable { ._swift_native_objc_runtime_base, ._typeEraser, ._unavailableFromAsync, + .abi, .attached, .available, .backDeployed, diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift index 2e7381a55e2..c999428f365 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -45,6 +45,9 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of value generics. public static let valueGenerics = Self (rawValue: 1 << 6) + /// Whether to enable the parsing of @abi attribute. + public static let abiAttribute = Self (rawValue: 1 << 7) + /// Creates a new value representing the experimental feature with the /// given name, or returns nil if the name is not recognized. public init?(name: String) { @@ -63,6 +66,8 @@ extension Parser.ExperimentalFeatures { self = .coroutineAccessors case "ValueGenerics": self = .valueGenerics + case "ABIAttribute": + self = .abiAttribute default: return nil } diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift index 13030c5143e..f3430a21cab 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -476,6 +476,27 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { SpaceSeparatedIdentifiersError(firstToken: previousToken, additionalTokens: tokens), fixIts: fixIts ) + } else if let parent = node.parent, + node.firstToken(viewMode: .sourceAccurate)?.tokenKind == .poundIf, + let otherNode = parent.children(viewMode: .sourceAccurate).last?.as(UnexpectedNodesSyntax.self), + otherNode.lastToken(viewMode: .sourceAccurate)?.tokenKind == .poundEndif + { + let diagnoseOn = parent.parent ?? parent + addDiagnostic( + diagnoseOn, + IfConfigDeclNotAllowedInContext(context: diagnoseOn), + highlights: [Syntax(node), Syntax(otherNode)], + fixIts: [ + FixIt( + message: RemoveNodesFixIt([Syntax(node), Syntax(otherNode)]), + changes: [ + .makeMissing([Syntax(node)], transferTrivia: false), + .makeMissing([Syntax(otherNode)], transferTrivia: false), + ] + ) + ], + handledNodes: [node.id, otherNode.id] + ) } else { addDiagnostic(node, UnexpectedNodesError(unexpectedNodes: node), highlights: [Syntax(node)]) } @@ -1339,6 +1360,12 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { } public override func visit(_ node: MissingDeclSyntax) -> SyntaxVisitorContinueKind { + // If the missing decl contains another decl as unexpected syntax, diagnose that decl as syntactically invalid. + if let invalidDecl = node.unexpectedBeforeAttributes?.only?.as(DeclSyntax.self) { + addDiagnostic(node, DeclarationNotPermittedInContext(missingDecl: node, invalidDecl: invalidDecl)) + return .skipChildren + } + return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id]) } diff --git a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift index f1a21255bb5..d093f346228 100644 --- a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift @@ -306,6 +306,30 @@ public struct CannotParseVersionTuple: ParserError { } } +public struct DeclarationNotPermittedInContext: ParserError { + public var missingDecl: MissingDeclSyntax + public var invalidDecl: DeclSyntax + + public var message: String { + return "\(self.invalidDeclDescription) is not permitted \(self.missingDeclContextDescription)" + } + + var invalidDeclDescription: String { + return invalidDecl.kind.nameForDiagnostics ?? "declaration" + } + + var missingDeclContextDescription: String { + guard + let description = missingDecl.parent?.ancestorOrSelf(mapping: { ancestor in + ancestor.nodeTypeNameForDiagnostics(allowBlockNames: false) + }) + else { + return "here" + } + return "as \(description)" + } +} + public struct DeinitializerSignatureError: ParserError { public let name: TokenSyntax? public let params: FunctionParameterClauseSyntax? @@ -383,6 +407,21 @@ public struct IdentifierNotAllowedInOperatorName: ParserError { } } +public struct IfConfigDeclNotAllowedInContext: ParserError { + public let context: Syntax + + private var contextDescription: String { + if let description = context.ancestorOrSelf(mapping: { $0.nodeTypeNameForDiagnostics(allowBlockNames: true) }) { + return "in \(description)" + } + return "here" + } + + public var message: String { + return "conditional compilation not permitted \(contextDescription)" + } +} + public struct InvalidFloatLiteralMissingLeadingZero: ParserError { public let decimalDigits: TokenSyntax diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index 4006f445c54..4259c11f2e8 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -23,6 +23,8 @@ extension SyntaxKind { switch self { case .token: return "token" + case .abiAttributeArguments: + return "ABI-providing declaration" case .accessorDecl: return "accessor" case .accessorEffectSpecifiers: diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 31a6ac2cac5..75ae283f668 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -17,6 +17,12 @@ @_spi(RawSyntax) public func childName(_ keyPath: AnyKeyPath) -> String? { switch keyPath { + case \ABIAttributeArgumentsSyntax.unexpectedBeforeProvider: + return "unexpectedBeforeProvider" + case \ABIAttributeArgumentsSyntax.provider: + return "provider" + case \ABIAttributeArgumentsSyntax.unexpectedAfterProvider: + return "unexpectedAfterProvider" case \AccessorBlockSyntax.unexpectedBeforeLeftBrace: return "unexpectedBeforeLeftBrace" case \AccessorBlockSyntax.leftBrace: diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index 15f2bbd87e9..2f37908d2c5 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -71,6 +71,10 @@ public enum Keyword: UInt8, Hashable, Sendable { case _underlyingVersion case _UnknownLayout case _version + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case abi case accesses case actor case addressWithNativeOwner @@ -272,6 +276,8 @@ public enum Keyword: UInt8, Hashable, Sendable { } case 3: switch text { + case "abi": + self = .abi case "any": self = .any case "Any": @@ -874,6 +880,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "_underlyingVersion", "_UnknownLayout", "_version", + "abi", "accesses", "actor", "addressWithNativeOwner", diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index 6b4a238d8cf..dc6991cd0f5 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -56,6 +56,20 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + override open func visit(_ node: ABIAttributeArgumentsSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + override open func visitPost(_ node: ABIAttributeArgumentsSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 4341ca1bc2f..1dd414f2cd5 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -1517,6 +1517,7 @@ extension Syntax { public static var structure: SyntaxNodeStructure { return .choices([ .node(TokenSyntax.self), + .node(ABIAttributeArgumentsSyntax.self), .node(AccessorBlockSyntax.self), .node(AccessorDeclListSyntax.self), .node(AccessorDeclSyntax.self), diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index fef31916caa..439e8dca904 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -15,6 +15,10 @@ /// Enum to exhaustively switch over all different syntax nodes. public enum SyntaxEnum: Sendable { case token(TokenSyntax) + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case abiAttributeArguments(ABIAttributeArgumentsSyntax) case accessorBlock(AccessorBlockSyntax) case accessorDeclList(AccessorDeclListSyntax) case accessorDecl(AccessorDeclSyntax) @@ -321,6 +325,8 @@ extension Syntax { switch raw.kind { case .token: return .token(TokenSyntax(self)!) + case .abiAttributeArguments: + return .abiAttributeArguments(ABIAttributeArgumentsSyntax(self)!) case .accessorBlock: return .accessorBlock(AccessorBlockSyntax(self)!) case .accessorDeclList: diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index e8efa17158f..c88765ee1f6 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -15,6 +15,10 @@ /// Enumerates the known kinds of Syntax represented in the Syntax tree. public enum SyntaxKind: Sendable { case token + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case abiAttributeArguments case accessorBlock case accessorDeclList case accessorDecl @@ -446,6 +450,8 @@ public enum SyntaxKind: Sendable { switch self { case .token: return TokenSyntax.self + case .abiAttributeArguments: + return ABIAttributeArgumentsSyntax.self case .accessorBlock: return AccessorBlockSyntax.self case .accessorDeclList: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index ade68592503..69a556a6e42 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -106,6 +106,16 @@ open class SyntaxRewriter { return rewritten.cast(T.self) } + /// Visit a `ABIAttributeArgumentsSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: ABIAttributeArgumentsSyntax) -> ABIAttributeArgumentsSyntax { + return visitChildren(node._syntaxNode).cast(ABIAttributeArgumentsSyntax.self) + } + /// Visit a ``AccessorBlockSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -2191,6 +2201,10 @@ open class SyntaxRewriter { return { self.visitImpl(&$0, TokenSyntax.self, self.visit) } + case .abiAttributeArguments: + return { + self.visitImpl(&$0, ABIAttributeArgumentsSyntax.self, self.visit) + } case .accessorBlock: return { self.visitImpl(&$0, AccessorBlockSyntax.self, self.visit) @@ -3333,6 +3347,8 @@ open class SyntaxRewriter { switch node.raw.kind { case .token: return visitImpl(&node, TokenSyntax.self, visit) + case .abiAttributeArguments: + return visitImpl(&node, ABIAttributeArgumentsSyntax.self, visit) case .accessorBlock: return visitImpl(&node, AccessorBlockSyntax.self, visit) case .accessorDeclList: diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index 67092f4ac60..f16ef3bd16a 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -38,6 +38,24 @@ open class SyntaxVisitor { visit(&syntaxNode) } + /// Visiting `ABIAttributeArgumentsSyntax` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: ABIAttributeArgumentsSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting `ABIAttributeArgumentsSyntax` and its descendants. + /// - node: the node we just finished visiting. + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visitPost(_ node: ABIAttributeArgumentsSyntax) { + } + /// Visiting ``AccessorBlockSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -3535,6 +3553,10 @@ open class SyntaxVisitor { // No children to visit. self.visitPost(node) } + case .abiAttributeArguments: + return { + self.visitImpl(&$0, ABIAttributeArgumentsSyntax.self, self.visit, self.visitPost) + } case .accessorBlock: return { self.visitImpl(&$0, AccessorBlockSyntax.self, self.visit, self.visitPost) @@ -4681,6 +4703,8 @@ open class SyntaxVisitor { _ = visit(node) // No children to visit. visitPost(node) + case .abiAttributeArguments: + visitImpl(&node, ABIAttributeArgumentsSyntax.self, visit, visitPost) case .accessorBlock: visitImpl(&node, AccessorBlockSyntax.self, visit, visitPost) case .accessorDeclList: diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift index 33b11426d6c..e25bab64ee6 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift @@ -12,6 +12,130 @@ // //===----------------------------------------------------------------------===// +#if compiler(>=5.8) +@_spi(ExperimentalLanguageFeatures) +#endif +@_spi(RawSyntax) +public struct RawABIAttributeArgumentsSyntax: RawSyntaxNodeProtocol { + public enum Provider: RawSyntaxNodeProtocol { + case associatedType(RawAssociatedTypeDeclSyntax) + case deinitializer(RawDeinitializerDeclSyntax) + case enumCase(RawEnumCaseDeclSyntax) + case function(RawFunctionDeclSyntax) + case initializer(RawInitializerDeclSyntax) + case missing(RawMissingDeclSyntax) + case `subscript`(RawSubscriptDeclSyntax) + case typeAlias(RawTypeAliasDeclSyntax) + case variable(RawVariableDeclSyntax) + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + RawAssociatedTypeDeclSyntax.isKindOf(raw) || RawDeinitializerDeclSyntax.isKindOf(raw) || RawEnumCaseDeclSyntax.isKindOf(raw) || RawFunctionDeclSyntax.isKindOf(raw) || RawInitializerDeclSyntax.isKindOf(raw) || RawMissingDeclSyntax.isKindOf(raw) || RawSubscriptDeclSyntax.isKindOf(raw) || RawTypeAliasDeclSyntax.isKindOf(raw) || RawVariableDeclSyntax.isKindOf(raw) + } + + public var raw: RawSyntax { + switch self { + case .associatedType(let node): + return node.raw + case .deinitializer(let node): + return node.raw + case .enumCase(let node): + return node.raw + case .function(let node): + return node.raw + case .initializer(let node): + return node.raw + case .missing(let node): + return node.raw + case .subscript(let node): + return node.raw + case .typeAlias(let node): + return node.raw + case .variable(let node): + return node.raw + } + } + + public init?(_ node: __shared some RawSyntaxNodeProtocol) { + if let node = node.as(RawAssociatedTypeDeclSyntax.self) { + self = .associatedType(node) + } else if let node = node.as(RawDeinitializerDeclSyntax.self) { + self = .deinitializer(node) + } else if let node = node.as(RawEnumCaseDeclSyntax.self) { + self = .enumCase(node) + } else if let node = node.as(RawFunctionDeclSyntax.self) { + self = .function(node) + } else if let node = node.as(RawInitializerDeclSyntax.self) { + self = .initializer(node) + } else if let node = node.as(RawMissingDeclSyntax.self) { + self = .missing(node) + } else if let node = node.as(RawSubscriptDeclSyntax.self) { + self = .subscript(node) + } else if let node = node.as(RawTypeAliasDeclSyntax.self) { + self = .typeAlias(node) + } else if let node = node.as(RawVariableDeclSyntax.self) { + self = .variable(node) + } else { + return nil + } + } + } + + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .abiAttributeArguments + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeProvider: RawUnexpectedNodesSyntax? = nil, + provider: Provider, + _ unexpectedAfterProvider: RawUnexpectedNodesSyntax? = nil, + arena: __shared SyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .abiAttributeArguments, uninitializedCount: 3, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeProvider?.raw + layout[1] = provider.raw + layout[2] = unexpectedAfterProvider?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeProvider: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var provider: RawSyntax { + layoutView.children[1]! + } + + public var unexpectedAfterProvider: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawAccessorBlockSyntax: RawSyntaxNodeProtocol { public enum Accessors: RawSyntaxNodeProtocol { @@ -1322,9 +1446,12 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { case unavailableFromAsyncArguments(RawUnavailableFromAsyncAttributeArgumentsSyntax) case effectsArguments(RawEffectsAttributeArgumentListSyntax) case documentationArguments(RawDocumentationAttributeArgumentListSyntax) + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + case abiArguments(RawABIAttributeArgumentsSyntax) public static func isKindOf(_ raw: RawSyntax) -> Bool { - RawLabeledExprListSyntax.isKindOf(raw) || RawTokenSyntax.isKindOf(raw) || RawStringLiteralExprSyntax.isKindOf(raw) || RawAvailabilityArgumentListSyntax.isKindOf(raw) || RawSpecializeAttributeArgumentListSyntax.isKindOf(raw) || RawObjCSelectorPieceListSyntax.isKindOf(raw) || RawImplementsAttributeArgumentsSyntax.isKindOf(raw) || RawDifferentiableAttributeArgumentsSyntax.isKindOf(raw) || RawDerivativeAttributeArgumentsSyntax.isKindOf(raw) || RawBackDeployedAttributeArgumentsSyntax.isKindOf(raw) || RawConventionAttributeArgumentsSyntax.isKindOf(raw) || RawConventionWitnessMethodAttributeArgumentsSyntax.isKindOf(raw) || RawOpaqueReturnTypeOfAttributeArgumentsSyntax.isKindOf(raw) || RawExposeAttributeArgumentsSyntax.isKindOf(raw) || RawOriginallyDefinedInAttributeArgumentsSyntax.isKindOf(raw) || RawUnderscorePrivateAttributeArgumentsSyntax.isKindOf(raw) || RawDynamicReplacementAttributeArgumentsSyntax.isKindOf(raw) || RawUnavailableFromAsyncAttributeArgumentsSyntax.isKindOf(raw) || RawEffectsAttributeArgumentListSyntax.isKindOf(raw) || RawDocumentationAttributeArgumentListSyntax.isKindOf(raw) + RawLabeledExprListSyntax.isKindOf(raw) || RawTokenSyntax.isKindOf(raw) || RawStringLiteralExprSyntax.isKindOf(raw) || RawAvailabilityArgumentListSyntax.isKindOf(raw) || RawSpecializeAttributeArgumentListSyntax.isKindOf(raw) || RawObjCSelectorPieceListSyntax.isKindOf(raw) || RawImplementsAttributeArgumentsSyntax.isKindOf(raw) || RawDifferentiableAttributeArgumentsSyntax.isKindOf(raw) || RawDerivativeAttributeArgumentsSyntax.isKindOf(raw) || RawBackDeployedAttributeArgumentsSyntax.isKindOf(raw) || RawConventionAttributeArgumentsSyntax.isKindOf(raw) || RawConventionWitnessMethodAttributeArgumentsSyntax.isKindOf(raw) || RawOpaqueReturnTypeOfAttributeArgumentsSyntax.isKindOf(raw) || RawExposeAttributeArgumentsSyntax.isKindOf(raw) || RawOriginallyDefinedInAttributeArgumentsSyntax.isKindOf(raw) || RawUnderscorePrivateAttributeArgumentsSyntax.isKindOf(raw) || RawDynamicReplacementAttributeArgumentsSyntax.isKindOf(raw) || RawUnavailableFromAsyncAttributeArgumentsSyntax.isKindOf(raw) || RawEffectsAttributeArgumentListSyntax.isKindOf(raw) || RawDocumentationAttributeArgumentListSyntax.isKindOf(raw) || RawABIAttributeArgumentsSyntax.isKindOf(raw) } public var raw: RawSyntax { @@ -1369,6 +1496,8 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { return node.raw case .documentationArguments(let node): return node.raw + case .abiArguments(let node): + return node.raw } } @@ -1413,6 +1542,8 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { self = .effectsArguments(node) } else if let node = node.as(RawDocumentationAttributeArgumentListSyntax.self) { self = .documentationArguments(node) + } else if let node = node.as(RawABIAttributeArgumentsSyntax.self) { + self = .abiArguments(node) } else { return nil } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 5e8dc7597b2..33f5a12838e 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -209,6 +209,12 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { switch kind { case .token: assertionFailure("validateLayout for .token kind is not supported") + case .abiAttributeArguments: + assert(layout.count == 3) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertAnyHasNoError(kind, 1, [ + verify(layout[1], as: RawSyntax.self)]) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) case .accessorBlock: assert(layout.count == 7) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift index bc2b9c74921..70af586989d 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift @@ -12,6 +12,394 @@ // //===----------------------------------------------------------------------===// +// MARK: - ABIAttributeArgumentsSyntax + +/// The arguments of the '@abi' attribute +/// +/// - Note: Requires experimental feature `abiAttribute`. +/// +/// ### Children +/// +/// - `provider`: (``AssociatedTypeDeclSyntax`` | ``DeinitializerDeclSyntax`` | ``EnumCaseDeclSyntax`` | ``FunctionDeclSyntax`` | ``InitializerDeclSyntax`` | ``MissingDeclSyntax`` | ``SubscriptDeclSyntax`` | ``TypeAliasDeclSyntax`` | ``VariableDeclSyntax``) +/// +/// ### Contained in +/// +/// - ``AttributeSyntax``.``AttributeSyntax/arguments`` +#if compiler(>=5.8) +@_spi(ExperimentalLanguageFeatures) +#endif +public struct ABIAttributeArgumentsSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public enum Provider: SyntaxChildChoices, SyntaxHashable { + case associatedType(AssociatedTypeDeclSyntax) + case deinitializer(DeinitializerDeclSyntax) + case enumCase(EnumCaseDeclSyntax) + case function(FunctionDeclSyntax) + case initializer(InitializerDeclSyntax) + case missing(MissingDeclSyntax) + case `subscript`(SubscriptDeclSyntax) + case typeAlias(TypeAliasDeclSyntax) + case variable(VariableDeclSyntax) + + public var _syntaxNode: Syntax { + switch self { + case .associatedType(let node): + return node._syntaxNode + case .deinitializer(let node): + return node._syntaxNode + case .enumCase(let node): + return node._syntaxNode + case .function(let node): + return node._syntaxNode + case .initializer(let node): + return node._syntaxNode + case .missing(let node): + return node._syntaxNode + case .subscript(let node): + return node._syntaxNode + case .typeAlias(let node): + return node._syntaxNode + case .variable(let node): + return node._syntaxNode + } + } + + public init(_ node: AssociatedTypeDeclSyntax) { + self = .associatedType(node) + } + + public init(_ node: DeinitializerDeclSyntax) { + self = .deinitializer(node) + } + + public init(_ node: EnumCaseDeclSyntax) { + self = .enumCase(node) + } + + public init(_ node: FunctionDeclSyntax) { + self = .function(node) + } + + public init(_ node: InitializerDeclSyntax) { + self = .initializer(node) + } + + public init(_ node: MissingDeclSyntax) { + self = .missing(node) + } + + public init(_ node: SubscriptDeclSyntax) { + self = .subscript(node) + } + + public init(_ node: TypeAliasDeclSyntax) { + self = .typeAlias(node) + } + + public init(_ node: VariableDeclSyntax) { + self = .variable(node) + } + + public init?(_ node: __shared some SyntaxProtocol) { + if let node = node.as(AssociatedTypeDeclSyntax.self) { + self = .associatedType(node) + } else if let node = node.as(DeinitializerDeclSyntax.self) { + self = .deinitializer(node) + } else if let node = node.as(EnumCaseDeclSyntax.self) { + self = .enumCase(node) + } else if let node = node.as(FunctionDeclSyntax.self) { + self = .function(node) + } else if let node = node.as(InitializerDeclSyntax.self) { + self = .initializer(node) + } else if let node = node.as(MissingDeclSyntax.self) { + self = .missing(node) + } else if let node = node.as(SubscriptDeclSyntax.self) { + self = .subscript(node) + } else if let node = node.as(TypeAliasDeclSyntax.self) { + self = .typeAlias(node) + } else if let node = node.as(VariableDeclSyntax.self) { + self = .variable(node) + } else { + return nil + } + } + + public static var structure: SyntaxNodeStructure { + return .choices([ + .node(AssociatedTypeDeclSyntax.self), + .node(DeinitializerDeclSyntax.self), + .node(EnumCaseDeclSyntax.self), + .node(FunctionDeclSyntax.self), + .node(InitializerDeclSyntax.self), + .node(MissingDeclSyntax.self), + .node(SubscriptDeclSyntax.self), + .node(TypeAliasDeclSyntax.self), + .node(VariableDeclSyntax.self) + ]) + } + + /// Checks if the current syntax node can be cast to ``AssociatedTypeDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: AssociatedTypeDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``AssociatedTypeDeclSyntax``. + /// + /// - Returns: An instance of ``AssociatedTypeDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: AssociatedTypeDeclSyntax.Type) -> AssociatedTypeDeclSyntax? { + return AssociatedTypeDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``AssociatedTypeDeclSyntax``. + /// + /// - Returns: An instance of ``AssociatedTypeDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: AssociatedTypeDeclSyntax.Type) -> AssociatedTypeDeclSyntax { + return self.as(AssociatedTypeDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``DeinitializerDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: DeinitializerDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``DeinitializerDeclSyntax``. + /// + /// - Returns: An instance of ``DeinitializerDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: DeinitializerDeclSyntax.Type) -> DeinitializerDeclSyntax? { + return DeinitializerDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``DeinitializerDeclSyntax``. + /// + /// - Returns: An instance of ``DeinitializerDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: DeinitializerDeclSyntax.Type) -> DeinitializerDeclSyntax { + return self.as(DeinitializerDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``EnumCaseDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: EnumCaseDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``EnumCaseDeclSyntax``. + /// + /// - Returns: An instance of ``EnumCaseDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: EnumCaseDeclSyntax.Type) -> EnumCaseDeclSyntax? { + return EnumCaseDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``EnumCaseDeclSyntax``. + /// + /// - Returns: An instance of ``EnumCaseDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: EnumCaseDeclSyntax.Type) -> EnumCaseDeclSyntax { + return self.as(EnumCaseDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``FunctionDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: FunctionDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``FunctionDeclSyntax``. + /// + /// - Returns: An instance of ``FunctionDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: FunctionDeclSyntax.Type) -> FunctionDeclSyntax? { + return FunctionDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``FunctionDeclSyntax``. + /// + /// - Returns: An instance of ``FunctionDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: FunctionDeclSyntax.Type) -> FunctionDeclSyntax { + return self.as(FunctionDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``InitializerDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: InitializerDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``InitializerDeclSyntax``. + /// + /// - Returns: An instance of ``InitializerDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: InitializerDeclSyntax.Type) -> InitializerDeclSyntax? { + return InitializerDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``InitializerDeclSyntax``. + /// + /// - Returns: An instance of ``InitializerDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: InitializerDeclSyntax.Type) -> InitializerDeclSyntax { + return self.as(InitializerDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``MissingDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: MissingDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``MissingDeclSyntax``. + /// + /// - Returns: An instance of ``MissingDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: MissingDeclSyntax.Type) -> MissingDeclSyntax? { + return MissingDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``MissingDeclSyntax``. + /// + /// - Returns: An instance of ``MissingDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: MissingDeclSyntax.Type) -> MissingDeclSyntax { + return self.as(MissingDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``SubscriptDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: SubscriptDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``SubscriptDeclSyntax``. + /// + /// - Returns: An instance of ``SubscriptDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: SubscriptDeclSyntax.Type) -> SubscriptDeclSyntax? { + return SubscriptDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``SubscriptDeclSyntax``. + /// + /// - Returns: An instance of ``SubscriptDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: SubscriptDeclSyntax.Type) -> SubscriptDeclSyntax { + return self.as(SubscriptDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``TypeAliasDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: TypeAliasDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``TypeAliasDeclSyntax``. + /// + /// - Returns: An instance of ``TypeAliasDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: TypeAliasDeclSyntax.Type) -> TypeAliasDeclSyntax? { + return TypeAliasDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``TypeAliasDeclSyntax``. + /// + /// - Returns: An instance of ``TypeAliasDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: TypeAliasDeclSyntax.Type) -> TypeAliasDeclSyntax { + return self.as(TypeAliasDeclSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``VariableDeclSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: VariableDeclSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``VariableDeclSyntax``. + /// + /// - Returns: An instance of ``VariableDeclSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: VariableDeclSyntax.Type) -> VariableDeclSyntax? { + return VariableDeclSyntax.init(self) + } + + /// Force-casts the current syntax node to ``VariableDeclSyntax``. + /// + /// - Returns: An instance of ``VariableDeclSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: VariableDeclSyntax.Type) -> VariableDeclSyntax { + return self.as(VariableDeclSyntax.self)! + } + } + + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .abiAttributeArguments else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeProvider: UnexpectedNodesSyntax? = nil, + provider: Provider, + _ unexpectedAfterProvider: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((SyntaxArena(), (unexpectedBeforeProvider, provider, unexpectedAfterProvider))) { (arena, _) in + let layout: [RawSyntax?] = [unexpectedBeforeProvider?.raw, provider.raw, unexpectedAfterProvider?.raw] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.abiAttributeArguments, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeProvider: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), arena: SyntaxArena()).cast(ABIAttributeArgumentsSyntax.self) + } + } + + public var provider: Provider { + get { + return Syntax(self).child(at: 1)!.cast(Provider.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), arena: SyntaxArena()).cast(ABIAttributeArgumentsSyntax.self) + } + } + + public var unexpectedAfterProvider: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), arena: SyntaxArena()).cast(ABIAttributeArgumentsSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([\Self.unexpectedBeforeProvider, \Self.provider, \Self.unexpectedAfterProvider]) +} + // MARK: - AccessorBlockSyntax /// ### Children @@ -2291,7 +2679,7 @@ public struct AssociatedTypeDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea /// - `atSign`: `@` /// - `attributeName`: ``TypeSyntax`` /// - `leftParen`: `(`? -/// - `arguments`: (``LabeledExprListSyntax`` | ``TokenSyntax`` | ``StringLiteralExprSyntax`` | ``AvailabilityArgumentListSyntax`` | ``SpecializeAttributeArgumentListSyntax`` | ``ObjCSelectorPieceListSyntax`` | ``ImplementsAttributeArgumentsSyntax`` | ``DifferentiableAttributeArgumentsSyntax`` | ``DerivativeAttributeArgumentsSyntax`` | ``BackDeployedAttributeArgumentsSyntax`` | ``ConventionAttributeArgumentsSyntax`` | ``ConventionWitnessMethodAttributeArgumentsSyntax`` | ``OpaqueReturnTypeOfAttributeArgumentsSyntax`` | ``ExposeAttributeArgumentsSyntax`` | ``OriginallyDefinedInAttributeArgumentsSyntax`` | ``UnderscorePrivateAttributeArgumentsSyntax`` | ``DynamicReplacementAttributeArgumentsSyntax`` | ``UnavailableFromAsyncAttributeArgumentsSyntax`` | ``EffectsAttributeArgumentListSyntax`` | ``DocumentationAttributeArgumentListSyntax``)? +/// - `arguments`: (``LabeledExprListSyntax`` | ``TokenSyntax`` | ``StringLiteralExprSyntax`` | ``AvailabilityArgumentListSyntax`` | ``SpecializeAttributeArgumentListSyntax`` | ``ObjCSelectorPieceListSyntax`` | ``ImplementsAttributeArgumentsSyntax`` | ``DifferentiableAttributeArgumentsSyntax`` | ``DerivativeAttributeArgumentsSyntax`` | ``BackDeployedAttributeArgumentsSyntax`` | ``ConventionAttributeArgumentsSyntax`` | ``ConventionWitnessMethodAttributeArgumentsSyntax`` | ``OpaqueReturnTypeOfAttributeArgumentsSyntax`` | ``ExposeAttributeArgumentsSyntax`` | ``OriginallyDefinedInAttributeArgumentsSyntax`` | ``UnderscorePrivateAttributeArgumentsSyntax`` | ``DynamicReplacementAttributeArgumentsSyntax`` | ``UnavailableFromAsyncAttributeArgumentsSyntax`` | ``EffectsAttributeArgumentListSyntax`` | ``DocumentationAttributeArgumentListSyntax`` | `ABIAttributeArgumentsSyntax`)? /// - `rightParen`: `)`? /// /// ### Contained in @@ -2320,6 +2708,9 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr case unavailableFromAsyncArguments(UnavailableFromAsyncAttributeArgumentsSyntax) case effectsArguments(EffectsAttributeArgumentListSyntax) case documentationArguments(DocumentationAttributeArgumentListSyntax) + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + case abiArguments(ABIAttributeArgumentsSyntax) public var _syntaxNode: Syntax { switch self { @@ -2363,6 +2754,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr return node._syntaxNode case .documentationArguments(let node): return node._syntaxNode + case .abiArguments(let node): + return node._syntaxNode } } @@ -2446,6 +2839,12 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr self = .documentationArguments(node) } + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + public init(_ node: ABIAttributeArgumentsSyntax) { + self = .abiArguments(node) + } + public init?(_ node: __shared some SyntaxProtocol) { if let node = node.as(LabeledExprListSyntax.self) { self = .argumentList(node) @@ -2487,6 +2886,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr self = .effectsArguments(node) } else if let node = node.as(DocumentationAttributeArgumentListSyntax.self) { self = .documentationArguments(node) + } else if let node = node.as(ABIAttributeArgumentsSyntax.self) { + self = .abiArguments(node) } else { return nil } @@ -2513,7 +2914,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr .node(DynamicReplacementAttributeArgumentsSyntax.self), .node(UnavailableFromAsyncAttributeArgumentsSyntax.self), .node(EffectsAttributeArgumentListSyntax.self), - .node(DocumentationAttributeArgumentListSyntax.self) + .node(DocumentationAttributeArgumentListSyntax.self), + .node(ABIAttributeArgumentsSyntax.self) ]) } @@ -2956,6 +3358,34 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr public func cast(_ syntaxType: DocumentationAttributeArgumentListSyntax.Type) -> DocumentationAttributeArgumentListSyntax { return self.as(DocumentationAttributeArgumentListSyntax.self)! } + + /// Checks if the current syntax node can be cast to `ABIAttributeArgumentsSyntax`. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + public func `is`(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to `ABIAttributeArgumentsSyntax`. + /// + /// - Returns: An instance of `ABIAttributeArgumentsSyntax`, or `nil` if the cast fails. + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + public func `as`(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> ABIAttributeArgumentsSyntax? { + return ABIAttributeArgumentsSyntax.init(self) + } + + /// Force-casts the current syntax node to `ABIAttributeArgumentsSyntax`. + /// + /// - Returns: An instance of `ABIAttributeArgumentsSyntax`. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + /// - Note: Requires experimental feature `abiAttribute`. + @_spi(ExperimentalLanguageFeatures) + public func cast(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> ABIAttributeArgumentsSyntax { + return self.as(ABIAttributeArgumentsSyntax.self)! + } } public let _syntaxNode: Syntax diff --git a/Tests/SwiftParserTest/AttributeTests.swift b/Tests/SwiftParserTest/AttributeTests.swift index 31304050ab7..7df9983e7b9 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -@_spi(RawSyntax) import SwiftParser -@_spi(RawSyntax) import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftParser +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax import XCTest final class AttributeTests: ParserTestCase { @@ -965,6 +965,370 @@ final class AttributeTests: ParserTestCase { ) } + func testABIAttribute() { + func abiAttr(_ provider: ABIAttributeArgumentsSyntax.Provider) -> AttributeListSyntax.Element { + return .attribute( + AttributeSyntax( + attributeName: TypeSyntax("abi"), + leftParen: .leftParenToken(), + arguments: .abiArguments( + ABIAttributeArgumentsSyntax( + provider: provider + ) + ), + rightParen: .rightParenToken() + ) + ) + } + + assertParse( + """ + @abi(func fn() -> Int) + func fn1() -> Int { } + """, + substructure: FunctionDeclSyntax( + attributes: [ + abiAttr( + .function( + FunctionDeclSyntax( + name: "fn", + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax {}, + returnClause: ReturnClauseSyntax(type: TypeSyntax("Int")) + ), + body: nil + ) + ) + ) + ], + name: "fn1", + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax {}, + returnClause: ReturnClauseSyntax(type: TypeSyntax("Int")) + ) + ) {}, + experimentalFeatures: [.abiAttribute] + ) + + assertParse( + """ + @abi(associatedtype AssocTy) + associatedtype AssocTy + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(deinit) + deinit {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + enum EnumCaseDeclNotParsedAtTopLevel { + @abi(case someCase) + case someCase + } + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(func fn()) + func fn() + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(init()) + init() {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(subscript(i: Int) -> Element) + subscript(i: Int) -> Element {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(typealias Typealias = @escaping () -> Void) + typealias Typealias = () -> Void + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(let c1, c2) + let c1, c2 + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(var v1, v2) + var v1, v2 + """, + experimentalFeatures: [.abiAttribute] + ) + + assertParse( + """ + @abi(1️⃣<#fnord#>) + func placeholder() {} + """, + substructure: abiAttr( + .missing( + MissingDeclSyntax(placeholder: .identifier("<#fnord#>")) + ) + ), + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file") + ], + experimentalFeatures: [.abiAttribute] + ) + + assertParse( + """ + @abi(1️⃣import Fnord) + func invalidDecl() {} + """, + substructure: abiAttr( + .missing( + MissingDeclSyntax( + [ + Syntax( + ImportDeclSyntax( + path: [ImportPathComponentSyntax(name: "Fnord")] + ) + ) + ], + placeholder: .identifier("<#declaration#>", presence: .missing) + ) + ) + ), + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "import is not permitted as ABI-providing declaration") + ], + experimentalFeatures: [.abiAttribute] + ) + + // + // Invalid, but diagnosed in the compiler + // + + assertParse( + """ + @abi(associatedtype AssocTy = T) + associatedtype AssocTy + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(deinit {}) + deinit {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + enum EnumCaseDeclNotParsedAtTopLevel { + @abi(case someCase = 42) + case someCase + } + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(func fn() {}) + func fn() + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(init() {}) + init() {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(subscript(i: Int) -> Element { get {} set {} }) + subscript(i: Int) -> Element {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(let c1 = 1, c2 = 2) + let c1, c2 + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(var v1 = 1, v2 = 2) + var v1, v2 + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(var v3 { get {} set {} }) + var v3 + """, + experimentalFeatures: [.abiAttribute] + ) + + // + // Invalid and diagnosed here + // + + assertParse( + """ + @abi(var 1️⃣) + var v1 + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected pattern in variable", + fixIts: ["insert pattern"] + ) + ], + fixedSource: """ + @abi(var <#pattern#>) + var v1 + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi3️⃣(var v22️⃣ + var v2 + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected ')' to end attribute", + notes: [ + NoteSpec( + locationMarker: "3️⃣", + message: "to match this opening '('" + ) + ], + fixIts: ["insert ')'"] + ) + ], + fixedSource: """ + @abi(var v2) + var v2 + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi(4️⃣) + func fn2() {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected argument for '@abi' attribute", + fixIts: ["insert attribute argument"] + ) + ], + // It'd be better to have a custom fix-it with a copy of the declaration it's attached to. + fixedSource: """ + @abi(<#declaration#>) + func fn2() {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi5️⃣ + func fn3() {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "5️⃣", + message: "expected '(', ABI-providing declaration, and ')' in attribute", + fixIts: ["insert '(', ABI-providing declaration, and ')'"] + ) + ], + fixedSource: """ + @abi(<#declaration#>) + func fn3() {} + """, + experimentalFeatures: [.abiAttribute] + ) + assertParse( + """ + @abi6️⃣ func fn4_abi()7️⃣) + func fn4() {} + """, + diagnostics: [ + // Suboptimal diagnostics, but we'd need to be able to scan past an entire decl in lookahead to do better. + DiagnosticSpec( + locationMarker: "6️⃣", + message: "expected '(', ABI-providing declaration, and ')' in attribute", + fixIts: ["insert '(', ABI-providing declaration, and ')'"] + ), + DiagnosticSpec( + locationMarker: "7️⃣", + message: "unexpected code ')' before function" + ), + ], + fixedSource: """ + @abi(<#declaration#>) func fn4_abi()) + func fn4() {} + """, + experimentalFeatures: [.abiAttribute] + ) + + // `#if` is banned inside an `@abi` attribute. + // The code that generates feature checks in module interfaces could easily fail in ways that would generate this + // syntax, so we want to make sure we give a good diagnostic. + + assertParse( + """ + @abi( + 1️⃣#if $TypedThrows + func _fn() throws(E) + #endif + ) + func fn() throws(E) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "conditional compilation not permitted in ABI-providing declaration", + highlight: """ + + #if $TypedThrows + #endif + """, + fixIts: ["remove '#if $TypedThrows' and '#endif'"] + ) + ], + fixedSource: """ + @abi( + func _fn() throws(E) + ) + func fn() throws(E) {} + """, + experimentalFeatures: [.abiAttribute] + ) + } + func testSpaceBetweenAtAndAttribute() { assertParse( "@1️⃣ custom func foo() {}",