diff --git a/Package.resolved b/Package.resolved index b787898..88a205f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,24 +1,24 @@ { - "originHash" : "9a8b2e43912bb723fe640cc01f9bd94e48c9cf3a774b73885923fc042ba4c6cb", + "originHash" : "391d32c204e1b963294b50309bc17bbef49d82f2a2e9b159d8beda0f358fb6af", "pins" : [ { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" + "revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b", + "version" : "1.7.1" } }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", + "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "fa8f95c2d536d6620cc2f504ebe8a6167c9fc2dd", - "version" : "510.0.1" + "revision" : "9de99a78f099e59caf2b2beec65a4c45d54b2081", + "version" : "603.0.1" } } ], "version" : 3 -} \ No newline at end of file +} diff --git a/Package.swift b/Package.swift index 82cf736..6908ddf 100644 --- a/Package.swift +++ b/Package.swift @@ -11,8 +11,8 @@ let package = Package( .library(name: "SwiftAstGenLib", targets: ["SwiftAstGenLib"]) ], dependencies: [ - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"), - .package(url: "https://github.com/apple/swift-syntax", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.1"), + .package(url: "https://github.com/swiftlang/swift-syntax", from: "603.0.1"), ], targets: [ .target( diff --git a/Sources/CodeGeneration/SyntaxSupport/AttributeNodes.swift b/Sources/CodeGeneration/SyntaxSupport/AttributeNodes.swift index 9cdec68..cc561d2 100644 --- a/Sources/CodeGeneration/SyntaxSupport/AttributeNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/AttributeNodes.swift @@ -41,7 +41,6 @@ public let ATTRIBUTE_NODES: [Node] = [ children: [ Child( name: "atSign", - deprecatedName: "atSignToken", kind: .token(choices: [.token(.atSign)]), documentation: "The `@` sign." ), @@ -59,88 +58,77 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "argument", kind: .nodeChoices(choices: [ Child( name: "argumentList", + // Regular function-like attribute arguments. kind: .node(kind: .labeledExprList) ), - Child( - name: "token", - kind: .node(kind: .token) - ), - Child( - name: "string", - kind: .node(kind: .stringLiteralExpr) - ), Child( name: "availability", + // Special arguments for platform versions. kind: .node(kind: .availabilityArgumentList) ), Child( name: "specializeArguments", + // Special arguments for keyword decl name e.g. 'subscript(_:)', and availability arguments. kind: .node(kind: .specializeAttributeArgumentList) ), + Child( + name: "specializedArguments", + // Special arguments for generic where clause. + kind: .node(kind: .specializedAttributeArgument) + ), Child( name: "objCName", + // Special arguments for Objective-C names. e.g. 'methodNameWithArg1:Arg2:' kind: .node(kind: .objCSelectorPieceList) ), Child( name: "implementsArguments", + // Special arguments for keyword decl name e.g. 'subscript(_:)'. kind: .node(kind: .implementsAttributeArguments) ), Child( name: "differentiableArguments", + // Special arguments for 'where' clause. kind: .node(kind: .differentiableAttributeArguments) ), Child( name: "derivativeRegistrationArguments", + // Special arguments for 'where' clause. kind: .node(kind: .derivativeAttributeArguments) ), Child( name: "backDeployedArguments", + // Special arguments for platform versions. kind: .node(kind: .backDeployedAttributeArguments) ), - Child( - name: "conventionArguments", - kind: .node(kind: .conventionAttributeArguments) - ), - Child( - name: "conventionWitnessMethodArguments", - kind: .node(kind: .conventionWitnessMethodAttributeArguments) - ), - Child( - name: "opaqueReturnTypeOfAttributeArguments", - kind: .node(kind: .opaqueReturnTypeOfAttributeArguments) - ), - Child( - name: "exposeAttributeArguments", - kind: .node(kind: .exposeAttributeArguments) - ), Child( name: "originallyDefinedInArguments", + // Special arguments for platform versions. kind: .node(kind: .originallyDefinedInAttributeArguments) ), - Child( - name: "underscorePrivateAttributeArguments", - kind: .node(kind: .underscorePrivateAttributeArguments) - ), Child( name: "dynamicReplacementArguments", + // Special arguments for keyword decl name e.g. 'subscript(_:)'. kind: .node(kind: .dynamicReplacementAttributeArguments) ), - Child( - name: "unavailableFromAsyncArguments", - kind: .node(kind: .unavailableFromAsyncAttributeArguments) - ), Child( name: "effectsArguments", + // Special arguments for arbitrary token list, processed in SIL. kind: .node(kind: .effectsAttributeArgumentList) ), Child( name: "documentationArguments", + // Special arguments for access-level keywords. E.g. 'private'. kind: .node(kind: .documentationAttributeArgumentList) ), + Child( + name: "abiArguments", + // Special arguments for declaration syntax. e.g. @abi(func abiName() -> Int) + kind: .node(kind: .abiAttributeArguments) + ), ]), documentation: """ The arguments of the attribute. @@ -156,6 +144,12 @@ public let ATTRIBUTE_NODES: [Node] = [ documentation: "If the attribute takes arguments, the closing parenthesis.", isOptional: true ), + ], + childHistory: [ + [ + "atSign": .renamed(from: "atSignToken"), + "arguments": .renamed(from: "argument"), + ] ] ), @@ -167,7 +161,6 @@ public let ATTRIBUTE_NODES: [Node] = [ children: [ Child( name: "availabilityLabel", - deprecatedName: "label", kind: .token(choices: [.keyword(.availability)]), nameForDiagnostics: "label", documentation: "The label of the argument" @@ -179,13 +172,22 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "availabilityArguments", - deprecatedName: "availabilityList", - kind: .collection(kind: .availabilityArgumentList, collectionElementName: "AvailabilityArgument", deprecatedCollectionElementName: "Availability") + kind: .collection( + kind: .availabilityArgumentList, + collectionElementName: "AvailabilityArgument", + deprecatedCollectionElementName: "Availability" + ) ), Child( name: "semicolon", kind: .token(choices: [.token(.semicolon)]) ), + ], + childHistory: [ + [ + "availabilityLabel": .renamed(from: "label"), + "availabilityArguments": .renamed(from: "availabilityList"), + ] ] ), @@ -198,8 +200,8 @@ public let ATTRIBUTE_NODES: [Node] = [ children: [ Child( name: "platformVersion", - deprecatedName: "availabilityVersionRestriction", - kind: .node(kind: .platformVersion) + kind: .node(kind: .platformVersion), + documentation: "The platform/version pair, e.g. `iOS 10.1`" ), Child( name: "trailingComma", @@ -207,6 +209,11 @@ public let ATTRIBUTE_NODES: [Node] = [ documentation: "A trailing comma if the argument is followed by another argument", isOptional: true ), + ], + childHistory: [ + [ + "platformVersion": .renamed(from: "availabilityVersionRestriction") + ] ] ), @@ -235,65 +242,41 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "platforms", - deprecatedName: "versionList", - kind: .collection(kind: .platformVersionItemList, collectionElementName: "Platform", deprecatedCollectionElementName: "Availability"), + kind: .collection( + kind: .platformVersionItemList, + collectionElementName: "Platform", + deprecatedCollectionElementName: "Availability" + ), documentation: "The list of OS versions in which the declaration became ABI stable." ), + ], + childHistory: [ + [ + "platforms": .renamed(from: "versionList") + ] ] ), Node( - kind: .conventionAttributeArguments, - base: .syntax, - nameForDiagnostics: "@convention(...) arguments", - documentation: "The arguments for the '@convention(...)'.", - children: [ - Child( - name: "conventionLabel", - kind: .token(choices: [.token(.identifier)]), - documentation: "The convention label." - ), - Child( - name: "comma", - kind: .token(choices: [.token(.comma)]), - isOptional: true - ), - Child( - name: "cTypeLabel", - kind: .token(choices: [.keyword(.cType)]), - isOptional: true - ), - Child( - name: "colon", - kind: .token(choices: [.token(.colon)]), - isOptional: true - ), - Child( - name: "cTypeString", - kind: .node(kind: .stringLiteralExpr), - isOptional: true - ), - ] - ), - - Node( - kind: .conventionWitnessMethodAttributeArguments, + kind: .abiAttributeArguments, base: .syntax, - nameForDiagnostics: "@convention(...) arguments for witness methods", - documentation: "The arguments for the '@convention(witness_method: ...)'.", + nameForDiagnostics: "ABI-providing declaration", + documentation: "The arguments of the '@abi' attribute", children: [ Child( - name: "witnessMethodLabel", - kind: .token(choices: [.keyword(.witness_method)]) - ), - Child( - name: "colon", - kind: .token(choices: [.token(.colon)]) - ), - Child( - name: "protocolName", - kind: .token(choices: [.token(.identifier)]) - ), + 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)), + ]) + ) ] ), @@ -327,8 +310,7 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "accessorSpecifier", - deprecatedName: "accessorKind", - kind: .token(choices: [.keyword(.get), .keyword(.set)]), + kind: .token(choices: [.keyword(.get), .keyword(.set), .keyword(._modify)]), documentation: "The accessor name.", isOptional: true ), @@ -339,10 +321,15 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "diffParams", kind: .node(kind: .differentiabilityWithRespectToArgument), isOptional: true ), + ], + childHistory: [ + [ + "accessorSpecifier": .renamed(from: "accessorKind"), + "arguments": .renamed(from: "diffParams"), + ] ] ), @@ -357,14 +344,14 @@ public let ATTRIBUTE_NODES: [Node] = [ kind: .differentiabilityArgument, base: .syntax, nameForDiagnostics: "differentiability argument", - documentation: "A differentiability argument: either the \"self\" identifier, a function parameter name, or a function parameter index.", + documentation: + "A differentiability argument: either the \"self\" identifier, a function parameter name, or a function parameter index.", traits: [ "WithTrailingComma" ], children: [ Child( name: "argument", - deprecatedName: "parameter", kind: .token(choices: [.token(.identifier), .token(.integerLiteral), .keyword(.self)]) ), Child( @@ -372,6 +359,11 @@ public let ATTRIBUTE_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "argument": .renamed(from: "parameter") + ] ] ), @@ -393,21 +385,31 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "parameters", - kind: .nodeChoices(choices: [ - Child( - name: "argument", - deprecatedName: "parameter", - kind: .node(kind: .differentiabilityArgument) - ), - Child( - name: "argumentList", - deprecatedName: "parameterList", - kind: .node(kind: .differentiabilityArguments) - ), - ]), + kind: .nodeChoices( + choices: [ + Child( + name: "argument", + kind: .node(kind: .differentiabilityArgument) + ), + Child( + name: "argumentList", + kind: .node(kind: .differentiabilityArguments) + ), + ], + childHistory: [ + [ + "argument": .renamed(from: "parameter"), + "argumentList": .renamed(from: "parameterList"), + ] + ] + ), nameForDiagnostics: "arguments" ), + ], + childHistory: [ + [ + "arguments": .renamed(from: "parameters") + ] ] ), @@ -423,7 +425,6 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "differentiabilityParameters", kind: .collection(kind: .differentiabilityArgumentList, collectionElementName: "Argument"), documentation: "The parameters for differentiation." ), @@ -431,6 +432,11 @@ public let ATTRIBUTE_NODES: [Node] = [ name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "arguments": .renamed(from: "differentiabilityParameters") + ] ] ), @@ -443,37 +449,44 @@ public let ATTRIBUTE_NODES: [Node] = [ children: [ Child( name: "kindSpecifier", - deprecatedName: "diffKind", kind: .token(choices: [.keyword(._forward), .keyword(.reverse), .keyword(._linear)]), + documentation: "The differentiability kind, if it exists.", isOptional: true ), Child( name: "kindSpecifierComma", - deprecatedName: "diffKindComma", kind: .token(choices: [.token(.comma)]), documentation: "The comma following the differentiability kind, if it exists.", isOptional: true ), Child( name: "arguments", - deprecatedName: "diffParams", kind: .node(kind: .differentiabilityWithRespectToArgument), + documentation: "The differentiability arguments, if any exists.", isOptional: true ), Child( name: "argumentsComma", - deprecatedName: "diffParamsComma", kind: .token(choices: [.token(.comma)]), documentation: "The comma following the differentiability arguments clause, if it exists.", isOptional: true ), Child( name: "genericWhereClause", - deprecatedName: "whereClause", kind: .node(kind: .genericWhereClause), - documentation: "A `where` clause that places additional constraints on generic parameters like `where T: Differentiable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where T: Differentiable`.", isOptional: true ), + ], + childHistory: [ + [ + "kindSpecifier": .renamed(from: "diffKind"), + "kindSpecifierComma": .renamed(from: "diffKindComma"), + "arguments": .renamed(from: "diffParams"), + "argumentsComma": .renamed(from: "diffParamsComma"), + "genericWhereClause": .renamed(from: "whereClause"), + ] ] ), @@ -547,9 +560,13 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "declName", - deprecatedName: "declname", kind: .node(kind: .declReferenceExpr) ), + ], + childHistory: [ + [ + "declName": .renamed(from: "declname") + ] ] ), @@ -561,34 +578,12 @@ public let ATTRIBUTE_NODES: [Node] = [ elementChoices: [.token] ), - Node( - kind: .exposeAttributeArguments, - base: .syntax, - nameForDiagnostics: "@_expose arguments", - documentation: "The arguments for the '@_expose' attribute", - children: [ - Child( - name: "language", - kind: .node(kind: .token) - ), - Child( - name: "comma", - kind: .token(choices: [.token(.comma)]), - isOptional: true - ), - Child( - name: "cxxName", - kind: .node(kind: .stringLiteralExpr), - isOptional: true - ), - ] - ), - Node( kind: .implementsAttributeArguments, base: .syntax, - nameForDiagnostics: "@_implements arguemnts", - documentation: "The arguments for the `@_implements` attribute of the form `Type, methodName(arg1Label:arg2Label:)`", + nameForDiagnostics: "@_implements arguments", + documentation: + "The arguments for the `@_implements` attribute of the form `Type, methodName(arg1Label:arg2Label:)`", children: [ Child( name: "type", @@ -603,11 +598,15 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "declName", - deprecatedName: "declname", kind: .node(kind: .declReferenceExpr), nameForDiagnostics: "declaration name", documentation: "The value for this argument" ), + ], + childHistory: [ + [ + "declName": .renamed(from: "declname") + ] ] ), @@ -664,11 +663,13 @@ public let ATTRIBUTE_NODES: [Node] = [ name: "name", kind: .node(kind: .token), nameForDiagnostics: "name", + documentation: "The identifier name for a nullary selection, if it exists.", isOptional: true ), Child( name: "colon", kind: .token(choices: [.token(.colon)]), + documentation: "The colon separating the label and the value or a colon representing an unlabeled argument", isOptional: true ), ] @@ -681,29 +682,6 @@ public let ATTRIBUTE_NODES: [Node] = [ elementChoices: [.objCSelectorPiece] ), - Node( - kind: .opaqueReturnTypeOfAttributeArguments, - base: .syntax, - nameForDiagnostics: "opaque return type arguments", - documentation: "The arguments for the '@_opaqueReturnTypeOf()'.", - children: [ - Child( - name: "mangledName", - kind: .node(kind: .stringLiteralExpr), - documentation: "The mangled name of a declaration." - ), - Child( - name: "comma", - kind: .token(choices: [.token(.comma)]) - ), - Child( - name: "ordinal", - kind: .token(choices: [.token(.integerLiteral)]), - documentation: "The ordinal corresponding to the 'some' keyword that introduced this opaque type." - ), - ] - ), - Node( kind: .originallyDefinedInAttributeArguments, base: .syntax, @@ -738,21 +716,37 @@ public let ATTRIBUTE_NODES: [Node] = [ base: .syntaxCollection, nameForDiagnostics: "argument to '@_specialize", documentation: "A collection of arguments for the `@_specialize` attribute", - elementChoices: [.labeledSpecializeArgument, .specializeAvailabilityArgument, .specializeTargetFunctionArgument, .genericWhereClause] + elementChoices: [ + .labeledSpecializeArgument, .specializeAvailabilityArgument, .specializeTargetFunctionArgument, + .genericWhereClause, + ] + ), + + Node( + kind: .specializedAttributeArgument, + base: .syntax, + nameForDiagnostics: "argument to '@specialized", + documentation: "The generic where clause for the `@specialized` attribute", + children: [ + Child( + name: "genericWhereClause", + kind: .node(kind: .genericWhereClause) + ) + ] ), Node( kind: .specializeTargetFunctionArgument, base: .syntax, nameForDiagnostics: "attribute argument", - documentation: "A labeled argument for the `@_specialize` attribute with a function decl value like `target: myFunc(_:)`", + documentation: + "A labeled argument for the `@_specialize` attribute with a function decl value like `target: myFunc(_:)`", traits: [ "WithTrailingComma" ], children: [ Child( name: "targetLabel", - deprecatedName: "label", kind: .token(choices: [.keyword(.target)]), nameForDiagnostics: "label", documentation: "The label of the argument" @@ -764,7 +758,6 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "declName", - deprecatedName: "declname", kind: .node(kind: .declReferenceExpr), nameForDiagnostics: "declaration name", documentation: "The value for this argument" @@ -775,49 +768,12 @@ public let ATTRIBUTE_NODES: [Node] = [ documentation: "A trailing comma if this argument is followed by another one", isOptional: true ), + ], + childHistory: [ + [ + "targetLabel": .renamed(from: "label"), + "declName": .renamed(from: "declname"), + ] ] ), - - Node( - kind: .unavailableFromAsyncAttributeArguments, - base: .syntax, - nameForDiagnostics: "@_unavailableFromAsync argument", - documentation: "The arguments for the '@_unavailableFromAsync' attribute", - children: [ - Child( - name: "messageLabel", - kind: .token(choices: [.keyword(.message)]) - ), - Child( - name: "colon", - kind: .token(choices: [.token(.colon)]) - ), - Child( - name: "message", - kind: .node(kind: .stringLiteralExpr) - ), - ] - ), - - Node( - kind: .underscorePrivateAttributeArguments, - base: .syntax, - nameForDiagnostics: "@_private argument", - documentation: "The arguments for the '@_private' attribute", - children: [ - Child( - name: "sourceFileLabel", - kind: .token(choices: [.keyword(.sourceFile)]) - ), - Child( - name: "colon", - kind: .token(choices: [.token(.colon)]) - ), - Child( - name: "filename", - kind: .node(kind: .stringLiteralExpr) - ), - ] - ), - ] diff --git a/Sources/CodeGeneration/SyntaxSupport/AvailabilityNodes.swift b/Sources/CodeGeneration/SyntaxSupport/AvailabilityNodes.swift index be317e4..e75439a 100644 --- a/Sources/CodeGeneration/SyntaxSupport/AvailabilityNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/AvailabilityNodes.swift @@ -15,12 +15,12 @@ public let AVAILABILITY_NODES: [Node] = [ kind: .availabilityArgument, base: .syntax, nameForDiagnostics: "availability argument", - documentation: "A single argument to an `@available` argument like `*`, `iOS 10.1`, or `message: \"This has been deprecated\"`.", + documentation: + "A single argument to an `@available` argument like `*`, `iOS 10.1`, or `message: \"This has been deprecated\"`.", traits: ["WithTrailingComma"], children: [ Child( name: "argument", - deprecatedName: "entry", kind: .nodeChoices(choices: [ Child( name: "token", @@ -47,6 +47,11 @@ public let AVAILABILITY_NODES: [Node] = [ documentation: "A trailing comma if the argument is followed by another argument.", isOptional: true ), + ], + childHistory: [ + [ + "argument": .renamed(from: "entry") + ] ] ), @@ -54,7 +59,8 @@ public let AVAILABILITY_NODES: [Node] = [ kind: .availabilityLabeledArgument, base: .syntax, nameForDiagnostics: "availability argument", - documentation: "An argument to an `@available` attribute that consists of a label and a value, e.g. `message: \"This has been deprecated\"`.", + documentation: + "An argument to an `@available` attribute that consists of a label and a value, e.g. `message: \"This has been deprecated\"`.", children: [ Child( name: "label", @@ -102,7 +108,8 @@ public let AVAILABILITY_NODES: [Node] = [ kind: .platformVersion, base: .syntax, nameForDiagnostics: "version restriction", - documentation: "An argument to `@available` that restricts the availability on a certain platform to a version, e.g. `iOS 10` or `swift 3.4`.", + documentation: + "An argument to `@available` that restricts the availability on a certain platform to a version, e.g. `iOS 10` or `swift 3.4`.", children: [ Child( name: "platform", @@ -157,7 +164,9 @@ public let AVAILABILITY_NODES: [Node] = [ kind: .versionTuple, base: .syntax, nameForDiagnostics: "version tuple", - documentation: "A version number like `1.2.0`. Only the first version component is required. There might be an arbitrary number of following components.", + documentation: + "A version number like `1.2.0`. Only the first version component is required. There might be an arbitrary number of following components.", + parserFunction: "parseVersionTuple", children: [ Child( name: "major", @@ -167,9 +176,9 @@ public let AVAILABILITY_NODES: [Node] = [ Child( name: "components", kind: .collection(kind: .versionComponentList, collectionElementName: "VersionComponent"), - documentation: "Any version components that are not the major version . For example, for `1.2.0`, this will contain `.2.0`." + documentation: + "Any version components that are not the major version . For example, for `1.2.0`, this will contain `.2.0`." ), ] ), - ] diff --git a/Sources/CodeGeneration/SyntaxSupport/BuilderInitializableTypes.swift b/Sources/CodeGeneration/SyntaxSupport/BuilderInitializableTypes.swift deleted file mode 100644 index 487541f..0000000 --- a/Sources/CodeGeneration/SyntaxSupport/BuilderInitializableTypes.swift +++ /dev/null @@ -1,35 +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 -// -//===----------------------------------------------------------------------===// - -/// As keys, contains all node kinds that can be initialized using a result -/// builder. If the value is not nil, the result builder construct the value's -/// type and synthesize all other members to form the node. -public let BUILDER_INITIALIZABLE_TYPES: [SyntaxNodeKind: SyntaxNodeKind?] = [ - .arrayElementList: nil, - .closureCaptureList: nil, - .codeBlock: .codeBlockItemList, - .codeBlockItemList: nil, - .enumCaseElementList: nil, - .exprList: nil, - .functionParameterList: nil, - .genericArgumentList: nil, - .genericParameterList: nil, - .genericRequirementList: nil, - .inheritedTypeList: nil, - .labeledExprList: nil, - .memberBlock: .memberBlockItemList, - .memberBlockItemList: nil, - .patternBindingList: nil, - .switchCaseItemList: nil, - .switchCaseList: nil, - .tuplePatternElementList: nil, -] diff --git a/Sources/CodeGeneration/SyntaxSupport/Child.swift b/Sources/CodeGeneration/SyntaxSupport/Child.swift index 45afefc..6c9badb 100644 --- a/Sources/CodeGeneration/SyntaxSupport/Child.swift +++ b/Sources/CodeGeneration/SyntaxSupport/Child.swift @@ -14,7 +14,7 @@ import SwiftSyntax /// The kind of token a node can contain. Either a token of a specific kind or a /// keyword with the given text. -public enum TokenChoice: Equatable { +public enum TokenChoice: Equatable, IdentifierConvertible { case keyword(Keyword) case token(Token) @@ -25,12 +25,13 @@ public enum TokenChoice: Equatable { } } - public var varOrCaseName: TokenSyntax { + /// The name of this token choice as an identifier. + public var identifier: TokenSyntax { switch self { case .keyword(let keyword): - return keyword.spec.varOrCaseName + return keyword.spec.identifier case .token(let token): - return token.spec.varOrCaseName + return token.spec.identifier } } } @@ -39,9 +40,16 @@ public enum ChildKind { /// The child always contains a node of the given `kind`. case node(kind: SyntaxNodeKind) /// The child always contains a node that matches one of the `choices`. - case nodeChoices(choices: [Child]) + case nodeChoices(choices: [Child], childHistory: Child.History = []) + // FIXME: We don't appear to have ever generated compatibility layers for children of node choices! /// The child is a collection of `kind`. - case collection(kind: SyntaxNodeKind, collectionElementName: String, defaultsToEmpty: Bool = false, deprecatedCollectionElementName: String? = nil) + case collection( + kind: SyntaxNodeKind, + collectionElementName: String? = nil, + defaultsToEmpty: Bool = false, + deprecatedCollectionElementName: String? = nil, + generateDeprecatedAddFunction: Bool = true + ) /// The child is a token that matches one of the given `choices`. /// If `requiresLeadingSpace` or `requiresTrailingSpace` is not `nil`, it /// overrides the default leading/trailing space behavior of the token. @@ -64,7 +72,7 @@ public enum ChildKind { } public var isNodeChoicesEmpty: Bool { - if case .nodeChoices(let nodeChoices) = self { + if case .nodeChoices(let nodeChoices, _) = self { return nodeChoices.isEmpty } else { return true @@ -74,25 +82,21 @@ public enum ChildKind { /// A child of a node, that may be declared optional or a token with a /// restricted subset of acceptable kinds or texts. -public class Child { +public class Child: NodeChoiceConvertible { /// The name of the child. /// /// The first character of the name is always uppercase. public let name: String - /// If the child has been renamed, its old, now deprecated, name. - /// - /// This is used to generate deprecated compatibility layers. - public let deprecatedName: String? - /// The kind of the child (node, token, collection, ...) public let kind: ChildKind /// Whether this child is optional and can be `nil`. public let isOptional: Bool - /// The experimental feature the child represents, or `nil` if this isn't - /// for an experimental feature. + /// Whether this child provides a default value when used as a parameter in a function. + public let providesDefaultInitialization: Bool + public let experimentalFeature: ExperimentalFeature? /// A name of this child that can be shown in diagnostics. @@ -123,25 +127,34 @@ public class Child { /// The first line of the child's documentation public let documentationAbstract: String - /// If `true`, this is for an experimental language feature, and any public - /// API generated should be SPI. - public var isExperimental: Bool { experimentalFeature != nil } - public var syntaxNodeKind: SyntaxNodeKind { switch kind { - case .node(kind: let kind): + case .node(let kind): return kind case .nodeChoices: return .syntax - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): return kind case .token: return .token } } - /// A name of this child that's suitable to be used for variable or enum case names. - public var varOrCaseName: TokenSyntax { + /// Should this child be hidden? + /// + /// A hidden child is one that is not accessible in any way at a specific point in the history, but still needs to be + /// (default) initialized. As always, its `newestChildPath` indicates the current way to access it. + /// + /// Hidden children are used for `Refactoring.introduced` and for the implicit changeset that creates + /// non-experimental APIs that ignore experimental children. + public let isHidden: Bool + + /// True if this child was created by a `childHistory` change set. Such children + /// are part of the compatibility layer and are therefore deprecated. + public var isHistorical: Bool + + /// A name of this child as an identifier. + public var identifier: TokenSyntax { return .identifier(lowercaseFirstWord(name: name)) } @@ -150,7 +163,7 @@ public class Child { /// For any other kind of child nodes, accessing this property crashes. public var syntaxChoicesType: TypeSyntax { precondition(kind.isNodeChoices, "Cannot get `syntaxChoicesType` for node that doesn’t have nodeChoices") - return "\(raw: name.withFirstCharacterUppercased)" + return "\(raw: newestName.withFirstCharacterUppercased)" } /// If this child only has tokens, the type that the generated `TokenSpecSet` should get. @@ -158,20 +171,70 @@ public class Child { /// For any other kind of child nodes, accessing this property crashes. public var tokenSpecSetType: TypeSyntax { precondition(kind.isToken, "Cannot get `tokenSpecSetType` for node that isn’t a token") - return "\(raw: name.withFirstCharacterUppercased)Options" + return "\(raw: newestName.withFirstCharacterUppercased)Options" } - /// The deprecated name of this child that's suitable to be used for variable or enum case names. - public var deprecatedVarName: TokenSyntax? { - guard let deprecatedName = deprecatedName else { - return nil + /// If this child is part of a compatibility layer, describes the sequence of accesses necessary + /// to reach the equivalent value using non-compatibility-layer children; if the child + /// is not deprecated, this array is empty. + /// + /// Think of the elements of this array like components in a key path: + /// `newestChildPath[0]` is a child of the same node `self` is a child of, + /// `newestChildPath[1]` is a child of the node in `newestChildPath[0]`, + /// `newestChildPath[2]` is a child of the node in `newestChildPath[1]`, + /// and so on. To access the current value of this child, you must access each child + /// in the path *in sequence* on the node returned by the last access. + /// + /// ``` + /// // Suppose the `newestChildPath` of `fooBar` looks like: + /// // + /// // [ Child(name: "foo", ...), Child(name: "bar", ...) ] + /// // + /// // A compatibility property might look like this: + /// var fooBar: BarSyntax { + /// get { + /// return self.foo.bar + /// } + /// set { + /// self.foo.bar = newValue + /// } + /// } + /// ``` + /// + /// If the child has only ever had `Refactoring.renamed(from:)`, there will + /// only be one element in the path; if `Refactoring.extracted` is involved, + /// there may be many elements. + /// + /// - Invariant: `newestChildPath.first`, if present, is always a sibling of `self`. + /// - Invariant: `newestChildPath.last`, if present, always has the same `kind` as `self`. + /// - Invariant: All elements in `newestChildPath` are not historical. + /// - Note: This array does *not* record all of the previous versions + /// of the child. That information is not directly available anywhere. + public let newestChildPath: [Child] + + /// Replaces the nodes in `newerChildPath` with their own `newerChildPath`s, + /// if any, to form a child path enitrely of non-historical nodes. + static private func makeNewestChildPath(from newerChildPath: [Child]) -> [Child] { + var result: [Child] = [] + + // Push the children onto the stack in reverse order so they end up in the right place once they're resolved. + var workStack = Array(newerChildPath.reversed()) + + while let elem = workStack.popLast() { + if !elem.newestChildPath.isEmpty { + // There's an even newer version. Start working on that. + workStack.append(contentsOf: elem.newestChildPath.reversed()) + } else { + // We've reached the current version of the child. + result.append(elem) + } } - return .identifier(lowercaseFirstWord(name: deprecatedName)) + + return result } - /// Determines if this child has a deprecated name - public var hasDeprecatedName: Bool { - return deprecatedName != nil + private var newestName: String { + return newestChildPath.last?.name ?? name } /// If the child ends with "token" in the kind, it's considered a token node. @@ -192,7 +255,7 @@ public class Child { The old string-based implementation returned "Token" to ensure that `tokenKind` is not nil and that `isToken` computed-property will return true, but the value "Token" had never been used in other cases. We should try to remove this computed property altogether in the issue: - https://github.com/apple/swift-syntax/issues/2010 + https://github.com/swiftlang/swift-syntax/issues/2010 */ return .unknown } @@ -213,7 +276,7 @@ public class Child { /// Whether this child has syntax kind `UnexpectedNodes`. public var isUnexpectedNodes: Bool { switch kind { - case .collection(kind: .unexpectedNodes, _, _, _): + case .collection(kind: .unexpectedNodes, _, _, _, _): return true default: return false @@ -224,11 +287,11 @@ public class Child { /// it has no node choices. public var hasBaseType: Bool { switch kind { - case .nodeChoices(let choices): + case .nodeChoices(let choices, _): return choices.isEmpty case .node(let kind): return kind.isBase - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): return kind.isBase case .token: return false @@ -244,29 +307,136 @@ public class Child { return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline) } - /// If a classification is passed, it specifies the color identifiers in - /// that subtree should inherit for syntax coloring. Must be a member of - /// ``SyntaxClassification``. - /// If `forceClassification` is also set to true, all child nodes (not only - /// identifiers) inherit the syntax classification. init( name: String, - deprecatedName: String? = nil, kind: ChildKind, experimentalFeature: ExperimentalFeature? = nil, nameForDiagnostics: String? = nil, documentation: String? = nil, - isOptional: Bool = false + isOptional: Bool = false, + providesDefaultInitialization: Bool = true, + newerChildPath: [Child] = [], + isHistorical: Bool = false ) { precondition(name.first?.isLowercase ?? true, "The first letter of a child’s name should be lowercase") - precondition(deprecatedName?.first?.isLowercase ?? true, "The first letter of a child’s deprecatedName should be lowercase") self.name = name - self.deprecatedName = deprecatedName + self.newestChildPath = Self.makeNewestChildPath(from: newerChildPath) self.kind = kind self.experimentalFeature = experimentalFeature self.nameForDiagnostics = nameForDiagnostics self.documentationSummary = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.documentationAbstract = String(documentation?.split(whereSeparator: \.isNewline).first ?? "") self.isOptional = isOptional + self.providesDefaultInitialization = providesDefaultInitialization + self.isHidden = false + self.isHistorical = isHistorical + } + + /// Create a node that is a copy of the last node in `newerChildPath`, but + /// with modifications. + init( + renamingTo replacementName: String? = nil, + makingHistorical: Bool = false, + makingHidden: Bool = false, + newerChildPath: [Child] + ) { + let other = newerChildPath.last! + + self.name = replacementName ?? other.name + self.newestChildPath = Self.makeNewestChildPath(from: newerChildPath) + self.kind = other.kind + self.experimentalFeature = other.experimentalFeature + self.nameForDiagnostics = other.nameForDiagnostics + self.documentationSummary = other.documentationSummary + self.documentationAbstract = other.documentationAbstract + self.isOptional = other.isOptional + self.providesDefaultInitialization = other.providesDefaultInitialization + self.isHidden = makingHidden || other.isHidden + self.isHistorical = makingHistorical || other.isHistorical + } + + /// Create a child for the unexpected nodes between two children (either or + /// both of which may be `nil`). + convenience init(forUnexpectedBetween earlier: Child?, and later: Child?, newerChildPath: [Child] = []) { + let name = + switch (earlier, later) { + case (nil, let later?): + "unexpectedBefore\(later.name.withFirstCharacterUppercased)" + case (let earlier?, nil): + "unexpectedAfter\(earlier.name.withFirstCharacterUppercased)" + case (let earlier?, let later?): + "unexpectedBetween\(earlier.name.withFirstCharacterUppercased)And\(later.name.withFirstCharacterUppercased)" + case (nil, nil): + fatalError("unexpected node has no siblings?") + } + + self.init( + name: name, + kind: .collection(kind: .unexpectedNodes, collectionElementName: name.withFirstCharacterUppercased), + experimentalFeature: earlier?.experimentalFeature ?? later?.experimentalFeature, + nameForDiagnostics: nil, + documentation: nil, + isOptional: true, + providesDefaultInitialization: true, + newerChildPath: newerChildPath, + isHistorical: (earlier?.isHistorical ?? false) || (later?.isHistorical ?? false) + ) + } +} + +extension Child: Hashable { + public static func == (lhs: Child, rhs: Child) -> Bool { + lhs === rhs + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } +} + +extension Child { + /// A set of changes to the children that were all made simultaneously. The + /// key is the *new* name of the child; any names in the value are old names. + public typealias ChangeSet = KeyValuePairs + + /// A history of change sets applied to a group of children, ordered from + /// most recent set of changes to most distant. + /// + /// The first element is the newest set of changes; the last element is the oldest set + /// of changes. The change sets are applied on top of one another; for + /// example, this node has a child that was originally named `foo`, then + /// `bar`, and now `baz`: + /// + /// ```swift + /// Node( + /// ... + /// children: [ + /// Child(name: "baz", ...), + /// ], + /// childHistory: [ + /// // The key here is "baz", the current name + /// [ "baz": .renamed(from: "bar") ], + /// // The key here is "bar", the name before the previous change set + /// [ "bar": .renamed(from: "foo") ], + /// } + /// } + /// ``` + public typealias History = [ChangeSet] + + /// Specifies a historical change to a given child, and is able to generate + /// replacement children to substitute for it in deprecated compatibility + /// layers. + public enum Refactoring { + /// This child was renamed at some point in the past, so a deprecated alias + /// should be provided. The associated value is the *old*, now-deprecated name. + case renamed(from: String) + + /// Several adjacent children were extracted into a separate node at some + /// point in the past, so deprecated aliases that flatten the other node's + /// children into this node should be provided. + case extracted + + /// A new child was added (and it's important to preserve the names around it). + case introduced } } diff --git a/Sources/CodeGeneration/SyntaxSupport/CommonNodes.swift b/Sources/CodeGeneration/SyntaxSupport/CommonNodes.swift index 9b9f511..9bd9c76 100644 --- a/Sources/CodeGeneration/SyntaxSupport/CommonNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/CommonNodes.swift @@ -177,7 +177,7 @@ public let COMMON_NODES: [Node] = [ kind: .decl, base: .syntax, nameForDiagnostics: "declaration", - parserFunction: "parseDeclaration" + parserFunction: "parseDeclarationOrIfConfig" ), Node( @@ -191,7 +191,8 @@ public let COMMON_NODES: [Node] = [ kind: .missingDecl, base: .decl, nameForDiagnostics: "declaration", - documentation: "In case the source code is missing a declaration, this node stands in place of the missing declaration.", + documentation: + "In case the source code is missing a declaration, this node stands in place of the missing declaration.", traits: [ "MissingNode", "WithAttributes", @@ -201,12 +202,14 @@ public let COMMON_NODES: [Node] = [ Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), - documentation: "If there were standalone attributes without a declaration to attach them to, the ``MissingDeclSyntax`` will contain these." + documentation: + "If there were standalone attributes without a declaration to attach them to, the ``MissingDeclSyntax`` will contain these." ), Child( name: "modifiers", kind: .collection(kind: .declModifierList, collectionElementName: "Modifier", defaultsToEmpty: true), - documentation: "If there were standalone modifiers without a declaration to attach them to, the ``MissingDeclSyntax`` will contain these." + documentation: + "If there were standalone modifiers without a declaration to attach them to, the ``MissingDeclSyntax`` will contain these." ), Child( name: "placeholder", @@ -224,7 +227,8 @@ public let COMMON_NODES: [Node] = [ kind: .missingExpr, base: .expr, nameForDiagnostics: "expression", - documentation: "In case the source code is missing an expression, this node stands in place of the missing expression.", + documentation: + "In case the source code is missing an expression, this node stands in place of the missing expression.", traits: [ "MissingNode" ], @@ -266,7 +270,8 @@ public let COMMON_NODES: [Node] = [ kind: .missingStmt, base: .stmt, nameForDiagnostics: "statement", - documentation: "In case the source code is missing a statement, this node stands in place of the missing statement.", + documentation: + "In case the source code is missing a statement, this node stands in place of the missing statement.", traits: [ "MissingNode" ], @@ -325,6 +330,24 @@ public let COMMON_NODES: [Node] = [ ] ), + Node( + kind: .moduleSelector, + base: .syntax, + nameForDiagnostics: "module selector", + children: [ + Child( + name: "moduleName", + kind: .token(choices: [.token(.identifier)]), + nameForDiagnostics: "module name" + ), + Child( + name: "colonColon", + kind: .token(choices: [.token(.colonColon)]), + nameForDiagnostics: "'::' operator" + ), + ] + ), + Node( kind: .pattern, base: .syntax, @@ -372,8 +395,23 @@ public let COMMON_NODES: [Node] = [ kind: .unexpectedNodes, base: .syntaxCollection, nameForDiagnostics: nil, - documentation: "A collection of syntax nodes that occurred in the source code but could not be used to form a valid syntax tree.", + documentation: + "A collection of syntax nodes that occurred in the source code but could not be used to form a valid syntax tree.", elementChoices: [.syntax] ), + Node( + kind: .unexpectedCodeDecl, + base: .decl, + nameForDiagnostics: nil, + documentation: "Unexpected code at declaration position", + children: [ + Child( + name: "unexpectedCode", + // NOTE: This is not .collection() on purpose. We don't need collection related functions for this. + kind: .node(kind: .unexpectedNodes) + ) + ], + noInterleaveUnexpected: true + ), ] diff --git a/Sources/CodeGeneration/SyntaxSupport/CompatibilityLayer.swift b/Sources/CodeGeneration/SyntaxSupport/CompatibilityLayer.swift new file mode 100644 index 0000000..ce70152 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/CompatibilityLayer.swift @@ -0,0 +1,325 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 +// +//===----------------------------------------------------------------------===// + +/// Computes and caches information about properties and initializers that ought to be generated for the compatibility layer. +public struct CompatibilityLayer { + /// Deprecated members that the compatibility layer needs for each node. + private var deprecatedMembersByNode: [SyntaxNodeKind: DeprecatedMemberInfo] = [:] + + /// Deprecated members that the compatibility layer needs for each trait. + public var deprecatedMembersByTrait: [String: DeprecatedMemberInfo] = [:] + + /// Cache for `replacementChildren(for:by:historical:)`. Ensures that we don't create two different replacement + /// children even if we refactor the same child twice, so we can reliably equate and hash `Child` objects by + /// object identity. + private var cachedReplacementChildren: [Child: [Child]] = [:] + + /// Returns the deprecated members that the compatibility layer needs for `node`. + public func deprecatedMembers(for node: LayoutNode) -> DeprecatedMemberInfo { + return deprecatedMembersByNode[node.kind] ?? DeprecatedMemberInfo() + } + + /// Returns the deprecated members that the compatibility layer needs for `trait`. + public func deprecatedMembers(for trait: Trait) -> DeprecatedMemberInfo { + return deprecatedMembersByTrait[trait.traitName] ?? DeprecatedMemberInfo() + } + + internal init(nodes: [Node], traits: [Trait]) { + // This instance will be stored in a global that's used from multiple threads simultaneously, so it won't be safe + // to mutate once the initializer returns. We therefore do all the work to populate its tables up front, rather + // than computing it lazily on demand. + for node in nodes { + computeMembers(for: node) + } + for trait in traits { + computeMembers(for: trait) + } + } + + /// Returns the child or children that would have existed in place of this + /// child before this refactoring was applied. + /// + /// - Parameters: + /// - newerChild: The child which is being replaced. + /// - refactoring: The refactoring which created that child and must be + /// reversed. + fileprivate mutating func replacementChildren( + for newerChild: Child, + by refactoring: Child.Refactoring, + historical: Bool + ) -> [Child] { + func make() -> [Child] { + switch refactoring { + case .renamed(from: let deprecatedName): + return [ + Child( + renamingTo: deprecatedName, + makingHistorical: historical, + newerChildPath: [newerChild] + ) + ] + + case .extracted: + let extractedNode = SYNTAX_NODE_MAP[newerChild.syntaxNodeKind]! + computeMembers(for: extractedNode) + + var newerGrandchildren = extractedNode.layoutNode!.children[...] + + // Drop the leading and trailing unexpected nodes--these will be newly introduced. + if newerGrandchildren.first?.isUnexpectedNodes ?? false { + newerGrandchildren.removeFirst() + } + if newerGrandchildren.last?.isUnexpectedNodes ?? false { + newerGrandchildren.removeLast() + } + + return newerGrandchildren.map { newerGrandchild in + Child( + makingHistorical: historical, + newerChildPath: [newerChild, newerGrandchild] + ) + } + + case .introduced: + return [ + Child( + makingHistorical: historical, + makingHidden: true, + newerChildPath: [newerChild] + ) + ] + } + } + + // Make sure we return the same instance even if we're called twice. + if cachedReplacementChildren[newerChild] == nil { + cachedReplacementChildren[newerChild] = make() + } + return cachedReplacementChildren[newerChild]! + } + + /// Compute and cache compatibility layer information for the given node, unless it is already present. + private mutating func computeMembers(for node: Node) { + guard deprecatedMembersByNode[node.syntaxNodeKind] == nil, let layoutNode = node.layoutNode else { + return + } + + let result = computeMembersFor( + typeName: layoutNode.kind.rawValue, + initialChildren: layoutNode.children, + history: layoutNode.childHistory, + areRequirements: false + ) + + deprecatedMembersByNode[node.syntaxNodeKind] = result + } + + /// Compute and cache compatibility layer information for the given children. + private mutating func computeMembers(for trait: Trait) { + guard deprecatedMembersByTrait[trait.traitName] == nil else { + return + } + + let result = computeMembersFor( + typeName: trait.traitName, + initialChildren: trait.children, + history: trait.childHistory, + areRequirements: true + ) + + deprecatedMembersByTrait[trait.traitName] = result + } + + /// Compute compatibility layer information for the given children. + private mutating func computeMembersFor( + typeName: String, + initialChildren: [Child], + history: Child.History, + areRequirements: Bool + ) -> DeprecatedMemberInfo { + var builder = DeprecatedMemberInfo.Builder( + typeName: typeName, + children: initialChildren, + areRequirements: areRequirements + ) + + // If any of the children are experimental, apply an initial change set that hides them, ensuring that we generate + // APIs which aren't experimental. + let experimentalChildren = initialChildren.filter { $0.isExperimental && !$0.isUnexpectedNodes } + if !experimentalChildren.isEmpty { + let syntheticChangeSet = experimentalChildren.map { ($0.name, Child.Refactoring.introduced) } + builder.applyChangeSet(syntheticChangeSet, for: &self, historical: false) + } + + // Apply changes in the history + for changeSet in history { + builder.applyChangeSet(changeSet, for: &self, historical: true) + } + + return builder.make() + } +} + +/// Describes the deprecated members of a given type that the compatibility layer ought to provide. +public struct DeprecatedMemberInfo { + /// Properties that are needed in the compatibility layer, in the order they ought to appear in the generated file. + public var vars: [Child] = [] + + /// Initializer signatures that are needed in the compatibility layer, in the order they ought to appear in the generated file. + public var inits: [InitSignature] = [] + + /// Is there anything whatsoever that we ought to generate? + public var isEmpty: Bool { + return vars.isEmpty && inits.isEmpty + } + + fileprivate struct Builder { + /// Properties that are needed in the compatibility layer, in the order they ought to appear in the generated file. + /// This becomes a property of the `DeprecatedMemberInfo`. + var vars: [Child] = [] + + /// Initializer signatures that are needed in the compatibility layer, in the order they ought to appear in the generated file. + /// This becomes a property of the `DeprecatedMemberInfo`. + var inits: [InitSignature] = [] + + /// Name of the type we're generating a compatibility layer for. + private let typeName: String + + /// Are we building a compatibility layer for requirements of a trait? Traits don't have unexpected children or + /// initializers. + private let areRequirements: Bool + + /// The current set of children after applying all of the change sets ever passed to `applyChangeSet(_:for:historical:)`. + /// This is working state. + private var children: [Child] + + /// The set of all children that have ever been added to `vars`, plus the ones that were originally present. + /// Used to ensure duplicates aren't added to `vars`. This is working state. + private var knownVars: Set + + /// Creates a builder with no deprecated members, but ready to start adding change sets. + init(typeName: String, children: [Child], areRequirements: Bool) { + self.typeName = typeName + self.areRequirements = areRequirements + + self.children = children + self.knownVars = Set(children) + } + + /// Creates a `DeprecatedMemberInfo` from all the change sets that have been passed to + /// `applyChangeSet(_:for:historical:)`. + func make() -> DeprecatedMemberInfo { + return DeprecatedMemberInfo(vars: vars, inits: inits) + } + + func lastVisibleChild(before i: Int, isUnexpectedNodes: Bool) -> Child? { + let allEarlier = children.prefix(through: max(i - 1, children.startIndex)) + return allEarlier.last { !$0.isHidden && $0.isUnexpectedNodes == isUnexpectedNodes } + } + + func firstVisibleChild(after i: Int, isUnexpectedNodes: Bool) -> Child? { + let allLater = children.suffix(from: min(i + 1, children.endIndex)) + return allLater.first { !$0.isHidden && $0.isUnexpectedNodes == isUnexpectedNodes } + } + + /// Generate the new `vars` and `inits` that are required to maintain compatibility with `changeSet`. + /// + /// - Parameters: + /// - changeSet: The changes to apply. This type is basically a generic form of `Child.ChangeSet`. + /// - compatibilityLayer: The compatibility layer that these children will ultimately belong to. + /// - historical: Should the children created by this change set be marked historical (and thus be deprecated)? + mutating func applyChangeSet( + _ changeSet: some RandomAccessCollection<(key: String, value: Child.Refactoring)>, + for compatibilityLayer: inout CompatibilityLayer, + historical: Bool + ) { + var unexpectedChildrenWithNewNames: Set = [] + + // First pass: Apply the changes explicitly specified in the change set. + for (currentName, refactoring) in changeSet { + let i = firstIndexOfChild(named: currentName) + let oldChild = children[i] + + let replacementChildren = compatibilityLayer.replacementChildren( + for: children[i], + by: refactoring, + historical: historical + ) + children.replaceSubrange(i...i, with: replacementChildren) + + if !areRequirements { + func isDifferent(_ newChild: Child) -> Bool { + oldChild.isHidden != newChild.isHidden || oldChild.name != newChild.name + } + + // Mark adjacent unexpected node children whose names have changed too. + if let firstNewChild = replacementChildren.first, isDifferent(firstNewChild), + let adjacentUnexpectedChild = lastVisibleChild(before: i, isUnexpectedNodes: true) + { + unexpectedChildrenWithNewNames.insert(adjacentUnexpectedChild) + } + if let lastNewChild = replacementChildren.last, isDifferent(lastNewChild), + let adjacentUnexpectedChild = firstVisibleChild( + after: i + replacementChildren.count - 1, + isUnexpectedNodes: true + ) + { + unexpectedChildrenWithNewNames.insert(adjacentUnexpectedChild) + } + } + } + + // Second pass: Update unexpected node children adjacent to those changes whose names have probably changed. + for unexpectedChild in unexpectedChildrenWithNewNames where !unexpectedChild.isHidden { + precondition(unexpectedChild.isUnexpectedNodes) + let i = firstIndexOfChild(named: unexpectedChild.name) + + guard i == 0 || !children[i - 1].isHidden else { + // Special case: `unexpectedChild` follows a hidden child and should be hidden too. + children[i] = Child(makingHistorical: historical, makingHidden: true, newerChildPath: [unexpectedChild]) + continue + } + + // Find nearest expected, non-hidden nodes around `unexpectedChild` + let earlier = lastVisibleChild(before: i, isUnexpectedNodes: false) + let later = firstVisibleChild(after: i, isUnexpectedNodes: false) + + precondition(!(earlier?.isUnexpectedNodes ?? false) && !(later?.isUnexpectedNodes ?? false)) + + let newChild = Child(forUnexpectedBetween: earlier, and: later, newerChildPath: [unexpectedChild]) + precondition(newChild.name != unexpectedChild.name) + precondition(!children.contains { $0.name == newChild.name }) + + children[i] = newChild + } + + // Third pass: Append newly-created children to vars. We do this now so that changes from the first two passes + // are properly interleaved, preserving source order. + self.vars += children.filter { !$0.isHidden && knownVars.insert($0).inserted } + + // We don't create compatibility layers for protocol requirement inits. + if !areRequirements { + self.inits.append(InitSignature(children: children)) + } + } + + @_optimize(none) // Workaround for SIL optimizer crash (rdar://158353230) + private func firstIndexOfChild(named targetName: String) -> Int { + guard let i = children.firstIndex(where: { $0.name == targetName }) else { + fatalError( + "couldn't find '\(targetName)' in current children of \(typeName): \(String(reflecting: children.map(\.name)))" + ) + } + return i + } + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/CompilerNodes.swift b/Sources/CodeGeneration/SyntaxSupport/CompilerNodes.swift new file mode 100644 index 0000000..7052d76 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/CompilerNodes.swift @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +public let COMPILER_NODES: [Node] = [ + Node( + kind: .availabilityMacroDefinitionFile, + base: .syntax, + spi: "Compiler", + nameForDiagnostics: "availability macro definition", + documentation: """ + Syntax for '-define-availability' compiler arguments, never appear in Swift source code. + """, + parserFunction: "parseAvailabilityMacroDefinitionFile", + children: [ + Child( + name: "platformVersion", + kind: .node(kind: .platformVersion) + ), + Child( + name: "colon", + kind: .token(choices: [.token(.colon)]) + ), + Child( + name: "specs", + kind: .collection(kind: .availabilityArgumentList) + ), + Child( + name: "endOfFileToken", + kind: .token(choices: [.token(.endOfFile)]) + ), + ] + ), + + Node( + kind: .accessorBlockFile, + base: .syntax, + spi: "Compiler", + nameForDiagnostics: "accessors", + documentation: """ + Syntax for 'accessor' macro expansion, never appear in Swift source code. + """, + parserFunction: "parseAccessorBlockFile", + children: [ + Child( + name: "leftBrace", + kind: .token(choices: [.token(.leftBrace)]), + isOptional: true + ), + Child( + name: "accessors", + kind: .collection(kind: .accessorDeclList) + ), + Child( + name: "rightBrace", + kind: .token(choices: [.token(.rightBrace)]), + isOptional: true + ), + Child( + name: "endOfFileToken", + kind: .token(choices: [.token(.endOfFile)]) + ), + ] + ), + + Node( + kind: .attributeClauseFile, + base: .syntax, + spi: "Compiler", + nameForDiagnostics: "attribute list", + documentation: """ + Syntax for 'memberAttribute' macro expansion or 'swift_attr' attribute in Clang, never appear in Swift source code. + """, + parserFunction: "parseAttributeClauseFile", + traits: [ + "WithAttributes", + "WithModifiers", + ], + children: [ + Child( + name: "attributes", + kind: .collection( + kind: .attributeList, + collectionElementName: "Attribute" + ) + ), + Child( + name: "modifiers", + kind: .collection(kind: .declModifierList) + ), + Child( + name: "endOfFileToken", + kind: .token(choices: [.token(.endOfFile)]) + ), + ] + ), + + Node( + kind: .codeBlockFile, + base: .syntax, + spi: "Compiler", + nameForDiagnostics: "code block", + documentation: """ + Syntax for 'body' macro expansions, never appear in Swift source code. + """, + parserFunction: "parseCodeBlockFile", + children: [ + Child( + name: "body", + kind: .node(kind: .codeBlock) + ), + Child( + name: "endOfFileToken", + kind: .token(choices: [.token(.endOfFile)]) + ), + ] + ), + + Node( + kind: .memberBlockItemListFile, + base: .syntax, + spi: "Compiler", + nameForDiagnostics: "member list", + documentation: """ + Syntax for declaration macro expansions in type contexts, never appear in Swift source code. + """, + parserFunction: "parseMemberBlockItemListFile", + children: [ + Child( + name: "members", + kind: .collection(kind: .memberBlockItemList) + ), + Child( + name: "endOfFileToken", + kind: .token(choices: [.token(.endOfFile)]) + ), + ] + ), +] diff --git a/Sources/CodeGeneration/SyntaxSupport/DeclNodes.swift b/Sources/CodeGeneration/SyntaxSupport/DeclNodes.swift index acd632e..50daee2 100644 --- a/Sources/CodeGeneration/SyntaxSupport/DeclNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/DeclNodes.swift @@ -28,10 +28,14 @@ public let DECL_NODES: [Node] = [ ), Child( name: "trailingPeriod", - deprecatedName: "trailingDot", - kind: .token(choices: [.token(.period)]), + kind: .token(choices: [.token(.period), .token(.colonColon)]), isOptional: true ), + ], + childHistory: [ + [ + "trailingPeriod": .renamed(from: "trailingDot") + ] ] ), @@ -100,7 +104,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "accessorSpecifier", - deprecatedName: "accessorKind", kind: .token(choices: [ .keyword(.get), .keyword(.set), @@ -113,13 +116,16 @@ public let DECL_NODES: [Node] = [ .keyword(.mutableAddressWithOwner), .keyword(.mutableAddressWithNativeOwner), .keyword(._read), + .keyword(.read), .keyword(._modify), + .keyword(.modify), .keyword(.`init`), + .keyword(.borrow), + .keyword(.mutate), ]) ), Child( name: "parameters", - deprecatedName: "parameter", kind: .node(kind: .accessorParameters), nameForDiagnostics: "parameter", isOptional: true @@ -134,6 +140,12 @@ public let DECL_NODES: [Node] = [ kind: .node(kind: .codeBlock), isOptional: true ), + ], + childHistory: [ + [ + "accessorSpecifier": .renamed(from: "accessorKind"), + "parameters": .renamed(from: "parameter"), + ] ] ), @@ -198,7 +210,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of the actor. If the name matches a reserved keyword use backticks to escape it." ), @@ -219,13 +230,19 @@ public let DECL_NODES: [Node] = [ name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child( name: "memberBlock", kind: .node(kind: .memberBlock) ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -285,7 +302,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of this associated type." ), @@ -299,7 +315,8 @@ public let DECL_NODES: [Node] = [ Child( name: "initializer", kind: .node(kind: .typeInitializerClause), - documentation: "The type initializer clause for this associated type declaration which represents a default type assignment for the associated type.", + documentation: + "The type initializer clause for this associated type declaration which represents a default type assignment for the associated type.", isOptional: true ), Child( @@ -309,6 +326,11 @@ public let DECL_NODES: [Node] = [ documentation: "The `where` clause that applies to the generic parameters of this associated type declaration.", isOptional: true ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -368,7 +390,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of the class." ), @@ -399,6 +420,11 @@ public let DECL_NODES: [Node] = [ documentation: "The members of the class declaration. As class extension declarations may declare additional members, the contents of this member block isn't guaranteed to be a complete list of members for this type." ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -464,12 +490,11 @@ public let DECL_NODES: [Node] = [ .keyword(.private), .keyword(.public), .keyword(.reasync), - .keyword(._resultDependsOnSelf), .keyword(.required), .keyword(.static), - .keyword(.transferring), .keyword(.unowned), .keyword(.weak), + .keyword(.sending), ]) ), Child( @@ -485,7 +510,7 @@ public let DECL_NODES: [Node] = [ base: .decl, nameForDiagnostics: "deinitializer", documentation: """ - A `deint` declaration + A `deinit` declaration An example of a deinitializer is @@ -571,21 +596,27 @@ public let DECL_NODES: [Node] = [ Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), - documentation: "If there were attributes before the editor placeholder, the ``EditorPlaceholderDeclSyntax`` will contain these." + documentation: + "If there were attributes before the editor placeholder, the ``EditorPlaceholderDeclSyntax`` will contain these." ), Child( name: "modifiers", kind: .collection(kind: .declModifierList, collectionElementName: "Modifier", defaultsToEmpty: true), - documentation: "If there were modifiers before the editor placeholder, the `EditorPlaceholderDecl` will contain these." + documentation: + "If there were modifiers before the editor placeholder, the `EditorPlaceholderDecl` will contain these." ), Child( name: "placeholder", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: """ The actual editor placeholder that starts with `<#` and ends with `#>`. """ ), + ], + childHistory: [ + [ + "placeholder": .renamed(from: "identifier") + ] ] ), @@ -604,7 +635,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "parameters", - deprecatedName: "parameterList", kind: .collection(kind: .enumCaseParameterList, collectionElementName: "Parameter"), nameForDiagnostics: "parameters", documentation: "The actual parameters." @@ -614,6 +644,11 @@ public let DECL_NODES: [Node] = [ kind: .token(choices: [.token(.rightParen)]), documentation: "The ')' to close the parameter clause." ), + ], + childHistory: [ + [ + "parameters": .renamed(from: "parameterList") + ] ] ), @@ -660,7 +695,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "defaultValue", - deprecatedName: "defaultArgument", kind: .node(kind: .initializerClause), nameForDiagnostics: "default value", documentation: "If the parameter has a default value, the initializer clause describing the default value.", @@ -672,6 +706,11 @@ public let DECL_NODES: [Node] = [ documentation: "If the parameter is followed by another parameter, the comma separating them.", isOptional: true ), + ], + childHistory: [ + [ + "defaultValue": .renamed(from: "defaultArgument") + ] ] ), @@ -679,7 +718,8 @@ public let DECL_NODES: [Node] = [ kind: .enumCaseDecl, base: .decl, nameForDiagnostics: "enum case", - documentation: "A `case` declaration of a Swift `enum`. It can have 1 or more `EnumCaseElement`s inside, each declaring a different case of the enum.", + documentation: + "A `case` declaration of a Swift `enum`. It can have 1 or more `EnumCaseElement`s inside, each declaring a different case of the enum.", traits: [ "WithAttributes", "WithModifiers", @@ -723,20 +763,19 @@ public let DECL_NODES: [Node] = [ kind: .enumCaseElement, base: .syntax, nameForDiagnostics: nil, - documentation: "An element of an enum case, containing the name of the case and, optionally, either associated values or an assignment to a raw value.", + documentation: + "An element of an enum case, containing the name of the case and, optionally, either associated values or an assignment to a raw value.", traits: [ "WithTrailingComma" ], children: [ Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of this case." ), Child( name: "parameterClause", - deprecatedName: "associatedValue", kind: .node(kind: .enumCaseParameterClause), nameForDiagnostics: "associated values", documentation: "The set of associated values of the case.", @@ -754,6 +793,12 @@ public let DECL_NODES: [Node] = [ documentation: "The trailing comma of this element, if the case has multiple elements.", isOptional: true ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier"), + "parameterClause": .renamed(from: "associatedValue"), + ] ] ), @@ -789,13 +834,12 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), - documentation: "Declares the name of this enum. If the name matches a reserved keyword use backticks to escape it." + documentation: + "Declares the name of this enum. If the name matches a reserved keyword use backticks to escape it." ), Child( name: "genericParameterClause", - deprecatedName: "genericParameters", kind: .node(kind: .genericParameterClause), nameForDiagnostics: "generic parameter clause", documentation: "The generic parameters, if any, for this enum declaration.", @@ -821,6 +865,12 @@ public let DECL_NODES: [Node] = [ documentation: "The cases and other members associated with this enum declaration. Because enum extension declarations may declare additional members the contents of this member block isn't guaranteed to be a complete list of members for this type." ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier"), + "genericParameterClause": .renamed(from: "genericParameters"), + ] ] ), @@ -828,6 +878,19 @@ public let DECL_NODES: [Node] = [ kind: .extensionDecl, base: .decl, nameForDiagnostics: "extension", + documentation: """ + An `extension` declaration. + + ### Example + + ```swift + extension String { + var url: URL? { + URL(string: self) + } + } + ``` + """, traits: [ "DeclGroup", "WithAttributes", @@ -837,7 +900,18 @@ public let DECL_NODES: [Node] = [ Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), - nameForDiagnostics: "attributes" + nameForDiagnostics: "attributes", + documentation: """ + Attributes that are attached to the extension declaration. + + ### Example + + `@MainActor` in + + ```swift + @MainActor extension SomeType {} + ``` + """ ), Child( name: "modifiers", @@ -847,23 +921,27 @@ public let DECL_NODES: [Node] = [ ), Child( name: "extensionKeyword", - kind: .token(choices: [.keyword(.extension)]) + kind: .token(choices: [.keyword(.extension)]), + documentation: "The `extension` keyword." ), Child( name: "extendedType", - kind: .node(kind: .type) + kind: .node(kind: .type), + documentation: "The extended `type` for which the extension is added." ), Child( name: "inheritanceClause", kind: .node(kind: .inheritanceClause), nameForDiagnostics: "inheritance clause", + documentation: "The inheritance clause describing one or more conformances for this extension declaration.", isOptional: true ), Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child( @@ -879,6 +957,27 @@ public let DECL_NODES: [Node] = [ kind: .functionDecl, base: .decl, nameForDiagnostics: "function", + documentation: """ + A Swift `func` declaration. + + ### Example + + A func declaration may be declared without any parameter. + + ```swift + func foo() { + + } + ``` + + A func declaration with multiple parameters. + + ```swift + func bar(_ arg1: Int, _ arg2: Int) { + + } + ``` + """, traits: [ "NamedDecl", "WithAttributes", @@ -890,7 +989,8 @@ public let DECL_NODES: [Node] = [ Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), - nameForDiagnostics: "attributes" + nameForDiagnostics: "attributes", + documentation: "Attributes that are attached to the function declaration." ), Child( name: "modifiers", @@ -900,11 +1000,11 @@ public let DECL_NODES: [Node] = [ ), Child( name: "funcKeyword", - kind: .token(choices: [.keyword(.func)]) + kind: .token(choices: [.keyword(.func)]), + documentation: "The `func` keyword." ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [ .token(.identifier), .token(.binaryOperator), @@ -923,20 +1023,30 @@ public let DECL_NODES: [Node] = [ Child( name: "signature", kind: .node(kind: .functionSignature), - nameForDiagnostics: "function signature" + nameForDiagnostics: "function signature", + documentation: """ + A function signature that defines the interface of the function. + """ ), Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child( name: "body", kind: .node(kind: .codeBlock), + documentation: "The function's body.", isOptional: true ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -944,6 +1054,18 @@ public let DECL_NODES: [Node] = [ kind: .functionParameterList, base: .syntaxCollection, nameForDiagnostics: "parameter list", + documentation: """ + A list of function parameters that are type annotated and a label. + The function parameters are represented by `FunctionParameterListSyntax`. + + ### Example + + ```swift + func foo(bar: Int, baz: Int) { + + } + ``` + """, elementChoices: [.functionParameter] ), @@ -951,55 +1073,70 @@ public let DECL_NODES: [Node] = [ kind: .functionParameter, base: .syntax, nameForDiagnostics: "parameter", + documentation: "A function parameter", parserFunction: "parseFunctionParameter", traits: ["WithTrailingComma", "WithAttributes", "WithModifiers"], children: [ Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), - nameForDiagnostics: "attributes" + nameForDiagnostics: "attributes", + documentation: "Attributes that are attached to the parameter." ), Child( name: "modifiers", kind: .collection(kind: .declModifierList, collectionElementName: "Modifier", defaultsToEmpty: true), - nameForDiagnostics: "modifiers" + nameForDiagnostics: "modifiers", + documentation: "Modifiers that are attached to the parameter." ), Child( name: "firstName", - kind: .token(choices: [.token(.identifier), .token(.wildcard)]) + kind: .token(choices: [.token(.identifier), .token(.wildcard)]), + documentation: "The label of this parameter that will be used when the function is called." ), Child( name: "secondName", kind: .token(choices: [.token(.identifier), .token(.wildcard)], requiresLeadingSpace: true), nameForDiagnostics: "internal name", + documentation: + "If this is specified, it is the name by which the parameter can be referenced inside the function body.", isOptional: true ), Child( name: "colon", - kind: .token(choices: [.token(.colon)]) + kind: .token(choices: [.token(.colon)]), + documentation: "The colon separating the label from the type." ), Child( name: "type", kind: .node(kind: .type), - nameForDiagnostics: "type" + nameForDiagnostics: "type", + documentation: "The parameter's type." ), Child( name: "ellipsis", kind: .token(choices: [.token(.ellipsis)]), + documentation: "If the parameter is variadic, `...` to indicate that.", isOptional: true ), Child( name: "defaultValue", - deprecatedName: "defaultArgument", kind: .node(kind: .initializerClause), nameForDiagnostics: "default value", + documentation: "If the parameter has a default value, the expression describing the default value.", isOptional: true ), Child( name: "trailingComma", kind: .token(choices: [.token(.comma)]), + documentation: "If the parameter is followed by another parameter, the comma separating them.", isOptional: true ), + ], + childHistory: [ + [ + "defaultValue": .renamed(from: "defaultArgument") + ] ] ), @@ -1007,23 +1144,31 @@ public let DECL_NODES: [Node] = [ kind: .functionSignature, base: .syntax, nameForDiagnostics: "function signature", + documentation: "A function signature that defines the interface of the function.", children: [ Child( name: "parameterClause", - deprecatedName: "input", - kind: .node(kind: .functionParameterClause) + kind: .node(kind: .functionParameterClause), + documentation: "The parameters of the function." ), Child( name: "effectSpecifiers", kind: .node(kind: .functionEffectSpecifiers), + documentation: "The effect indicators of the function, like `async` or `throws`", isOptional: true ), Child( name: "returnClause", - deprecatedName: "output", kind: .node(kind: .returnClause), + documentation: "The return type of the function.", isOptional: true ), + ], + childHistory: [ + [ + "parameterClause": .renamed(from: "input"), + "returnClause": .renamed(from: "output"), + ] ] ), @@ -1122,17 +1267,16 @@ public let DECL_NODES: [Node] = [ name: "modifiers", kind: .collection(kind: .declModifierList, collectionElementName: "Modifier", defaultsToEmpty: true), nameForDiagnostics: "modifiers", - documentation: "Modifiers that are attached to the import declaration. Currently, no modifiers are supported by Swift." + documentation: + "Modifiers that are attached to the import declaration. Currently, no modifiers are supported by Swift." ), Child( name: "importKeyword", - deprecatedName: "importTok", kind: .token(choices: [.keyword(.import)]), documentation: "The `import` keyword for this declaration." ), Child( name: "importKindSpecifier", - deprecatedName: "importKind", kind: .token(choices: [ .keyword(.typealias), .keyword(.struct), @@ -1156,6 +1300,43 @@ public let DECL_NODES: [Node] = [ kind: .collection(kind: .importPathComponentList, collectionElementName: "PathComponent"), documentation: "The path to the module, submodule or symbol being imported." ), + ], + childHistory: [ + [ + "importKeyword": .renamed(from: "importTok"), + "importKindSpecifier": .renamed(from: "importKind"), + ] + ] + ), + + Node( + kind: .usingDecl, + base: .decl, + experimentalFeature: .defaultIsolationPerFile, + nameForDiagnostics: "using", + documentation: """ + A `using` declaration, currently used to control actor isolation within the current file. + + An example of a `using` declaration is + + ```swift + using @MainActor + ``` + """, + children: [ + Child( + name: "usingKeyword", + kind: .token(choices: [.keyword(.using)]), + documentation: "The `using` keyword for this declaration." + ), + Child( + name: "specifier", + kind: .nodeChoices(choices: [ + Child(name: "attribute", kind: .node(kind: .attribute)), + Child(name: "modifier", kind: .token(choices: [.token(.identifier)])), + ]), + documentation: "The specifier that could be either an attribute or a modifier." + ), ] ), @@ -1176,7 +1357,6 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "type", - deprecatedName: "typeName", kind: .node(kind: .type) ), Child( @@ -1184,6 +1364,11 @@ public let DECL_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "type": .renamed(from: "typeName") + ] ] ), @@ -1263,7 +1448,8 @@ public let DECL_NODES: [Node] = [ name: "signature", kind: .node(kind: .functionSignature), nameForDiagnostics: "function signature", - documentation: "The arguments of the initializer. While the function signature allows specifying a return clause, doing so is not semantically valid." + documentation: + "The arguments of the initializer. While the function signature allows specifying a return clause, doing so is not semantically valid." ), Child( name: "genericWhereClause", @@ -1308,7 +1494,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]) ), Child( @@ -1333,9 +1518,15 @@ public let DECL_NODES: [Node] = [ name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -1362,18 +1553,20 @@ public let DECL_NODES: [Node] = [ ), Child( name: "pound", - deprecatedName: "poundToken", kind: .token(choices: [.token(.pound)]), documentation: "The `#` sign." ), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "macroName", - deprecatedName: "macro", kind: .token(choices: [.token(.identifier)]) ), Child( name: "genericArgumentClause", - deprecatedName: "genericArguments", kind: .node(kind: .genericArgumentClause), isOptional: true ), @@ -1384,7 +1577,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "argumentList", kind: .collection(kind: .labeledExprList, collectionElementName: "Argument") ), Child( @@ -1399,8 +1591,23 @@ public let DECL_NODES: [Node] = [ ), Child( name: "additionalTrailingClosures", - kind: .collection(kind: .multipleTrailingClosureElementList, collectionElementName: "AdditionalTrailingClosure", defaultsToEmpty: true) + kind: .collection( + kind: .multipleTrailingClosureElementList, + collectionElementName: "AdditionalTrailingClosure", + defaultsToEmpty: true + ) ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ], + [ + "pound": .renamed(from: "poundToken"), + "macroName": .renamed(from: "macro"), + "genericArgumentClause": .renamed(from: "genericArguments"), + "arguments": .renamed(from: "argumentList"), + ], ] ), @@ -1471,7 +1678,6 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "fixitySpecifier", - deprecatedName: "fixity", kind: .token(choices: [.keyword(.prefix), .keyword(.postfix), .keyword(.infix)]), nameForDiagnostics: "fixity", documentation: "The fixity applied to the 'operator' declaration." @@ -1482,7 +1688,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.binaryOperator), .token(.prefixOperator), .token(.postfixOperator)]) ), Child( @@ -1491,6 +1696,12 @@ public let DECL_NODES: [Node] = [ documentation: "Optionally specify a precedence group and designated types.", isOptional: true ), + ], + childHistory: [ + [ + "fixitySpecifier": .renamed(from: "fixity"), + "name": .renamed(from: "identifier"), + ] ] ), @@ -1498,7 +1709,8 @@ public let DECL_NODES: [Node] = [ kind: .operatorPrecedenceAndTypes, base: .syntax, nameForDiagnostics: nil, - documentation: "A clause to specify precedence group in infix operator declarations, and designated types in any operator declaration.", + documentation: + "A clause to specify precedence group in infix operator declarations, and designated types in any operator declaration.", children: [ Child( name: "colon", @@ -1532,7 +1744,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "parameters", - deprecatedName: "parameterList", kind: .collection(kind: .functionParameterList, collectionElementName: "Parameter"), nameForDiagnostics: "parameters" ), @@ -1540,6 +1751,11 @@ public let DECL_NODES: [Node] = [ name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "parameters": .renamed(from: "parameterList") + ] ] ), @@ -1599,7 +1815,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "accessorBlock", - deprecatedName: "accessor", kind: .node(kind: .accessorBlock), documentation: """ If the variable is computed, the accessors that get (and optionally set) the value. @@ -1611,6 +1826,11 @@ public let DECL_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "accessorBlock": .renamed(from: "accessor") + ] ] ), @@ -1621,12 +1841,10 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "fileLabel", - deprecatedName: "fileArgLabel", kind: .token(choices: [.keyword(.file)]) ), Child( name: "fileColon", - deprecatedName: "fileArgColon", kind: .token(choices: [.token(.colon)]) ), Child( @@ -1640,12 +1858,10 @@ public let DECL_NODES: [Node] = [ ), Child( name: "lineLabel", - deprecatedName: "lineArgLabel", kind: .token(choices: [.keyword(.line)]) ), Child( name: "lineColon", - deprecatedName: "lineArgColon", kind: .token(choices: [.token(.colon)]) ), Child( @@ -1653,6 +1869,14 @@ public let DECL_NODES: [Node] = [ kind: .token(choices: [.token(.integerLiteral)]), nameForDiagnostics: "line number" ), + ], + childHistory: [ + [ + "fileLabel": .renamed(from: "fileArgLabel"), + "fileColon": .renamed(from: "fileArgColon"), + "lineLabel": .renamed(from: "lineArgLabel"), + "lineColon": .renamed(from: "lineArgColon"), + ] ] ), @@ -1674,7 +1898,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "args", kind: .node(kind: .poundSourceLocationArguments), nameForDiagnostics: "arguments", isOptional: true @@ -1683,6 +1906,11 @@ public let DECL_NODES: [Node] = [ name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "arguments": .renamed(from: "args") + ] ] ), @@ -1694,7 +1922,6 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "assignmentLabel", - deprecatedName: "assignmentKeyword", kind: .token(choices: [.keyword(.assignment)]) ), Child( @@ -1703,11 +1930,16 @@ public let DECL_NODES: [Node] = [ ), Child( name: "value", - deprecatedName: "flag", kind: .token(choices: [.keyword(.true), .keyword(.false)]), documentation: "When true, an operator in the corresponding precedence group uses the same grouping rules during optional chaining as the assignment operators from the standard library. Otherwise, operators in the precedence group follows the same optional chaining rules as operators that don't perform assignment." ), + ], + childHistory: [ + [ + "assignmentLabel": .renamed(from: "assignmentKeyword"), + "value": .renamed(from: "flag"), + ] ] ), @@ -1715,11 +1947,11 @@ public let DECL_NODES: [Node] = [ kind: .precedenceGroupAssociativity, base: .syntax, nameForDiagnostics: "'associativity' property of precedencegroup", - documentation: "Specifies how a sequence of operators with the same precedence level are grouped together in the absence of grouping parentheses.", + documentation: + "Specifies how a sequence of operators with the same precedence level are grouped together in the absence of grouping parentheses.", children: [ Child( name: "associativityLabel", - deprecatedName: "associativityKeyword", kind: .token(choices: [.keyword(.associativity)]) ), Child( @@ -1732,6 +1964,11 @@ public let DECL_NODES: [Node] = [ documentation: "Operators that are `left`-associative group left-to-right. Operators that are `right`-associative group right-to-left. Operators that are specified with an associativity of `none` don't associate at all" ), + ], + childHistory: [ + [ + "associativityLabel": .renamed(from: "associativityKeyword") + ] ] ), Node( @@ -1771,7 +2008,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of this precedence group." ), @@ -1788,6 +2024,11 @@ public let DECL_NODES: [Node] = [ name: "rightBrace", kind: .token(choices: [.token(.rightBrace)]) ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -1825,7 +2066,6 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "higherThanOrLowerThanLabel", - deprecatedName: "higherThanOrLowerThan", kind: .token(choices: [.keyword(.higherThan), .keyword(.lowerThan)]), documentation: "The relation to specified other precedence groups." ), @@ -1835,10 +2075,15 @@ public let DECL_NODES: [Node] = [ ), Child( name: "precedenceGroups", - deprecatedName: "otherNames", kind: .collection(kind: .precedenceGroupNameList, collectionElementName: "OtherName"), documentation: "The name of other precedence group to which this precedence group relates." ), + ], + childHistory: [ + [ + "higherThanOrLowerThanLabel": .renamed(from: "higherThanOrLowerThan"), + "precedenceGroups": .renamed(from: "otherNames"), + ] ] ), @@ -1883,7 +2128,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), documentation: "The name of the protocol." ), @@ -1913,6 +2157,11 @@ public let DECL_NODES: [Node] = [ kind: .node(kind: .memberBlock), documentation: "The members of the protocol declaration." ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -1923,14 +2172,20 @@ public let DECL_NODES: [Node] = [ children: [ Child( name: "arrow", - kind: .token(choices: [.token(.arrow)]) + kind: .token(choices: [.token(.arrow)]), + documentation: "If return type is presented, the arrow introducing the return type." ), Child( name: "type", - deprecatedName: "returnType", kind: .node(kind: .type), - nameForDiagnostics: "return type" + nameForDiagnostics: "return type", + documentation: "The `return` type." ), + ], + childHistory: [ + [ + "type": .renamed(from: "returnType") + ] ] ), @@ -1953,9 +2208,13 @@ public let DECL_NODES: [Node] = [ ), Child( name: "endOfFileToken", - deprecatedName: "eofToken", kind: .token(choices: [.token(.endOfFile)]) ), + ], + childHistory: [ + [ + "endOfFileToken": .renamed(from: "eofToken") + ] ] ), @@ -2049,9 +2308,9 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]), - documentation: "Declares the name of this struct. If the name matches a reserved keyword use backticks to escape it." + documentation: + "Declares the name of this struct. If the name matches a reserved keyword use backticks to escape it." ), Child( name: "genericParameterClause", @@ -2064,7 +2323,8 @@ public let DECL_NODES: [Node] = [ name: "inheritanceClause", kind: .node(kind: .inheritanceClause), nameForDiagnostics: "inheritance clause", - documentation: "The struct declaration inheritance clause describing one or more conformances for this struct declaration.", + documentation: + "The struct declaration inheritance clause describing one or more conformances for this struct declaration.", isOptional: true ), Child( @@ -2080,6 +2340,11 @@ public let DECL_NODES: [Node] = [ documentation: "The members of the struct declaration. Because struct extension declarations may declare additional members the contents of this member block isn't guaranteed to be a complete list of members for this type." ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -2116,27 +2381,32 @@ public let DECL_NODES: [Node] = [ ), Child( name: "parameterClause", - deprecatedName: "indices", kind: .node(kind: .functionParameterClause) ), Child( name: "returnClause", - deprecatedName: "result", kind: .node(kind: .returnClause) ), Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child( name: "accessorBlock", - deprecatedName: "accessor", kind: .node(kind: .accessorBlock), isOptional: true ), + ], + childHistory: [ + [ + "parameterClause": .renamed(from: "indices"), + "returnClause": .renamed(from: "result"), + "accessorBlock": .renamed(from: "accessor"), + ] ] ), @@ -2151,9 +2421,13 @@ public let DECL_NODES: [Node] = [ ), Child( name: "inheritedTypes", - deprecatedName: "inheritedTypeCollection", kind: .collection(kind: .inheritedTypeList, collectionElementName: "InheritedType") ), + ], + childHistory: [ + [ + "inheritedTypes": .renamed(from: "inheritedTypeCollection") + ] ] ), @@ -2201,7 +2475,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "name", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]) ), Child( @@ -2219,9 +2492,15 @@ public let DECL_NODES: [Node] = [ name: "genericWhereClause", kind: .node(kind: .genericWhereClause), nameForDiagnostics: "generic where clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), + ], + childHistory: [ + [ + "name": .renamed(from: "identifier") + ] ] ), @@ -2253,7 +2532,6 @@ public let DECL_NODES: [Node] = [ ), Child( name: "bindingSpecifier", - deprecatedName: "bindingKeyword", kind: .token(choices: [ .keyword(.let), .keyword(.var), .keyword(.inout), .keyword(._mutating), .keyword(._borrowing), .keyword(._consuming), @@ -2279,6 +2557,11 @@ public let DECL_NODES: [Node] = [ ``` """ ), + ], + childHistory: [ + [ + "bindingSpecifier": .renamed(from: "bindingKeyword") + ] ] ), ] diff --git a/Sources/CodeGeneration/SyntaxSupport/ExperimentalFeatures.swift b/Sources/CodeGeneration/SyntaxSupport/ExperimentalFeatures.swift index c1ad579..e6c5a03 100644 --- a/Sources/CodeGeneration/SyntaxSupport/ExperimentalFeatures.swift +++ b/Sources/CodeGeneration/SyntaxSupport/ExperimentalFeatures.swift @@ -17,11 +17,41 @@ public enum ExperimentalFeature: String, CaseIterable { case thenStatements case doExpressions case nonescapableTypes - case transferringArgsAndResults - case borrowingSwitch + case trailingComma + case coroutineAccessors + case keypathWithMethodMembers + case oldOwnershipOperatorSpellings + case defaultIsolationPerFile + case borrowAndMutateAccessors - /// The name of the feature, which is used in the doc comment. + /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { + switch self { + case .referenceBindings: + return "ReferenceBindings" + case .thenStatements: + return "ThenStatements" + case .doExpressions: + return "DoExpressions" + case .nonescapableTypes: + return "NonescapableTypes" + case .trailingComma: + return "TrailingComma" + case .coroutineAccessors: + return "CoroutineAccessors" + case .keypathWithMethodMembers: + return "KeypathWithMethodMembers" + case .oldOwnershipOperatorSpellings: + return "OldOwnershipOperatorSpellings" + case .defaultIsolationPerFile: + return "DefaultIsolationPerFile" + case .borrowAndMutateAccessors: + return "BorrowAndMutateAccessors" + } + } + + /// A brief description of the feature that is used in the doc comment. + public var documentationDescription: String { switch self { case .referenceBindings: return "reference bindings" @@ -30,11 +60,19 @@ public enum ExperimentalFeature: String, CaseIterable { case .doExpressions: return "'do' expressions" case .nonescapableTypes: - return "NonEscableTypes" - case .transferringArgsAndResults: - return "TransferringArgsAndResults" - case .borrowingSwitch: - return "borrowing pattern matching" + return "non-escapable types" + case .trailingComma: + return "trailing commas" + case .coroutineAccessors: + return "coroutine accessors" + case .keypathWithMethodMembers: + return "keypaths with method members" + case .oldOwnershipOperatorSpellings: + return "`_move` and `_borrow` as ownership operators" + case .defaultIsolationPerFile: + return "set default actor isolation for a file" + case .borrowAndMutateAccessors: + return "borrow and mutate accessors" } } diff --git a/Sources/CodeGeneration/SyntaxSupport/ExprNodes.swift b/Sources/CodeGeneration/SyntaxSupport/ExprNodes.swift index 525687a..4920cc7 100644 --- a/Sources/CodeGeneration/SyntaxSupport/ExprNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/ExprNodes.swift @@ -48,7 +48,6 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "leftSquare", - deprecatedName: "leftSquareBracket", kind: .token(choices: [.token(.leftSquare)]) ), Child( @@ -57,9 +56,14 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "rightSquare", - deprecatedName: "rightSquareBracket", kind: .token(choices: [.token(.rightSquare)]) ), + ], + childHistory: [ + [ + "leftSquare": .renamed(from: "leftSquareBracket"), + "rightSquare": .renamed(from: "rightSquareBracket"), + ] ] ), @@ -86,9 +90,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "arrow", - deprecatedName: "arrowToken", kind: .token(choices: [.token(.arrow)]) ), + ], + childHistory: [ + [ + "arrow": .renamed(from: "arrowToken") + ] ] ), @@ -97,7 +105,7 @@ public let EXPR_NODES: [Node] = [ base: .expr, nameForDiagnostics: "'as'", documentation: """ - The cast of an expressison to a different type. + The cast of an expression to a different type. ### Examples @@ -119,7 +127,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "asKeyword", - deprecatedName: "asTok", kind: .token(choices: [.keyword(.as)]) ), Child( @@ -129,9 +136,14 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "type", - deprecatedName: "typeName", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "asKeyword": .renamed(from: "asTok"), + "type": .renamed(from: "typeName"), + ] ] ), @@ -142,9 +154,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "equal", - deprecatedName: "assignToken", kind: .token(choices: [.token(.equal)]) ) + ], + childHistory: [ + [ + "equal": .renamed(from: "assignToken") + ] ] ), @@ -164,6 +180,22 @@ public let EXPR_NODES: [Node] = [ ] ), + Node( + kind: .unsafeExpr, + base: .expr, + nameForDiagnostics: "'unsafe' expression", + children: [ + Child( + name: "unsafeKeyword", + kind: .token(choices: [.keyword(.unsafe)]) + ), + Child( + name: "expression", + kind: .node(kind: .expr) + ), + ] + ), + Node( kind: .binaryOperatorExpr, base: .expr, @@ -178,9 +210,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "operator", - deprecatedName: "operatorToken", kind: .token(choices: [.token(.binaryOperator)]) ) + ], + childHistory: [ + [ + "operator": .renamed(from: "operatorToken") + ] ] ), @@ -191,9 +227,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "literal", - deprecatedName: "booleanLiteral", kind: .token(choices: [.keyword(.true), .keyword(.false)]) ) + ], + childHistory: [ + [ + "literal": .renamed(from: "booleanLiteral") + ] ] ), @@ -204,7 +244,7 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "borrowKeyword", - kind: .token(choices: [.keyword(._borrow)]) + kind: .token(choices: [.keyword(._borrow), .keyword(.borrow)]) ), Child( name: "expression", @@ -214,7 +254,7 @@ public let EXPR_NODES: [Node] = [ ), Node( - kind: .canImportExpr, + kind: ._canImportExpr, base: .expr, nameForDiagnostics: "'canImport' expression", children: [ @@ -232,7 +272,7 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "versionInfo", - kind: .node(kind: .canImportVersionInfo), + kind: .node(kind: ._canImportVersionInfo), isOptional: true ), Child( @@ -243,7 +283,7 @@ public let EXPR_NODES: [Node] = [ ), Node( - kind: .canImportVersionInfo, + kind: ._canImportVersionInfo, base: .expr, nameForDiagnostics: nil, children: [ @@ -261,9 +301,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "version", - deprecatedName: "versionTuple", kind: .node(kind: .versionTuple) ), + ], + childHistory: [ + [ + "version": .renamed(from: "versionTuple") + ] ] ), @@ -341,19 +385,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "name", - kind: .token(choices: [.token(.identifier)]), - isOptional: true + kind: .token(choices: [.token(.identifier), .keyword(.self)]) ), Child( - name: "equal", - deprecatedName: "assignToken", - kind: .token(choices: [.token(.equal)]), + name: "initializer", + kind: .node(kind: .initializerClause), isOptional: true ), - Child( - name: "expression", - kind: .node(kind: .expr) - ), Child( name: "trailingComma", kind: .token(choices: [.token(.comma)]), @@ -461,7 +499,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "parameters", - deprecatedName: "parameterList", kind: .collection(kind: .closureParameterList, collectionElementName: "Parameter"), nameForDiagnostics: "parameters", documentation: "The actual parameters." @@ -471,6 +508,11 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.rightParen)]), documentation: "The ')' to close the parameter clause." ), + ], + childHistory: [ + [ + "parameters": .renamed(from: "parameterList") + ] ] ), @@ -565,18 +607,23 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "parameterClause", - deprecatedName: "input", - kind: .nodeChoices(choices: [ - Child( - name: "simpleInput", - kind: .node(kind: .closureShorthandParameterList) - ), - Child( - name: "parameterClause", - deprecatedName: "input", - kind: .node(kind: .closureParameterClause) - ), - ]), + kind: .nodeChoices( + choices: [ + Child( + name: "simpleInput", + kind: .node(kind: .closureShorthandParameterList) + ), + Child( + name: "parameterClause", + kind: .node(kind: .closureParameterClause) + ), + ], + childHistory: [ + [ + "parameterClause": .renamed(from: "input") + ] + ] + ), isOptional: true ), Child( @@ -586,15 +633,20 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "returnClause", - deprecatedName: "output", kind: .node(kind: .returnClause), isOptional: true ), Child( name: "inKeyword", - deprecatedName: "inTok", kind: .token(choices: [.keyword(.in)]) ), + ], + childHistory: [ + [ + "parameterClause": .renamed(from: "input"), + "returnClause": .renamed(from: "output"), + "inKeyword": .renamed(from: "inTok"), + ] ] ), @@ -662,7 +714,6 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "key", - deprecatedName: "keyExpression", kind: .node(kind: .expr), nameForDiagnostics: "key" ), @@ -672,7 +723,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "value", - deprecatedName: "valueExpression", kind: .node(kind: .expr), nameForDiagnostics: "value" ), @@ -681,6 +731,12 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "key": .renamed(from: "keyExpression"), + "value": .renamed(from: "valueExpression"), + ] ] ), @@ -798,9 +854,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "placeholder", - deprecatedName: "identifier", kind: .token(choices: [.token(.identifier)]) ) + ], + childHistory: [ + [ + "placeholder": .renamed(from: "identifier") + ] ] ), @@ -831,7 +891,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "pounds", - deprecatedName: "delimiter", kind: .token(choices: [.token(.rawStringPoundDelimiter)]), isOptional: true ), @@ -847,6 +906,11 @@ public let EXPR_NODES: [Node] = [ name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "pounds": .renamed(from: "delimiter") + ] ] ), @@ -857,9 +921,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "literal", - deprecatedName: "floatingDigits", kind: .token(choices: [.token(.floatLiteral)]) ) + ], + childHistory: [ + [ + "literal": .renamed(from: "floatingDigits") + ] ] ), @@ -892,18 +960,19 @@ public let EXPR_NODES: [Node] = [ Child( name: "leftParen", kind: .token(choices: [.token(.leftParen)]), - isOptional: true + isOptional: true, + providesDefaultInitialization: false ), Child( name: "arguments", - deprecatedName: "argumentList", kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"), nameForDiagnostics: "arguments" ), Child( name: "rightParen", kind: .token(choices: [.token(.rightParen)]), - isOptional: true + isOptional: true, + providesDefaultInitialization: false ), Child( name: "trailingClosure", @@ -913,9 +982,18 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "additionalTrailingClosures", - kind: .collection(kind: .multipleTrailingClosureElementList, collectionElementName: "AdditionalTrailingClosure", defaultsToEmpty: true), + kind: .collection( + kind: .multipleTrailingClosureElementList, + collectionElementName: "AdditionalTrailingClosure", + defaultsToEmpty: true + ), nameForDiagnostics: "trailing closures" ), + ], + childHistory: [ + [ + "arguments": .renamed(from: "argumentList") + ] ] ), @@ -924,9 +1002,13 @@ public let EXPR_NODES: [Node] = [ base: .expr, nameForDiagnostics: nil, children: [ + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "baseName", - deprecatedName: "identifier", kind: .token(choices: [ .token(.identifier), .keyword(.self), @@ -941,10 +1023,18 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "argumentNames", - deprecatedName: "declNameArguments", kind: .node(kind: .declNameArguments), isOptional: true ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ], + [ + "baseName": .renamed(from: "identifier"), + "argumentNames": .renamed(from: "declNameArguments"), + ], ] ), @@ -1027,13 +1117,17 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "operator", - deprecatedName: "operatorOperand", kind: .node(kind: .expr) ), Child( name: "rightOperand", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "operator": .renamed(from: "operatorOperand") + ] ] ), @@ -1044,9 +1138,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "literal", - deprecatedName: "digits", kind: .token(choices: [.token(.integerLiteral)]) ) + ], + childHistory: [ + [ + "literal": .renamed(from: "digits") + ] ] ), @@ -1074,16 +1172,21 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "isKeyword", - deprecatedName: "isTok", kind: .token(choices: [.keyword(.is)]), documentation: "The `is` keyword for this expression." ), Child( name: "type", - deprecatedName: "typeName", kind: .node(kind: .type), - documentation: "The type against which the expression will be checked to see if the expression can be cast to it." + documentation: + "The type against which the expression will be checked to see if the expression can be cast to it." ), + ], + childHistory: [ + [ + "isKeyword": .renamed(from: "isTok"), + "type": .renamed(from: "typeName"), + ] ] ), @@ -1113,6 +1216,11 @@ public let EXPR_NODES: [Node] = [ name: "property", kind: .node(kind: .keyPathPropertyComponent) ), + Child( + name: "method", + kind: .node(kind: .keyPathMethodComponent), + experimentalFeature: .keypathWithMethodMembers + ), Child( name: "subscript", kind: .node(kind: .keyPathSubscriptComponent) @@ -1196,20 +1304,51 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "leftSquare", - deprecatedName: "leftBracket", kind: .token(choices: [.token(.leftSquare)]) ), Child( name: "arguments", - deprecatedName: "argumentList", kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"), nameForDiagnostics: "arguments" ), Child( name: "rightSquare", - deprecatedName: "rightBracket", kind: .token(choices: [.token(.rightSquare)]) ), + ], + childHistory: [ + [ + "leftSquare": .renamed(from: "leftBracket"), + "arguments": .renamed(from: "argumentList"), + "rightSquare": .renamed(from: "rightBracket"), + ] + ] + ), + + Node( + kind: .keyPathMethodComponent, + base: .syntax, + experimentalFeature: .keypathWithMethodMembers, + nameForDiagnostics: "key path method component", + documentation: "A key path component like `.method()`, `.method(10)`, or `.method(val: 10)`.", + children: [ + Child( + name: "declName", + kind: .node(kind: .declReferenceExpr) + ), + Child( + name: "leftParen", + kind: .token(choices: [.token(.leftParen)]) + ), + Child( + name: "arguments", + kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"), + nameForDiagnostics: "arguments" + ), + Child( + name: "rightParen", + kind: .token(choices: [.token(.rightParen)]) + ), ] ), @@ -1224,18 +1363,20 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "pound", - deprecatedName: "poundToken", kind: .token(choices: [.token(.pound)]), documentation: "The `#` sign." ), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "macroName", - deprecatedName: "macro", kind: .token(choices: [.token(.identifier)]) ), Child( name: "genericArgumentClause", - deprecatedName: "genericArguments", kind: .node(kind: .genericArgumentClause), isOptional: true ), @@ -1246,7 +1387,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "arguments", - deprecatedName: "argumentList", kind: .collection(kind: .labeledExprList, collectionElementName: "Argument") ), Child( @@ -1261,8 +1401,23 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "additionalTrailingClosures", - kind: .collection(kind: .multipleTrailingClosureElementList, collectionElementName: "AdditionalTrailingClosure", defaultsToEmpty: true) + kind: .collection( + kind: .multipleTrailingClosureElementList, + collectionElementName: "AdditionalTrailingClosure", + defaultsToEmpty: true + ) ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ], + [ + "pound": .renamed(from: "poundToken"), + "macroName": .renamed(from: "macro"), + "genericArgumentClause": .renamed(from: "genericArguments"), + "arguments": .renamed(from: "argumentList"), + ], ] ), @@ -1281,7 +1436,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "period", - deprecatedName: "dot", kind: .token(choices: [.token(.period)]) ), Child( @@ -1290,6 +1444,11 @@ public let EXPR_NODES: [Node] = [ nameForDiagnostics: "name", documentation: "The name of the referenced function or a property." ), + ], + childHistory: [ + [ + "period": .renamed(from: "dot") + ] ] ), @@ -1300,13 +1459,17 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "consumeKeyword", - deprecatedName: "moveKeyword", kind: .token(choices: [.keyword(._move), .keyword(.consume)]) ), Child( name: "expression", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "consumeKeyword": .renamed(from: "moveKeyword") + ] ] ), @@ -1394,9 +1557,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "pack", - deprecatedName: "packRefExpr", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "pack": .renamed(from: "packRefExpr") + ] ] ), @@ -1412,9 +1579,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "repetitionPattern", - deprecatedName: "patternExpr", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "repetitionPattern": .renamed(from: "patternExpr") + ] ] ), @@ -1446,9 +1617,13 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "operator", - deprecatedName: "operatorToken", kind: .token(choices: [.token(.postfixOperator)]) ), + ], + childHistory: [ + [ + "operator": .renamed(from: "operatorToken") + ] ] ), @@ -1472,14 +1647,18 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "operator", - deprecatedName: "operatorToken", kind: .token(choices: [.token(.prefixOperator)]) ), Child( name: "expression", - deprecatedName: "postfixExpression", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "operator": .renamed(from: "operatorToken"), + "expression": .renamed(from: "postfixExpression"), + ] ] ), @@ -1495,17 +1674,14 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "openingSlash", - deprecatedName: "openSlash", kind: .token(choices: [.token(.regexSlash)]) ), Child( name: "regex", - deprecatedName: "regexPattern", kind: .token(choices: [.token(.regexLiteralPattern)]) ), Child( name: "closingSlash", - deprecatedName: "closeSlash", kind: .token(choices: [.token(.regexSlash)]) ), Child( @@ -1513,6 +1689,13 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.regexPoundDelimiter)]), isOptional: true ), + ], + childHistory: [ + [ + "openingSlash": .renamed(from: "openSlash"), + "regex": .renamed(from: "regexPattern"), + "closingSlash": .renamed(from: "closeSlash"), + ] ] ), @@ -1569,13 +1752,11 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "openingPounds", - deprecatedName: "openDelimiter", kind: .token(choices: [.token(.rawStringPoundDelimiter)]), isOptional: true ), Child( name: "openingQuote", - deprecatedName: "openQuote", kind: .token(choices: [.token(.stringQuote), .token(.multilineStringQuote), .token(.singleQuote)]) ), Child( @@ -1584,15 +1765,21 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "closingQuote", - deprecatedName: "closeQuote", kind: .token(choices: [.token(.stringQuote), .token(.multilineStringQuote), .token(.singleQuote)]) ), Child( name: "closingPounds", - deprecatedName: "closeDelimiter", kind: .token(choices: [.token(.rawStringPoundDelimiter)]), isOptional: true ), + ], + childHistory: [ + [ + "openingPounds": .renamed(from: "openDelimiter"), + "openingQuote": .renamed(from: "openQuote"), + "closingQuote": .renamed(from: "closeQuote"), + "closingPounds": .renamed(from: "closeDelimiter"), + ] ] ), @@ -1664,18 +1851,15 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "leftSquare", - deprecatedName: "leftBracket", kind: .token(choices: [.token(.leftSquare)]) ), Child( name: "arguments", - deprecatedName: "argumentList", kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"), nameForDiagnostics: "arguments" ), Child( name: "rightSquare", - deprecatedName: "rightBracket", kind: .token(choices: [.token(.rightSquare)]) ), Child( @@ -1686,9 +1870,20 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "additionalTrailingClosures", - kind: .collection(kind: .multipleTrailingClosureElementList, collectionElementName: "AdditionalTrailingClosure", defaultsToEmpty: true), + kind: .collection( + kind: .multipleTrailingClosureElementList, + collectionElementName: "AdditionalTrailingClosure", + defaultsToEmpty: true + ), nameForDiagnostics: "trailing closures" ), + ], + childHistory: [ + [ + "leftSquare": .renamed(from: "leftBracket"), + "arguments": .renamed(from: "argumentList"), + "rightSquare": .renamed(from: "rightBracket"), + ] ] ), @@ -1740,7 +1935,6 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "attribute", - deprecatedName: "unknownAttr", kind: .node(kind: .attribute), documentation: "The `@unknown` attribute of a default label, if present.", isOptional: true @@ -1763,6 +1957,11 @@ public let EXPR_NODES: [Node] = [ name: "statements", kind: .collection(kind: .codeBlockItemList, collectionElementName: "Statement") ), + ], + childHistory: [ + [ + "attribute": .renamed(from: "unknownAttr") + ] ] ), @@ -1798,7 +1997,7 @@ public let EXPR_NODES: [Node] = [ } ``` - A switch ecpression may be declared without any cases. + A switch expression may be declared without any cases. """, traits: [ "Braced" @@ -1811,7 +2010,6 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "subject", - deprecatedName: "expression", kind: .node(kind: .expr), documentation: "The expression to switch over." ), @@ -1830,13 +2028,18 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.rightBrace)]), documentation: "The brace closing the switch body." ), + ], + childHistory: [ + [ + "subject": .renamed(from: "expression") + ] ] ), Node( kind: .ternaryExpr, base: .expr, - nameForDiagnostics: "ternay expression", + nameForDiagnostics: "ternary expression", documentation: """ The ternary operator with operator precedences resolved. @@ -1852,7 +2055,6 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "condition", - deprecatedName: "conditionExpression", kind: .node(kind: .expr), nameForDiagnostics: "condition" ), @@ -1862,21 +2064,26 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "thenExpression", - deprecatedName: "firstChoice", kind: .node(kind: .expr), nameForDiagnostics: "first choice" ), Child( name: "colon", - deprecatedName: "colonMark", kind: .token(choices: [.token(.colon)]) ), Child( name: "elseExpression", - deprecatedName: "secondChoice", kind: .node(kind: .expr), nameForDiagnostics: "second choice" ), + ], + childHistory: [ + [ + "condition": .renamed(from: "conditionExpression"), + "thenExpression": .renamed(from: "firstChoice"), + "colon": .renamed(from: "colonMark"), + "elseExpression": .renamed(from: "secondChoice"), + ] ] ), @@ -1978,13 +2185,17 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "elements", - deprecatedName: "elementList", kind: .collection(kind: .labeledExprList, collectionElementName: "Element") ), Child( name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "elements": .renamed(from: "elementList") + ] ] ), @@ -2013,7 +2224,6 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "asKeyword", - deprecatedName: "asTok", kind: .token(choices: [.keyword(.as)]) ), Child( @@ -2021,6 +2231,11 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.postfixQuestionMark), .token(.exclamationMark)]), isOptional: true ), + ], + childHistory: [ + [ + "asKeyword": .renamed(from: "asTok") + ] ] ), @@ -2037,9 +2252,13 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "isKeyword", - deprecatedName: "isTok", kind: .token(choices: [.keyword(.is)]) ) + ], + childHistory: [ + [ + "isKeyword": .renamed(from: "isTok") + ] ] ), @@ -2073,14 +2292,18 @@ public let EXPR_NODES: [Node] = [ ), Child( name: "thenExpression", - deprecatedName: "firstChoice", kind: .node(kind: .expr) ), Child( name: "colon", - deprecatedName: "colonMark", kind: .token(choices: [.token(.colon)]) ), + ], + childHistory: [ + [ + "thenExpression": .renamed(from: "firstChoice"), + "colon": .renamed(from: "colonMark"), + ] ] ), diff --git a/Sources/CodeGeneration/SyntaxSupport/GenericNodes.swift b/Sources/CodeGeneration/SyntaxSupport/GenericNodes.swift index b2b8657..c6254e9 100644 --- a/Sources/CodeGeneration/SyntaxSupport/GenericNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/GenericNodes.swift @@ -18,7 +18,6 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "leftType", - deprecatedName: "leftTypeIdentifier", kind: .node(kind: .type) ), Child( @@ -27,9 +26,14 @@ public let GENERIC_NODES: [Node] = [ ), Child( name: "rightType", - deprecatedName: "rightTypeIdentifier", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "leftType": .renamed(from: "leftTypeIdentifier"), + "rightType": .renamed(from: "rightTypeIdentifier"), + ] ] ), @@ -42,28 +46,37 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "leftAngle", - deprecatedName: "leftAngleBracket", kind: .token(choices: [.token(.leftAngle)]), documentation: "The opening angle bracket (`<`) of the generic parameter clause." ), Child( name: "parameters", - deprecatedName: "genericParameterList", - kind: .collection(kind: .genericParameterList, collectionElementName: "Parameter", deprecatedCollectionElementName: "GenericParameter"), + kind: .collection( + kind: .genericParameterList, + collectionElementName: "Parameter", + deprecatedCollectionElementName: "GenericParameter" + ), documentation: "The list of generic parameters in the clause." ), Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child( name: "rightAngle", - deprecatedName: "rightAngleBracket", kind: .token(choices: [.token(.rightAngle)]), documentation: "The closing angle bracket (`>`) of the generic parameter clause." ), + ], + childHistory: [ + [ + "leftAngle": .renamed(from: "leftAngleBracket"), + "parameters": .renamed(from: "genericParameterList"), + "rightAngle": .renamed(from: "rightAngleBracket"), + ] ] ), @@ -88,10 +101,12 @@ public let GENERIC_NODES: [Node] = [ kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true) ), Child( - name: "eachKeyword", - deprecatedName: "each", - kind: .token(choices: [.keyword(.each)]), - nameForDiagnostics: "parameter pack specifier", + name: "specifier", + kind: .token(choices: [ + .keyword(.each), + .keyword(.let), + ]), + nameForDiagnostics: "specifier", isOptional: true ), Child( @@ -115,6 +130,11 @@ public let GENERIC_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "specifier": .renamed(from: "eachKeyword") + ] ] ), @@ -135,7 +155,6 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "requirement", - deprecatedName: "body", kind: .nodeChoices(choices: [ Child( name: "sameTypeRequirement", @@ -156,6 +175,11 @@ public let GENERIC_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "requirement": .renamed(from: "body") + ] ] ), @@ -163,7 +187,8 @@ public let GENERIC_NODES: [Node] = [ kind: .genericWhereClause, base: .syntax, nameForDiagnostics: "'where' clause", - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", children: [ Child( name: "whereKeyword", @@ -172,10 +197,14 @@ public let GENERIC_NODES: [Node] = [ ), Child( name: "requirements", - deprecatedName: "requirementList", kind: .collection(kind: .genericRequirementList, collectionElementName: "Requirement"), documentation: "The list of requirements in the clause." ), + ], + childHistory: [ + [ + "requirements": .renamed(from: "requirementList") + ] ] ), @@ -186,7 +215,6 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "type", - deprecatedName: "typeIdentifier", kind: .node(kind: .type), nameForDiagnostics: "constrained type" ), @@ -196,7 +224,6 @@ public let GENERIC_NODES: [Node] = [ ), Child( name: "layoutSpecifier", - deprecatedName: "layoutConstraint", kind: .token(choices: [ .keyword(._Trivial), .keyword(._TrivialAtMost), @@ -236,6 +263,12 @@ public let GENERIC_NODES: [Node] = [ kind: .token(choices: [.token(.rightParen)]), isOptional: true ), + ], + childHistory: [ + [ + "type": .renamed(from: "typeIdentifier"), + "layoutSpecifier": .renamed(from: "layoutConstraint"), + ] ] ), @@ -246,19 +279,23 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "leftAngle", - deprecatedName: "leftAngleBracket", kind: .token(choices: [.token(.leftAngle)]) ), Child( name: "primaryAssociatedTypes", - deprecatedName: "primaryAssociatedTypeList", kind: .collection(kind: .primaryAssociatedTypeList, collectionElementName: "PrimaryAssociatedType") ), Child( name: "rightAngle", - deprecatedName: "rightAngleBracket", kind: .token(choices: [.token(.rightAngle)]) ), + ], + childHistory: [ + [ + "leftAngle": .renamed(from: "leftAngleBracket"), + "primaryAssociatedTypes": .renamed(from: "primaryAssociatedTypeList"), + "rightAngle": .renamed(from: "rightAngleBracket"), + ] ] ), @@ -297,21 +334,47 @@ public let GENERIC_NODES: [Node] = [ children: [ Child( name: "leftType", - deprecatedName: "leftTypeIdentifier", - kind: .node(kind: .type), - nameForDiagnostics: "left-hand type" + kind: .nodeChoices(choices: [ + Child( + name: "type", + kind: .node(kind: .type) + ), + Child( + name: "expr", + kind: .node(kind: .expr) + ), + ]), + nameForDiagnostics: "left-hand type", + documentation: + "The left hand side type for a same type requirement. This can either be a regular type argument or an expression for value generics." ), Child( name: "equal", - deprecatedName: "equalityToken", kind: .token(choices: [.token(.binaryOperator), .token(.prefixOperator), .token(.postfixOperator)]) ), Child( name: "rightType", - deprecatedName: "rightTypeIdentifier", - kind: .node(kind: .type), - nameForDiagnostics: "right-hand type" + kind: .nodeChoices(choices: [ + Child( + name: "type", + kind: .node(kind: .type) + ), + Child( + name: "expr", + kind: .node(kind: .expr) + ), + ]), + nameForDiagnostics: "right-hand type", + documentation: + "The right hand side type for a same type requirement. This can either be a regular type argument or an expression for value generics." ), + ], + childHistory: [ + [ + "leftType": .renamed(from: "leftTypeIdentifier"), + "equal": .renamed(from: "equalityToken"), + "rightType": .renamed(from: "rightTypeIdentifier"), + ] ] ), diff --git a/Sources/CodeGeneration/SyntaxSupport/GrammarGenerator.swift b/Sources/CodeGeneration/SyntaxSupport/GrammarGenerator.swift index aa7e492..819cbf0 100644 --- a/Sources/CodeGeneration/SyntaxSupport/GrammarGenerator.swift +++ b/Sources/CodeGeneration/SyntaxSupport/GrammarGenerator.swift @@ -28,7 +28,7 @@ struct GrammarGenerator { if let tokenText = tokenSpec.text { return "`\(tokenText)`" } else { - return "`<\(tokenSpec.varOrCaseName)>`" + return "`<\(tokenSpec.identifier)>`" } } } @@ -38,10 +38,10 @@ struct GrammarGenerator { switch child.kind { case .node(let kind): return "\(kind.doccLink)\(optionality)" - case .nodeChoices(let choices): + case .nodeChoices(let choices, _): let choicesDescriptions = choices.map { grammar(for: $0) } return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)" - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): return "\(kind.doccLink)\(optionality)" case .token(let choices, _, _): if choices.count == 1 { @@ -62,7 +62,7 @@ struct GrammarGenerator { return children .filter { !$0.isUnexpectedNodes } - .map { " - `\($0.varOrCaseName)`: \(generator.grammar(for: $0))" } + .map { " - `\($0.identifier)`: \(generator.grammar(for: $0))" } .joined(separator: "\n") } diff --git a/Sources/CodeGeneration/SyntaxSupport/IdentifierConvertible.swift b/Sources/CodeGeneration/SyntaxSupport/IdentifierConvertible.swift new file mode 100644 index 0000000..2ff5f65 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/IdentifierConvertible.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 + +/// Instances of a conforming type should provide an identifier to be used in code generation. +public protocol IdentifierConvertible { + /// The name of the instance as an identifer. + var identifier: TokenSyntax { + get + } +} + +public extension IdentifierConvertible { + /// ``identifier`` escaped as a base name suitable for call sites. + var baseCallName: TokenSyntax { + identifier.declNameOrVarCallName + } + + /// ``identifier`` escaped as an enum case name suitable for call sites. + var enumCaseCallName: TokenSyntax { + memberCallName + } + + /// ``identifier`` escaped as a member name suitable for call sites. + var memberCallName: TokenSyntax { + identifier.nonVarCallNameOrLabelDeclName + } + + /// ``identifier`` escaped as an enum case name suitable for declaration sites. + var enumCaseDeclName: TokenSyntax { + identifier.declNameOrVarCallName + } + + /// ``identifier`` escaped as a function name suitable for declaration sites. + var funcDeclName: TokenSyntax { + identifier.declNameOrVarCallName + } + + /// ``identifier`` escaped as an argument label name suitable for declaration sites. + var labelDeclName: TokenSyntax { + identifier.nonVarCallNameOrLabelDeclName + } + + /// ``identifier`` escaped as a variable name suitable for declaration sites. + var varDeclName: TokenSyntax { + identifier.declNameOrVarCallName + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/InitSignature.swift b/Sources/CodeGeneration/SyntaxSupport/InitSignature.swift new file mode 100644 index 0000000..799f62b --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/InitSignature.swift @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 +// +//===----------------------------------------------------------------------===// + +/// Represents an initializer that should be generated. +public struct InitSignature { + /// The list of children which should be initialized by the initializer. + /// Includes hidden children. + public var children: [Child] + + /// The list of children which should be given parameters. Excludes hidden + /// children. + public var visibleChildren: LazyFilterSequence<[Child]> { + return children.lazy.filter { !$0.isHidden } + } + + /// Does this initializer cover any experimental features? + public var isExperimental: Bool { + return visibleChildren.contains { $0.isExperimental && !$0.isUnexpectedNodes } + } + + /// Does this initializer cover any historical children (vs. just omitting experimental features)? + public var isHistorical: Bool { + return visibleChildren.contains { $0.isHistorical && !$0.isUnexpectedNodes } + } + + /// Create an initializer with an arbitrary array of children. + public init(children: [Child]) { + self.children = children + } + + /// Create an initializer for the (non-deprecated) children of a given layout node. + public init(_ layoutNode: LayoutNode) { + self.init(children: layoutNode.children) + } + + /// Create an initializer for the (non-deprecated) children of a given trait. + public init(_ trait: Trait) { + self.init(children: trait.children) + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/KeywordSpec.swift b/Sources/CodeGeneration/SyntaxSupport/KeywordSpec.swift index 895411b..2b1809a 100644 --- a/Sources/CodeGeneration/SyntaxSupport/KeywordSpec.swift +++ b/Sources/CodeGeneration/SyntaxSupport/KeywordSpec.swift @@ -12,14 +12,13 @@ import SwiftSyntax -public struct KeywordSpec { +public struct KeywordSpec: IdentifierConvertible { /// The name of the keyword. public let name: String /// The experimental feature the keyword is part of, or `nil` if this isn't /// for an experimental feature. public let experimentalFeature: ExperimentalFeature? - public let experimentalFeature2: ExperimentalFeature? /// Indicates if the token kind is switched from being an identifier to a keyword in the lexer. public let isLexerClassified: Bool @@ -30,21 +29,21 @@ public struct KeywordSpec { /// API generated should be marked as SPI public var isExperimental: Bool { experimentalFeature != nil } - /// The name of this keyword that's suitable to be used for variable or enum case names. - public var varOrCaseName: TokenSyntax { - if name == "init" { - return "`init`" - } else { - return TokenSyntax.identifier(name) - } + /// The name of this keyword as an identifier. + public var identifier: TokenSyntax { + TokenSyntax.identifier(name) } /// The attributes that should be printed on any API for the generated keyword. /// /// This is typically used to mark APIs as SPI when the keyword is part of an experimental language feature. public var apiAttributes: AttributeListSyntax { - guard isExperimental else { return "" } - return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline) + AttributeListSyntax { + if isExperimental { + AttributeSyntax("@_spi(ExperimentalLanguageFeatures)") + .with(\.trailingTrivia, .newline) + } + } } /// Initializes a new `KeywordSpec` instance. @@ -60,26 +59,6 @@ public struct KeywordSpec { ) { self.name = name self.experimentalFeature = experimentalFeature - self.experimentalFeature2 = nil - self.isLexerClassified = isLexerClassified - } - - /// Initializes a new `KeywordSpec` instance. - /// - /// - Parameters: - /// - name: A name of the keyword. - /// - experimentalFeature: The experimental feature the keyword is part of, or `nil` if this isn't for an experimental feature. - /// - or: A second experimental feature the keyword is also part of, or `nil` if this isn't for an experimental feature. - /// - isLexerClassified: Indicates if the token kind is switched from being an identifier to a keyword in the lexer. - init( - _ name: String, - experimentalFeature: ExperimentalFeature, - or experimentalFeature2: ExperimentalFeature, - isLexerClassified: Bool = false - ) { - self.name = name - self.experimentalFeature = experimentalFeature - self.experimentalFeature2 = experimentalFeature2 self.isLexerClassified = isLexerClassified } } @@ -99,57 +78,42 @@ public enum Keyword: CaseIterable { case __owned case __setter_access case __shared - case _alignment case _backDeploy case _borrow case _borrowing case _BridgeObject - case _cdecl case _Class case _compilerInitialized case _const - case _consume case _consuming - case _copy case _documentation case _dynamicReplacement case _effects - case _expose case _forward case _implements case _linear case _local case _modify case _move - case _mutate case _mutating case _NativeClass case _NativeRefCountedObject case _noMetadata - case _nonSendable - case _objcImplementation - case _objcRuntimeName case _opaqueReturnTypeOf - case _optimize case _originallyDefinedIn case _PackageDescription - case _private - case _projectedValueProperty case _read case _RefCountedObject - case _semantics + case specialized case _specialize - case _spi case _spi_available - case _swift_native_objc_runtime_base case _Trivial case _TrivialAtMost case _TrivialStride - case _typeEraser - case _unavailableFromAsync case _underlyingVersion case _UnknownLayout case _version + case abi case accesses case actor case addressWithNativeOwner @@ -169,6 +133,7 @@ public enum Keyword: CaseIterable { case backDeployed case before case block + case borrow case borrowing case `break` case canImport @@ -182,10 +147,10 @@ public enum Keyword: CaseIterable { case `continue` case convenience case convention - case cType case `default` case `defer` case `deinit` + case dependsOn case deprecated case derivative case didSet @@ -197,7 +162,6 @@ public enum Keyword: CaseIterable { case `else` case `enum` case escaping - case exclusivity case exported case `extension` case `fallthrough` @@ -220,7 +184,6 @@ public enum Keyword: CaseIterable { case infix case `init` case initializes - case inline case `inout` case `internal` case introduced @@ -236,7 +199,9 @@ public enum Keyword: CaseIterable { case macro case message case metadata + case modify case module + case mutate case mutableAddressWithNativeOwner case mutableAddressWithOwner case mutating @@ -247,6 +212,7 @@ public enum Keyword: CaseIterable { case none case nonisolated case nonmutating + case nonsending case objc case obsoleted case of @@ -263,24 +229,24 @@ public enum Keyword: CaseIterable { case `Protocol` case `protocol` case `public` + case read case reasync case renamed case `repeat` case required - case _resultDependsOn - case _resultDependsOnSelf case `rethrows` case retroactive case `return` case reverse case right case safe + case scoped case `self` + case sending case `Self` case Sendable case set case some - case sourceFile case spi case spiModule case `static` @@ -293,7 +259,6 @@ public enum Keyword: CaseIterable { case then case `throw` case `throws` - case transferring case transpose case `true` case `try` @@ -305,13 +270,13 @@ public enum Keyword: CaseIterable { case unsafe case unsafeAddress case unsafeMutableAddress + case using case `var` case visibility case weak case `where` case `while` case willSet - case witness_method case wrt case yield @@ -325,38 +290,28 @@ public enum Keyword: CaseIterable { return KeywordSpec("__setter_access") case .__shared: return KeywordSpec("__shared") - case ._alignment: - return KeywordSpec("_alignment") case ._backDeploy: return KeywordSpec("_backDeploy") case ._borrow: return KeywordSpec("_borrow") case ._borrowing: - return KeywordSpec("_borrowing", experimentalFeature: .referenceBindings, or: .borrowingSwitch) + return KeywordSpec("_borrowing") case ._BridgeObject: return KeywordSpec("_BridgeObject") - case ._cdecl: - return KeywordSpec("_cdecl") case ._Class: return KeywordSpec("_Class") case ._compilerInitialized: return KeywordSpec("_compilerInitialized") case ._const: return KeywordSpec("_const") - case ._consume: - return KeywordSpec("_consume", experimentalFeature: .nonescapableTypes) case ._consuming: return KeywordSpec("_consuming", experimentalFeature: .referenceBindings) - case ._copy: - return KeywordSpec("_copy", experimentalFeature: .nonescapableTypes) case ._documentation: return KeywordSpec("_documentation") case ._dynamicReplacement: return KeywordSpec("_dynamicReplacement") case ._effects: return KeywordSpec("_effects") - case ._expose: - return KeywordSpec("_expose") case ._forward: return KeywordSpec("_forward") case ._implements: @@ -369,8 +324,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("_modify") case ._move: return KeywordSpec("_move") - case ._mutate: - return KeywordSpec("_mutate", experimentalFeature: .nonescapableTypes) case ._mutating: return KeywordSpec("_mutating", experimentalFeature: .referenceBindings) case ._NativeClass: @@ -379,54 +332,36 @@ public enum Keyword: CaseIterable { return KeywordSpec("_NativeRefCountedObject") case ._noMetadata: return KeywordSpec("_noMetadata") - case ._nonSendable: - return KeywordSpec("_nonSendable") - case ._objcImplementation: - return KeywordSpec("_objcImplementation") - case ._objcRuntimeName: - return KeywordSpec("_objcRuntimeName") case ._opaqueReturnTypeOf: return KeywordSpec("_opaqueReturnTypeOf") - case ._optimize: - return KeywordSpec("_optimize") case ._originallyDefinedIn: return KeywordSpec("_originallyDefinedIn") case ._PackageDescription: return KeywordSpec("_PackageDescription") - case ._private: - return KeywordSpec("_private") - case ._projectedValueProperty: - return KeywordSpec("_projectedValueProperty") case ._read: return KeywordSpec("_read") case ._RefCountedObject: return KeywordSpec("_RefCountedObject") - case ._semantics: - return KeywordSpec("_semantics") + case .specialized: + return KeywordSpec("specialized") case ._specialize: return KeywordSpec("_specialize") - case ._spi: - return KeywordSpec("_spi") case ._spi_available: return KeywordSpec("_spi_available") - case ._swift_native_objc_runtime_base: - return KeywordSpec("_swift_native_objc_runtime_base") case ._Trivial: return KeywordSpec("_Trivial") case ._TrivialAtMost: return KeywordSpec("_TrivialAtMost") case ._TrivialStride: return KeywordSpec("_TrivialStride") - case ._typeEraser: - return KeywordSpec("_typeEraser") - case ._unavailableFromAsync: - return KeywordSpec("_unavailableFromAsync") case ._underlyingVersion: return KeywordSpec("_underlyingVersion") case ._UnknownLayout: return KeywordSpec("_UnknownLayout") case ._version: return KeywordSpec("_version") + case .abi: + return KeywordSpec("abi") case .accesses: return KeywordSpec("accesses") case .actor: @@ -465,6 +400,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("before") case .block: return KeywordSpec("block") + case .borrow: + return KeywordSpec("borrow") case .borrowing: return KeywordSpec("borrowing") case .break: @@ -491,14 +428,14 @@ public enum Keyword: CaseIterable { return KeywordSpec("convenience") case .convention: return KeywordSpec("convention") - case .cType: - return KeywordSpec("cType") case .default: return KeywordSpec("default", isLexerClassified: true) case .defer: return KeywordSpec("defer", isLexerClassified: true) case .deinit: return KeywordSpec("deinit", isLexerClassified: true) + case .dependsOn: + return KeywordSpec("dependsOn", experimentalFeature: .nonescapableTypes) case .deprecated: return KeywordSpec("deprecated") case .derivative: @@ -521,8 +458,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("enum", isLexerClassified: true) case .escaping: return KeywordSpec("escaping") - case .exclusivity: - return KeywordSpec("exclusivity") case .exported: return KeywordSpec("exported") case .extension: @@ -567,8 +502,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("init", isLexerClassified: true) case .initializes: return KeywordSpec("initializes") - case .inline: - return KeywordSpec("inline") case .inout: return KeywordSpec("inout", isLexerClassified: true) case .internal: @@ -599,8 +532,12 @@ public enum Keyword: CaseIterable { return KeywordSpec("message") case .metadata: return KeywordSpec("metadata") + case .modify: + return KeywordSpec("modify", experimentalFeature: .coroutineAccessors) case .module: return KeywordSpec("module") + case .mutate: + return KeywordSpec("mutate", experimentalFeature: .borrowAndMutateAccessors) case .mutableAddressWithNativeOwner: return KeywordSpec("mutableAddressWithNativeOwner") case .mutableAddressWithOwner: @@ -621,6 +558,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("nonisolated") case .nonmutating: return KeywordSpec("nonmutating") + case .nonsending: + return KeywordSpec("nonsending") case .objc: return KeywordSpec("objc") case .obsoleted: @@ -651,6 +590,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("protocol", isLexerClassified: true) case .public: return KeywordSpec("public", isLexerClassified: true) + case .read: + return KeywordSpec("read", experimentalFeature: .coroutineAccessors) case .reasync: return KeywordSpec("reasync") case .renamed: @@ -659,10 +600,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("repeat", isLexerClassified: true) case .required: return KeywordSpec("required") - case ._resultDependsOn: - return KeywordSpec("_resultDependsOn", experimentalFeature: .nonescapableTypes) - case ._resultDependsOnSelf: - return KeywordSpec("_resultDependsOnSelf", experimentalFeature: .nonescapableTypes) case .rethrows: return KeywordSpec("rethrows", isLexerClassified: true) case .retroactive: @@ -675,6 +612,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("right") case .safe: return KeywordSpec("safe") + case .scoped: + return KeywordSpec("scoped", experimentalFeature: .nonescapableTypes) case .self: return KeywordSpec("self", isLexerClassified: true) case .Self: @@ -685,8 +624,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("set") case .some: return KeywordSpec("some") - case .sourceFile: - return KeywordSpec("sourceFile") case .spi: return KeywordSpec("spi") case .spiModule: @@ -711,11 +648,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("throw", isLexerClassified: true) case .throws: return KeywordSpec("throws", isLexerClassified: true) - case .transferring: - return KeywordSpec( - "transferring", - experimentalFeature: .transferringArgsAndResults - ) + case .sending: + return KeywordSpec("sending") case .transpose: return KeywordSpec("transpose") case .true: @@ -740,6 +674,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("unsafeAddress") case .unsafeMutableAddress: return KeywordSpec("unsafeMutableAddress") + case .using: + return KeywordSpec("using") case .var: return KeywordSpec("var", isLexerClassified: true) case .visibility: @@ -752,8 +688,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("while", isLexerClassified: true) case .willSet: return KeywordSpec("willSet") - case .witness_method: - return KeywordSpec("witness_method") case .wrt: return KeywordSpec("wrt") case .yield: diff --git a/Sources/CodeGeneration/SyntaxSupport/Node.swift b/Sources/CodeGeneration/SyntaxSupport/Node.swift index 5fec1d9..73fa940 100644 --- a/Sources/CodeGeneration/SyntaxSupport/Node.swift +++ b/Sources/CodeGeneration/SyntaxSupport/Node.swift @@ -21,9 +21,9 @@ import SwiftSyntax /// but fixed types. /// - Collection nodes contains an arbitrary number of children but all those /// children are of the same type. -public class Node { +public class Node: NodeChoiceConvertible { fileprivate enum Data { - case layout(children: [Child], traits: [String]) + case layout(children: [Child], childHistory: Child.History, traits: [String]) case collection(choices: [SyntaxNodeKind]) } @@ -40,10 +40,11 @@ public class Node { /// The kind of node’s supertype. This kind must have `isBase == true` public let base: SyntaxNodeKind - /// The experimental feature the node is part of, or `nil` if this isn't - /// for an experimental feature. public let experimentalFeature: ExperimentalFeature? + /// SPI name if this node is only available for the SPI. + public let spi: TokenSyntax? + /// When the node name is printed for diagnostics, this name is used. /// If `nil`, `nameForDiagnostics` will print the parent node’s name. public let nameForDiagnostics: String? @@ -57,14 +58,13 @@ public class Node { /// function that should be invoked to create this node. public let parserFunction: TokenSyntax? - /// If `true`, this is for an experimental language feature, and any public - /// API generated should be SPI. - public var isExperimental: Bool { experimentalFeature != nil } + public var syntaxNodeKind: SyntaxNodeKind { + self.kind + } - /// A name for this node that is suitable to be used as a variables or enum - /// case's name. - public var varOrCaseName: TokenSyntax { - return kind.varOrCaseName + /// A name for this node as an identifier. + public var identifier: TokenSyntax { + return kind.identifier } /// If this is a layout node, return a view of the node that provides access @@ -96,31 +96,24 @@ public class Node { /// Retrieve the attributes that should be printed on any API for the /// generated node. If `forRaw` is true, this is for the raw syntax node. public func apiAttributes(forRaw: Bool = false) -> AttributeListSyntax { - let attrList = AttributeListSyntax { + AttributeListSyntax { if isExperimental { - // SPI for enum cases currently requires Swift 5.8 to work correctly. - let experimentalSPI: AttributeListSyntax = """ - #if compiler(>=5.8) - @_spi(ExperimentalLanguageFeatures) - #endif - """ - experimentalSPI.with(\.trailingTrivia, .newline) + AttributeSyntax("@_spi(ExperimentalLanguageFeatures)") + .with(\.trailingTrivia, .newline) + } + if let spi = self.spi { + AttributeSyntax("@_spi(\(spi))") + .with(\.trailingTrivia, .newline) } if forRaw { - "@_spi(RawSyntax)" + AttributeSyntax("@_spi(RawSyntax)") + .with(\.trailingTrivia, .newline) } } - return attrList.with(\.trailingTrivia, attrList.isEmpty ? [] : .newline) } - /// The documentation note to print for an experimental feature. - public var experimentalDocNote: SwiftSyntax.Trivia { - let comment = experimentalFeature.map { - """ - - Experiment: Requires experimental feature `\($0.token)`. - """ - } - return SwiftSyntax.Trivia.docCommentTrivia(from: comment) + public var apiAttributes: AttributeListSyntax { + self.apiAttributes() } /// Construct the specification for a layout syntax node. @@ -128,11 +121,14 @@ public class Node { kind: SyntaxNodeKind, base: SyntaxNodeKind, experimentalFeature: ExperimentalFeature? = nil, + spi: TokenSyntax? = nil, nameForDiagnostics: String?, documentation: String? = nil, parserFunction: TokenSyntax? = nil, traits: [String] = [], - children: [Child] = [] + children: [Child] = [], + childHistory: Child.History = [], + noInterleaveUnexpected: Bool = false ) { precondition(base != .syntaxCollection) precondition(base.isBase, "unknown base kind '\(base)' for node '\(kind)'") @@ -140,55 +136,19 @@ public class Node { self.kind = kind self.base = base self.experimentalFeature = experimentalFeature + self.spi = spi self.nameForDiagnostics = nameForDiagnostics self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.parserFunction = parserFunction - let childrenWithUnexpected: [Child] - if children.isEmpty { - childrenWithUnexpected = [ - Child(name: "unexpected", kind: .collection(kind: .unexpectedNodes, collectionElementName: "Unexpected"), isOptional: true) - ] - } else { - // Add implicitly generated UnexpectedNodes children between - // any two defined children - childrenWithUnexpected = - children.enumerated().flatMap { (i, child) -> [Child] in - let childName = child.name.withFirstCharacterUppercased - - let unexpectedName: String - let unexpectedDeprecatedName: String? - - if i == 0 { - unexpectedName = "unexpectedBefore\(childName)" - unexpectedDeprecatedName = child.deprecatedName.map { "unexpectedBefore\($0.withFirstCharacterUppercased)" } - } else { - unexpectedName = "unexpectedBetween\(children[i - 1].name.withFirstCharacterUppercased)And\(childName)" - if let deprecatedName = children[i - 1].deprecatedName?.withFirstCharacterUppercased { - unexpectedDeprecatedName = "unexpectedBetween\(deprecatedName)And\(child.deprecatedName?.withFirstCharacterUppercased ?? childName)" - } else if let deprecatedName = child.deprecatedName?.withFirstCharacterUppercased { - unexpectedDeprecatedName = "unexpectedBetween\(children[i - 1].name.withFirstCharacterUppercased)And\(deprecatedName)" - } else { - unexpectedDeprecatedName = nil - } - } - let unexpectedBefore = Child( - name: unexpectedName, - deprecatedName: unexpectedDeprecatedName, - kind: .collection(kind: .unexpectedNodes, collectionElementName: unexpectedName), - isOptional: true - ) - return [unexpectedBefore, child] - } + [ - Child( - name: "unexpectedAfter\(children.last!.name.withFirstCharacterUppercased)", - deprecatedName: children.last!.deprecatedName.map { "unexpectedAfter\($0.withFirstCharacterUppercased)" }, - kind: .collection(kind: .unexpectedNodes, collectionElementName: "UnexpectedAfter\(children.last!.name.withFirstCharacterUppercased)"), - isOptional: true - ) - ] - } - self.data = .layout(children: childrenWithUnexpected, traits: traits) + let childrenWithUnexpected = + (kind.isBase || noInterleaveUnexpected) ? children : interleaveUnexpectedChildren(children) + + self.data = .layout(children: childrenWithUnexpected, childHistory: childHistory, traits: traits) + } + + public var hiddenInDocumentation: Bool { + self.isExperimental || self.spi != nil || self.kind.isDeprecated } /// A doc comment that lists all the nodes in which this node occurs as a child in. @@ -199,7 +159,10 @@ public class Node { return [] } var childIn: [(node: SyntaxNodeKind, child: Child?)] = [] - for node in SYNTAX_NODES where !node.isExperimental { + for node in SYNTAX_NODES { + if !self.hiddenInDocumentation && node.hiddenInDocumentation { + continue + } if let layout = node.layoutNode { for child in layout.children { if child.kinds.contains(self.kind) { @@ -220,11 +183,15 @@ public class Node { let list = childIn .map { - if let childName = $0.child?.varOrCaseName { + if let childName = $0.child?.identifier { // This will repeat the syntax type before and after the dot, which is // a little unfortunate, but it's the only way I found to get docc to // generate a fully-qualified type + member. - return " - \($0.node.doccLink).``\($0.node.syntaxType)/\(childName)``" + if $0.node.isAvailableInDocc { + return " - \($0.node.doccLink).``\($0.node.syntaxType)/\(childName)``" + } else { + return " - \($0.node.doccLink).`\($0.node.syntaxType)/\(childName)`" + } } else { return " - \($0.node.doccLink)" } @@ -248,7 +215,7 @@ public class Node { let list = SYNTAX_NODES - .filter { $0.base == self.kind && !$0.isExperimental } + .filter { $0.base == self.kind && (!$0.hiddenInDocumentation || self.hiddenInDocumentation) } .map { "- \($0.kind.doccLink)" } .joined(separator: "\n") @@ -272,6 +239,7 @@ public class Node { kind: SyntaxNodeKind, base: SyntaxNodeKind, experimentalFeature: ExperimentalFeature? = nil, + spi: TokenSyntax? = nil, nameForDiagnostics: String?, documentation: String? = nil, parserFunction: TokenSyntax? = nil, @@ -281,6 +249,7 @@ public class Node { precondition(base == .syntaxCollection) self.base = base self.experimentalFeature = experimentalFeature + self.spi = spi self.nameForDiagnostics = nameForDiagnostics self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.parserFunction = parserFunction @@ -317,7 +286,7 @@ public struct LayoutNode { /// This includes unexpected children public var children: [Child] { switch node.data { - case .layout(children: let children, traits: _): + case .layout(let children, childHistory: _, traits: _): return children case .collection: preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") @@ -329,10 +298,20 @@ public struct LayoutNode { return children.filter { !$0.isUnexpectedNodes } } + /// The history of the layout node's children. + public var childHistory: Child.History { + switch node.data { + case .layout(children: _, let childHistory, traits: _): + return childHistory + case .collection: + preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") + } + } + /// Traits that the node conforms to. public var traits: [String] { switch node.data { - case .layout(children: _, traits: let traits): + case .layout(children: _, childHistory: _, let traits): return traits case .collection: preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") @@ -384,7 +363,7 @@ public struct CollectionNode { switch node.data { case .layout: preconditionFailure("NodeLayoutView must wrap a Node with data `.collection`") - case .collection(choices: let choices): + case .collection(let choices): return choices } } @@ -412,12 +391,21 @@ fileprivate extension Child { switch kind { case .node(let kind): return [kind] - case .nodeChoices(let choices): + case .nodeChoices(let choices, _): return choices.flatMap(\.kinds) - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): return [kind] case .token: return [.token] } } } + +private func interleaveUnexpectedChildren(_ children: [Child]) -> [Child] { + let liftedChildren = children.lazy.map(Optional.some) + let pairedChildren = zip([nil] + liftedChildren, liftedChildren + [nil]) + + return pairedChildren.flatMap { earlier, later in + [earlier, Child(forUnexpectedBetween: earlier, and: later)].compactMap { $0 } + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/NodeChoiceConvertible.swift b/Sources/CodeGeneration/SyntaxSupport/NodeChoiceConvertible.swift new file mode 100644 index 0000000..386ac90 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/NodeChoiceConvertible.swift @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 + +/// Instances of a conforming type should provide necessary information for generating code of a node choice. +public protocol NodeChoiceConvertible: IdentifierConvertible { + /// A docc comment describing the syntax node convertible, including the trivia provided when + /// initializing the syntax node convertible, and the list of possible token choices inferred automatically. + var documentation: SwiftSyntax.Trivia { get } + + /// The experimental feature the syntax node convertible represents, or `nil` if this isn't + /// for an experimental feature. + var experimentalFeature: ExperimentalFeature? { get } + + /// The attributes that should be printed on any API for the syntax node convertible. + /// + /// This is typically used to mark APIs as SPI when the keyword is part of + /// an experimental language feature. + var apiAttributes: AttributeListSyntax { get } + + /// The kind of the syntax node convertible. + var syntaxNodeKind: SyntaxNodeKind { get } +} + +public extension NodeChoiceConvertible { + /// The documentation note to print for an experimental feature. + var experimentalDocNote: SwiftSyntax.Trivia { + guard let experimentalFeature else { + return [] + } + return .docCommentTrivia(from: "- Note: Requires experimental feature `\(experimentalFeature.token)`.") + } + + /// If `true`, this is for an experimental language feature, and any public + /// API generated should be SPI. + var isExperimental: Bool { + self.experimentalFeature != nil + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/PatternNodes.swift b/Sources/CodeGeneration/SyntaxSupport/PatternNodes.swift index 9113ace..dbb019d 100644 --- a/Sources/CodeGeneration/SyntaxSupport/PatternNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/PatternNodes.swift @@ -109,7 +109,6 @@ public let PATTERN_NODES: [Node] = [ children: [ Child( name: "label", - deprecatedName: "labelName", kind: .token(choices: [.token(.identifier)]), nameForDiagnostics: "label", documentation: "The label of the pattern.", @@ -117,7 +116,6 @@ public let PATTERN_NODES: [Node] = [ ), Child( name: "colon", - deprecatedName: "labelColon", kind: .token(choices: [.token(.colon)]), documentation: "The colon separating label and pattern.", isOptional: true @@ -133,6 +131,12 @@ public let PATTERN_NODES: [Node] = [ documentation: "The comma separating elements.", isOptional: true ), + ], + childHistory: [ + [ + "label": .renamed(from: "labelName"), + "colon": .renamed(from: "labelColon"), + ] ] ), @@ -197,17 +201,22 @@ public let PATTERN_NODES: [Node] = [ children: [ Child( name: "bindingSpecifier", - deprecatedName: "bindingKeyword", kind: .token(choices: [ .keyword(.let), .keyword(.var), .keyword(.inout), .keyword(._mutating), .keyword(._borrowing), .keyword(._consuming), + .keyword(.borrowing), ]) ), Child( name: "pattern", - deprecatedName: "valuePattern", kind: .node(kind: .pattern) ), + ], + childHistory: [ + [ + "bindingSpecifier": .renamed(from: "bindingKeyword"), + "pattern": .renamed(from: "valuePattern"), + ] ] ), diff --git a/Sources/CodeGeneration/SyntaxSupport/RawSyntaxNodeKind.swift b/Sources/CodeGeneration/SyntaxSupport/RawSyntaxNodeKind.swift new file mode 100644 index 0000000..ded2446 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/RawSyntaxNodeKind.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 + +/// A wrapper of ``SyntaxNodeKind`` providing syntax type information for the raw side. +public struct RawSyntaxNodeKind: TypeConvertible { + public var syntaxNodeKind: SyntaxNodeKind + + public var isBase: Bool { + self.syntaxNodeKind.isBase + } + + public var syntaxType: TypeSyntax { + "Raw\(self.syntaxNodeKind.syntaxType)" + } + + public var protocolType: TypeSyntax { + switch self { + case .syntax, .syntaxCollection: + return "RawSyntaxNodeProtocol" + default: + return "\(self.syntaxType)NodeProtocol" + } + } + + public var isAvailableInDocc: Bool { + false + } + + public static func ~= (lhs: SyntaxNodeKind, rhs: Self) -> Bool { + lhs == rhs.syntaxNodeKind + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/StmtNodes.swift b/Sources/CodeGeneration/SyntaxSupport/StmtNodes.swift index 8d641dc..3ac5bab 100644 --- a/Sources/CodeGeneration/SyntaxSupport/StmtNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/StmtNodes.swift @@ -26,13 +26,17 @@ public let STMT_NODES: [Node] = [ ), Child( name: "availabilityArguments", - deprecatedName: "availabilitySpec", kind: .collection(kind: .availabilityArgumentList, collectionElementName: "AvailabilityArgument") ), Child( name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "availabilityArguments": .renamed(from: "availabilitySpec") + ] ] ), @@ -280,6 +284,11 @@ public let STMT_NODES: [Node] = [ kind: .token(choices: [.keyword(.await)]), isOptional: true ), + Child( + name: "unsafeKeyword", + kind: .token(choices: [.keyword(.unsafe)]), + isOptional: true + ), Child( name: "caseKeyword", kind: .token(choices: [.keyword(.case)]), @@ -300,7 +309,6 @@ public let STMT_NODES: [Node] = [ ), Child( name: "sequence", - deprecatedName: "sequenceExpr", kind: .node(kind: .expr) ), Child( @@ -313,6 +321,11 @@ public let STMT_NODES: [Node] = [ kind: .node(kind: .codeBlock), nameForDiagnostics: "body" ), + ], + childHistory: [ + [ + "sequence": .renamed(from: "sequenceExpr") + ] ] ), @@ -368,19 +381,23 @@ public let STMT_NODES: [Node] = [ children: [ Child( name: "label", - deprecatedName: "labelName", kind: .token(choices: [.token(.identifier)]), nameForDiagnostics: "label name" ), Child( name: "colon", - deprecatedName: "labelColon", kind: .token(choices: [.token(.colon)]) ), Child( name: "statement", kind: .node(kind: .stmt) ), + ], + childHistory: [ + [ + "label": .renamed(from: "labelName"), + "colon": .renamed(from: "labelColon"), + ] ] ), @@ -416,7 +433,6 @@ public let STMT_NODES: [Node] = [ children: [ Child( name: "bindingSpecifier", - deprecatedName: "bindingKeyword", kind: .token(choices: [ .keyword(.let), .keyword(.var), .keyword(.inout), .keyword(._mutating), .keyword(._borrowing), .keyword(._consuming), @@ -436,6 +452,11 @@ public let STMT_NODES: [Node] = [ kind: .node(kind: .initializerClause), isOptional: true ), + ], + childHistory: [ + [ + "bindingSpecifier": .renamed(from: "bindingKeyword") + ] ] ), @@ -512,9 +533,13 @@ public let STMT_NODES: [Node] = [ ), Child( name: "condition", - deprecatedName: "guardResult", kind: .node(kind: .expr) ), + ], + childHistory: [ + [ + "condition": .renamed(from: "guardResult") + ] ] ), @@ -552,13 +577,17 @@ public let STMT_NODES: [Node] = [ ), Child( name: "elements", - deprecatedName: "elementList", kind: .collection(kind: .yieldedExpressionList, collectionElementName: "Element") ), Child( name: "rightParen", kind: .token(choices: [.token(.rightParen)]) ), + ], + childHistory: [ + [ + "elements": .renamed(from: "elementList") + ] ] ), @@ -573,20 +602,30 @@ public let STMT_NODES: [Node] = [ ), Child( name: "yieldedExpressions", - deprecatedName: "yields", - kind: .nodeChoices(choices: [ - Child( - name: "multiple", - deprecatedName: "yieldList", - kind: .node(kind: .yieldedExpressionsClause) - ), - Child( - name: "single", - deprecatedName: "simpleYield", - kind: .node(kind: .expr) - ), - ]) + kind: .nodeChoices( + choices: [ + Child( + name: "multiple", + kind: .node(kind: .yieldedExpressionsClause) + ), + Child( + name: "single", + kind: .node(kind: .expr) + ), + ], + childHistory: [ + [ + "multiple": .renamed(from: "yieldList"), + "single": .renamed(from: "simpleYield"), + ] + ] + ) ), + ], + childHistory: [ + [ + "yieldedExpressions": .renamed(from: "yields") + ] ] ), diff --git a/Sources/CodeGeneration/SyntaxSupport/String+Extensions.swift b/Sources/CodeGeneration/SyntaxSupport/String+Extensions.swift index 93884aa..3737ef8 100644 --- a/Sources/CodeGeneration/SyntaxSupport/String+Extensions.swift +++ b/Sources/CodeGeneration/SyntaxSupport/String+Extensions.swift @@ -10,16 +10,26 @@ // //===----------------------------------------------------------------------===// -public extension StringProtocol { - var withFirstCharacterLowercased: String { prefix(1).lowercased() + dropFirst() } - var withFirstCharacterUppercased: String { prefix(1).uppercased() + dropFirst() } - var backtickedIfNeeded: String { - if Keyword.allCases.map(\.spec).contains(where: { - $0.name == self && ($0.isLexerClassified || $0.name == "Type" || $0.name == "Protocol") - }) { - return "`\(self)`" - } else { - return String(self) +extension StringProtocol { + public var withFirstCharacterLowercased: String { + guard first?.isLetter ?? false else { + return String(first!) + dropFirst().withFirstCharacterLowercased } + return prefix(1).lowercased() + dropFirst() + } + public var withFirstCharacterUppercased: String { + guard first?.isLetter ?? false else { + return String(first!) + dropFirst().withFirstCharacterUppercased + } + return prefix(1).uppercased() + dropFirst() + } +} + +extension String { + public var droppingLeadingUnderscores: String { + if first == "_" { + return String(self.dropFirst()) + } + return self } } diff --git a/Sources/CodeGeneration/SyntaxSupport/SyntaxNodeKind.swift b/Sources/CodeGeneration/SyntaxSupport/SyntaxNodeKind.swift index 5ea2614..70eae62 100644 --- a/Sources/CodeGeneration/SyntaxSupport/SyntaxNodeKind.swift +++ b/Sources/CodeGeneration/SyntaxSupport/SyntaxNodeKind.swift @@ -17,10 +17,14 @@ import SwiftSyntaxBuilder /// /// Using the cases of this enum, children of syntax nodes can refer the syntax /// node that defines their layout. -public enum SyntaxNodeKind: String, CaseIterable { +public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeConvertible { // Please keep this list sorted alphabetically + case _canImportExpr + case _canImportVersionInfo + case abiAttributeArguments case accessorBlock + case accessorBlockFile case accessorDecl case accessorDeclList case accessorEffectSpecifiers @@ -36,19 +40,19 @@ public enum SyntaxNodeKind: String, CaseIterable { case associatedTypeDecl case attribute case attributedType + case attributeClauseFile case attributeList case availabilityArgument case availabilityArgumentList case availabilityCondition case availabilityLabeledArgument + case availabilityMacroDefinitionFile case awaitExpr case backDeployedAttributeArguments case binaryOperatorExpr case booleanLiteralExpr case borrowExpr case breakStmt - case canImportExpr - case canImportVersionInfo case catchClause case catchClauseList case catchItem @@ -67,6 +71,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case closureShorthandParameterList case closureSignature case codeBlock + case codeBlockFile case codeBlockItem case codeBlockItemList case compositionType @@ -77,8 +82,6 @@ public enum SyntaxNodeKind: String, CaseIterable { case conformanceRequirement case consumeExpr case continueStmt - case conventionAttributeArguments - case conventionWitnessMethodAttributeArguments case copyExpr case decl case declModifier @@ -120,7 +123,6 @@ public enum SyntaxNodeKind: String, CaseIterable { case enumCaseParameterClause case enumCaseParameterList case enumDecl - case exposeAttributeArguments case expr case expressionPattern case expressionSegment @@ -167,6 +169,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case inheritedTypeList case initializerClause case initializerDecl + case inlineArrayType case inOutExpr case integerLiteralExpr case isExpr @@ -177,6 +180,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case keyPathOptionalComponent case keyPathPropertyComponent case keyPathSubscriptComponent + case keyPathMethodComponent case labeledExpr case labeledExprList case labeledSpecializeArgument @@ -193,6 +197,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case memberBlock case memberBlockItem case memberBlockItemList + case memberBlockItemListFile case memberType case metatypeType case missing @@ -201,13 +206,16 @@ public enum SyntaxNodeKind: String, CaseIterable { case missingPattern case missingStmt case missingType + case moduleSelector case multipleTrailingClosureElement case multipleTrailingClosureElementList case namedOpaqueReturnType case nilLiteralExpr + case nonisolatedSpecifierArgument + case nonisolatedSpecifierArgumentList + case nonisolatedTypeSpecifier case objCSelectorPiece case objCSelectorPieceList - case opaqueReturnTypeOfAttributeArguments case operatorDecl case operatorPrecedenceAndTypes case optionalBindingCondition @@ -252,6 +260,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case simpleStringLiteralSegmentList case someOrAnyType case sourceFile + case specializedAttributeArgument case specializeAttributeArgumentList case specializeAvailabilityArgument case specializeTargetFunctionArgument @@ -292,12 +301,13 @@ public enum SyntaxNodeKind: String, CaseIterable { case typeSpecifier case lifetimeSpecifierArguments case typeSpecifierList - case unavailableFromAsyncAttributeArguments - case underscorePrivateAttributeArguments + case unexpectedCodeDecl case unexpectedNodes case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr + case unsafeExpr + case usingDecl case valueBindingPattern case variableDecl case versionComponent @@ -327,7 +337,6 @@ public enum SyntaxNodeKind: String, CaseIterable { } } - /// Whether this is one of the syntax base nodes. public var isBase: Bool { switch self { case .decl, .expr, .pattern, .stmt, .syntax, .syntaxCollection, .type: @@ -337,13 +346,19 @@ public enum SyntaxNodeKind: String, CaseIterable { } } - /// A name for this node that is suitable to be used as a variables or enum - /// case's name. - public var varOrCaseName: TokenSyntax { + public var identifier: TokenSyntax { return .identifier(rawValue) } - /// The type name of this node in the SwiftSyntax module. + public var uppercasedFirstWordRawValue: String { + switch self { + case .abiAttributeArguments: + "ABIAttributeArguments" + default: + rawValue.withFirstCharacterUppercased + } + } + public var syntaxType: TypeSyntax { switch self { case .syntax: @@ -351,43 +366,24 @@ public enum SyntaxNodeKind: String, CaseIterable { case .syntaxCollection: return "SyntaxCollection" default: - return "\(raw: rawValue.withFirstCharacterUppercased)Syntax" + return "\(raw: uppercasedFirstWordRawValue)Syntax" } } - /// If this node is non-experimental a docc link wrapped in two backticks. - /// - /// For experimental nodes, the node's type name in code font. - public var doccLink: String { + public var isAvailableInDocc: Bool { if let node = SYNTAX_NODE_MAP[self], node.isExperimental { - return "`\(syntaxType)`" + return false + } else if isDeprecated { + return false } else { - return "``\(syntaxType)``" + return true } } - /// For base nodes, the name of the corresponding protocol to which all the - /// concrete nodes that have this base kind, conform. public var protocolType: TypeSyntax { return "\(syntaxType)Protocol" } - /// The name of this node at the `RawSyntax` level. - public var rawType: TypeSyntax { - return "Raw\(syntaxType)" - } - - /// For base nodes, the name of the corresponding raw protocol to which all the - /// concrete raw nodes that have this base kind, conform. - public var rawProtocolType: TypeSyntax { - switch self { - case .syntax, .syntaxCollection: - return "RawSyntaxNodeProtocol" - default: - return "Raw\(raw: rawValue.withFirstCharacterUppercased)SyntaxNodeProtocol" - } - } - /// For base node types, generates the name of the protocol to which all /// concrete leaf nodes that derive from this base kind should conform. /// @@ -405,6 +401,8 @@ public enum SyntaxNodeKind: String, CaseIterable { /// deprecated. public var deprecatedRawValue: String? { switch self { + case ._canImportExpr: return "canImportExpr" + case ._canImportVersionInfo: return "canImportVersionInfo" case .accessorDeclList: return "accessorList" case .accessorParameters: return "accessorParameter" case .associatedTypeDecl: return "associatedtypeDecl" @@ -460,6 +458,7 @@ public enum SyntaxNodeKind: String, CaseIterable { case .someOrAnyType: return "constrainedSugarType" case .simpleTypeSpecifier: return "typeSpecifier" case .specializeAttributeArgumentList: return "specializeAttributeSpecList" + case .specializedAttributeArgument: return "specializedAttribute" case .specializeAvailabilityArgument: return "availabilityEntry" case .specializeTargetFunctionArgument: return "targetFunctionEntry" case .stringLiteralSegmentList: return "stringLiteralSegments" @@ -468,11 +467,34 @@ public enum SyntaxNodeKind: String, CaseIterable { case .switchCaseItem: return "caseItem" case .switchCaseItemList: return "caseItemList" case .typeAliasDecl: return "typealiasDecl" - case .unavailableFromAsyncAttributeArguments: return "unavailableFromAsyncArguments" case .yieldedExpression: return "yieldExprListElement" case .yieldedExpressionList: return "yieldExprList" case .yieldedExpressionsClause: return "yieldList" default: return nil } } + + public var deprecationMessage: String? { + switch self { + case ._canImportExpr: return "'canImport' directives are now represented as a `FunctionCallExpr`" + case ._canImportVersionInfo: return "'canImport' directives are now represented as a `FunctionCallExpr`" + default: return nil + } + } + + public var isDeprecated: Bool { + return rawValue.first == "_" + } + + var deprecationAttribute: AttributeSyntax? { + if let deprecationMessage = deprecationMessage { + AttributeSyntax("@available(*, deprecated, message: \(literal: deprecationMessage)") + } else { + AttributeSyntax(#"@available(*, deprecated, renamed: "\#(syntaxType)")"#) + } + } + + public var raw: RawSyntaxNodeKind { + RawSyntaxNodeKind(syntaxNodeKind: self) + } } diff --git a/Sources/CodeGeneration/SyntaxSupport/SyntaxNodes.swift b/Sources/CodeGeneration/SyntaxSupport/SyntaxNodes.swift index f06a97f..d1f42af 100644 --- a/Sources/CodeGeneration/SyntaxSupport/SyntaxNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/SyntaxNodes.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -public let SYNTAX_NODES: [Node] = - (COMMON_NODES +private let unsortedSyntaxNodes: [Node] = + COMMON_NODES + EXPR_NODES + DECL_NODES + ATTRIBUTE_NODES @@ -19,7 +19,16 @@ public let SYNTAX_NODES: [Node] = + GENERIC_NODES + TYPE_NODES + PATTERN_NODES - + AVAILABILITY_NODES).sorted { $0.kind.syntaxType.description < $1.kind.syntaxType.description } + + AVAILABILITY_NODES + + COMPILER_NODES + +public let SYNTAX_NODES: [Node] = + unsortedSyntaxNodes + .sorted { (lhs: Node, rhs: Node) -> Bool in + let lhsSortKey = lhs.kind.syntaxType.description.droppingLeadingUnderscores + let rhsSortKey = rhs.kind.syntaxType.description.droppingLeadingUnderscores + return lhsSortKey < rhsSortKey + } /// A lookup table of nodes indexed by their kind. public let SYNTAX_NODE_MAP: [SyntaxNodeKind: Node] = Dictionary( @@ -27,3 +36,5 @@ public let SYNTAX_NODE_MAP: [SyntaxNodeKind: Node] = Dictionary( ) public let NON_BASE_SYNTAX_NODES = SYNTAX_NODES.filter { !$0.kind.isBase } + +public let SYNTAX_COMPATIBILITY_LAYER = CompatibilityLayer(nodes: SYNTAX_NODES, traits: TRAITS) diff --git a/Sources/CodeGeneration/SyntaxSupport/TokenSpec.swift b/Sources/CodeGeneration/SyntaxSupport/TokenSpec.swift index c156f10..b276a71 100644 --- a/Sources/CodeGeneration/SyntaxSupport/TokenSpec.swift +++ b/Sources/CodeGeneration/SyntaxSupport/TokenSpec.swift @@ -13,7 +13,7 @@ import SwiftSyntax /// Represents the specification for a Token in the TokenSyntax file. -public struct TokenSpec { +public struct TokenSpec: IdentifierConvertible { public enum Kind { case punctuation /// The `keyword` TokenKind that contains the actual keyword as an associated value @@ -21,8 +21,8 @@ public struct TokenSpec { case other } - /// The name of the token, suitable for use in variable or enum case names. - public let varOrCaseName: TokenSyntax + /// The name of the token as an identifier. + public let identifier: TokenSyntax /// The experimental feature the token is part of, or `nil` if this isn't /// for an experimental feature. @@ -66,7 +66,7 @@ public struct TokenSpec { text: String? = nil, kind: Kind ) { - self.varOrCaseName = .identifier(name) + self.identifier = .identifier(name) self.experimentalFeature = experimentalFeature self.nameForDiagnostics = nameForDiagnostics self.text = text @@ -126,6 +126,7 @@ public enum Token: CaseIterable { case backtick case binaryOperator case colon + case colonColon case comma case dollarIdentifier case ellipsis @@ -185,6 +186,8 @@ public enum Token: CaseIterable { return .other(name: "binaryOperator", nameForDiagnostics: "binary operator") case .colon: return .punctuator(name: "colon", text: ":") + case .colonColon: + return .punctuator(name: "colonColon", text: "::") case .comma: return .punctuator(name: "comma", text: ",") case .dollarIdentifier: diff --git a/Sources/CodeGeneration/SyntaxSupport/Traits.swift b/Sources/CodeGeneration/SyntaxSupport/Traits.swift index 6148737..10169c0 100644 --- a/Sources/CodeGeneration/SyntaxSupport/Traits.swift +++ b/Sources/CodeGeneration/SyntaxSupport/Traits.swift @@ -22,14 +22,22 @@ public class Trait { public let protocolName: TokenSyntax public let documentation: SwiftSyntax.Trivia public let children: [Child] + public let childHistory: Child.History - init(traitName: String, baseKind: SyntaxNodeKind? = nil, documentation: String? = nil, children: [Child]) { + init( + traitName: String, + baseKind: SyntaxNodeKind? = nil, + documentation: String? = nil, + children: [Child], + childHistory: Child.History = [] + ) { precondition(baseKind?.isBase != false, "`baseKind` must be a base syntax node kind") self.traitName = traitName self.baseKind = baseKind self.protocolName = .identifier("\(traitName)Syntax") self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.children = children + self.childHistory = childHistory } } @@ -49,14 +57,18 @@ public let TRAITS: [Trait] = [ Child(name: "modifiers", kind: .node(kind: .declModifierList)), Child( name: "introducer", - kind: .token(choices: [.keyword(.actor), .keyword(.class), .keyword(.enum), .keyword(.extension), .keyword(.protocol), .keyword(.struct)]), + kind: .token(choices: [ + .keyword(.actor), .keyword(.class), .keyword(.enum), .keyword(.extension), .keyword(.protocol), + .keyword(.struct), + ]), documentation: "The token that introduces this declaration, eg. `class` for a class declaration." ), Child(name: "inheritanceClause", kind: .node(kind: .inheritanceClause), isOptional: true), Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), Child(name: "memberBlock", kind: .node(kind: .memberBlock)), @@ -67,7 +79,11 @@ public let TRAITS: [Trait] = [ children: [ Child(name: "unexpectedBeforeAsyncSpecifier", kind: .node(kind: .unexpectedNodes), isOptional: true), Child(name: "asyncSpecifier", kind: .token(choices: [.keyword(.async), .keyword(.reasync)]), isOptional: true), - Child(name: "unexpectedBetweenAsyncSpecifierAndThrowsClause", kind: .node(kind: .unexpectedNodes), isOptional: true), + Child( + name: "unexpectedBetweenAsyncSpecifierAndThrowsClause", + kind: .node(kind: .unexpectedNodes), + isOptional: true + ), Child(name: "throwsClause", kind: .node(kind: .throwsClause), isOptional: true), Child(name: "unexpectedAfterThrowsClause", kind: .node(kind: .unexpectedNodes), isOptional: true), ] @@ -75,14 +91,30 @@ public let TRAITS: [Trait] = [ Trait( traitName: "FreestandingMacroExpansion", children: [ - Child(name: "pound", deprecatedName: "poundToken", kind: .token(choices: [.token(.pound)])), - Child(name: "macroName", deprecatedName: "macro", kind: .token(choices: [.token(.identifier)])), + Child(name: "pound", kind: .token(choices: [.token(.pound)])), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), + Child(name: "macroName", kind: .token(choices: [.token(.identifier)])), Child(name: "genericArgumentClause", kind: .node(kind: .genericArgumentClause), isOptional: true), Child(name: "leftParen", kind: .token(choices: [.token(.leftParen)]), isOptional: true), - Child(name: "arguments", deprecatedName: "argumentList", kind: .node(kind: .labeledExprList)), + Child(name: "arguments", kind: .node(kind: .labeledExprList)), Child(name: "rightParen", kind: .token(choices: [.token(.rightParen)]), isOptional: true), Child(name: "trailingClosure", kind: .node(kind: .closureExpr), isOptional: true), Child(name: "additionalTrailingClosures", kind: .node(kind: .multipleTrailingClosureElementList)), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ], + [ + "pound": .renamed(from: "poundToken"), + "macroName": .renamed(from: "macro"), + "arguments": .renamed(from: "argumentList"), + "genericArgumentClause": .renamed(from: "genericArguments"), + ], ] ), Trait( @@ -145,7 +177,8 @@ public let TRAITS: [Trait] = [ Child( name: "genericWhereClause", kind: .node(kind: .genericWhereClause), - documentation: "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", + documentation: + "A `where` clause that places additional constraints on generic parameters like `where Element: Hashable`.", isOptional: true ), ] diff --git a/Sources/CodeGeneration/SyntaxSupport/Trivia.swift b/Sources/CodeGeneration/SyntaxSupport/Trivia.swift index 28ddb88..ee3f839 100644 --- a/Sources/CodeGeneration/SyntaxSupport/Trivia.swift +++ b/Sources/CodeGeneration/SyntaxSupport/Trivia.swift @@ -12,6 +12,26 @@ import SwiftSyntax +public struct TriviaTraits: OptionSet { + public var rawValue: UInt8 + + public init(rawValue: UInt8) { + self.rawValue = rawValue + } + + // Indicates this is a whitespace. + public static var whitespace: Self { .init(rawValue: 1 << 0) } + + // Indicates a newline in Swift source code. + public static var newline: Self { .init(rawValue: 1 << 1) } + + // Horizontal space. + public static var spaceOrTab: Self { .init(rawValue: 1 << 2) } + + // Comment in Swift source code. + public static var comment: Self { .init(rawValue: 1 << 3) } +} + public class Trivia { /// The name of the trivia. public let name: TokenSyntax @@ -19,20 +39,11 @@ public class Trivia { /// The doc comment describing the trivia. public let comment: SwiftSyntax.Trivia - /// The list of characters that make up the trivia. - /// - /// Useful for multi-character trivias like `\r\n`. - public let characters: [Character] - - /// The list of characters as they would appear in Swift code. - /// - /// This might differ from `characters` due to Swift's character escape requirements. - public let swiftCharacters: [Character] + /// The characters that make up the trivia. + public let characters: String? - /// Indicates if the trivia represents a comment. - /// - /// If `true`, the trivia is some form of a comment in the Swift code. - public let isComment: Bool + /// The traits. + public let traits: TriviaTraits /// The name of the trivia in lowercase. public var lowerName: TokenSyntax { .identifier(lowercaseFirstWord(name: name.text)) } @@ -52,23 +63,10 @@ public class Trivia { } } - /// The length of the `characters` array. - public var charactersLen: Int { characters.count } - /// Indicates if the trivia is a collection of characters. /// /// If `true`, the trivia is made up of multiple characters. - public var isCollection: Bool { charactersLen > 0 } - - /// Indicates if the trivia contains only whitespace characters. - public var isBlank: Bool { - characters.contains { $0.isWhitespace } - } - - /// Indicates if the trivia contains newline characters. - public var isNewLine: Bool { - characters.contains { $0.isNewline } - } + public var isCollection: Bool { characters != nil } /// Initializes a new `Trivia` instance. /// @@ -76,27 +74,17 @@ public class Trivia { /// - name: A name of the trivia. /// - comment: A doc comment describing the trivia. /// - characters: A list of characters that make up the trivia. - /// - swiftCharacters: A list of characters as they would appear in Swift code. /// - isComment: Indicates if the trivia represents a comment. init( name: TokenSyntax, comment: SwiftSyntax.Trivia, - characters: [Character] = [], - swiftCharacters: [Character] = [], - isComment: Bool = false + characters: String? = nil, + traits: TriviaTraits = [] ) { self.name = name self.comment = comment - self.isComment = isComment self.characters = characters - - // Swift sometimes doesn't support escaped characters like \f or \v; - // we should allow specifying alternatives explicitly. - if !swiftCharacters.isEmpty { - self.swiftCharacters = swiftCharacters - } else { - self.swiftCharacters = characters - } + self.traits = traits } } @@ -104,116 +92,80 @@ public let TRIVIAS: [Trivia] = [ Trivia( name: "Backslash", comment: #"A backslash that is at the end of a line in a multi-line string literal to escape the newline."#, - characters: [ - Character("\\") - ], - swiftCharacters: [ - Character("\\") - ] + characters: "\\" ), Trivia( name: "BlockComment", comment: #"A developer block comment, starting with '/*' and ending with '*/'."#, - isComment: true + traits: [.comment] ), Trivia( name: "CarriageReturn", comment: #"A newline '\r' character."#, - characters: [ - Character("\r") - ], - swiftCharacters: [ - Character("\r") - ] + characters: "\r", + traits: [.whitespace, .newline] ), Trivia( name: "CarriageReturnLineFeed", comment: #"A newline consists of contiguous '\r' and '\n' characters."#, - characters: [ - Character("\r"), - Character("\n"), - ], - swiftCharacters: [ - Character("\r"), - Character("\n"), - ] + characters: "\r\n", + traits: [.whitespace, .newline] ), Trivia( name: "DocBlockComment", comment: #"A documentation block comment, starting with '/**' and ending with '*/'."#, - isComment: true + traits: [.comment] ), Trivia( name: "DocLineComment", - comment: #"A documentation line comment, starting with '///'."#, - isComment: true + comment: #"A documentation line comment, starting with '///' and excluding the trailing newline."#, + traits: [.comment] ), // Swift don't support form feed '\f' so we use the raw unicode Trivia( name: "Formfeed", comment: #"A form-feed 'f' character."#, - characters: [ - Character("\u{c}") - ], - swiftCharacters: [ - Character("\u{240C}") - ] + characters: "\u{000C}", + traits: [.whitespace] ), Trivia( name: "LineComment", - comment: #"A developer line comment, starting with '//'"#, - isComment: true + comment: #"A developer line comment, starting with '//' and excluding the trailing newline."#, + traits: [.comment] ), Trivia( name: "Newline", comment: #"A newline '\n' character."#, - characters: [ - Character("\n") - ], - swiftCharacters: [ - Character("\n") - ] + characters: "\n", + traits: [.whitespace, .newline] ), Trivia( name: "Pound", comment: #"A '#' that is at the end of a line in a multi-line string literal to escape the newline."#, - characters: [ - Character("#") - ], - swiftCharacters: [ - Character("#") - ] + characters: "#" ), Trivia( name: "Space", comment: #"A space ' ' character."#, - characters: [ - Character(" ") - ], - swiftCharacters: [ - Character(" ") - ] + characters: " ", + traits: [.whitespace, .spaceOrTab] ), Trivia( name: "Tab", comment: #"A tab '\t' character."#, - characters: [ - Character("\t") - ], - swiftCharacters: [ - Character("\t") - ] + characters: "\t", + traits: [.whitespace, .spaceOrTab] ), Trivia( @@ -225,11 +177,7 @@ public let TRIVIAS: [Trivia] = [ Trivia( name: "VerticalTab", comment: #"A vertical tab '\v' character."#, - characters: [ - Character("\u{b}") - ], - swiftCharacters: [ - Character("\u{2B7F}") - ] + characters: "\u{000B}", + traits: [.whitespace] ), ] diff --git a/Sources/CodeGeneration/SyntaxSupport/TypeConvertible.swift b/Sources/CodeGeneration/SyntaxSupport/TypeConvertible.swift new file mode 100644 index 0000000..d783c77 --- /dev/null +++ b/Sources/CodeGeneration/SyntaxSupport/TypeConvertible.swift @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 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 + +/// Instances of a conforming type should provide syntax type information to be used in code generation. +public protocol TypeConvertible { + /// Whether this is one of the syntax base nodes. + var isBase: Bool { + get + } + + /// The type name of this node in the SwiftSyntax module. + var syntaxType: TypeSyntax { + get + } + + /// For base nodes, the name of the corresponding protocol to which all the + /// concrete nodes that have this base kind, conform. + var protocolType: TypeSyntax { + get + } + + /// Whether the node is public API and not underscored/deprecated and can thus be referenced in docc links. + var isAvailableInDocc: Bool { + get + } +} + +public extension TypeConvertible { + /// If this node is non-experimental a docc link wrapped in two backticks. + /// + /// For experimental nodes, the node's type name in code font. + var doccLink: String { + if self.isAvailableInDocc { + return "``\(self.syntaxType)``" + } else { + return "`\(self.syntaxType)`" + } + } +} diff --git a/Sources/CodeGeneration/SyntaxSupport/TypeNodes.swift b/Sources/CodeGeneration/SyntaxSupport/TypeNodes.swift index 93a2598..250450a 100644 --- a/Sources/CodeGeneration/SyntaxSupport/TypeNodes.swift +++ b/Sources/CodeGeneration/SyntaxSupport/TypeNodes.swift @@ -18,19 +18,23 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "leftSquare", - deprecatedName: "leftSquareBracket", kind: .token(choices: [.token(.leftSquare)]) ), Child( name: "element", - deprecatedName: "elementType", kind: .node(kind: .type) ), Child( name: "rightSquare", - deprecatedName: "rightSquareBracket", kind: .token(choices: [.token(.rightSquare)]) ), + ], + childHistory: [ + [ + "leftSquare": .renamed(from: "leftSquareBracket"), + "element": .renamed(from: "elementType"), + "rightSquare": .renamed(from: "rightSquareBracket"), + ] ] ), @@ -45,13 +49,25 @@ public let TYPE_NODES: [Node] = [ Child( name: "specifiers", kind: .collection(kind: .typeSpecifierList, collectionElementName: "Specifier", defaultsToEmpty: true), - documentation: "A list of specifiers that can be attached to the type, such as `inout`, `isolated`, or `consuming`." + documentation: + "A list of specifiers that can be attached to the type, such as `inout`, `isolated`, or `consuming`." ), Child( name: "attributes", kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), documentation: "A list of attributes that can be attached to the type, such as `@escaping`." ), + Child( + name: "lateSpecifiers", + kind: .collection( + kind: .typeSpecifierList, + collectionElementName: "Specifier", + defaultsToEmpty: true, + generateDeprecatedAddFunction: false + ), + documentation: + "A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'." + ), Child( name: "baseType", kind: .node(kind: .type), @@ -119,9 +135,13 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "constraint", - deprecatedName: "baseType", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "constraint": .renamed(from: "baseType") + ] ] ), @@ -132,12 +152,10 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "leftSquare", - deprecatedName: "leftSquareBracket", kind: .token(choices: [.token(.leftSquare)]) ), Child( name: "key", - deprecatedName: "keyType", kind: .node(kind: .type), nameForDiagnostics: "key type" ), @@ -147,15 +165,21 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "value", - deprecatedName: "valueType", kind: .node(kind: .type), nameForDiagnostics: "value type" ), Child( name: "rightSquare", - deprecatedName: "rightSquareBracket", kind: .token(choices: [.token(.rightSquare)]) ), + ], + childHistory: [ + [ + "leftSquare": .renamed(from: "leftSquareBracket"), + "key": .renamed(from: "keyType"), + "value": .renamed(from: "valueType"), + "rightSquare": .renamed(from: "rightSquareBracket"), + ] ] ), @@ -173,8 +197,11 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "parameters", - deprecatedName: "arguments", - kind: .collection(kind: .tupleTypeElementList, collectionElementName: "Parameter", deprecatedCollectionElementName: "Argument") + kind: .collection( + kind: .tupleTypeElementList, + collectionElementName: "Parameter", + deprecatedCollectionElementName: "Argument" + ) ), Child( name: "rightParen", @@ -187,9 +214,14 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "returnClause", - deprecatedName: "output", kind: .node(kind: .returnClause) ), + ], + childHistory: [ + [ + "parameters": .renamed(from: "arguments"), + "returnClause": .renamed(from: "output"), + ] ] ), @@ -200,7 +232,6 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "leftAngle", - deprecatedName: "leftAngleBracket", kind: .token(choices: [.token(.leftAngle)]) ), Child( @@ -209,9 +240,14 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "rightAngle", - deprecatedName: "rightAngleBracket", kind: .token(choices: [.token(.rightAngle)]) ), + ], + childHistory: [ + [ + "leftAngle": .renamed(from: "leftAngleBracket"), + "rightAngle": .renamed(from: "rightAngleBracket"), + ] ] ), @@ -232,14 +268,29 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "argument", - deprecatedName: "argumentType", - kind: .node(kind: .type) + kind: .nodeChoices(choices: [ + Child( + name: "type", + kind: .node(kind: .type) + ), + Child( + name: "expr", + kind: .node(kind: .expr) + ), + ]), + documentation: + "The argument type for a generic argument. This can either be a regular type argument or an expression for value generics." ), Child( name: "trailingComma", kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "argument": .renamed(from: "argumentType") + ] ] ), @@ -259,6 +310,47 @@ public let TYPE_NODES: [Node] = [ ] ), + Node( + kind: .inlineArrayType, + base: .type, + nameForDiagnostics: "inline array type", + documentation: "An inline array type `[3 of Int]`, sugar for `InlineArray<3, Int>`.", + children: [ + Child( + name: "leftSquare", + kind: .token(choices: [.token(.leftSquare)]) + ), + Child( + name: "count", + kind: .node(kind: .genericArgument), + nameForDiagnostics: "count", + documentation: """ + The `count` argument for the inline array type. + + - Note: In semantically valid Swift code, this is always an integer or a wildcard type, e.g `_` in `[_ of Int]`. + """ + ), + Child( + name: "separator", + kind: .token(choices: [.keyword(.of)]) + ), + Child( + name: "element", + kind: .node(kind: .genericArgument), + nameForDiagnostics: "element type", + documentation: """ + The `element` argument for the inline array type. + + - Note: In semantically valid Swift code, this is always a type. + """ + ), + Child( + name: "rightSquare", + kind: .token(choices: [.token(.rightSquare)]) + ), + ] + ), + Node( kind: .memberType, base: .type, @@ -273,6 +365,12 @@ public let TYPE_NODES: [Node] = [ name: "period", kind: .token(choices: [.token(.period)]) ), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + nameForDiagnostics: "module selector", + isOptional: true + ), Child( name: "name", kind: .token(choices: [.token(.identifier), .keyword(.self)]), @@ -283,6 +381,11 @@ public let TYPE_NODES: [Node] = [ kind: .node(kind: .genericArgumentClause), isOptional: true ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ] ] ), @@ -302,9 +405,13 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "metatypeSpecifier", - deprecatedName: "typeOrProtocol", kind: .token(choices: [.keyword(.Type), .keyword(.Protocol)]) ), + ], + childHistory: [ + [ + "metatypeSpecifier": .renamed(from: "typeOrProtocol") + ] ] ), @@ -315,15 +422,19 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "genericParameterClause", - deprecatedName: "genericParameters", kind: .node(kind: .genericParameterClause), documentation: "The parameter clause that defines the generic parameters." ), Child( name: "type", - deprecatedName: "baseType", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "genericParameterClause": .renamed(from: "genericParameters"), + "type": .renamed(from: "baseType"), + ] ] ), @@ -354,9 +465,13 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "type", - deprecatedName: "patternType", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "type": .renamed(from: "patternType") + ] ] ), @@ -371,9 +486,13 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "repetitionPattern", - deprecatedName: "patternType", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "repetitionPattern": .renamed(from: "patternType") + ] ] ), @@ -388,9 +507,13 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "pack", - deprecatedName: "packType", kind: .node(kind: .type) ), + ], + childHistory: [ + [ + "pack": .renamed(from: "packType") + ] ] ), @@ -399,6 +522,12 @@ public let TYPE_NODES: [Node] = [ base: .type, nameForDiagnostics: "type", children: [ + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + nameForDiagnostics: "module selector", + isOptional: true + ), Child( name: "name", kind: .token(choices: [ @@ -413,6 +542,11 @@ public let TYPE_NODES: [Node] = [ kind: .node(kind: .genericArgumentClause), isOptional: true ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ] ] ), @@ -433,13 +567,11 @@ public let TYPE_NODES: [Node] = [ children: [ Child( name: "inoutKeyword", - deprecatedName: "inOut", kind: .token(choices: [.keyword(.inout)]), isOptional: true ), Child( name: "firstName", - deprecatedName: "name", kind: .token(choices: [.token(.identifier), .token(.wildcard)]), nameForDiagnostics: "name", isOptional: true @@ -469,6 +601,12 @@ public let TYPE_NODES: [Node] = [ kind: .token(choices: [.token(.comma)]), isOptional: true ), + ], + childHistory: [ + [ + "inoutKeyword": .renamed(from: "inOut"), + "firstName": .renamed(from: "name"), + ] ] ), @@ -538,15 +676,47 @@ public let TYPE_NODES: [Node] = [ ), Node( - kind: .lifetimeSpecifierArguments, + kind: .lifetimeTypeSpecifier, base: .syntax, experimentalFeature: .nonescapableTypes, + nameForDiagnostics: "lifetime specifier", + documentation: "A specifier that specifies function parameter on whose lifetime a type depends", + children: [ + Child( + name: "dependsOnKeyword", + kind: .token(choices: [.keyword(.dependsOn)]), + documentation: "lifetime dependence specifier on the return type" + ), + Child( + name: "leftParen", + kind: .token(choices: [.token(.leftParen)]) + ), + Child( + name: "scopedKeyword", + kind: .token(choices: [.keyword(.scoped)]), + documentation: "lifetime of return value is scoped to the lifetime of the original value", + isOptional: true + ), + Child( + name: "arguments", + kind: .collection(kind: .lifetimeSpecifierArgumentList, collectionElementName: "Arguments") + ), + Child( + name: "rightParen", + kind: .token(choices: [.token(.rightParen)]) + ), + ] + ), + + Node( + kind: .nonisolatedSpecifierArgument, + base: .syntax, nameForDiagnostics: nil, documentation: """ - An optional argument passed to a type parameter. + A single argument that can be added to a nonisolated specifier: 'nonsending'. ### Example - `borrow(data)` in `func foo(data: Array) -> borrow(data) ComplexReferenceType` + `data` in `func foo(data: nonisolated(nonsending) () async -> Void) -> X` """, traits: [ "Parenthesized" @@ -557,11 +727,8 @@ public let TYPE_NODES: [Node] = [ kind: .token(choices: [.token(.leftParen)]) ), Child( - name: "arguments", - kind: .collection(kind: .lifetimeSpecifierArgumentList, collectionElementName: "Arguments"), - documentation: """ - The function parameters that the lifetime of the annotated type depends on. - """ + name: "nonsendingKeyword", + kind: .token(choices: [.keyword(.nonsending)]) ), Child( name: "rightParen", @@ -571,25 +738,18 @@ public let TYPE_NODES: [Node] = [ ), Node( - kind: .lifetimeTypeSpecifier, + kind: .nonisolatedTypeSpecifier, base: .syntax, - experimentalFeature: .nonescapableTypes, - nameForDiagnostics: "lifetime specifier", - documentation: "A specifier that specifies function parameter on whose lifetime a type depends", + nameForDiagnostics: "'nonisolated' specifier", children: [ Child( - name: "specifier", - kind: .token(choices: [ - .keyword(._copy), - .keyword(._consume), - .keyword(._borrow), - .keyword(._mutate), - ]), - documentation: "The specifier token that's attached to the type." + name: "nonisolatedKeyword", + kind: .token(choices: [.keyword(.nonisolated)]) ), Child( - name: "arguments", - kind: .node(kind: .lifetimeSpecifierArguments) + name: "argument", + kind: .node(kind: .nonisolatedSpecifierArgument), + isOptional: true ), ] ), @@ -610,8 +770,7 @@ public let TYPE_NODES: [Node] = [ .keyword(._const), .keyword(.borrowing), .keyword(.consuming), - .keyword(.transferring), - .keyword(._resultDependsOn), + .keyword(.sending), ]), documentation: "The specifier token that's attached to the type." ) @@ -622,6 +781,6 @@ public let TYPE_NODES: [Node] = [ kind: .typeSpecifierList, base: .syntaxCollection, nameForDiagnostics: nil, - elementChoices: [.simpleTypeSpecifier, .lifetimeTypeSpecifier] + elementChoices: [.simpleTypeSpecifier, .lifetimeTypeSpecifier, .nonisolatedTypeSpecifier] ), ] diff --git a/Sources/CodeGeneration/SyntaxSupport/Utils.swift b/Sources/CodeGeneration/SyntaxSupport/Utils.swift index ed2091f..032192d 100644 --- a/Sources/CodeGeneration/SyntaxSupport/Utils.swift +++ b/Sources/CodeGeneration/SyntaxSupport/Utils.swift @@ -58,9 +58,9 @@ extension SwiftSyntax.Trivia { } } -public extension Collection { +extension Collection { /// If the collection contains a single element, return it, otherwise `nil`. - var only: Element? { + public var only: Element? { if !isEmpty && index(after: startIndex) == endIndex { return self.first! } else { @@ -69,14 +69,29 @@ public extension Collection { } } -public extension TokenSyntax { - var backtickedIfNeeded: TokenSyntax { - if Keyword.allCases.map(\.spec).contains(where: { - $0.name == self.description && ($0.isLexerClassified || $0.name == "Type" || $0.name == "Protocol") - }) { +private extension Keyword { + static let backticksNeeded: Set = Set( + Keyword.allCases.map(\.spec).filter { + $0.isLexerClassified || $0.name == "Type" || $0.name == "Protocol" + }.map(\.name) + ) +} + +extension TokenSyntax { + public var declNameOrVarCallName: Self { + if Keyword.backticksNeeded.contains(self.description) { return "`\(self)`" } else { return self } } + + public var nonVarCallNameOrLabelDeclName: Self { + switch self.tokenKind { + case .keyword(.`init`), .identifier("init"): + return "`init`" + default: + return self + } + } } diff --git a/Sources/SwiftAstGenLib/PackageTestTargetParser.swift b/Sources/SwiftAstGenLib/PackageTestTargetParser.swift index 4ccc1e2..735ac7f 100644 --- a/Sources/SwiftAstGenLib/PackageTestTargetParser.swift +++ b/Sources/SwiftAstGenLib/PackageTestTargetParser.swift @@ -2,7 +2,6 @@ import Foundation import SwiftParser -/// Visitor that extracts test target information from Package.swift import SwiftSyntax private class TestTargetVisitor: SyntaxVisitor { diff --git a/Sources/SwiftAstGenLib/ScalaAstGenerator.swift b/Sources/SwiftAstGenLib/ScalaAstGenerator.swift index 866af4b..1509aac 100644 --- a/Sources/SwiftAstGenLib/ScalaAstGenerator.swift +++ b/Sources/SwiftAstGenLib/ScalaAstGenerator.swift @@ -1,6 +1,12 @@ import CodeGeneration import Foundation +private func indented(_ value: Any) -> String { + String(describing: value) + .trimmingCharacters(in: .whitespacesAndNewlines) + .replacingOccurrences(of: "\n", with: "\n\t") +} + public class ScalaAstGenerator { private let defaultScalaOutFileUrl = URL(fileURLWithPath: "./SwiftNodeSyntax.scala") @@ -16,11 +22,11 @@ public class ScalaAstGenerator { } private func dateString() -> String { - let currentDateTime = Date() let formatter = DateFormatter() - formatter.timeStyle = .long - formatter.dateStyle = .long - return formatter.string(from: currentDateTime) + formatter.locale = Locale(identifier: "en_US_POSIX") + // Explicit format avoids locale-inserted narrow no-break spaces around AM/PM on recent OSes. + formatter.dateFormat = "MMMM d, yyyy 'at' h:mm:ss a zzz" + return formatter.string(from: Date()) } private func backtickedIfNeeded(name: String) -> String { @@ -37,19 +43,10 @@ public class ScalaAstGenerator { """ } - public init() throws { - if FileManager.default.fileExists(atPath: defaultScalaOutFileUrl.path) { - try FileManager.default.removeItem(at: defaultScalaOutFileUrl) - } - _ = FileManager.default.createFile( - atPath: defaultScalaOutFileUrl.path, - contents: nil, - attributes: nil - ) - } + public init() {} public func generate() throws { - let allBaseNodeNames = Set(SYNTAX_NODES.map(baseNode)).map { "\($0)" } + let allBaseNodeNames = Set(SYNTAX_NODES.map(baseNode)).sorted() let baseNodes = allBaseNodeNames.map { """ @@ -62,7 +59,7 @@ public class ScalaAstGenerator { "sealed trait \($0.traitName)" } else { """ - \n\t\(String(describing: $0.documentation).replacingOccurrences(of: "\n", with: "\n\t")) + \n\t\(indented($0.documentation)) \tsealed trait \($0.traitName) """ } @@ -70,13 +67,7 @@ public class ScalaAstGenerator { let allNodes = NON_BASE_SYNTAX_NODES.map { node in let syntaxType = node.kind.syntaxType - let inherits = inheritsFrom(node: node) - let inheritsString = - if inherits.count == 1 { - "extends \(inherits[0])" - } else { - "extends \(inherits[0]) with \(inherits[1...inherits.count-1].joined(separator: " with "))" - } + let inheritsString = "extends \(inheritsFrom(node: node).joined(separator: " with "))" let allChildren = node.layoutNode?.children ?? [] var childrenString = "" @@ -85,12 +76,13 @@ public class ScalaAstGenerator { allChildren .filter { !$0.isUnexpectedNodes } .map { child in - let name = backtickedIfNeeded(name: "\(child.varOrCaseName)") + let varName = lowercaseFirstWord(name: child.name) + let name = backtickedIfNeeded(name: varName) let childType = TypeGenerator.type(for: child) if child.isOptional { - return "\tdef \(name): Option[\(childType)] = _childrenMap.get(\"\(child.varOrCaseName)\").map(c => createSwiftNode(c).asInstanceOf[\(childType)])" + return "\tdef \(name): Option[\(childType)] = _childrenMap.get(\"\(varName)\").map(c => createSwiftNode(c).asInstanceOf[\(childType)])" } else { - return "\tdef \(name): \(childType) = createSwiftNode(_childrenMap(\"\(child.varOrCaseName)\")).asInstanceOf[\(childType)]" + return "\tdef \(name): \(childType) = createSwiftNode(_childrenMap(\"\(varName)\")).asInstanceOf[\(childType)]" } }.joined(separator: "\n\t") } else { @@ -100,20 +92,14 @@ public class ScalaAstGenerator { "\tdef children: Seq[\(elementType)] = json(\"children\").arr.iterator.map(c => createSwiftNode(c).asInstanceOf[\(elementType)]).toSeq" } - var documentation = String(describing: node.documentation) - if documentation.isEmpty { - documentation = "/// No documentation available." - } else { - documentation = documentation.replacingOccurrences(of: "\n", with: "\n\t") - } + let indentedDoc = indented(node.documentation) + let documentation = indentedDoc.isEmpty ? "/// No documentation available." : indentedDoc let childrenDoc = node.layoutNode?.grammar ?? node.collectionNode?.grammar ?? "/// no children available" - let childrenDocString = String(describing: childrenDoc).replacingOccurrences( - of: "\n", with: "\n\t") + let childrenDocString = indented(childrenDoc) - var containedInDocString = String(describing: node.containedIn) - containedInDocString = containedInDocString.replacingOccurrences(of: "\n", with: "\n\t") + let containedInDocString = indented(node.containedIn) let docString = """ \n\t/** diff --git a/Sources/SwiftAstGenLib/SwiftAstGenerator.swift b/Sources/SwiftAstGenLib/SwiftAstGenerator.swift index 19b80b5..a343a94 100644 --- a/Sources/SwiftAstGenLib/SwiftAstGenerator.swift +++ b/Sources/SwiftAstGenLib/SwiftAstGenerator.swift @@ -2,42 +2,42 @@ import Foundation public class SwiftAstGenerator { - private var srcDir: URL - private var outputDir: URL - private var prettyPrint: Bool - private var ignorePathsFromPackageSwift: [String] = [] + private static let ignoredPathSubstrings: [String] = [ + "/.", "/__", "/tests/", "/specs/", "/test/", "/spec/", + ] + + private let srcDir: URL + private let outputDir: URL + private let prettyPrint: Bool + private let ignorePathsFromPackageSwift: [String] private let availableProcessors: Int = ProcessInfo.processInfo.activeProcessorCount public init(srcDir: URL, outputDir: URL, prettyPrint: Bool) throws { self.srcDir = srcDir self.outputDir = outputDir self.prettyPrint = prettyPrint - self.ignorePathsFromPackageSwift = PackageTestTargetParser(srcDir: srcDir).getTestTargetPaths() + self.ignorePathsFromPackageSwift = PackageTestTargetParser(srcDir: srcDir) + .getTestTargetPaths() + .map { $0.lowercased() } - if !FileManager.default.fileExists(atPath: outputDir.path) { - try FileManager.default.createDirectory( - atPath: outputDir.path, - withIntermediateDirectories: true, - attributes: nil - ) - } + try FileManager.default.createDirectory( + atPath: outputDir.path, + withIntermediateDirectories: true, + attributes: nil + ) } - private func ignoreDirectory(name: String) -> Bool { - let nameLowercased: String = name.lowercased() - return nameLowercased.contains("/.") - || nameLowercased.contains("/__") - || nameLowercased.contains("/tests/") - || nameLowercased.contains("/specs/") - || nameLowercased.contains("/test/") - || nameLowercased.contains("/spec/") - || ignorePathsFromPackageSwift.contains { nameLowercased.contains($0.lowercased()) } + private func shouldIgnore(path: String) -> Bool { + let pathLowercased = path.lowercased() + for substring in Self.ignoredPathSubstrings where pathLowercased.contains(substring) { + return true + } + return ignorePathsFromPackageSwift.contains { pathLowercased.contains($0) } } - private func parseFile(fileUrl: URL) { + private func parseFile(fileUrl: URL, relativeFilePath: String) { do { - let relativeFilePath = fileUrl.relativePath(from: srcDir)! - let astJsonString = try SyntaxParser.parse( + let astJsonData = try SyntaxParser.parse( srcDir: srcDir, fileUrl: fileUrl, relativeFilePath: relativeFilePath, @@ -49,19 +49,13 @@ public class SwiftAstGenerator { .appendingPathExtension("json") let outfileDirUrl = outFileUrl.deletingLastPathComponent() - if !FileManager.default.fileExists(atPath: outfileDirUrl.path) { - try FileManager.default.createDirectory( - atPath: outfileDirUrl.path, - withIntermediateDirectories: true, - attributes: nil - ) - } - - try astJsonString.write( - to: outFileUrl, - atomically: true, - encoding: String.Encoding.utf8 + try FileManager.default.createDirectory( + atPath: outfileDirUrl.path, + withIntermediateDirectories: true, + attributes: nil ) + + try astJsonData.write(to: outFileUrl, options: .atomic) print("Generated AST for file: `\(fileUrl.path)`") } catch { print("Parsing failed for file: `\(fileUrl.path)` (\(error))") @@ -83,9 +77,9 @@ public class SwiftAstGenerator { let fileAttributes = try! fileURL.resourceValues(forKeys: [.isRegularFileKey]) if fileAttributes.isRegularFile! && fileURL.pathExtension == "swift" { let relativeFilePath = fileURL.relativePath(from: srcDir)! - if !ignoreDirectory(name: "/\(relativeFilePath)") { + if !shouldIgnore(path: "/\(relativeFilePath)") { queue.addOperation { - self.parseFile(fileUrl: fileURL) + self.parseFile(fileUrl: fileURL, relativeFilePath: relativeFilePath) } } } diff --git a/Sources/SwiftAstGenLib/SyntaxParser.swift b/Sources/SwiftAstGenLib/SyntaxParser.swift index a2ca135..ee734a4 100644 --- a/Sources/SwiftAstGenLib/SyntaxParser.swift +++ b/Sources/SwiftAstGenLib/SyntaxParser.swift @@ -17,7 +17,7 @@ extension SyntaxProtocol { } let sourceRange = sourceRange(converter: converter) - let rangeNode = Range( + let rangeNode = SourceRange( startOffset: sourceRange.start.offset, endOffset: sourceRange.end.offset, startLine: sourceRange.start.line, @@ -57,8 +57,8 @@ struct SyntaxParser { /// - Parameter text: The input string to count lines in. /// - Returns: The number of lines in the string. static func countLines(in text: String) -> Int { - // Use CharacterSet.newlines which matches \n, \r, \r\n, Unicode line/paragraph separators, etc. - // Split omitting empty subsequences to correctly handle trailing newlines. + // `Character.isNewline` matches \n, \r, \r\n, and Unicode line/paragraph separators. + // Keep empty subsequences so trailing newlines are counted correctly. let lines = text.split(omittingEmptySubsequences: false, whereSeparator: { $0.isNewline }) return lines.count } @@ -68,7 +68,7 @@ struct SyntaxParser { fileUrl: URL, relativeFilePath: String, prettyPrint: Bool - ) throws -> String { + ) throws -> Data { let code = try String(contentsOf: fileUrl) let loc = countLines(in: code) let opPrecedence = OperatorTable.standardOperators @@ -86,7 +86,8 @@ struct SyntaxParser { let encoder = JSONEncoder() if prettyPrint { encoder.outputFormatting = .prettyPrinted } - return String(decoding: try encoder.encode(treeNode), as: UTF8.self) + // JSONEncoder output is always UTF-8; return Data so callers can write directly without a round-trip through String. + return try encoder.encode(treeNode) } } diff --git a/Sources/SwiftAstGenLib/TreeNode.swift b/Sources/SwiftAstGenLib/TreeNode.swift index cc3b46d..5f53b20 100644 --- a/Sources/SwiftAstGenLib/TreeNode.swift +++ b/Sources/SwiftAstGenLib/TreeNode.swift @@ -10,13 +10,13 @@ final class TreeNode: Codable { var name: String var tokenKind: String var nodeType: String - var range: Range + var range: SourceRange var children: [TreeNode] init( tokenKind: String, nodeType: String, - range: Range, + range: SourceRange, children: [TreeNode] ) { self.index = -1 @@ -27,7 +27,7 @@ final class TreeNode: Codable { self.children = children } } -struct Range: Codable { +struct SourceRange: Codable { let startOffset: Int let endOffset: Int let startLine: Int diff --git a/Sources/SwiftAstGenLib/TypeGenerator.swift b/Sources/SwiftAstGenLib/TypeGenerator.swift index e35853f..9e1d38d 100644 --- a/Sources/SwiftAstGenLib/TypeGenerator.swift +++ b/Sources/SwiftAstGenLib/TypeGenerator.swift @@ -11,10 +11,10 @@ struct TypeGenerator { switch child.kind { case .node(let kind): return "\(kind.syntaxType)" - case .nodeChoices(let choices): + case .nodeChoices(let choices, _): let choicesDescriptions = choices.map { type(for: $0) } return "\(choicesDescriptions.joined(separator: " | "))" - case .collection(let kind, _, _, _): + case .collection(let kind, _, _, _, _): return "\(kind.syntaxType)" case .token(_, _, _): return "SwiftToken" diff --git a/Tests/SwiftAstGenTests/PackageTestTargetParserTests.swift b/Tests/SwiftAstGenTests/PackageTestTargetParserTests.swift index 4664552..6571fd2 100644 --- a/Tests/SwiftAstGenTests/PackageTestTargetParserTests.swift +++ b/Tests/SwiftAstGenTests/PackageTestTargetParserTests.swift @@ -4,7 +4,7 @@ import XCTest @testable import class SwiftAstGenLib.PackageTestTargetParser -final class PackageTestTargetParserTests: XCTestCase { +final class PackageTestTargetParserTests: XCTestCase, TestUtils { static var allTests = [ ("testSingleTestTarget", testSingleTestTarget), @@ -15,20 +15,8 @@ final class PackageTestTargetParserTests: XCTestCase { ("testMissingPackageSwift", testMissingPackageSwift), ] - private func createTemporaryDirectory() -> URL { - let tempDir = URL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent("PackageTestTargetParserTests-\(UUID().uuidString)", isDirectory: true) - try! FileManager.default.createDirectory(atPath: tempDir.path, withIntermediateDirectories: true) - return tempDir - } - - private func cleanup(directory: URL) { - try? FileManager.default.removeItem(at: directory) - } - private func createPackageSwift(in directory: URL, content: String) { - let packageSwiftUrl = directory.appendingPathComponent("Package.swift") - try! content.write(to: packageSwiftUrl, atomically: true, encoding: .utf8) + createFile(at: directory, path: "Package.swift", content: content) } func testSingleTestTarget() throws { diff --git a/Tests/SwiftAstGenTests/TestUtils.swift b/Tests/SwiftAstGenTests/TestUtils.swift index 3f3279b..8d26bb8 100644 --- a/Tests/SwiftAstGenTests/TestUtils.swift +++ b/Tests/SwiftAstGenTests/TestUtils.swift @@ -26,18 +26,16 @@ extension TestUtils { func createFile(at baseDir: URL, path: String, content: String) { let fileUrl = baseDir.appendingPathComponent(path) let dirUrl = fileUrl.deletingLastPathComponent() - - if !FileManager.default.fileExists(atPath: dirUrl.path) { - try! FileManager.default.createDirectory( - atPath: dirUrl.path, - withIntermediateDirectories: true - ) - } - + + try! FileManager.default.createDirectory( + atPath: dirUrl.path, + withIntermediateDirectories: true + ) + try! content.write(to: fileUrl, atomically: true, encoding: .utf8) } - private func temporaryFileURL(fileName: String) -> URL? { + private func temporaryFileURL(fileName: String) -> URL { return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent( fileName, isDirectory: true @@ -45,10 +43,9 @@ extension TestUtils { } func loadJson(file: URL) -> TreeNode? { - let decoder = JSONDecoder() guard - let content = try? String(contentsOf: file, encoding: .utf8), - let treeNode = try? decoder.decode(TreeNode.self, from: content.data(using: .utf8)!) + let data = try? Data(contentsOf: file), + let treeNode = try? JSONDecoder().decode(TreeNode.self, from: data) else { return nil } @@ -56,7 +53,7 @@ extension TestUtils { } func withCode(code: String, testFunction: (URL, URL, URL) throws -> Void) throws { - let srcTmpDir = temporaryFileURL(fileName: createUniqueName())! + let srcTmpDir = temporaryFileURL(fileName: createUniqueName()) let outTmpDir = srcTmpDir.appendingPathComponent("out", isDirectory: true) let srcFile = srcTmpDir.appendingPathComponent("source.swift") let jsonFile = outTmpDir.appendingPathComponent("source.swift.json") @@ -66,11 +63,6 @@ extension TestUtils { withIntermediateDirectories: true, attributes: nil ) - _ = FileManager.default.createFile( - atPath: srcFile.path, - contents: nil, - attributes: nil - ) try code.write( to: srcFile, atomically: true,