diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 96e24e703e1..2d063195ca8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,7 +8,7 @@ # Order is important. The last matching pattern has the most precedence. # Owner of anything in SwiftSyntax not owned by anyone else. -* @ahoppen @bnbarham +* @ahoppen @bnbarham @hamishknight @rintaro # Macros /Sources/SwiftSyntaxMacros @DougGregor diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index b260e72b4ed..613ece97b95 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -1,6 +1,8 @@ name: Create PR to merge main into release branch # In the first period after branching the release branch, we typically want to include many changes from `main` in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch. # Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. To do so, disable the workflow as described in https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow +permissions: + contents: read on: schedule: - cron: '0 9 * * MON' diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 44f68cce276..fd95e0caf27 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -1,5 +1,8 @@ name: Publish Release +permissions: + contents: read + on: workflow_dispatch: inputs: @@ -24,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - run: | - if [[ "${{ github.triggering_actor }}" != "ahoppen" ]]; then + if [[ "${{ github.triggering_actor }}" != "bnbarham" ]]; then echo "${{ github.triggering_actor }} is not allowed to create a release" exit 1 fi @@ -38,7 +41,7 @@ jobs: with: # We require that releases of swift-syntax build without warnings linux_build_command: swift test -Xswiftc -warnings-as-errors ${{ matrix.release && '-c release' || '' }} - windows_build_command: swift test -Xswiftc -warnings-as-errors ${{ matrix.release && '-c release' || '' }} + windows_build_command: Invoke-Program swift test -Xswiftc -warnings-as-errors ${{ matrix.release && '-c release' || '' }} create_tag: name: Create Tag runs-on: ubuntu-latest diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index fd8e6f14272..fa5a292067c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -1,5 +1,10 @@ name: Pull request +permissions: + contents: read + +# PRs created by GitHub Actions don't kick off further actions (https://github.com/peter-evans/create-pull-request/blob/d57e551ebc1a16dee0b8c9ea6d24dba7627a6e35/docs/concepts-guidelines.md#triggering-further-workflow-runs). +# As a workaround, we mark automerge PRs that are created by GitHub actions as draft and trigger the GitHub actions by marking the PR as ready for review. We'd prefer not re-triggering testing on a normal user's PR in this case, but skipping them causes the checks to reset. on: pull_request: types: [opened, reopened, synchronize, ready_for_review] @@ -11,20 +16,17 @@ concurrency: jobs: tests: name: Test - # PRs created by GitHub Actions don't kick off further actions (https://github.com/peter-evans/create-pull-request/blob/d57e551ebc1a16dee0b8c9ea6d24dba7627a6e35/docs/concepts-guidelines.md#triggering-further-workflow-runs). - # As a workaround, we mark automerge PRs that are created by GitHub actions as draft and trigger the GitHub actions by marking the PR as ready for review. But we don't want to re-trigger testing this when a normal user's PR is marked as ready for review. - if: (github.event.action != 'ready_for_review') || (github.event.action == 'ready_for_review' && github.event.pull_request.user.login == 'github-actions[bot]') uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main + with: + enable_wasm_sdk_build: true soundness: name: Soundness - if: (github.event.action != 'ready_for_review') || (github.event.action == 'ready_for_review' && github.event.pull_request.user.login == 'github-actions[bot]') uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main with: api_breakage_check_enabled: false # https://github.com/swiftlang/swift-syntax/issues/3010 docs_check_additional_arguments: "--disable-parameters-and-returns-validation" verify_source_code: name: Validate generated code - if: (github.event.action != 'ready_for_review') || (github.event.action == 'ready_for_review' && github.event.pull_request.user.login == 'github-actions[bot]') runs-on: ubuntu-latest container: image: swift:latest @@ -37,7 +39,6 @@ jobs: run: "./swift-syntax-dev-utils verify-source-code --toolchain /usr" test_using_swift_syntax_dev_utils_linux: name: Run tests using swift-syntax-dev-utils (Linux) - if: (github.event.action != 'ready_for_review') || (github.event.action == 'ready_for_review' && github.event.pull_request.user.login == 'github-actions[bot]') runs-on: ubuntu-latest container: image: swift:latest @@ -50,7 +51,6 @@ jobs: run: "./swift-syntax-dev-utils test --enable-rawsyntax-validation --enable-test-fuzzing --toolchain /usr" test_using_swift_syntax_dev_utils_windows: name: Run tests using swift-syntax-dev-utils (Windows) - if: (github.event.action != 'ready_for_review') || (github.event.action == 'ready_for_review' && github.event.pull_request.user.login == 'github-actions[bot]') runs-on: windows-2022 steps: - name: Pull Docker image diff --git a/.spi.yml b/.spi.yml index 1118de8d5a6..6f03c01270f 100644 --- a/.spi.yml +++ b/.spi.yml @@ -18,6 +18,7 @@ builder: - SwiftParser - SwiftParserDiagnostics - SwiftRefactor + - SwiftWarningControl - SwiftSyntaxBuilder - SwiftSyntaxMacros - SwiftSyntaxMacroExpansion diff --git a/.swift-format b/.swift-format index 41a022f2627..e882b3623ce 100644 --- a/.swift-format +++ b/.swift-format @@ -1,18 +1,76 @@ { - "version": 1, - "lineLength": 120, + "fileScopedDeclarationPrivacy": { + "accessLevel": "private" + }, + "indentConditionalCompilationBlocks": false, + "indentSwitchCaseLabels": false, "indentation": { "spaces": 2 }, + "lineBreakAroundMultilineExpressionChainComponents": false, + "lineBreakBeforeControlFlowKeywords": false, "lineBreakBeforeEachArgument": true, - "indentConditionalCompilationBlocks": false, + "lineBreakBeforeEachGenericRequirement": false, + "lineBreakBetweenDeclarationAttributes": false, + "lineLength": 120, + "maximumBlankLines": 1, + "multiElementCollectionTrailingCommas": true, + "noAssignmentInExpressions": { + "allowedFunctions": [ + "XCTAssertNoThrow" + ] + }, "prioritizeKeepingFunctionOutputTogether": true, + "reflowMultilineStringLiterals": { + "never": { + + } + }, + "respectsExistingLineBreaks": true, "rules": { + "AllPublicDeclarationsHaveDocumentation": false, + "AlwaysUseLiteralForEmptyCollectionInit": false, "AlwaysUseLowerCamelCase": false, "AmbiguousTrailingClosureOverload": false, + "BeginDocumentationCommentWithOneLineSummary": false, + "DoNotUseSemicolons": false, + "DontRepeatTypeInStaticProperties": false, + "FileScopedDeclarationPrivacy": true, + "FullyIndirectEnum": true, + "GroupNumericLiterals": true, + "IdentifiersMustBeASCII": true, + "NeverForceUnwrap": false, + "NeverUseForceTry": false, + "NeverUseImplicitlyUnwrappedOptionals": false, + "NoAccessLevelOnExtensionDeclaration": false, + "NoAssignmentInExpressions": true, "NoBlockComments": false, + "NoCasesWithOnlyFallthrough": true, + "NoEmptyTrailingClosureParentheses": true, + "NoLabelsInCasePatterns": true, + "NoLeadingUnderscores": false, + "NoParensAroundConditions": true, + "NoPlaygroundLiterals": true, + "NoVoidReturnOnFunctionSignature": true, + "OmitExplicitReturns": false, + "OneCasePerLine": true, + "OneVariableDeclarationPerLine": true, + "OnlyOneTrailingClosureArgument": true, "OrderedImports": true, + "ReplaceForEachWithForLoop": true, + "ReturnVoidInsteadOfEmptyTuple": true, + "TypeNamesShouldBeCapitalized": true, + "UseEarlyExits": false, + "UseExplicitNilCheckInConditions": true, "UseLetInEveryBoundCaseVariable": false, - "UseSynthesizedInitializer": false - } + "UseShorthandTypeNames": true, + "UseSingleLinePropertyGetter": true, + "UseSynthesizedInitializer": false, + "UseTripleSlashForDocumentationComments": true, + "UseWhereClausesInForLoops": false, + "ValidateDocumentationComments": false + }, + "spacesAroundRangeFormationOperators": false, + "spacesBeforeEndOfLineComments": 2, + "version": 1, } diff --git a/BUILD.bazel b/BUILD.bazel index 86301f4eec6..1f548b9bb3b 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -378,16 +378,16 @@ cc_library( name = "_InstructionCounter", srcs = glob(["Sources/_InstructionCounter/src/*.c"]), hdrs = glob(["Sources/_InstructionCounter/include/*.h"]), + aspect_hints = ["@build_bazel_rules_swift//swift:auto_module"], includes = ["Sources/_InstructionCounter/include"], - tags = ["swift_module=_InstructionCounter"], visibility = ["//visibility:private"], ) cc_library( name = "_SwiftLibraryPluginProviderCShims", hdrs = glob(["Sources/_SwiftLibraryPluginProviderCShims/include/*.h"]), + aspect_hints = ["@build_bazel_rules_swift//swift:auto_module"], includes = ["Sources/_SwiftLibraryPluginProviderCShims/include"], - tags = ["swift_module=_SwiftLibraryPluginProviderCShims"], visibility = ["//visibility:private"], ) @@ -395,8 +395,8 @@ cc_library( name = "_SwiftSyntaxCShims", srcs = glob(["Sources/_SwiftSyntaxCShims/*.c"]), hdrs = glob(["Sources/_SwiftSyntaxCShims/include/*.h"]), + aspect_hints = ["@build_bazel_rules_swift//swift:auto_module"], includes = ["Sources/_SwiftSyntaxCShims/include"], - tags = ["swift_module=_SwiftSyntaxCShims"], visibility = ["//visibility:private"], ) diff --git a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift index 26041a0cf89..cc561d27a49 100644 --- a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift @@ -74,6 +74,11 @@ public let ATTRIBUTE_NODES: [Node] = [ // 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:' @@ -122,8 +127,7 @@ public let ATTRIBUTE_NODES: [Node] = [ Child( name: "abiArguments", // Special arguments for declaration syntax. e.g. @abi(func abiName() -> Int) - kind: .node(kind: .abiAttributeArguments), - experimentalFeature: .abiAttribute + kind: .node(kind: .abiAttributeArguments) ), ]), documentation: """ @@ -256,7 +260,6 @@ public let ATTRIBUTE_NODES: [Node] = [ Node( kind: .abiAttributeArguments, base: .syntax, - experimentalFeature: .abiAttribute, nameForDiagnostics: "ABI-providing declaration", documentation: "The arguments of the '@abi' attribute", children: [ @@ -578,7 +581,7 @@ public let ATTRIBUTE_NODES: [Node] = [ Node( kind: .implementsAttributeArguments, base: .syntax, - nameForDiagnostics: "@_implements arguemnts", + nameForDiagnostics: "@_implements arguments", documentation: "The arguments for the `@_implements` attribute of the form `Type, methodName(arg1Label:arg2Label:)`", children: [ @@ -719,6 +722,19 @@ public let ATTRIBUTE_NODES: [Node] = [ ] ), + 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, diff --git a/CodeGeneration/Sources/SyntaxSupport/Child.swift b/CodeGeneration/Sources/SyntaxSupport/Child.swift index 683c0441953..6c9badb33eb 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Child.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Child.swift @@ -47,7 +47,8 @@ public enum ChildKind { kind: SyntaxNodeKind, collectionElementName: String? = nil, defaultsToEmpty: Bool = false, - deprecatedCollectionElementName: String? = nil + 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 @@ -128,17 +129,30 @@ public class Child: NodeChoiceConvertible { 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 } } + /// 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)) @@ -160,8 +174,8 @@ public class Child: NodeChoiceConvertible { return "\(raw: newestName.withFirstCharacterUppercased)Options" } - /// If this child is deprecated, describes the sequence of accesses necessary - /// to reach the equivalent value using non-deprecated children; if the child + /// 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: @@ -198,12 +212,6 @@ public class Child: NodeChoiceConvertible { /// of the child. That information is not directly available anywhere. public let newestChildPath: [Child] - /// True if this child was created by a `Child.Refactoring`. Such children - /// are part of the compatibility layer and are therefore deprecated. - public var isHistorical: Bool { - !newestChildPath.isEmpty - } - /// 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] { @@ -213,7 +221,7 @@ public class Child: NodeChoiceConvertible { var workStack = Array(newerChildPath.reversed()) while let elem = workStack.popLast() { - if elem.isHistorical { + if !elem.newestChildPath.isEmpty { // There's an even newer version. Start working on that. workStack.append(contentsOf: elem.newestChildPath.reversed()) } else { @@ -268,7 +276,7 @@ public class Child: NodeChoiceConvertible { /// 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 @@ -283,7 +291,7 @@ public class Child: NodeChoiceConvertible { 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 @@ -307,7 +315,8 @@ public class Child: NodeChoiceConvertible { documentation: String? = nil, isOptional: Bool = false, providesDefaultInitialization: Bool = true, - newerChildPath: [Child] = [] + newerChildPath: [Child] = [], + isHistorical: Bool = false ) { precondition(name.first?.isLowercase ?? true, "The first letter of a child’s name should be lowercase") self.name = name @@ -319,11 +328,18 @@ public class Child: NodeChoiceConvertible { 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, newerChildPath: [Child]) { + init( + renamingTo replacementName: String? = nil, + makingHistorical: Bool = false, + makingHidden: Bool = false, + newerChildPath: [Child] + ) { let other = newerChildPath.last! self.name = replacementName ?? other.name @@ -335,6 +351,8 @@ public class Child: NodeChoiceConvertible { 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 @@ -360,7 +378,8 @@ public class Child: NodeChoiceConvertible { documentation: nil, isOptional: true, providesDefaultInitialization: true, - newerChildPath: newerChildPath + newerChildPath: newerChildPath, + isHistorical: (earlier?.isHistorical ?? false) || (later?.isHistorical ?? false) ) } } @@ -416,5 +435,8 @@ extension Child { /// 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/CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift b/CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift index 7247e17a909..9bd9c760f8f 100644 --- a/CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift @@ -177,7 +177,7 @@ public let COMMON_NODES: [Node] = [ kind: .decl, base: .syntax, nameForDiagnostics: "declaration", - parserFunction: "parseDeclaration" + parserFunction: "parseDeclarationOrIfConfig" ), Node( @@ -330,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, @@ -382,4 +400,18 @@ public let COMMON_NODES: [Node] = [ 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/CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift b/CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift index f95f3143669..ce70152e6e3 100644 --- a/CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift +++ b/CodeGeneration/Sources/SyntaxSupport/CompatibilityLayer.swift @@ -18,8 +18,9 @@ public struct CompatibilityLayer { /// Deprecated members that the compatibility layer needs for each trait. public var deprecatedMembersByTrait: [String: DeprecatedMemberInfo] = [:] - /// Cache for `replacementChildren(for:by:)`. 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. + /// 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`. @@ -46,13 +47,23 @@ public struct CompatibilityLayer { /// Returns the child or children that would have existed in place of this /// child before this refactoring was applied. - private mutating func replacementChildren(for newerChild: Child, by refactoring: Child.Refactoring) -> [Child] { + /// + /// - 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] ) ] @@ -72,8 +83,20 @@ public struct CompatibilityLayer { } return newerGrandchildren.map { newerGrandchild in - Child(newerChildPath: [newerChild, newerGrandchild]) + Child( + makingHistorical: historical, + newerChildPath: [newerChild, newerGrandchild] + ) } + + case .introduced: + return [ + Child( + makingHistorical: historical, + makingHidden: true, + newerChildPath: [newerChild] + ) + ] } } @@ -100,6 +123,7 @@ public struct CompatibilityLayer { 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 @@ -115,58 +139,160 @@ public struct CompatibilityLayer { deprecatedMembersByTrait[trait.traitName] = result } - /// Compute and cache compatibility layer information for the given children. + /// Compute compatibility layer information for the given children. private mutating func computeMembersFor( typeName: String, initialChildren: [Child], history: Child.History, areRequirements: Bool ) -> DeprecatedMemberInfo { - // The results that will ultimately be saved into the 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] = [] - var initSignatures: [InitSignature] = [] - // Temporary working state for the loop. - var children = initialChildren - var knownVars = Set(children) + /// 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] = [] - 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 + /// 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) } - for changeSet in history { + /// 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 = replacementChildren(for: children[i], by: refactoring) + 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 currentName != replacementChildren.first?.name { - unexpectedChildrenWithNewNames.insert(children[i - 1]) + if let firstNewChild = replacementChildren.first, isDifferent(firstNewChild), + let adjacentUnexpectedChild = lastVisibleChild(before: i, isUnexpectedNodes: true) + { + unexpectedChildrenWithNewNames.insert(adjacentUnexpectedChild) } - if currentName != replacementChildren.last?.name { - unexpectedChildrenWithNewNames.insert(children[i + replacementChildren.count]) + 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 { + for unexpectedChild in unexpectedChildrenWithNewNames where !unexpectedChild.isHidden { precondition(unexpectedChild.isUnexpectedNodes) let i = firstIndexOfChild(named: unexpectedChild.name) - let earlier = children[checked: i - 1] - let later = children[checked: i + 1] + 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]) @@ -176,33 +302,24 @@ public struct CompatibilityLayer { 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. - vars += children.filter { knownVars.insert($0).inserted } + // 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 { - initSignatures.append(InitSignature(children: children)) + self.inits.append(InitSignature(children: children)) } } - return DeprecatedMemberInfo(vars: vars, inits: initSignatures) - } -} - -/// 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] = [] -} - -extension Array { - /// Returns `nil` if `i` is out of bounds, or the indicated element otherwise. - fileprivate subscript(checked i: Index) -> Element? { - get { - return indices.contains(i) ? self[i] : nil + @_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/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift index 0dea2d63426..50daee24036 100644 --- a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift @@ -28,7 +28,7 @@ public let DECL_NODES: [Node] = [ ), Child( name: "trailingPeriod", - kind: .token(choices: [.token(.period)]), + kind: .token(choices: [.token(.period), .token(.colonColon)]), isOptional: true ), ], @@ -120,6 +120,8 @@ public let DECL_NODES: [Node] = [ .keyword(._modify), .keyword(.modify), .keyword(.`init`), + .keyword(.borrow), + .keyword(.mutate), ]) ), Child( @@ -1307,6 +1309,37 @@ public let DECL_NODES: [Node] = [ ] ), + 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." + ), + ] + ), + Node( kind: .inheritedTypeList, base: .syntaxCollection, @@ -1523,6 +1556,11 @@ public let DECL_NODES: [Node] = [ kind: .token(choices: [.token(.pound)]), documentation: "The `#` sign." ), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "macroName", kind: .token(choices: [.token(.identifier)]) @@ -1561,12 +1599,15 @@ public let DECL_NODES: [Node] = [ ), ], childHistory: [ + [ + "moduleSelector": .introduced + ], [ "pound": .renamed(from: "poundToken"), "macroName": .renamed(from: "macro"), "genericArgumentClause": .renamed(from: "genericArguments"), "arguments": .renamed(from: "argumentList"), - ] + ], ] ), diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index bdc09c1f462..e6c5a0304ba 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -19,10 +19,10 @@ public enum ExperimentalFeature: String, CaseIterable { case nonescapableTypes case trailingComma case coroutineAccessors - case abiAttribute case keypathWithMethodMembers case oldOwnershipOperatorSpellings - case inlineArrayTypeSugar + case defaultIsolationPerFile + case borrowAndMutateAccessors /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -39,14 +39,14 @@ public enum ExperimentalFeature: String, CaseIterable { return "TrailingComma" case .coroutineAccessors: return "CoroutineAccessors" - case .abiAttribute: - return "ABIAttribute" case .keypathWithMethodMembers: return "KeypathWithMethodMembers" case .oldOwnershipOperatorSpellings: return "OldOwnershipOperatorSpellings" - case .inlineArrayTypeSugar: - return "InlineArrayTypeSugar" + case .defaultIsolationPerFile: + return "DefaultIsolationPerFile" + case .borrowAndMutateAccessors: + return "BorrowAndMutateAccessors" } } @@ -65,14 +65,14 @@ public enum ExperimentalFeature: String, CaseIterable { return "trailing commas" case .coroutineAccessors: return "coroutine accessors" - case .abiAttribute: - return "@abi attribute" case .keypathWithMethodMembers: return "keypaths with method members" case .oldOwnershipOperatorSpellings: return "`_move` and `_borrow` as ownership operators" - case .inlineArrayTypeSugar: - return "sugar type for InlineArray" + case .defaultIsolationPerFile: + return "set default actor isolation for a file" + case .borrowAndMutateAccessors: + return "borrow and mutate accessors" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index bfe92058354..4920cc7af98 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -105,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 @@ -1002,6 +1002,11 @@ public let EXPR_NODES: [Node] = [ base: .expr, nameForDiagnostics: nil, children: [ + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "baseName", kind: .token(choices: [ @@ -1023,10 +1028,13 @@ public let EXPR_NODES: [Node] = [ ), ], childHistory: [ + [ + "moduleSelector": .introduced + ], [ "baseName": .renamed(from: "identifier"), "argumentNames": .renamed(from: "declNameArguments"), - ] + ], ] ), @@ -1358,6 +1366,11 @@ public let EXPR_NODES: [Node] = [ kind: .token(choices: [.token(.pound)]), documentation: "The `#` sign." ), + Child( + name: "moduleSelector", + kind: .node(kind: .moduleSelector), + isOptional: true + ), Child( name: "macroName", kind: .token(choices: [.token(.identifier)]) @@ -1396,12 +1409,15 @@ public let EXPR_NODES: [Node] = [ ), ], childHistory: [ + [ + "moduleSelector": .introduced + ], [ "pound": .renamed(from: "poundToken"), "macroName": .renamed(from: "macro"), "genericArgumentClause": .renamed(from: "genericArguments"), "arguments": .renamed(from: "argumentList"), - ] + ], ] ), @@ -1981,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" @@ -2023,7 +2039,7 @@ public let EXPR_NODES: [Node] = [ Node( kind: .ternaryExpr, base: .expr, - nameForDiagnostics: "ternay expression", + nameForDiagnostics: "ternary expression", documentation: """ The ternary operator with operator precedences resolved. diff --git a/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift b/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift index ae84735dbf9..819cbf0f082 100644 --- a/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift +++ b/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift @@ -41,7 +41,7 @@ struct GrammarGenerator { 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 { diff --git a/CodeGeneration/Sources/SyntaxSupport/InitSignature.swift b/CodeGeneration/Sources/SyntaxSupport/InitSignature.swift index e93ac4ed12a..799f62b1535 100644 --- a/CodeGeneration/Sources/SyntaxSupport/InitSignature.swift +++ b/CodeGeneration/Sources/SyntaxSupport/InitSignature.swift @@ -12,9 +12,26 @@ /// Represents an initializer that should be generated. public struct InitSignature { - /// The list of children which shoudl be given parameters and initialized by the initializer. + /// 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 diff --git a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift index 7c4b72eeafb..2b1809ae634 100644 --- a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift @@ -104,6 +104,7 @@ public enum Keyword: CaseIterable { case _PackageDescription case _read case _RefCountedObject + case specialized case _specialize case _spi_available case _Trivial @@ -200,6 +201,7 @@ public enum Keyword: CaseIterable { case metadata case modify case module + case mutate case mutableAddressWithNativeOwner case mutableAddressWithOwner case mutating @@ -268,6 +270,7 @@ public enum Keyword: CaseIterable { case unsafe case unsafeAddress case unsafeMutableAddress + case using case `var` case visibility case weak @@ -275,7 +278,6 @@ public enum Keyword: CaseIterable { case `while` case willSet case wrt - case x case yield public var spec: KeywordSpec { @@ -340,6 +342,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("_read") case ._RefCountedObject: return KeywordSpec("_RefCountedObject") + case .specialized: + return KeywordSpec("specialized") case ._specialize: return KeywordSpec("_specialize") case ._spi_available: @@ -357,7 +361,7 @@ public enum Keyword: CaseIterable { case ._version: return KeywordSpec("_version") case .abi: - return KeywordSpec("abi", experimentalFeature: .abiAttribute) + return KeywordSpec("abi") case .accesses: return KeywordSpec("accesses") case .actor: @@ -532,6 +536,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("modify", experimentalFeature: .coroutineAccessors) case .module: return KeywordSpec("module") + case .mutate: + return KeywordSpec("mutate", experimentalFeature: .borrowAndMutateAccessors) case .mutableAddressWithNativeOwner: return KeywordSpec("mutableAddressWithNativeOwner") case .mutableAddressWithOwner: @@ -668,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: @@ -682,8 +690,6 @@ public enum Keyword: CaseIterable { return KeywordSpec("willSet") case .wrt: return KeywordSpec("wrt") - case .x: - return KeywordSpec("x", experimentalFeature: .inlineArrayTypeSugar) case .yield: return KeywordSpec("yield") } diff --git a/CodeGeneration/Sources/SyntaxSupport/Node.swift b/CodeGeneration/Sources/SyntaxSupport/Node.swift index 913473c1c85..73fa9404835 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Node.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Node.swift @@ -127,7 +127,8 @@ public class Node: NodeChoiceConvertible { parserFunction: TokenSyntax? = nil, traits: [String] = [], children: [Child] = [], - childHistory: Child.History = [] + childHistory: Child.History = [], + noInterleaveUnexpected: Bool = false ) { precondition(base != .syntaxCollection) precondition(base.isBase, "unknown base kind '\(base)' for node '\(kind)'") @@ -140,7 +141,8 @@ public class Node: NodeChoiceConvertible { self.documentation = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.parserFunction = parserFunction - let childrenWithUnexpected = kind.isBase ? children : interleaveUnexpectedChildren(children) + let childrenWithUnexpected = + (kind.isBase || noInterleaveUnexpected) ? children : interleaveUnexpectedChildren(children) self.data = .layout(children: childrenWithUnexpected, childHistory: childHistory, traits: traits) } @@ -284,7 +286,7 @@ public struct LayoutNode { /// This includes unexpected children public var children: [Child] { switch node.data { - case .layout(children: let children, childHistory: _, traits: _): + case .layout(let children, childHistory: _, traits: _): return children case .collection: preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") @@ -299,7 +301,7 @@ public struct LayoutNode { /// The history of the layout node's children. public var childHistory: Child.History { switch node.data { - case .layout(children: _, childHistory: let childHistory, traits: _): + case .layout(children: _, let childHistory, traits: _): return childHistory case .collection: preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") @@ -309,7 +311,7 @@ public struct LayoutNode { /// Traits that the node conforms to. public var traits: [String] { switch node.data { - case .layout(children: _, childHistory: _, traits: let traits): + case .layout(children: _, childHistory: _, let traits): return traits case .collection: preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`") @@ -361,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 } } @@ -391,7 +393,7 @@ fileprivate extension Child { return [kind] case .nodeChoices(let choices, _): return choices.flatMap(\.kinds) - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): return [kind] case .token: return [.token] @@ -399,7 +401,7 @@ fileprivate extension Child { } } -fileprivate func interleaveUnexpectedChildren(_ children: [Child]) -> [Child] { +private func interleaveUnexpectedChildren(_ children: [Child]) -> [Child] { let liftedChildren = children.lazy.map(Optional.some) let pairedChildren = zip([nil] + liftedChildren, liftedChildren + [nil]) diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index 6e50fe39500..70eae620e70 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -206,6 +206,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case missingPattern case missingStmt case missingType + case moduleSelector case multipleTrailingClosureElement case multipleTrailingClosureElementList case namedOpaqueReturnType @@ -259,6 +260,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case simpleStringLiteralSegmentList case someOrAnyType case sourceFile + case specializedAttributeArgument case specializeAttributeArgumentList case specializeAvailabilityArgument case specializeTargetFunctionArgument @@ -299,11 +301,13 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case typeSpecifier case lifetimeSpecifierArguments case typeSpecifierList + case unexpectedCodeDecl case unexpectedNodes case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr case unsafeExpr + case usingDecl case valueBindingPattern case variableDecl case versionComponent @@ -454,6 +458,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon 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" diff --git a/CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift b/CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift index 2abbc28adc8..b276a715251 100644 --- a/CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift @@ -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/CodeGeneration/Sources/SyntaxSupport/Traits.swift b/CodeGeneration/Sources/SyntaxSupport/Traits.swift index c9eb2828931..10169c0a096 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Traits.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Traits.swift @@ -92,6 +92,11 @@ public let TRAITS: [Trait] = [ traitName: "FreestandingMacroExpansion", children: [ 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), @@ -101,12 +106,15 @@ public let TRAITS: [Trait] = [ 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( diff --git a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift index 3d65755fa45..250450a6e7d 100644 --- a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift @@ -57,6 +57,17 @@ public let TYPE_NODES: [Node] = [ 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), @@ -302,9 +313,8 @@ public let TYPE_NODES: [Node] = [ Node( kind: .inlineArrayType, base: .type, - experimentalFeature: .inlineArrayTypeSugar, nameForDiagnostics: "inline array type", - documentation: "An inline array type `[3 x Int]`, sugar for `InlineArray<3, Int>`.", + documentation: "An inline array type `[3 of Int]`, sugar for `InlineArray<3, Int>`.", children: [ Child( name: "leftSquare", @@ -317,12 +327,12 @@ public let TYPE_NODES: [Node] = [ 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 `[_ x Int]`. + - 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(.x)]) + kind: .token(choices: [.keyword(.of)]) ), Child( name: "element", @@ -355,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)]), @@ -365,6 +381,11 @@ public let TYPE_NODES: [Node] = [ kind: .node(kind: .genericArgumentClause), isOptional: true ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ] ] ), @@ -501,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: [ @@ -515,6 +542,11 @@ public let TYPE_NODES: [Node] = [ kind: .node(kind: .genericArgumentClause), isOptional: true ), + ], + childHistory: [ + [ + "moduleSelector": .introduced + ] ] ), diff --git a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift index 9ad78761e04..b01de637761 100644 --- a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift +++ b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift @@ -29,11 +29,11 @@ extension Child { public var buildableType: SyntaxBuildableType { let buildableKind: SyntaxOrTokenNodeKind switch kind { - case .node(kind: let kind): + case .node(let kind): buildableKind = .node(kind: kind) case .nodeChoices: buildableKind = .node(kind: .syntax) - case .collection(kind: let kind, _, _, _): + case .collection(let kind, _, _, _, _): buildableKind = .node(kind: kind) case .token: buildableKind = .token(self.tokenKind!) @@ -65,7 +65,7 @@ extension Child { return ExprSyntax("nil") } } - if case .collection(_, _, defaultsToEmpty: true, _) = kind { + if case .collection(_, _, defaultsToEmpty: true, _, _) = kind { return ExprSyntax("[]") } guard let token = token, isToken else { diff --git a/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift b/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift index a9b68bf05ce..a11131a6357 100644 --- a/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift +++ b/CodeGeneration/Sources/Utils/SyntaxBuildableType.swift @@ -122,7 +122,7 @@ public struct SyntaxBuildableType: Hashable { /// without any question marks attached. public var syntaxBaseName: TypeSyntax { switch kind { - case .node(kind: let kind): + case .node(let kind): return kind.syntaxType case .token: return "TokenSyntax" @@ -150,7 +150,7 @@ public struct SyntaxBuildableType: Hashable { /// that can be used to build the collection. public var resultBuilderType: TypeSyntax { switch kind { - case .node(kind: let kind): + case .node(let kind): return TypeSyntax("\(raw: kind.uppercasedFirstWordRawValue)Builder") case .token: preconditionFailure("Tokens cannot be constructed using result builders") diff --git a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift index 4889bf1d639..9ca90dcc3ed 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift @@ -226,7 +226,7 @@ struct GenerateSwiftSyntax: AsyncParsableCommand { } -fileprivate func generateFile( +private func generateFile( contents: @autoclosure () -> String, destination: URL, verbose: Bool diff --git a/CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift b/CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift index 97a61847829..9c7e8a46096 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift @@ -17,7 +17,7 @@ import Utils extension InitSignature { var compoundName: String { - let renamedArguments = children.map { child in + let renamedArguments = visibleChildren.map { child in if child.isUnexpectedNodes { return "_:" } else { @@ -28,8 +28,16 @@ extension InitSignature { return "init(leadingTrivia:\(renamedArguments)trailingTrivia:)" } + func generateInitializerAttributes() -> AttributeListSyntax { + return AttributeListSyntax { + if self.isExperimental { + AttributeSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .space) + } + } + } + func generateInitializerDeclHeader() -> SyntaxNodeString { - if children.isEmpty { + if visibleChildren.isEmpty { return "public init()" } @@ -66,7 +74,7 @@ extension InitSignature { let params = FunctionParameterListSyntax { FunctionParameterSyntax("leadingTrivia: Trivia? = nil") - for child in children { + for child in visibleChildren { createFunctionParameterSyntax(for: child) } @@ -74,8 +82,10 @@ extension InitSignature { .with(\.leadingTrivia, .newline) } + let attributes = generateInitializerAttributes() + return """ - public init( + \(attributes)public init( \(params) ) """ @@ -93,7 +103,7 @@ extension InitSignature { - Parameters: - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. \ If the node is empty, there is no token to attach the trivia to and the parameter is ignored. - \(children.compactMap(generateParamDocComment).joined(separator: "\n")) + \(visibleChildren.compactMap(generateParamDocComment).joined(separator: "\n")) - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. \ If the node is empty, there is no token to attach the trivia to and the parameter is ignored. """.removingEmptyLines @@ -114,7 +124,7 @@ extension InitSignature { var builderParameters: [FunctionParameterSyntax] = [] var delegatedInitArgs: [LabeledExprSyntax] = [] - for child in children { + for child in visibleChildren { /// The expression that is used to call the default initializer defined above. let produceExpr: ExprSyntax let childName = child.identifier @@ -174,10 +184,12 @@ extension InitSignature { FunctionParameterSyntax("trailingTrivia: Trivia? = nil") } + let attributes = generateInitializerAttributes() + return try InitializerDeclSyntax( """ /// A convenience initializer that allows initializing syntax collections using result builders - public init\(params) rethrows + \(attributes)public init\(params) rethrows """ ) { FunctionCallExprSyntax(callee: ExprSyntax("try self.init")) { @@ -191,7 +203,7 @@ extension InitSignature { } } -fileprivate func convertFromSyntaxProtocolToSyntaxType( +private func convertFromSyntaxProtocolToSyntaxType( child: Child ) -> ExprSyntax { let childName = child.identifier @@ -209,6 +221,7 @@ extension InitSignature { func makeArgumentsToInitializeNewestChildren() -> [LabeledExprSyntax] { var root: [InitParameterMapping] = [] + // This should also map hidden children. for child in children { InitParameterMapping.addChild(child, to: &root) } @@ -350,8 +363,12 @@ extension InitParameterMapping { func makeArgumentExpr() -> LabeledExprSyntax { let argValue = switch argument { - case .decl(olderChild: let olderChild): - ExprSyntax(DeclReferenceExprSyntax(baseName: olderChild.baseCallName)) + case .decl(let olderChild): + if olderChild.isHidden { + olderChild.defaultValue! + } else { + ExprSyntax(DeclReferenceExprSyntax(baseName: olderChild.baseCallName)) + } case .nestedInit(let initArgs): ExprSyntax( diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/ExperimentalFeaturesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/ExperimentalFeaturesFile.swift index 0879e1a3a41..4ebbaf0dd12 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/ExperimentalFeaturesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/ExperimentalFeaturesFile.swift @@ -20,7 +20,7 @@ let experimentalFeaturesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) """ extension Parser { @_spi(ExperimentalLanguageFeatures) - public struct ExperimentalFeatures: OptionSet, Sendable { + public struct ExperimentalFeatures: OptionSet, Hashable, Sendable { public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/LayoutNodesParsableFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/LayoutNodesParsableFile.swift index bb6006711f6..4c383adeee7 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/LayoutNodesParsableFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftparser/LayoutNodesParsableFile.swift @@ -68,7 +68,7 @@ let layoutNodesParsableFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { DeclSyntax( """ mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax { - guard let node = self.parseCodeBlockItem(isAtTopLevel: false, allowInitDecl: true) else { + guard let node = self.parseCodeBlockItem(allowInitDecl: true, until: { _ in false }) else { // The missing item is not necessary to be a declaration, // which is just a placeholder here return RawCodeBlockItemSyntax( diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/KeywordFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/KeywordFile.swift index 9b89f040daf..71b80a09b9e 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/KeywordFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/KeywordFile.swift @@ -40,20 +40,27 @@ let keywordFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { try! SwitchExprSyntax("switch text.count") { for (length, keywords) in keywordsByLength() { SwitchCaseSyntax("case \(raw: length):") { - try! SwitchExprSyntax("switch text") { - for keyword in keywords { - SwitchCaseSyntax("case \(literal: keyword.name):") { - ExprSyntax("self = .\(keyword.enumCaseCallName)") - } - } - SwitchCaseSyntax("default: return nil") - } + ExprSyntax("self.init(_length\(raw: length): text)") } } SwitchCaseSyntax("default: return nil") } } + // Split into individual initializers by length to reduce stack use + for (length, keywords) in keywordsByLength() { + try! InitializerDeclSyntax("private init?(_length\(raw: length) text: SyntaxText)") { + try! SwitchExprSyntax("switch text") { + for keyword in keywords { + SwitchCaseSyntax("case \(literal: keyword.name):") { + ExprSyntax("self = .\(keyword.enumCaseCallName)") + } + } + SwitchCaseSyntax("default: return nil") + } + } + } + DeclSyntax( """ /// This is really unfortunate. Really, we should have a `switch` in diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RawSyntaxValidationFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RawSyntaxValidationFile.swift index 15ea4eacba3..c4936f8d8df 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RawSyntaxValidationFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RawSyntaxValidationFile.swift @@ -209,7 +209,7 @@ let rawSyntaxValidationFile = try! SourceFileSyntax(leadingTrivia: copyrightHead } ExprSyntax("assertAnyHasNoError(kind, \(raw: index), \(verifiedChoices))") - case .token(choices: let choices, requiresLeadingSpace: _, requiresTrailingSpace: _): + case .token(let choices, requiresLeadingSpace: _, requiresTrailingSpace: _): let choices = ArrayExprSyntax { for choice in choices { switch choice { diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift index aea1375e54c..8053ab29c99 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift @@ -16,36 +16,40 @@ import SyntaxSupport import Utils let renamedChildrenCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) { - for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ !$0.childHistory.isEmpty }) { + for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode) { var deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: layoutNode) - try ExtensionDeclSyntax("extension \(layoutNode.type.syntaxBaseName)") { - for child in deprecatedMembers.vars { - makeCompatibilityVar(for: child) - if let addMethod = makeCompatibilityAddMethod(for: child) { - addMethod + if !deprecatedMembers.isEmpty { + try ExtensionDeclSyntax("extension \(layoutNode.type.syntaxBaseName)") { + for child in deprecatedMembers.vars { + makeCompatibilityVar(for: child) + if let addMethod = makeCompatibilityAddMethod(for: child) { + addMethod + } } - } - let renamedName = InitSignature(layoutNode).compoundName - for signature in deprecatedMembers.inits { - makeCompatibilityInit(for: signature, renamedName: renamedName) + let renamedName = InitSignature(layoutNode).compoundName + for signature in deprecatedMembers.inits { + makeCompatibilityInit(for: signature, renamedName: renamedName) + } } } } - for trait in TRAITS.filter({ !$0.childHistory.isEmpty }) { + for trait in TRAITS { var deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: trait) - try ExtensionDeclSyntax("extension \(trait.protocolName)") { - for child in deprecatedMembers.vars { - makeCompatibilityVar(for: child) - if let addMethod = makeCompatibilityAddMethod(for: child) { - addMethod + if !deprecatedMembers.isEmpty { + try ExtensionDeclSyntax("extension \(trait.protocolName)") { + for child in deprecatedMembers.vars { + makeCompatibilityVar(for: child) + if let addMethod = makeCompatibilityAddMethod(for: child) { + addMethod + } } - } - // Not currently generating compatibility inits for traits. + // Not currently generating compatibility inits for traits. + } } } } @@ -63,10 +67,16 @@ func makeCompatibilityVar(for child: Child) -> DeclSyntax { ExprSyntax(MemberAccessExprSyntax(base: base, name: child.baseCallName)) } + let attributes = AttributeListSyntax { + if child.isHistorical { + AttributeSyntax("@available(*, deprecated, renamed: \(literal: childPathString))") + .with(\.trailingTrivia, .newlines(1)) + } + } + return DeclSyntax( """ - @available(*, deprecated, renamed: \(literal: childPathString)) - public var \(child.identifier): \(type) { + \(attributes)public var \(child.identifier): \(type) { get { return \(childAccess) } @@ -85,7 +95,8 @@ func makeCompatibilityAddMethod(for child: Child) -> DeclSyntax? { kind: _, collectionElementName: let collectionElementName?, defaultsToEmpty: _, - deprecatedCollectionElementName: let deprecatedCollectionElementName? + deprecatedCollectionElementName: let deprecatedCollectionElementName?, + generateDeprecatedAddFunction: _ ) = child.kind { let childEltType = childNode.collectionElementType.syntaxBaseName @@ -104,11 +115,20 @@ func makeCompatibilityAddMethod(for child: Child) -> DeclSyntax? { } func makeCompatibilityInit(for signature: InitSignature, renamedName: String) -> InitializerDeclSyntax { - try! InitializerDeclSyntax( + let prefix: SyntaxNodeString + if signature.isHistorical { + prefix = """ + @available(*, deprecated, renamed: \(literal: renamedName)) + @_disfavoredOverload + + """ + } else { + prefix = "" + } + + return try! InitializerDeclSyntax( """ - @available(*, deprecated, renamed: \(literal: renamedName)) - @_disfavoredOverload - \(signature.generateInitializerDeclHeader()) + \(prefix)\(signature.generateInitializerDeclHeader()) """ ) { FunctionCallExprSyntax(callee: ExprSyntax("self.init")) { diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift index 6c5bd8bad67..cb3a5f184eb 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -176,7 +176,8 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax { // If needed, this could be added in the future, but for now withUnexpected should be sufficient. if let childNode = SYNTAX_NODE_MAP[child.syntaxNodeKind]?.collectionNode, !child.isUnexpectedNodes, - case .collection(_, collectionElementName: let childElt?, _, _) = child.kind + case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = + child.kind { let childEltType = childNode.collectionElementType.syntaxBaseName diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TriviaPiecesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TriviaPiecesFile.swift index 937ca2639ce..24f31b9c2d2 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TriviaPiecesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TriviaPiecesFile.swift @@ -268,7 +268,7 @@ let triviaPiecesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { try! generateIsHelpers(for: "RawTriviaPiece") } -fileprivate func generateIsHelpers(for pieceName: TokenSyntax) throws -> ExtensionDeclSyntax { +private func generateIsHelpers(for pieceName: TokenSyntax) throws -> ExtensionDeclSyntax { func generateHelper(_ header: SyntaxNodeString, trait: TriviaTraits) throws -> VariableDeclSyntax { try VariableDeclSyntax(header) { try SwitchExprSyntax("switch self") { diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/RenamedChildrenBuilderCompatibilityFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/RenamedChildrenBuilderCompatibilityFile.swift index 3f08259a46c..4d95acfa33a 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/RenamedChildrenBuilderCompatibilityFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/RenamedChildrenBuilderCompatibilityFile.swift @@ -18,25 +18,35 @@ import Utils let renamedChildrenBuilderCompatibilityFile = try! SourceFileSyntax(leadingTrivia: copyrightHeader) { importSwiftSyntax(accessLevel: .public) - for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode).filter({ !$0.childHistory.isEmpty }) { + for layoutNode in SYNTAX_NODES.compactMap(\.layoutNode) { let deprecatedMembers = SYNTAX_COMPATIBILITY_LAYER.deprecatedMembers(for: layoutNode) - - for signature in deprecatedMembers.inits { - if let convenienceInit = try signature.createConvenienceBuilderInitializer() { - let deprecatedNames = layoutNode.children - .filter { !$0.isUnexpectedNodes && !signature.children.contains($0) } - .compactMap { $0.identifier.description } - .joined(separator: ", ") - - DeclSyntax( - """ - extension \(layoutNode.type.syntaxBaseName) { - @available(*, deprecated, message: "Use an initializer with \(raw: deprecatedNames) argument(s).") - @_disfavoredOverload - \(convenienceInit) + if !deprecatedMembers.isEmpty { + for signature in deprecatedMembers.inits { + if let convenienceInit = try signature.createConvenienceBuilderInitializer() { + if signature.isHistorical { + let deprecatedNames = layoutNode.children + .filter { !$0.isUnexpectedNodes && !signature.children.contains($0) } + .compactMap { $0.identifier.description } + .joined(separator: ", ") + DeclSyntax( + """ + extension \(layoutNode.type.syntaxBaseName) { + @available(*, deprecated, message: "Use an initializer with \(raw: deprecatedNames) argument(s).") + @_disfavoredOverload + \(convenienceInit) + } + """ + ) + } else { + DeclSyntax( + """ + extension \(layoutNode.type.syntaxBaseName) { + \(convenienceInit) + } + """ + ) } - """ - ) + } } } } diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift index ba5909e2b00..c8dfc47dfcc 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift @@ -36,6 +36,8 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFileSyntax(le """ #if compiler(>=6) extension \(type): Swift.ExpressibleByStringInterpolation {} + // Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. + extension \(type): Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif """ ) diff --git a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift index d2eceb9334f..a686edb059d 100644 --- a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift +++ b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift @@ -13,7 +13,7 @@ import SyntaxSupport import XCTest -fileprivate func assertNoFailures( +private func assertNoFailures( _ failures: [ValidationFailure], message: String, file: StaticString = #filePath, @@ -34,7 +34,7 @@ fileprivate func assertNoFailures( XCTFail(message, file: file, line: line) } -fileprivate func assertFailuresMatchXFails( +private func assertFailuresMatchXFails( _ failures: [ValidationFailure], expectedFailures: [ValidationFailure], file: StaticString = #filePath, @@ -55,13 +55,13 @@ fileprivate extension ChildKind { return kind == otherKind case (.nodeChoices(let choices, _), .nodeChoices(let otherChoices, _)): return choices.count == otherChoices.count && zip(choices, otherChoices).allSatisfy { $0.hasSameType(as: $1) } - case (.collection(kind: let kind, _, _, _), .collection(kind: let otherKind, _, _, _)): + case (.collection(kind: let kind, _, _, _, _), .collection(kind: let otherKind, _, _, _, _)): return kind == otherKind case (.token(let choices, _, _), .token(let otherChoices, _, _)): return choices == otherChoices - case (.node(let kind), .collection(kind: let otherKind, _, _, _)): + case (.node(let kind), .collection(kind: let otherKind, _, _, _, _)): return kind == otherKind - case (.collection(kind: let kind, _, _, _), .node(let otherKind)): + case (.collection(kind: let kind, _, _, _, _), .node(let otherKind)): return kind == otherKind default: return false @@ -357,10 +357,6 @@ class ValidateSyntaxNodes: XCTestCase { "child 'leadingComma' has a comma keyword as its only token choice and should thus be named 'comma' or 'trailingComma'" ), // This is similar to `TrailingComma` - ValidationFailure( - node: .importPathComponent, - message: "child 'trailingPeriod' has a token as its only token choice and should thus be named 'period'" - ), // `~` is the only operator that’s allowed here ValidationFailure( node: .suppressedType, @@ -372,10 +368,11 @@ class ValidateSyntaxNodes: XCTestCase { message: "child 'defaultKeyword' has a single keyword as its only token choice and is followed by a colon. It should thus be named 'defaultLabel'" ), - // 'separator' is more descriptive than 'xKeyword' + // 'separator' is more descriptive than 'ofKeyword' ValidationFailure( node: .inlineArrayType, - message: "child 'separator' has a single keyword as its only token choice and should thus be named 'xKeyword'" + message: + "child 'separator' has a single keyword as its only token choice and should thus be named 'ofKeyword'" ), ] ) @@ -644,7 +641,12 @@ class ValidateSyntaxNodes: XCTestCase { assertFailuresMatchXFails( failures, - expectedFailures: [] + expectedFailures: [ + ValidationFailure( + node: .unexpectedCodeDecl, + message: "child 'unexpectedCode' is a SyntaxCollection but child is not marked as a collection" + ) + ] ) } diff --git a/EditorExtension/SwiftRefactorExtension/SourceEditorCommand.swift b/EditorExtension/SwiftRefactorExtension/SourceEditorCommand.swift index 777119368c5..012134909c6 100644 --- a/EditorExtension/SwiftRefactorExtension/SourceEditorCommand.swift +++ b/EditorExtension/SwiftRefactorExtension/SourceEditorCommand.swift @@ -40,7 +40,7 @@ final class SourceEditorCommand: NSObject, XCSourceEditorCommand { } override func visitAny(_ node: Syntax) -> Syntax? { - func withOpenedRefactoringProvider(_ providerType: T.Type) -> Syntax? { + func withOpenedRefactoringProvider(_ providerType: T.Type) throws -> Syntax? { guard let input = node.as(providerType.Input.self), providerType.Context.self == Void.self @@ -48,10 +48,10 @@ final class SourceEditorCommand: NSObject, XCSourceEditorCommand { return nil } let context = unsafeBitCast(Void(), to: providerType.Context.self) - return providerType.refactor(syntax: input, in: context).map { Syntax($0) } + return try Syntax(providerType.refactor(syntax: input, in: context)) } - return _openExistential(self.provider, do: withOpenedRefactoringProvider) + return try? _openExistential(self.provider, do: withOpenedRefactoringProvider) } } diff --git a/Examples/Sources/MacroExamples/Implementation/ComplexMacros/OptionSetMacro.swift b/Examples/Sources/MacroExamples/Implementation/ComplexMacros/OptionSetMacro.swift index 2d1706c9114..6a14a93213f 100644 --- a/Examples/Sources/MacroExamples/Implementation/ComplexMacros/OptionSetMacro.swift +++ b/Examples/Sources/MacroExamples/Implementation/ComplexMacros/OptionSetMacro.swift @@ -195,7 +195,7 @@ extension OptionSetMacro: MemberMacro { // Find all of the case elements. let caseElements: [EnumCaseElementSyntax] = optionsEnum.memberBlock.members.flatMap { member in guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { - return Array() + return [EnumCaseElementSyntax]() } return Array(caseDecl.elements) diff --git a/Examples/Sources/MacroExamples/Implementation/Expression/URLMacro.swift b/Examples/Sources/MacroExamples/Implementation/Expression/URLMacro.swift index 5b5ccf88951..c811c054158 100644 --- a/Examples/Sources/MacroExamples/Implementation/Expression/URLMacro.swift +++ b/Examples/Sources/MacroExamples/Implementation/Expression/URLMacro.swift @@ -29,7 +29,7 @@ public enum URLMacro: ExpressionMacro { throw CustomError.message("#URL requires a static string literal") } - guard let _ = URL(string: literalSegment.content.text) else { + guard URL(string: literalSegment.content.text) != nil else { throw CustomError.message("malformed url: \(argument)") } diff --git a/Examples/Sources/MacroExamples/Implementation/Member/MetaEnumMacro.swift b/Examples/Sources/MacroExamples/Implementation/Member/MetaEnumMacro.swift index af65ab35148..03f66de7391 100644 --- a/Examples/Sources/MacroExamples/Implementation/Member/MetaEnumMacro.swift +++ b/Examples/Sources/MacroExamples/Implementation/Member/MetaEnumMacro.swift @@ -95,7 +95,7 @@ extension EnumDeclSyntax { var caseElements: [EnumCaseElementSyntax] { memberBlock.members.flatMap { member in guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { - return Array() + return [EnumCaseElementSyntax]() } return Array(caseDecl.elements) diff --git a/Examples/Sources/MacroExamples/Implementation/Peer/AddAsyncMacro.swift b/Examples/Sources/MacroExamples/Implementation/Peer/AddAsyncMacro.swift index 923f002c6c4..1ebe01a8a07 100644 --- a/Examples/Sources/MacroExamples/Implementation/Peer/AddAsyncMacro.swift +++ b/Examples/Sources/MacroExamples/Implementation/Peer/AddAsyncMacro.swift @@ -42,7 +42,9 @@ public struct AddAsyncMacro: PeerMacro { } // This only makes sense void functions - if funcDecl.signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text != "Void" { + if let returnClause = funcDecl.signature.returnClause, + returnClause.type.as(IdentifierTypeSyntax.self)?.name.text != "Void" + { throw CustomError.message( "@addAsync requires an function that returns void" ) diff --git a/Examples/Sources/MacroExamples/Playground/PeerMacrosPlayground.swift b/Examples/Sources/MacroExamples/Playground/PeerMacrosPlayground.swift index bc5ba242ca1..f4bfaf335fc 100644 --- a/Examples/Sources/MacroExamples/Playground/PeerMacrosPlayground.swift +++ b/Examples/Sources/MacroExamples/Playground/PeerMacrosPlayground.swift @@ -17,12 +17,12 @@ func runPeerMacrosPlayground() { struct MyStruct { @AddAsync - func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) -> Void { + func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) { completionBlock(.success("a: \(a), b: \(b), value: \(value)")) } @AddAsync - func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) -> Void { + func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) { completionBlock(true) } } diff --git a/Examples/Tests/MacroExamples/Implementation/Peer/AddAsyncMacroTests.swift b/Examples/Tests/MacroExamples/Implementation/Peer/AddAsyncMacroTests.swift index 79178633770..f936f425dd0 100644 --- a/Examples/Tests/MacroExamples/Implementation/Peer/AddAsyncMacroTests.swift +++ b/Examples/Tests/MacroExamples/Implementation/Peer/AddAsyncMacroTests.swift @@ -79,6 +79,32 @@ final class AddAsyncMacroTests: XCTestCase { ) } + func testImplicitVoidResult() { + assertMacroExpansion( + """ + @AddAsync + func d(a: Int, completionBlock: @escaping (Bool) -> Void) { + } + """, + expandedSource: """ + func d(a: Int, completionBlock: @escaping (Bool) -> Void) { + } + + func d(a: Int) async -> Bool { + await withCheckedContinuation { continuation in + d(a: a) { returnValue in + + continuation.resume(returning: returnValue) + } + } + + } + """, + macros: macros, + indentationWidth: .spaces(2) + ) + } + func testExpansionOnStoredPropertyEmitsError() { assertMacroExpansion( """ @@ -105,18 +131,18 @@ final class AddAsyncMacroTests: XCTestCase { ) } - func testExpansionOnAsyncFunctionEmitsError() { + func testNonVoidResult() { assertMacroExpansion( """ struct Test { @AddAsync - async func sayHello() { + func sayHello() -> Int { } } """, expandedSource: """ struct Test { - async func sayHello() { + func sayHello() -> Int { } } """, @@ -132,4 +158,50 @@ final class AddAsyncMacroTests: XCTestCase { indentationWidth: .spaces(2) ) } + + func testAlreadyAsync() { + assertMacroExpansion( + """ + @AddAsync + func d(a: Int, completionBlock: @escaping (Bool) -> Void) async { + } + """, + expandedSource: """ + func d(a: Int, completionBlock: @escaping (Bool) -> Void) async { + } + """, + diagnostics: [ + DiagnosticSpec( + message: "@addAsync requires an non async function", + line: 1, + column: 1, + severity: .error + ) + ], + macros: macros, + indentationWidth: .spaces(2) + ) + } + + func testNoCompletionHandler() { + assertMacroExpansion( + """ + @AddAsync + func sayHello(x: Int) {} + """, + expandedSource: """ + func sayHello(x: Int) {} + """, + diagnostics: [ + DiagnosticSpec( + message: "@addAsync requires an function that has a completion handler as last parameter", + line: 1, + column: 1, + severity: .error + ) + ], + macros: macros, + indentationWidth: .spaces(2) + ) + } } diff --git a/MODULE.bazel b/MODULE.bazel index 957b5b67d67..2557653456d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -17,5 +17,10 @@ module( ) bazel_dep(name = "apple_support", version = "1.13.0", repo_name = "build_bazel_apple_support") -bazel_dep(name = "rules_apple", version = "3.3.0", repo_name = "build_bazel_rules_apple") -bazel_dep(name = "rules_swift", version = "1.18.0", max_compatibility_level = 2, repo_name = "build_bazel_rules_swift") +bazel_dep(name = "rules_apple", version = "4.0.1", repo_name = "build_bazel_rules_apple") +bazel_dep( + name = "rules_swift", + version = "2.9.0", + max_compatibility_level = 3, + repo_name = "build_bazel_rules_swift", +) diff --git a/Package.swift b/Package.swift index 844bfacc40a..3511e32325d 100644 --- a/Package.swift +++ b/Package.swift @@ -29,6 +29,7 @@ if buildDynamicLibrary { .library(name: "SwiftDiagnostics", targets: ["SwiftDiagnostics"]), .library(name: "SwiftIDEUtils", targets: ["SwiftIDEUtils"]), .library(name: "SwiftIfConfig", targets: ["SwiftIfConfig"]), + .library(name: "SwiftWarningControl", targets: ["SwiftWarningControl"]), .library(name: "SwiftLexicalLookup", targets: ["SwiftLexicalLookup"]), .library(name: "SwiftOperators", targets: ["SwiftOperators"]), .library(name: "SwiftParser", targets: ["SwiftParser"]), @@ -166,7 +167,7 @@ let package = Package( .target( name: "SwiftIfConfig", - dependencies: ["SwiftSyntax", "SwiftSyntaxBuilder", "SwiftDiagnostics", "SwiftOperators"], + dependencies: ["SwiftSyntax", "SwiftSyntaxBuilder", "SwiftDiagnostics", "SwiftOperators", "SwiftParser"], exclude: ["CMakeLists.txt"] ), @@ -180,6 +181,24 @@ let package = Package( ] ), + // MARK: SwiftWarningControl + + .target( + name: "SwiftWarningControl", + dependencies: ["SwiftSyntax", "SwiftParser"], + exclude: ["CMakeLists.txt", "SwiftWarningControl.md"] + ), + + .testTarget( + name: "SwiftWarningControlTest", + dependencies: [ + "_SwiftSyntaxTestSupport", + "SwiftWarningControl", + "SwiftParser", + "SwiftSyntaxMacrosGenericTestSupport", + ] + ), + // MARK: SwiftLexicalLookup .target( @@ -275,7 +294,13 @@ let package = Package( .target( name: "SwiftSyntaxMacros", - dependencies: ["SwiftDiagnostics", "SwiftParser", "SwiftSyntax", "SwiftSyntaxBuilder"], + dependencies: [ + "SwiftDiagnostics", + "SwiftIfConfig", + "SwiftParser", + "SwiftSyntax", + "SwiftSyntaxBuilder", + ], exclude: ["CMakeLists.txt"] ), @@ -322,6 +347,7 @@ let package = Package( "_SwiftSyntaxGenericTestSupport", "SwiftDiagnostics", "SwiftIDEUtils", + "SwiftIfConfig", "SwiftParser", "SwiftSyntaxMacros", "SwiftSyntaxMacroExpansion", @@ -393,7 +419,7 @@ let package = Package( .testTarget( name: "SwiftRefactorTest", - dependencies: ["_SwiftSyntaxTestSupport", "SwiftRefactor"] + dependencies: ["_SwiftSyntaxTestSupport", "SwiftIDEUtils", "SwiftRefactor"] ), // MARK: - Deprecated targets diff --git a/Release Notes/602.md b/Release Notes/602.md index 0d5750bdd2e..7ef16677d8f 100644 --- a/Release Notes/602.md +++ b/Release Notes/602.md @@ -54,7 +54,13 @@ - Pull Request: https://github.com/swiftlang/swift-syntax/pull/3028 - Migration steps: Use `AttributeSyntax.Arguments.argumentList(LabeledExprListSyntax)` instead. - Notes: Removed cases from `AttributeSyntax.Arguments`: `token(TokenSyntax)`, `string(StringLiteralExprSyntax)`, `conventionArguments(ConventionAttributeArgumentsSyntax)`, `conventionWitnessMethodArguments(ConventionWitnessMethodAttributeArgumentsSyntax)`, `opaqueReturnTypeOfAttributeArguments(OpaqueReturnTypeOfAttributeArgumentsSyntax)`, `exposeAttributeArguments(ExposeAttributeArgumentsSyntax)`, `underscorePrivateAttributeArguments(UnderscorePrivateAttributeArgumentsSyntax)`, and `unavailableFromAsyncArguments(UnavailableFromAsyncAttributeArgumentsSyntax)`. Removed Syntax kinds: `ConventionAttributeArgumentsSyntax`, `ConventionWitnessMethodAttributeArgumentsSyntax`, `OpaqueReturnTypeOfAttributeArgumentsSyntax`, `ExposeAttributeArgumentsSyntax`, `UnderscorePrivateAttributeArgumentsSyntax`, and `UnavailableFromAsyncAttributeArgumentsSyntax`. -, + +- `ExpandEditorPlaceholdersToLiteralClosures` & `CallToTrailingClosures` now take a `Syntax` parameter + - Description: These refactorings now take an arbitrary `Syntax` and return a `Syntax?`. If a non-function-like syntax node is passed, `nil` is returned. The previous `FunctionCallExprSyntax` overloads are deprecated. + - Pull Request: https://github.com/swiftlang/swift-syntax/pull/3092 + - Migration steps: Insert a `Syntax(...)` initializer call for the argument, and cast the result with `.as(...)` if necessary. + - Notes: This allows the refactorings to correctly handle macro expansion expressions and declarations. + ## Template - *Affected API or two word description* diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 7c00da25f0e..0b28ca19927 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -24,4 +24,5 @@ add_subdirectory(SwiftSyntaxMacroExpansion) add_subdirectory(SwiftCompilerPluginMessageHandling) add_subdirectory(SwiftIDEUtils) add_subdirectory(SwiftCompilerPlugin) +add_subdirectory(SwiftWarningControl) add_subdirectory(VersionMarkerModules) diff --git a/Sources/SwiftBasicFormat/BasicFormat.swift b/Sources/SwiftBasicFormat/BasicFormat.swift index 1cf8b170bcb..77f84cfc7a8 100644 --- a/Sources/SwiftBasicFormat/BasicFormat.swift +++ b/Sources/SwiftBasicFormat/BasicFormat.swift @@ -308,10 +308,12 @@ open class BasicFormat: SyntaxRewriter { case (.atSign, _), (.backslash, _), (.backtick, _), + (.colonColon, .identifier), (.dollarIdentifier, .period), // a.b (.endOfFile, _), (.exclamationMark, .period), // myOptionalBar!.foo() (.regexPoundDelimiter, .regexSlash), // opening extended regex delimiter should never be separate by a space + (.identifier, .colonColon), (.identifier, .leftAngle), // MyType (.identifier, .leftSquare), // myArray[1] (.identifier, .period), // a.b diff --git a/Sources/SwiftBasicFormat/InferIndentation.swift b/Sources/SwiftBasicFormat/InferIndentation.swift index a0c601534dc..0c6d4868496 100644 --- a/Sources/SwiftBasicFormat/InferIndentation.swift +++ b/Sources/SwiftBasicFormat/InferIndentation.swift @@ -124,7 +124,7 @@ private class IndentationInferrer: SyntaxVisitor { } } -fileprivate extension Array { +fileprivate extension [Int] { var sum: Int { return self.reduce(0) { return $0 + $1 } } diff --git a/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt index e0323a7abfc..3c6a9f416ab 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt +++ b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt @@ -25,6 +25,7 @@ target_link_swift_syntax_libraries(SwiftCompilerPluginMessageHandling PUBLIC SwiftSyntax SwiftBasicFormat SwiftDiagnostics + SwiftIfConfig SwiftParser SwiftSyntaxMacros SwiftSyntaxMacroExpansion diff --git a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift index 14f30c50d86..4065a814b71 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift @@ -21,8 +21,10 @@ import _SwiftSyntaxCShims #endif #if compiler(>=6) +internal import SwiftIfConfig public import SwiftSyntaxMacros #else +import SwiftIfConfig import SwiftSyntaxMacros #endif @@ -189,12 +191,25 @@ public class PluginProviderMessageHandler: PluginMessa let macroRole, let discriminator, let expandingSyntax, - let lexicalContext + let lexicalContext, + let staticBuildConfigurationString ): + // Decode the static build configuration. + let staticBuildConfiguration: StaticBuildConfiguration? + if let staticBuildConfigurationString { + var mutableConfigurationString = staticBuildConfigurationString + staticBuildConfiguration = mutableConfigurationString.withUTF8 { + try? JSON.decode(StaticBuildConfiguration.self, from: $0) + } + } else { + staticBuildConfiguration = nil + } + return expandFreestandingMacro( macro: macro, macroRole: macroRole, discriminator: discriminator, + staticBuildConfiguration: staticBuildConfiguration, expandingSyntax: expandingSyntax, lexicalContext: lexicalContext ) @@ -208,12 +223,25 @@ public class PluginProviderMessageHandler: PluginMessa let parentDeclSyntax, let extendedTypeSyntax, let conformanceListSyntax, - let lexicalContext + let lexicalContext, + let staticBuildConfigurationString ): + // Decode the static build configuration. + let staticBuildConfiguration: StaticBuildConfiguration? + if let staticBuildConfigurationString { + var mutableConfigurationString = staticBuildConfigurationString + staticBuildConfiguration = mutableConfigurationString.withUTF8 { + try? JSON.decode(StaticBuildConfiguration.self, from: $0) + } + } else { + staticBuildConfiguration = nil + } + return expandAttachedMacro( macro: macro, macroRole: macroRole, discriminator: discriminator, + staticBuildConfiguration: staticBuildConfiguration, attributeSyntax: attributeSyntax, declSyntax: declSyntax, parentDeclSyntax: parentDeclSyntax, diff --git a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift index 41f630008ec..52e0d9e69ab 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift @@ -13,14 +13,18 @@ #if compiler(>=6) internal import SwiftBasicFormat internal import SwiftDiagnostics +@_spi(ExperimentalLanguageFeatures) internal import SwiftIfConfig internal import SwiftOperators +@_spi(ExperimentalLanguageFeatures) internal import SwiftParser internal import SwiftSyntax @_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) internal import SwiftSyntaxMacroExpansion @_spi(ExperimentalLanguageFeature) internal import SwiftSyntaxMacros #else import SwiftBasicFormat import SwiftDiagnostics +@_spi(ExperimentalLanguageFeatures) import SwiftIfConfig import SwiftOperators +@_spi(ExperimentalLanguageFeatures) import SwiftParser import SwiftSyntax @_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacroExpansion @_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros @@ -36,6 +40,8 @@ extension PluginProviderMessageHandler { private static func resolveLexicalContext( _ lexicalContext: [PluginMessage.Syntax]?, sourceManager: SourceManager, + swiftVersion: Parser.SwiftVersion?, + experimentalFeatures: Parser.ExperimentalFeatures?, operatorTable: OperatorTable, fallbackSyntax: some SyntaxProtocol ) -> [Syntax] { @@ -45,7 +51,14 @@ extension PluginProviderMessageHandler { return fallbackSyntax.allMacroLexicalContexts() } - return lexicalContext.map { sourceManager.add($0, foldingWith: operatorTable) } + return lexicalContext.map { + sourceManager.add( + $0, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures, + foldingWith: operatorTable + ) + } } /// Expand `@freestainding(XXX)` macros. @@ -53,21 +66,32 @@ extension PluginProviderMessageHandler { macro: PluginMessage.MacroReference, macroRole pluginMacroRole: PluginMessage.MacroRole?, discriminator: String, + staticBuildConfiguration: StaticBuildConfiguration?, expandingSyntax: PluginMessage.Syntax, lexicalContext: [PluginMessage.Syntax]? ) -> PluginToHostMessage { let sourceManager = SourceManager(syntaxRegistry: syntaxRegistry) - let syntax = sourceManager.add(expandingSyntax, foldingWith: .standardOperators) + let swiftVersion = staticBuildConfiguration?.parserSwiftVersion + let experimentalFeatures = staticBuildConfiguration?.experimentalFeatures + let syntax = sourceManager.add( + expandingSyntax, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures, + foldingWith: .standardOperators + ) let context = PluginMacroExpansionContext( sourceManager: sourceManager, lexicalContext: Self.resolveLexicalContext( lexicalContext, sourceManager: sourceManager, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures, operatorTable: .standardOperators, fallbackSyntax: syntax ), - expansionDiscriminator: discriminator + expansionDiscriminator: discriminator, + staticBuildConfiguration: staticBuildConfiguration ) let expandedSource: String? @@ -113,6 +137,7 @@ extension PluginProviderMessageHandler { macro: PluginMessage.MacroReference, macroRole: PluginMessage.MacroRole, discriminator: String, + staticBuildConfiguration: StaticBuildConfiguration?, attributeSyntax: PluginMessage.Syntax, declSyntax: PluginMessage.Syntax, parentDeclSyntax: PluginMessage.Syntax?, @@ -121,17 +146,29 @@ extension PluginProviderMessageHandler { lexicalContext: [PluginMessage.Syntax]? ) -> PluginToHostMessage { let sourceManager = SourceManager(syntaxRegistry: syntaxRegistry) - let attributeNode = sourceManager.add( - attributeSyntax, - foldingWith: .standardOperators - ).cast(AttributeSyntax.self) - let declarationNode = sourceManager.add(declSyntax) - let parentDeclNode = parentDeclSyntax.map { sourceManager.add($0).cast(DeclSyntax.self) } + let swiftVersion = staticBuildConfiguration?.parserSwiftVersion + let experimentalFeatures = staticBuildConfiguration?.experimentalFeatures + + func addToSourceManager(_ syntax: PluginMessage.Syntax, foldOperators: Bool) -> Syntax { + sourceManager.add( + syntax, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures, + foldingWith: foldOperators ? .standardOperators : nil + ) + } + + let attributeNode = addToSourceManager(attributeSyntax, foldOperators: true) + .cast(AttributeSyntax.self) + let declarationNode = addToSourceManager(declSyntax, foldOperators: false) + let parentDeclNode = parentDeclSyntax.map { + addToSourceManager($0, foldOperators: false).cast(DeclSyntax.self) + } let extendedType = extendedTypeSyntax.map { - sourceManager.add($0).cast(TypeSyntax.self) + addToSourceManager($0, foldOperators: false).cast(TypeSyntax.self) } let conformanceList = conformanceListSyntax.map { - let placeholderStruct = sourceManager.add($0).cast(StructDeclSyntax.self) + let placeholderStruct = addToSourceManager($0, foldOperators: false).cast(StructDeclSyntax.self) return placeholderStruct.inheritanceClause!.inheritedTypes } @@ -140,10 +177,13 @@ extension PluginProviderMessageHandler { lexicalContext: Self.resolveLexicalContext( lexicalContext, sourceManager: sourceManager, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures, operatorTable: .standardOperators, fallbackSyntax: declarationNode ), - expansionDiscriminator: discriminator + expansionDiscriminator: discriminator, + staticBuildConfiguration: staticBuildConfiguration ) // TODO: Make this a 'String?' and remove non-'hasExpandMacroResult' branches. diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift index 85b5e5d54ec..2a53c520f2a 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift @@ -12,14 +12,16 @@ #if compiler(>=6) internal import SwiftDiagnostics +internal import SwiftIfConfig internal import SwiftOperators -internal import SwiftParser +@_spi(ExperimentalLanguageFeatures) internal import SwiftParser internal import SwiftSyntax internal import SwiftSyntaxMacros #else import SwiftDiagnostics +import SwiftIfConfig import SwiftOperators -import SwiftParser +@_spi(ExperimentalLanguageFeatures) import SwiftParser import SwiftSyntax import SwiftSyntaxMacros #endif @@ -29,6 +31,8 @@ class ParsedSyntaxRegistry { struct Key: Hashable { let source: String let kind: PluginMessage.Syntax.Kind + let swiftVersion: Parser.SwiftVersion + let experimentalFeatures: Parser.ExperimentalFeatures } private var storage: LRUCache @@ -37,8 +41,17 @@ class ParsedSyntaxRegistry { self.storage = LRUCache(capacity: cacheCapacity) } - private func parse(source: String, kind: PluginMessage.Syntax.Kind) -> Syntax { - var parser = Parser(source) + private func parse( + source: String, + kind: PluginMessage.Syntax.Kind, + swiftVersion: Parser.SwiftVersion, + experimentalFeatures: Parser.ExperimentalFeatures + ) -> Syntax { + var parser = Parser( + source, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures + ) switch kind { case .declaration: return Syntax(DeclSyntax.parse(from: &parser)) @@ -55,13 +68,30 @@ class ParsedSyntaxRegistry { } } - func get(source: String, kind: PluginMessage.Syntax.Kind) -> Syntax { - let key = Key(source: source, kind: kind) + func get( + source: String, + kind: PluginMessage.Syntax.Kind, + swiftVersion: Parser.SwiftVersion?, + experimentalFeatures: Parser.ExperimentalFeatures? + ) -> Syntax { + let swiftVersion = swiftVersion ?? Parser.defaultSwiftVersion + let experimentalFeatures = experimentalFeatures ?? Parser.ExperimentalFeatures() + let key = Key( + source: source, + kind: kind, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures + ) if let cached = storage[key] { return cached } - let node = parse(source: source, kind: kind) + let node = parse( + source: source, + kind: kind, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures + ) storage[key] = node return node } @@ -124,10 +154,16 @@ class SourceManager { /// are cached in the source manager to provide `location(of:)` et al. func add( _ syntaxInfo: PluginMessage.Syntax, - foldingWith operatorTable: OperatorTable? = nil + swiftVersion: Parser.SwiftVersion?, + experimentalFeatures: Parser.ExperimentalFeatures?, + foldingWith operatorTable: OperatorTable? ) -> Syntax { - - var node = syntaxRegistry.get(source: syntaxInfo.source, kind: syntaxInfo.kind) + var node = syntaxRegistry.get( + source: syntaxInfo.source, + kind: syntaxInfo.kind, + swiftVersion: swiftVersion, + experimentalFeatures: experimentalFeatures + ) if let operatorTable { node = operatorTable.foldAll(node, errorHandler: { _ in /*ignore*/ }) } @@ -262,6 +298,10 @@ class PluginMacroExpansionContext { /// to produce unique names. private var expansionDiscriminator: String + /// The static build configuration, if any, that will be used for the + /// macro-expanded code. + private var staticBuildConfiguration: StaticBuildConfiguration? + /// Counter for each of the uniqued names. /// /// Used in conjunction with `expansionDiscriminator`. @@ -271,10 +311,16 @@ class PluginMacroExpansionContext { /// macro. internal private(set) var diagnostics: [Diagnostic] = [] - init(sourceManager: SourceManager, lexicalContext: [Syntax], expansionDiscriminator: String = "") { + init( + sourceManager: SourceManager, + lexicalContext: [Syntax], + expansionDiscriminator: String = "", + staticBuildConfiguration: StaticBuildConfiguration? + ) { self.sourceManager = sourceManager self.lexicalContext = lexicalContext self.expansionDiscriminator = expansionDiscriminator + self.staticBuildConfiguration = staticBuildConfiguration } } @@ -321,4 +367,8 @@ extension PluginMacroExpansionContext: MacroExpansionContext { } return AbstractSourceLocation(location) } + + public var buildConfiguration: (any BuildConfiguration)? { + staticBuildConfiguration + } } diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index 4d8b5a3bf72..ab55433521a 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -25,7 +25,8 @@ public enum HostToPluginMessage: Codable { macroRole: PluginMessage.MacroRole? = nil, discriminator: String, syntax: PluginMessage.Syntax, - lexicalContext: [PluginMessage.Syntax]? = nil + lexicalContext: [PluginMessage.Syntax]? = nil, + staticBuildConfiguration: String? = nil ) /// Expand an '@attached' macro. @@ -38,7 +39,8 @@ public enum HostToPluginMessage: Codable { parentDeclSyntax: PluginMessage.Syntax?, extendedTypeSyntax: PluginMessage.Syntax?, conformanceListSyntax: PluginMessage.Syntax?, - lexicalContext: [PluginMessage.Syntax]? = nil + lexicalContext: [PluginMessage.Syntax]? = nil, + staticBuildConfiguration: String? = nil ) /// Optionally implemented message to load a dynamic link library. diff --git a/Sources/SwiftCompilerPluginMessageHandling/StandardIOMessageConnection.swift b/Sources/SwiftCompilerPluginMessageHandling/StandardIOMessageConnection.swift index 9ad7cc46542..74bb6768d38 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/StandardIOMessageConnection.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/StandardIOMessageConnection.swift @@ -152,6 +152,10 @@ public struct StandardIOMessageConnection: MessageConnection { // Read the JSON payload. let count = Int(UInt64(littleEndian: header)) + // Empty message is a termination signal. + if count == 0 { + return nil + } let data = UnsafeMutableRawBufferPointer.allocate(byteCount: count, alignment: 1) defer { data.deallocate() } try _read(into: data) diff --git a/Sources/SwiftIDEUtils/DeclNameLocation.swift b/Sources/SwiftIDEUtils/DeclNameLocation.swift index c5176f42443..18df8864b1d 100644 --- a/Sources/SwiftIDEUtils/DeclNameLocation.swift +++ b/Sources/SwiftIDEUtils/DeclNameLocation.swift @@ -68,9 +68,9 @@ public struct DeclNameLocation: Equatable { case .labeled(let firstName, let secondName): let endPosition = secondName?.upperBound ?? firstName.upperBound return firstName.lowerBound.. DeclNameLocation.Argument { switch self { - case .labeled(firstName: let firstName, secondName: let secondName): + case .labeled(let firstName, let secondName): return .labeled(firstName: firstName.advanced(by: utf8Offset), secondName: secondName?.advanced(by: utf8Offset)) - case .labeledCall(label: let label, colon: let colon): + case .labeledCall(let label, let colon): return .labeledCall(label: label.advanced(by: utf8Offset), colon: colon.advanced(by: utf8Offset)) - case .unlabeled(argumentPosition: let argumentPosition): + case .unlabeled(let argumentPosition): return .unlabeled(argumentPosition: argumentPosition.advanced(by: utf8Offset)) } } @@ -122,7 +122,7 @@ public struct DeclNameLocation: Equatable { switch self { case .noArguments: return .noArguments - case .call(let arguments, firstTrailingClosureIndex: let firstTrailingClosureIndex): + case .call(let arguments, let firstTrailingClosureIndex): return .call( arguments.map { $0.advanced(by: utf8Offset) }, firstTrailingClosureIndex: firstTrailingClosureIndex diff --git a/Sources/SwiftIDEUtils/FixItApplier.swift b/Sources/SwiftIDEUtils/FixItApplier.swift index de5785ff941..6253710930d 100644 --- a/Sources/SwiftIDEUtils/FixItApplier.swift +++ b/Sources/SwiftIDEUtils/FixItApplier.swift @@ -27,12 +27,15 @@ public enum FixItApplier { /// - filterByMessages: An optional array of message strings to filter which Fix-Its to apply. /// If `nil`, the first Fix-It from each diagnostic is applied. /// - tree: The syntax tree to which the Fix-Its will be applied. + /// - allowDuplicateInsertions: Whether to apply duplicate insertions. + /// Defaults to `true`. /// /// - Returns: A `String` representation of the modified syntax tree after applying the Fix-Its. public static func applyFixes( from diagnostics: [Diagnostic], filterByMessages messages: [String]?, - to tree: any SyntaxProtocol + to tree: some SyntaxProtocol, + allowDuplicateInsertions: Bool = true ) -> String { let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message } @@ -43,51 +46,89 @@ public enum FixItApplier { .filter { messages.contains($0.message.message) } .flatMap(\.edits) - return self.apply(edits: edits, to: tree) + return self.apply(edits: edits, to: tree, allowDuplicateInsertions: allowDuplicateInsertions) } - /// Apply the given edits to the syntax tree. + /// Applies the given edits to the given syntax tree. /// /// - Parameters: - /// - edits: The edits to apply to the syntax tree - /// - tree: he syntax tree to which the edits should be applied. - /// - Returns: A `String` representation of the modified syntax tree after applying the edits. + /// - edits: The edits to apply. + /// - tree: The syntax tree to which the edits should be applied. + /// - allowDuplicateInsertions: Whether to apply duplicate insertions. + /// Defaults to `true`. + /// + /// - Returns: A `String` representation of the modified syntax tree. public static func apply( edits: [SourceEdit], - to tree: any SyntaxProtocol + to tree: some SyntaxProtocol, + allowDuplicateInsertions: Bool = true ) -> String { var edits = edits var source = tree.description - while let edit = edits.first { - edits = Array(edits.dropFirst()) + for var editIndex in edits.indices { + let edit = edits[editIndex] + + // Empty edits do nothing. + guard !edit.isEmpty else { + continue + } + + do { + let utf8 = source.utf8 + let startIndex = utf8.index(utf8.startIndex, offsetBy: edit.startUtf8Offset) + let endIndex = utf8.index(utf8.startIndex, offsetBy: edit.endUtf8Offset) + + source.replaceSubrange(startIndex.. Bool { + // Insertions never conflict between themselves, unless we were asked + // to drop duplicate insertions. + if edit.range.isEmpty && remainingEdit.range.isEmpty { + if allowDuplicateInsertions { + return false + } - let startIndex = source.utf8.index(source.utf8.startIndex, offsetBy: edit.startUtf8Offset) - let endIndex = source.utf8.index(source.utf8.startIndex, offsetBy: edit.endUtf8Offset) + return edit == remainingEdit + } - source.replaceSubrange(startIndex.. remainingEdit.startUtf8Offset + && edit.startUtf8Offset < remainingEdit.endUtf8Offset + } - edits = edits.compactMap { remainingEdit -> SourceEdit? in - if remainingEdit.replacementRange.overlaps(edit.replacementRange) { - // The edit overlaps with the previous edit. We can't apply both - // without conflicts. Apply the one that's listed first and drop the - // later edit. - return nil + guard !shouldDropRemainingEdit() else { + // Drop the edit by swapping it for an empty one. + edits[editIndex] = SourceEdit() + continue } // If the remaining edit starts after or at the end of the edit that we just applied, // shift it by the current edit's difference in length. if edit.endUtf8Offset <= remainingEdit.startUtf8Offset { - let startPosition = AbsolutePosition( - utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length - ) - let endPosition = AbsolutePosition( - utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count + edit.replacementLength.utf8Length - ) - return SourceEdit(range: startPosition.. Index { + self.formIndex(after: &index) as Void + return index + } +} + private extension SourceEdit { var startUtf8Offset: Int { return range.lowerBound.utf8Offset @@ -104,7 +152,15 @@ private extension SourceEdit { return range.upperBound.utf8Offset } - var replacementRange: Range { - return startUtf8Offset.. SyntaxVisitorContinueKind { @@ -411,7 +411,7 @@ fileprivate class FindFeatureCheckVisitor: SyntaxVisitor { override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { if let calleeDeclRef = node.calledExpression.as(DeclReferenceExprSyntax.self), let calleeName = calleeDeclRef.simpleIdentifier?.name, - (calleeName == "compiler" || calleeName == "_compiler_version") + calleeName == "compiler" || calleeName == "_compiler_version" { foundFeatureCheck = true } diff --git a/Sources/SwiftIfConfig/BuildConfiguration.swift b/Sources/SwiftIfConfig/BuildConfiguration.swift index 407aa1a13d6..51daaaeac84 100644 --- a/Sources/SwiftIfConfig/BuildConfiguration.swift +++ b/Sources/SwiftIfConfig/BuildConfiguration.swift @@ -14,7 +14,7 @@ import SwiftSyntax /// Describes the ordering of a sequence of bytes that make up a word of /// storage for a particular architecture. -public enum Endianness: String { +public enum Endianness: String, Codable { /// Little endian, meaning that the least significant byte of a word is /// stored at the lowest address. case little @@ -39,11 +39,14 @@ public enum CanImportVersion { enum BuildConfigurationError: Error, CustomStringConvertible { case experimentalFeature(name: String) + case notImplemented(name: String) var description: String { switch self { case .experimentalFeature(let name): return "'\(name)' is an experimental feature" + case .notImplemented(let name): + return "'\(name)' not implemented" } } } @@ -228,18 +231,17 @@ public protocol BuildConfiguration { /// Determine whether the given name is the active target object file format (e.g., ELF). /// /// The target object file format can only be queried by an experimental - /// syntax `_objectFileFormat()`, e.g., + /// syntax `objectFormat()`, e.g., /// /// ```swift - /// #if _objectFileFormat(ELF) + /// #if objectFormat(ELF) /// // Special logic for ELF object file formats /// #endif /// ``` /// - Parameters: /// - name: The name of the object file format. /// - Returns: Whether the target object file format matches the given name. - @_spi(ExperimentalLanguageFeatures) - func isActiveTargetObjectFileFormat(name: String) throws -> Bool + func isActiveTargetObjectFormat(name: String) throws -> Bool /// The bit width of a data pointer for the target architecture. /// @@ -307,9 +309,8 @@ public protocol BuildConfiguration { /// Default implementation of BuildConfiguration, to avoid a revlock with the /// swift repo, and breaking clients with the new addition to the protocol. extension BuildConfiguration { - /// FIXME: This should be @_spi(ExperimentalLanguageFeatures) but cannot due - /// to rdar://147943518, https://github.com/swiftlang/swift/issues/80313 - public func isActiveTargetObjectFileFormat(name: String) throws -> Bool { - throw BuildConfigurationError.experimentalFeature(name: "_objectFileFormat") + @available(*, deprecated, message: "`BuildConfiguration` conformance must implement `isActiveTargetObjectFormat`") + public func isActiveTargetObjectFormat(name: String) throws -> Bool { + throw BuildConfigurationError.notImplemented(name: "isActiveTargetObjectFormat") } } diff --git a/Sources/SwiftIfConfig/CMakeLists.txt b/Sources/SwiftIfConfig/CMakeLists.txt index 2df83517c5b..90601a0ae59 100644 --- a/Sources/SwiftIfConfig/CMakeLists.txt +++ b/Sources/SwiftIfConfig/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_syntax_library(SwiftIfConfig IfConfigDiagnostic.swift IfConfigEvaluation.swift IfConfigFunctions.swift + StaticBuildConfiguration.swift SyntaxLiteralUtils.swift SyntaxProtocol+IfConfig.swift VersionTuple+Parsing.swift @@ -27,4 +28,5 @@ target_link_swift_syntax_libraries(SwiftIfConfig PUBLIC SwiftSyntax SwiftSyntaxBuilder SwiftDiagnostics - SwiftOperators) + SwiftOperators + SwiftParser) diff --git a/Sources/SwiftIfConfig/ConfiguredRegions.swift b/Sources/SwiftIfConfig/ConfiguredRegions.swift index 67d1afb29c6..7329ba324c3 100644 --- a/Sources/SwiftIfConfig/ConfiguredRegions.swift +++ b/Sources/SwiftIfConfig/ConfiguredRegions.swift @@ -174,7 +174,7 @@ extension SyntaxProtocol { } /// Helper class that walks a syntax tree looking for configured regions. -fileprivate class ConfiguredRegionVisitor: SyntaxVisitor { +private class ConfiguredRegionVisitor: SyntaxVisitor { let configuration: Configuration /// The regions we've found so far. diff --git a/Sources/SwiftIfConfig/IfConfigDiagnostic.swift b/Sources/SwiftIfConfig/IfConfigDiagnostic.swift index b2ad7bd2f69..a8deec63227 100644 --- a/Sources/SwiftIfConfig/IfConfigDiagnostic.swift +++ b/Sources/SwiftIfConfig/IfConfigDiagnostic.swift @@ -47,22 +47,22 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible { case .unknownExpression: return "invalid conditional compilation expression" - case .unhandledFunction(name: let name, syntax: _): + case .unhandledFunction(let name, syntax: _): return "build configuration cannot handle '\(name)'" - case .requiresUnlabeledArgument(name: let name, role: let role, syntax: _): + case .requiresUnlabeledArgument(let name, let role, syntax: _): return "'\(name)' requires a single unlabeled argument for the \(role)" - case .unsupportedVersionOperator(name: let name, operator: let op): + case .unsupportedVersionOperator(let name, operator: let op): return "'\(name)' version check does not support operator '\(op.trimmedDescription)'" - case .invalidVersionOperand(name: let name, syntax: let version): + case .invalidVersionOperand(let name, syntax: let version): return "'\(name)' version check has invalid version '\(version.trimmedDescription)'" case .emptyVersionComponent(syntax: _): return "found empty version component" - case .compilerVersionOutOfRange(value: let value, upperLimit: let upperLimit, syntax: _): + case .compilerVersionOutOfRange(let value, let upperLimit, syntax: _): return "compiler version component '\(value)' is not in the allowed range 0...\(upperLimit)" case .compilerVersionSecondComponentNotWildcard(syntax: _): @@ -80,10 +80,10 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible { case .canImportTwoParameters(syntax: _): return "'canImport' can take only two parameters" - case .ignoredTrailingComponents(version: let version, syntax: _): + case .ignoredTrailingComponents(let version, syntax: _): return "trailing components of version '\(version.description)' are ignored" - case .integerLiteralCondition(syntax: let syntax, replacement: let replacement): + case .integerLiteralCondition(let syntax, let replacement): return "'\(syntax.trimmedDescription)' is not a valid conditional compilation expression, use '\(replacement)'" case .likelySimulatorPlatform: @@ -121,26 +121,26 @@ enum IfConfigDiagnostic: Error, CustomStringConvertible { var syntax: Syntax { switch self { case .unknownExpression(let syntax), - .unhandledFunction(name: _, syntax: let syntax), - .requiresUnlabeledArgument(name: _, role: _, syntax: let syntax), - .invalidVersionOperand(name: _, syntax: let syntax), - .emptyVersionComponent(syntax: let syntax), - .compilerVersionOutOfRange(value: _, upperLimit: _, syntax: let syntax), - .compilerVersionTooManyComponents(syntax: let syntax), - .compilerVersionSecondComponentNotWildcard(syntax: let syntax), - .canImportMissingModule(syntax: let syntax), - .canImportLabel(syntax: let syntax), - .canImportTwoParameters(syntax: let syntax), - .ignoredTrailingComponents(version: _, syntax: let syntax), - .integerLiteralCondition(syntax: let syntax, replacement: _), - .likelySimulatorPlatform(syntax: let syntax), - .likelyTargetOS(syntax: let syntax, replacement: _), - .endiannessDoesNotMatch(syntax: let syntax, argument: _), - .macabiIsMacCatalyst(syntax: let syntax), - .expectedModuleName(syntax: let syntax), - .badInfixOperator(syntax: let syntax), - .badPrefixOperator(syntax: let syntax), - .unexpectedDefined(syntax: let syntax, argument: _): + .unhandledFunction(name: _, let syntax), + .requiresUnlabeledArgument(name: _, role: _, let syntax), + .invalidVersionOperand(name: _, let syntax), + .emptyVersionComponent(let syntax), + .compilerVersionOutOfRange(value: _, upperLimit: _, let syntax), + .compilerVersionTooManyComponents(let syntax), + .compilerVersionSecondComponentNotWildcard(let syntax), + .canImportMissingModule(let syntax), + .canImportLabel(let syntax), + .canImportTwoParameters(let syntax), + .ignoredTrailingComponents(version: _, let syntax), + .integerLiteralCondition(let syntax, replacement: _), + .likelySimulatorPlatform(let syntax), + .likelyTargetOS(let syntax, replacement: _), + .endiannessDoesNotMatch(let syntax, argument: _), + .macabiIsMacCatalyst(let syntax), + .expectedModuleName(let syntax), + .badInfixOperator(let syntax), + .badPrefixOperator(let syntax), + .unexpectedDefined(let syntax, argument: _): return Syntax(syntax) case .unsupportedVersionOperator(name: _, operator: let op): diff --git a/Sources/SwiftIfConfig/IfConfigEvaluation.swift b/Sources/SwiftIfConfig/IfConfigEvaluation.swift index 67141335684..50c28802544 100644 --- a/Sources/SwiftIfConfig/IfConfigEvaluation.swift +++ b/Sources/SwiftIfConfig/IfConfigEvaluation.swift @@ -79,7 +79,7 @@ func evaluateIfConfig( // Integer literals aren't allowed, but we recognize them. if let intLiteral = condition.as(IntegerLiteralExprSyntax.self), - (intLiteral.literal.text == "0" || intLiteral.literal.text == "1") + intLiteral.literal.text == "0" || intLiteral.literal.text == "1" { let result = intLiteral.literal.text == "1" @@ -307,8 +307,8 @@ func evaluateIfConfig( case .targetEnvironment: return doSingleIdentifierArgumentCheck(configuration.isActiveTargetEnvironment, role: "environment") - case ._objectFileFormat: - return doSingleIdentifierArgumentCheck(configuration.isActiveTargetObjectFileFormat, role: "object file format") + case .objectFormat: + return doSingleIdentifierArgumentCheck(configuration.isActiveTargetObjectFormat, role: "object file format") case ._runtime: return doSingleIdentifierArgumentCheck(configuration.isActiveTargetRuntime, role: "runtime") @@ -821,8 +821,8 @@ private struct CanImportSuppressingBuildConfiguration return try other.isActiveTargetPointerAuthentication(name: name) } - func isActiveTargetObjectFileFormat(name: String) throws -> Bool { - return try other.isActiveTargetObjectFileFormat(name: name) + func isActiveTargetObjectFormat(name: String) throws -> Bool { + return try other.isActiveTargetObjectFormat(name: name) } var targetPointerBitWidth: Int { return other.targetPointerBitWidth } diff --git a/Sources/SwiftIfConfig/IfConfigFunctions.swift b/Sources/SwiftIfConfig/IfConfigFunctions.swift index f129af35ca4..66407751360 100644 --- a/Sources/SwiftIfConfig/IfConfigFunctions.swift +++ b/Sources/SwiftIfConfig/IfConfigFunctions.swift @@ -50,7 +50,7 @@ enum IfConfigFunctions: String { case _pointerBitWidth /// A check for the target object file format (e.g., ELF) - case _objectFileFormat + case objectFormat /// A check for the target runtime paired with the Swift runtime (e.g., _ObjC) /// via `_runtime()`. @@ -72,7 +72,7 @@ enum IfConfigFunctions: String { return true case .hasAttribute, .hasFeature, .canImport, .os, .arch, .targetEnvironment, - ._hasAtomicBitWidth, ._endian, ._pointerBitWidth, ._objectFileFormat, ._runtime, ._ptrauth, .defined: + ._hasAtomicBitWidth, ._endian, ._pointerBitWidth, .objectFormat, ._runtime, ._ptrauth, .defined: return false } } diff --git a/Sources/SwiftIfConfig/StaticBuildConfiguration.swift b/Sources/SwiftIfConfig/StaticBuildConfiguration.swift new file mode 100644 index 00000000000..f15ffdd3f06 --- /dev/null +++ b/Sources/SwiftIfConfig/StaticBuildConfiguration.swift @@ -0,0 +1,461 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6) +@_spi(ExperimentalLanguageFeatures) public import SwiftParser +public import SwiftSyntax +#else +@_spi(ExperimentalLanguageFeatures) import SwiftParser +import SwiftSyntax +#endif + +/// A statically-determined build configuration that can be used with any +/// API that requires a build configuration. Static build configurations can +/// be (de-)serialized via Codable. +/// +/// Static build configurations can not be used for canImport checks, because +/// such checks require deeper integration with the compiler itself. +public struct StaticBuildConfiguration: Codable { + public init( + customConditions: Set = [], + features: Set = [], + attributes: Set = [], + targetOSs: Set = [], + targetArchitectures: Set = [], + targetEnvironments: Set = [], + targetRuntimes: Set = [], + targetPointerAuthenticationSchemes: Set = [], + targetObjectFileFormats: Set = [], + targetPointerBitWidth: Int = 64, + targetAtomicBitWidths: [Int] = [], + endianness: Endianness = .little, + languageVersion: VersionTuple, + compilerVersion: VersionTuple + ) { + self.customConditions = customConditions + self.features = features + self.attributes = attributes + self.targetOSs = targetOSs + self.targetArchitectures = targetArchitectures + self.targetEnvironments = targetEnvironments + self.targetRuntimes = targetRuntimes + self.targetPointerAuthenticationSchemes = targetPointerAuthenticationSchemes + self.targetObjectFileFormats = targetObjectFileFormats + self.targetPointerBitWidth = targetPointerBitWidth + self.targetAtomicBitWidths = targetAtomicBitWidths + self.endianness = endianness + self.languageMode = languageVersion + self.compilerVersion = compilerVersion + } + + /// The set of custom conditions that are present and can be used with `#if`. + /// + /// Custom build conditions can be set by the `-D` command line option to + /// the Swift compiler. For example, `-DDEBUG` sets the custom condition + /// named `DEBUG`, which could be checked with, e.g., + /// + /// ```swift + /// #if DEBUG + /// // ... + /// #endif + /// ``` + public var customConditions: Set = [] + + /// The set of language features that are enabled. + /// + /// Features are determined by the Swift compiler, language mode, and other + /// options such as `--enable-upcoming-feature`, and can be checked with + /// the `hasFeature` syntax, e.g., + /// + /// ```swift + /// #if hasFeature(VariadicGenerics) + /// // ... + /// #endif + /// ``` + public var features: Set = [] + + /// The set of attributes that are available. + /// + /// Attributes are determined by the Swift compiler. They can be checked + /// with `hasAttribute` syntax, e.g., + /// + /// ```swift + /// #if hasAttribute(available) + /// // ... + /// #endif + /// ``` + public var attributes: Set = [] + + /// The active target OS, e.g., "Windows", "iOS". + /// + /// The target operating system can be queried with `os()`, e.g., + /// + /// ```swift + /// #if os(Linux) + /// // Linux-specific implementation + /// #endif + /// ``` + public var targetOSs: Set = [] + + /// The active target architectures, e.g., "x64_64". + /// + /// The target processor architecture can be queried with `arch()`, e.g., + /// + /// ```swift + /// #if arch(x86_64) + /// // 64-bit x86 Intel-specific code + /// #endif + /// ``` + public var targetArchitectures: Set = [] + + /// The active target environments, e.g., "simulator". + /// + /// The target environment can be queried with `targetEnvironment()`, + /// e.g., + /// + /// ```swift + /// #if targetEnvironment(simulator) + /// // Simulator-specific code + /// #endif + /// ``` + public var targetEnvironments: Set = [] + + /// The active target runtimes, e.g., _ObjC. + /// + /// The target runtime can only be queried by an experimental syntax + /// `_runtime()`, e.g., + /// + /// ```swift + /// #if _runtime(_ObjC) + /// // Code that depends on Swift being built for use with the Objective-C + /// // runtime, e.g., on Apple platforms. + /// #endif + /// ``` + public var targetRuntimes: Set = [] + + /// The active target's pointer authentication schemes, e.g., "arm64e". + /// + /// The target pointer authentication scheme describes how pointers are + /// signed, as a security mitigation. This scheme can only be queried by + /// an experimental syntax `_ptrath()`, e.g., + /// + /// ```swift + /// #if _ptrauth(arm64e) + /// // Special logic for arm64e pointer signing + /// #endif + /// ``` + public var targetPointerAuthenticationSchemes: Set = [] + + /// The active target's object file formats, e.g., "COFF" + /// + /// The target object file format can only be queried by an experimental + /// syntax `_objectFileFormat()`, e.g., + /// + /// ```swift + /// #if _objectFileFormat(ELF) + /// // Special logic for ELF object file formats + /// #endif + /// ``` + public var targetObjectFileFormats: Set = [] + + /// The bit width of a data pointer for the target architecture. + /// + /// The target's pointer bit width (which also corresponds to the number of + /// bits in `Int`/`UInt`) can only be queried with the experimental syntax + /// `_pointerBitWidth(_)`, e.g., + /// + /// ```swift + /// #if _pointerBitWidth(_32) + /// // 32-bit system + /// #endif + /// ``` + public var targetPointerBitWidth: Int = 64 + + /// The atomic bit widths that are natively supported by the target + /// architecture. + /// + /// This lists all of the bit widths for which the target provides support + /// for atomic operations. It can be queried with + /// `_hasAtomicBitWidth(_)`, e.g. + /// + /// ```swift + /// #if _hasAtomicBitWidth(_64) + /// // 64-bit atomics are available + /// #endif + public var targetAtomicBitWidths: [Int] = [] + + /// The endianness of the target architecture. + /// + /// The target's endianness can onyl be queried with the experimental syntax + /// `_endian()`, where `` can be either "big" or "little", e.g., + /// + /// ```swift + /// #if _endian(little) + /// // Swap some bytes around for network byte order + /// #endif + /// ``` + public var endianness: Endianness = .little + + /// The effective language mode, which can be set by the user (e.g., 5.0). + /// + /// The language version can be queried with the `swift` directive that checks + /// how the supported language version compares, as described by + /// [SE-0212](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0212-compiler-version-directive.md). For example: + /// + /// ```swift + /// #if swift(>=6.0) + /// // Hooray, we can use tasks! + /// ``` + public var languageMode: VersionTuple + + /// The version of the compiler (e.g., 5.9). + /// + /// The compiler version can be queried with the `compiler` directive that + /// checks the specific version of the compiler being used to process the + /// code, e.g., + /// + /// ```swift + /// #if compiler(>=5.7) + /// // Hoorway, we can implicitly open existentials! + /// #endif + public var compilerVersion: VersionTuple +} + +extension StaticBuildConfiguration: BuildConfiguration { + /// Determine whether a given custom build condition has been set. + /// + /// Custom build conditions can be set by the `-D` command line option to + /// the Swift compiler. For example, `-DDEBUG` sets the custom condition + /// named `DEBUG`, which could be checked with, e.g., + /// + /// ```swift + /// #if DEBUG + /// // ... + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the custom build condition being checked (e.g., + /// `DEBUG`. + /// - Returns: Whether the custom condition is set. + public func isCustomConditionSet(name: String) -> Bool { + // "$FeatureName" can be used to check for a language feature. + if let firstChar = name.first, firstChar == "$" { + return features.contains(String(name.dropFirst())) + } + + return customConditions.contains(name) + } + + /// Determine whether the given feature is enabled. + /// + /// Features are determined by the Swift compiler, language mode, and other + /// options such as `--enable-upcoming-feature`, and can be checked with + /// the `hasFeature` syntax, e.g., + /// + /// ```swift + /// #if hasFeature(VariadicGenerics) + /// // ... + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the feature being checked. + /// - Returns: Whether the requested feature is available. + public func hasFeature(name: String) -> Bool { + features.contains(name) + } + + /// Determine whether the given attribute is available. + /// + /// Attributes are determined by the Swift compiler. They can be checked + /// with `hasAttribute` syntax, e.g., + /// + /// ```swift + /// #if hasAttribute(available) + /// // ... + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the attribute being queried. + /// - Returns: Whether the requested attribute is supported. + public func hasAttribute(name: String) -> Bool { + attributes.contains(name) + } + + /// Determine whether a module with the given import path can be imported, + /// with additional version information. + /// + /// This implementation always throws an error, because static build + /// configurations cannot evaluate canImport checks. + public func canImport(importPath: [(TokenSyntax, String)], version: CanImportVersion) throws -> Bool { + throw StaticBuildConfiguration.Error.canImportUnavailable + } + + /// Determine whether the given name is the active target OS (e.g., Linux, iOS). + /// + /// The target operating system can be queried with `os()`, e.g., + /// + /// ```swift + /// #if os(Linux) + /// // Linux-specific implementation + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the operating system being queried, such as `Linux`, + /// `Windows`, `macOS`, etc. + /// - Returns: Whether the given operating system name is the target operating + /// system, i.e., the operating system for which code is being generated. + public func isActiveTargetOS(name: String) -> Bool { + targetOSs.contains(name) + } + + /// Determine whether the given name is the active target architecture + /// (e.g., x86_64, arm64). + /// + /// The target processor architecture can be queried with `arch()`, e.g., + /// + /// ```swift + /// #if arch(x86_64) + /// // 64-bit x86 Intel-specific code + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the target architecture to check. + /// - Returns: Whether the given processor architecture is the target + /// architecture. + public func isActiveTargetArchitecture(name: String) -> Bool { + targetArchitectures.contains(name) + } + + /// Determine whether the given name is the active target environment (e.g., simulator) + /// + /// The target environment can be queried with `targetEnvironment()`, + /// e.g., + /// + /// ```swift + /// #if targetEnvironment(simulator) + /// // Simulator-specific code + /// #endif + /// ``` + /// + /// - Parameters: + /// - name: The name of the target environment to check. + /// - Returns: Whether the target platform is for a specific environment, + /// such as a simulator or emulator. + public func isActiveTargetEnvironment(name: String) -> Bool { + targetEnvironments.contains(name) + } + + /// Determine whether the given name is the active target runtime (e.g., _ObjC vs. _Native) + /// + /// The target runtime can only be queried by an experimental syntax + /// `_runtime()`, e.g., + /// + /// ```swift + /// #if _runtime(_ObjC) + /// // Code that depends on Swift being built for use with the Objective-C + /// // runtime, e.g., on Apple platforms. + /// #endif + /// ``` + /// + /// The only other runtime is "none", when Swift isn't tying into any other + /// specific runtime. + /// + /// - Parameters: + /// - name: The name of the runtime. + /// - Returns: Whether the target runtime matches the given name. + public func isActiveTargetRuntime(name: String) -> Bool { + targetRuntimes.contains(name) + } + + /// Determine whether the given name is the active target pointer authentication scheme (e.g., arm64e). + /// + /// The target pointer authentication scheme describes how pointers are + /// signed, as a security mitigation. This scheme can only be queried by + /// an experimental syntax `_ptrath()`, e.g., + /// + /// ```swift + /// #if _ptrauth(arm64e) + /// // Special logic for arm64e pointer signing + /// #endif + /// ``` + /// - Parameters: + /// - name: The name of the pointer authentication scheme to check. + /// - Returns: Whether the code generated for the target will use the given + /// pointer authentication scheme. + public func isActiveTargetPointerAuthentication(name: String) -> Bool { + targetPointerAuthenticationSchemes.contains(name) + } + + /// Determine whether the given name is the active target object file format (e.g., ELF). + /// + /// The target object file format can only be queried by an experimental + /// syntax `_objectFileFormat()`, e.g., + /// + /// ```swift + /// #if _objectFileFormat(ELF) + /// // Special logic for ELF object file formats + /// #endif + /// ``` + /// - Parameters: + /// - name: The name of the object file format. + /// - Returns: Whether the target object file format matches the given name. + public func isActiveTargetObjectFormat(name: String) -> Bool { + targetObjectFileFormats.contains(name) + } + + /// Equivalent to `languageMode`, but required for conformance to the + /// `BuildConfiguration` protocol. + public var languageVersion: VersionTuple { + languageMode + } +} + +extension StaticBuildConfiguration { + enum Error: Swift.Error { + /// Indicates when the static build configuration was asked to evaluate + /// canImport, which it cannot do correctly. + case canImportUnavailable + } +} + +extension StaticBuildConfiguration { + /// The Swift version that can be set for the parser. + public var parserSwiftVersion: Parser.SwiftVersion { + if languageMode < VersionTuple(5) { + return .v4 + } else if languageMode < VersionTuple(6) { + return .v5 + } else if languageMode < VersionTuple(7) { + return .v6 + } else { + return Parser.defaultSwiftVersion + } + } + + /// Determine the set of experimental features that are enabled by this + /// static build configuration. + @_spi(ExperimentalLanguageFeatures) + public var experimentalFeatures: Parser.ExperimentalFeatures { + var result: Parser.ExperimentalFeatures = [] + for feature in features { + if let experimentalFeature = Parser.ExperimentalFeatures(name: feature) { + result.insert(experimentalFeature) + } + } + return result + } +} diff --git a/Sources/SwiftIfConfig/VersionTuple+Parsing.swift b/Sources/SwiftIfConfig/VersionTuple+Parsing.swift index d72100864c7..804f5185db8 100644 --- a/Sources/SwiftIfConfig/VersionTuple+Parsing.swift +++ b/Sources/SwiftIfConfig/VersionTuple+Parsing.swift @@ -56,7 +56,7 @@ extension VersionTuple { /// Record a component after checking its value. func recordComponent(_ value: Int) throws { - let limit = components.isEmpty ? 9223371 : 999 + let limit = components.isEmpty ? 9_223_371 : 999 if value < 0 || value > limit { throw IfConfigDiagnostic.compilerVersionOutOfRange(value: value, upperLimit: limit, syntax: versionSyntax) } diff --git a/Sources/SwiftIfConfig/VersionTuple.swift b/Sources/SwiftIfConfig/VersionTuple.swift index d31d544b59b..ce2484c5a3c 100644 --- a/Sources/SwiftIfConfig/VersionTuple.swift +++ b/Sources/SwiftIfConfig/VersionTuple.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// /// Describes a version such as `5.9`. -public struct VersionTuple: Sendable { +public struct VersionTuple: Sendable, Codable { /// The components of the version tuple, start with the major version. public var components: [Int] diff --git a/Sources/SwiftLibraryPluginProvider/LibraryPluginProvider.swift b/Sources/SwiftLibraryPluginProvider/LibraryPluginProvider.swift index 7cb686a0622..ae95aeb7445 100644 --- a/Sources/SwiftLibraryPluginProvider/LibraryPluginProvider.swift +++ b/Sources/SwiftLibraryPluginProvider/LibraryPluginProvider.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#if !os(WASI) + #if compiler(>=6) public import SwiftSyntaxMacros @_spi(PluginMessage) public import SwiftCompilerPluginMessageHandling @@ -221,3 +223,5 @@ extension UnsafeMutableBufferPointer { } } #endif + +#endif // !os(WASI) diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index 3cfc6ae801d..9add4a654ae 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -17,18 +17,52 @@ #endif extension Parser { - mutating func parseAttributeList() -> RawAttributeListSyntax { - guard self.at(.atSign, .poundIf) else { - return self.emptyCollection(RawAttributeListSyntax.self) + private mutating func parseAttributeListElement() -> RawAttributeListSyntax.Element { + if self.at(.poundIf) { + // 'consumeIfConfigOfAttributes()' check in 'parseAttributeList()' already guarantees + // that this '#if' only contains attribute list elements. We don't check + // `consumeIfConfigOfAttributes()` here again because it's not only redundant, but it + // does *not* work for cases like: + // + // #if COND1 + // @attr + // #if COND2 + // #endif + // #endif + // func fn() {} + // + // In such cases, the second `#if` is not `consumeIfConfigOfAttributes()`. + return .ifConfigDecl( + self.parsePoundIfDirective({ .attributes($0.parseAttributeList()) }) + ) + } else { + return .attribute(self.parseAttribute()) } + } + mutating func parseAttributeList() -> RawAttributeListSyntax { var elements = [RawAttributeListSyntax.Element]() + + func shouldContinue() -> Bool { + if self.at(.atSign) { + return true + } + if self.at(.poundIf) && self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { + return true + } + return false + } + var loopProgress = LoopProgressCondition() - repeat { - let attribute = self.parseAttribute() + while self.hasProgressed(&loopProgress) && shouldContinue() { + let attribute = self.parseAttributeListElement() elements.append(attribute) - } while self.at(.atSign, .poundIf) && self.hasProgressed(&loopProgress) - return RawAttributeListSyntax(elements: elements, arena: self.arena) + } + if elements.isEmpty { + return self.emptyCollection(RawAttributeListSyntax.self) + } else { + return RawAttributeListSyntax(elements: elements, arena: self.arena) + } } } @@ -41,6 +75,7 @@ extension Parser { case _effects case _implements case _originallyDefinedIn + case specialized case _specialize case _spi_available case `rethrows` @@ -63,6 +98,7 @@ extension Parser { case TokenSpec(._effects): self = ._effects case TokenSpec(._implements): self = ._implements case TokenSpec(._originallyDefinedIn): self = ._originallyDefinedIn + case TokenSpec(.specialized): self = .specialized case TokenSpec(._specialize): self = ._specialize case TokenSpec(._spi_available): self = ._spi_available case TokenSpec(.`rethrows`): self = .rethrows @@ -89,6 +125,7 @@ extension Parser { case ._effects: return .keyword(._effects) case ._implements: return .keyword(._implements) case ._originallyDefinedIn: return .keyword(._originallyDefinedIn) + case .specialized: return .keyword(.specialized) case ._specialize: return .keyword(._specialize) case ._spi_available: return .keyword(._spi_available) case .`rethrows`: return .keyword(.rethrows) @@ -145,7 +182,7 @@ extension Parser { parseMissingArguments: ( (inout Parser) -> (unexpectedBefore: RawUnexpectedNodesSyntax?, arguments: RawAttributeSyntax.Arguments) )? = nil - ) -> RawAttributeListSyntax.Element { + ) -> RawAttributeSyntax { var (unexpectedBeforeAtSign, atSign) = self.expect(.atSign) if atSign.trailingTriviaByteLength > 0 || self.currentToken.leadingTriviaByteLength > 0 { let diagnostic = TokenDiagnostic( @@ -155,30 +192,35 @@ extension Parser { atSign = atSign.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena) } let attributeName = self.parseAttributeName() + let attributeNameHasTrailingSpace = attributeName.raw.trailingTriviaByteLength > 0 + let shouldParseArgument: Bool switch argumentMode { case .required: shouldParseArgument = true case .customAttribute: - shouldParseArgument = - self.withLookahead { $0.atAttributeOrSpecifierArgument() } - && self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) + shouldParseArgument = self.withLookahead { + $0.atAttributeOrSpecifierArgument(lastTokenHadSpace: attributeNameHasTrailingSpace, forCustomAttribute: true) + } case .optional: - shouldParseArgument = self.at(.leftParen) + shouldParseArgument = self.withLookahead { + $0.atAttributeOrSpecifierArgument(lastTokenHadSpace: attributeNameHasTrailingSpace, forCustomAttribute: false) + } case .noArgument: shouldParseArgument = false } if shouldParseArgument { - var (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) - if unexpectedBeforeLeftParen == nil - && (attributeName.raw.trailingTriviaByteLength > 0 || leftParen.leadingTriviaByteLength > 0) - { + var (unexpectedBeforeLeftParen, leftParen) = self.expect(TokenSpec(.leftParen, allowAtStartOfLine: false)) + + // Diagnose spaces between the name and the '('. + if unexpectedBeforeLeftParen == nil && (attributeNameHasTrailingSpace || leftParen.leadingTriviaByteLength > 0) { let diagnostic = TokenDiagnostic( self.swiftVersion < .v6 ? .extraneousLeadingWhitespaceWarning : .extraneousLeadingWhitespaceError, byteOffset: 0 ) leftParen = leftParen.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena) } + let unexpectedBeforeArguments: RawUnexpectedNodesSyntax? let argument: RawAttributeSyntax.Arguments if let parseMissingArguments, leftParen.presence == .missing { @@ -187,52 +229,42 @@ extension Parser { (unexpectedBeforeArguments, argument) = parseArguments(&self) } let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) - return .attribute( - RawAttributeSyntax( - unexpectedBeforeAtSign, - atSign: atSign, - attributeName: attributeName, - unexpectedBeforeLeftParen, - leftParen: leftParen, - unexpectedBeforeArguments, - arguments: argument, - unexpectedBeforeRightParen, - rightParen: rightParen, - arena: self.arena - ) + return RawAttributeSyntax( + unexpectedBeforeAtSign, + atSign: atSign, + attributeName: attributeName, + unexpectedBeforeLeftParen, + leftParen: leftParen, + unexpectedBeforeArguments, + arguments: argument, + unexpectedBeforeRightParen, + rightParen: rightParen, + arena: self.arena ) } else { - return .attribute( - RawAttributeSyntax( - unexpectedBeforeAtSign, - atSign: atSign, - attributeName: attributeName, - leftParen: nil, - arguments: nil, - rightParen: nil, - arena: self.arena - ) + return RawAttributeSyntax( + unexpectedBeforeAtSign, + atSign: atSign, + attributeName: attributeName, + leftParen: nil, + arguments: nil, + rightParen: nil, + arena: self.arena ) } } - mutating func parseAttribute() -> RawAttributeListSyntax.Element { - if self.at(.poundIf) { - return .ifConfigDecl( - self.parsePoundIfDirective { (parser, _) -> RawAttributeListSyntax.Element in - return parser.parseAttribute() - } syntax: { parser, attributes in - return .attributes(RawAttributeListSyntax(elements: attributes, arena: parser.arena)) - } - ) - } + mutating func parseAttribute() -> RawAttributeSyntax { + // An attribute qualified by a module selector is *always* a custom attribute, even if it has the same name (or + // module name) as a builtin attribute. + let builtinAttr = self.unlessPeekModuleSelector { $0.peek(isAtAnyIn: DeclarationAttributeWithSpecialSyntax.self) } - switch peek(isAtAnyIn: DeclarationAttributeWithSpecialSyntax.self) { + switch builtinAttr { case .abi: return parseAttribute(argumentMode: .required) { parser in - return parser.parseABIAttributeArguments() + return (nil, .abiArguments(parser.parseABIAttributeArguments())) } parseMissingArguments: { parser in - return parser.parseABIAttributeArguments(missingLParen: true) + return (nil, .abiArguments(parser.parseABIAttributeArguments(missingLParen: true))) } case .available, ._spi_available: return parseAttribute(argumentMode: .required) { parser in @@ -254,6 +286,10 @@ extension Parser { return parseAttribute(argumentMode: .optional) { parser in return (nil, .objCName(parser.parseObjectiveCSelector())) } + case .specialized: + return parseAttribute(argumentMode: .required) { parser in + return (nil, .specializedArguments(parser.parseSpecializedAttributeArgument())) + } case ._specialize: return parseAttribute(argumentMode: .required) { parser in return (nil, .specializeArguments(parser.parseSpecializeAttributeArgumentList())) @@ -292,17 +328,20 @@ extension Parser { case .rethrows: let (unexpectedBeforeAtSign, atSign) = self.expect(.atSign) let (unexpectedBeforeAttributeName, attributeName) = self.expect(TokenSpec(.rethrows, remapping: .identifier)) - return .attribute( - RawAttributeSyntax( - unexpectedBeforeAtSign, - atSign: atSign, - unexpectedBeforeAttributeName, - attributeName: RawIdentifierTypeSyntax(name: attributeName, genericArgumentClause: nil, arena: self.arena), - leftParen: nil, - arguments: nil, - rightParen: nil, + return RawAttributeSyntax( + unexpectedBeforeAtSign, + atSign: atSign, + unexpectedBeforeAttributeName, + attributeName: RawIdentifierTypeSyntax( + moduleSelector: nil, + name: attributeName, + genericArgumentClause: nil, arena: self.arena - ) + ), + leftParen: nil, + arguments: nil, + rightParen: nil, + arena: self.arena ) case .Sendable: return parseAttribute(argumentMode: .noArgument) { parser in @@ -322,7 +361,9 @@ extension Parser { extension RawLabeledExprSyntax { fileprivate init( - _ unexpectedBeforeIdentifier: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBeforeModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndIdentifier: RawUnexpectedNodesSyntax? = nil, identifier: RawTokenSyntax, _ unexpectedBetweenIdentifierAndTrailingComma: RawUnexpectedNodesSyntax? = nil, trailingComma: RawTokenSyntax? = nil, @@ -332,7 +373,9 @@ extension RawLabeledExprSyntax { label: nil, colon: nil, expression: RawDeclReferenceExprSyntax( - unexpectedBeforeIdentifier, + unexpectedBeforeModuleSelector, + moduleSelector: moduleSelector, + unexpectedBetweenModuleSelectorAndIdentifier, baseName: identifier, argumentNames: nil, arena: arena @@ -354,6 +397,7 @@ extension Parser { let roleTrailingComma = self.consume(if: .comma) let roleElement = RawLabeledExprSyntax( + moduleSelector: nil, unexpectedBeforeRole, identifier: role, trailingComma: roleTrailingComma, @@ -383,7 +427,12 @@ extension Parser { unexpectedBeforeAtSign, atSign: atSign, unexpectedBeforeDifferentiable, - attributeName: RawIdentifierTypeSyntax(name: differentiable, genericArgumentClause: nil, arena: self.arena), + attributeName: RawIdentifierTypeSyntax( + moduleSelector: nil, + name: differentiable, + genericArgumentClause: nil, + arena: self.arena + ), unexpectedBeforeLeftParen, leftParen: leftParen, arguments: .differentiableArguments(argument), @@ -524,8 +573,13 @@ extension Parser { return RawAttributeSyntax( unexpectedBeforeAtSign, atSign: atSign, - unexpectedBeforeDerivative, - attributeName: RawIdentifierTypeSyntax(name: derivative, genericArgumentClause: nil, arena: self.arena), + attributeName: RawIdentifierTypeSyntax( + moduleSelector: nil, + unexpectedBeforeDerivative, + name: derivative, + genericArgumentClause: nil, + arena: self.arena + ), unexpectedBeforeLeftParen, leftParen: leftParen, arguments: .derivativeRegistrationArguments(argument), @@ -547,7 +601,12 @@ extension Parser { unexpectedBeforeAtSign, atSign: atSign, unexpectedBeforeTranspose, - attributeName: RawIdentifierTypeSyntax(name: transpose, genericArgumentClause: nil, arena: self.arena), + attributeName: RawIdentifierTypeSyntax( + moduleSelector: nil, + name: transpose, + genericArgumentClause: nil, + arena: self.arena + ), unexpectedBeforeLeftParen, leftParen: leftParen, arguments: .derivativeRegistrationArguments(argument), @@ -602,8 +661,8 @@ extension Parser { var elements = [RawObjCSelectorPieceSyntax]() var loopProgress = LoopProgressCondition() while self.hasProgressed(&loopProgress) { - // Empty selector piece. - if let colon = self.consume(if: .colon) { + // Empty selector piece, splitting `::` into two colons. + if let colon = self.consume(ifPrefix: ":", as: .colon) { elements.append( RawObjCSelectorPieceSyntax( name: nil, @@ -627,7 +686,8 @@ extension Parser { break } - let (unexpectedBeforeColon, colon) = self.expect(.colon) + // Match ending colon, spliting `::` into two colons. + let (unexpectedBeforeColon, colon) = self.expect(prefix: ":", as: .colon) elements.append( RawObjCSelectorPieceSyntax( name: name, @@ -645,6 +705,11 @@ extension Parser { } extension Parser { + mutating func parseSpecializedAttributeArgument() -> RawSpecializedAttributeArgumentSyntax { + let whereClause = self.parseGenericWhereClause() + return RawSpecializedAttributeArgumentSyntax(genericWhereClause: whereClause, arena: self.arena) + } + mutating func parseSpecializeAttributeArgumentList() -> RawSpecializeAttributeArgumentListSyntax { var elements = [RawSpecializeAttributeArgumentListSyntax.Element]() // Parse optional "exported" and "kind" labeled parameters. @@ -776,6 +841,7 @@ extension Parser { let (unexpectedBeforeIsolationKind, isolationKind) = self.expectIdentifier(allowKeywordsAsIdentifier: true) let isolationKindElement = RawLabeledExprSyntax( + moduleSelector: nil, unexpectedBeforeIsolationKind, identifier: isolationKind, arena: self.arena @@ -794,9 +860,9 @@ extension Parser { /// - Parameter missingLParen: `true` if the opening paren for the argument list was missing. mutating func parseABIAttributeArguments( missingLParen: Bool = false - ) -> (RawUnexpectedNodesSyntax?, RawAttributeSyntax.Arguments) { - func makeMissingProviderArguments(unexpectedBefore: [RawSyntax]) -> RawAttributeSyntax.Arguments { - let args = RawABIAttributeArgumentsSyntax( + ) -> RawABIAttributeArgumentsSyntax { + func makeMissingProviderArguments(unexpectedBefore: [RawSyntax]) -> RawABIAttributeArgumentsSyntax { + return RawABIAttributeArgumentsSyntax( provider: .missing( RawMissingDeclSyntax( unexpectedBefore.isEmpty ? nil : RawUnexpectedNodesSyntax(elements: unexpectedBefore, arena: self.arena), @@ -808,7 +874,6 @@ extension Parser { ), arena: self.arena ) - return .abiArguments(args) } // Consider the three kinds of mistakes we might see here: @@ -824,23 +889,41 @@ extension Parser { // In lieu of that, we judge that recovering gracefully from #3 is more important than #2 and therefore do not even // attempt to parse the argument unless we've seen a left paren. guard !missingLParen && !self.at(.rightParen) else { - return (nil, makeMissingProviderArguments(unexpectedBefore: [])) + return makeMissingProviderArguments(unexpectedBefore: []) } - let decl = parseDeclaration(in: .argumentList) - - guard experimentalFeatures.contains(.abiAttribute) else { - return ( - RawUnexpectedNodesSyntax([decl], arena: self.arena), - .argumentList(RawLabeledExprListSyntax(elements: [], arena: self.arena)) + let decl: RawDeclSyntax + if self.at(.poundIf) { + // '#if' is not accepted in '@abi' attribute, but for recovery, parse it + // and wrap the first decl in it with unexpected nodes. + let ifConfig = self.parsePoundIfDirective({ parser in + let decl = parser.parseDeclaration(in: .argumentList) + let member = RawMemberBlockItemSyntax(decl: decl, semicolon: nil, arena: parser.arena) + return .decls(RawMemberBlockItemListSyntax(elements: [member], arena: parser.arena)) + }) + decl = ifConfig.makeUnexpectedKeepingFirstNode( + of: RawDeclSyntax.self, + arena: self.arena, + where: { !$0.is(RawIfConfigDeclSyntax.self) }, + makeMissing: { + RawDeclSyntax( + RawMissingDeclSyntax( + attributes: self.emptyCollection(RawAttributeListSyntax.self), + modifiers: self.emptyCollection(RawDeclModifierListSyntax.self), + arena: self.arena + ) + ) + } ) + } else { + decl = self.parseDeclaration(in: .argumentList) } guard let provider = RawABIAttributeArgumentsSyntax.Provider(decl) else { - return (nil, makeMissingProviderArguments(unexpectedBefore: [decl.raw])) + return makeMissingProviderArguments(unexpectedBefore: [decl.raw]) } - return (nil, .abiArguments(RawABIAttributeArgumentsSyntax(provider: provider, arena: self.arena))) + return RawABIAttributeArgumentsSyntax(provider: provider, arena: self.arena) } } @@ -914,6 +997,7 @@ extension Parser { let declName: RawDeclReferenceExprSyntax if label.isMissing && colon.isMissing && self.atStartOfLine { declName = RawDeclReferenceExprSyntax( + moduleSelector: nil, baseName: RawTokenSyntax(missing: .identifier, arena: self.arena), argumentNames: nil, arena: self.arena @@ -1018,42 +1102,70 @@ extension Parser { // MARK: Lookahead extension Parser.Lookahead { - mutating func atAttributeOrSpecifierArgument() -> Bool { - var lookahead = self.lookahead() - lookahead.skipSingle() - - // If we have any keyword, identifier, or token that follows a function - // type's parameter list, this is a parameter list and not an attribute. - // Alternatively, we might have a token that illustrates we're not going to - // get anything following the attribute, which means the parentheses describe - // what follows the attribute. - switch lookahead.currentToken { - case TokenSpec(.arrow), - TokenSpec(.throw), - TokenSpec(.throws), - TokenSpec(.rethrows), - TokenSpec(.rightParen), - TokenSpec(.rightBrace), - TokenSpec(.rightSquare), - TokenSpec(.rightAngle): - return false - case _ where lookahead.at(.keyword(.async)): - return false - case _ where lookahead.at(.keyword(.reasync)): + mutating func atAttributeOrSpecifierArgument( + lastTokenHadSpace: Bool, + forCustomAttribute: Bool = false + ) -> Bool { + if !self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) { return false - default: - return true + } + + if self.swiftVersion >= .v6 { + if !lastTokenHadSpace && currentToken.leadingTriviaByteLength == 0 { + return true + } + + return withLookahead({ + $0.skipSingle() + return $0.at(.atSign) || $0.atStartOfDeclaration() + }) + } else { + if !forCustomAttribute { + return true + } + var lookahead = self.lookahead() + lookahead.skipSingle() + + // If we have any keyword, identifier, or token that follows a function + // type's parameter list, this is a parameter list and not an attribute. + // Alternatively, we might have a token that illustrates we're not going to + // get anything following the attribute, which means the parentheses describe + // what follows the attribute. + switch lookahead.currentToken { + case TokenSpec(.arrow), + TokenSpec(.throw), + TokenSpec(.throws), + TokenSpec(.rethrows), + TokenSpec(.rightParen), + TokenSpec(.rightBrace), + TokenSpec(.rightSquare), + TokenSpec(.rightAngle): + return false + case _ where lookahead.at(.keyword(.async)): + return false + case _ where lookahead.at(.keyword(.reasync)): + return false + default: + return true + } } } mutating func canParseCustomAttribute() -> Bool { - guard self.canParseType() else { + guard + let numTypeTokens = self.withLookahead({ $0.canParseSimpleType() ? $0.tokensConsumed : nil }), + numTypeTokens >= 1 + else { return false } + // Check if the last token had trailing white spaces. + for _ in 0.. 0 + self.consumeAnyToken() - if self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) - && self.withLookahead({ $0.atAttributeOrSpecifierArgument() }) - { + if self.atAttributeOrSpecifierArgument(lastTokenHadSpace: hasSpace, forCustomAttribute: true) { self.skipSingle() } diff --git a/Sources/SwiftParser/CollectionNodes+Parsable.swift b/Sources/SwiftParser/CollectionNodes+Parsable.swift index da31a9424ec..3424de77418 100644 --- a/Sources/SwiftParser/CollectionNodes+Parsable.swift +++ b/Sources/SwiftParser/CollectionNodes+Parsable.swift @@ -116,7 +116,7 @@ extension CodeBlockItemListSyntax: SyntaxParseable { extension MemberBlockItemListSyntax: SyntaxParseable { public static func parse(from parser: inout Parser) -> Self { return parse(from: &parser) { parser in - return parser.parseMemberDeclList() + return parser.parseMemberDeclList(until: { _ in false }) } makeMissing: { remainingTokens, arena in let missingDecl = RawMissingDeclSyntax( attributes: RawAttributeListSyntax(elements: [], arena: arena), diff --git a/Sources/SwiftParser/CompilerFiles.swift b/Sources/SwiftParser/CompilerFiles.swift index dbaea1a1902..2424c7964a6 100644 --- a/Sources/SwiftParser/CompilerFiles.swift +++ b/Sources/SwiftParser/CompilerFiles.swift @@ -89,7 +89,7 @@ extension Parser { /// Parse a declaration macro expansions in type contexts. mutating func parseMemberBlockItemListFile() -> RawMemberBlockItemListFileSyntax { - let members = self.parseMemberDeclList() + let members = self.parseMemberDeclList(until: { _ in false }) let (unexpectedBeforeEndOfFileToken, endOfFile) = self.expectEndOfFile() return RawMemberBlockItemListFileSyntax( diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index a9e4a01f449..bfe5d3022e1 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -52,81 +52,42 @@ extension TokenConsumer { } } + /// Returns `true` if the current token represents the start of a declaration + /// item. + /// + /// - Parameters + /// - allowInitDecl: whether to consider 'init' a declaration in the context. + /// Only initializer bodies should use `false` for this. + /// - requiresDecl: Whether only declarations are expected in the context. + /// For example, in member blocks. + /// + /// - Note: this returns `false` for `#if` unless it's an attribute list. mutating func atStartOfDeclaration( - isAtTopLevel: Bool = false, allowInitDecl: Bool = true, - allowRecovery: Bool = false + requiresDecl: Bool = false ) -> Bool { - if self.at(.poundIf) { - return true - } - var subparser = self.lookahead() - var hasAttribute = false - var attributeProgress = LoopProgressCondition() - while subparser.hasProgressed(&attributeProgress) && subparser.at(.atSign) { - hasAttribute = true - _ = subparser.consumeAttributeList() - } - - var hasModifier = false - if subparser.currentToken.isLexerClassifiedKeyword || subparser.currentToken.rawTokenKind == .identifier { - var modifierProgress = LoopProgressCondition() - while let (modifierKind, handle) = subparser.at(anyIn: DeclarationModifier.self), - modifierKind != .class, - subparser.hasProgressed(&modifierProgress) - { - hasModifier = true - subparser.eat(handle) - if modifierKind != .open && subparser.at(.leftParen) && modifierKind.canHaveParenthesizedArgument { - // When determining whether we are at a declaration, don't consume anything in parentheses after 'open' - // so we don't consider a function call to open as a decl modifier. This matches the C++ parser. - subparser.consumeAnyToken() - subparser.consume(to: .rightParen) - } - } - } + let (hasAttribute, hasModifier) = subparser.skipAttributesAndModifiers() if hasAttribute { if subparser.at(.rightBrace) || subparser.at(.endOfFile) || subparser.at(.poundEndif) { return true } + if subparser.at(.stringQuote) { + // `@"abc"` is an invalid Objective-C-style string literal, not a declaration. + return false + } } - if subparser.at(.poundIf) { - var attrLookahead = subparser.lookahead() - return attrLookahead.consumeIfConfigOfAttributes() - } - - let declStartKeyword: DeclarationKeyword? - if allowRecovery { - declStartKeyword = - subparser.canRecoverTo( - anyIn: DeclarationKeyword.self, - overrideRecoveryPrecedence: isAtTopLevel ? nil : .closingBrace - )?.0 - } else { - declStartKeyword = subparser.at(anyIn: DeclarationKeyword.self)?.0 - } - switch declStartKeyword { + switch subparser.at(anyIn: DeclarationKeyword.self)?.0 { case .lhs(.actor): // actor Foo {} - if subparser.peek().rawTokenKind == .identifier { - return true - } - // actor may be somewhere in the modifier list. Eat the tokens until we get - // to something that isn't the start of a decl. If that is an identifier, - // it's an actor declaration, otherwise, it isn't. - var lookahead = subparser.lookahead() - repeat { - lookahead.consumeAnyToken() - } while lookahead.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) - return lookahead.at(.identifier) + return subparser.atStartOfActor(allowInitDecl: allowInitDecl, requiresDecl: requiresDecl) case .lhs(.case): // When 'case' appears inside a function, it's probably a switch // case, not an enum case declaration. - return false + return requiresDecl case .lhs(.`init`): return allowInitDecl case .lhs(.macro): @@ -134,12 +95,19 @@ extension TokenConsumer { return subparser.peek().rawTokenKind == .identifier case .lhs(.pound): // Force parsing '#' after attributes as a macro expansion decl. - if hasAttribute || hasModifier { + if hasAttribute || hasModifier || requiresDecl { return true } // Otherwise, parse it as an expression. return false + case .lhs(.using): + // This declaration doesn't support attributes or modifiers + if hasAttribute || hasModifier { + return false + } + + return subparser.atStartOfUsing() case .some(_): // All other decl start keywords unconditionally start a decl. return true @@ -147,13 +115,107 @@ extension TokenConsumer { if subparser.at(anyIn: ContextualDeclKeyword.self)?.0 != nil { subparser.consumeAnyToken() return subparser.atStartOfDeclaration( - isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl, - allowRecovery: allowRecovery + requiresDecl: requiresDecl ) } + if requiresDecl { + // If we found any attributes or modifiers, consider it's a missing decl. + if hasAttribute || hasModifier { + return true + } + if subparser.atFunctionDeclarationWithoutFuncKeyword() { + return true + } + if subparser.atBindingDeclarationWithoutVarKeyword() { + return true + } + if subparser.currentToken.isEditorPlaceholder { + return true + } + } + // Special recovery for 'try let/var'. + if subparser.at(.keyword(.try)), + subparser.peek(isAtAnyIn: VariableDeclSyntax.BindingSpecifierOptions.self) != nil + { + return true + } + return false + } + } +} + +extension Parser.Lookahead { + fileprivate mutating func skipAttributesAndModifiers() -> (hasAttribute: Bool, hasModifier: Bool) { + var hasAttribute = false + var attributeProgress = LoopProgressCondition() + while self.hasProgressed(&attributeProgress) { + if self.at(.atSign) { + _ = self.consumeAttributeList() + hasAttribute = true + } else if self.at(.poundIf) && self.consumeIfConfigOfAttributes() { + hasAttribute = true + } else { + break + } + } + + var hasModifier = false + if self.currentToken.isLexerClassifiedKeyword || self.currentToken.rawTokenKind == .identifier { + var modifierProgress = LoopProgressCondition() + while let (modifierKind, handle) = self.at(anyIn: DeclarationModifier.self), + modifierKind != .class, + self.hasProgressed(&modifierProgress) + { + hasModifier = true + self.eat(handle) + if modifierKind != .open && self.at(.leftParen) && modifierKind.canHaveParenthesizedArgument { + // When determining whether we are at a declaration, don't consume anything in parentheses after 'open' + // so we don't consider a function call to open as a decl modifier. This matches the C++ parser. + self.consumeAnyToken() + self.consume(to: .rightParen) + } + } + } + + return (hasAttribute, hasModifier) + } + + fileprivate mutating func atStartOfActor( + allowInitDecl: Bool, + requiresDecl: Bool + ) -> Bool { + if self.peek().rawTokenKind == .identifier { + return true + } + // actor may be somewhere in the modifier list. Eat the tokens until we get + // to something that isn't the start of a decl. If that is an identifier, + // it's an actor declaration, otherwise, it isn't. + var lookahead = self.lookahead() + repeat { + lookahead.consumeAnyToken() + } while lookahead.atStartOfDeclaration(allowInitDecl: allowInitDecl, requiresDecl: requiresDecl) + return lookahead.at(.identifier) + } + + fileprivate mutating func atStartOfUsing() -> Bool { + var lookahead = self.lookahead() + + // Consume 'using' + lookahead.consumeAnyToken() + + // Allow parsing 'using' as declaration only if + // it's immediately followed by either `@` or + // an identifier. + if lookahead.atStartOfLine { return false } + + guard lookahead.at(.atSign) || lookahead.at(.identifier) else { + return false + } + + return true } } @@ -223,50 +285,6 @@ extension Parser { /// - Parameter context: Describes the code around the declaration being parsed. This affects how the parser tries /// to recover from malformed syntax in the declaration. mutating func parseDeclaration(in context: DeclarationParseContext = .topLevelOrCodeBlock) -> RawDeclSyntax { - // If we are at a `#if` of attributes, the `#if` directive should be - // parsed when we're parsing the attributes. - if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { - let directive = self.parsePoundIfDirective { (parser, _) in - let parsedDecl = parser.parseDeclaration() - let semicolon = parser.consume(if: .semicolon) - return RawMemberBlockItemSyntax( - decl: parsedDecl, - semicolon: semicolon, - arena: parser.arena - ) - } addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, parser in - if lastElement.semicolon == nil && !newItemAtStartOfLine { - return RawMemberBlockItemSyntax( - lastElement.unexpectedBeforeDecl, - decl: lastElement.decl, - lastElement.unexpectedBetweenDeclAndSemicolon, - semicolon: parser.missingToken(.semicolon), - lastElement.unexpectedAfterSemicolon, - arena: parser.arena - ) - } else { - return nil - } - } syntax: { parser, elements in - return .decls(RawMemberBlockItemListSyntax(elements: elements, arena: parser.arena)) - } - if !context.allowsIfConfigDecl { - // Convert the IfConfig to unexpected syntax around the first decl inside it, if any. - return directive.makeUnexpectedKeepingFirstNode(of: RawDeclSyntax.self, arena: self.arena) { node in - return !node.is(RawIfConfigDeclSyntax.self) - } makeMissing: { - return RawDeclSyntax( - RawMissingDeclSyntax( - attributes: self.emptyCollection(RawAttributeListSyntax.self), - modifiers: self.emptyCollection(RawDeclModifierListSyntax.self), - arena: self.arena - ) - ) - } - } - return RawDeclSyntax(directive) - } - let attrs = DeclAttributes( attributes: self.parseAttributeList(), modifiers: self.parseDeclModifierList() @@ -280,11 +298,11 @@ extension Parser { // We aren't at a declaration keyword and it looks like we are at a function // declaration. Parse a function declaration. recoveryResult = (.lhs(.func), .missing(.keyword(.func))) + } else if atBindingDeclarationWithoutVarKeyword() { + recoveryResult = (.rhs(.var), .missing(.keyword(.var))) } else { // In all other cases, use standard token recovery to find the declaration // to parse. - // If we are inside a memberDecl list, we don't want to eat closing braces (which most likely close the outer context) - // while recovering to the declaration start. recoveryResult = self.canRecoverTo( anyIn: DeclarationKeyword.self, overrideRecoveryPrecedence: context.recoveryPrecedence @@ -338,6 +356,8 @@ extension Parser { return RawDeclSyntax(self.parseMacroDeclaration(attrs: attrs, introducerHandle: handle)) case (.lhs(.pound), let handle)?: return RawDeclSyntax(self.parseMacroExpansionDeclaration(attrs, handle)) + case (.lhs(.using), let handle)?: + return RawDeclSyntax(self.parseUsingDeclaration(attrs: attrs, introducerHandle: handle)) case (.rhs, let handle)?: return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, in: context)) case nil: @@ -345,13 +365,6 @@ extension Parser { } if context.requiresDecl { - let isProbablyVarDecl = self.at(.identifier, .wildcard) && self.peek(isAt: .colon, .equal, .comma) - let isProbablyTupleDecl = self.at(.leftParen) && self.peek(isAt: .identifier, .wildcard) - - if isProbablyVarDecl || isProbablyTupleDecl { - return RawDeclSyntax(self.parseBindingDeclaration(attrs, .missing(.keyword(.var)), in: context)) - } - if self.currentToken.isEditorPlaceholder { let placeholder = self.parseAnyIdentifier() return RawDeclSyntax( @@ -363,10 +376,6 @@ extension Parser { ) ) } - - if atFunctionDeclarationWithoutFuncKeyword() { - return RawDeclSyntax(self.parseFuncDeclaration(attrs, .missing(.keyword(.func)))) - } } return RawDeclSyntax( RawMissingDeclSyntax( @@ -376,6 +385,27 @@ extension Parser { ) ) } +} + +extension TokenConsumer { + /// Returns `true` if it looks like the parser is positioned at a variable declaration that’s missing the `var` keyword. + fileprivate mutating func atBindingDeclarationWithoutVarKeyword() -> Bool { + if self.at(.identifier, .wildcard), + self.peek(isAt: .colon, .equal, .comma) + { + return true + } + if self.at(.leftParen), + self.peek(isAt: .identifier, .wildcard), + self.withLookahead({ + $0.skipSingle(); return $0.at(.colon, .equal) + }) + { + return true + } + + return false + } /// Returns `true` if it looks like the parser is positioned at a function declaration that’s missing the `func` keyword. fileprivate mutating func atFunctionDeclarationWithoutFuncKeyword() -> Bool { @@ -404,7 +434,7 @@ extension Parser { ) -> RawImportDeclSyntax { let (unexpectedBeforeImportKeyword, importKeyword) = self.eat(handle) let kind = self.parseImportKind() - let path = self.parseImportPath() + let path = self.parseImportPath(hasImportKind: kind != nil) return RawImportDeclSyntax( attributes: attrs.attributes, modifiers: attrs.modifiers, @@ -420,25 +450,120 @@ extension Parser { return self.consume(ifAnyIn: ImportDeclSyntax.ImportKindSpecifierOptions.self) } - mutating func parseImportPath() -> RawImportPathComponentListSyntax { + mutating func parseImportPath(hasImportKind: Bool) -> RawImportPathComponentListSyntax { var elements = [RawImportPathComponentSyntax]() - var keepGoing: RawTokenSyntax? = nil - var loopProgress = LoopProgressCondition() - repeat { - let name = self.parseAnyIdentifier() - keepGoing = self.consume(if: .period) - elements.append( + + // Special case: scoped import with module selector-style syntax. This always has exactly two path components + // separated by '::'. + if hasImportKind, + let (moduleNameOrUnexpected, colonColon, unexpectedAfterColonColon, skipQualifiedName) = + self.consumeModuleSelectorTokensIfPresent() + { + // Is the token in module name position really a module name? + let unexpectedBeforeModuleName: RawUnexpectedNodesSyntax? + let moduleName: RawTokenSyntax + if moduleNameOrUnexpected.tokenKind == .identifier { + unexpectedBeforeModuleName = nil + moduleName = moduleNameOrUnexpected + } else { + unexpectedBeforeModuleName = RawUnexpectedNodesSyntax([moduleNameOrUnexpected], arena: self.arena) + moduleName = self.missingToken(.identifier) + } + + let declName = skipQualifiedName ? self.missingToken(.identifier) : self.parseAnyIdentifier() + + elements = [ + RawImportPathComponentSyntax( + unexpectedBeforeModuleName, + name: moduleName, + trailingPeriod: colonColon, + RawUnexpectedNodesSyntax(unexpectedAfterColonColon, arena: self.arena), + arena: self.arena + ), RawImportPathComponentSyntax( - name: name, - trailingPeriod: keepGoing, + name: declName, + trailingPeriod: nil, arena: self.arena + ), + ] + } else { + var keepGoing: RawTokenSyntax? = nil + var loopProgress = LoopProgressCondition() + repeat { + let name = self.parseAnyIdentifier() + keepGoing = self.consume(if: .period) + + // '::' is not valid if we got here, but someone might try to use it anyway. + let unexpectedAfterTrailingPeriod: RawUnexpectedNodesSyntax? + if keepGoing == nil, let colonColon = self.consume(if: .colonColon) { + unexpectedAfterTrailingPeriod = RawUnexpectedNodesSyntax([colonColon], arena: self.arena) + keepGoing = self.missingToken(.period) + } else { + unexpectedAfterTrailingPeriod = nil + } + + elements.append( + RawImportPathComponentSyntax( + name: name, + trailingPeriod: keepGoing, + unexpectedAfterTrailingPeriod, + arena: self.arena + ) ) - ) - } while keepGoing != nil && self.hasProgressed(&loopProgress) + } while keepGoing != nil && self.hasProgressed(&loopProgress) + } return RawImportPathComponentListSyntax(elements: elements, arena: self.arena) } } +extension Parser { + mutating func parseUsingDeclaration( + attrs: DeclAttributes, + introducerHandle handle: RecoveryConsumptionHandle + ) -> RawUsingDeclSyntax { + let unexpectedAttributes: RawUnexpectedNodesSyntax? = + if !attrs.attributes.isEmpty { + RawUnexpectedNodesSyntax(attrs.attributes.elements, arena: self.arena) + } else { + nil + } + + let unexpectedModifiers: RawUnexpectedNodesSyntax? = + if !attrs.modifiers.isEmpty { + RawUnexpectedNodesSyntax(attrs.modifiers.elements, arena: self.arena) + } else { + nil + } + + let (unexpectedBeforeKeyword, usingKeyword) = self.eat(handle) + + let unexpectedBeforeUsingKeyword = RawUnexpectedNodesSyntax( + combining: unexpectedAttributes, + unexpectedModifiers, + unexpectedBeforeKeyword, + arena: self.arena + ) + + if self.at(.atSign) { + return RawUsingDeclSyntax( + unexpectedBeforeUsingKeyword, + usingKeyword: usingKeyword, + specifier: .attribute(self.parseAttribute()), + arena: self.arena + ) + } + + let modifier = self.expectWithoutRecovery(.identifier) + + return RawUsingDeclSyntax( + unexpectedBeforeUsingKeyword, + usingKeyword: usingKeyword, + specifier: .modifier(modifier), + arena: self.arena + ) + } +} + extension Parser { /// Parse an extension declaration. mutating func parseExtensionDeclaration( @@ -531,6 +656,7 @@ extension Parser { unexpectedBeforeInherited = RawUnexpectedNodesSyntax([classKeyword], arena: self.arena) inherited = RawTypeSyntax( RawIdentifierTypeSyntax( + moduleSelector: nil, name: missingToken(.identifier, text: "AnyObject"), genericArgumentClause: nil, arena: self.arena @@ -829,7 +955,7 @@ extension Parser { } extension Parser { - mutating func parseMemberBlockItem() -> RawMemberBlockItemSyntax? { + mutating func parseMemberBlockItem(until stopCondition: (inout Parser) -> Bool) -> RawMemberBlockItemSyntax? { let startToken = self.currentToken if let syntax = self.loadCurrentSyntaxNodeFromCache(for: .memberBlockItem) { self.registerNodeForIncrementalParse(node: syntax.raw, startToken: startToken) @@ -850,16 +976,41 @@ extension Parser { } let decl: RawDeclSyntax + let attachSemi: Bool if self.at(.poundSourceLocation) { decl = RawDeclSyntax(self.parsePoundSourceLocationDirective()) - } else { + attachSemi = false + } else if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { + decl = RawDeclSyntax( + self.parsePoundIfDirective { parser in + return .decls(parser.parseMemberDeclList(until: { $0.atEndOfIfConfigClauseBody() })) + } + ) + attachSemi = false + } else if self.atStartOfDeclaration(allowInitDecl: true, requiresDecl: true) { decl = self.parseDeclaration(in: .memberDeclList) + attachSemi = true + } else { + // Otherwise, eat the unexpected tokens into an "decl". + decl = RawDeclSyntax( + self.parseUnexpectedCodeDeclaration(allowInitDecl: true, requiresDecl: true, until: stopCondition) + ) + attachSemi = true } - let semi = self.consume(if: .semicolon) + let semi: RawTokenSyntax? var trailingSemis: [RawTokenSyntax] = [] - while let trailingSemi = self.consume(if: .semicolon) { - trailingSemis.append(trailingSemi) + if attachSemi { + if !decl.isEmpty { + semi = self.consume(if: .semicolon) + } else { + semi = nil + } + while let trailingSemi = self.consume(if: .semicolon) { + trailingSemis.append(trailingSemi) + } + } else { + semi = nil } if decl.isEmpty && semi == nil && trailingSemis.isEmpty { @@ -878,16 +1029,20 @@ extension Parser { return result } - mutating func parseMemberDeclList() -> RawMemberBlockItemListSyntax { + mutating func parseMemberDeclList( + until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) || $0.atEndOfIfConfigClauseBody() } + ) -> RawMemberBlockItemListSyntax { var elements = [RawMemberBlockItemSyntax]() do { var loopProgress = LoopProgressCondition() - while !self.at(.endOfFile, .rightBrace) && self.hasProgressed(&loopProgress) { + while !stopCondition(&self), !self.at(.endOfFile), self.hasProgressed(&loopProgress) { let newItemAtStartOfLine = self.atStartOfLine - guard let newElement = self.parseMemberBlockItem() else { + guard let newElement = self.parseMemberBlockItem(until: stopCondition) else { break } - if let lastItem = elements.last, lastItem.semicolon == nil && !newItemAtStartOfLine { + if let lastItem = elements.last, + lastItem.semicolon == nil && !newItemAtStartOfLine && !newElement.decl.is(RawUnexpectedCodeDeclSyntax.self) + { elements[elements.count - 1] = RawMemberBlockItemSyntax( lastItem.unexpectedBeforeDecl, decl: lastItem.decl, @@ -1631,7 +1786,7 @@ extension Parser { // There can only be an implicit getter if no other accessors were // seen before this one. guard let accessorList else { - let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) }) + let body = parseCodeBlockItemList() let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) return RawAccessorBlockSyntax( @@ -2115,6 +2270,14 @@ extension Parser { ) pound = pound.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena) } + + let moduleSelector: RawModuleSelectorSyntax? + if !self.atStartOfLine { + (moduleSelector, _) = self.parseModuleSelectorIfPresent() + } else { + moduleSelector = nil + } + let unexpectedBeforeMacro: RawUnexpectedNodesSyntax? let macro: RawTokenSyntax if !self.atStartOfLine { @@ -2167,6 +2330,7 @@ extension Parser { modifiers: attrs.modifiers, unexpectedBeforePound, pound: pound, + moduleSelector: moduleSelector, unexpectedBeforeMacro, macroName: macro, genericArgumentClause: generics, @@ -2182,4 +2346,64 @@ extension Parser { arena: self.arena ) } + + /// Eats tokens until a start of decl, statement, or expression. + /// Returns consumed tokens as a `RawUnexpectedCodeDeclSyntax` declaration. + mutating func parseUnexpectedCodeDeclaration( + allowInitDecl: Bool, + requiresDecl: Bool, + until stopCondition: (inout Parser) -> Bool + ) -> RawUnexpectedCodeDeclSyntax { + var unexpectedTokens = [RawSyntax]() + var loopProgress = LoopProgressCondition() + while !self.at(.endOfFile, .semicolon), !stopCondition(&self), self.hasProgressed(&loopProgress) { + let numTokensToSkip = self.withLookahead { + $0.skipSingle() + return $0.tokensConsumed + } + for _ in 0.. RawDeclSyntax { + if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { + return RawDeclSyntax( + self.parsePoundIfDirective({ + .decls($0.parseMemberDeclList(until: { $0.atEndOfIfConfigClauseBody() })) + }) + ) + } else { + return parseDeclaration(in: .memberDeclList) + } + } } diff --git a/Sources/SwiftParser/Directives.swift b/Sources/SwiftParser/Directives.swift index b33c61e00b2..cd26429f3ca 100644 --- a/Sources/SwiftParser/Directives.swift +++ b/Sources/SwiftParser/Directives.swift @@ -17,6 +17,10 @@ #endif extension Parser { + mutating func atEndOfIfConfigClauseBody() -> Bool { + return self.at(.poundElseif, .poundElse, .poundEndif) || self.atElifTypo() + } + private enum IfConfigContinuationClauseStartKeyword: TokenSpecSet { case poundElseif case poundElse @@ -42,25 +46,10 @@ extension Parser { /// Parse a conditional compilation block. /// - /// This function should be used to parse conditional compilation statements, - /// declarations, and expressions. It is generic over the particular kind of - /// parse that must occur for these elements, and allows a context-specific - /// syntax kind to be emitted to collect the results. For example, declaration - /// parsing parses items and collects the items into a ``MemberDeclListSyntax`` - /// node. - /// /// - Parameters: - /// - parseElement: Parse an element of the conditional compilation block. - /// - addSemicolonIfNeeded: If elements need to be separated by a newline, this - /// allows the insertion of missing semicolons to the - /// previous element. - /// - syntax: A function that aggregates the parsed conditional elements - /// into a syntax collection. - mutating func parsePoundIfDirective( - _ parseElement: (_ parser: inout Parser, _ isFirstElement: Bool) -> Element?, - addSemicolonIfNeeded: - (_ lastElement: Element, _ newItemAtStartOfLine: Bool, _ parser: inout Parser) -> Element? = { _, _, _ in nil }, - syntax: (inout Parser, [Element]) -> RawIfConfigClauseSyntax.Elements? + /// - parseBody: Parse a body of single conditional compilation clause. + mutating func parsePoundIfDirective( + _ parseBody: (_ parser: inout Parser) -> RawIfConfigClauseSyntax.Elements? ) -> RawIfConfigDeclSyntax { if let remainingTokens = remainingTokensIfMaximumNestingLevelReached() { return RawIfConfigDeclSyntax( @@ -84,7 +73,7 @@ extension Parser { poundKeyword: poundIf, condition: condition, unexpectedBetweenConditionAndElements, - elements: syntax(&self, parseIfConfigClauseElements(parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded)), + elements: parseBody(&self), arena: self.arena ) ) @@ -145,10 +134,7 @@ extension Parser { poundKeyword: pound, condition: condition, unexpectedBetweenConditionAndElements, - elements: syntax( - &self, - parseIfConfigClauseElements(parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded) - ), + elements: parseBody(&self), arena: self.arena ) ) @@ -182,38 +168,13 @@ extension Parser { // `#elif` or `#elif(…)` could be macro invocations. return lookahead.at(TokenSpec(.identifier, allowAtStartOfLine: false)) } - - private mutating func parseIfConfigClauseElements( - _ parseElement: (_ parser: inout Parser, _ isFirstElement: Bool) -> Element?, - addSemicolonIfNeeded: (_ lastElement: Element, _ newItemAtStartOfLine: Bool, _ parser: inout Parser) -> Element? - ) -> [Element] { - var elements = [Element]() - var elementsProgress = LoopProgressCondition() - while !self.at(.endOfFile) - && !self.at(.poundElse, .poundElseif, .poundEndif) - && !self.atElifTypo() - && self.hasProgressed(&elementsProgress) - { - let newItemAtStartOfLine = self.atStartOfLine - guard let element = parseElement(&self, elements.isEmpty), !element.isEmpty else { - break - } - if let lastElement = elements.last, - let fixedUpLastItem = addSemicolonIfNeeded(lastElement, newItemAtStartOfLine, &self) - { - elements[elements.count - 1] = fixedUpLastItem - } - elements.append(element) - } - return elements - } } extension Parser { /// Parse a line control directive. mutating func parsePoundSourceLocationDirective() -> RawPoundSourceLocationSyntax { let line = self.consumeAnyToken() - let (unexpectedBeforeLParen, lparen) = self.expect(.leftParen) + let (unexpectedBeforeLParen, lparen) = self.expect(TokenSpec(.leftParen, allowAtStartOfLine: false)) let arguments: RawPoundSourceLocationArgumentsSyntax? if !self.at(.rightParen) { let (unexpectedBeforeFile, file) = self.expect(.keyword(.file)) diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 70e2a433f56..5a1f4da04cc 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -18,6 +18,12 @@ extension TokenConsumer { mutating func atStartOfExpression() -> Bool { + if self.isAtModuleSelector() { + var lookahead = self.lookahead() + _ = lookahead.consumeModuleSelectorTokensIfPresent() + return lookahead.atStartOfExpression() + } + switch self.at(anyIn: ExpressionStart.self) { case (.awaitTryMove, let handle)?: var lookahead = self.lookahead() @@ -45,15 +51,13 @@ extension TokenConsumer { case nil: break } - if self.at(.atSign) || self.at(.keyword(.inout)) { - var lookahead = self.lookahead() - if lookahead.canParseType() { - return true - } + if self.at(.atSign) && self.peek(isAt: .stringQuote) { + // Invalid Objective-C-style string literal + return true } // 'repeat' is the start of a pack expansion expression. - if (self.at(.keyword(.repeat))) { + if self.at(.keyword(.repeat)) { // FIXME: 'repeat' followed by '{' could still be a pack // expansion, but we need to do more lookahead to figure out // whether the '{' is the start of a closure expression or a @@ -383,11 +387,14 @@ extension Parser { /// Make sure that we only accept `nonisolated(nonsending)` as a valid type specifier /// in expression context to minimize source compatibility impact. - func canParseNonisolatedAsSpecifierInExpressionContext() -> Bool { + mutating func canParseNonisolatedAsSpecifierInExpressionContext() -> Bool { + // If the token isn't even here, bail out before creating a lookahead. + guard self.at(.keyword(.nonisolated)) else { + return false + } + return withLookahead { - guard $0.consume(if: .keyword(.nonisolated)) != nil else { - return false - } + $0.consumeAnyToken() if $0.currentToken.isAtStartOfLine { return false @@ -411,22 +418,38 @@ extension Parser { pattern: PatternContext = .none ) -> RawExprSyntax { // Try to parse '@' sign, 'inout', or 'nonisolated' as an attributed typerepr. + if let type = parseAttributedTypeExprIfPresent() { + return RawExprSyntax(type) + } + + if let prefixExpr = parseSequenceExpressionElementPrefixIfPresent(flavor: flavor, pattern: pattern) { + return prefixExpr + } + + return self.parseUnaryExpression(flavor: flavor, pattern: pattern) + } + + private mutating func parseAttributedTypeExprIfPresent() -> RawTypeExprSyntax? { if self.at(.atSign, .keyword(.inout)) || self.canParseNonisolatedAsSpecifierInExpressionContext() { var lookahead = self.lookahead() if lookahead.canParseType() { let type = self.parseType() - return RawExprSyntax( - RawTypeExprSyntax( - type: type, - arena: self.arena - ) + return RawTypeExprSyntax( + type: type, + arena: self.arena ) } } + return nil + } - EXPR_PREFIX: switch self.at(anyIn: ExpressionModifierKeyword.self) { + private mutating func parseSequenceExpressionElementPrefixIfPresent( + flavor: ExprFlavor, + pattern: PatternContext + ) -> RawExprSyntax? { + switch self.at(anyIn: ExpressionModifierKeyword.self) { case (.await, let handle)?: let awaitTok = self.eat(handle) let sub = self.parseSequenceExpressionElement( @@ -440,6 +463,7 @@ extension Parser { arena: self.arena ) ) + case (.try, let handle)?: let tryKeyword = self.eat(handle) let mark = self.consume(if: .exclamationMark, .postfixQuestionMark) @@ -458,7 +482,7 @@ extension Parser { ) case (.unsafe, let handle)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor, acceptClosure: true, preferPostfixExpr: false) { - break EXPR_PREFIX + break } let unsafeTok = self.eat(handle) @@ -479,7 +503,7 @@ extension Parser { fallthrough case (.borrow, let handle)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor) { - break EXPR_PREFIX + break } let borrowTok = self.eat(handle) let sub = self.parseSequenceExpressionElement( @@ -496,7 +520,7 @@ extension Parser { case (.copy, let handle)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor) { - break EXPR_PREFIX + break } let copyTok = self.eat(handle) @@ -517,7 +541,7 @@ extension Parser { fallthrough case (.consume, let handle)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor) { - break EXPR_PREFIX + break } let consumeKeyword = self.eat(handle) @@ -539,7 +563,7 @@ extension Parser { case (.each, let handle)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor) { - break EXPR_PREFIX + break } let each = self.eat(handle) @@ -554,7 +578,7 @@ extension Parser { case (.any, _)?: if !atContextualKeywordPrefixedSyntax(exprFlavor: flavor) && !self.peek().isContextualPunctuator("~") { - break EXPR_PREFIX + break } // 'any' is parsed as a part of 'type'. @@ -564,7 +588,7 @@ extension Parser { case nil: break } - return self.parseUnaryExpression(flavor: flavor, pattern: pattern) + return nil } /// Parse an optional prefix operator followed by an expression. @@ -652,6 +676,7 @@ extension Parser { if skipMemberName { let missingIdentifier = missingToken(.identifier) let declName = RawDeclReferenceExprSyntax( + moduleSelector: nil, baseName: missingIdentifier, argumentNames: nil, arena: self.arena @@ -661,9 +686,10 @@ extension Parser { // Parse the name portion. let declName: RawDeclReferenceExprSyntax - if let indexOrSelf = self.consume(if: .integerLiteral, .keyword(.self)) { + if !self.isAtModuleSelector(), let indexOrSelf = self.consume(if: .integerLiteral, .keyword(.self)) { // Handle "x.42" - a tuple index. declName = RawDeclReferenceExprSyntax( + moduleSelector: nil, baseName: indexOrSelf, argumentNames: nil, arena: self.arena @@ -714,10 +740,7 @@ extension Parser { ) -> RawExprSyntax { precondition(self.at(.poundIf)) - let config = self.parsePoundIfDirective { (parser, isFirstElement) -> RawExprSyntax? in - if !isFirstElement { - return nil - } + let config = self.parsePoundIfDirective { parser in let head: RawExprSyntax if parser.at(.period) { head = parser.parseDottedExpressionSuffix(nil) @@ -732,15 +755,7 @@ extension Parser { flavor: flavor, pattern: .none ) - - // TODO: diagnose and skip the remaining token in the current clause. - return result - } syntax: { (parser, elements) -> RawIfConfigClauseSyntax.Elements? in - switch elements.count { - case 0: return nil - case 1: return .postfixExpression(elements.first!) - default: fatalError("Postfix #if should only have one element") - } + return .postfixExpression(result) } return RawExprSyntax( @@ -773,99 +788,32 @@ extension Parser { } // If there is an expr-call-suffix, parse it and form a call. - if let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) { - let args = self.parseArgumentListElements( - pattern: pattern, - flavor: flavor.callArgumentFlavor, - allowTrailingComma: true - ) - let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen) - - // If we can parse trailing closures, do so. - let trailingClosure: RawClosureExprSyntax? - let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax - if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) - { - (trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor) - } else { - trailingClosure = nil - additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self) - } - - leadingExpr = RawExprSyntax( - RawFunctionCallExprSyntax( - calledExpression: leadingExpr, - leftParen: lparen, - arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena), - unexpectedBeforeRParen, - rightParen: rparen, - trailingClosure: trailingClosure, - additionalTrailingClosures: additionalTrailingClosures, - arena: self.arena - ) - ) + if let call = self.parsePostfixExpressionCallSuffixIfPresent( + leadingExpr: leadingExpr, + flavor: flavor, + pattern: pattern + ) { + leadingExpr = call continue } // Check for a [expr] suffix. // Note that this cannot be the start of a new line. - if let lsquare = self.consume(if: TokenSpec(.leftSquare, allowAtStartOfLine: false)) { - let args: [RawLabeledExprSyntax] - if self.at(.rightSquare) { - args = [] - } else { - args = self.parseArgumentListElements( - pattern: pattern, - allowTrailingComma: true - ) - } - let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare) - - // If we can parse trailing closures, do so. - let trailingClosure: RawClosureExprSyntax? - let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax - if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) - { - (trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor) - } else { - trailingClosure = nil - additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self) - } - - leadingExpr = RawExprSyntax( - RawSubscriptCallExprSyntax( - calledExpression: leadingExpr, - leftSquare: lsquare, - arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena), - unexpectedBeforeRSquare, - rightSquare: rsquare, - trailingClosure: trailingClosure, - additionalTrailingClosures: additionalTrailingClosures, - arena: self.arena - ) - ) + if let sub = self.parsePostfixExpressionSubscriptSuffixIfPresent( + leadingExpr: leadingExpr, + flavor: flavor, + pattern: pattern + ) { + leadingExpr = sub continue } // Check for a trailing closure, if allowed. - if self.at(.leftBrace) && !leadingExpr.raw.kind.isLiteral - && self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) - { - // Add dummy blank argument list to the call expression syntax. - let list = RawLabeledExprListSyntax(elements: [], arena: self.arena) - let (first, rest) = self.parseTrailingClosures(flavor: flavor) - - leadingExpr = RawExprSyntax( - RawFunctionCallExprSyntax( - calledExpression: leadingExpr, - leftParen: nil, - arguments: list, - rightParen: nil, - trailingClosure: first, - additionalTrailingClosures: rest, - arena: self.arena - ) - ) + if let implicitCall = self.parsePostfixExpressionTrailingClosureSuffixIfPresent( + leadingExpr: leadingExpr, + flavor: flavor + ) { + leadingExpr = implicitCall // We only allow a single trailing closure on a call. This could be // generalized in the future, but needs further design. @@ -875,77 +823,209 @@ extension Parser { continue } - // Check for a ? suffix. - if let question = self.consume(if: .postfixQuestionMark) { - leadingExpr = RawExprSyntax( - RawOptionalChainingExprSyntax( - expression: leadingExpr, - questionMark: question, - arena: self.arena - ) - ) + // Check for a ?, !, or operator suffix. + if let postfixOp = self.parsePostfixExpressionOperatorSuffixIfPresent(leadingExpr: leadingExpr) { + leadingExpr = postfixOp continue } - // Check for a ! suffix. - if let exlaim = self.consume(if: .exclamationMark) { - leadingExpr = RawExprSyntax( - RawForceUnwrapExprSyntax( - expression: leadingExpr, - exclamationMark: exlaim, - arena: self.arena - ) - ) + if self.at(.poundIf) { + guard let ifConfigExpr = self.parsePostfixExpressionIfConfigSuffix(leadingExpr: leadingExpr, flavor: flavor) + else { + break + } + leadingExpr = ifConfigExpr continue } - // Check for a postfix-operator suffix. - if let op = self.consume(if: .postfixOperator) { - leadingExpr = RawExprSyntax( - RawPostfixOperatorExprSyntax( - expression: leadingExpr, - operator: op, - arena: self.arena - ) + // Otherwise, we don't know what this token is, it must end the expression. + break + } + return leadingExpr + } + + private mutating func parsePostfixExpressionCallSuffixIfPresent( + leadingExpr: RawExprSyntax, + flavor: ExprFlavor, + pattern: PatternContext + ) -> RawExprSyntax? { + guard let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) else { + return nil + } + + let args = self.parseArgumentListElements( + pattern: pattern, + flavor: flavor.callArgumentFlavor, + allowTrailingComma: true + ) + let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen) + + // If we can parse trailing closures, do so. + let trailingClosure: RawClosureExprSyntax? + let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax + if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) { + (trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor) + } else { + trailingClosure = nil + additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self) + } + + return RawExprSyntax( + RawFunctionCallExprSyntax( + calledExpression: leadingExpr, + leftParen: lparen, + arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena), + unexpectedBeforeRParen, + rightParen: rparen, + trailingClosure: trailingClosure, + additionalTrailingClosures: additionalTrailingClosures, + arena: self.arena + ) + ) + } + + private mutating func parsePostfixExpressionSubscriptSuffixIfPresent( + leadingExpr: RawExprSyntax, + flavor: ExprFlavor, + pattern: PatternContext + ) -> RawExprSyntax? { + guard let lsquare = self.consume(if: TokenSpec(.leftSquare, allowAtStartOfLine: false)) else { + return nil + } + + let args: [RawLabeledExprSyntax] + if self.at(.rightSquare) { + args = [] + } else { + args = self.parseArgumentListElements( + pattern: pattern, + allowTrailingComma: true + ) + } + let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare) + + // If we can parse trailing closures, do so. + let trailingClosure: RawClosureExprSyntax? + let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax + if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) { + (trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor) + } else { + trailingClosure = nil + additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self) + } + + return RawExprSyntax( + RawSubscriptCallExprSyntax( + calledExpression: leadingExpr, + leftSquare: lsquare, + arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena), + unexpectedBeforeRSquare, + rightSquare: rsquare, + trailingClosure: trailingClosure, + additionalTrailingClosures: additionalTrailingClosures, + arena: self.arena + ) + ) + } + + private mutating func parsePostfixExpressionTrailingClosureSuffixIfPresent( + leadingExpr: RawExprSyntax, + flavor: ExprFlavor + ) -> RawExprSyntax? { + guard + self.at(.leftBrace) && !leadingExpr.raw.kind.isLiteral + && self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) }) + else { + return nil + } + + // Add dummy blank argument list to the call expression syntax. + let list = RawLabeledExprListSyntax(elements: [], arena: self.arena) + let (first, rest) = self.parseTrailingClosures(flavor: flavor) + + return RawExprSyntax( + RawFunctionCallExprSyntax( + calledExpression: leadingExpr, + leftParen: nil, + arguments: list, + rightParen: nil, + trailingClosure: first, + additionalTrailingClosures: rest, + arena: self.arena + ) + ) + } + + private mutating func parsePostfixExpressionOperatorSuffixIfPresent( + leadingExpr: RawExprSyntax + ) -> RawExprSyntax? { + // Check for a ? suffix. + if let question = self.consume(if: .postfixQuestionMark) { + return RawExprSyntax( + RawOptionalChainingExprSyntax( + expression: leadingExpr, + questionMark: question, + arena: self.arena ) - continue - } + ) + } - if self.at(.poundIf) { - // Check if the first '#if' body starts with '.' , and parse - // it as a "postfix ifconfig expression". - do { - var lookahead = self.lookahead() - // Skip to the first body. We may need to skip multiple '#if' directives - // since we support nested '#if's. e.g. - // baseExpr - // #if CONDITION_1 - // #if CONDITION_2 - // .someMember - var loopProgress = LoopProgressCondition() - repeat { - lookahead.eat(.poundIf) - while !lookahead.at(.endOfFile) && !lookahead.currentToken.isAtStartOfLine { - lookahead.skipSingle() - } - } while lookahead.at(.poundIf) && lookahead.hasProgressed(&loopProgress) + // Check for a ! suffix. + if let exlaim = self.consume(if: .exclamationMark) { + return RawExprSyntax( + RawForceUnwrapExprSyntax( + expression: leadingExpr, + exclamationMark: exlaim, + arena: self.arena + ) + ) + } - guard lookahead.atStartOfPostfixExprSuffix() else { - break - } + // Check for a postfix-operator suffix. + if let op = self.consume(if: .postfixOperator) { + return RawExprSyntax( + RawPostfixOperatorExprSyntax( + expression: leadingExpr, + operator: op, + arena: self.arena + ) + ) + } + + return nil + } + + private mutating func parsePostfixExpressionIfConfigSuffix( + leadingExpr: RawExprSyntax, + flavor: ExprFlavor + ) -> RawExprSyntax? { + // Check if the first '#if' body starts with '.' , and parse + // it as a "postfix ifconfig expression". + do { + var lookahead = self.lookahead() + // Skip to the first body. We may need to skip multiple '#if' directives + // since we support nested '#if's. e.g. + // baseExpr + // #if CONDITION_1 + // #if CONDITION_2 + // .someMember + var loopProgress = LoopProgressCondition() + repeat { + lookahead.eat(.poundIf) + while !lookahead.at(.endOfFile) && !lookahead.currentToken.isAtStartOfLine { + lookahead.skipSingle() } + } while lookahead.at(.poundIf) && lookahead.hasProgressed(&loopProgress) - leadingExpr = self.parseIfConfigExpressionSuffix( - leadingExpr, - flavor: flavor - ) - continue + guard lookahead.atStartOfPostfixExprSuffix() else { + return nil } - - // Otherwise, we don't know what this token is, it must end the expression. - break } - return leadingExpr + + return self.parseIfConfigExpressionSuffix( + leadingExpr, + flavor: flavor + ) } } @@ -1239,6 +1319,13 @@ extension Parser { arena: self.arena ) ) + case (.colonColon, _)?, // Module selector with no module name + (.wildcard, _)? where self.isAtModuleSelector(), // Module selectors with invalid module names + (.Any, _)? where self.isAtModuleSelector(), + (.`self`, _)? where self.isAtModuleSelector(), + (.`Self`, _)? where self.isAtModuleSelector(), + (.super, _)? where self.isAtModuleSelector(): + return self.parseIdentifierExpression(flavor: flavor) case (.identifier, let handle)?, (.self, let handle)?, (.`init`, let handle)?, (.`deinit`, let handle)?, (.`subscript`, let handle)?: // If we have "case let x" followed by ".", "(", "[", or a generic @@ -1246,11 +1333,11 @@ extension Parser { // is the start of an enum or expr pattern. if pattern.admitsBinding && self.lookahead().isInBindingPatternPosition() { let identifier = self.eat(handle) - let pattern = RawIdentifierPatternSyntax( + let patternNode = RawIdentifierPatternSyntax( identifier: identifier, arena: self.arena ) - return RawExprSyntax(RawPatternExprSyntax(pattern: pattern, arena: self.arena)) + return RawExprSyntax(RawPatternExprSyntax(pattern: patternNode, arena: self.arena)) } return self.parseIdentifierExpression(flavor: flavor) @@ -1276,6 +1363,7 @@ extension Parser { let poundAvailable = self.parsePoundAvailableConditionElement() return RawExprSyntax( RawDeclReferenceExprSyntax( + moduleSelector: nil, RawUnexpectedNodesSyntax([poundAvailable], arena: self.arena), baseName: missingToken(.identifier), argumentNames: nil, @@ -1382,6 +1470,14 @@ extension Parser { ) pound = pound.tokenView.withTokenDiagnostic(tokenDiagnostic: diagnostic, arena: self.arena) } + + let moduleSelector: RawModuleSelectorSyntax? + if !self.atStartOfLine { + (moduleSelector, _) = self.parseModuleSelectorIfPresent() + } else { + moduleSelector = nil + } + let unexpectedBeforeMacroName: RawUnexpectedNodesSyntax? let macroName: RawTokenSyntax if !self.atStartOfLine { @@ -1429,6 +1525,7 @@ extension Parser { return RawMacroExpansionExprSyntax( unexpectedBeforePound, pound: pound, + moduleSelector: moduleSelector, unexpectedBeforeMacroName, macroName: macroName, genericArgumentClause: generics, @@ -1547,6 +1644,76 @@ extension Parser { value: RawExprSyntax ) case array(RawExprSyntax) + + fileprivate func makeElement(trailingComma: RawTokenSyntax?, arena: RawSyntaxArena) -> RawSyntax { + switch self { + case .array(let el): + return RawSyntax( + RawArrayElementSyntax( + expression: el, + trailingComma: trailingComma, + arena: arena + ) + ) + case .dictionary(let key, let unexpectedBeforeColon, let colon, let value): + return RawSyntax( + RawDictionaryElementSyntax( + key: key, + unexpectedBeforeColon, + colon: colon, + value: value, + trailingComma: trailingComma, + arena: arena + ) + ) + } + } + + fileprivate func makeCollection( + _ unexpectedBeforeLSquare: RawUnexpectedNodesSyntax?, + lsquare: RawTokenSyntax, + elements: [RawSyntax], + _ unexpectedBeforeRSquare: RawUnexpectedNodesSyntax?, + rsquare: RawTokenSyntax, + arena: RawSyntaxArena + ) -> RawExprSyntax { + switch self { + case .dictionary: + return RawExprSyntax( + RawDictionaryExprSyntax( + unexpectedBeforeLSquare, + leftSquare: lsquare, + content: .elements( + RawDictionaryElementListSyntax( + elements: elements.map { + $0.as(RawDictionaryElementSyntax.self)! + }, + arena: arena + ) + ), + unexpectedBeforeRSquare, + rightSquare: rsquare, + arena: arena + ) + ) + case .array: + return RawExprSyntax( + RawArrayExprSyntax( + unexpectedBeforeLSquare, + leftSquare: lsquare, + elements: RawArrayElementListSyntax( + elements: elements.map { + $0.as(RawArrayElementSyntax.self)! + }, + arena: arena + ), + unexpectedBeforeRSquare, + rightSquare: rsquare, + arena: arena + ) + ) + } + } } /// Parse an element of an array or dictionary literal. @@ -1583,37 +1750,11 @@ extension Parser { let (unexpectedBeforeLSquare, lsquare) = self.expect(.leftSquare) - // Check to see if we have an InlineArray type in expression position. - if self.isAtStartOfInlineArrayTypeBody() { - let type = self.parseInlineArrayType( - unexpectedBeforeLSquare: unexpectedBeforeLSquare, - leftSquare: lsquare - ) - return RawExprSyntax(RawTypeExprSyntax(type: type, arena: self.arena)) - } - - if let rsquare = self.consume(if: .rightSquare) { - return RawExprSyntax( - RawArrayExprSyntax( - unexpectedBeforeLSquare, - leftSquare: lsquare, - elements: RawArrayElementListSyntax(elements: [], arena: self.arena), - rightSquare: rsquare, - arena: self.arena - ) - ) - } - - if let (colon, rsquare) = self.consume(if: .colon, followedBy: .rightSquare) { - return RawExprSyntax( - RawDictionaryExprSyntax( - unexpectedBeforeLSquare, - leftSquare: lsquare, - content: .colon(colon), - rightSquare: rsquare, - arena: self.arena - ) - ) + if let specialLiteral = parseSpecialCollectionLiteral( + unexpectedBeforeLSquare: unexpectedBeforeLSquare, + lsquare: lsquare + ) { + return specialLiteral } var elementKind: CollectionKind? = nil @@ -1621,7 +1762,7 @@ extension Parser { do { var collectionProgress = LoopProgressCondition() var keepGoing: RawTokenSyntax? - COLLECTION_LOOP: repeat { + repeat { elementKind = self.parseCollectionElement(elementKind) /// Whether expression of an array element or the value of a dictionary @@ -1647,71 +1788,64 @@ extension Parser { keepGoing = nil } - switch elementKind! { - case .array(let el): - let element = RawArrayElementSyntax( - expression: el, - trailingComma: keepGoing, - arena: self.arena - ) - if element.isEmpty { - break COLLECTION_LOOP - } else { - elements.append(RawSyntax(element)) - } - case .dictionary(let key, let unexpectedBeforeColon, let colon, let value): - let element = RawDictionaryElementSyntax( - key: key, - unexpectedBeforeColon, - colon: colon, - value: value, - trailingComma: keepGoing, - arena: self.arena - ) - if element.isEmpty { - break COLLECTION_LOOP - } else { - elements.append(RawSyntax(element)) - } + let element = elementKind!.makeElement(trailingComma: keepGoing, arena: self.arena) + if element.isEmpty { + break + } else { + elements.append(RawSyntax(element)) } } while keepGoing != nil && self.hasProgressed(&collectionProgress) } let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare) - switch elementKind! { - case .dictionary: + return elementKind!.makeCollection( + unexpectedBeforeLSquare, + lsquare: lsquare, + elements: elements, + unexpectedBeforeRSquare, + rsquare: rsquare, + arena: self.arena + ) + } + + private mutating func parseSpecialCollectionLiteral( + unexpectedBeforeLSquare: RawUnexpectedNodesSyntax?, + lsquare: RawTokenSyntax + ) -> RawExprSyntax? { + // Check to see if we have an InlineArray type in expression position. + if self.isAtStartOfInlineArrayTypeBody() { + let type = self.parseInlineArrayType( + unexpectedBeforeLSquare: unexpectedBeforeLSquare, + leftSquare: lsquare + ) + return RawExprSyntax(RawTypeExprSyntax(type: type, arena: self.arena)) + } + + if let rsquare = self.consume(if: .rightSquare) { return RawExprSyntax( - RawDictionaryExprSyntax( + RawArrayExprSyntax( + unexpectedBeforeLSquare, leftSquare: lsquare, - content: .elements( - RawDictionaryElementListSyntax( - elements: elements.map { - $0.as(RawDictionaryElementSyntax.self)! - }, - arena: self.arena - ) - ), - unexpectedBeforeRSquare, + elements: RawArrayElementListSyntax(elements: [], arena: self.arena), rightSquare: rsquare, arena: self.arena ) ) - case .array: + } + + if let (colon, rsquare) = self.consume(if: .colon, followedBy: .rightSquare) { return RawExprSyntax( - RawArrayExprSyntax( + RawDictionaryExprSyntax( + unexpectedBeforeLSquare, leftSquare: lsquare, - elements: RawArrayElementListSyntax( - elements: elements.map { - $0.as(RawArrayElementSyntax.self)! - }, - arena: self.arena - ), - unexpectedBeforeRSquare, + content: .colon(colon), rightSquare: rsquare, arena: self.arena ) ) } + + return nil } } @@ -1743,6 +1877,7 @@ extension Parser { mutating func parseAnonymousClosureArgument() -> RawDeclReferenceExprSyntax { let (unexpectedBeforeBaseName, baseName) = self.expect(.dollarIdentifier) return RawDeclReferenceExprSyntax( + moduleSelector: nil, unexpectedBeforeBaseName, baseName: baseName, argumentNames: nil, @@ -1754,13 +1889,24 @@ extension Parser { extension Parser { /// Parse a closure expression. mutating func parseClosureExpression() -> RawClosureExprSyntax { + if let remainingTokens = self.remainingTokensIfMaximumNestingLevelReached() { + return RawClosureExprSyntax( + remainingTokens, + leftBrace: missingToken(.leftBrace), + signature: nil, + statements: RawCodeBlockItemListSyntax(elements: [], arena: self.arena), + rightBrace: missingToken(.rightBrace), + arena: self.arena + ) + } + // Parse the opening left brace. let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) // Parse the closure-signature, if present. let signature = self.parseClosureSignatureIfPresent() // Parse the body. - let elements = parseCodeBlockItemList(until: { $0.at(.rightBrace) }) + let elements = parseCodeBlockItemList() // Parse the closing '}'. let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace) @@ -1964,6 +2110,14 @@ extension Parser { } } +extension TokenConsumer { + mutating func atBinaryOperatorArgument() -> Bool { + var lookahead = self.lookahead() + _ = lookahead.consumeModuleSelectorTokensIfPresent() + return lookahead.at(.binaryOperator) && lookahead.peek(isAt: .comma, .rightParen, .rightSquare) + } +} + extension Parser { /// Parse the elements of an argument list. /// @@ -2015,7 +2169,7 @@ extension Parser { // this case lexes as a binary operator because it neither leads nor // follows a proper subexpression. let expr: RawExprSyntax - if self.at(.binaryOperator) && self.peek(isAt: .comma, .rightParen, .rightSquare) { + if self.atBinaryOperatorArgument() { expr = RawExprSyntax(self.parseDeclReferenceExpr(.operators)) } else { expr = self.parseExpression(flavor: flavor, pattern: pattern) @@ -2310,32 +2464,20 @@ extension Parser { mutating func parseSwitchCases(allowStandaloneStmtRecovery: Bool) -> RawSwitchCaseListSyntax { var elements = [RawSwitchCaseListSyntax.Element]() var elementsProgress = LoopProgressCondition() - while !self.at(.endOfFile, .rightBrace) && !self.at(.poundEndif, .poundElseif, .poundElse) - && self.hasProgressed(&elementsProgress) - { - if self.withLookahead({ $0.atStartOfSwitchCase(allowRecovery: false) }) { + while !self.at(.endOfFile, .rightBrace), !self.atEndOfIfConfigClauseBody(), self.hasProgressed(&elementsProgress) { + if self.withLookahead({ $0.atStartOfSwitchCase() }) { elements.append(.switchCase(self.parseSwitchCase())) } else if self.canRecoverTo(.poundIf) != nil { // '#if' in 'case' position can enclose zero or more 'case' or 'default' // clauses. elements.append( .ifConfigDecl( - self.parsePoundIfDirective( - { (parser, _) in parser.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) }, - syntax: { parser, cases in - guard cases.count == 1, let firstCase = cases.first else { - precondition(cases.isEmpty) - return .switchCases(RawSwitchCaseListSyntax(elements: [], arena: parser.arena)) - } - return .switchCases(firstCase) - } - ) + self.parsePoundIfDirective({ parser in + .switchCases(parser.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery)) + }) ) ) - } else if allowStandaloneStmtRecovery - && (self.atStartOfExpression() || self.atStartOfStatement(preferExpr: false) - || self.atStartOfDeclaration()) - { + } else if allowStandaloneStmtRecovery { // Synthesize a label for the statement or declaration that isn't covered by a case right now. let statements = parseSwitchCaseBody() if statements.isEmpty { @@ -2371,8 +2513,6 @@ extension Parser { ) ) ) - } else if self.withLookahead({ $0.atStartOfSwitchCase(allowRecovery: true) }) { - elements.append(.switchCase(self.parseSwitchCase())) } else { break } @@ -2382,8 +2522,13 @@ extension Parser { mutating func parseSwitchCaseBody() -> RawCodeBlockItemListSyntax { parseCodeBlockItemList(until: { - $0.at(.rightBrace) || $0.at(.poundEndif, .poundElseif, .poundElse) - || $0.withLookahead({ $0.atStartOfConditionalSwitchCases() }) + if $0.at(.rightBrace, .keyword(.case), .keyword(.default)) || $0.atEndOfIfConfigClauseBody() { + return true + } + if $0.withLookahead({ $0.atStartOfConditionalSwitchCases() }) { + return true + } + return false }) } @@ -2395,8 +2540,13 @@ extension Parser { unknownAttr = RawAttributeSyntax( atSign: at, - unexpectedBeforeIdent, - attributeName: RawIdentifierTypeSyntax(name: ident, genericArgumentClause: nil, arena: self.arena), + attributeName: RawIdentifierTypeSyntax( + moduleSelector: nil, + unexpectedBeforeIdent, + name: ident, + genericArgumentClause: nil, + arena: self.arena + ), leftParen: nil, arguments: nil, rightParen: nil, @@ -2550,8 +2700,9 @@ extension Parser.Lookahead { mutating func canParseClosureSignature() -> Bool { // Consume attributes. var lookahead = self.lookahead() + var sawTopLevelArrowInLookahead = false var attributesProgress = LoopProgressCondition() - while let _ = lookahead.consume(if: .atSign), lookahead.hasProgressed(&attributesProgress) { + while lookahead.consume(if: .atSign) != nil, lookahead.hasProgressed(&attributesProgress) { guard lookahead.at(.identifier) else { break } @@ -2613,15 +2764,21 @@ extension Parser.Lookahead { return false } + sawTopLevelArrowInLookahead = true + lookahead.consumeEffectsSpecifiers() } // Parse the 'in' at the end. - guard lookahead.at(.keyword(.in)) else { - return false + if lookahead.at(.keyword(.in)) { + // Okay, we have a closure signature. + return true } - // Okay, we have a closure signature. - return true + + // Even if 'in' is missing, the presence of a top-level '->' makes this look like a + // closure signature. There's no other valid syntax that could legally + // contain '->' at this position. + return sawTopLevelArrowInLookahead } } diff --git a/Sources/SwiftParser/IncrementalParseTransition.swift b/Sources/SwiftParser/IncrementalParseTransition.swift index c2ebb127817..3d5d6debceb 100644 --- a/Sources/SwiftParser/IncrementalParseTransition.swift +++ b/Sources/SwiftParser/IncrementalParseTransition.swift @@ -47,7 +47,7 @@ extension Parser { /// /// This is also used for testing purposes to ensure incremental reparsing /// worked as expected. -public typealias ReusedNodeCallback = (_ node: Syntax) -> () +public typealias ReusedNodeCallback = (_ node: Syntax) -> Void /// Keeps track of a previously parsed syntax tree and the source edits that /// occurred since it was created. @@ -208,7 +208,7 @@ struct IncrementalParseLookup { /// Functions as an iterator that walks the tree looking for nodes with a /// certain position. -fileprivate struct SyntaxCursor { +private struct SyntaxCursor { var node: Syntax var finished: Bool let viewMode = SyntaxTreeViewMode.sourceAccurate diff --git a/Sources/SwiftParser/Lexer/Cursor.swift b/Sources/SwiftParser/Lexer/Cursor.swift index 13d2af8c289..d55f6024068 100644 --- a/Sources/SwiftParser/Lexer/Cursor.swift +++ b/Sources/SwiftParser/Lexer/Cursor.swift @@ -79,7 +79,7 @@ extension Lexer.Cursor { /// The lexer has finished lexing the contents of a string literal and is now /// looking for the closing quote. - case afterStringLiteral(isRawString: Bool) + case afterStringLiteral(kind: StringLiteralKind, isRawString: Bool) /// The lexer has lexed the closing quote of a string literal that had raw /// string delimiters and is now looking for the closing raw string delimiters. @@ -176,7 +176,7 @@ extension Lexer.Cursor { mutating func perform(stateTransition: Lexer.StateTransition, stateAllocator: BumpPtrAllocator) { switch stateTransition { - case .push(newState: let newState): + case .push(let newState): if let topState { if let stateStack = stateStack { let newStateStack = stateAllocator.allocate(State.self, count: stateStack.count + 1) @@ -197,7 +197,7 @@ extension Lexer.Cursor { ), stateAllocator: stateAllocator ) - case .replace(newState: let newState): + case .replace(let newState): topState = newState case .pop: if let stateStack { @@ -255,6 +255,8 @@ extension Lexer { } var position: Position + var experimentalFeatures: Parser.ExperimentalFeatures + /// If we have already lexed a token, the kind of the previously lexed token var previousTokenKind: RawTokenKind? @@ -267,8 +269,9 @@ extension Lexer { private var stateStack: StateStack = StateStack() - init(input: UnsafeBufferPointer, previous: UInt8) { + init(input: UnsafeBufferPointer, previous: UInt8, experimentalFeatures: Parser.ExperimentalFeatures) { self.position = Position(input: input, previous: previous) + self.experimentalFeatures = experimentalFeatures } /// Returns `true` if this cursor is sufficiently different to `other` in a way that indicates that the lexer has @@ -449,17 +452,17 @@ extension Lexer.Cursor { // In this state we lex a single token with the flag set, and then pop the state. result = lexNormal(sourceBufferStart: sourceBufferStart, preferRegexOverBinaryOperator: true) self.stateStack.perform(stateTransition: .pop, stateAllocator: stateAllocator) - case .afterRawStringDelimiter(delimiterLength: let delimiterLength): + case .afterRawStringDelimiter(let delimiterLength): result = lexAfterRawStringDelimiter(delimiterLength: delimiterLength) - case .inStringLiteral(kind: let stringLiteralKind, delimiterLength: let delimiterLength): + case .inStringLiteral(kind: let stringLiteralKind, let delimiterLength): result = lexInStringLiteral(stringLiteralKind: stringLiteralKind, delimiterLength: delimiterLength) - case .afterStringLiteral(isRawString: _): - result = lexAfterStringLiteral() + case .afterStringLiteral(kind: let stringLiteralKind, isRawString: _): + result = lexAfterStringLiteral(stringLiteralKind: stringLiteralKind) case .afterClosingStringQuote: result = lexAfterClosingStringQuote() - case .inStringInterpolationStart(stringLiteralKind: let stringLiteralKind): + case .inStringInterpolationStart(let stringLiteralKind): result = lexInStringInterpolationStart(stringLiteralKind: stringLiteralKind) - case .inStringInterpolation(stringLiteralKind: let stringLiteralKind, parenCount: let parenCount): + case .inStringInterpolation(let stringLiteralKind, let parenCount): result = lexInStringInterpolation( stringLiteralKind: stringLiteralKind, parenCount: parenCount, @@ -903,7 +906,7 @@ extension Lexer.Cursor { case "/": // A following comment counts as whitespace, so this token is not right bound. - if (self.is(offset: 1, at: "/", "*")) { + if self.is(offset: 1, at: "/", "*") { return false } else { return true @@ -940,46 +943,19 @@ extension Lexer.Cursor { case ",": _ = self.advance(); return Lexer.Result(.comma) case ";": _ = self.advance(); return Lexer.Result(.semicolon) - case ":": _ = self.advance(); return Lexer.Result(.colon) case "\\": _ = self.advance(); return Lexer.Result(.backslash) - case "#": - // Try lex shebang. - if self.isAtStartOfFile, self.peek(at: 1) == "!" { - self.advanceToEndOfLine() - return Lexer.Result(.shebang) - } - // Try lex a raw string literal. - if let delimiterLength = self.advanceIfOpeningRawStringDelimiter() { - return Lexer.Result( - .rawStringPoundDelimiter, - stateTransition: .push(newState: .afterRawStringDelimiter(delimiterLength: delimiterLength)) - ) - } - - // Try lex a regex literal. - if let result = self.lexRegexLiteral() { - return result - } - // Otherwise try lex a magic pound literal. - return self.lexMagicPoundLiteral() + case ":": return self.lexNormalColon() + case "#": return self.lexNormalPound() case "!", "?": - if let result = lexPostfixOptionalChain(sourceBufferStart: sourceBufferStart) { - return result - } - return self.lexOperatorIdentifier( + return self.lexNormalQuestionOrExclamation( sourceBufferStart: sourceBufferStart, preferRegexOverBinaryOperator: preferRegexOverBinaryOperator ) case "<": - if self.is(offset: 1, at: "#"), - let result = self.tryLexEditorPlaceholder(sourceBufferStart: sourceBufferStart) - { - return result - } - return self.lexOperatorIdentifier( + return self.lexNormalLeftAngle( sourceBufferStart: sourceBufferStart, preferRegexOverBinaryOperator: preferRegexOverBinaryOperator ) @@ -998,38 +974,108 @@ extension Lexer.Cursor { case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": return self.lexNumber() case #"'"#, #"""#: - return self.lexStringQuote(isOpening: true, leadingDelimiterLength: 0) + return self.lexStringQuote(matchingOpening: nil, leadingDelimiterLength: 0) case "`": return self.lexEscapedIdentifier() case nil: return Lexer.Result(.endOfFile) default: - var tmp = self - if tmp.advance(if: { $0.isValidIdentifierStartCodePoint }) { - return self.lexIdentifier() - } + return lexNormalMiscellaneous( + sourceBufferStart: sourceBufferStart, + preferRegexOverBinaryOperator: preferRegexOverBinaryOperator + ) + } + } - if tmp.advance(if: { $0.isOperatorStartCodePoint }) { - return self.lexOperatorIdentifier( - sourceBufferStart: sourceBufferStart, - preferRegexOverBinaryOperator: preferRegexOverBinaryOperator - ) - } + private mutating func lexNormalColon() -> Lexer.Result { + _ = self.advance() + guard self.peek() == ":" else { + return Lexer.Result(.colon) + } - switch self.lexUnknown() { - case .lexemeContents(let result): - return result - case .trivia: - preconditionFailure("Invalid UTF-8 sequence should be eaten by lexTrivia as LeadingTrivia") - } + _ = self.advance() + return Lexer.Result(.colonColon) + } + + private mutating func lexNormalPound() -> Lexer.Result { + // Try lex shebang. + if self.isAtStartOfFile, self.peek(at: 1) == "!" { + self.advanceToEndOfLine() + return Lexer.Result(.shebang) + } + // Try lex a raw string literal. + if let delimiterLength = self.advanceIfOpeningRawStringDelimiter() { + return Lexer.Result( + .rawStringPoundDelimiter, + stateTransition: .push(newState: .afterRawStringDelimiter(delimiterLength: delimiterLength)) + ) + } + + // Try lex a regex literal. + if let result = self.lexRegexLiteral() { + return result + } + // Otherwise try lex a magic pound literal. + return self.lexMagicPoundLiteral() + } + + private mutating func lexNormalQuestionOrExclamation( + sourceBufferStart: Lexer.Cursor, + preferRegexOverBinaryOperator: Bool + ) -> Lexer.Result { + if let result = lexPostfixOptionalChain(sourceBufferStart: sourceBufferStart) { + return result + } + return self.lexOperatorIdentifier( + sourceBufferStart: sourceBufferStart, + preferRegexOverBinaryOperator: preferRegexOverBinaryOperator + ) + } + + private mutating func lexNormalLeftAngle( + sourceBufferStart: Lexer.Cursor, + preferRegexOverBinaryOperator: Bool + ) -> Lexer.Result { + if self.is(offset: 1, at: "#"), + let result = self.tryLexEditorPlaceholder(sourceBufferStart: sourceBufferStart) + { + return result + } + return self.lexOperatorIdentifier( + sourceBufferStart: sourceBufferStart, + preferRegexOverBinaryOperator: preferRegexOverBinaryOperator + ) + } + + private mutating func lexNormalMiscellaneous( + sourceBufferStart: Lexer.Cursor, + preferRegexOverBinaryOperator: Bool + ) -> Lexer.Result { + var tmp = self + if tmp.advance(if: { $0.isValidIdentifierStartCodePoint }) { + return self.lexIdentifier() + } + + if tmp.advance(if: { $0.isOperatorStartCodePoint }) { + return self.lexOperatorIdentifier( + sourceBufferStart: sourceBufferStart, + preferRegexOverBinaryOperator: preferRegexOverBinaryOperator + ) + } + + switch self.lexUnknown() { + case .lexemeContents(let result): + return result + case .trivia: + preconditionFailure("Invalid UTF-8 sequence should be eaten by lexTrivia as LeadingTrivia") } } private mutating func lexAfterRawStringDelimiter(delimiterLength: Int) -> Lexer.Result { switch self.peek() { case #"'"#, #"""#: - return self.lexStringQuote(isOpening: true, leadingDelimiterLength: delimiterLength) + return self.lexStringQuote(matchingOpening: nil, leadingDelimiterLength: delimiterLength) case nil: return Lexer.Result(.endOfFile) default: @@ -1037,10 +1083,10 @@ extension Lexer.Cursor { } } - private mutating func lexAfterStringLiteral() -> Lexer.Result { + private mutating func lexAfterStringLiteral(stringLiteralKind: StringLiteralKind) -> Lexer.Result { switch self.peek() { case #"'"#, #"""#: - return self.lexStringQuote(isOpening: false, leadingDelimiterLength: 0) + return self.lexStringQuote(matchingOpening: stringLiteralKind, leadingDelimiterLength: 0) case nil: return Lexer.Result(.endOfFile) default: @@ -1796,11 +1842,11 @@ extension Lexer.Cursor { extension Lexer.Cursor { private func stateTransitionAfterLexingStringQuote(kind: StringLiteralKind) -> Lexer.StateTransition { switch currentState { - case .afterStringLiteral(isRawString: true): + case .afterStringLiteral(kind: _, isRawString: true): return .replace(newState: .afterClosingStringQuote) - case .afterStringLiteral(isRawString: false): + case .afterStringLiteral(kind: _, isRawString: false): return .pop - case .afterRawStringDelimiter(delimiterLength: let delimiterLength): + case .afterRawStringDelimiter(let delimiterLength): return .replace(newState: .inStringLiteral(kind: kind, delimiterLength: delimiterLength)) case .normal, .preferRegexOverBinaryOperator, .inStringInterpolation: return .push(newState: .inStringLiteral(kind: kind, delimiterLength: 0)) @@ -1809,9 +1855,12 @@ extension Lexer.Cursor { } } - /// `isOpening` is `true` if this string quote is the opening quote of a string - /// literal and `false` if we are lexing the closing quote of a string literal. - mutating func lexStringQuote(isOpening: Bool, leadingDelimiterLength: Int) -> Lexer.Result { + /// `matchingOpening` is the opening literal kind if this string quote is the + /// closing quote of a string literal, `nil` if it's the opening quote. + mutating func lexStringQuote( + matchingOpening: StringLiteralKind?, + leadingDelimiterLength: Int + ) -> Lexer.Result { if self.advance(matching: "'") { return Lexer.Result(.singleQuote, stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleQuote)) } @@ -1819,52 +1868,57 @@ extension Lexer.Cursor { let firstQuoteConsumed = self.advance(matching: #"""#) precondition(firstQuoteConsumed) + // Check to see if we have a multi-line delimiter. If we're matching an + // opening '"' then we want to bail since e.g `"a"""` shouldn't try to eat + // the '"""' as its closing delimiter. var lookingForMultilineString = self - if lookingForMultilineString.advance(matching: #"""#), lookingForMultilineString.advance(matching: #"""#) { - if leadingDelimiterLength > 0 { - // If this is a string literal, check if we have the closing delimiter on the same line to correctly parse things like `#"""#` as a single line string containing a quote. - var isSingleLineString = lookingForMultilineString - - if isSingleLineString.advanceIfStringDelimiter(delimiterLength: leadingDelimiterLength) { - // If we have the correct number of delimiters now, we have something like `#"""#`. - // This is a single-line string. - return Lexer.Result(.stringQuote, stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine)) - } + if matchingOpening == .singleLine + || !(lookingForMultilineString.advance(matching: #"""#) && lookingForMultilineString.advance(matching: #"""#)) + { + return Lexer.Result(.stringQuote, stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine)) + } - // Scan ahead until the end of the line. Every time we see a closing - // quote, check if it is followed by the correct number of closing delimiters. - while isSingleLineString.is(notAt: "\r", "\n") { - if isSingleLineString.advance(if: { $0 == #"""# }) { - if isSingleLineString.advanceIfStringDelimiter(delimiterLength: leadingDelimiterLength) { - return Lexer.Result( - .stringQuote, - stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine) - ) - } - continue - } - _ = isSingleLineString.advance() - } + if leadingDelimiterLength > 0 { + // If this is a string literal, check if we have the closing delimiter on the same line to correctly parse things like `#"""#` as a single line string containing a quote. + var isSingleLineString = lookingForMultilineString + + if isSingleLineString.advanceIfStringDelimiter(delimiterLength: leadingDelimiterLength) { + // If we have the correct number of delimiters now, we have something like `#"""#`. + // This is a single-line string. + return Lexer.Result(.stringQuote, stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine)) } - self = lookingForMultilineString - let trailingTriviaLexingMode: TriviaLexingMode? - if isOpening && self.is(at: "\n", "\r") { - // The opening quote of a multi-line string literal must be followed by - // a newline that's not part of the represented string. - trailingTriviaLexingMode = .escapedNewlineInMultiLineStringLiteral - } else { - trailingTriviaLexingMode = nil + // Scan ahead until the end of the line. Every time we see a closing + // quote, check if it is followed by the correct number of closing delimiters. + while isSingleLineString.is(notAt: "\r", "\n") { + if isSingleLineString.advance(if: { $0 == #"""# }) { + if isSingleLineString.advanceIfStringDelimiter(delimiterLength: leadingDelimiterLength) { + return Lexer.Result( + .stringQuote, + stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine) + ) + } + continue + } + _ = isSingleLineString.advance() } + } - return Lexer.Result( - .multilineStringQuote, - stateTransition: stateTransitionAfterLexingStringQuote(kind: .multiLine), - trailingTriviaLexingMode: trailingTriviaLexingMode - ) + self = lookingForMultilineString + let trailingTriviaLexingMode: TriviaLexingMode? + if matchingOpening == nil && self.is(at: "\n", "\r") { + // The opening quote of a multi-line string literal must be followed by + // a newline that's not part of the represented string. + trailingTriviaLexingMode = .escapedNewlineInMultiLineStringLiteral } else { - return Lexer.Result(.stringQuote, stateTransition: stateTransitionAfterLexingStringQuote(kind: .singleLine)) + trailingTriviaLexingMode = nil } + + return Lexer.Result( + .multilineStringQuote, + stateTransition: stateTransitionAfterLexingStringQuote(kind: .multiLine), + trailingTriviaLexingMode: trailingTriviaLexingMode + ) } /// Returns `true` if the cursor is positioned at `\##(` with `delimiterLength` @@ -1935,7 +1989,9 @@ extension Lexer.Cursor { return Lexer.Result( .stringSegment, error: error, - stateTransition: .replace(newState: .afterStringLiteral(isRawString: delimiterLength > 0)) + stateTransition: .replace( + newState: .afterStringLiteral(kind: stringLiteralKind, isRawString: delimiterLength > 0) + ) ) default: break @@ -1967,7 +2023,9 @@ extension Lexer.Cursor { return Lexer.Result( .stringSegment, error: error, - stateTransition: .replace(newState: .afterStringLiteral(isRawString: delimiterLength > 0)) + stateTransition: .replace( + newState: .afterStringLiteral(kind: stringLiteralKind, isRawString: delimiterLength > 0) + ) ) } } @@ -2161,14 +2219,14 @@ extension Lexer.Cursor { case ".": return (.period, error: nil) case "?": - if (leftBound) { + if leftBound { return (.postfixQuestionMark, error: nil) } return (.infixQuestionMark, error: nil) default: break } - } else if (operEnd.input.baseAddress! - operStart.input.baseAddress! == 2) { + } else if operEnd.input.baseAddress! - operStart.input.baseAddress! == 2 { switch (operStart.peek(), operStart.peek(at: 1)) { case ("-", ">"): // -> return (.arrow, error: nil) @@ -2460,7 +2518,7 @@ extension Lexer.Cursor { return false } - guard let end = Self.findConflictEnd(start, markerKind: kind) else { + guard let end = Self.findConflictEnd(start, markerKind: kind, experimentalFeatures: experimentalFeatures) else { // No end of conflict marker found. return false } @@ -2476,13 +2534,18 @@ extension Lexer.Cursor { } /// Find the end of a version control conflict marker. - static func findConflictEnd(_ curPtr: Lexer.Cursor, markerKind: ConflictMarker) -> Lexer.Cursor? { + static func findConflictEnd( + _ curPtr: Lexer.Cursor, + markerKind: ConflictMarker, + experimentalFeatures: Parser.ExperimentalFeatures + ) -> Lexer.Cursor? { // Get a reference to the rest of the buffer minus the length of the start // of the conflict marker. let advanced = curPtr.input.baseAddress?.advanced(by: markerKind.introducer.count) var restOfBuffer = Lexer.Cursor( input: .init(start: advanced, count: curPtr.input.count - markerKind.introducer.count), - previous: curPtr.input[markerKind.introducer.count - 1] + previous: curPtr.input[markerKind.introducer.count - 1], + experimentalFeatures: experimentalFeatures ) let terminator = markerKind.terminator let terminatorStart = terminator.first! @@ -2503,7 +2566,8 @@ extension Lexer.Cursor { let advanced = restOfBuffer.input.baseAddress?.advanced(by: terminator.count) return Lexer.Cursor( input: .init(start: advanced, count: restOfBuffer.input.count - terminator.count), - previous: restOfBuffer.input[terminator.count - 1] + previous: restOfBuffer.input[terminator.count - 1], + experimentalFeatures: experimentalFeatures ) } return nil diff --git a/Sources/SwiftParser/Lexer/LexemeSequence.swift b/Sources/SwiftParser/Lexer/LexemeSequence.swift index e64d976f12d..1f9950aae18 100644 --- a/Sources/SwiftParser/Lexer/LexemeSequence.swift +++ b/Sources/SwiftParser/Lexer/LexemeSequence.swift @@ -152,12 +152,21 @@ extension Lexer { public static func tokenize( _ input: UnsafeBufferPointer, from startIndex: Int = 0, - lookaheadTracker: UnsafeMutablePointer + lookaheadTracker: UnsafeMutablePointer, + experimentalFeatures: Parser.ExperimentalFeatures ) -> LexemeSequence { precondition(input.isEmpty || startIndex < input.endIndex) let startChar = startIndex == input.startIndex ? UInt8(ascii: "\0") : input[startIndex - 1] - let start = Cursor(input: input, previous: UInt8(ascii: "\0")) - let cursor = Cursor(input: UnsafeBufferPointer(rebasing: input[startIndex...]), previous: startChar) - return LexemeSequence(sourceBufferStart: start, cursor: cursor, lookaheadTracker: lookaheadTracker) + let start = Cursor(input: input, previous: UInt8(ascii: "\0"), experimentalFeatures: experimentalFeatures) + let cursor = Cursor( + input: UnsafeBufferPointer(rebasing: input[startIndex...]), + previous: startChar, + experimentalFeatures: experimentalFeatures + ) + return LexemeSequence( + sourceBufferStart: start, + cursor: cursor, + lookaheadTracker: lookaheadTracker + ) } } diff --git a/Sources/SwiftParser/Lexer/RegexLiteralLexer.swift b/Sources/SwiftParser/Lexer/RegexLiteralLexer.swift index c5c565405cd..477c2773e68 100644 --- a/Sources/SwiftParser/Lexer/RegexLiteralLexer.swift +++ b/Sources/SwiftParser/Lexer/RegexLiteralLexer.swift @@ -17,7 +17,7 @@ #endif /// A separate lexer specifically for regex literals. -fileprivate struct RegexLiteralLexer { +private struct RegexLiteralLexer { enum LexResult { /// Continue the lex, this is returned from `lexPatternCharacter` when /// it successfully lexed a character. @@ -643,6 +643,10 @@ extension Lexer.Cursor { case .identifier, .dollarIdentifier, .wildcard: return false + // Module selectors are allowed before an operator, but not a regex. + case .colonColon: + return false + // Literals are themselves expressions and therefore don't sequence expressions. case .floatLiteral, .integerLiteral: return false @@ -737,7 +741,7 @@ extension Lexer.Cursor { // an unapplied operator is legal, and we should prefer to lex as that // instead. switch previousTokenKind { - case .leftParen, .leftSquare, .comma, .colon: + case .leftParen, .leftSquare, .comma, .colon, .colonColon: break default: mustBeRegex = true diff --git a/Sources/SwiftParser/Lexer/UnicodeScalarExtensions.swift b/Sources/SwiftParser/Lexer/UnicodeScalarExtensions.swift index acb2eb7d8f2..4ec46c3ddbf 100644 --- a/Sources/SwiftParser/Lexer/UnicodeScalarExtensions.swift +++ b/Sources/SwiftParser/Lexer/UnicodeScalarExtensions.swift @@ -74,7 +74,7 @@ extension Unicode.Scalar { } var isValidIdentifierStartCodePoint: Bool { - if (self.isASCII) { + if self.isASCII { return self.isAsciiIdentifierStart } guard self.isValidIdentifierContinuationCodePoint else { @@ -84,8 +84,8 @@ extension Unicode.Scalar { // N1518: Recommendations for extended identifier characters for C and C++ // Proposed Annex X.2: Ranges of characters disallowed initially let c = self.value - if ((c >= 0x0300 && c <= 0x036F) || (c >= 0x1DC0 && c <= 0x1DFF) || (c >= 0x20D0 && c <= 0x20FF) - || (c >= 0xFE20 && c <= 0xFE2F)) + if (c >= 0x0300 && c <= 0x036F) || (c >= 0x1DC0 && c <= 0x1DFF) || (c >= 0x20D0 && c <= 0x20FF) + || (c >= 0xFE20 && c <= 0xFE2F) { return false } @@ -183,7 +183,7 @@ extension Unicode.Scalar { return nil } - if (curByte < 0x80) { + if curByte < 0x80 { return Unicode.Scalar(curByte) } @@ -216,7 +216,7 @@ extension Unicode.Scalar { } // If the high bit isn't set or the second bit isn't clear, then this is not // a continuation byte! - if (curByte < 0x80 || curByte >= 0xC0) { + if curByte < 0x80 || curByte >= 0xC0 { return nil } diff --git a/Sources/SwiftParser/Lookahead.swift b/Sources/SwiftParser/Lookahead.swift index 9aa7afe8c4b..ff3b1a66b2f 100644 --- a/Sources/SwiftParser/Lookahead.swift +++ b/Sources/SwiftParser/Lookahead.swift @@ -163,19 +163,21 @@ extension Parser.Lookahead { // MARK: Skipping Tokens extension Parser.Lookahead { - mutating func skipTypeAttribute() { - // These are keywords that we accept as attribute names. - guard self.at(.identifier) || self.at(.keyword(.in), .keyword(.inout)) else { - return - } + /// Skip *any* single attribute. I.e. a type attribute, a decl attribute, or + /// a custom attribute. + mutating func consumeAnyAttribute() { + self.eat(.atSign) + + let nameHadSpace = self.currentToken.trailingTriviaByteLength > 0 // Determine which attribute it is. if let (attr, handle) = self.at(anyIn: TypeAttribute.self) { - // Ok, it is a valid attribute, eat it, and then process it. self.eat(handle) switch attr { - case .convention, .isolated: - self.skipSingle() + case .convention, .isolated, .differentiable: + if self.atAttributeOrSpecifierArgument(lastTokenHadSpace: nameHadSpace) { + self.skipSingle() + } default: break } @@ -183,20 +185,9 @@ extension Parser.Lookahead { } if let (_, handle) = self.at(anyIn: Parser.DeclarationAttributeWithSpecialSyntax.self) { - // This is a valid decl attribute so they should have put it on the decl - // instead of the type. - // - // Recover by eating @foo(...) self.eat(handle) - if self.at(.leftParen) { - var lookahead = self.lookahead() - lookahead.skipSingle() - // If we found '->', or 'throws' after paren, it's likely a parameter - // of function type. - guard lookahead.at(.arrow) || lookahead.at(.keyword(.throws), .keyword(.rethrows), .keyword(.throw)) else { - self.skipSingle() - return - } + if self.atAttributeOrSpecifierArgument(lastTokenHadSpace: nameHadSpace) { + self.skipSingle() } return } @@ -212,21 +203,9 @@ extension Parser.Lookahead { return false } - while let _ = self.consume(if: .atSign) { - // Consume qualified names that may or may not involve generic arguments. - repeat { - self.consume(if: .identifier, .keyword(.rethrows)) - // We don't care whether this succeeds or fails to eat generic - // parameters. - _ = self.consumeGenericArguments() - } while self.consume(if: .period) != nil - - if self.consume(if: .leftParen) != nil { - while !self.at(.endOfFile, .rightParen, .poundEndif) { - self.skipSingle() - } - self.consume(if: .rightParen) - } + var attributeProgress = LoopProgressCondition() + while self.at(.atSign), self.hasProgressed(&attributeProgress) { + self.consumeAnyAttribute() } return true } @@ -254,7 +233,9 @@ extension Parser.Lookahead { didSeeAnyAttributes = true _ = self.consumeAttributeList() case .poundIf: - _ = self.consumeIfConfigOfAttributes() + if self.consumeIfConfigOfAttributes() { + didSeeAnyAttributes = true + } default: break ATTRIBUTE_LOOP } @@ -285,7 +266,7 @@ extension Parser.Lookahead { } // If we don't have attributes, then it cannot be an accessor block. - if nextToken.rawTokenKind != .atSign { + guard self.peek(isAt: .atSign) else { return false } @@ -293,6 +274,13 @@ extension Parser.Lookahead { var lookahead = self.lookahead() lookahead.eat(.leftBrace) + // '@_accessorBlock' is a builtin disambiguation marker. + if lookahead.peek(isAt: .identifier), + lookahead.peek().tokenText == "_accessorBlock" + { + return true + } + // Eat attributes, if present. while lookahead.consume(if: .atSign) != nil { guard lookahead.consume(if: .identifier) != nil else { @@ -402,7 +390,7 @@ extension Parser.Lookahead { case nil: self.consumeAnyToken() } - case .skipSinglePost(start: let start): + case .skipSinglePost(let start): switch start { case .leftParen: self.consume(if: .rightParen) diff --git a/Sources/SwiftParser/Names.swift b/Sources/SwiftParser/Names.swift index 3a9efe5878f..3563fd412ee 100644 --- a/Sources/SwiftParser/Names.swift +++ b/Sources/SwiftParser/Names.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #if compiler(>=6) -@_spi(RawSyntax) internal import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) internal import SwiftSyntax #else -@_spi(RawSyntax) import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax #endif extension Parser { @@ -45,6 +45,118 @@ extension Parser { } } +extension TokenConsumer { + /// Do the subsequent tokens have the form of a module selector? Encompasses some invalid syntax which nonetheless + /// can be handled by `consumeModuleSelectorTokensIfPresent()`. + /// + /// - Postcondition: If `true`, either the current token or the next token is `.colonColon`. + mutating func isAtModuleSelector() -> Bool { + // If this is a module selector, the next token should be `::`. + guard self.peek(isAt: .colonColon) else { + // ...however, we will also allow the *current* token to be `::`. `consumeModuleSelectorTokensIfPresent()` will + // create a missing identifier. + return self.at(.colonColon) + } + + // Technically the current token *should* be an identifier, but we also want to diagnose other tokens that might be + // used by accident or given special meanings later ('_', certain keywords). + return self.at(.identifier, .wildcard, .keyword(.Any)) + || self.at(.keyword(.self), .keyword(.Self), .keyword(.super)) + } + + mutating func unlessPeekModuleSelector(_ operation: (inout Self) -> T?) -> T? { + var lookahead = self.lookahead() + lookahead.skipSingle() + if lookahead.isAtModuleSelector() { + return nil + } + return operation(&self) + } + + /// If the subsequent tokens have the form of a module selector, valid or otherwise, consume and return them; + /// otherwise consume nothing and return `nil`. Additionally consumes invalid chained module selectors. + /// + /// Returns a tuple comprised of: + /// + /// - `moduleNameOrUnexpected`: The module name if present; in a valid module selector, this will be a present + /// identifier, but either of those can be untrue in invalid code. + /// - `colonColonToken`: The `::` indicating this module selector. Always `.colonColon`, always present. + /// - `extra`: Tokens for additional trailing module selectors. There is no situation in which two module selectors + /// can be validly chained. + /// - `skipQualifiedName`: True if the next token should be interpreted as a different statement. + @_optimize(size) // Work around SIL optimizer bug (rdar://158171994) + mutating func consumeModuleSelectorTokensIfPresent() -> ( + moduleNameOrUnexpected: Token, colonColonToken: Token, extra: [Token], skipQualifiedName: Bool + )? { + guard self.isAtModuleSelector() else { + return nil + } + + let moduleName: Token + let colonColonToken: Token + + // Did we forget the module name? + if let earlyColonColon = self.consume(if: .colonColon) { + moduleName = self.missingToken(.identifier) + colonColonToken = earlyColonColon + } else { + // Consume whatever comes before the `::`, plus the `::` itself. (Whether or not the "name" is an identifier is + // checked elsewhere.) + moduleName = self.consumeAnyToken() + colonColonToken = self.eat(.colonColon) + } + + var extra: [Token] = [] + while !self.currentToken.isAtStartOfLine && self.isAtModuleSelector() { + if !self.at(.colonColon) { + extra.append(self.consumeAnyToken()) + } + extra.append(self.eat(.colonColon)) + } + + let afterContainsAnyNewline = self.atStartOfLine + + return (moduleName, colonColonToken, extra, afterContainsAnyNewline) + } +} + +extension Parser { + /// Parses one or more module selectors, if present. + mutating func parseModuleSelectorIfPresent() -> (moduleSelector: RawModuleSelectorSyntax?, skipQualifiedName: Bool) { + guard + let (moduleNameOrUnexpected, colonColon, extra, skipQualifiedName) = + consumeModuleSelectorTokensIfPresent() + else { + return (nil, false) + } + + let leadingUnexpected: [RawSyntax] + let moduleName: RawTokenSyntax + let trailingUnexpected: [RawSyntax] + + if moduleNameOrUnexpected.tokenKind == .identifier { + leadingUnexpected = [] + moduleName = moduleNameOrUnexpected + } else { + leadingUnexpected = [RawSyntax(moduleNameOrUnexpected)] + moduleName = RawTokenSyntax(missing: .identifier, arena: arena) + } + + trailingUnexpected = extra.map { RawSyntax($0) } + + return ( + moduleSelector: RawModuleSelectorSyntax( + RawUnexpectedNodesSyntax(leadingUnexpected, arena: arena), + moduleName: moduleName, + colonColon: colonColon, + RawUnexpectedNodesSyntax(trailingUnexpected, arena: arena), + arena: arena + ), + skipQualifiedName: skipQualifiedName + ) + } +} + extension Parser { struct DeclNameOptions: OptionSet { var rawValue: UInt8 @@ -68,33 +180,48 @@ extension Parser { } mutating func parseDeclReferenceExpr(_ flags: DeclNameOptions = []) -> RawDeclReferenceExprSyntax { - // Consume the base name. - let base: RawTokenSyntax - if let identOrSelf = self.consume(if: .identifier, .keyword(.self), .keyword(.Self)) - ?? self.consume(if: .keyword(.`init`)) - { - base = identOrSelf - } else if flags.contains(.operators), let (_, _) = self.at(anyIn: Operator.self) { - base = self.consumeAnyToken(remapping: .binaryOperator) - } else if flags.contains(.keywordsUsingSpecialNames), - let special = self.consume(if: .keyword(.`deinit`), .keyword(.`subscript`)) - { - base = special - } else if flags.contains(.keywords) && self.currentToken.isLexerClassifiedKeyword { - base = self.consumeAnyToken(remapping: .identifier) - } else { - base = missingToken(.identifier) - } + // Consume the module selector, if present, and base name. + let (moduleSelector, base) = self.parseDeclReferenceBase(flags) // Parse an argument list, if the flags allow it and it's present. let args = self.parseArgLabelList(flags) return RawDeclReferenceExprSyntax( + moduleSelector: moduleSelector, baseName: base, argumentNames: args, arena: self.arena ) } + private mutating func parseDeclReferenceBase( + _ flags: DeclNameOptions + ) -> (moduleSelector: RawModuleSelectorSyntax?, base: RawTokenSyntax) { + // Consume a module selector if present. + let (moduleSelector, skipQualifiedName) = self.parseModuleSelectorIfPresent() + + // Consume the base name. + if !skipQualifiedName { + if let identOrInit = self.consume(if: .identifier, .keyword(.`init`)) { + return (moduleSelector, identOrInit) + } + if moduleSelector == nil, let selfOrSelf = self.consume(if: .keyword(.`self`), .keyword(.`Self`)) { + return (moduleSelector, selfOrSelf) + } + if flags.contains(.operators), let (_, _) = self.at(anyIn: Operator.self) { + return (moduleSelector, self.consumeAnyToken(remapping: .binaryOperator)) + } + if flags.contains(.keywordsUsingSpecialNames), + let special = self.consume(if: .keyword(.`deinit`), .keyword(.`subscript`)) + { + return (moduleSelector, special) + } + if (flags.contains(.keywords) || moduleSelector != nil) && self.currentToken.isLexerClassifiedKeyword { + return (moduleSelector, self.consumeAnyToken(remapping: .identifier)) + } + } + return (moduleSelector, missingToken(.identifier)) + } + mutating func parseArgLabelList(_ flags: DeclNameOptions) -> RawDeclNameArgumentsSyntax? { guard flags.contains(.compoundNames) else { return nil @@ -187,47 +314,32 @@ extension Parser { } mutating func parseQualifiedTypeIdentifier() -> RawTypeSyntax { - if self.at(.keyword(.Any)) { - return RawTypeSyntax(self.parseAnyType()) - } - let (unexpectedBeforeName, name) = self.expect(anyIn: IdentifierTypeSyntax.NameOptions.self, default: .identifier) - let generics: RawGenericArgumentClauseSyntax? - if self.at(prefix: "<") { - generics = self.parseGenericArguments() - } else { - generics = nil + let identifierType = self.parseTypeIdentifier() + var result = RawTypeSyntax(identifierType) + + // There are no nested types inside `Any`. + if case TokenSpec.keyword(.Any) = identifierType.name { + return result } - var result = RawTypeSyntax( - RawIdentifierTypeSyntax( - unexpectedBeforeName, - name: name, - genericArgumentClause: generics, - arena: self.arena - ) - ) + func hasAnotherMember() -> Bool { + // If qualified name base type cannot be parsed from the current + // point (i.e. the next type identifier is not followed by a '.'), + // then the next identifier is the final declaration name component. + var lookahead = self.lookahead() + return lookahead.consume(ifPrefix: ".", as: .period) != nil && lookahead.canParseBaseTypeForQualifiedDeclName() + } - // If qualified name base type cannot be parsed from the current - // point (i.e. the next type identifier is not followed by a '.'), - // then the next identifier is the final declaration name component. - var lookahead = self.lookahead() - guard - lookahead.consume(ifPrefix: ".", as: .period) != nil, - lookahead.canParseBaseTypeForQualifiedDeclName() - else { + guard hasAnotherMember() else { return result } var keepGoing = self.consume(if: .period) var loopProgress = LoopProgressCondition() while keepGoing != nil && self.hasProgressed(&loopProgress) { - let (unexpectedBeforeName, name) = self.expect( - .identifier, - .keyword(.self), - TokenSpec(.Self, remapping: .identifier), - default: .identifier - ) + let (memberModuleSelector, skipQualifiedName) = self.parseModuleSelectorIfPresent() + let name = self.parseMemberTypeName(moduleSelector: memberModuleSelector, skipName: skipQualifiedName) let generics: RawGenericArgumentClauseSyntax? if self.at(prefix: "<") { generics = self.parseGenericArguments() @@ -238,21 +350,14 @@ extension Parser { RawMemberTypeSyntax( baseType: result, period: keepGoing!, - unexpectedBeforeName, + moduleSelector: memberModuleSelector, name: name, genericArgumentClause: generics, arena: self.arena ) ) - // If qualified name base type cannot be parsed from the current - // point (i.e. the next type identifier is not followed by a '.'), - // then the next identifier is the final declaration name component. - var lookahead = self.lookahead() - guard - lookahead.consume(ifPrefix: ".", as: .period) != nil, - lookahead.canParseBaseTypeForQualifiedDeclName() - else { + guard !skipQualifiedName && hasAnotherMember() else { break } diff --git a/Sources/SwiftParser/Nominals.swift b/Sources/SwiftParser/Nominals.swift index bbd15cf5618..eebdcd6fa0c 100644 --- a/Sources/SwiftParser/Nominals.swift +++ b/Sources/SwiftParser/Nominals.swift @@ -361,6 +361,11 @@ extension Parser { arena: self.arena ) ) + + // If this was a trailing comma, there are no more elements + if at(prefix: ">") { + break + } } while keepGoing != nil && self.hasProgressed(&loopProgress) } let rangle = self.expectWithoutRecovery(prefix: ">", as: .rightAngle) diff --git a/Sources/SwiftParser/Parameters.swift b/Sources/SwiftParser/Parameters.swift index 1a40901237b..e6ec75d36bb 100644 --- a/Sources/SwiftParser/Parameters.swift +++ b/Sources/SwiftParser/Parameters.swift @@ -131,6 +131,7 @@ extension Parser { // Synthesize the secondName parameter as a type node. type = RawTypeSyntax( RawIdentifierTypeSyntax( + moduleSelector: nil, name: secondName, genericArgumentClause: nil, arena: self.arena diff --git a/Sources/SwiftParser/Parser.swift b/Sources/SwiftParser/Parser.swift index 6d6e8b37cb3..ef268d21da5 100644 --- a/Sources/SwiftParser/Parser.swift +++ b/Sources/SwiftParser/Parser.swift @@ -137,7 +137,7 @@ public struct Parser { #endif /// The Swift version as which source files should be parsed if no Swift version is explicitly specified in the parser. - static let defaultSwiftVersion: SwiftVersion = .v6 + public static let defaultSwiftVersion: SwiftVersion = .v6 var _emptyRawMultipleTrailingClosureElementListSyntax: RawMultipleTrailingClosureElementListSyntax? @@ -240,7 +240,11 @@ public struct Parser { self.experimentalFeatures = experimentalFeatures self.lookaheadTrackerOwner = LookaheadTrackerOwner() - self.lexemes = Lexer.tokenize(input, lookaheadTracker: lookaheadTrackerOwner.lookaheadTracker) + self.lexemes = Lexer.tokenize( + input, + lookaheadTracker: lookaheadTrackerOwner.lookaheadTracker, + experimentalFeatures: experimentalFeatures + ) self.currentToken = self.lexemes.advance() if let parseTransition { self.parseLookup = IncrementalParseLookup(transition: parseTransition) @@ -641,6 +645,25 @@ extension Parser { ) } + /// Attempts to consume a token starting with the given `prefix` and forming it into `tokenKind`. + /// If it cannot be found, the parser tries + /// 1. To eat unexpected tokens that have lower ``TokenPrecedence`` than + /// specified by `TokenSpec(tokenKind)` and see if the token occurs after that unexpected. + /// 2. If the token couldn't be found after skipping unexpected, it synthesizes + /// a missing token of the requested kind. + @inline(__always) + mutating func expect( + prefix: SyntaxText, + as tokenKind: RawTokenKind + ) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) { + let spec = TokenSpec(tokenKind) + return expectImpl( + consume: { $0.consume(ifPrefix: prefix, as: tokenKind) }, + canRecoverTo: { $0.canRecoverTo(spec) }, + makeMissing: { $0.missingToken(spec) } + ) + } + /// If the current token starts with the given prefix, consume the prefis as the given token kind. /// /// Otherwise, synthesize a missing token of the given kind. @@ -699,7 +722,7 @@ extension Parser { self.missingToken(.identifier) ) } else if keywordRecovery, - (self.currentToken.isLexerClassifiedKeyword || self.at(.wildcard)), + self.currentToken.isLexerClassifiedKeyword || self.at(.wildcard), !self.atStartOfLine { let keyword = self.consumeAnyToken() diff --git a/Sources/SwiftParser/Patterns.swift b/Sources/SwiftParser/Patterns.swift index b58d68e51b0..37e5c390ecf 100644 --- a/Sources/SwiftParser/Patterns.swift +++ b/Sources/SwiftParser/Patterns.swift @@ -272,7 +272,10 @@ extension Parser.Lookahead { // than likely need to be made contextual as well before finalizing their // grammar. case ._borrowing, .borrowing: - return peek(isAt: TokenSpec(.identifier, allowAtStartOfLine: false)) + return peek( + isAt: TokenSpec(.identifier, allowAtStartOfLine: false), + TokenSpec(.wildcard, allowAtStartOfLine: false) + ) default: // Other keywords can be parsed unconditionally. return true diff --git a/Sources/SwiftParser/Recovery.swift b/Sources/SwiftParser/Recovery.swift index 955497ca1f3..f42608f09cb 100644 --- a/Sources/SwiftParser/Recovery.swift +++ b/Sources/SwiftParser/Recovery.swift @@ -89,8 +89,79 @@ extension Parser.Lookahead { _ spec3: TokenSpec, recursionDepth: Int = 1 ) -> RecoveryConsumptionHandle? { + #if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION + if shouldRecordAlternativeTokenChoices { + recordAlternativeTokenChoice(for: self.currentToken, choices: [spec1, spec2, spec3]) + } + #endif + + let result = canRecoverToImpl( + recoveryPrecedence: min(spec1.recoveryPrecedence, spec2.recoveryPrecedence, spec3.recoveryPrecedence), + allowAtStartOfLine: spec1.allowAtStartOfLine && spec2.allowAtStartOfLine && spec3.allowAtStartOfLine, + recursionDepth: recursionDepth, + matchesSpec: { lookahead -> (TokenSpec, _)? in + let match: TokenSpec? = + switch lookahead.currentToken { + case spec1: + spec1 + case spec2: + spec2 + case spec3: + spec3 + default: + nil + } + guard let match else { return nil } + return (match, match) + } + ) + return result?.handle + } + + /// Checks if we can reach a token in `subset` by skipping tokens that have + /// a precedence that have a lower ``TokenPrecedence`` than the minimum + /// precedence of a token in that subset. + /// If so, return the token that we can recover to and a handle that can be + /// used to consume the unexpected tokens and the token we recovered to. + mutating func canRecoverTo( + anyIn specSet: SpecSet.Type, + overrideRecoveryPrecedence: TokenPrecedence? = nil + ) -> (match: SpecSet, handle: RecoveryConsumptionHandle)? { + #if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION + if shouldRecordAlternativeTokenChoices { + recordAlternativeTokenChoice(for: self.currentToken, choices: specSet.allCases.map(\.spec)) + } + #endif + + if specSet.allCases.isEmpty { + return nil + } + + let recoveryPrecedence = + overrideRecoveryPrecedence ?? specSet.allCases.map({ + return $0.spec.recoveryPrecedence + }).min()! + + return self.canRecoverToImpl( + recoveryPrecedence: recoveryPrecedence, + allowAtStartOfLine: specSet.allCases.allSatisfy(\.spec.allowAtStartOfLine), + recursionDepth: 1, + matchesSpec: { lookahead in + guard let (specSet, _) = lookahead.at(anyIn: specSet) else { return nil } + return (specSet, specSet.spec) + } + ) + } + + @inline(__always) + private mutating func canRecoverToImpl( + recoveryPrecedence: TokenPrecedence, + allowAtStartOfLine: Bool, + recursionDepth: Int, + matchesSpec: (inout Parser.Lookahead) -> (Match, TokenSpec)? + ) -> (match: Match, handle: RecoveryConsumptionHandle)? { if recursionDepth > 10 { - // `canRecoverTo` calls itself recursively if it finds a nested opening token, eg. when calling `canRecoverTo` on + // `canRecoverToImpl` calls itself recursively if it finds a nested opening token, eg. when calling `canRecoverTo` on // `{{{`. To avoid stack overflowing, limit the number of nested `canRecoverTo` calls we make. Since returning a // recovery handle from this function only improves error recovery but is not necessary for correctness, bailing // from recovery is safe. @@ -98,38 +169,19 @@ extension Parser.Lookahead { // 10 nested open and closing delimiters. return nil } - #if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION - if shouldRecordAlternativeTokenChoices { - recordAlternativeTokenChoice(for: self.currentToken, choices: [spec1, spec2, spec3]) - } - #endif let initialTokensConsumed = self.tokensConsumed - - let recoveryPrecedence = min(spec1.recoveryPrecedence, spec2.recoveryPrecedence, spec3.recoveryPrecedence) - let shouldSkipOverNewlines = - recoveryPrecedence.shouldSkipOverNewlines && spec1.allowAtStartOfLine && spec2.allowAtStartOfLine - && spec3.allowAtStartOfLine + let shouldSkipOverNewlines = recoveryPrecedence.shouldSkipOverNewlines && allowAtStartOfLine while !self.at(.endOfFile) { if !shouldSkipOverNewlines, self.atStartOfLine { break } - let matchedSpec: TokenSpec? - switch self.currentToken { - case spec1: - matchedSpec = spec1 - case spec2: - matchedSpec = spec2 - case spec3: - matchedSpec = spec3 - default: - matchedSpec = nil - } - if let matchedSpec { - return RecoveryConsumptionHandle( + if let (matchedSpec, tokenSpec) = matchesSpec(&self) { + let handle = RecoveryConsumptionHandle( unexpectedTokens: self.tokensConsumed - initialTokensConsumed, - tokenConsumptionHandle: TokenConsumptionHandle(spec: matchedSpec) + tokenConsumptionHandle: TokenConsumptionHandle(spec: tokenSpec) ) + return (matchedSpec, handle) } let currentTokenPrecedence = TokenPrecedence(self.currentToken) if currentTokenPrecedence >= recoveryPrecedence { @@ -167,59 +219,4 @@ extension Parser.Lookahead { return nil } - - /// Checks if we can reach a token in `subset` by skipping tokens that have - /// a precedence that have a lower ``TokenPrecedence`` than the minimum - /// precedence of a token in that subset. - /// If so, return the token that we can recover to and a handle that can be - /// used to consume the unexpected tokens and the token we recovered to. - mutating func canRecoverTo( - anyIn specSet: SpecSet.Type, - overrideRecoveryPrecedence: TokenPrecedence? = nil - ) -> (match: SpecSet, handle: RecoveryConsumptionHandle)? { - #if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION - if shouldRecordAlternativeTokenChoices { - recordAlternativeTokenChoice(for: self.currentToken, choices: specSet.allCases.map(\.spec)) - } - #endif - let initialTokensConsumed = self.tokensConsumed - - if specSet.allCases.isEmpty { - return nil - } - - let recoveryPrecedence = - overrideRecoveryPrecedence ?? specSet.allCases.map({ - return $0.spec.recoveryPrecedence - }).min()! - var loopProgress = LoopProgressCondition() - while !self.at(.endOfFile) && self.hasProgressed(&loopProgress) { - if !recoveryPrecedence.shouldSkipOverNewlines, self.atStartOfLine { - break - } - if let (kind, handle) = self.at(anyIn: specSet) { - return ( - kind, - RecoveryConsumptionHandle( - unexpectedTokens: self.tokensConsumed - initialTokensConsumed, - tokenConsumptionHandle: handle - ) - ) - } - let currentTokenPrecedence = TokenPrecedence(self.currentToken) - if currentTokenPrecedence >= recoveryPrecedence { - break - } - self.consumeAnyToken() - if let closingDelimiter = currentTokenPrecedence.closingTokenKind { - let closingDelimiterSpec = TokenSpec(closingDelimiter) - guard self.canRecoverTo(closingDelimiterSpec) != nil else { - break - } - self.eat(closingDelimiterSpec) - } - } - - return nil - } } diff --git a/Sources/SwiftParser/Statements.swift b/Sources/SwiftParser/Statements.swift index b6834a69af5..53ee258ce81 100644 --- a/Sources/SwiftParser/Statements.swift +++ b/Sources/SwiftParser/Statements.swift @@ -21,31 +21,15 @@ extension TokenConsumer { /// item. /// /// - Parameters: - /// - allowRecovery: Whether to attempt to perform recovery. /// - preferExpr: If either an expression or statement could be /// parsed and this parameter is `true`, the function returns `false` /// such that an expression can be parsed. /// /// - Note: This function must be kept in sync with `parseStatement()`. /// - Seealso: ``Parser/parseStatement()`` - func atStartOfStatement(allowRecovery: Bool = false, preferExpr: Bool) -> Bool { + func atStartOfStatement(preferExpr: Bool) -> Bool { var lookahead = self.lookahead() - if allowRecovery { - // Attributes are not allowed on statements. But for recovery, skip over - // misplaced attributes. - _ = lookahead.consumeAttributeList() - } - return lookahead.atStartOfStatement(allowRecovery: allowRecovery, preferExpr: preferExpr) - } -} - -extension Parser.Lookahead { - mutating func atStartOfSwitchCaseItem() -> Bool { - while self.consume(if: .atSign) != nil { - self.consume(if: .identifier) - } - - return self.at(anyIn: SwitchCaseStart.self) != nil + return lookahead.atStartOfStatement(preferExpr: preferExpr) } } @@ -141,7 +125,9 @@ extension Parser { mutating func parseGuardStatement(guardHandle: RecoveryConsumptionHandle) -> RawGuardStmtSyntax { let (unexpectedBeforeGuardKeyword, guardKeyword) = self.eat(guardHandle) let conditions = self.parseConditionList(isGuardStatement: true) - let (unexpectedBeforeElseKeyword, elseKeyword) = self.expect(.keyword(.else)) + let (unexpectedBeforeElseKeyword, elseKeyword) = self.expect( + TokenSpec(.else, recoveryPrecedence: .openingBrace(closingDelimiter: .rightBrace)) + ) let body = self.parseCodeBlock(introducer: guardKeyword) return RawGuardStmtSyntax( unexpectedBeforeGuardKeyword, @@ -703,6 +689,9 @@ extension Parser { if self.atStartOfStatement(preferExpr: true) || self.atStartOfDeclaration() { return false } + if self.atStartOfLine && self.withLookahead({ $0.atStartOfSwitchCase() }) { + return false + } return true } @@ -969,6 +958,9 @@ extension TokenConsumer { return false case .semicolon, .endOfFile, .poundElse, .poundElseif, .poundEndif: return false + case .colonColon: + // E.g. :: + return false case .leftAngle, .rightAngle: // Lexer never produce these token kinds. @@ -1029,29 +1021,15 @@ extension Parser.Lookahead { /// item. /// /// - Parameters: - /// - allowRecovery: Whether to attempt to perform recovery. /// - preferExpr: If either an expression or statement could be /// parsed and this parameter is `true`, the function returns `false` /// such that an expression can be parsed. /// /// - Note: This function must be kept in sync with `parseStatement()`. /// - Seealso: ``Parser/parseStatement()`` - mutating func atStartOfStatement(allowRecovery: Bool = false, preferExpr: Bool) -> Bool { - if (self.at(anyIn: SwitchCaseStart.self) != nil || self.at(.atSign)) - && withLookahead({ $0.atStartOfSwitchCaseItem() }) - { - // We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression. - return true - } - + mutating func atStartOfStatement(preferExpr: Bool) -> Bool { _ = self.consume(if: .identifier, followedBy: .colon) - let switchSubject: CanBeStatementStart? - if allowRecovery { - switchSubject = self.canRecoverTo(anyIn: CanBeStatementStart.self)?.0 - } else { - switchSubject = self.at(anyIn: CanBeStatementStart.self)?.0 - } - switch switchSubject { + switch self.at(anyIn: CanBeStatementStart.self)?.0 { case .return?, .throw?, .defer?, @@ -1084,28 +1062,25 @@ extension Parser.Lookahead { ) case nil: + // Special recovery 'try return' etc.. + if !preferExpr, + consume(if: .keyword(.try)) != nil, + self.at(anyIn: SingleValueStatementExpression.self) == nil + { + return atStartOfStatement(preferExpr: preferExpr) + } return false } } /// Returns whether the parser's current position is the start of a switch case, /// given that we're in the middle of a switch already. - mutating func atStartOfSwitchCase(allowRecovery: Bool = false) -> Bool { + mutating func atStartOfSwitchCase() -> Bool { // Check for and consume attributes. The only valid attribute is `@unknown` // but that's a semantic restriction. var lookahead = self.lookahead() - var loopProgress = LoopProgressCondition() - var hasAttribute = false - while lookahead.at(.atSign) && lookahead.hasProgressed(&loopProgress) { - guard lookahead.peek().rawTokenKind == .identifier else { - return false - } - - lookahead.eat(.atSign) - lookahead.eat(.identifier) - hasAttribute = true - } + let hasAttribute = lookahead.consumeAttributeList() if hasAttribute && lookahead.at(.rightBrace) { // If we are at an attribute that's the last token in the SwitchCase, parse // that as an attribute to a missing 'case'. That way, if the developer writes @@ -1115,11 +1090,7 @@ extension Parser.Lookahead { return true } - if allowRecovery { - return lookahead.canRecoverTo(anyIn: SwitchCaseStart.self) != nil - } else { - return lookahead.at(anyIn: SwitchCaseStart.self) != nil - } + return lookahead.at(anyIn: SwitchCaseStart.self) != nil } mutating func atStartOfConditionalSwitchCases() -> Bool { diff --git a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift index ad7c9d6e944..d4d30e62b50 100644 --- a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift +++ b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift @@ -90,7 +90,7 @@ extension StringSegmentSyntax { } rawText.withBuffer { buffer in - var cursor = Lexer.Cursor(input: buffer, previous: 0) + var cursor = Lexer.Cursor(input: buffer, previous: 0, experimentalFeatures: []) // Put the cursor in the string literal lexing state. This is just // defensive as it's currently not used by `lexCharacterInStringLiteral`. diff --git a/Sources/SwiftParser/StringLiterals.swift b/Sources/SwiftParser/StringLiterals.swift index b03c8877def..40c6a7875ba 100644 --- a/Sources/SwiftParser/StringLiterals.swift +++ b/Sources/SwiftParser/StringLiterals.swift @@ -18,7 +18,7 @@ // MARK: - Check multiline string literal indentation -fileprivate class StringLiteralExpressionIndentationChecker { +private class StringLiteralExpressionIndentationChecker { // MARK: Entry init(expectedIndentation: SyntaxText, arena: RawSyntaxArena) { @@ -340,22 +340,6 @@ extension Parser { unexpectedBeforeClosingQuote: [RawTokenSyntax], closingQuote: RawTokenSyntax ) { - // ------------------------------------------------------------------------- - // Precondition - - precondition( - allSegments.allSatisfy { - if case .stringSegment(let segment) = $0 { - return segment.unexpectedBeforeContent == nil - && segment.unexpectedAfterContent == nil - && segment.content.leadingTriviaByteLength == 0 - } else { - return true - } - }, - "String segment produced by the lexer should not have unexpected text or trivia because we would drop it during post-processing" - ) - // ------------------------------------------------------------------------- // Variables @@ -395,6 +379,9 @@ extension Parser { // Parse indentation of the closing quote if let lastSegment, + lastSegment.unexpectedBeforeContent == nil, + lastSegment.unexpectedAfterContent == nil, + lastSegment.content.leadingTriviaByteLength == 0, let parsedTrivia = parseIndentationTrivia(text: lastSegment.content.tokenText) { indentationTrivia = parsedTrivia @@ -409,10 +396,9 @@ extension Parser { arena: self.arena ) } else { - if let lastSegment = lastSegment { - indentationTrivia = TriviaParser.parseTrivia(lastSegment.content.tokenText, position: .leading).prefix(while: { - $0.isIndentationWhitespace - }) + if let lastSegment { + indentationTrivia = TriviaParser.parseTrivia(lastSegment.content.tokenText, position: .leading) + .prefix(while: \.isIndentationWhitespace) let indentationByteLength = indentationTrivia.reduce(0, { $0 + $1.byteLength }) indentation = SyntaxText(rebasing: lastSegment.content.tokenText[0.. enum DeclarationModifier: TokenSpecSet { @@ -832,6 +835,7 @@ enum PrimaryExpressionStart: TokenSpecSet { case `Any` case atSign // For recovery case `Self` + case colonColon case `deinit` case dollarIdentifier case `false` @@ -864,6 +868,7 @@ enum PrimaryExpressionStart: TokenSpecSet { case TokenSpec(.Any): self = .Any case TokenSpec(.atSign): self = .atSign case TokenSpec(.Self): self = .Self + case TokenSpec(.colonColon): self = .colonColon case TokenSpec(.deinit): self = .`deinit` case TokenSpec(.dollarIdentifier): self = .dollarIdentifier case TokenSpec(.false): self = .false @@ -899,6 +904,7 @@ enum PrimaryExpressionStart: TokenSpecSet { case .Any: return .keyword(.Any) case .atSign: return .atSign case .Self: return .keyword(.Self) + case .colonColon: return .colonColon case .`deinit`: return .keyword(.`deinit`) case .dollarIdentifier: return .dollarIdentifier case .false: return .keyword(.false) diff --git a/Sources/SwiftParser/TopLevel.swift b/Sources/SwiftParser/TopLevel.swift index cdaa7cf34a0..9ceaeed59d0 100644 --- a/Sources/SwiftParser/TopLevel.swift +++ b/Sources/SwiftParser/TopLevel.swift @@ -67,18 +67,21 @@ extension Parser { extension Parser { mutating func parseCodeBlockItemList( - isAtTopLevel: Bool = false, allowInitDecl: Bool = true, - until stopCondition: (inout Parser) -> Bool + until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) || $0.atEndOfIfConfigClauseBody() } ) -> RawCodeBlockItemListSyntax { var elements = [RawCodeBlockItemSyntax]() var loopProgress = LoopProgressCondition() - while !stopCondition(&self), self.hasProgressed(&loopProgress) { + while !stopCondition(&self), !self.at(.endOfFile), self.hasProgressed(&loopProgress) { let newItemAtStartOfLine = self.atStartOfLine - guard let newElement = self.parseCodeBlockItem(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) else { + guard let newItem = self.parseCodeBlockItem(allowInitDecl: allowInitDecl, until: stopCondition) else { break } - if let lastItem = elements.last, lastItem.semicolon == nil && !newItemAtStartOfLine { + if let lastItem = elements.last, + lastItem.semicolon == nil, + !newItemAtStartOfLine, + !newItem.item.is(RawUnexpectedCodeDeclSyntax.self) + { elements[elements.count - 1] = RawCodeBlockItemSyntax( lastItem.unexpectedBeforeItem, item: .init(lastItem.item)!, @@ -88,14 +91,14 @@ extension Parser { arena: self.arena ) } - elements.append(newElement) + elements.append(newItem) } return .init(elements: elements, arena: self.arena) } /// Parse the top level items in a source file. mutating func parseTopLevelCodeBlockItems() -> RawCodeBlockItemListSyntax { - return parseCodeBlockItemList(isAtTopLevel: true, until: { _ in false }) + return parseCodeBlockItemList(until: { _ in false }) } /// The optional form of `parseCodeBlock` that checks to see if the parser has @@ -117,7 +120,7 @@ extension Parser { /// indented to close this code block or a surrounding context. See `expectRightBrace`. mutating func parseCodeBlock(introducer: RawTokenSyntax? = nil, allowInitDecl: Bool = true) -> RawCodeBlockSyntax { let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) - let itemList = parseCodeBlockItemList(allowInitDecl: allowInitDecl, until: { $0.at(.rightBrace) }) + let itemList = parseCodeBlockItemList(allowInitDecl: allowInitDecl) let (unexpectedBeforeRBrace, rbrace) = self.expectRightBrace(leftBrace: lbrace, introducer: introducer) return .init( @@ -134,7 +137,10 @@ extension Parser { /// /// Returns `nil` if the parser did not consume any tokens while trying to /// parse the code block item. - mutating func parseCodeBlockItem(isAtTopLevel: Bool, allowInitDecl: Bool) -> RawCodeBlockItemSyntax? { + mutating func parseCodeBlockItem( + allowInitDecl: Bool, + until stopCondition: (inout Parser) -> Bool + ) -> RawCodeBlockItemSyntax? { let startToken = self.currentToken if let syntax = self.loadCurrentSyntaxNodeFromCache(for: .codeBlockItem) { self.registerNodeForIncrementalParse(node: syntax.raw, startToken: startToken) @@ -149,7 +155,34 @@ extension Parser { arena: self.arena ) } - if self.at(.keyword(.case), .keyword(.default)) { + + let item: RawCodeBlockItemSyntax.Item + let attachSemi: Bool + if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { + // If config of attributes is parsed as part of declaration parsing as it + // doesn't constitute its own code block item. + let directive = self.parsePoundIfDirective { parser in + let items = parser.parseCodeBlockItemList( + allowInitDecl: allowInitDecl, + until: { $0.atEndOfIfConfigClauseBody() } + ) + return .statements(items) + } + item = .init(decl: directive) + attachSemi = false + } else if self.at(.poundSourceLocation) { + item = .init(decl: self.parsePoundSourceLocationDirective()) + attachSemi = false + } else if self.atStartOfDeclaration(allowInitDecl: allowInitDecl) { + item = .decl(self.parseDeclaration()) + attachSemi = true + } else if self.atStartOfStatement(preferExpr: false) { + item = self.parseStatementItem() + attachSemi = true + } else if self.atStartOfExpression() { + item = .expr(self.parseExpression(flavor: .basic, pattern: .none)) + attachSemi = true + } else if self.withLookahead({ $0.atStartOfSwitchCase() }) { // 'case' and 'default' are invalid in code block items. // Parse them and put them in their own CodeBlockItem but as an unexpected node. let switchCase = self.parseSwitchCase() @@ -159,16 +192,33 @@ extension Parser { semicolon: nil, arena: self.arena ) + } else if (self.at(.atSign) && peek(isAt: .identifier)) || self.at(anyIn: DeclarationModifier.self) != nil { + // Force parsing '@' as a declaration, as there's no valid + // expression or statement starting with an attribute. + item = .decl(self.parseDeclaration()) + attachSemi = true + } else { + // Otherwise, eat the unexpected tokens into an "decl". + item = .decl( + RawDeclSyntax( + self.parseUnexpectedCodeDeclaration(allowInitDecl: allowInitDecl, requiresDecl: false, until: stopCondition) + ) + ) + attachSemi = true } - let item = self.parseItem(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) - let semi = self.consume(if: .semicolon) + let semi: RawTokenSyntax? var trailingSemis: [RawTokenSyntax] = [] - while let trailingSemi = self.consume(if: .semicolon) { - trailingSemis.append(trailingSemi) + if attachSemi { + semi = self.consume(if: .semicolon) + while let trailingSemi = self.consume(if: .semicolon) { + trailingSemis.append(trailingSemi) + } + } else { + semi = nil } - if item.raw.isEmpty && semi == nil && trailingSemis.isEmpty { + if item.isEmpty && semi == nil && trailingSemis.isEmpty { return nil } @@ -180,7 +230,6 @@ extension Parser { ) self.registerNodeForIncrementalParse(node: result.raw, startToken: startToken) - return result } @@ -211,52 +260,4 @@ extension Parser { } return .stmt(stmt) } - - /// `isAtTopLevel` determines whether this is trying to parse an item that's at - /// the top level of the source file. If this is the case, we allow skipping - /// closing braces while trying to recover to the next item. - /// If we are not at the top level, such a closing brace should close the - /// wrapping declaration instead of being consumed by lookahead. - private mutating func parseItem( - isAtTopLevel: Bool = false, - allowInitDecl: Bool = true - ) -> RawCodeBlockItemSyntax.Item { - if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) { - // If config of attributes is parsed as part of declaration parsing as it - // doesn't constitute its own code block item. - let directive = self.parsePoundIfDirective { (parser, _) in - parser.parseCodeBlockItem(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) - } addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, parser in - if lastElement.semicolon == nil && !newItemAtStartOfLine { - return RawCodeBlockItemSyntax( - lastElement.unexpectedBeforeItem, - item: .init(lastElement.item)!, - lastElement.unexpectedBetweenItemAndSemicolon, - semicolon: parser.missingToken(.semicolon), - lastElement.unexpectedAfterSemicolon, - arena: parser.arena - ) - } else { - return nil - } - } syntax: { parser, items in - return .statements(RawCodeBlockItemListSyntax(elements: items, arena: parser.arena)) - } - return .init(decl: directive) - } else if self.at(.poundSourceLocation) { - return .init(decl: self.parsePoundSourceLocationDirective()) - } else if self.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl) { - return .decl(self.parseDeclaration()) - } else if self.atStartOfStatement(preferExpr: false) { - return self.parseStatementItem() - } else if self.atStartOfExpression() { - return .expr(self.parseExpression(flavor: .basic, pattern: .none)) - } else if self.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl, allowRecovery: true) { - return .decl(self.parseDeclaration()) - } else if self.atStartOfStatement(allowRecovery: true, preferExpr: false) { - return self.parseStatementItem() - } else { - return .init(expr: RawMissingExprSyntax(arena: self.arena)) - } - } } diff --git a/Sources/SwiftParser/TriviaParser.swift b/Sources/SwiftParser/TriviaParser.swift index e6fe482c8d8..4ef4d3fe9f5 100644 --- a/Sources/SwiftParser/TriviaParser.swift +++ b/Sources/SwiftParser/TriviaParser.swift @@ -25,7 +25,9 @@ public struct TriviaParser { var pieces: [RawTriviaPiece] = [] var cursor = Lexer.Cursor( input: UnsafeBufferPointer(start: source.baseAddress, count: source.count), - previous: 0 + previous: 0, + // There are currently no experimental features that affect trivia parsing. + experimentalFeatures: [] ) while true { diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 67c7604ce0f..91441f903c5 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -100,6 +100,7 @@ extension Parser { RawAttributedTypeSyntax( specifiers: specifiersAndAttributes.specifiers, attributes: specifiersAndAttributes.attributes, + lateSpecifiers: specifiersAndAttributes.lateSpecifiers, baseType: base, arena: self.arena ) @@ -193,6 +194,26 @@ extension Parser { mutating func parseSimpleType( allowMemberTypes: Bool = true, forAttributeName: Bool = false + ) -> RawTypeSyntax { + let tilde = self.consumeIfContextualPunctuator("~", remapping: .prefixOperator) + + let baseType = self.parseUnsuppressedSimpleType( + allowMemberTypes: allowMemberTypes, + forAttributeName: forAttributeName + ) + + guard let tilde else { + return baseType + } + + return RawTypeSyntax( + RawSuppressedTypeSyntax(withoutTilde: tilde, type: baseType, arena: self.arena) + ) + } + + mutating func parseUnsuppressedSimpleType( + allowMemberTypes: Bool = true, + forAttributeName: Bool = false ) -> RawTypeSyntax { enum TypeBaseStart: TokenSpecSet { case `Self` @@ -226,27 +247,10 @@ extension Parser { } } - // Eat any '~' preceding the type. - let maybeTilde = self.consumeIfContextualPunctuator("~", remapping: .prefixOperator) - - // Wrap as a suppressed type if needed. - func wrapInTilde(_ node: RawTypeSyntax) -> RawTypeSyntax { - if let tilde = maybeTilde { - return RawTypeSyntax( - RawSuppressedTypeSyntax( - withoutTilde: tilde, - type: node, - arena: self.arena - ) - ) - } - return node - } - var base: RawTypeSyntax - switch self.at(anyIn: TypeBaseStart.self)?.spec { + switch self.isAtModuleSelector() ? .identifier : self.at(anyIn: TypeBaseStart.self)?.spec { case .Self, .Any, .identifier: - base = self.parseTypeIdentifier() + base = RawTypeSyntax(self.parseTypeIdentifier()) case .leftParen: base = RawTypeSyntax(self.parseTupleTypeBody()) case .leftSquare: @@ -254,7 +258,7 @@ extension Parser { case .wildcard: base = RawTypeSyntax(self.parsePlaceholderType()) case nil: - return wrapInTilde(RawTypeSyntax(RawMissingTypeSyntax(arena: self.arena))) + return RawTypeSyntax(RawMissingTypeSyntax(arena: self.arena)) } var loopProgress = LoopProgressCondition() @@ -268,13 +272,16 @@ extension Parser { baseType: base, unexpectedPeriod, period: period, + moduleSelector: nil, name: missingIdentifier, genericArgumentClause: nil, arena: self.arena ) ) break - } else if self.at(.keyword(.Type)) || self.at(.keyword(.Protocol)) { + } + + if !self.isAtModuleSelector() && (self.at(.keyword(.Type)) || self.at(.keyword(.Protocol))) { let metatypeSpecifier = self.consume(if: .keyword(.Type)) ?? self.consume(if: .keyword(.Protocol))! base = RawTypeSyntax( RawMetatypeTypeSyntax( @@ -286,14 +293,8 @@ extension Parser { ) ) } else { - let name: RawTokenSyntax - if let handle = self.at(anyIn: MemberTypeSyntax.NameOptions.self)?.handle { - name = self.eat(handle) - } else if self.currentToken.isLexerClassifiedKeyword { - name = self.consumeAnyToken(remapping: .identifier) - } else { - name = missingToken(.identifier) - } + let (memberModuleSelector, skipQualifiedName) = self.parseModuleSelectorIfPresent() + let name = self.parseMemberTypeName(moduleSelector: memberModuleSelector, skipName: skipQualifiedName) let generics: RawGenericArgumentClauseSyntax? if self.at(prefix: "<") { generics = self.parseGenericArguments() @@ -305,11 +306,15 @@ extension Parser { baseType: base, unexpectedPeriod, period: period, + moduleSelector: memberModuleSelector, name: name, genericArgumentClause: generics, arena: self.arena ) ) + if skipQualifiedName { + break + } } continue } @@ -331,11 +336,44 @@ extension Parser { break } - base = wrapInTilde(base) - return base } + /// Parse a type name that has been qualiified by a module selector. This very aggressively interprets keywords as + /// identifiers. + /// + /// - Parameter skipQualifiedName: If `true`, the next token should not be parsed because it includes forbidden whitespace. + mutating func parseTypeNameAfterModuleSelector(skipQualifiedName: Bool) -> RawTokenSyntax { + if !skipQualifiedName { + if let identifier = self.consume(if: .identifier) { + return identifier + } else if self.currentToken.isLexerClassifiedKeyword { + return self.consumeAnyToken(remapping: .identifier) + } + } + return missingToken(.identifier) + } + + /// Parse the name of a member type, which may be a keyword that's + /// interpreted as an identifier (per SE-0071). + /// + /// - Parameter moduleSelector: The module selector that will be attached to this name, if any. + /// - Parameter skipName: If `true`, the next token should not be parsed because it includes forbidden whitespace. + mutating func parseMemberTypeName(moduleSelector: RawModuleSelectorSyntax?, skipName: Bool) -> RawTokenSyntax { + if moduleSelector != nil { + return self.parseTypeNameAfterModuleSelector(skipQualifiedName: skipName) + } + + if !skipName { + if let handle = self.at(anyIn: MemberTypeSyntax.NameOptions.self)?.handle { + return self.eat(handle) + } else if self.currentToken.isLexerClassifiedKeyword { + return self.consumeAnyToken(remapping: .identifier) + } + } + return missingToken(.identifier) + } + /// Parse an optional type. mutating func parseOptionalType(_ base: RawTypeSyntax) -> RawOptionalTypeSyntax { let (unexpectedBeforeMark, mark) = self.expect(.postfixQuestionMark) @@ -361,12 +399,22 @@ extension Parser { } /// Parse a type identifier. - mutating func parseTypeIdentifier() -> RawTypeSyntax { - if self.at(.keyword(.Any)) { - return RawTypeSyntax(self.parseAnyType()) + mutating func parseTypeIdentifier() -> RawIdentifierTypeSyntax { + let (moduleSelector, skipQualifiedName) = self.parseModuleSelectorIfPresent() + + if moduleSelector == nil && self.at(.keyword(.Any)) { + return self.parseAnyType() + } + + let unexpectedBeforeName: RawUnexpectedNodesSyntax? + let name: RawTokenSyntax + if moduleSelector == nil { + (unexpectedBeforeName, name) = self.expect(anyIn: IdentifierTypeSyntax.NameOptions.self, default: .identifier) + } else { + unexpectedBeforeName = nil + name = self.parseTypeNameAfterModuleSelector(skipQualifiedName: skipQualifiedName) } - let (unexpectedBeforeName, name) = self.expect(anyIn: IdentifierTypeSyntax.NameOptions.self, default: .identifier) let generics: RawGenericArgumentClauseSyntax? if self.at(prefix: "<") { generics = self.parseGenericArguments() @@ -374,13 +422,12 @@ extension Parser { generics = nil } - return RawTypeSyntax( - RawIdentifierTypeSyntax( - unexpectedBeforeName, - name: name, - genericArgumentClause: generics, - arena: self.arena - ) + return RawIdentifierTypeSyntax( + moduleSelector: moduleSelector, + unexpectedBeforeName, + name: name, + genericArgumentClause: generics, + arena: self.arena ) } @@ -388,6 +435,7 @@ extension Parser { mutating func parseAnyType() -> RawIdentifierTypeSyntax { let (unexpectedBeforeName, name) = self.expect(.keyword(.Any)) return RawIdentifierTypeSyntax( + moduleSelector: nil, unexpectedBeforeName, name: name, genericArgumentClause: nil, @@ -399,6 +447,7 @@ extension Parser { mutating func parsePlaceholderType() -> RawIdentifierTypeSyntax { let (unexpectedBeforeName, name) = self.expect(.wildcard) return RawIdentifierTypeSyntax( + moduleSelector: nil, unexpectedBeforeName, name: name, genericArgumentClause: nil, @@ -430,6 +479,11 @@ extension Parser { arena: self.arena ) ) + + // If this was a trailing comma, we're done parsing the list + if self.at(prefix: ">") { + break + } } while keepGoing != nil && self.hasProgressed(&loopProgress) } @@ -529,7 +583,12 @@ extension Parser { secondName: nil, RawUnexpectedNodesSyntax(combining: misplacedSpecifiers, unexpectedBeforeColon, arena: self.arena), colon: nil, - type: RawIdentifierTypeSyntax(name: first, genericArgumentClause: nil, arena: self.arena), + type: RawIdentifierTypeSyntax( + moduleSelector: nil, + name: first, + genericArgumentClause: nil, + arena: self.arena + ), ellipsis: nil, trailingComma: self.missingToken(.comma), arena: self.arena @@ -641,14 +700,12 @@ extension Parser { unexpectedBeforeLSquare: RawUnexpectedNodesSyntax?, leftSquare: RawTokenSyntax ) -> RawTypeSyntax { - precondition(self.experimentalFeatures.contains(.inlineArrayTypeSugar)) - // We allow both values and types here and for the element type for - // better recovery in cases where the user writes e.g '[Int x 3]'. + // better recovery in cases where the user writes e.g '[Int of 3]'. let count = self.parseGenericArgumentType() let (unexpectedBeforeSeparator, separator) = self.expect( - TokenSpec(.x, allowAtStartOfLine: false) + TokenSpec(.of, allowAtStartOfLine: false) ) let element = self.parseGenericArgumentType() @@ -733,6 +790,7 @@ extension Parser.Lookahead { case .keyword(.dependsOn): let canParseDependsOn = self.withLookahead({ + let nameHadSpace = $0.currentToken.trailingTriviaByteLength > 0 // Consume 'dependsOn' $0.consumeAnyToken() @@ -741,7 +799,7 @@ extension Parser.Lookahead { } // `dependsOn` requires an argument list. - guard $0.atAttributeOrSpecifierArgument() else { + guard $0.atAttributeOrSpecifierArgument(lastTokenHadSpace: nameHadSpace) else { return false } @@ -760,11 +818,7 @@ extension Parser.Lookahead { } } - var attributeProgress = LoopProgressCondition() - while self.at(.atSign), self.hasProgressed(&attributeProgress) { - self.consumeAnyToken() - self.skipTypeAttribute() - } + _ = self.consumeAttributeList() return true } @@ -870,22 +924,18 @@ extension Parser.Lookahead { /// Checks whether we can parse the start of an InlineArray type. This does /// not include the element type. mutating func canParseStartOfInlineArrayTypeBody() -> Bool { - guard self.experimentalFeatures.contains(.inlineArrayTypeSugar) else { - return false - } - - // We must have at least '[ x', which cannot be any other + // We must have at least '[ of', which cannot be any other // kind of expression or type. We specifically look for both types and // integers for better recovery in e.g cases where the user writes e.g - // '[Int x 2]'. We only do type-scalar since variadics would be ambiguous - // e.g 'Int...x'. + // '[Int of 2]'. We only do type-scalar since variadics would be ambiguous + // e.g 'Int...of'. guard self.canParseTypeScalar() || self.canParseIntegerLiteral() else { return false } // We don't currently allow multi-line since that would require // disambiguation with array literals. - return self.consume(if: TokenSpec(.x, allowAtStartOfLine: false)) != nil + return self.consume(if: TokenSpec(.of, allowAtStartOfLine: false)) != nil } mutating func canParseInlineArrayTypeBody() -> Bool { @@ -893,7 +943,7 @@ extension Parser.Lookahead { return false } // Note we look for both types and integers for better recovery in e.g cases - // where the user writes e.g '[Int x 2]'. + // where the user writes e.g '[Int of 2]'. guard self.canParseGenericArgument() else { return false } @@ -902,7 +952,7 @@ extension Parser.Lookahead { mutating func canParseCollectionTypeBody() -> Bool { // Check to see if we have an InlineArray sugar type. - if self.experimentalFeatures.contains(.inlineArrayTypeSugar) { + do { var lookahead = self.lookahead() if lookahead.canParseInlineArrayTypeBody() { self = lookahead @@ -939,13 +989,13 @@ extension Parser.Lookahead { // by a type annotation. if self.startsParameterName(isClosure: false, allowMisplacedSpecifierRecovery: false) { self.consumeAnyToken() + // If we have a secondary argument label, consume it. if self.atArgumentLabel() { self.consumeAnyToken() - guard self.at(.colon) else { - return false - } } - self.eat(.colon) + guard self.consume(if: .colon) != nil else { + return false + } // Parse a type. guard self.canParseType() else { @@ -975,7 +1025,8 @@ extension Parser.Lookahead { } self.consumeIfContextualPunctuator("...") - } while self.consume(if: .comma) != nil && self.hasProgressed(&loopProgress) + + } while self.consume(if: .comma) != nil && !self.at(.rightParen) && self.hasProgressed(&loopProgress) return self.consume(if: .rightParen) != nil } @@ -988,6 +1039,8 @@ extension Parser.Lookahead { } mutating func canParseTypeIdentifier(allowKeyword: Bool = false) -> Bool { + _ = self.consumeModuleSelectorTokensIfPresent() + if self.at(.keyword(.Any)) { self.consumeAnyToken() return true @@ -1052,7 +1105,8 @@ extension Parser.Lookahead { return false } // Parse the comma, if the list continues. - } while self.consume(if: .comma) != nil && self.hasProgressed(&loopProgress) + // This could be the trailing comma. + } while self.consume(if: .comma) != nil && !self.at(prefix: ">") && self.hasProgressed(&loopProgress) } guard self.consume(ifPrefix: ">", as: .rightAngle) != nil else { @@ -1130,7 +1184,9 @@ extension Parser { // using `nonsisolated` without an argument is allowed in // an inheritance clause. // - The '(nonsending)' was omitted. - if !self.at(.leftParen) { + if !self.withLookahead({ + $0.atAttributeOrSpecifierArgument(lastTokenHadSpace: nonisolatedKeyword.trailingTriviaByteLength > 0) + }) { // `nonisolated P<...>` is allowed in an inheritance clause. if withLookahead({ $0.canParseTypeIdentifier() }) { let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( @@ -1158,16 +1214,18 @@ extension Parser { ) return .nonisolatedTypeSpecifier(nonisolatedSpecifier) - } + } else { + let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) + let (unexpectedBeforeModifier, modifier) = self.expect(.keyword(.nonsending)) + let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) - // Avoid being to greedy about `(` since this modifier should be associated with - // function types, it's possible that the argument is omitted and what follows - // is a function type i.e. `nonisolated () async -> Void`. - if self.at(.leftParen) && !withLookahead({ $0.atAttributeOrSpecifierArgument() }) { let argument = RawNonisolatedSpecifierArgumentSyntax( - leftParen: missingToken(.leftParen), - nonsendingKeyword: missingToken(.keyword(.nonsending)), - rightParen: missingToken(.rightParen), + unexpectedBeforeLeftParen, + leftParen: leftParen, + unexpectedBeforeModifier, + nonsendingKeyword: modifier, + unexpectedBeforeRightParen, + rightParen: rightParen, arena: self.arena ) @@ -1177,31 +1235,8 @@ extension Parser { argument: argument, arena: self.arena ) - return .nonisolatedTypeSpecifier(nonisolatedSpecifier) } - - let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) - let (unexpectedBeforeModifier, modifier) = self.expect(.keyword(.nonsending)) - let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) - - let argument = RawNonisolatedSpecifierArgumentSyntax( - unexpectedBeforeLeftParen, - leftParen: leftParen, - unexpectedBeforeModifier, - nonsendingKeyword: modifier, - unexpectedBeforeRightParen, - rightParen: rightParen, - arena: self.arena - ) - - let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( - unexpectedBeforeNonisolatedKeyword, - nonisolatedKeyword: nonisolatedKeyword, - argument: argument, - arena: self.arena - ) - return .nonisolatedTypeSpecifier(nonisolatedSpecifier) } private mutating func parseSimpleTypeSpecifier( @@ -1215,7 +1250,8 @@ extension Parser { misplacedSpecifiers: [RawTokenSyntax] = [] ) -> ( specifiers: RawTypeSpecifierListSyntax, - attributes: RawAttributeListSyntax + attributes: RawAttributeListSyntax, + lateSpecifiers: RawTypeSpecifierListSyntax )? { var specifiers: [RawTypeSpecifierListSyntax.Element] = [] SPECIFIER_PARSING: while canHaveParameterSpecifier { @@ -1254,7 +1290,15 @@ extension Parser { attributes = nil } - guard !specifiers.isEmpty || attributes != nil else { + // Only handle `nonisolated` as a late specifier. + var lateSpecifiers: [RawTypeSpecifierListSyntax.Element] = [] + if self.at(.keyword(.nonisolated)) && !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) + && canHaveParameterSpecifier + { + lateSpecifiers.append(parseNonisolatedTypeSpecifier()) + } + + guard !specifiers.isEmpty || attributes != nil || !lateSpecifiers.isEmpty else { // No specifiers or attributes on this type return nil } @@ -1265,9 +1309,17 @@ extension Parser { specifierList = RawTypeSpecifierListSyntax(elements: specifiers, arena: arena) } + let lateSpecifierList: RawTypeSpecifierListSyntax + if lateSpecifiers.isEmpty { + lateSpecifierList = self.emptyCollection(RawTypeSpecifierListSyntax.self) + } else { + lateSpecifierList = RawTypeSpecifierListSyntax(elements: lateSpecifiers, arena: arena) + } + return ( specifierList, - attributes ?? self.emptyCollection(RawAttributeListSyntax.self) + attributes ?? self.emptyCollection(RawAttributeListSyntax.self), + lateSpecifierList ) } @@ -1281,7 +1333,11 @@ extension Parser { } mutating func parseTypeAttribute() -> RawAttributeListSyntax.Element { - switch peek(isAtAnyIn: TypeAttribute.self) { + // An attribute qualified by a module selector is *always* a custom attribute, even if it has the same name (or + // module name) as a builtin attribute. + let builtinAttr = self.unlessPeekModuleSelector { $0.peek(isAtAnyIn: TypeAttribute.self) } + + switch builtinAttr { case ._local, ._noMetadata, .async, .escaping, .noDerivative, .noescape, .preconcurrency, .retroactive, .Sendable, .unchecked, .autoclosure: // Known type attribute that doesn't take any arguments @@ -1290,17 +1346,21 @@ extension Parser { return .attribute(self.parseDifferentiableAttribute()) case .isolated: - return parseAttribute(argumentMode: .required) { parser in - return (nil, .argumentList(parser.parseIsolatedAttributeArguments())) - } + return .attribute( + parseAttribute(argumentMode: .required) { parser in + return (nil, .argumentList(parser.parseIsolatedAttributeArguments())) + } + ) case .convention, ._opaqueReturnTypeOf, nil: // Custom attribute - return parseAttribute(argumentMode: .customAttribute) { parser in - let arguments = parser.parseArgumentListElements( - pattern: .none, - allowTrailingComma: true - ) - return (nil, .argumentList(RawLabeledExprListSyntax(elements: arguments, arena: parser.arena))) - } + return .attribute( + parseAttribute(argumentMode: .customAttribute) { parser in + let arguments = parser.parseArgumentListElements( + pattern: .none, + allowTrailingComma: true + ) + return (nil, .argumentList(RawLabeledExprListSyntax(elements: arguments, arena: parser.arena))) + } + ) } } @@ -1308,9 +1368,7 @@ extension Parser { extension Parser { mutating func parseResultType() -> RawTypeSyntax { - if self.currentToken.isEditorPlaceholder { - return self.parseTypeIdentifier() - } else if self.at(prefix: "<") { + if self.at(prefix: "<") && !self.currentToken.isEditorPlaceholder { let generics = self.parseGenericParameters() let baseType = self.parseType() return RawTypeSyntax( @@ -1327,6 +1385,9 @@ extension Parser { return result } + // The rest of this tries to recover from a missing left square bracket like ` -> [Int]]? {`. We can do this for + // result types because we know there isn't an enclosing expression context. + // If the right square bracket is at a new line, we should just return the result if let rightSquare = self.consume(if: TokenSpec(.rightSquare, allowAtStartOfLine: false)) { result = RawTypeSyntax( diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift index 77c5206bb99..938e7d56796 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -15,7 +15,7 @@ extension Parser { @_spi(ExperimentalLanguageFeatures) - public struct ExperimentalFeatures: OptionSet, Sendable { + public struct ExperimentalFeatures: OptionSet, Hashable, Sendable { public let rawValue: UInt public init(rawValue: UInt) { @@ -43,17 +43,17 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of coroutine accessors. public static let coroutineAccessors = Self (rawValue: 1 << 5) - /// Whether to enable the parsing of @abi attribute. - public static let abiAttribute = Self (rawValue: 1 << 6) - /// Whether to enable the parsing of keypaths with method members. - public static let keypathWithMethodMembers = Self (rawValue: 1 << 7) + public static let keypathWithMethodMembers = Self (rawValue: 1 << 6) /// Whether to enable the parsing of `_move` and `_borrow` as ownership operators. - public static let oldOwnershipOperatorSpellings = Self (rawValue: 1 << 8) + public static let oldOwnershipOperatorSpellings = Self (rawValue: 1 << 7) + + /// Whether to enable the parsing of set default actor isolation for a file. + public static let defaultIsolationPerFile = Self (rawValue: 1 << 8) - /// Whether to enable the parsing of sugar type for InlineArray. - public static let inlineArrayTypeSugar = Self (rawValue: 1 << 9) + /// Whether to enable the parsing of borrow and mutate accessors. + public static let borrowAndMutateAccessors = Self (rawValue: 1 << 9) /// Creates a new value representing the experimental feature with the /// given name, or returns nil if the name is not recognized. @@ -71,14 +71,14 @@ extension Parser.ExperimentalFeatures { self = .trailingComma case "CoroutineAccessors": self = .coroutineAccessors - case "ABIAttribute": - self = .abiAttribute case "KeypathWithMethodMembers": self = .keypathWithMethodMembers case "OldOwnershipOperatorSpellings": self = .oldOwnershipOperatorSpellings - case "InlineArrayTypeSugar": - self = .inlineArrayTypeSugar + case "DefaultIsolationPerFile": + self = .defaultIsolationPerFile + case "BorrowAndMutateAccessors": + self = .borrowAndMutateAccessors default: return nil } diff --git a/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift index c880b12f8e5..7dd4f5f89ab 100644 --- a/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift +++ b/Sources/SwiftParser/generated/LayoutNodes+Parsable.swift @@ -135,7 +135,7 @@ extension CodeBlockSyntax: SyntaxParseable { extension DeclSyntax: SyntaxParseable { public static func parse(from parser: inout Parser) -> Self { parse(from: &parser) { - $0.parseDeclaration() + $0.parseDeclarationOrIfConfig() } } } @@ -238,7 +238,9 @@ extension VersionTupleSyntax: SyntaxParseable { fileprivate extension Parser { mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax { - guard let node = self.parseCodeBlockItem(isAtTopLevel: false, allowInitDecl: true) else { + guard let node = self.parseCodeBlockItem(allowInitDecl: true, until: { _ in + false + }) else { // The missing item is not necessary to be a declaration, // which is just a placeholder here return RawCodeBlockItemSyntax( diff --git a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift index bcdb11f0b95..92d9b8d96a4 100644 --- a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift +++ b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift @@ -39,6 +39,9 @@ extension AccessorDeclSyntax { @_spi(ExperimentalLanguageFeatures) case modify case `init` + case borrow + @_spi(ExperimentalLanguageFeatures) + case mutate init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { switch PrepareForKeywordMatch(lexeme) { @@ -72,6 +75,10 @@ extension AccessorDeclSyntax { self = .modify case TokenSpec(.`init`): self = .`init` + case TokenSpec(.borrow): + self = .borrow + case TokenSpec(.mutate) where experimentalFeatures.contains(.borrowAndMutateAccessors): + self = .mutate default: return nil } @@ -109,6 +116,10 @@ extension AccessorDeclSyntax { self = .modify case TokenSpec(.`init`): self = .`init` + case TokenSpec(.borrow): + self = .borrow + case TokenSpec(.mutate): + self = .mutate default: return nil } @@ -146,6 +157,10 @@ extension AccessorDeclSyntax { return .keyword(.modify) case .`init`: return .keyword(.`init`) + case .borrow: + return .keyword(.borrow) + case .mutate: + return .keyword(.mutate) } } @@ -185,6 +200,10 @@ extension AccessorDeclSyntax { return .keyword(.modify) case .`init`: return .keyword(.`init`) + case .borrow: + return .keyword(.borrow) + case .mutate: + return .keyword(.mutate) } } } @@ -2378,6 +2397,58 @@ extension ImportPathComponentSyntax { } } +extension ImportPathComponentSyntax { + @_spi(Diagnostics) + public enum TrailingPeriodOptions: TokenSpecSet { + case period + case colonColon + + init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { + switch PrepareForKeywordMatch(lexeme) { + case TokenSpec(.period): + self = .period + case TokenSpec(.colonColon): + self = .colonColon + default: + return nil + } + } + + public init?(token: TokenSyntax) { + switch token { + case TokenSpec(.period): + self = .period + case TokenSpec(.colonColon): + self = .colonColon + default: + return nil + } + } + + var spec: TokenSpec { + switch self { + case .period: + return .period + case .colonColon: + return .colonColon + } + } + + /// Returns a token that satisfies the `TokenSpec` of this case. + /// + /// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text. + @_spi(Diagnostics) + public var tokenSyntax: TokenSyntax { + switch self { + case .period: + return .periodToken() + case .colonColon: + return .colonColonToken() + } + } + } +} + extension InitializerDeclSyntax { @_spi(Diagnostics) public enum OptionalMarkOptions: TokenSpecSet { diff --git a/Sources/SwiftParser/generated/TokenSpecStaticMembers.swift b/Sources/SwiftParser/generated/TokenSpecStaticMembers.swift index 853e2fbb690..13b547bb2ca 100644 --- a/Sources/SwiftParser/generated/TokenSpecStaticMembers.swift +++ b/Sources/SwiftParser/generated/TokenSpecStaticMembers.swift @@ -44,6 +44,10 @@ extension TokenSpec { return TokenSpec(.colon) } + static var colonColon: TokenSpec { + return TokenSpec(.colonColon) + } + static var comma: TokenSpec { return TokenSpec(.comma) } diff --git a/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift index 73cd7a3c9e7..c39030b1c29 100644 --- a/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift @@ -20,7 +20,7 @@ import SwiftDiagnostics @_spi(RawSyntax) import SwiftSyntax #endif -fileprivate let diagnosticDomain: String = "SwiftLexer" +private let diagnosticDomain: String = "SwiftLexer" /// An error diagnostic whose ID is determined by the diagnostic's type. public protocol TokenError: DiagnosticMessage { diff --git a/Sources/SwiftParserDiagnostics/MissingNodesError.swift b/Sources/SwiftParserDiagnostics/MissingNodesError.swift index 5a410d2b636..82009342a6a 100644 --- a/Sources/SwiftParserDiagnostics/MissingNodesError.swift +++ b/Sources/SwiftParserDiagnostics/MissingNodesError.swift @@ -23,7 +23,7 @@ import SwiftDiagnostics // MARK: - Shared code /// Returns the bottommost node that is an ancestor of all nodes in `nodes`. -fileprivate func findCommonAncestor(_ nodes: [Syntax]) -> Syntax? { +private func findCommonAncestor(_ nodes: [Syntax]) -> Syntax? { return findCommonAncestorOrSelf(nodes.compactMap({ $0.parent })) } @@ -35,7 +35,7 @@ class NoNewlinesFormat: BasicFormat { } } -fileprivate enum NodesDescriptionPart { +private enum NodesDescriptionPart { case tokensWithDefaultText([TokenSyntax]) case tokenWithoutDefaultText(TokenSyntax) case node(Syntax) @@ -232,7 +232,7 @@ fileprivate extension TokenKind { } /// Checks whether a node contains any tokens (missing or present) -fileprivate class HasTokenChecker: SyntaxAnyVisitor { +private class HasTokenChecker: SyntaxAnyVisitor { var hasToken: Bool = false override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift index 20b7c7702fd..8ddf99358b1 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -20,7 +20,7 @@ import SwiftDiagnostics @_spi(ExperimentalLanguageFeatures) import SwiftSyntax #endif -fileprivate func getTokens(between first: TokenSyntax, and second: TokenSyntax) -> [TokenSyntax] { +private func getTokens(between first: TokenSyntax, and second: TokenSyntax) -> [TokenSyntax] { var first = first if first.presence == .missing { let nextPresentToken = first.nextToken(viewMode: .sourceAccurate) @@ -482,21 +482,36 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { otherNode.lastToken(viewMode: .sourceAccurate)?.tokenKind == .poundEndif { let diagnoseOn = parent.parent ?? parent - addDiagnostic( - diagnoseOn, - IfConfigDeclNotAllowedInContext(context: diagnoseOn), - highlights: [Syntax(node), Syntax(otherNode)], - fixIts: [ - FixIt( - message: RemoveNodesFixIt([Syntax(node), Syntax(otherNode)]), - changes: [ - .makeMissing([Syntax(node)], transferTrivia: false), - .makeMissing([Syntax(otherNode)], transferTrivia: false), - ] - ) - ], - handledNodes: [node.id, otherNode.id] - ) + if node == otherNode { + addDiagnostic( + diagnoseOn, + IfConfigDeclNotAllowedInContext(context: diagnoseOn), + highlights: [Syntax(node)], + fixIts: [ + FixIt( + message: RemoveNodesFixIt([Syntax(node)]), + changes: .makeMissing([Syntax(node)], transferTrivia: false) + ) + ], + handledNodes: [node.id] + ) + } else { + addDiagnostic( + diagnoseOn, + IfConfigDeclNotAllowedInContext(context: diagnoseOn), + highlights: [Syntax(node), Syntax(otherNode)], + fixIts: [ + FixIt( + message: RemoveNodesFixIt([Syntax(node), Syntax(otherNode)]), + changes: [ + .makeMissing([Syntax(node)], transferTrivia: false), + .makeMissing([Syntax(otherNode)], transferTrivia: false), + ] + ) + ], + handledNodes: [node.id, otherNode.id] + ) + } } else { addDiagnostic(node, UnexpectedNodesError(unexpectedNodes: node), highlights: [Syntax(node)]) } @@ -1085,53 +1100,65 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { if shouldSkip(node) { return .skipChildren } - if node.baseName.isMissing, let unexpected = node.unexpectedBeforeBaseName { - if unexpected.first?.as(TokenSyntax.self)?.tokenKind == .pound { - addDiagnostic( - unexpected, - UnknownDirectiveError(unexpected: unexpected), - handledNodes: [unexpected.id, node.baseName.id] - ) - } else if let availability = unexpected.first?.as(AvailabilityConditionSyntax.self) { - if let prefixOperatorExpr = node.parent?.as(PrefixOperatorExprSyntax.self), - prefixOperatorExpr.operator.text == "!", - let conditionElement = prefixOperatorExpr.parent?.as(ConditionElementSyntax.self) - { - // Diagnose !#available(...) and !#unavailable(...) - let negatedAvailabilityKeyword = availability.availabilityKeyword.negatedAvailabilityKeyword - let negatedConditionElement = ConditionElementSyntax( - condition: .availability(availability.with(\.availabilityKeyword, negatedAvailabilityKeyword)), - trailingComma: conditionElement.trailingComma - ) + if node.baseName.isMissing { + func considerUnexpectedBeforeBaseName(_ unexpected: UnexpectedNodesSyntax) { + if unexpected.first?.as(TokenSyntax.self)?.tokenKind == .pound { addDiagnostic( unexpected, - NegatedAvailabilityCondition( - availabilityCondition: availability, - negatedAvailabilityKeyword: negatedAvailabilityKeyword - ), - fixIts: [ - FixIt( - message: ReplaceTokensFixIt( - replaceTokens: [prefixOperatorExpr.operator, availability.availabilityKeyword], - replacements: [negatedAvailabilityKeyword] - ), - changes: [ - .replace(oldNode: Syntax(conditionElement), newNode: Syntax(negatedConditionElement)) - ] - ) - ], - handledNodes: [unexpected.id, node.baseName.id] - ) - } else { - addDiagnostic( - unexpected, - AvailabilityConditionInExpression(availabilityCondition: availability), + UnknownDirectiveError(unexpected: unexpected), handledNodes: [unexpected.id, node.baseName.id] ) + } else if let availability = unexpected.first?.as(AvailabilityConditionSyntax.self) { + if let prefixOperatorExpr = node.parent?.as(PrefixOperatorExprSyntax.self), + prefixOperatorExpr.operator.text == "!", + let conditionElement = prefixOperatorExpr.parent?.as(ConditionElementSyntax.self) + { + // Diagnose !#available(...) and !#unavailable(...) + + let negatedAvailabilityKeyword = availability.availabilityKeyword.negatedAvailabilityKeyword + let negatedConditionElement = ConditionElementSyntax( + condition: .availability(availability.with(\.availabilityKeyword, negatedAvailabilityKeyword)), + trailingComma: conditionElement.trailingComma + ) + addDiagnostic( + unexpected, + NegatedAvailabilityCondition( + availabilityCondition: availability, + negatedAvailabilityKeyword: negatedAvailabilityKeyword + ), + fixIts: [ + FixIt( + message: ReplaceTokensFixIt( + replaceTokens: [prefixOperatorExpr.operator, availability.availabilityKeyword], + replacements: [negatedAvailabilityKeyword] + ), + changes: [ + .replace(oldNode: Syntax(conditionElement), newNode: Syntax(negatedConditionElement)) + ] + ) + ], + handledNodes: [unexpected.id, node.baseName.id] + ) + } else { + addDiagnostic( + unexpected, + AvailabilityConditionInExpression(availabilityCondition: availability), + handledNodes: [unexpected.id, node.baseName.id] + ) + } } } + + // We care more about the first token in the unexpected node before the base name, so favor the module selector's + // unexpected nodes. + if node.moduleSelector == nil, let unexpected = node.unexpectedBeforeModuleSelector { + considerUnexpectedBeforeBaseName(unexpected) + } else if let unexpected = node.unexpectedBetweenModuleSelectorAndBaseName { + considerUnexpectedBeforeBaseName(unexpected) + } } + return .visitChildren } @@ -1237,6 +1264,40 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { return .visitChildren } + public override func visit(_ node: ImportPathComponentSyntax) -> SyntaxVisitorContinueKind { + if shouldSkip(node) { + return .skipChildren + } + + if let colonColon = node.unexpectedAfterTrailingPeriod?.first?.as(TokenSyntax.self), + colonColon.tokenKind == .colonColon, + colonColon.isPresent, + let trailingPeriod = node.trailingPeriod, + trailingPeriod.tokenKind == .period, + trailingPeriod.isMissing + { + addDiagnostic( + colonColon, + .submoduleCannotBeImportedUsingModuleSelector, + fixIts: [ + FixIt( + message: ReplaceTokensFixIt(replaceTokens: [colonColon], replacements: [trailingPeriod]), + changes: [ + .makeMissing(colonColon), + .makePresent(trailingPeriod), + ] + ) + ], + handledNodes: [ + colonColon.id, + trailingPeriod.id, + ] + ) + } + + return .visitChildren + } + public override func visit(_ node: InitializerClauseSyntax) -> SyntaxVisitorContinueKind { if shouldSkip(node) { return .skipChildren diff --git a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift index f8d7aac2805..d67c1412de9 100644 --- a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift @@ -20,7 +20,7 @@ import SwiftDiagnostics @_spi(RawSyntax) import SwiftSyntax #endif -fileprivate let diagnosticDomain: String = "SwiftParser" +private let diagnosticDomain: String = "SwiftParser" /// An error diagnostic whose ID is determined by the diagnostic's type. public protocol ParserError: DiagnosticMessage { @@ -233,6 +233,9 @@ extension DiagnosticMessage where Self == StaticParserError { public static var subscriptsCannotHaveNames: Self { .init("subscripts cannot have a name") } + public static var submoduleCannotBeImportedUsingModuleSelector: Self { + .init("submodule cannot be imported using module selector") + } public static var tooManyClosingPoundDelimiters: Self { .init("too many '#' characters in closing delimiter") } diff --git a/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift b/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift index 3dc0aa8e82b..d044e030de5 100644 --- a/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift +++ b/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift @@ -127,6 +127,14 @@ extension SyntaxProtocol { memberDeclItem.firstToken(viewMode: .all) == self.firstToken(viewMode: .all) { return true + } else if let selfToken = self.as(TokenSyntax.self), + selfToken.isMissing, + selfToken.tokenKind.isIdentifier, + let nextToken = self.nextToken(viewMode: .sourceAccurate), + nextToken.isPresent, + nextToken.tokenKind == .colonColon + { + return true } else { return false } diff --git a/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift index 4a00dbace1f..f047dc2cc51 100644 --- a/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift @@ -179,6 +179,8 @@ private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String? { return "condition" case \GuardStmtSyntax.body: return "body" + case \IdentifierTypeSyntax.moduleSelector: + return "module selector" case \IfConfigClauseSyntax.condition: return "condition" case \IfExprSyntax.body: @@ -255,10 +257,16 @@ private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String? { return "name" case \MemberTypeSyntax.baseType: return "base type" + case \MemberTypeSyntax.moduleSelector: + return "module selector" case \MemberTypeSyntax.name: return "name" case \MetatypeTypeSyntax.baseType: return "base type" + case \ModuleSelectorSyntax.moduleName: + return "module name" + case \ModuleSelectorSyntax.colonColon: + return "'::' operator" case \MultipleTrailingClosureElementSyntax.label: return "label" case \ObjCSelectorPieceSyntax.name: diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index 9a9d00393e7..6e3216cdcb8 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -221,7 +221,7 @@ extension SyntaxKind { case .ifExpr: return "'if' statement" case .implementsAttributeArguments: - return "@_implements arguemnts" + return "@_implements arguments" case .implicitlyUnwrappedOptionalType: return "implicitly unwrapped optional type" case .importDecl: @@ -290,6 +290,8 @@ extension SyntaxKind { return "statement" case .missingType: return "type" + case .moduleSelector: + return "module selector" case .multipleTrailingClosureElement: return "trailing closure" case .namedOpaqueReturnType: @@ -362,6 +364,8 @@ extension SyntaxKind { return "availability entry" case .specializeTargetFunctionArgument: return "attribute argument" + case .specializedAttributeArgument: + return "argument to '@specialized" case .stringLiteralExpr: return "string literal" case .structDecl: @@ -377,7 +381,7 @@ extension SyntaxKind { case .switchExpr: return "'switch' statement" case .ternaryExpr: - return "ternay expression" + return "ternary expression" case .thenStmt: return "'then' statement" case .throwStmt: @@ -406,6 +410,8 @@ extension SyntaxKind { return "ternary operator" case .unsafeExpr: return "'unsafe' expression" + case .usingDecl: + return "using" case .valueBindingPattern: return "value binding pattern" case .variableDecl: diff --git a/Sources/SwiftParserDiagnostics/generated/TokenNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/TokenNameForDiagnostics.swift index 4d1a90253da..2f7cfd67902 100644 --- a/Sources/SwiftParserDiagnostics/generated/TokenNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/TokenNameForDiagnostics.swift @@ -34,6 +34,8 @@ extension TokenKind { return "binary operator" case .colon: return ":" + case .colonColon: + return "::" case .comma: return "," case .dollarIdentifier: diff --git a/Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift b/Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift index 9d9afc2646c..b87fff1d09c 100644 --- a/Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift +++ b/Sources/SwiftRefactor/AddSeparatorsToIntegerLiteral.swift @@ -37,11 +37,12 @@ import SwiftSyntax /// 0b1_010 /// ``` public struct AddSeparatorsToIntegerLiteral: SyntaxRefactoringProvider { - public static func refactor(syntax lit: IntegerLiteralExprSyntax, in context: Void) -> IntegerLiteralExprSyntax? { + public static func refactor( + syntax lit: IntegerLiteralExprSyntax, + in context: Void + ) throws -> IntegerLiteralExprSyntax { if lit.literal.text.contains("_") { - guard let strippedLiteral = RemoveSeparatorsFromIntegerLiteral.refactor(syntax: lit) else { - return nil - } + let strippedLiteral = try RemoveSeparatorsFromIntegerLiteral.refactor(syntax: lit) return self.addSeparators(to: strippedLiteral) } else { return self.addSeparators(to: lit) diff --git a/Sources/SwiftRefactor/CMakeLists.txt b/Sources/SwiftRefactor/CMakeLists.txt index 1864c71f2ab..e478a428a79 100644 --- a/Sources/SwiftRefactor/CMakeLists.txt +++ b/Sources/SwiftRefactor/CMakeLists.txt @@ -8,6 +8,7 @@ add_swift_syntax_library(SwiftRefactor AddSeparatorsToIntegerLiteral.swift + CallLikeSyntax.swift CallToTrailingClosures.swift ConvertComputedPropertyToStored.swift ConvertComputedPropertyToZeroParameterFunction.swift @@ -21,6 +22,20 @@ add_swift_syntax_library(SwiftRefactor RefactoringProvider.swift RemoveSeparatorsFromIntegerLiteral.swift SyntaxUtils.swift + + PackageManifest/AddPackageDependency.swift + PackageManifest/AddPackageTarget.swift + PackageManifest/AddPluginUsage.swift + PackageManifest/AddProduct.swift + PackageManifest/AddSwiftSetting.swift + PackageManifest/AddTargetDependency.swift + PackageManifest/ManifestEditError.swift + PackageManifest/ManifestSyntaxRepresentable.swift + PackageManifest/PackageDependency.swift + PackageManifest/PackageTarget.swift + PackageManifest/ProductDescription.swift + PackageManifest/StringUtils.swift + PackageManifest/SyntaxEditUtils.swift ) target_link_swift_syntax_libraries(SwiftRefactor PUBLIC diff --git a/Sources/SwiftRefactor/CallLikeSyntax.swift b/Sources/SwiftRefactor/CallLikeSyntax.swift new file mode 100644 index 00000000000..c620a52fe97 --- /dev/null +++ b/Sources/SwiftRefactor/CallLikeSyntax.swift @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6) +public import SwiftSyntax +#else +import SwiftSyntax +#endif + +// TODO: We ought to consider exposing this as a public syntax protocol. +@_spi(Testing) public protocol CallLikeSyntax: SyntaxProtocol { + var arguments: LabeledExprListSyntax { get set } + var leftParen: TokenSyntax? { get set } + var rightParen: TokenSyntax? { get set } + var trailingClosure: ClosureExprSyntax? { get set } + var additionalTrailingClosures: MultipleTrailingClosureElementListSyntax { get set } +} +@_spi(Testing) extension FunctionCallExprSyntax: CallLikeSyntax {} +@_spi(Testing) extension MacroExpansionExprSyntax: CallLikeSyntax {} +@_spi(Testing) extension MacroExpansionDeclSyntax: CallLikeSyntax {} + +extension SyntaxProtocol { + @_spi(Testing) public func asProtocol(_: CallLikeSyntax.Protocol) -> (any CallLikeSyntax)? { + Syntax(self).asProtocol(SyntaxProtocol.self) as? CallLikeSyntax + } +} diff --git a/Sources/SwiftRefactor/CallToTrailingClosures.swift b/Sources/SwiftRefactor/CallToTrailingClosures.swift index f2781d4b7ed..3641494334e 100644 --- a/Sources/SwiftRefactor/CallToTrailingClosures.swift +++ b/Sources/SwiftRefactor/CallToTrailingClosures.swift @@ -50,22 +50,48 @@ public struct CallToTrailingClosures: SyntaxRefactoringProvider { } } - // TODO: Rather than returning nil, we should consider throwing errors with - // appropriate messages instead. + public typealias Input = Syntax + public typealias Output = Syntax + + /// Apply the refactoring to a given syntax node. If either a + /// non-function-like syntax node is passed, or the refactoring fails, + /// an error is thrown. + public static func refactor( + syntax: Syntax, + in context: Context = Context() + ) throws -> Syntax { + guard let call = syntax.asProtocol(CallLikeSyntax.self) else { + throw RefactoringNotApplicableError("not a call") + } + return try Syntax(fromProtocol: _refactor(syntax: call, in: context)) + } + + @available(*, deprecated, message: "Pass a Syntax argument instead of FunctionCallExprSyntax") public static func refactor( syntax call: FunctionCallExprSyntax, in context: Context = Context() - ) -> FunctionCallExprSyntax? { - let converted = call.convertToTrailingClosures(from: context.startAtArgument) - return converted?.formatted().as(FunctionCallExprSyntax.self) + ) throws -> FunctionCallExprSyntax { + try _refactor(syntax: call, in: context) + } + + internal static func _refactor( + syntax call: C, + in context: Context = Context() + ) throws -> C { + let converted = try call.convertToTrailingClosures(from: context.startAtArgument) + + guard let formatted = converted.formatted().as(C.self) else { + throw RefactoringNotApplicableError("format error") + } + + return formatted } } -extension FunctionCallExprSyntax { - fileprivate func convertToTrailingClosures(from startAtArgument: Int) -> FunctionCallExprSyntax? { +extension CallLikeSyntax { + fileprivate func convertToTrailingClosures(from startAtArgument: Int) throws -> Self { guard trailingClosure == nil, additionalTrailingClosures.isEmpty, leftParen != nil, rightParen != nil else { - // Already have trailing closures - return nil + throw RefactoringNotApplicableError("call already uses trailing closures") } var closures = [(original: LabeledExprSyntax, closure: ClosureExprSyntax)]() @@ -84,7 +110,7 @@ extension FunctionCallExprSyntax { } guard !closures.isEmpty else { - return nil + throw RefactoringNotApplicableError("no arguments to convert to closures") } // First trailing closure won't have label/colon. Transfer their trivia. diff --git a/Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift b/Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift index fd7685317b4..f2c14b7378e 100644 --- a/Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift +++ b/Sources/SwiftRefactor/ConvertComputedPropertyToStored.swift @@ -17,11 +17,15 @@ import SwiftSyntax #endif public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider { - public static func refactor(syntax: VariableDeclSyntax, in context: ()) -> VariableDeclSyntax? { - guard syntax.bindings.count == 1, let binding = syntax.bindings.first, - let accessorBlock = binding.accessorBlock, case let .getter(body) = accessorBlock.accessors, !body.isEmpty + public static func refactor(syntax: VariableDeclSyntax, in context: ()) throws -> VariableDeclSyntax { + guard syntax.bindings.count == 1, let binding = syntax.bindings.first else { + throw RefactoringNotApplicableError("unsupported variable declaration") + } + + guard let accessorBlock = binding.accessorBlock, + case let .getter(body) = accessorBlock.accessors, !body.isEmpty else { - return nil + throw RefactoringNotApplicableError("getter is missing or empty") } let refactored = { (initializer: InitializerClauseSyntax) -> VariableDeclSyntax in @@ -55,7 +59,7 @@ public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider { } guard body.count == 1, let item = body.first?.item else { - return nil + throw RefactoringNotApplicableError("getter body is not a single expression") } if let item = item.as(ReturnStmtSyntax.self), let expression = item.expression { @@ -79,6 +83,6 @@ public struct ConvertComputedPropertyToStored: SyntaxRefactoringProvider { ) } - return nil + throw RefactoringNotApplicableError("could not extract initial value of stored property") } } diff --git a/Sources/SwiftRefactor/ConvertComputedPropertyToZeroParameterFunction.swift b/Sources/SwiftRefactor/ConvertComputedPropertyToZeroParameterFunction.swift index 60aa341ea44..e6224fe7f31 100644 --- a/Sources/SwiftRefactor/ConvertComputedPropertyToZeroParameterFunction.swift +++ b/Sources/SwiftRefactor/ConvertComputedPropertyToZeroParameterFunction.swift @@ -17,17 +17,17 @@ import SwiftSyntax #endif public struct ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringProvider { - public static func refactor(syntax: VariableDeclSyntax, in context: Void) -> FunctionDeclSyntax? { + public static func refactor(syntax: VariableDeclSyntax, in context: Void) throws -> FunctionDeclSyntax { guard syntax.bindings.count == 1, let binding = syntax.bindings.first, let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) - else { return nil } + else { throw RefactoringNotApplicableError("unsupported variable declaration") } var statements: CodeBlockItemListSyntax guard let typeAnnotation = binding.typeAnnotation, var accessorBlock = binding.accessorBlock - else { return nil } + else { throw RefactoringNotApplicableError("no type annotation or stored") } var effectSpecifiers: AccessorEffectSpecifiersSyntax? @@ -35,7 +35,7 @@ public struct ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringP case .accessors(let accessors): guard accessors.count == 1, let accessor = accessors.first, accessor.accessorSpecifier.tokenKind == .keyword(.get), let codeBlock = accessor.body - else { return nil } + else { throw RefactoringNotApplicableError("not a getter-only declaration") } effectSpecifiers = accessor.effectSpecifiers statements = codeBlock.statements let accessorSpecifier = accessor.accessorSpecifier diff --git a/Sources/SwiftRefactor/ConvertStoredPropertyToComputed.swift b/Sources/SwiftRefactor/ConvertStoredPropertyToComputed.swift index 2ea0976e675..625432e603c 100644 --- a/Sources/SwiftRefactor/ConvertStoredPropertyToComputed.swift +++ b/Sources/SwiftRefactor/ConvertStoredPropertyToComputed.swift @@ -17,9 +17,9 @@ import SwiftSyntax #endif public struct ConvertStoredPropertyToComputed: SyntaxRefactoringProvider { - public static func refactor(syntax: VariableDeclSyntax, in context: ()) -> VariableDeclSyntax? { + public static func refactor(syntax: VariableDeclSyntax, in context: ()) throws -> VariableDeclSyntax { guard syntax.bindings.count == 1, let binding = syntax.bindings.first, let initializer = binding.initializer else { - return nil + throw RefactoringNotApplicableError("unsupported variable declaration") } var codeBlockSyntax: CodeBlockItemListSyntax @@ -27,7 +27,11 @@ public struct ConvertStoredPropertyToComputed: SyntaxRefactoringProvider { if let functionExpression = initializer.value.as(FunctionCallExprSyntax.self), let closureExpression = functionExpression.calledExpression.as(ClosureExprSyntax.self) { - guard functionExpression.arguments.isEmpty else { return nil } + guard functionExpression.arguments.isEmpty else { + throw RefactoringNotApplicableError( + "initializer is a closure that takes arguments" + ) + } codeBlockSyntax = closureExpression.statements codeBlockSyntax.leadingTrivia = diff --git a/Sources/SwiftRefactor/ConvertZeroParameterFunctionToComputedProperty.swift b/Sources/SwiftRefactor/ConvertZeroParameterFunctionToComputedProperty.swift index 7027b0f5d93..7215b2fe028 100644 --- a/Sources/SwiftRefactor/ConvertZeroParameterFunctionToComputedProperty.swift +++ b/Sources/SwiftRefactor/ConvertZeroParameterFunctionToComputedProperty.swift @@ -17,10 +17,10 @@ import SwiftSyntax #endif public struct ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringProvider { - public static func refactor(syntax: FunctionDeclSyntax, in context: ()) -> VariableDeclSyntax? { + public static func refactor(syntax: FunctionDeclSyntax, in context: ()) throws -> VariableDeclSyntax { guard syntax.signature.parameterClause.parameters.isEmpty, let body = syntax.body - else { return nil } + else { throw RefactoringNotApplicableError("not a zero parameter function") } let variableName = PatternSyntax( IdentifierPatternSyntax( diff --git a/Sources/SwiftRefactor/ExpandEditorPlaceholder.swift b/Sources/SwiftRefactor/ExpandEditorPlaceholder.swift index 7bf87269489..a9ef7deaf9b 100644 --- a/Sources/SwiftRefactor/ExpandEditorPlaceholder.swift +++ b/Sources/SwiftRefactor/ExpandEditorPlaceholder.swift @@ -180,7 +180,7 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider { placeholder.baseName.isEditorPlaceholder, let arg = placeholder.parent?.as(LabeledExprSyntax.self), let argList = arg.parent?.as(LabeledExprListSyntax.self), - let call = argList.parent?.as(FunctionCallExprSyntax.self), + let call = argList.parent?.asProtocol(CallLikeSyntax.self), let expandedClosures = ExpandEditorPlaceholdersToLiteralClosures.expandClosurePlaceholders( in: call, ifIncluded: arg, @@ -266,6 +266,32 @@ public struct ExpandEditorPlaceholdersToLiteralClosures: SyntaxRefactoringProvid } } + public typealias Input = Syntax + public typealias Output = Syntax + + /// Apply the refactoring to a given syntax node. If either a + /// non-function-like syntax node is passed, or the refactoring fails, + /// `nil` is returned. + public static func refactor( + syntax: Syntax, + in context: Context = Context() + ) throws -> Syntax { + guard let call = syntax.asProtocol(CallLikeSyntax.self) else { + throw RefactoringNotApplicableError("not a call") + } + guard + let expanded = Self.expandClosurePlaceholders( + in: call, + ifIncluded: nil, + context: context + ) + else { + throw RefactoringNotApplicableError("could not expand closure placeholders") + } + return Syntax(fromProtocol: expanded) + } + + @available(*, deprecated, message: "Pass a Syntax argument instead of FunctionCallExprSyntax") public static func refactor( syntax call: FunctionCallExprSyntax, in context: Context = Context() @@ -282,11 +308,11 @@ public struct ExpandEditorPlaceholdersToLiteralClosures: SyntaxRefactoringProvid /// closure, then return a replacement of this call with one that uses /// closures based on the function types provided by each editor placeholder. /// Otherwise return nil. - fileprivate static func expandClosurePlaceholders( - in call: FunctionCallExprSyntax, + fileprivate static func expandClosurePlaceholders( + in call: C, ifIncluded arg: LabeledExprSyntax?, context: Context - ) -> FunctionCallExprSyntax? { + ) -> C? { switch context.format { case let .custom(formatter, allowNestedPlaceholders: allowNesting): let expanded = call.expandClosurePlaceholders( @@ -305,11 +331,7 @@ public struct ExpandEditorPlaceholdersToLiteralClosures: SyntaxRefactoringProvid let callToTrailingContext = CallToTrailingClosures.Context( startAtArgument: call.arguments.count - expanded.numClosures ) - guard let trailing = CallToTrailingClosures.refactor(syntax: expanded.expr, in: callToTrailingContext) else { - return nil - } - - return trailing + return try? CallToTrailingClosures._refactor(syntax: expanded.expr, in: callToTrailingContext) } } } @@ -382,7 +404,7 @@ extension TupleTypeElementSyntax { } } -extension FunctionCallExprSyntax { +extension CallLikeSyntax { /// If the given argument is `nil` or one of the last arguments that are all /// function-typed placeholders and this call doesn't have a trailing /// closure, then return a replacement of this call with one that uses @@ -393,7 +415,7 @@ extension FunctionCallExprSyntax { indentationWidth: Trivia? = nil, customFormat: BasicFormat? = nil, allowNestedPlaceholders: Bool = false - ) -> (expr: FunctionCallExprSyntax, numClosures: Int)? { + ) -> (expr: Self, numClosures: Int)? { var includedArg = false var argsToExpand = 0 for arg in arguments.reversed() { @@ -480,5 +502,5 @@ public func wrapInTypePlaceholder(_ str: String, type: String) -> String { return wrapInPlaceholder("T##" + str + "##" + type) } -fileprivate let placeholderStart: String = "<#" -fileprivate let placeholderEnd: String = "#>" +private let placeholderStart: String = "<#" +private let placeholderEnd: String = "#>" diff --git a/Sources/SwiftRefactor/FormatRawStringLiteral.swift b/Sources/SwiftRefactor/FormatRawStringLiteral.swift index 50c13366fd0..c8208b56ec3 100644 --- a/Sources/SwiftRefactor/FormatRawStringLiteral.swift +++ b/Sources/SwiftRefactor/FormatRawStringLiteral.swift @@ -35,7 +35,7 @@ import SwiftSyntax /// "Hello World" /// ``` public struct FormatRawStringLiteral: SyntaxRefactoringProvider { - public static func refactor(syntax lit: StringLiteralExprSyntax, in context: Void) -> StringLiteralExprSyntax? { + public static func refactor(syntax lit: StringLiteralExprSyntax, in context: Void) -> StringLiteralExprSyntax { var maximumHashes = 0 for segment in lit.segments { switch segment { diff --git a/Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift b/Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift index 6c98f890c18..268d6a51c46 100644 --- a/Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift +++ b/Sources/SwiftRefactor/MigrateToNewIfLetSyntax.swift @@ -39,7 +39,7 @@ import SwiftSyntax /// // ... /// } public struct MigrateToNewIfLetSyntax: SyntaxRefactoringProvider { - public static func refactor(syntax node: IfExprSyntax, in context: ()) -> IfExprSyntax? { + public static func refactor(syntax node: IfExprSyntax, in context: ()) -> IfExprSyntax { // Visit all conditions in the node. let newConditions = node.conditions.enumerated().map { (index, condition) -> ConditionElementListSyntax.Element in var conditionCopy = condition diff --git a/Sources/SwiftRefactor/OpaqueParameterToGeneric.swift b/Sources/SwiftRefactor/OpaqueParameterToGeneric.swift index f79382d51ab..c001aa3127b 100644 --- a/Sources/SwiftRefactor/OpaqueParameterToGeneric.swift +++ b/Sources/SwiftRefactor/OpaqueParameterToGeneric.swift @@ -18,7 +18,7 @@ import SwiftSyntax /// Describes a "some" parameter that has been rewritten into a generic /// parameter. -fileprivate struct RewrittenSome { +private struct RewrittenSome { let original: SomeOrAnyTypeSyntax let genericParam: GenericParameterSyntax let genericParamRef: IdentifierTypeSyntax @@ -37,7 +37,7 @@ fileprivate struct RewrittenSome { /// ```swift /// func someFunction(_ input: T1) {} /// ``` -fileprivate class SomeParameterRewriter: SyntaxRewriter { +private class SomeParameterRewriter: SyntaxRewriter { var rewrittenSomeParameters: [RewrittenSome] = [] override func visit(_ node: SomeOrAnyTypeSyntax) -> TypeSyntax { @@ -179,7 +179,7 @@ public struct OpaqueParameterToGeneric: SyntaxRefactoringProvider { public static func refactor( syntax decl: DeclSyntax, in context: Void - ) -> DeclSyntax? { + ) throws -> DeclSyntax { // Function declaration. if let funcSyntax = decl.as(FunctionDeclSyntax.self) { guard @@ -188,7 +188,7 @@ public struct OpaqueParameterToGeneric: SyntaxRefactoringProvider { augmenting: funcSyntax.genericParameterClause ) else { - return nil + throw RefactoringNotApplicableError("found no parameters to rewrite") } return DeclSyntax( @@ -206,7 +206,7 @@ public struct OpaqueParameterToGeneric: SyntaxRefactoringProvider { augmenting: initSyntax.genericParameterClause ) else { - return nil + throw RefactoringNotApplicableError("found no parameters to rewrite") } return DeclSyntax( @@ -224,7 +224,7 @@ public struct OpaqueParameterToGeneric: SyntaxRefactoringProvider { augmenting: subscriptSyntax.genericParameterClause ) else { - return nil + throw RefactoringNotApplicableError("found no parameters to rewrite") } return DeclSyntax( @@ -234,6 +234,6 @@ public struct OpaqueParameterToGeneric: SyntaxRefactoringProvider { ) } - return nil + throw RefactoringNotApplicableError("unsupported declaration") } } diff --git a/Sources/SwiftRefactor/PackageManifest/AddPackageDependency.swift b/Sources/SwiftRefactor/PackageManifest/AddPackageDependency.swift new file mode 100644 index 00000000000..0c29c68b642 --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddPackageDependency.swift @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a package dependency to a package manifest's source code. +@_spi(PackageRefactor) +public struct AddPackageDependency: EditRefactoringProvider { + public struct Context { + public var dependency: PackageDependency + + public init(dependency: PackageDependency) { + self.dependency = dependency + } + } + + /// The set of argument labels that can occur after the "dependencies" + /// argument in the Package initializers. + private static let argumentLabelsAfterDependencies: Set = [ + "targets", + "swiftLanguageVersions", + "cLanguageStandard", + "cxxLanguageStandard", + ] + + /// Produce the set of source edits needed to add the given package + /// dependency to the given manifest file. + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + let dependency = context.dependency + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + guard + try !dependencyAlreadyAdded( + dependency, + in: packageCall + ) + else { + return [] + } + + let newPackageCall = try addPackageDependencyLocal( + dependency, + to: packageCall + ) + + return [ + .replace(packageCall, with: newPackageCall.description) + ] + } + + /// Return `true` if the dependency already exists in the manifest, otherwise return `false`. + /// Throws an error if a dependency already exists with the same id or url, but different arguments. + private static func dependencyAlreadyAdded( + _ dependency: PackageDependency, + in packageCall: FunctionCallExprSyntax + ) throws -> Bool { + let dependencySyntax = dependency.asSyntax() + guard let dependencyFnSyntax = dependencySyntax.as(FunctionCallExprSyntax.self) else { + throw ManifestEditError.cannotFindPackage + } + + guard + let id = dependencyFnSyntax.arguments.first(where: { + $0.label?.text == "url" || $0.label?.text == "id" || $0.label?.text == "path" + }) + else { + throw ManifestEditError.malformedManifest(error: "missing id or url argument in dependency syntax") + } + + if let existingDependencies = packageCall.findArgument(labeled: "dependencies") { + // If we have an existing dependencies array, we need to check if + // it's already added. + if let expr = existingDependencies.expression.as(ArrayExprSyntax.self) { + // Iterate through existing dependencies and look for an argument that matches + // either the `id` or `url` argument of the new dependency. + let existingArgument = expr.elements.first { elem in + if let funcExpr = elem.expression.as(FunctionCallExprSyntax.self) { + return funcExpr.arguments.contains { + $0.trimmedDescription == id.trimmedDescription + } + } + return true + } + + if let existingArgument { + let normalizedExistingArgument = existingArgument.detached.with(\.trailingComma, nil) + // This exact dependency already exists, return false to indicate we should do nothing. + if normalizedExistingArgument.trimmedDescription == dependencySyntax.trimmedDescription { + return true + } + throw ManifestEditError.existingDependency(dependencyName: dependency.identifier) + } + } + } + return false + } + + /// Implementation of adding a package dependency to an existing call. + static func addPackageDependencyLocal( + _ dependency: PackageDependency, + to packageCall: FunctionCallExprSyntax + ) throws -> FunctionCallExprSyntax { + try packageCall.appendingToArrayArgument( + label: "dependencies", + labelsAfter: Self.argumentLabelsAfterDependencies, + newElement: dependency.asSyntax() + ) + } +} + +fileprivate extension PackageDependency { + var identifier: String { + switch self { + case .sourceControl(let info): + return info.location + case .fileSystem(let info): + return info.path + case .registry(let info): + return info.identity + } + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/AddPackageTarget.swift b/Sources/SwiftRefactor/PackageManifest/AddPackageTarget.swift new file mode 100644 index 00000000000..396053e271f --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddPackageTarget.swift @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a target to a manifest's source code. +@_spi(PackageRefactor) +public struct AddPackageTarget: EditRefactoringProvider { + public struct Context { + public let target: PackageTarget + public var testHarness: TestHarness + + public init( + target: PackageTarget, + testHarness: TestHarness = .default + ) { + self.target = target + self.testHarness = testHarness + } + } + + /// The set of argument labels that can occur after the "targets" + /// argument in the Package initializers. + private static let argumentLabelsAfterTargets: Set = [ + "swiftLanguageVersions", + "swiftLanguageModes", + "cLanguageStandard", + "cxxLanguageStandard", + ] + + /// The kind of test harness to use. This isn't part of the manifest + /// itself, but is used to guide the generation process. + public enum TestHarness: String, Codable, Sendable { + /// Don't use any library + case none + + /// Create a test using the XCTest library. + case xctest + + /// Create a test using the swift-testing package. + case swiftTesting = "swift-testing" + + /// The default testing library to use. + public static let `default`: TestHarness = .swiftTesting + } + + /// Add the given target to the manifest, producing a set of edit results + /// that updates the manifest and adds some source files to stub out the + /// new target. + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + // Create a mutable version of target to which we can add more + // content when needed. + var target = context.target + + // Add dependencies needed for various targets. + switch target.type { + case .macro: + // Macro targets need to depend on a couple of libraries from + // SwiftSyntax. + target.dependencies.append(contentsOf: macroTargetDependencies) + + default: + break + } + + var newPackageCall = try packageCall.appendingToArrayArgument( + label: "targets", + labelsAfter: Self.argumentLabelsAfterTargets, + newElement: target.asSyntax() + ) + + // Perform any other actions that are needed for this target type. + var extraManifestEdits: [SourceEdit] = [] + switch target.type { + case .macro: + if !manifest.containsStringLiteral("swift-syntax") { + newPackageCall = + try AddPackageDependency + .addPackageDependencyLocal( + .swiftSyntax(from: "<#version#>"), + to: newPackageCall + ) + + // Look for the first import declaration and insert an + // import of `CompilerPluginSupport` there. + let newImport = "import CompilerPluginSupport\n" + for node in manifest.statements { + if let importDecl = node.item.as(ImportDeclSyntax.self) { + let insertPos = importDecl + .positionAfterSkippingLeadingTrivia + extraManifestEdits.append( + SourceEdit( + range: insertPos.. PackageDependency { + return .sourceControl( + .init( + location: swiftSyntaxURL, + requirement: .rangeFrom(version) + ) + ) + } +} + +extension SourceFileSyntax { + private class ContainsLiteralVisitor: SyntaxVisitor { + let string: String + var found: Bool = false + + init(string: String) { + self.string = string + super.init(viewMode: .sourceAccurate) + } + + override func visit(_ node: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind { + if let representedLiteralValue = node.representedLiteralValue, + representedLiteralValue == string + { + found = true + } + + return .skipChildren + } + } + + /// Determine whether this source file contains a string literal + /// matching the given contents. + fileprivate func containsStringLiteral(_ contents: String) -> Bool { + let visitor = ContainsLiteralVisitor(string: contents) + visitor.walk(self) + return visitor.found + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/AddPluginUsage.swift b/Sources/SwiftRefactor/PackageManifest/AddPluginUsage.swift new file mode 100644 index 00000000000..420c6468f9e --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddPluginUsage.swift @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a plugin usage to a particular target in the manifest's source +/// code. +@_spi(PackageRefactor) +public struct AddPluginUsage: EditRefactoringProvider { + public struct Context { + public let targetName: String + public let pluginUsage: PackageTarget.PluginUsage + + public init(targetName: String, pluginUsage: PackageTarget.PluginUsage) { + self.targetName = targetName + self.pluginUsage = pluginUsage + } + } + + /// Produce the set of source edits needed to add the given package + /// dependency to the given manifest file. + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + let targetName = context.targetName + let pluginUsage = context.pluginUsage + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + // Find the target to be modified. + let targetCall = try packageCall.findManifestTargetCall(targetName: targetName) + + let newTargetCall = try targetCall.appendingToArrayArgument( + label: "plugins", + labelsAfter: [], + newElement: pluginUsage.asSyntax() + ) + + return [ + .replace(targetCall, with: newTargetCall.description) + ] + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/AddProduct.swift b/Sources/SwiftRefactor/PackageManifest/AddProduct.swift new file mode 100644 index 00000000000..366d543e6d9 --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddProduct.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a product to the manifest's source code. +@_spi(PackageRefactor) +public struct AddProduct: EditRefactoringProvider { + public struct Context { + public let product: ProductDescription + + public init(product: ProductDescription) { + self.product = product + } + } + /// The set of argument labels that can occur after the "products" + /// argument in the Package initializers. + private static let argumentLabelsAfterProducts: Set = [ + "dependencies", + "targets", + "swiftLanguageVersions", + "cLanguageStandard", + "cxxLanguageStandard", + ] + + /// Produce the set of source edits needed to add the given package + /// dependency to the given manifest file. + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + let product = context.product + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + let newPackageCall = try packageCall.appendingToArrayArgument( + label: "products", + labelsAfter: argumentLabelsAfterProducts, + newElement: product.asSyntax() + ) + + return [ + .replace(packageCall, with: newPackageCall.description) + ] + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/AddSwiftSetting.swift b/Sources/SwiftRefactor/PackageManifest/AddSwiftSetting.swift new file mode 100644 index 00000000000..e51c294accb --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddSwiftSetting.swift @@ -0,0 +1,161 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a swift setting to a manifest's source code. +@_spi(PackageRefactor) +public struct AddSwiftSetting: EditRefactoringProvider { + public struct Context { + let target: String + let setting: String + let value: ExprSyntax? + } + + /// The set of argument labels that can occur after the "targets" + /// argument in the Package initializers. + private static let argumentLabelsAfterSwiftSettings: Set = [ + "linkerSettings", + "plugins", + ] + + public static func upcomingFeature( + to target: String, + name: String, + manifest: SourceFileSyntax + ) throws -> [SourceEdit] { + try textRefactor( + syntax: manifest, + in: .init( + target: target, + setting: "enableUpcomingFeature", + value: name.asSyntax() + ) + ) + } + + public static func experimentalFeature( + to target: String, + name: String, + manifest: SourceFileSyntax + ) throws -> [SourceEdit] { + try textRefactor( + syntax: manifest, + in: .init( + target: target, + setting: "enableExperimentalFeature", + value: name.asSyntax() + ) + ) + } + + public static func languageMode( + to target: String, + mode rawMode: String, + manifest: SourceFileSyntax + ) throws -> [SourceEdit] { + let mode: String + switch rawMode { + case "3", "4", "5", "6": + mode = ".v\(rawMode)" + case "4.2": + mode = ".v4_2" + default: + mode = ".version(\"\(rawMode)\")" + } + + return try textRefactor( + syntax: manifest, + in: .init( + target: target, + setting: "swiftLanguageMode", + value: "\(raw: mode)" + ) + ) + } + + public static func strictMemorySafety( + to target: String, + manifest: SourceFileSyntax + ) throws -> [SourceEdit] { + try textRefactor( + syntax: manifest, + in: .init( + target: target, + setting: "strictMemorySafety()", + value: .none + ) + ) + } + + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + guard let targetsArgument = packageCall.findArgument(labeled: "targets"), + let targetArray = targetsArgument.expression.findArrayArgument() + else { + throw ManifestEditError.cannotFindTargets + } + + let targetCall = targetArray + .elements + .lazy + .compactMap { + $0.expression.as(FunctionCallExprSyntax.self) + }.first { targetCall in + if let nameArgument = targetCall.findArgument(labeled: "name"), + let nameLiteral = nameArgument.expression.as(StringLiteralExprSyntax.self), + nameLiteral.representedLiteralValue == context.target + { + return true + } + + return false + } + + guard let targetCall else { + throw ManifestEditError.cannotFindTarget(targetName: context.target) + } + + if let memberRef = targetCall.calledExpression.as(MemberAccessExprSyntax.self), + memberRef.declName.baseName.text == "plugin" + { + throw ManifestEditError.cannotAddSettingsToPluginTarget + } + + let newTargetCall = + if let value = context.value { + try targetCall.appendingToArrayArgument( + label: "swiftSettings", + labelsAfter: self.argumentLabelsAfterSwiftSettings, + newElement: ".\(raw: context.setting)(\(value))" + ) + } else { + try targetCall.appendingToArrayArgument( + label: "swiftSettings", + labelsAfter: self.argumentLabelsAfterSwiftSettings, + newElement: ".\(raw: context.setting)" + ) + } + + return [ + .replace(targetCall, with: newTargetCall.description) + ] + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/AddTargetDependency.swift b/Sources/SwiftRefactor/PackageManifest/AddTargetDependency.swift new file mode 100644 index 00000000000..9b3e0fceef2 --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/AddTargetDependency.swift @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Add a target dependency to a manifest's source code. +@_spi(PackageRefactor) +public struct AddTargetDependency: EditRefactoringProvider { + public struct Context { + /// The dependency to add. + public var dependency: PackageTarget.Dependency + + /// The name of the target to which the dependency will be added. + public var targetName: String + + public init(dependency: PackageTarget.Dependency, targetName: String) { + self.dependency = dependency + self.targetName = targetName + } + } + + /// The set of argument labels that can occur after the "dependencies" + /// argument in the various target initializers. + private static let argumentLabelsAfterDependencies: Set = [ + "path", + "exclude", + "sources", + "resources", + "publicHeadersPath", + "packageAccess", + "cSettings", + "cxxSettings", + "swiftSettings", + "linkerSettings", + "plugins", + ] + + /// Produce the set of source edits needed to add the given target + /// dependency to the given manifest file. + public static func textRefactor( + syntax manifest: SourceFileSyntax, + in context: Context + ) throws -> [SourceEdit] { + let dependency = context.dependency + let targetName = context.targetName + + guard let packageCall = manifest.findCall(calleeName: "Package") else { + throw ManifestEditError.cannotFindPackage + } + + // Find the target to be modified. + let targetCall = try packageCall.findManifestTargetCall(targetName: targetName) + + let newTargetCall = try addTargetDependencyLocal( + dependency, + to: targetCall + ) + + return [ + .replace(targetCall, with: newTargetCall.description) + ] + } + + /// Implementation of adding a target dependency to an existing call. + static func addTargetDependencyLocal( + _ dependency: PackageTarget.Dependency, + to targetCall: FunctionCallExprSyntax + ) throws -> FunctionCallExprSyntax { + try targetCall.appendingToArrayArgument( + label: "dependencies", + labelsAfter: Self.argumentLabelsAfterDependencies, + newElement: dependency.asSyntax() + ) + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/ManifestEditError.swift b/Sources/SwiftRefactor/PackageManifest/ManifestEditError.swift new file mode 100644 index 00000000000..d87de291394 --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/ManifestEditError.swift @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// An error describing problems that can occur when attempting to edit a +/// package manifest programattically. +@_spi(PackageRefactor) +public enum ManifestEditError: Error, Equatable { + case cannotFindPackage + case cannotFindTargets + case cannotFindTarget(targetName: String) + case cannotFindArrayLiteralArgument(argumentName: String) + case cannotAddSettingsToPluginTarget + case existingDependency(dependencyName: String) + case malformedManifest(error: String) +} + +extension ManifestEditError: CustomStringConvertible { + public var description: String { + switch self { + case .cannotFindPackage: + return "invalid manifest: unable to find 'Package' declaration" + case .cannotFindTargets: + return "unable to find package targets in manifest" + case .cannotFindTarget(targetName: let name): + return "unable to find target named '\(name)' in package" + case .cannotFindArrayLiteralArgument(argumentName: let name): + return "unable to find array literal for '\(name)' argument" + case .cannotAddSettingsToPluginTarget: + return "plugin targets do not support settings" + case .existingDependency(let name): + return "unable to add dependency '\(name)' because it already exists in the list of dependencies" + case .malformedManifest(let error): + return "invalid manifest: \(error)" + } + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/ManifestSyntaxRepresentable.swift b/Sources/SwiftRefactor/PackageManifest/ManifestSyntaxRepresentable.swift new file mode 100644 index 00000000000..00e35dd00ea --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/ManifestSyntaxRepresentable.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// Describes an entity in the package model that can be represented as +/// a syntax node. +protocol ManifestSyntaxRepresentable { + /// The most specific kind of syntax node that best describes this entity + /// in the manifest. + /// + /// There might be other kinds of syntax nodes that can also represent + /// the syntax, but this is the one that a canonical manifest will use. + /// As an example, a package dependency is usually expressed as, e.g., + /// .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + /// + /// However, there could be other forms, e.g., this is also valid: + /// Package.Dependency.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + associatedtype PreferredSyntax: SyntaxProtocol + + /// Provides a suitable syntax node to describe this entity in the package + /// model. + /// + /// The resulting syntax is a fragment that describes just this entity, + /// and it's enclosing entity will need to understand how to fit it in. + /// For example, a `PackageDependency` entity would map to syntax for + /// something like + /// .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + func asSyntax() -> PreferredSyntax +} + +extension String: ManifestSyntaxRepresentable { + typealias PreferredSyntax = ExprSyntax + + func asSyntax() -> ExprSyntax { "\(literal: self)" } +} diff --git a/Sources/SwiftRefactor/PackageManifest/PackageDependency.swift b/Sources/SwiftRefactor/PackageManifest/PackageDependency.swift new file mode 100644 index 00000000000..3db844e3ddc --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/PackageDependency.swift @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder + +/// Describes a package dependency for refactoring purposes. This is a syntactic +/// subset of the full package manifest's description of a package dependency. +@_spi(PackageRefactor) +public enum PackageDependency: Sendable { + case fileSystem(FileSystem) + case sourceControl(SourceControl) + case registry(Registry) + + public struct FileSystem: Sendable { + public let path: String + + public init(path: String) { + self.path = path + } + } + + public struct SourceControl: Sendable { + public let location: String + public let requirement: Requirement + + public init(location: String, requirement: Requirement) { + self.location = location + self.requirement = requirement + } + + public enum Requirement: Sendable { + case exact(String) + case rangeFrom(String) + case range(lowerBound: String, upperBound: String) + case revision(String) + case branch(String) + } + } + + public struct Registry: Sendable { + public let identity: String + public let requirement: Requirement + + public init(identity: String, requirement: Requirement) { + self.identity = identity + self.requirement = requirement + } + + /// The dependency requirement. + public enum Requirement: Sendable { + case exact(String) + case rangeFrom(String) + case range(lowerBound: String, upperBound: String) + } + } +} + +extension PackageDependency: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + switch self { + case .fileSystem(let filesystem): return filesystem.asSyntax() + case .sourceControl(let sourceControl): return sourceControl.asSyntax() + case .registry(let registry): return registry.asSyntax() + } + } +} + +extension PackageDependency.FileSystem: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + ".package(path: \(literal: path.description))" + } +} + +extension PackageDependency.SourceControl: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + ".package(url: \(literal: location.description), \(requirement.asSyntax()))" + } +} + +extension PackageDependency.Registry: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + ".package(id: \(literal: identity.description), \(requirement.asSyntax()))" + } +} + +extension PackageDependency.SourceControl.Requirement: ManifestSyntaxRepresentable { + func asSyntax() -> LabeledExprSyntax { + switch self { + case .exact(let version): + return LabeledExprSyntax( + label: "exact", + expression: version.asSyntax() + ) + + case .rangeFrom(let range): + return LabeledExprSyntax( + label: "from", + expression: range.asSyntax() + ) + + case .range(let lowerBound, let upperBound): + return LabeledExprSyntax( + expression: "\(literal: lowerBound)..<\(literal: upperBound)" as ExprSyntax + ) + + case .revision(let revision): + return LabeledExprSyntax( + label: "revision", + expression: "\(literal: revision)" as ExprSyntax + ) + + case .branch(let branch): + return LabeledExprSyntax( + label: "branch", + expression: "\(literal: branch)" as ExprSyntax + ) + } + } +} + +extension PackageDependency.Registry.Requirement: ManifestSyntaxRepresentable { + func asSyntax() -> LabeledExprSyntax { + switch self { + case .exact(let version): + return LabeledExprSyntax( + label: "exact", + expression: version.asSyntax() + ) + + case .rangeFrom(let range): + return LabeledExprSyntax( + label: "from", + expression: range.asSyntax() + ) + + case .range(let lowerBound, let upperBound): + return LabeledExprSyntax( + expression: "\(lowerBound.asSyntax())..<\(upperBound.asSyntax())" as ExprSyntax + ) + } + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/PackageTarget.swift b/Sources/SwiftRefactor/PackageManifest/PackageTarget.swift new file mode 100644 index 00000000000..5c12ba32eef --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/PackageTarget.swift @@ -0,0 +1,163 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// Syntactic wrapper type that describes a target for refactoring +/// purposes but does not interpret its contents. +@_spi(PackageRefactor) +public struct PackageTarget { + public let name: String + + public var sanitizedName: String { + name + .mangledToC99ExtendedIdentifier() + .localizedFirstWordCapitalized() + } + + /// The type of target. + public let type: TargetKind + + public internal(set) var dependencies: [Dependency] + + public let path: String? + + public let url: String? + + public let checksum: String? + + /// The usages of package plugins by the target. + public let pluginUsages: [PluginUsage]? + + /// Represents a target's usage of a plugin target or product. + public enum PluginUsage { + case plugin(name: String, package: String?) + } + + public enum TargetKind: String { + case binary + case executable + case library + case macro + case plugin + case system + case test + } + + public enum Dependency: Sendable { + case byName(name: String) + case target(name: String) + case product(name: String, package: String?) + + /// Retrieve the name of the dependency + public var name: String { + switch self { + case .byName(let name), + .target(let name), + .product(let name, package: _): + return name + } + } + } + + public init( + name: String, + type: TargetKind = .library, + dependencies: [Dependency] = [], + path: String? = nil, + url: String? = nil, + checksum: String? = nil, + pluginUsages: [PluginUsage]? = nil + ) { + self.name = name + self.type = type + self.dependencies = dependencies + self.path = path + self.url = url + self.checksum = checksum + self.pluginUsages = pluginUsages + } +} + +extension PackageTarget: ManifestSyntaxRepresentable { + /// The function name in the package manifest. + private var functionName: String { + switch type { + case .binary: return "binaryTarget" + case .executable: return "executableTarget" + case .library: return "target" + case .macro: return "macro" + case .plugin: return "plugin" + case .system: return "systemLibrary" + case .test: return "testTarget" + } + } + + func asSyntax() -> ExprSyntax { + var arguments: [LabeledExprSyntax] = [] + arguments.append(label: "name", stringLiteral: name) + // FIXME: pluginCapability + + arguments.appendIfNonEmpty( + label: "dependencies", + arrayLiteral: dependencies + ) + + arguments.appendIf(label: "path", stringLiteral: path) + arguments.appendIf(label: "url", stringLiteral: url) + + // Only for plugins + arguments.appendIf(label: "checksum", stringLiteral: checksum) + + if let pluginUsages { + arguments.appendIfNonEmpty(label: "plugins", arrayLiteral: pluginUsages) + } + + let separateParen: String = arguments.count > 1 ? "\n" : "" + let argumentsSyntax = LabeledExprListSyntax(arguments) + return ".\(raw: functionName)(\(argumentsSyntax)\(raw: separateParen))" + } +} + +extension PackageTarget.Dependency: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + switch self { + case .byName(let name): + return "\(literal: name)" + + case .target(let name): + return ".target(name: \(literal: name))" + + case .product(let name, package: nil): + return ".product(name: \(literal: name))" + + case .product(let name, let package): + return ".product(name: \(literal: name), package: \(literal: package))" + } + } +} + +extension PackageTarget.PluginUsage: ManifestSyntaxRepresentable { + func asSyntax() -> ExprSyntax { + switch self { + case .plugin(let name, package: nil): + return ".plugin(name: \(literal: name))" + + case .plugin(let name, let package): + return ".plugin(name: \(literal: name), package: \(literal: package))" + } + } +} + +fileprivate extension String { + func localizedFirstWordCapitalized() -> String { prefix(1).uppercased() + dropFirst() } +} diff --git a/Sources/SwiftRefactor/PackageManifest/ProductDescription.swift b/Sources/SwiftRefactor/PackageManifest/ProductDescription.swift new file mode 100644 index 00000000000..1bf34d317eb --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/ProductDescription.swift @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// Syntactic wrapper type that describes a product for refactoring +/// purposes but does not interpret its contents. +@_spi(PackageRefactor) +public struct ProductDescription { + /// The name of the product. + public let name: String + + /// The targets in the product. + public let targets: [String] + + /// The type of product. + public let type: ProductType + + public enum ProductType { + /// The type of library. + public enum LibraryType: String, Codable, Sendable { + + /// Static library. + case `static` + + /// Dynamic library. + case `dynamic` + + /// The type of library is unspecified and should be decided by package manager. + case automatic + } + + /// A library product. + case library(LibraryType) + + /// An executable product. + case executable + + /// An executable code snippet. + case snippet + + /// An plugin product. + case plugin + + /// A test product. + case test + + /// A macro product. + case `macro` + } + + public init( + name: String, + type: ProductType, + targets: [String] + ) { + self.name = name + self.type = type + self.targets = targets + } +} + +extension ProductDescription: ManifestSyntaxRepresentable { + /// The function name in the package manifest. + /// + /// Some of these are actually invalid, but it's up to the caller + /// to check the precondition. + private var functionName: String { + switch type { + case .executable: return "executable" + case .library: return "library" + case .macro: return "macro" + case .plugin: return "plugin" + case .snippet: return "snippet" + case .test: return "test" + } + } + + func asSyntax() -> ExprSyntax { + var arguments: [LabeledExprSyntax] = [] + arguments.append(label: "name", stringLiteral: name) + + // Libraries have a type. + if case .library(let libraryType) = type { + switch libraryType { + case .automatic: + break + + case .dynamic, .static: + arguments.append( + label: "type", + expression: ".\(raw: libraryType.rawValue)" + ) + } + } + + arguments.appendIfNonEmpty( + label: "targets", + arrayLiteral: targets + ) + + let separateParen: String = arguments.count > 1 ? "\n" : "" + let argumentsSyntax = LabeledExprListSyntax(arguments) + return ".\(raw: functionName)(\(argumentsSyntax)\(raw: separateParen))" + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/StringUtils.swift b/Sources/SwiftRefactor/PackageManifest/StringUtils.swift new file mode 100644 index 00000000000..a964cc33ec8 --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/StringUtils.swift @@ -0,0 +1,224 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +@_spi(PackageRefactor) +extension String { + /// Returns a form of the string that is valid C99 Extended Identifier (by + /// replacing any invalid characters in an unspecified but consistent way). + /// The output string is guaranteed to be non-empty as long as the input + /// string is non-empty. + func mangledToC99ExtendedIdentifier() -> String { + // Map invalid C99-invalid Unicode scalars to a replacement character. + let replacementUnichar: UnicodeScalar = "_" + var mangledUnichars: [UnicodeScalar] = self.unicodeScalars.map { + switch $0.value { + case // A-Z + 0x0041...0x005A, + // a-z + 0x0061...0x007A, + // 0-9 + 0x0030...0x0039, + // _ + 0x005F, + // Latin (1) + 0x00AA...0x00AA, + // Special characters (1) + 0x00B5...0x00B5, 0x00B7...0x00B7, + // Latin (2) + 0x00BA...0x00BA, 0x00C0...0x00D6, 0x00D8...0x00F6, + 0x00F8...0x01F5, 0x01FA...0x0217, 0x0250...0x02A8, + // Special characters (2) + 0x02B0...0x02B8, 0x02BB...0x02BB, 0x02BD...0x02C1, + 0x02D0...0x02D1, 0x02E0...0x02E4, 0x037A...0x037A, + // Greek (1) + 0x0386...0x0386, 0x0388...0x038A, 0x038C...0x038C, + 0x038E...0x03A1, 0x03A3...0x03CE, 0x03D0...0x03D6, + 0x03DA...0x03DA, 0x03DC...0x03DC, 0x03DE...0x03DE, + 0x03E0...0x03E0, 0x03E2...0x03F3, + // Cyrillic + 0x0401...0x040C, 0x040E...0x044F, 0x0451...0x045C, + 0x045E...0x0481, 0x0490...0x04C4, 0x04C7...0x04C8, + 0x04CB...0x04CC, 0x04D0...0x04EB, 0x04EE...0x04F5, + 0x04F8...0x04F9, + // Armenian (1) + 0x0531...0x0556, + // Special characters (3) + 0x0559...0x0559, + // Armenian (2) + 0x0561...0x0587, + // Hebrew + 0x05B0...0x05B9, 0x05BB...0x05BD, 0x05BF...0x05BF, + 0x05C1...0x05C2, 0x05D0...0x05EA, 0x05F0...0x05F2, + // Arabic (1) + 0x0621...0x063A, 0x0640...0x0652, + // Digits (1) + 0x0660...0x0669, + // Arabic (2) + 0x0670...0x06B7, 0x06BA...0x06BE, 0x06C0...0x06CE, + 0x06D0...0x06DC, 0x06E5...0x06E8, 0x06EA...0x06ED, + // Digits (2) + 0x06F0...0x06F9, + // Devanagari and Special character 0x093D. + 0x0901...0x0903, 0x0905...0x0939, 0x093D...0x094D, + 0x0950...0x0952, 0x0958...0x0963, + // Digits (3) + 0x0966...0x096F, + // Bengali (1) + 0x0981...0x0983, 0x0985...0x098C, 0x098F...0x0990, + 0x0993...0x09A8, 0x09AA...0x09B0, 0x09B2...0x09B2, + 0x09B6...0x09B9, 0x09BE...0x09C4, 0x09C7...0x09C8, + 0x09CB...0x09CD, 0x09DC...0x09DD, 0x09DF...0x09E3, + // Digits (4) + 0x09E6...0x09EF, + // Bengali (2) + 0x09F0...0x09F1, + // Gurmukhi (1) + 0x0A02...0x0A02, 0x0A05...0x0A0A, 0x0A0F...0x0A10, + 0x0A13...0x0A28, 0x0A2A...0x0A30, 0x0A32...0x0A33, + 0x0A35...0x0A36, 0x0A38...0x0A39, 0x0A3E...0x0A42, + 0x0A47...0x0A48, 0x0A4B...0x0A4D, 0x0A59...0x0A5C, + 0x0A5E...0x0A5E, + // Digits (5) + 0x0A66...0x0A6F, + // Gurmukhi (2) + 0x0A74...0x0A74, + // Gujarti + 0x0A81...0x0A83, 0x0A85...0x0A8B, 0x0A8D...0x0A8D, + 0x0A8F...0x0A91, 0x0A93...0x0AA8, 0x0AAA...0x0AB0, + 0x0AB2...0x0AB3, 0x0AB5...0x0AB9, 0x0ABD...0x0AC5, + 0x0AC7...0x0AC9, 0x0ACB...0x0ACD, 0x0AD0...0x0AD0, + 0x0AE0...0x0AE0, + // Digits (6) + 0x0AE6...0x0AEF, + // Oriya and Special character 0x0B3D + 0x0B01...0x0B03, 0x0B05...0x0B0C, 0x0B0F...0x0B10, + 0x0B13...0x0B28, 0x0B2A...0x0B30, 0x0B32...0x0B33, + 0x0B36...0x0B39, 0x0B3D...0x0B43, 0x0B47...0x0B48, + 0x0B4B...0x0B4D, 0x0B5C...0x0B5D, 0x0B5F...0x0B61, + // Digits (7) + 0x0B66...0x0B6F, + // Tamil + 0x0B82...0x0B83, 0x0B85...0x0B8A, 0x0B8E...0x0B90, + 0x0B92...0x0B95, 0x0B99...0x0B9A, 0x0B9C...0x0B9C, + 0x0B9E...0x0B9F, 0x0BA3...0x0BA4, 0x0BA8...0x0BAA, + 0x0BAE...0x0BB5, 0x0BB7...0x0BB9, 0x0BBE...0x0BC2, + 0x0BC6...0x0BC8, 0x0BCA...0x0BCD, + // Digits (8) + 0x0BE7...0x0BEF, + // Telugu + 0x0C01...0x0C03, 0x0C05...0x0C0C, 0x0C0E...0x0C10, + 0x0C12...0x0C28, 0x0C2A...0x0C33, 0x0C35...0x0C39, + 0x0C3E...0x0C44, 0x0C46...0x0C48, 0x0C4A...0x0C4D, + 0x0C60...0x0C61, + // Digits (9) + 0x0C66...0x0C6F, + // Kannada + 0x0C82...0x0C83, 0x0C85...0x0C8C, 0x0C8E...0x0C90, + 0x0C92...0x0CA8, 0x0CAA...0x0CB3, 0x0CB5...0x0CB9, + 0x0CBE...0x0CC4, 0x0CC6...0x0CC8, 0x0CCA...0x0CCD, + 0x0CDE...0x0CDE, 0x0CE0...0x0CE1, + // Digits (10) + 0x0CE6...0x0CEF, + // Malayam + 0x0D02...0x0D03, 0x0D05...0x0D0C, 0x0D0E...0x0D10, + 0x0D12...0x0D28, 0x0D2A...0x0D39, 0x0D3E...0x0D43, + 0x0D46...0x0D48, 0x0D4A...0x0D4D, 0x0D60...0x0D61, + // Digits (11) + 0x0D66...0x0D6F, + // Thai...including Digits 0x0E50...0x0E59 } + 0x0E01...0x0E3A, 0x0E40...0x0E5B, + // Lao (1) + 0x0E81...0x0E82, 0x0E84...0x0E84, 0x0E87...0x0E88, + 0x0E8A...0x0E8A, 0x0E8D...0x0E8D, 0x0E94...0x0E97, + 0x0E99...0x0E9F, 0x0EA1...0x0EA3, 0x0EA5...0x0EA5, + 0x0EA7...0x0EA7, 0x0EAA...0x0EAB, 0x0EAD...0x0EAE, + 0x0EB0...0x0EB9, 0x0EBB...0x0EBD, 0x0EC0...0x0EC4, + 0x0EC6...0x0EC6, 0x0EC8...0x0ECD, + // Digits (12) + 0x0ED0...0x0ED9, + // Lao (2) + 0x0EDC...0x0EDD, + // Tibetan (1) + 0x0F00...0x0F00, 0x0F18...0x0F19, + // Digits (13) + 0x0F20...0x0F33, + // Tibetan (2) + 0x0F35...0x0F35, 0x0F37...0x0F37, 0x0F39...0x0F39, + 0x0F3E...0x0F47, 0x0F49...0x0F69, 0x0F71...0x0F84, + 0x0F86...0x0F8B, 0x0F90...0x0F95, 0x0F97...0x0F97, + 0x0F99...0x0FAD, 0x0FB1...0x0FB7, 0x0FB9...0x0FB9, + // Georgian + 0x10A0...0x10C5, 0x10D0...0x10F6, + // Latin (3) + 0x1E00...0x1E9B, 0x1EA0...0x1EF9, + // Greek (2) + 0x1F00...0x1F15, 0x1F18...0x1F1D, 0x1F20...0x1F45, + 0x1F48...0x1F4D, 0x1F50...0x1F57, 0x1F59...0x1F59, + 0x1F5B...0x1F5B, 0x1F5D...0x1F5D, 0x1F5F...0x1F7D, + 0x1F80...0x1FB4, 0x1FB6...0x1FBC, + // Special characters (4) + 0x1FBE...0x1FBE, + // Greek (3) + 0x1FC2...0x1FC4, 0x1FC6...0x1FCC, 0x1FD0...0x1FD3, + 0x1FD6...0x1FDB, 0x1FE0...0x1FEC, 0x1FF2...0x1FF4, + 0x1FF6...0x1FFC, + // Special characters (5) + 0x203F...0x2040, + // Latin (4) + 0x207F...0x207F, + // Special characters (6) + 0x2102...0x2102, 0x2107...0x2107, 0x210A...0x2113, + 0x2115...0x2115, 0x2118...0x211D, 0x2124...0x2124, + 0x2126...0x2126, 0x2128...0x2128, 0x212A...0x2131, + 0x2133...0x2138, 0x2160...0x2182, 0x3005...0x3007, + 0x3021...0x3029, + // Hiragana + 0x3041...0x3093, 0x309B...0x309C, + // Katakana + 0x30A1...0x30F6, 0x30FB...0x30FC, + // Bopmofo [sic] + 0x3105...0x312C, + // CJK Unified Ideographs + 0x4E00...0x9FA5, + // Hangul, + 0xAC00...0xD7A3: + return $0 + default: + return replacementUnichar + } + } + + // Apply further restrictions to the prefix. + LOOP: for (idx, c) in mangledUnichars.enumerated() { + switch c.value { + case // 0-9 + 0x0030...0x0039, + // Annex D. + 0x0660...0x0669, 0x06F0...0x06F9, 0x0966...0x096F, + 0x09E6...0x09EF, 0x0A66...0x0A6F, 0x0AE6...0x0AEF, + 0x0B66...0x0B6F, 0x0BE7...0x0BEF, 0x0C66...0x0C6F, + 0x0CE6...0x0CEF, 0x0D66...0x0D6F, 0x0E50...0x0E59, + 0x0ED0...0x0ED9, 0x0F20...0x0F33: + mangledUnichars[idx] = replacementUnichar + break LOOP + default: + break LOOP + } + } + + // Combine the characters as a string again and return it. + // FIXME: We should only construct a new string if anything changed. + // FIXME: There doesn't seem to be a way to create a string from an + // array of Unicode scalars; but there must be a better way. + return String(decoding: mangledUnichars.flatMap { $0.utf8 }, as: UTF8.self) + } +} diff --git a/Sources/SwiftRefactor/PackageManifest/SyntaxEditUtils.swift b/Sources/SwiftRefactor/PackageManifest/SyntaxEditUtils.swift new file mode 100644 index 00000000000..2f1abdb3a0c --- /dev/null +++ b/Sources/SwiftRefactor/PackageManifest/SyntaxEditUtils.swift @@ -0,0 +1,547 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftBasicFormat +import SwiftParser +import SwiftSyntax + +/// Default indent when we have to introduce indentation but have no context +/// to get it right. +let defaultIndent = TriviaPiece.spaces(4) + +@_spi(PackageRefactor) +extension Trivia { + /// Determine whether this trivia has newlines or not. + var hasNewlines: Bool { + contains(where: \.isNewline) + } +} + +/// Syntax walker to find the first occurrence of a given node kind that +/// matches a specific predicate. +private class FirstNodeFinder: SyntaxAnyVisitor { + let predicate: (Node) -> Bool + var found: Node? = nil + + init(predicate: @escaping (Node) -> Bool) { + self.predicate = predicate + super.init(viewMode: .sourceAccurate) + } + + override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { + if found != nil { + return .skipChildren + } + + if let matchedNode = node.as(Node.self), predicate(matchedNode) { + found = matchedNode + return .skipChildren + } + + return .visitChildren + } +} + +@_spi(PackageRefactor) +extension SyntaxProtocol { + /// Find the first node of the Self type that matches the given predicate. + static func findFirst( + in node: some SyntaxProtocol, + matching predicate: (Self) -> Bool + ) -> Self? { + withoutActuallyEscaping(predicate) { escapingPredicate in + let visitor = FirstNodeFinder(predicate: escapingPredicate) + visitor.walk(node) + return visitor.found + } + } +} + +@_spi(PackageRefactor) +extension FunctionCallExprSyntax { + /// Check whether this call expression has a callee that is a reference + /// to a declaration with the given name. + func hasCallee(named name: String) -> Bool { + guard let calleeDeclRef = calledExpression.as(DeclReferenceExprSyntax.self) else { + return false + } + + return calleeDeclRef.baseName.text == name + } + + /// Find a call argument based on its label. + func findArgument(labeled label: String) -> LabeledExprSyntax? { + arguments.first { $0.label?.text == label } + } + + /// Find a call argument index based on its label. + func findArgumentIndex(labeled label: String) -> LabeledExprListSyntax.Index? { + arguments.firstIndex { $0.label?.text == label } + } +} + +@_spi(PackageRefactor) +extension LabeledExprListSyntax { + /// Find the index at which the one would insert a new argument given + /// the set of argument labels that could come after the argument we + /// want to insert. + fileprivate func findArgumentInsertionPosition( + labelsAfter: Set + ) -> SyntaxChildrenIndex { + firstIndex { + guard let label = $0.label else { + return false + } + + return labelsAfter.contains(label.text) + } ?? endIndex + } + + /// Form a new argument list that inserts a new argument at the specified + /// position in this argument list. + /// + /// This operation will attempt to introduce trivia to match the + /// surrounding context where possible. The actual argument will be + /// created by the `generator` function, which is provided with leading + /// trivia and trailing comma it should use to match the surrounding + /// context. + fileprivate func insertingArgument( + at position: SyntaxChildrenIndex, + generator: (_ leadingTrivia: Trivia, _ trailingComma: TokenSyntax?) -> LabeledExprSyntax + ) -> LabeledExprListSyntax { + // Turn the arguments into an array so we can manipulate them. + var arguments = Array(self) + + let positionIdx = distance(from: startIndex, to: position) + + // Figure out leading trivia and adjust the prior argument (if there is + // one) by adding a comma, if necessary. + let leadingTrivia: Trivia + if position > startIndex { + let priorArgument = arguments[positionIdx - 1] + + // Our indentation will be based on the prior argument's. + if priorArgument.leadingTrivia.hasNewlines { + leadingTrivia = .newline + (priorArgument.firstToken(viewMode: .sourceAccurate)?.indentationOfLine ?? Trivia()) + } else { + leadingTrivia = priorArgument.leadingTrivia.indentation(isOnNewline: false) ?? Trivia() + } + + // If the prior argument is missing a trailing comma, add one. + if priorArgument.trailingComma == nil { + arguments[positionIdx - 1].trailingComma = .commaToken() + } + } else if positionIdx + 1 < count { + leadingTrivia = arguments[positionIdx + 1].leadingTrivia + } else { + leadingTrivia = Trivia() + } + + // Determine whether we need a trailing comma on this argument. + let trailingComma: TokenSyntax? + if position < endIndex { + trailingComma = .commaToken() + } else { + trailingComma = nil + } + + // Create the argument and insert it into the argument list. + let argument = generator(leadingTrivia, trailingComma) + arguments.insert(argument, at: positionIdx) + + return LabeledExprListSyntax(arguments) + } +} + +@_spi(PackageRefactor) +extension SyntaxProtocol { + /// Look for a call expression to a callee with the given name. + func findCall(calleeName: String) -> FunctionCallExprSyntax? { + return FunctionCallExprSyntax.findFirst(in: self) { call in + return call.hasCallee(named: calleeName) + } + } +} + +@_spi(PackageRefactor) +extension FunctionCallExprSyntax { + /// Find the call that forms a target with the given name in this + /// package manifest. + func findManifestTargetCall(targetName: String) throws -> FunctionCallExprSyntax { + // Dig out the array of targets. + guard let targetsArgument = findArgument(labeled: "targets"), + let targetArray = targetsArgument.expression.findArrayArgument() + else { + throw ManifestEditError.cannotFindTargets + } + + // Look for a call whose name is a string literal matching the + // requested target name. + func matchesTargetCall(call: FunctionCallExprSyntax) -> Bool { + guard let nameArgument = call.findArgument(labeled: "name") else { + return false + } + + guard let literalValue = nameArgument.expression.as(StringLiteralExprSyntax.self)?.representedLiteralValue else { + return false + } + + return literalValue == targetName + } + + guard let targetCall = FunctionCallExprSyntax.findFirst(in: targetArray, matching: matchesTargetCall) else { + throw ManifestEditError.cannotFindTarget(targetName: targetName) + } + + return targetCall + } +} + +@_spi(PackageRefactor) +extension ArrayExprSyntax { + /// Produce a new array literal expression that appends the given + /// element, while trying to maintain similar indentation. + func appending( + element: ExprSyntax, + outerLeadingTrivia: Trivia + ) -> ArrayExprSyntax { + var elements = self.elements + + // If there are already elements, tack it on. + let leadingTrivia: Trivia + let trailingTrivia: Trivia + let leftSquareTrailingTrivia: Trivia + if let last = elements.last { + // The leading indentation of the new element should match that of the + // last element. + if last.leadingTrivia.hasNewlines { + leadingTrivia = .newline + (last.firstToken(viewMode: .sourceAccurate)?.indentationOfLine ?? Trivia()) + } else { + leadingTrivia = last.leadingTrivia.indentation(isOnNewline: false) ?? Trivia() + } + + // Add a trailing comma to the last element if it isn't already + // there. + if last.trailingComma == nil { + var newElements = Array(elements) + newElements[newElements.count - 1].trailingComma = .commaToken(trailingTrivia: last.expression.trailingTrivia) + newElements[newElements.count - 1].expression.trailingTrivia = Trivia() + elements = ArrayElementListSyntax(newElements) + } + + trailingTrivia = Trivia() + leftSquareTrailingTrivia = leftSquare.trailingTrivia + } else { + leadingTrivia = outerLeadingTrivia.appending(defaultIndent) + trailingTrivia = outerLeadingTrivia + if leftSquare.trailingTrivia.hasNewlines { + leftSquareTrailingTrivia = leftSquare.trailingTrivia + } else { + leftSquareTrailingTrivia = Trivia() + } + } + + elements.append( + ArrayElementSyntax( + expression: element.with(\.leadingTrivia, leadingTrivia), + trailingComma: .commaToken().with(\.trailingTrivia, trailingTrivia) + ) + ) + + let newLeftSquare = leftSquare.with( + \.trailingTrivia, + leftSquareTrailingTrivia + ) + + return with(\.elements, elements).with(\.leftSquare, newLeftSquare) + } +} + +@_spi(PackageRefactor) +extension ExprSyntax { + /// Find an array argument either at the top level or within a sequence + /// expression. + func findArrayArgument() -> ArrayExprSyntax? { + if let arrayExpr = self.as(ArrayExprSyntax.self) { + return arrayExpr + } + + if let sequenceExpr = self.as(SequenceExprSyntax.self) { + return sequenceExpr.elements.lazy.compactMap { + $0.findArrayArgument() + }.first + } + + return nil + } +} + +// MARK: Utilities to oeprate on arrays of array literal elements. +@_spi(PackageRefactor) +extension [ArrayElementSyntax] { + /// Append a new argument expression. + mutating func append(expression: ExprSyntax) { + // Add a comma on the prior expression, if there is one. + let leadingTrivia: Trivia? + if !isEmpty { + self[count - 1].trailingComma = TokenSyntax.commaToken() + leadingTrivia = .newline + + // Adjust the first element to start with a newline + if count == 1 { + self[0].leadingTrivia = .newline + } + } else { + leadingTrivia = nil + } + + append( + ArrayElementSyntax( + leadingTrivia: leadingTrivia, + expression: expression + ) + ) + } +} + +// MARK: Utilities to operate on arrays of call arguments. + +@_spi(PackageRefactor) +extension [LabeledExprSyntax] { + /// Append a potentially labeled argument with the argument expression. + mutating func append(label: String?, expression: ExprSyntax) { + // Add a comma on the prior expression, if there is one. + let leadingTrivia: Trivia + if count > 0 { + self[count - 1].trailingComma = TokenSyntax.commaToken() + leadingTrivia = .newline + + // Adjust the first element to start with a newline + if count == 1 { + self[0].leadingTrivia = .newline + } + } else { + leadingTrivia = Trivia() + } + + // Add the new expression. + append( + LabeledExprSyntax( + label: label, + expression: expression + ).with(\.leadingTrivia, leadingTrivia) + ) + } + + /// Append a potentially labeled argument with a string literal. + mutating func append(label: String?, stringLiteral: String) { + append(label: label, expression: "\(literal: stringLiteral)") + } + + /// Append a potentially labeled argument with a string literal, but only + /// when the string literal is not nil. + mutating func appendIf(label: String?, stringLiteral: String?) { + guard let stringLiteral else { return } + append(label: label, stringLiteral: stringLiteral) + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes. + mutating func append( + label: String?, + arrayLiteral: [T] + ) where T.PreferredSyntax == ExprSyntax { + var elements: [ArrayElementSyntax] = [] + for element in arrayLiteral { + elements.append(expression: element.asSyntax()) + } + + // Figure out the trivia for the left and right square + let leftSquareTrailingTrivia: Trivia + let rightSquareLeadingTrivia: Trivia + switch elements.count { + case 0: + // Put a single space between the square brackets. + leftSquareTrailingTrivia = Trivia() + rightSquareLeadingTrivia = .space + + case 1: + // Put spaces around the single element + leftSquareTrailingTrivia = .space + rightSquareLeadingTrivia = .space + + default: + // Each of the elements will have a leading newline. Add a leading + // newline before the close bracket. + leftSquareTrailingTrivia = Trivia() + rightSquareLeadingTrivia = .newline + } + + let array = ArrayExprSyntax( + leftSquare: .leftSquareToken( + trailingTrivia: leftSquareTrailingTrivia + ), + elements: ArrayElementListSyntax(elements), + rightSquare: .rightSquareToken( + leadingTrivia: rightSquareLeadingTrivia + ) + ) + append(label: label, expression: ExprSyntax(array)) + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes. + mutating func appendIf( + label: String?, + arrayLiteral: [T]? + ) where T.PreferredSyntax == ExprSyntax { + guard let arrayLiteral else { return } + append(label: label, arrayLiteral: arrayLiteral) + } + + /// Append an array literal containing elements that can be rendered + /// into expression syntax nodes, but only if it's not empty. + mutating func appendIfNonEmpty( + label: String?, + arrayLiteral: [T] + ) where T.PreferredSyntax == ExprSyntax { + if arrayLiteral.isEmpty { return } + + append(label: label, arrayLiteral: arrayLiteral) + } +} + +// MARK: Utilities for adding arguments into calls. +private class ReplacingRewriter: SyntaxRewriter { + let childNode: Syntax + let newChildNode: Syntax + + init(childNode: Syntax, newChildNode: Syntax) { + self.childNode = childNode + self.newChildNode = newChildNode + super.init() + } + + override func visitAny(_ node: Syntax) -> Syntax? { + if node == childNode { + return newChildNode + } + + return nil + } +} + +@_spi(PackageRefactor) +fileprivate extension SyntaxProtocol { + /// Replace the given child with a new child node. + func replacingChild(_ childNode: Syntax, with newChildNode: Syntax) -> Self { + return ReplacingRewriter( + childNode: childNode, + newChildNode: newChildNode + ).rewrite(self).cast(Self.self) + } +} + +@_spi(PackageRefactor) +extension FunctionCallExprSyntax { + /// Perform source edits that will add the given new element to the + /// array for an argument with the given label (if there is one), or + /// introduce a new argument with an array literal containing only the + /// new element. + /// + /// - Parameters: + /// - label: The argument label for the argument whose array will be + /// added or modified. + /// - labelsAfter: The argument labels that could follow the label, + /// which helps determine where the argument should be inserted if + /// it doesn't exist yet. + /// - newElement: The new element. + /// - Returns: the function call after making this change. + func appendingToArrayArgument( + label: String, + labelsAfter: Set, + newElement: ExprSyntax + ) throws -> FunctionCallExprSyntax { + // If there is already an argument with this name, append to the array + // literal in there. + if let arg = findArgument(labeled: label) { + guard let argArray = arg.expression.findArrayArgument() else { + throw ManifestEditError.cannotFindArrayLiteralArgument( + argumentName: label + ) + } + + // Format the element appropriately for the context. + let indentation = Trivia( + pieces: arg.leadingTrivia.filter(\.isSpaceOrTab) + ) + let format = BasicFormat( + indentationWidth: [defaultIndent], + initialIndentation: indentation.appending(defaultIndent) + ) + let formattedElement = newElement.formatted(using: format) + .cast(ExprSyntax.self) + + let updatedArgArray = argArray.appending( + element: formattedElement, + outerLeadingTrivia: arg.leadingTrivia + ) + + return replacingChild(Syntax(argArray), with: Syntax(updatedArgArray)) + } + + // There was no argument, so we need to create one. + + // Insert the new argument at the appropriate place in the call. + let insertionPos = arguments.findArgumentInsertionPosition( + labelsAfter: labelsAfter + ) + let newArguments = arguments.insertingArgument( + at: insertionPos + ) { (leadingTrivia, trailingComma) in + // Format the element appropriately for the context. + let indentation = Trivia(pieces: leadingTrivia.filter(\.isSpaceOrTab)) + let format = BasicFormat( + indentationWidth: [defaultIndent], + initialIndentation: indentation.appending(defaultIndent) + ) + let formattedElement = newElement.formatted(using: format) + .cast(ExprSyntax.self) + + // Form the array. + let newArgument = ArrayExprSyntax( + leadingTrivia: .space, + leftSquare: .leftSquareToken(trailingTrivia: .newline), + elements: ArrayElementListSyntax( + [ + ArrayElementSyntax( + expression: formattedElement, + trailingComma: .commaToken() + ) + ] + ), + rightSquare: .rightSquareToken(leadingTrivia: leadingTrivia) + ) + + // Create the labeled argument for the array. + return LabeledExprSyntax( + leadingTrivia: leadingTrivia, + label: "\(raw: label)", + colon: .colonToken(), + expression: ExprSyntax(newArgument), + trailingComma: trailingComma + ) + } + + return with(\.arguments, newArguments) + } +} diff --git a/Sources/SwiftRefactor/RefactoringProvider.swift b/Sources/SwiftRefactor/RefactoringProvider.swift index 63bd8b1c400..d8a7781784e 100644 --- a/Sources/SwiftRefactor/RefactoringProvider.swift +++ b/Sources/SwiftRefactor/RefactoringProvider.swift @@ -16,6 +16,18 @@ public import SwiftSyntax import SwiftSyntax #endif +/// Error that refactoring actions can throw when the refactoring fails. +/// +/// The reason should start with a single lowercase character and +/// should be formatted similarly to compiler error messages. +public struct RefactoringNotApplicableError: Error, CustomStringConvertible { + public var description: String + + public init(_ reason: String) { + self.description = reason + } +} + /// A refactoring expressed as textual edits on the original syntax tree. In /// general clients should prefer `SyntaxRefactoringProvider` where possible. public protocol EditRefactoringProvider { @@ -29,10 +41,10 @@ public protocol EditRefactoringProvider { /// - Parameters: /// - syntax: The syntax to transform. /// - context: Contextual information used by the refactoring action. + /// - Throws: Throws an error if the refactoring action fails or is not applicable. /// - Returns: Textual edits that describe how to apply the result of the - /// refactoring action on locations within the original tree. An - /// empty array if the refactoring could not be performed. - static func textRefactor(syntax: Input, in context: Context) -> [SourceEdit] + /// refactoring action on locations within the original tree. + static func textRefactor(syntax: Input, in context: Context) throws -> [SourceEdit] } extension EditRefactoringProvider where Context == Void { @@ -41,9 +53,10 @@ extension EditRefactoringProvider where Context == Void { /// /// - Parameters: /// - syntax: The syntax to transform. + /// - Throws: Throws an error if the refactoring action fails or is not applicable. /// - Returns: Textual edits describing the refactoring to perform. - public static func textRefactor(syntax: Input) -> [SourceEdit] { - return self.textRefactor(syntax: syntax, in: ()) + public static func textRefactor(syntax: Input) throws -> [SourceEdit] { + return try self.textRefactor(syntax: syntax, in: ()) } } @@ -84,9 +97,9 @@ public protocol SyntaxRefactoringProvider: EditRefactoringProvider { /// - Parameters: /// - syntax: The syntax to transform. /// - context: Contextual information used by the refactoring action. - /// - Returns: The result of applying the refactoring action, or `nil` if the - /// action could not be performed. - static func refactor(syntax: Input, in context: Context) -> Output? + /// - Throws: Throws an error if the refactoring action fails or is not applicable. + /// - Returns: The result of applying the refactoring action. + static func refactor(syntax: Input, in context: Context) throws -> Output } extension SyntaxRefactoringProvider where Context == Void { @@ -95,10 +108,10 @@ extension SyntaxRefactoringProvider where Context == Void { /// /// - Parameters: /// - syntax: The syntax to transform. - /// - Returns: The result of applying the refactoring action, or `nil` if the - /// action could not be performed. - public static func refactor(syntax: Input) -> Output? { - return self.refactor(syntax: syntax, in: ()) + /// - Throws: Throws an error if the refactoring action fails or is not applicable. + /// - Returns: The result of applying the refactoring action. + public static func refactor(syntax: Input) throws -> Output { + return try self.refactor(syntax: syntax, in: ()) } } @@ -106,10 +119,8 @@ extension SyntaxRefactoringProvider { /// Provides a default implementation for /// `EditRefactoringProvider.textRefactor(syntax:in:)` that produces an edit /// to replace the input of `refactor(syntax:in:)` with its returned output. - public static func textRefactor(syntax: Input, in context: Context) -> [SourceEdit] { - guard let output = refactor(syntax: syntax, in: context) else { - return [] - } + public static func textRefactor(syntax: Input, in context: Context) throws -> [SourceEdit] { + let output = try refactor(syntax: syntax, in: context) return [SourceEdit.replace(syntax, with: output.description)] } } diff --git a/Sources/SwiftRefactor/RemoveSeparatorsFromIntegerLiteral.swift b/Sources/SwiftRefactor/RemoveSeparatorsFromIntegerLiteral.swift index 038d531ac79..ea0e1aa95d8 100644 --- a/Sources/SwiftRefactor/RemoveSeparatorsFromIntegerLiteral.swift +++ b/Sources/SwiftRefactor/RemoveSeparatorsFromIntegerLiteral.swift @@ -31,10 +31,8 @@ import SwiftSyntax /// 0xFFFFFFFFF /// ``` public struct RemoveSeparatorsFromIntegerLiteral: SyntaxRefactoringProvider { - public static func refactor(syntax lit: IntegerLiteralExprSyntax, in context: Void) -> IntegerLiteralExprSyntax? { - guard lit.literal.text.contains("_") else { - return lit - } + public static func refactor(syntax lit: IntegerLiteralExprSyntax, in context: Void) -> IntegerLiteralExprSyntax { + guard lit.literal.text.contains("_") else { return lit } let formattedText = lit.literal.text.filter({ $0 != "_" }) return lit.with(\.literal, lit.literal.with(\.tokenKind, .integerLiteral(formattedText))) } diff --git a/Sources/SwiftSyntax/AbsolutePosition.swift b/Sources/SwiftSyntax/AbsolutePosition.swift index bd2ce4efe9d..dfd49784f7b 100644 --- a/Sources/SwiftSyntax/AbsolutePosition.swift +++ b/Sources/SwiftSyntax/AbsolutePosition.swift @@ -21,11 +21,17 @@ public struct AbsolutePosition: Comparable, Hashable, Sendable { self.utf8Offset = utf8Offset } + public static func < (lhs: AbsolutePosition, rhs: AbsolutePosition) -> Bool { + return lhs.utf8Offset < rhs.utf8Offset + } +} + +extension AbsolutePosition: Strideable { public func advanced(by offset: Int) -> AbsolutePosition { - return AbsolutePosition(utf8Offset: self.utf8Offset + offset) + AbsolutePosition(utf8Offset: self.utf8Offset + offset) } - public static func < (lhs: AbsolutePosition, rhs: AbsolutePosition) -> Bool { - return lhs.utf8Offset < rhs.utf8Offset + public func distance(to other: AbsolutePosition) -> Int { + self.utf8Offset.distance(to: other.utf8Offset) } } diff --git a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md index 4ec126dbe4f..64ce006603e 100644 --- a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md +++ b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md @@ -79,6 +79,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - ### Expressions @@ -183,6 +184,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - @@ -303,6 +305,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. ### Attributes +- - - - @@ -326,6 +329,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- ### Miscellaneous Syntax @@ -358,6 +362,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - diff --git a/Sources/SwiftSyntax/Raw/RawSyntax.swift b/Sources/SwiftSyntax/Raw/RawSyntax.swift index 4f8b106e6cb..519e73902d8 100644 --- a/Sources/SwiftSyntax/Raw/RawSyntax.swift +++ b/Sources/SwiftSyntax/Raw/RawSyntax.swift @@ -227,7 +227,7 @@ public struct RawSyntax: Sendable { } internal var payload: RawSyntaxData.Payload { - get { rawData.payload } + rawData.payload } } diff --git a/Sources/SwiftSyntax/Raw/RawSyntaxArena.swift b/Sources/SwiftSyntax/Raw/RawSyntaxArena.swift index 504ba38b1e8..cb6a163b812 100644 --- a/Sources/SwiftSyntax/Raw/RawSyntaxArena.swift +++ b/Sources/SwiftSyntax/Raw/RawSyntaxArena.swift @@ -271,7 +271,7 @@ struct RawSyntaxArenaRef: Hashable, @unchecked Sendable { /// Returns the ``RawSyntaxArena`` private var value: RawSyntaxArena { - get { self._value.takeUnretainedValue() } + self._value.takeUnretainedValue() } /// Assuming that this references a `ParsingRawSyntaxArena`, diff --git a/Sources/SwiftSyntax/SourceLocation.swift b/Sources/SwiftSyntax/SourceLocation.swift index dcca9d2a74f..fb9e63ebf78 100644 --- a/Sources/SwiftSyntax/SourceLocation.swift +++ b/Sources/SwiftSyntax/SourceLocation.swift @@ -103,7 +103,7 @@ public struct SourceRange: Hashable, Codable, Sendable { } } -fileprivate struct SourceLocationDirectiveArguments { +private struct SourceLocationDirectiveArguments { enum Error: Swift.Error, CustomStringConvertible { case nonDecimalLineNumber(TokenSyntax) case stringInterpolationInFileName(SimpleStringLiteralExprSyntax) @@ -685,7 +685,7 @@ private func computeLines(tree: Syntax) -> SourceLineTable { } /// Compute ``SourceLineTable`` from a ``SyntaxText``. -fileprivate func computeLines(text: SyntaxText) -> SourceLineTable { +private func computeLines(text: SyntaxText) -> SourceLineTable { var lineEnds: [AbsolutePosition] = [] let endPos = text.forEachEndOfLine(position: .startOfFile) { pos in lineEnds.append(pos) @@ -701,7 +701,7 @@ fileprivate extension SyntaxText { /// - Returns: The position at the end of the walk. func forEachEndOfLine( position: AbsolutePosition, - body: (AbsolutePosition) -> () + body: (AbsolutePosition) -> Void ) -> AbsolutePosition { guard let startPtr = buffer.baseAddress else { return position @@ -745,7 +745,7 @@ fileprivate extension RawTriviaPiece { /// - Returns: The position at the end of the walk. func forEachEndOfLine( position: AbsolutePosition, - body: (AbsolutePosition) -> () + body: (AbsolutePosition) -> Void ) -> AbsolutePosition { var position = position switch self { @@ -788,7 +788,7 @@ fileprivate extension RawTriviaPieceBuffer { /// - Returns: The position at the end of the walk. func forEachEndOfLine( position: AbsolutePosition, - body: (AbsolutePosition) -> () + body: (AbsolutePosition) -> Void ) -> AbsolutePosition { var position = position for piece in self { @@ -810,8 +810,8 @@ fileprivate extension RawSyntax { /// - Returns: The position at the end of the walk. func forEachEndOfLine( position: AbsolutePosition, - body: (AbsolutePosition) -> (), - handleSourceLocationDirective: (_ position: AbsolutePosition, _ rawSyntax: RawSyntax) -> () + body: (AbsolutePosition) -> Void, + handleSourceLocationDirective: (_ position: AbsolutePosition, _ rawSyntax: RawSyntax) -> Void ) -> AbsolutePosition { var position = position switch self.rawData.payload { diff --git a/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift b/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift index a7333a7838d..3adfb33fbd0 100644 --- a/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift +++ b/Sources/SwiftSyntax/SwiftSyntaxCompatibility.swift @@ -405,6 +405,21 @@ extension GenericArgumentSyntax { } } +extension FreestandingMacroExpansionSyntax { + // SPI protocol requirements require a default implementation, but this should never be used because there should be + // no conforming types outside of SwiftSyntax. + @_spi(ExperimentalLanguageFeatures) + @available(*, deprecated, message: "concrete type should implement its own moduleSelector property") + public var moduleSelector: ModuleSelectorSyntax? { + get { + return nil + } + set { + fatalError("\(Self.self) should implement its own moduleSelector property") + } + } +} + extension FunctionCallExprSyntax { @_disfavoredOverload @available( @@ -705,6 +720,18 @@ extension KeyPathPropertyComponentSyntax { } } +extension MemberTypeSyntax { + @available(*, deprecated, renamed: "unexpectedBetweenModuleSelectorAndName") + public var unexpectedBeforeName: UnexpectedNodesSyntax? { + get { + return unexpectedBetweenModuleSelectorAndName + } + set(value) { + unexpectedBetweenModuleSelectorAndName = value + } + } +} + extension NamedDeclSyntax { @available(*, deprecated, renamed: "name") public var identifier: TokenSyntax { diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 802b6ae8003..db537fb970b 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -286,8 +286,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenSpecifiersAndAttributes" case \AttributedTypeSyntax.attributes: return "attributes" - case \AttributedTypeSyntax.unexpectedBetweenAttributesAndBaseType: - return "unexpectedBetweenAttributesAndBaseType" + case \AttributedTypeSyntax.unexpectedBetweenAttributesAndLateSpecifiers: + return "unexpectedBetweenAttributesAndLateSpecifiers" + case \AttributedTypeSyntax.lateSpecifiers: + return "lateSpecifiers" + case \AttributedTypeSyntax.unexpectedBetweenLateSpecifiersAndBaseType: + return "unexpectedBetweenLateSpecifiersAndBaseType" case \AttributedTypeSyntax.baseType: return "baseType" case \AttributedTypeSyntax.unexpectedAfterBaseType: @@ -820,8 +824,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "rightParen" case \DeclNameArgumentsSyntax.unexpectedAfterRightParen: return "unexpectedAfterRightParen" - case \DeclReferenceExprSyntax.unexpectedBeforeBaseName: - return "unexpectedBeforeBaseName" + case \DeclReferenceExprSyntax.unexpectedBeforeModuleSelector: + return "unexpectedBeforeModuleSelector" + case \DeclReferenceExprSyntax.moduleSelector: + return "moduleSelector" + case \DeclReferenceExprSyntax.unexpectedBetweenModuleSelectorAndBaseName: + return "unexpectedBetweenModuleSelectorAndBaseName" case \DeclReferenceExprSyntax.baseName: return "baseName" case \DeclReferenceExprSyntax.unexpectedBetweenBaseNameAndArgumentNames: @@ -1648,8 +1656,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "identifier" case \IdentifierPatternSyntax.unexpectedAfterIdentifier: return "unexpectedAfterIdentifier" - case \IdentifierTypeSyntax.unexpectedBeforeName: - return "unexpectedBeforeName" + case \IdentifierTypeSyntax.unexpectedBeforeModuleSelector: + return "unexpectedBeforeModuleSelector" + case \IdentifierTypeSyntax.moduleSelector: + return "moduleSelector" + case \IdentifierTypeSyntax.unexpectedBetweenModuleSelectorAndName: + return "unexpectedBetweenModuleSelectorAndName" case \IdentifierTypeSyntax.name: return "name" case \IdentifierTypeSyntax.unexpectedBetweenNameAndGenericArgumentClause: @@ -2134,8 +2146,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenModifiersAndPound" case \MacroExpansionDeclSyntax.pound: return "pound" - case \MacroExpansionDeclSyntax.unexpectedBetweenPoundAndMacroName: - return "unexpectedBetweenPoundAndMacroName" + case \MacroExpansionDeclSyntax.unexpectedBetweenPoundAndModuleSelector: + return "unexpectedBetweenPoundAndModuleSelector" + case \MacroExpansionDeclSyntax.moduleSelector: + return "moduleSelector" + case \MacroExpansionDeclSyntax.unexpectedBetweenModuleSelectorAndMacroName: + return "unexpectedBetweenModuleSelectorAndMacroName" case \MacroExpansionDeclSyntax.macroName: return "macroName" case \MacroExpansionDeclSyntax.unexpectedBetweenMacroNameAndGenericArgumentClause: @@ -2168,8 +2184,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBeforePound" case \MacroExpansionExprSyntax.pound: return "pound" - case \MacroExpansionExprSyntax.unexpectedBetweenPoundAndMacroName: - return "unexpectedBetweenPoundAndMacroName" + case \MacroExpansionExprSyntax.unexpectedBetweenPoundAndModuleSelector: + return "unexpectedBetweenPoundAndModuleSelector" + case \MacroExpansionExprSyntax.moduleSelector: + return "moduleSelector" + case \MacroExpansionExprSyntax.unexpectedBetweenModuleSelectorAndMacroName: + return "unexpectedBetweenModuleSelectorAndMacroName" case \MacroExpansionExprSyntax.macroName: return "macroName" case \MacroExpansionExprSyntax.unexpectedBetweenMacroNameAndGenericArgumentClause: @@ -2272,8 +2292,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenBaseTypeAndPeriod" case \MemberTypeSyntax.period: return "period" - case \MemberTypeSyntax.unexpectedBetweenPeriodAndName: - return "unexpectedBetweenPeriodAndName" + case \MemberTypeSyntax.unexpectedBetweenPeriodAndModuleSelector: + return "unexpectedBetweenPeriodAndModuleSelector" + case \MemberTypeSyntax.moduleSelector: + return "moduleSelector" + case \MemberTypeSyntax.unexpectedBetweenModuleSelectorAndName: + return "unexpectedBetweenModuleSelectorAndName" case \MemberTypeSyntax.name: return "name" case \MemberTypeSyntax.unexpectedBetweenNameAndGenericArgumentClause: @@ -2340,6 +2364,16 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "placeholder" case \MissingTypeSyntax.unexpectedAfterPlaceholder: return "unexpectedAfterPlaceholder" + case \ModuleSelectorSyntax.unexpectedBeforeModuleName: + return "unexpectedBeforeModuleName" + case \ModuleSelectorSyntax.moduleName: + return "moduleName" + case \ModuleSelectorSyntax.unexpectedBetweenModuleNameAndColonColon: + return "unexpectedBetweenModuleNameAndColonColon" + case \ModuleSelectorSyntax.colonColon: + return "colonColon" + case \ModuleSelectorSyntax.unexpectedAfterColonColon: + return "unexpectedAfterColonColon" case \MultipleTrailingClosureElementSyntax.unexpectedBeforeLabel: return "unexpectedBeforeLabel" case \MultipleTrailingClosureElementSyntax.label: @@ -2962,6 +2996,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "trailingComma" case \SpecializeTargetFunctionArgumentSyntax.unexpectedAfterTrailingComma: return "unexpectedAfterTrailingComma" + case \SpecializedAttributeArgumentSyntax.unexpectedBeforeGenericWhereClause: + return "unexpectedBeforeGenericWhereClause" + case \SpecializedAttributeArgumentSyntax.genericWhereClause: + return "genericWhereClause" + case \SpecializedAttributeArgumentSyntax.unexpectedAfterGenericWhereClause: + return "unexpectedAfterGenericWhereClause" case \StringLiteralExprSyntax.unexpectedBeforeOpeningPounds: return "unexpectedBeforeOpeningPounds" case \StringLiteralExprSyntax.openingPounds: @@ -3404,6 +3444,8 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "value" case \TypeInitializerClauseSyntax.unexpectedAfterValue: return "unexpectedAfterValue" + case \UnexpectedCodeDeclSyntax.unexpectedCode: + return "unexpectedCode" case \UnresolvedAsExprSyntax.unexpectedBeforeAsKeyword: return "unexpectedBeforeAsKeyword" case \UnresolvedAsExprSyntax.asKeyword: @@ -3444,6 +3486,16 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "expression" case \UnsafeExprSyntax.unexpectedAfterExpression: return "unexpectedAfterExpression" + case \UsingDeclSyntax.unexpectedBeforeUsingKeyword: + return "unexpectedBeforeUsingKeyword" + case \UsingDeclSyntax.usingKeyword: + return "usingKeyword" + case \UsingDeclSyntax.unexpectedBetweenUsingKeywordAndSpecifier: + return "unexpectedBetweenUsingKeywordAndSpecifier" + case \UsingDeclSyntax.specifier: + return "specifier" + case \UsingDeclSyntax.unexpectedAfterSpecifier: + return "unexpectedAfterSpecifier" case \ValueBindingPatternSyntax.unexpectedBeforeBindingSpecifier: return "unexpectedBeforeBindingSpecifier" case \ValueBindingPatternSyntax.bindingSpecifier: diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index 20b36ff1455..31007c1ade1 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -46,6 +46,7 @@ public enum Keyword: UInt8, Hashable, Sendable { case _PackageDescription case _read case _RefCountedObject + case specialized case _specialize case _spi_available case _Trivial @@ -54,7 +55,6 @@ public enum Keyword: UInt8, Hashable, Sendable { case _underlyingVersion case _UnknownLayout case _version - @_spi(ExperimentalLanguageFeatures) case abi case accesses case actor @@ -145,6 +145,8 @@ public enum Keyword: UInt8, Hashable, Sendable { @_spi(ExperimentalLanguageFeatures) case modify case module + @_spi(ExperimentalLanguageFeatures) + case mutate case mutableAddressWithNativeOwner case mutableAddressWithOwner case mutating @@ -215,6 +217,7 @@ public enum Keyword: UInt8, Hashable, Sendable { case unsafe case unsafeAddress case unsafeMutableAddress + case using case `var` case visibility case weak @@ -222,533 +225,618 @@ public enum Keyword: UInt8, Hashable, Sendable { case `while` case willSet case wrt - @_spi(ExperimentalLanguageFeatures) - case x case yield @_spi(RawSyntax) public init?(_ text: SyntaxText) { switch text.count { - case 1: - switch text { - case "x": - self = .x - default: - return nil - } case 2: - switch text { - case "as": - self = .as - case "do": - self = .do - case "if": - self = .if - case "in": - self = .in - case "is": - self = .is - case "of": - self = .of - default: - return nil - } + self.init(_length2: text) case 3: - switch text { - case "abi": - self = .abi - case "any": - self = .any - case "Any": - self = .Any - case "for": - self = .for - case "get": - self = .get - case "let": - self = .let - case "nil": - self = .nil - case "set": - self = .set - case "spi": - self = .spi - case "try": - self = .try - case "var": - self = .var - case "wrt": - self = .wrt - default: - return nil - } + self.init(_length3: text) case 4: - switch text { - case "case": - self = .case - case "copy": - self = .copy - case "each": - self = .each - case "else": - self = .else - case "enum": - self = .enum - case "file": - self = .file - case "func": - self = .func - case "init": - self = .`init` - case "kind": - self = .kind - case "lazy": - self = .lazy - case "left": - self = .left - case "line": - self = .line - case "none": - self = .none - case "objc": - self = .objc - case "open": - self = .open - case "read": - self = .read - case "safe": - self = .safe - case "self": - self = .self - case "Self": - self = .Self - case "some": - self = .some - case "then": - self = .then - case "true": - self = .true - case "Type": - self = .Type - case "weak": - self = .weak - default: - return nil - } + self.init(_length4: text) case 5: - switch text { - case "_move": - self = ._move - case "_read": - self = ._read - case "actor": - self = .actor - case "async": - self = .async - case "await": - self = .await - case "block": - self = .block - case "break": - self = .break - case "catch": - self = .catch - case "class": - self = .class - case "defer": - self = .defer - case "false": - self = .false - case "final": - self = .final - case "guard": - self = .guard - case "infix": - self = .infix - case "inout": - self = .inout - case "macro": - self = .macro - case "right": - self = .right - case "super": - self = .super - case "swift": - self = .swift - case "throw": - self = .throw - case "where": - self = .where - case "while": - self = .while - case "yield": - self = .yield - default: - return nil - } + self.init(_length5: text) case 6: - switch text { - case "_Class": - self = ._Class - case "_const": - self = ._const - case "_local": - self = ._local - case "before": - self = .before - case "borrow": - self = .borrow - case "deinit": - self = .deinit - case "didSet": - self = .didSet - case "import": - self = .import - case "linear": - self = .linear - case "modify": - self = .modify - case "module": - self = .module - case "prefix": - self = .prefix - case "public": - self = .public - case "repeat": - self = .repeat - case "return": - self = .return - case "scoped": - self = .scoped - case "static": - self = .static - case "struct": - self = .struct - case "switch": - self = .switch - case "target": - self = .target - case "throws": - self = .throws - case "unsafe": - self = .unsafe - default: - return nil - } + self.init(_length6: text) case 7: - switch text { - case "__owned": - self = .__owned - case "_borrow": - self = ._borrow - case "_linear": - self = ._linear - case "_modify": - self = ._modify - case "consume": - self = .consume - case "default": - self = .default - case "dynamic": - self = .dynamic - case "discard": - self = .discard - case "forward": - self = .forward - case "message": - self = .message - case "noasync": - self = .noasync - case "package": - self = .package - case "postfix": - self = .postfix - case "private": - self = .private - case "reasync": - self = .reasync - case "renamed": - self = .renamed - case "reverse": - self = .reverse - case "sending": - self = .sending - case "unowned": - self = .unowned - case "willSet": - self = .willSet - default: - return nil - } + self.init(_length7: text) case 8: - switch text { - case "__shared": - self = .__shared - case "_effects": - self = ._effects - case "_forward": - self = ._forward - case "_Trivial": - self = ._Trivial - case "_version": - self = ._version - case "accesses": - self = .accesses - case "attached": - self = .attached - case "compiler": - self = .compiler - case "continue": - self = .continue - case "escaping": - self = .escaping - case "exported": - self = .exported - case "indirect": - self = .indirect - case "internal": - self = .internal - case "isolated": - self = .isolated - case "metadata": - self = .metadata - case "mutating": - self = .mutating - case "noescape": - self = .noescape - case "operator": - self = .operator - case "optional": - self = .optional - case "override": - self = .override - case "Protocol": - self = .Protocol - case "protocol": - self = .protocol - case "required": - self = .required - case "rethrows": - self = .rethrows - case "Sendable": - self = .Sendable - default: - return nil - } + self.init(_length8: text) case 9: - switch text { - case "_mutating": - self = ._mutating - case "available": - self = .available - case "borrowing": - self = .borrowing - case "canImport": - self = .canImport - case "consuming": - self = .consuming - case "dependsOn": - self = .dependsOn - case "extension": - self = .extension - case "lowerThan": - self = .lowerThan - case "obsoleted": - self = .obsoleted - case "spiModule": - self = .spiModule - case "subscript": - self = .subscript - case "transpose": - self = .transpose - case "typealias": - self = .typealias - case "unchecked": - self = .unchecked - default: - return nil - } + self.init(_length9: text) case 10: - switch text { - case "_borrowing": - self = ._borrowing - case "_consuming": - self = ._consuming - case "assignment": - self = .assignment - case "convention": - self = .convention - case "deprecated": - self = .deprecated - case "derivative": - self = .derivative - case "higherThan": - self = .higherThan - case "introduced": - self = .introduced - case "nonsending": - self = .nonsending - case "visibility": - self = .visibility - default: - return nil - } + self.init(_length10: text) case 11: - switch text { - case "__consuming": - self = .__consuming - case "_backDeploy": - self = ._backDeploy - case "_implements": - self = ._implements - case "_noMetadata": - self = ._noMetadata - case "_specialize": - self = ._specialize - case "autoclosure": - self = .autoclosure - case "convenience": - self = .convenience - case "distributed": - self = .distributed - case "fallthrough": - self = .fallthrough - case "fileprivate": - self = .fileprivate - case "initializes": - self = .initializes - case "nonisolated": - self = .nonisolated - case "nonmutating": - self = .nonmutating - case "retroactive": - self = .retroactive - case "unavailable": - self = .unavailable - default: - return nil - } + self.init(_length11: text) case 12: - switch text { - case "_NativeClass": - self = ._NativeClass - case "availability": - self = .availability - case "backDeployed": - self = .backDeployed - case "freestanding": - self = .freestanding - case "noDerivative": - self = .noDerivative - default: - return nil - } + self.init(_length12: text) case 13: - switch text { - case "_BridgeObject": - self = ._BridgeObject - case "associativity": - self = .associativity - case "unsafeAddress": - self = .unsafeAddress - default: - return nil - } + self.init(_length13: text) case 14: - switch text { - case "_documentation": - self = ._documentation - case "_spi_available": - self = ._spi_available - case "_TrivialAtMost": - self = ._TrivialAtMost - case "_TrivialStride": - self = ._TrivialStride - case "_UnknownLayout": - self = ._UnknownLayout - case "associatedtype": - self = .associatedtype - case "differentiable": - self = .differentiable - case "preconcurrency": - self = .preconcurrency - default: - return nil - } + self.init(_length14: text) case 15: - switch text { - case "__setter_access": - self = .__setter_access - case "precedencegroup": - self = .precedencegroup - default: - return nil - } + self.init(_length15: text) case 16: - switch text { - case "addressWithOwner": - self = .addressWithOwner - default: - return nil - } + self.init(_length16: text) case 17: - switch text { - case "_RefCountedObject": - self = ._RefCountedObject - default: - return nil - } + self.init(_length17: text) case 18: - switch text { - case "_underlyingVersion": - self = ._underlyingVersion - default: - return nil - } + self.init(_length18: text) case 19: - switch text { - case "_dynamicReplacement": - self = ._dynamicReplacement - case "_opaqueReturnTypeOf": - self = ._opaqueReturnTypeOf - case "_PackageDescription": - self = ._PackageDescription - default: - return nil - } + self.init(_length19: text) case 20: - switch text { - case "_compilerInitialized": - self = ._compilerInitialized - case "_originallyDefinedIn": - self = ._originallyDefinedIn - case "unsafeMutableAddress": - self = .unsafeMutableAddress - default: - return nil - } + self.init(_length20: text) case 22: - switch text { - case "addressWithNativeOwner": - self = .addressWithNativeOwner - default: - return nil - } + self.init(_length22: text) case 23: - switch text { - case "_NativeRefCountedObject": - self = ._NativeRefCountedObject - case "mutableAddressWithOwner": - self = .mutableAddressWithOwner - default: - return nil - } + self.init(_length23: text) case 29: - switch text { - case "mutableAddressWithNativeOwner": - self = .mutableAddressWithNativeOwner - default: - return nil - } + self.init(_length29: text) + default: + return nil + } + } + + private init?(_length2 text: SyntaxText) { + switch text { + case "as": + self = .as + case "do": + self = .do + case "if": + self = .if + case "in": + self = .in + case "is": + self = .is + case "of": + self = .of + default: + return nil + } + } + + private init?(_length3 text: SyntaxText) { + switch text { + case "abi": + self = .abi + case "any": + self = .any + case "Any": + self = .Any + case "for": + self = .for + case "get": + self = .get + case "let": + self = .let + case "nil": + self = .nil + case "set": + self = .set + case "spi": + self = .spi + case "try": + self = .try + case "var": + self = .var + case "wrt": + self = .wrt + default: + return nil + } + } + + private init?(_length4 text: SyntaxText) { + switch text { + case "case": + self = .case + case "copy": + self = .copy + case "each": + self = .each + case "else": + self = .else + case "enum": + self = .enum + case "file": + self = .file + case "func": + self = .func + case "init": + self = .`init` + case "kind": + self = .kind + case "lazy": + self = .lazy + case "left": + self = .left + case "line": + self = .line + case "none": + self = .none + case "objc": + self = .objc + case "open": + self = .open + case "read": + self = .read + case "safe": + self = .safe + case "self": + self = .self + case "Self": + self = .Self + case "some": + self = .some + case "then": + self = .then + case "true": + self = .true + case "Type": + self = .Type + case "weak": + self = .weak + default: + return nil + } + } + + private init?(_length5 text: SyntaxText) { + switch text { + case "_move": + self = ._move + case "_read": + self = ._read + case "actor": + self = .actor + case "async": + self = .async + case "await": + self = .await + case "block": + self = .block + case "break": + self = .break + case "catch": + self = .catch + case "class": + self = .class + case "defer": + self = .defer + case "false": + self = .false + case "final": + self = .final + case "guard": + self = .guard + case "infix": + self = .infix + case "inout": + self = .inout + case "macro": + self = .macro + case "right": + self = .right + case "super": + self = .super + case "swift": + self = .swift + case "throw": + self = .throw + case "using": + self = .using + case "where": + self = .where + case "while": + self = .while + case "yield": + self = .yield + default: + return nil + } + } + + private init?(_length6 text: SyntaxText) { + switch text { + case "_Class": + self = ._Class + case "_const": + self = ._const + case "_local": + self = ._local + case "before": + self = .before + case "borrow": + self = .borrow + case "deinit": + self = .deinit + case "didSet": + self = .didSet + case "import": + self = .import + case "linear": + self = .linear + case "modify": + self = .modify + case "module": + self = .module + case "mutate": + self = .mutate + case "prefix": + self = .prefix + case "public": + self = .public + case "repeat": + self = .repeat + case "return": + self = .return + case "scoped": + self = .scoped + case "static": + self = .static + case "struct": + self = .struct + case "switch": + self = .switch + case "target": + self = .target + case "throws": + self = .throws + case "unsafe": + self = .unsafe + default: + return nil + } + } + + private init?(_length7 text: SyntaxText) { + switch text { + case "__owned": + self = .__owned + case "_borrow": + self = ._borrow + case "_linear": + self = ._linear + case "_modify": + self = ._modify + case "consume": + self = .consume + case "default": + self = .default + case "dynamic": + self = .dynamic + case "discard": + self = .discard + case "forward": + self = .forward + case "message": + self = .message + case "noasync": + self = .noasync + case "package": + self = .package + case "postfix": + self = .postfix + case "private": + self = .private + case "reasync": + self = .reasync + case "renamed": + self = .renamed + case "reverse": + self = .reverse + case "sending": + self = .sending + case "unowned": + self = .unowned + case "willSet": + self = .willSet + default: + return nil + } + } + + private init?(_length8 text: SyntaxText) { + switch text { + case "__shared": + self = .__shared + case "_effects": + self = ._effects + case "_forward": + self = ._forward + case "_Trivial": + self = ._Trivial + case "_version": + self = ._version + case "accesses": + self = .accesses + case "attached": + self = .attached + case "compiler": + self = .compiler + case "continue": + self = .continue + case "escaping": + self = .escaping + case "exported": + self = .exported + case "indirect": + self = .indirect + case "internal": + self = .internal + case "isolated": + self = .isolated + case "metadata": + self = .metadata + case "mutating": + self = .mutating + case "noescape": + self = .noescape + case "operator": + self = .operator + case "optional": + self = .optional + case "override": + self = .override + case "Protocol": + self = .Protocol + case "protocol": + self = .protocol + case "required": + self = .required + case "rethrows": + self = .rethrows + case "Sendable": + self = .Sendable + default: + return nil + } + } + + private init?(_length9 text: SyntaxText) { + switch text { + case "_mutating": + self = ._mutating + case "available": + self = .available + case "borrowing": + self = .borrowing + case "canImport": + self = .canImport + case "consuming": + self = .consuming + case "dependsOn": + self = .dependsOn + case "extension": + self = .extension + case "lowerThan": + self = .lowerThan + case "obsoleted": + self = .obsoleted + case "spiModule": + self = .spiModule + case "subscript": + self = .subscript + case "transpose": + self = .transpose + case "typealias": + self = .typealias + case "unchecked": + self = .unchecked + default: + return nil + } + } + + private init?(_length10 text: SyntaxText) { + switch text { + case "_borrowing": + self = ._borrowing + case "_consuming": + self = ._consuming + case "assignment": + self = .assignment + case "convention": + self = .convention + case "deprecated": + self = .deprecated + case "derivative": + self = .derivative + case "higherThan": + self = .higherThan + case "introduced": + self = .introduced + case "nonsending": + self = .nonsending + case "visibility": + self = .visibility + default: + return nil + } + } + + private init?(_length11 text: SyntaxText) { + switch text { + case "__consuming": + self = .__consuming + case "_backDeploy": + self = ._backDeploy + case "_implements": + self = ._implements + case "_noMetadata": + self = ._noMetadata + case "specialized": + self = .specialized + case "_specialize": + self = ._specialize + case "autoclosure": + self = .autoclosure + case "convenience": + self = .convenience + case "distributed": + self = .distributed + case "fallthrough": + self = .fallthrough + case "fileprivate": + self = .fileprivate + case "initializes": + self = .initializes + case "nonisolated": + self = .nonisolated + case "nonmutating": + self = .nonmutating + case "retroactive": + self = .retroactive + case "unavailable": + self = .unavailable + default: + return nil + } + } + + private init?(_length12 text: SyntaxText) { + switch text { + case "_NativeClass": + self = ._NativeClass + case "availability": + self = .availability + case "backDeployed": + self = .backDeployed + case "freestanding": + self = .freestanding + case "noDerivative": + self = .noDerivative + default: + return nil + } + } + + private init?(_length13 text: SyntaxText) { + switch text { + case "_BridgeObject": + self = ._BridgeObject + case "associativity": + self = .associativity + case "unsafeAddress": + self = .unsafeAddress + default: + return nil + } + } + + private init?(_length14 text: SyntaxText) { + switch text { + case "_documentation": + self = ._documentation + case "_spi_available": + self = ._spi_available + case "_TrivialAtMost": + self = ._TrivialAtMost + case "_TrivialStride": + self = ._TrivialStride + case "_UnknownLayout": + self = ._UnknownLayout + case "associatedtype": + self = .associatedtype + case "differentiable": + self = .differentiable + case "preconcurrency": + self = .preconcurrency + default: + return nil + } + } + + private init?(_length15 text: SyntaxText) { + switch text { + case "__setter_access": + self = .__setter_access + case "precedencegroup": + self = .precedencegroup + default: + return nil + } + } + + private init?(_length16 text: SyntaxText) { + switch text { + case "addressWithOwner": + self = .addressWithOwner + default: + return nil + } + } + + private init?(_length17 text: SyntaxText) { + switch text { + case "_RefCountedObject": + self = ._RefCountedObject + default: + return nil + } + } + + private init?(_length18 text: SyntaxText) { + switch text { + case "_underlyingVersion": + self = ._underlyingVersion + default: + return nil + } + } + + private init?(_length19 text: SyntaxText) { + switch text { + case "_dynamicReplacement": + self = ._dynamicReplacement + case "_opaqueReturnTypeOf": + self = ._opaqueReturnTypeOf + case "_PackageDescription": + self = ._PackageDescription + default: + return nil + } + } + + private init?(_length20 text: SyntaxText) { + switch text { + case "_compilerInitialized": + self = ._compilerInitialized + case "_originallyDefinedIn": + self = ._originallyDefinedIn + case "unsafeMutableAddress": + self = .unsafeMutableAddress + default: + return nil + } + } + + private init?(_length22 text: SyntaxText) { + switch text { + case "addressWithNativeOwner": + self = .addressWithNativeOwner + default: + return nil + } + } + + private init?(_length23 text: SyntaxText) { + switch text { + case "_NativeRefCountedObject": + self = ._NativeRefCountedObject + case "mutableAddressWithOwner": + self = .mutableAddressWithOwner + default: + return nil + } + } + + private init?(_length29 text: SyntaxText) { + switch text { + case "mutableAddressWithNativeOwner": + self = .mutableAddressWithNativeOwner default: return nil } @@ -790,6 +878,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "_PackageDescription", "_read", "_RefCountedObject", + "specialized", "_specialize", "_spi_available", "_Trivial", @@ -886,6 +975,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "metadata", "modify", "module", + "mutate", "mutableAddressWithNativeOwner", "mutableAddressWithOwner", "mutating", @@ -954,6 +1044,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "unsafe", "unsafeAddress", "unsafeMutableAddress", + "using", "var", "visibility", "weak", @@ -961,7 +1052,6 @@ public enum Keyword: UInt8, Hashable, Sendable { "while", "willSet", "wrt", - "x", "yield", ] diff --git a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift index 88518daf105..1d82e4cbad3 100644 --- a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift +++ b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift @@ -1473,13 +1473,22 @@ extension ConsumeExprSyntax { } extension DeclReferenceExprSyntax { - @available(*, deprecated, renamed: "unexpectedBeforeBaseName") + public var unexpectedBeforeBaseName: UnexpectedNodesSyntax? { + get { + return unexpectedBeforeModuleSelector + } + set { + unexpectedBeforeModuleSelector = newValue + } + } + + @available(*, deprecated, renamed: "unexpectedBeforeModuleSelector") public var unexpectedBeforeIdentifier: UnexpectedNodesSyntax? { get { - return unexpectedBeforeBaseName + return unexpectedBeforeModuleSelector } set { - unexpectedBeforeBaseName = newValue + unexpectedBeforeModuleSelector = newValue } } @@ -1523,7 +1532,29 @@ extension DeclReferenceExprSyntax { } } - @available(*, deprecated, renamed: "init(leadingTrivia:_:baseName:_:argumentNames:_:trailingTrivia:)") + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeBaseName: UnexpectedNodesSyntax? = nil, + baseName: TokenSyntax, + _ unexpectedBetweenBaseNameAndArgumentNames: UnexpectedNodesSyntax? = nil, + argumentNames: DeclNameArgumentsSyntax? = nil, + _ unexpectedAfterArgumentNames: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeBaseName, + moduleSelector: nil, + nil, + baseName: baseName, + unexpectedBetweenBaseNameAndArgumentNames, + argumentNames: argumentNames, + unexpectedAfterArgumentNames, + trailingTrivia: trailingTrivia + ) + } + + @available(*, deprecated, renamed: "init(leadingTrivia:_:moduleSelector:_:baseName:_:argumentNames:_:trailingTrivia:)") @_disfavoredOverload public init( leadingTrivia: Trivia? = nil, @@ -1537,6 +1568,8 @@ extension DeclReferenceExprSyntax { self.init( leadingTrivia: leadingTrivia, unexpectedBeforeIdentifier, + moduleSelector: nil, + nil, baseName: identifier, unexpectedBetweenIdentifierAndDeclNameArguments, argumentNames: declNameArguments, @@ -3792,6 +3825,39 @@ extension GenericWhereClauseSyntax { } } +extension IdentifierTypeSyntax { + public var unexpectedBeforeName: UnexpectedNodesSyntax? { + get { + return unexpectedBeforeModuleSelector + } + set { + unexpectedBeforeModuleSelector = newValue + } + } + + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeName: UnexpectedNodesSyntax? = nil, + name: TokenSyntax, + _ unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + _ unexpectedAfterGenericArgumentClause: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeName, + moduleSelector: nil, + nil, + name: name, + unexpectedBetweenNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedAfterGenericArgumentClause, + trailingTrivia: trailingTrivia + ) + } +} + extension ImplementsAttributeArgumentsSyntax { @available(*, deprecated, renamed: "unexpectedBetweenCommaAndDeclName") public var unexpectedBetweenCommaAndDeclname: UnexpectedNodesSyntax? { @@ -4647,6 +4713,15 @@ extension MacroDeclSyntax { } extension MacroExpansionDeclSyntax { + public var unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? { + get { + return unexpectedBetweenPoundAndModuleSelector + } + set { + unexpectedBetweenPoundAndModuleSelector = newValue + } + } + @available(*, deprecated, renamed: "unexpectedBetweenModifiersAndPound") public var unexpectedBetweenModifiersAndPoundToken: UnexpectedNodesSyntax? { get { @@ -4667,13 +4742,13 @@ extension MacroExpansionDeclSyntax { } } - @available(*, deprecated, renamed: "unexpectedBetweenPoundAndMacroName") + @available(*, deprecated, renamed: "unexpectedBetweenPoundAndModuleSelector") public var unexpectedBetweenPoundTokenAndMacro: UnexpectedNodesSyntax? { get { - return unexpectedBetweenPoundAndMacroName + return unexpectedBetweenPoundAndModuleSelector } set { - unexpectedBetweenPoundAndMacroName = newValue + unexpectedBetweenPoundAndModuleSelector = newValue } } @@ -4747,7 +4822,61 @@ extension MacroExpansionDeclSyntax { } } - @available(*, deprecated, renamed: "init(leadingTrivia:_:attributes:_:modifiers:_:pound:_:macroName:_:genericArgumentClause:_:leftParen:_:arguments:_:rightParen:_:trailingClosure:_:additionalTrailingClosures:_:trailingTrivia:)") + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeAttributes: UnexpectedNodesSyntax? = nil, + attributes: AttributeListSyntax = [], + _ unexpectedBetweenAttributesAndModifiers: UnexpectedNodesSyntax? = nil, + modifiers: DeclModifierListSyntax = [], + _ unexpectedBetweenModifiersAndPound: UnexpectedNodesSyntax? = nil, + pound: TokenSyntax = .poundToken(), + _ unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + macroName: TokenSyntax, + _ unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + _ unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + _ unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + arguments: LabeledExprListSyntax, + _ unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + _ unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + _ unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + _ unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeAttributes, + attributes: attributes, + unexpectedBetweenAttributesAndModifiers, + modifiers: modifiers, + unexpectedBetweenModifiersAndPound, + pound: pound, + unexpectedBetweenPoundAndMacroName, + moduleSelector: nil, + nil, + macroName: macroName, + unexpectedBetweenMacroNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedBetweenGenericArgumentClauseAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: arguments, + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @available(*, deprecated, renamed: "init(leadingTrivia:_:attributes:_:modifiers:_:pound:_:moduleSelector:_:macroName:_:genericArgumentClause:_:leftParen:_:arguments:_:rightParen:_:trailingClosure:_:additionalTrailingClosures:_:trailingTrivia:)") @_disfavoredOverload public init( leadingTrivia: Trivia? = nil, @@ -4783,6 +4912,8 @@ extension MacroExpansionDeclSyntax { unexpectedBetweenModifiersAndPoundToken, pound: poundToken, unexpectedBetweenPoundTokenAndMacro, + moduleSelector: nil, + nil, macroName: macro, unexpectedBetweenMacroAndGenericArguments, genericArgumentClause: genericArguments, @@ -4803,6 +4934,15 @@ extension MacroExpansionDeclSyntax { } extension MacroExpansionExprSyntax { + public var unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? { + get { + return unexpectedBetweenPoundAndModuleSelector + } + set { + unexpectedBetweenPoundAndModuleSelector = newValue + } + } + @available(*, deprecated, renamed: "unexpectedBeforePound") public var unexpectedBeforePoundToken: UnexpectedNodesSyntax? { get { @@ -4823,13 +4963,13 @@ extension MacroExpansionExprSyntax { } } - @available(*, deprecated, renamed: "unexpectedBetweenPoundAndMacroName") + @available(*, deprecated, renamed: "unexpectedBetweenPoundAndModuleSelector") public var unexpectedBetweenPoundTokenAndMacro: UnexpectedNodesSyntax? { get { - return unexpectedBetweenPoundAndMacroName + return unexpectedBetweenPoundAndModuleSelector } set { - unexpectedBetweenPoundAndMacroName = newValue + unexpectedBetweenPoundAndModuleSelector = newValue } } @@ -4903,7 +5043,53 @@ extension MacroExpansionExprSyntax { } } - @available(*, deprecated, renamed: "init(leadingTrivia:_:pound:_:macroName:_:genericArgumentClause:_:leftParen:_:arguments:_:rightParen:_:trailingClosure:_:additionalTrailingClosures:_:trailingTrivia:)") + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforePound: UnexpectedNodesSyntax? = nil, + pound: TokenSyntax = .poundToken(), + _ unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + macroName: TokenSyntax, + _ unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + _ unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + _ unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + arguments: LabeledExprListSyntax, + _ unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + _ unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + _ unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + _ unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforePound, + pound: pound, + unexpectedBetweenPoundAndMacroName, + moduleSelector: nil, + nil, + macroName: macroName, + unexpectedBetweenMacroNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedBetweenGenericArgumentClauseAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: arguments, + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @available(*, deprecated, renamed: "init(leadingTrivia:_:pound:_:moduleSelector:_:macroName:_:genericArgumentClause:_:leftParen:_:arguments:_:rightParen:_:trailingClosure:_:additionalTrailingClosures:_:trailingTrivia:)") @_disfavoredOverload public init( leadingTrivia: Trivia? = nil, @@ -4931,6 +5117,8 @@ extension MacroExpansionExprSyntax { unexpectedBeforePoundToken, pound: poundToken, unexpectedBetweenPoundTokenAndMacro, + moduleSelector: nil, + nil, macroName: macro, unexpectedBetweenMacroAndGenericArguments, genericArgumentClause: genericArguments, @@ -5008,6 +5196,47 @@ extension MemberAccessExprSyntax { } } +extension MemberTypeSyntax { + public var unexpectedBetweenPeriodAndName: UnexpectedNodesSyntax? { + get { + return unexpectedBetweenPeriodAndModuleSelector + } + set { + unexpectedBetweenPeriodAndModuleSelector = newValue + } + } + + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeBaseType: UnexpectedNodesSyntax? = nil, + baseType: some TypeSyntaxProtocol, + _ unexpectedBetweenBaseTypeAndPeriod: UnexpectedNodesSyntax? = nil, + period: TokenSyntax = .periodToken(), + _ unexpectedBetweenPeriodAndName: UnexpectedNodesSyntax? = nil, + name: TokenSyntax, + _ unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + _ unexpectedAfterGenericArgumentClause: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeBaseType, + baseType: baseType, + unexpectedBetweenBaseTypeAndPeriod, + period: period, + unexpectedBetweenPeriodAndName, + moduleSelector: nil, + nil, + name: name, + unexpectedBetweenNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedAfterGenericArgumentClause, + trailingTrivia: trailingTrivia + ) + } +} + extension MetatypeTypeSyntax { @available(*, deprecated, renamed: "unexpectedBetweenPeriodAndMetatypeSpecifier") public var unexpectedBetweenPeriodAndTypeOrProtocol: UnexpectedNodesSyntax? { diff --git a/Sources/SwiftSyntax/generated/RenamedNodesCompatibility.swift b/Sources/SwiftSyntax/generated/RenamedNodesCompatibility.swift index d9eed528b19..3aa308c9fc1 100644 --- a/Sources/SwiftSyntax/generated/RenamedNodesCompatibility.swift +++ b/Sources/SwiftSyntax/generated/RenamedNodesCompatibility.swift @@ -178,6 +178,9 @@ public typealias SpecializeAttributeSpecListSyntax = SpecializeAttributeArgument @available(*, deprecated, renamed: "GenericSpecializationExprSyntax") public typealias SpecializeExprSyntax = GenericSpecializationExprSyntax +@available(*, deprecated, renamed: "SpecializedAttributeArgumentSyntax") +public typealias SpecializedAttributeSyntax = SpecializedAttributeArgumentSyntax + @available(*, deprecated, renamed: "StringLiteralSegmentListSyntax") public typealias StringLiteralSegmentsSyntax = StringLiteralSegmentListSyntax @@ -493,6 +496,11 @@ extension SyntaxKind { return .genericSpecializationExpr } + @available(*, deprecated, renamed: "SpecializedAttributeArgumentSyntax") + public static var specializedAttribute: Self { + return .specializedAttributeArgument + } + @available(*, deprecated, renamed: "StringLiteralSegmentListSyntax") public static var stringLiteralSegments: Self { return .stringLiteralSegmentList diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index 124561440e0..b5245e8feaf 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -57,12 +57,10 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visit(_ node: ABIAttributeArgumentsSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visitPost(_ node: ABIAttributeArgumentsSyntax) { visitAnyPost(node._syntaxNode) } @@ -1253,12 +1251,10 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visit(_ node: InlineArrayTypeSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visitPost(_ node: InlineArrayTypeSyntax) { visitAnyPost(node._syntaxNode) } @@ -1553,6 +1549,14 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + override open func visit(_ node: ModuleSelectorSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + override open func visitPost(_ node: ModuleSelectorSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: MultipleTrailingClosureElementListSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } @@ -1985,6 +1989,14 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + override open func visit(_ node: SpecializedAttributeArgumentSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + override open func visitPost(_ node: SpecializedAttributeArgumentSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } @@ -2251,6 +2263,14 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + override open func visit(_ node: UnexpectedCodeDeclSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + override open func visitPost(_ node: UnexpectedCodeDeclSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: UnexpectedNodesSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } @@ -2291,6 +2311,16 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + @_spi(ExperimentalLanguageFeatures) + override open func visit(_ node: UsingDeclSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + @_spi(ExperimentalLanguageFeatures) + override open func visitPost(_ node: UsingDeclSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: ValueBindingPatternSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 8c1a8444f88..971e335ecd5 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -179,6 +179,7 @@ extension Syntax { /// - ``StructDeclSyntax`` /// - ``SubscriptDeclSyntax`` /// - ``TypeAliasDeclSyntax`` +/// - ``UnexpectedCodeDeclSyntax`` /// - ``VariableDeclSyntax`` public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax @@ -214,7 +215,7 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { public init?(_ node: __shared some SyntaxProtocol) { switch node.raw.kind { - case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .variableDecl: + case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .unexpectedCodeDecl, .usingDecl, .variableDecl: self._syntaxNode = node._syntaxNode default: return nil @@ -262,6 +263,8 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { .node(StructDeclSyntax.self), .node(SubscriptDeclSyntax.self), .node(TypeAliasDeclSyntax.self), + .node(UnexpectedCodeDeclSyntax.self), + .node(UsingDeclSyntax.self), .node(VariableDeclSyntax.self) ]) } @@ -1368,6 +1371,7 @@ extension Syntax { /// - ``FunctionTypeSyntax`` /// - ``IdentifierTypeSyntax`` /// - ``ImplicitlyUnwrappedOptionalTypeSyntax`` +/// - ``InlineArrayTypeSyntax`` /// - ``MemberTypeSyntax`` /// - ``MetatypeTypeSyntax`` /// - ``MissingTypeSyntax`` @@ -1695,6 +1699,7 @@ extension Syntax { .node(MissingStmtSyntax.self), .node(MissingSyntax.self), .node(MissingTypeSyntax.self), + .node(ModuleSelectorSyntax.self), .node(MultipleTrailingClosureElementListSyntax.self), .node(MultipleTrailingClosureElementSyntax.self), .node(NamedOpaqueReturnTypeSyntax.self), @@ -1749,6 +1754,7 @@ extension Syntax { .node(SpecializeAttributeArgumentListSyntax.self), .node(SpecializeAvailabilityArgumentSyntax.self), .node(SpecializeTargetFunctionArgumentSyntax.self), + .node(SpecializedAttributeArgumentSyntax.self), .node(StringLiteralExprSyntax.self), .node(StringLiteralSegmentListSyntax.self), .node(StringSegmentSyntax.self), @@ -1782,11 +1788,13 @@ extension Syntax { .node(TypeExprSyntax.self), .node(TypeInitializerClauseSyntax.self), .node(TypeSpecifierListSyntax.self), + .node(UnexpectedCodeDeclSyntax.self), .node(UnexpectedNodesSyntax.self), .node(UnresolvedAsExprSyntax.self), .node(UnresolvedIsExprSyntax.self), .node(UnresolvedTernaryExprSyntax.self), .node(UnsafeExprSyntax.self), + .node(UsingDeclSyntax.self), .node(ValueBindingPatternSyntax.self), .node(VariableDeclSyntax.self), .node(VersionComponentListSyntax.self), diff --git a/Sources/SwiftSyntax/generated/SyntaxCollections.swift b/Sources/SwiftSyntax/generated/SyntaxCollections.swift index a34717855b7..529785471e9 100644 --- a/Sources/SwiftSyntax/generated/SyntaxCollections.swift +++ b/Sources/SwiftSyntax/generated/SyntaxCollections.swift @@ -1918,6 +1918,7 @@ public struct TupleTypeElementListSyntax: SyntaxCollection, SyntaxHashable { /// ### Contained in /// /// - ``AttributedTypeSyntax``.``AttributedTypeSyntax/specifiers`` +/// - ``AttributedTypeSyntax``.``AttributedTypeSyntax/lateSpecifiers`` public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { public enum Element: SyntaxChildChoices, SyntaxHashable { /// A specifier that can be attached to a type to eg. mark a parameter as `inout` or `consuming` diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index 283c1d82d03..83e7c87c244 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -16,7 +16,6 @@ /// Enum to exhaustively switch over all different syntax nodes. public enum SyntaxEnum: Sendable { case token(TokenSyntax) - @_spi(ExperimentalLanguageFeatures) case abiAttributeArguments(ABIAttributeArgumentsSyntax) @_spi(Compiler) case accessorBlockFile(AccessorBlockFileSyntax) @@ -170,7 +169,6 @@ public enum SyntaxEnum: Sendable { case inheritedType(InheritedTypeSyntax) case initializerClause(InitializerClauseSyntax) case initializerDecl(InitializerDeclSyntax) - @_spi(ExperimentalLanguageFeatures) case inlineArrayType(InlineArrayTypeSyntax) case integerLiteralExpr(IntegerLiteralExprSyntax) case isExpr(IsExprSyntax) @@ -212,6 +210,7 @@ public enum SyntaxEnum: Sendable { case missingStmt(MissingStmtSyntax) case missing(MissingSyntax) case missingType(MissingTypeSyntax) + case moduleSelector(ModuleSelectorSyntax) case multipleTrailingClosureElementList(MultipleTrailingClosureElementListSyntax) case multipleTrailingClosureElement(MultipleTrailingClosureElementSyntax) case namedOpaqueReturnType(NamedOpaqueReturnTypeSyntax) @@ -266,6 +265,7 @@ public enum SyntaxEnum: Sendable { case specializeAttributeArgumentList(SpecializeAttributeArgumentListSyntax) case specializeAvailabilityArgument(SpecializeAvailabilityArgumentSyntax) case specializeTargetFunctionArgument(SpecializeTargetFunctionArgumentSyntax) + case specializedAttributeArgument(SpecializedAttributeArgumentSyntax) case stringLiteralExpr(StringLiteralExprSyntax) case stringLiteralSegmentList(StringLiteralSegmentListSyntax) case stringSegment(StringSegmentSyntax) @@ -300,11 +300,14 @@ public enum SyntaxEnum: Sendable { case typeExpr(TypeExprSyntax) case typeInitializerClause(TypeInitializerClauseSyntax) case typeSpecifierList(TypeSpecifierListSyntax) + case unexpectedCodeDecl(UnexpectedCodeDeclSyntax) case unexpectedNodes(UnexpectedNodesSyntax) case unresolvedAsExpr(UnresolvedAsExprSyntax) case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) case unsafeExpr(UnsafeExprSyntax) + @_spi(ExperimentalLanguageFeatures) + case usingDecl(UsingDeclSyntax) case valueBindingPattern(ValueBindingPatternSyntax) case variableDecl(VariableDeclSyntax) case versionComponentList(VersionComponentListSyntax) @@ -693,6 +696,8 @@ extension Syntax { return .missing(MissingSyntax(self)!) case .missingType: return .missingType(MissingTypeSyntax(self)!) + case .moduleSelector: + return .moduleSelector(ModuleSelectorSyntax(self)!) case .multipleTrailingClosureElementList: return .multipleTrailingClosureElementList(MultipleTrailingClosureElementListSyntax(self)!) case .multipleTrailingClosureElement: @@ -801,6 +806,8 @@ extension Syntax { return .specializeAvailabilityArgument(SpecializeAvailabilityArgumentSyntax(self)!) case .specializeTargetFunctionArgument: return .specializeTargetFunctionArgument(SpecializeTargetFunctionArgumentSyntax(self)!) + case .specializedAttributeArgument: + return .specializedAttributeArgument(SpecializedAttributeArgumentSyntax(self)!) case .stringLiteralExpr: return .stringLiteralExpr(StringLiteralExprSyntax(self)!) case .stringLiteralSegmentList: @@ -867,6 +874,8 @@ extension Syntax { return .typeInitializerClause(TypeInitializerClauseSyntax(self)!) case .typeSpecifierList: return .typeSpecifierList(TypeSpecifierListSyntax(self)!) + case .unexpectedCodeDecl: + return .unexpectedCodeDecl(UnexpectedCodeDeclSyntax(self)!) case .unexpectedNodes: return .unexpectedNodes(UnexpectedNodesSyntax(self)!) case .unresolvedAsExpr: @@ -877,6 +886,8 @@ extension Syntax { return .unresolvedTernaryExpr(UnresolvedTernaryExprSyntax(self)!) case .unsafeExpr: return .unsafeExpr(UnsafeExprSyntax(self)!) + case .usingDecl: + return .usingDecl(UsingDeclSyntax(self)!) case .valueBindingPattern: return .valueBindingPattern(ValueBindingPatternSyntax(self)!) case .variableDecl: @@ -930,6 +941,9 @@ public enum DeclSyntaxEnum { case structDecl(StructDeclSyntax) case subscriptDecl(SubscriptDeclSyntax) case typeAliasDecl(TypeAliasDeclSyntax) + case unexpectedCodeDecl(UnexpectedCodeDeclSyntax) + @_spi(ExperimentalLanguageFeatures) + case usingDecl(UsingDeclSyntax) case variableDecl(VariableDeclSyntax) } @@ -983,6 +997,10 @@ extension DeclSyntax { return .subscriptDecl(SubscriptDeclSyntax(self)!) case .typeAliasDecl: return .typeAliasDecl(TypeAliasDeclSyntax(self)!) + case .unexpectedCodeDecl: + return .unexpectedCodeDecl(UnexpectedCodeDeclSyntax(self)!) + case .usingDecl: + return .usingDecl(UsingDeclSyntax(self)!) case .variableDecl: return .variableDecl(VariableDeclSyntax(self)!) default: @@ -1279,7 +1297,6 @@ public enum TypeSyntaxEnum { case functionType(FunctionTypeSyntax) case identifierType(IdentifierTypeSyntax) case implicitlyUnwrappedOptionalType(ImplicitlyUnwrappedOptionalTypeSyntax) - @_spi(ExperimentalLanguageFeatures) case inlineArrayType(InlineArrayTypeSyntax) case memberType(MemberTypeSyntax) case metatypeType(MetatypeTypeSyntax) diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index 62883dd7ac3..51d1448bdd3 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -16,7 +16,6 @@ /// Enumerates the known kinds of Syntax represented in the Syntax tree. public enum SyntaxKind: Sendable { case token - @_spi(ExperimentalLanguageFeatures) case abiAttributeArguments @_spi(Compiler) case accessorBlockFile @@ -170,7 +169,6 @@ public enum SyntaxKind: Sendable { case inheritedType case initializerClause case initializerDecl - @_spi(ExperimentalLanguageFeatures) case inlineArrayType case integerLiteralExpr case isExpr @@ -212,6 +210,7 @@ public enum SyntaxKind: Sendable { case missingStmt case missing case missingType + case moduleSelector case multipleTrailingClosureElementList case multipleTrailingClosureElement case namedOpaqueReturnType @@ -266,6 +265,7 @@ public enum SyntaxKind: Sendable { case specializeAttributeArgumentList case specializeAvailabilityArgument case specializeTargetFunctionArgument + case specializedAttributeArgument case stringLiteralExpr case stringLiteralSegmentList case stringSegment @@ -300,11 +300,14 @@ public enum SyntaxKind: Sendable { case typeExpr case typeInitializerClause case typeSpecifierList + case unexpectedCodeDecl case unexpectedNodes case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr case unsafeExpr + @_spi(ExperimentalLanguageFeatures) + case usingDecl case valueBindingPattern case variableDecl case versionComponentList @@ -818,6 +821,8 @@ public enum SyntaxKind: Sendable { return MissingSyntax.self case .missingType: return MissingTypeSyntax.self + case .moduleSelector: + return ModuleSelectorSyntax.self case .multipleTrailingClosureElementList: return MultipleTrailingClosureElementListSyntax.self case .multipleTrailingClosureElement: @@ -926,6 +931,8 @@ public enum SyntaxKind: Sendable { return SpecializeAvailabilityArgumentSyntax.self case .specializeTargetFunctionArgument: return SpecializeTargetFunctionArgumentSyntax.self + case .specializedAttributeArgument: + return SpecializedAttributeArgumentSyntax.self case .stringLiteralExpr: return StringLiteralExprSyntax.self case .stringLiteralSegmentList: @@ -992,6 +999,8 @@ public enum SyntaxKind: Sendable { return TypeInitializerClauseSyntax.self case .typeSpecifierList: return TypeSpecifierListSyntax.self + case .unexpectedCodeDecl: + return UnexpectedCodeDeclSyntax.self case .unexpectedNodes: return UnexpectedNodesSyntax.self case .unresolvedAsExpr: @@ -1002,6 +1011,8 @@ public enum SyntaxKind: Sendable { return UnresolvedTernaryExprSyntax.self case .unsafeExpr: return UnsafeExprSyntax.self + case .usingDecl: + return UsingDeclSyntax.self case .valueBindingPattern: return ValueBindingPatternSyntax.self case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index 77eb14701ba..57adc1e1096 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -99,10 +99,9 @@ open class SyntaxRewriter { return token } - /// Visit a `ABIAttributeArgumentsSyntax`. + /// Visit a ``ABIAttributeArgumentsSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: ABIAttributeArgumentsSyntax) -> ABIAttributeArgumentsSyntax { return ABIAttributeArgumentsSyntax(unsafeCasting: visitChildren(node._syntaxNode)) } @@ -1141,10 +1140,9 @@ open class SyntaxRewriter { return DeclSyntax(InitializerDeclSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } - /// Visit a `InlineArrayTypeSyntax`. + /// Visit a ``InlineArrayTypeSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: InlineArrayTypeSyntax) -> TypeSyntax { return TypeSyntax(InlineArrayTypeSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } @@ -1399,6 +1397,13 @@ open class SyntaxRewriter { return TypeSyntax(MissingTypeSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a ``ModuleSelectorSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ModuleSelectorSyntax) -> ModuleSelectorSyntax { + return ModuleSelectorSyntax(unsafeCasting: visitChildren(node._syntaxNode)) + } + /// Visit a ``MultipleTrailingClosureElementListSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -1777,6 +1782,13 @@ open class SyntaxRewriter { return SpecializeTargetFunctionArgumentSyntax(unsafeCasting: visitChildren(node._syntaxNode)) } + /// Visit a ``SpecializedAttributeArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SpecializedAttributeArgumentSyntax) -> SpecializedAttributeArgumentSyntax { + return SpecializedAttributeArgumentSyntax(unsafeCasting: visitChildren(node._syntaxNode)) + } + /// Visit a ``StringLiteralExprSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -2009,6 +2021,13 @@ open class SyntaxRewriter { return TypeSpecifierListSyntax(unsafeCasting: visitChildren(node._syntaxNode)) } + /// Visit a ``UnexpectedCodeDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnexpectedCodeDeclSyntax) -> DeclSyntax { + return DeclSyntax(UnexpectedCodeDeclSyntax(unsafeCasting: visitChildren(node._syntaxNode))) + } + /// Visit a ``UnexpectedNodesSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -2044,6 +2063,14 @@ open class SyntaxRewriter { return ExprSyntax(UnsafeExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a `UsingDeclSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: UsingDeclSyntax) -> DeclSyntax { + return DeclSyntax(UsingDeclSyntax(unsafeCasting: visitChildren(node._syntaxNode))) + } + /// Visit a ``ValueBindingPatternSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -3088,6 +3115,11 @@ open class SyntaxRewriter { Syntax(visit(MissingTypeSyntax(unsafeCasting: node))) } + @inline(never) + private func visitModuleSelectorSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(ModuleSelectorSyntax(unsafeCasting: node))) + } + @inline(never) private func visitMultipleTrailingClosureElementListSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(MultipleTrailingClosureElementListSyntax(unsafeCasting: node))) @@ -3358,6 +3390,11 @@ open class SyntaxRewriter { Syntax(visit(SpecializeTargetFunctionArgumentSyntax(unsafeCasting: node))) } + @inline(never) + private func visitSpecializedAttributeArgumentSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(SpecializedAttributeArgumentSyntax(unsafeCasting: node))) + } + @inline(never) private func visitStringLiteralExprSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(StringLiteralExprSyntax(unsafeCasting: node))) @@ -3523,6 +3560,11 @@ open class SyntaxRewriter { Syntax(visit(TypeSpecifierListSyntax(unsafeCasting: node))) } + @inline(never) + private func visitUnexpectedCodeDeclSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(UnexpectedCodeDeclSyntax(unsafeCasting: node))) + } + @inline(never) private func visitUnexpectedNodesSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(UnexpectedNodesSyntax(unsafeCasting: node))) @@ -3548,6 +3590,11 @@ open class SyntaxRewriter { Syntax(visit(UnsafeExprSyntax(unsafeCasting: node))) } + @inline(never) + private func visitUsingDeclSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(UsingDeclSyntax(unsafeCasting: node))) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(ValueBindingPatternSyntax(unsafeCasting: node))) @@ -4004,6 +4051,8 @@ open class SyntaxRewriter { return self.visitMissingSyntaxImpl(_:) case .missingType: return self.visitMissingTypeSyntaxImpl(_:) + case .moduleSelector: + return self.visitModuleSelectorSyntaxImpl(_:) case .multipleTrailingClosureElementList: return self.visitMultipleTrailingClosureElementListSyntaxImpl(_:) case .multipleTrailingClosureElement: @@ -4112,6 +4161,8 @@ open class SyntaxRewriter { return self.visitSpecializeAvailabilityArgumentSyntaxImpl(_:) case .specializeTargetFunctionArgument: return self.visitSpecializeTargetFunctionArgumentSyntaxImpl(_:) + case .specializedAttributeArgument: + return self.visitSpecializedAttributeArgumentSyntaxImpl(_:) case .stringLiteralExpr: return self.visitStringLiteralExprSyntaxImpl(_:) case .stringLiteralSegmentList: @@ -4178,6 +4229,8 @@ open class SyntaxRewriter { return self.visitTypeInitializerClauseSyntaxImpl(_:) case .typeSpecifierList: return self.visitTypeSpecifierListSyntaxImpl(_:) + case .unexpectedCodeDecl: + return self.visitUnexpectedCodeDeclSyntaxImpl(_:) case .unexpectedNodes: return self.visitUnexpectedNodesSyntaxImpl(_:) case .unresolvedAsExpr: @@ -4188,6 +4241,8 @@ open class SyntaxRewriter { return self.visitUnresolvedTernaryExprSyntaxImpl(_:) case .unsafeExpr: return self.visitUnsafeExprSyntaxImpl(_:) + case .usingDecl: + return self.visitUsingDeclSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -4590,6 +4645,8 @@ open class SyntaxRewriter { return visitMissingSyntaxImpl(node) case .missingType: return visitMissingTypeSyntaxImpl(node) + case .moduleSelector: + return visitModuleSelectorSyntaxImpl(node) case .multipleTrailingClosureElementList: return visitMultipleTrailingClosureElementListSyntaxImpl(node) case .multipleTrailingClosureElement: @@ -4698,6 +4755,8 @@ open class SyntaxRewriter { return visitSpecializeAvailabilityArgumentSyntaxImpl(node) case .specializeTargetFunctionArgument: return visitSpecializeTargetFunctionArgumentSyntaxImpl(node) + case .specializedAttributeArgument: + return visitSpecializedAttributeArgumentSyntaxImpl(node) case .stringLiteralExpr: return visitStringLiteralExprSyntaxImpl(node) case .stringLiteralSegmentList: @@ -4764,6 +4823,8 @@ open class SyntaxRewriter { return visitTypeInitializerClauseSyntaxImpl(node) case .typeSpecifierList: return visitTypeSpecifierListSyntaxImpl(node) + case .unexpectedCodeDecl: + return visitUnexpectedCodeDeclSyntaxImpl(node) case .unexpectedNodes: return visitUnexpectedNodesSyntaxImpl(node) case .unresolvedAsExpr: @@ -4774,6 +4835,8 @@ open class SyntaxRewriter { return visitUnresolvedTernaryExprSyntaxImpl(node) case .unsafeExpr: return visitUnsafeExprSyntaxImpl(node) + case .usingDecl: + return visitUsingDeclSyntaxImpl(node) case .valueBindingPattern: return visitValueBindingPatternSyntaxImpl(node) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/SyntaxTraits.swift b/Sources/SwiftSyntax/generated/SyntaxTraits.swift index 368b529b45b..670ea0e0dac 100644 --- a/Sources/SwiftSyntax/generated/SyntaxTraits.swift +++ b/Sources/SwiftSyntax/generated/SyntaxTraits.swift @@ -205,6 +205,11 @@ public protocol FreestandingMacroExpansionSyntax: SyntaxProtocol { set } + var moduleSelector: ModuleSelectorSyntax? { + get + set + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be ``. diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index 739a203874c..76b2db0d083 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -35,17 +35,15 @@ open class SyntaxVisitor { dispatchVisit(Syntax(node)) } - /// Visiting `ABIAttributeArgumentsSyntax` specifically. + /// Visiting ``ABIAttributeArgumentsSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: ABIAttributeArgumentsSyntax) -> SyntaxVisitorContinueKind { return .visitChildren } - /// The function called after visiting `ABIAttributeArgumentsSyntax` and its descendants. + /// The function called after visiting ``ABIAttributeArgumentsSyntax`` and its descendants. /// - node: the node we just finished visiting. - @_spi(ExperimentalLanguageFeatures) open func visitPost(_ node: ABIAttributeArgumentsSyntax) { } @@ -1823,17 +1821,15 @@ open class SyntaxVisitor { open func visitPost(_ node: InitializerDeclSyntax) { } - /// Visiting `InlineArrayTypeSyntax` specifically. + /// Visiting ``InlineArrayTypeSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: InlineArrayTypeSyntax) -> SyntaxVisitorContinueKind { return .visitChildren } - /// The function called after visiting `InlineArrayTypeSyntax` and its descendants. + /// The function called after visiting ``InlineArrayTypeSyntax`` and its descendants. /// - node: the node we just finished visiting. - @_spi(ExperimentalLanguageFeatures) open func visitPost(_ node: InlineArrayTypeSyntax) { } @@ -2267,6 +2263,18 @@ open class SyntaxVisitor { open func visitPost(_ node: MissingTypeSyntax) { } + /// Visiting ``ModuleSelectorSyntax`` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + open func visit(_ node: ModuleSelectorSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting ``ModuleSelectorSyntax`` and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: ModuleSelectorSyntax) { + } + /// Visiting ``MultipleTrailingClosureElementListSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -2915,6 +2923,18 @@ open class SyntaxVisitor { open func visitPost(_ node: SpecializeTargetFunctionArgumentSyntax) { } + /// Visiting ``SpecializedAttributeArgumentSyntax`` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + open func visit(_ node: SpecializedAttributeArgumentSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting ``SpecializedAttributeArgumentSyntax`` and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: SpecializedAttributeArgumentSyntax) { + } + /// Visiting ``StringLiteralExprSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -3313,6 +3333,18 @@ open class SyntaxVisitor { open func visitPost(_ node: TypeSpecifierListSyntax) { } + /// Visiting ``UnexpectedCodeDeclSyntax`` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + open func visit(_ node: UnexpectedCodeDeclSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting ``UnexpectedCodeDeclSyntax`` and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: UnexpectedCodeDeclSyntax) { + } + /// Visiting ``UnexpectedNodesSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -3373,6 +3405,20 @@ open class SyntaxVisitor { open func visitPost(_ node: UnsafeExprSyntax) { } + /// Visiting `UsingDeclSyntax` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: UsingDeclSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting `UsingDeclSyntax` and its descendants. + /// - node: the node we just finished visiting. + @_spi(ExperimentalLanguageFeatures) + open func visitPost(_ node: UsingDeclSyntax) { + } + /// Visiting ``ValueBindingPatternSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -5008,6 +5054,14 @@ open class SyntaxVisitor { visitPost(MissingTypeSyntax(unsafeCasting: node)) } + @inline(never) + private func visitModuleSelectorSyntaxImpl(_ node: Syntax) { + if visit(ModuleSelectorSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(ModuleSelectorSyntax(unsafeCasting: node)) + } + @inline(never) private func visitMultipleTrailingClosureElementListSyntaxImpl(_ node: Syntax) { if visit(MultipleTrailingClosureElementListSyntax(unsafeCasting: node)) == .visitChildren { @@ -5440,6 +5494,14 @@ open class SyntaxVisitor { visitPost(SpecializeTargetFunctionArgumentSyntax(unsafeCasting: node)) } + @inline(never) + private func visitSpecializedAttributeArgumentSyntaxImpl(_ node: Syntax) { + if visit(SpecializedAttributeArgumentSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(SpecializedAttributeArgumentSyntax(unsafeCasting: node)) + } + @inline(never) private func visitStringLiteralExprSyntaxImpl(_ node: Syntax) { if visit(StringLiteralExprSyntax(unsafeCasting: node)) == .visitChildren { @@ -5704,6 +5766,14 @@ open class SyntaxVisitor { visitPost(TypeSpecifierListSyntax(unsafeCasting: node)) } + @inline(never) + private func visitUnexpectedCodeDeclSyntaxImpl(_ node: Syntax) { + if visit(UnexpectedCodeDeclSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(UnexpectedCodeDeclSyntax(unsafeCasting: node)) + } + @inline(never) private func visitUnexpectedNodesSyntaxImpl(_ node: Syntax) { if visit(UnexpectedNodesSyntax(unsafeCasting: node)) == .visitChildren { @@ -5744,6 +5814,14 @@ open class SyntaxVisitor { visitPost(UnsafeExprSyntax(unsafeCasting: node)) } + @inline(never) + private func visitUsingDeclSyntaxImpl(_ node: Syntax) { + if visit(UsingDeclSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(UsingDeclSyntax(unsafeCasting: node)) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) { if visit(ValueBindingPatternSyntax(unsafeCasting: node)) == .visitChildren { @@ -6236,6 +6314,8 @@ open class SyntaxVisitor { return self.visitMissingSyntaxImpl(_:) case .missingType: return self.visitMissingTypeSyntaxImpl(_:) + case .moduleSelector: + return self.visitModuleSelectorSyntaxImpl(_:) case .multipleTrailingClosureElementList: return self.visitMultipleTrailingClosureElementListSyntaxImpl(_:) case .multipleTrailingClosureElement: @@ -6344,6 +6424,8 @@ open class SyntaxVisitor { return self.visitSpecializeAvailabilityArgumentSyntaxImpl(_:) case .specializeTargetFunctionArgument: return self.visitSpecializeTargetFunctionArgumentSyntaxImpl(_:) + case .specializedAttributeArgument: + return self.visitSpecializedAttributeArgumentSyntaxImpl(_:) case .stringLiteralExpr: return self.visitStringLiteralExprSyntaxImpl(_:) case .stringLiteralSegmentList: @@ -6410,6 +6492,8 @@ open class SyntaxVisitor { return self.visitTypeInitializerClauseSyntaxImpl(_:) case .typeSpecifierList: return self.visitTypeSpecifierListSyntaxImpl(_:) + case .unexpectedCodeDecl: + return self.visitUnexpectedCodeDeclSyntaxImpl(_:) case .unexpectedNodes: return self.visitUnexpectedNodesSyntaxImpl(_:) case .unresolvedAsExpr: @@ -6420,6 +6504,8 @@ open class SyntaxVisitor { return self.visitUnresolvedTernaryExprSyntaxImpl(_:) case .unsafeExpr: return self.visitUnsafeExprSyntaxImpl(_:) + case .usingDecl: + return self.visitUsingDeclSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -6822,6 +6908,8 @@ open class SyntaxVisitor { self.visitMissingSyntaxImpl(node) case .missingType: self.visitMissingTypeSyntaxImpl(node) + case .moduleSelector: + self.visitModuleSelectorSyntaxImpl(node) case .multipleTrailingClosureElementList: self.visitMultipleTrailingClosureElementListSyntaxImpl(node) case .multipleTrailingClosureElement: @@ -6930,6 +7018,8 @@ open class SyntaxVisitor { self.visitSpecializeAvailabilityArgumentSyntaxImpl(node) case .specializeTargetFunctionArgument: self.visitSpecializeTargetFunctionArgumentSyntaxImpl(node) + case .specializedAttributeArgument: + self.visitSpecializedAttributeArgumentSyntaxImpl(node) case .stringLiteralExpr: self.visitStringLiteralExprSyntaxImpl(node) case .stringLiteralSegmentList: @@ -6996,6 +7086,8 @@ open class SyntaxVisitor { self.visitTypeInitializerClauseSyntaxImpl(node) case .typeSpecifierList: self.visitTypeSpecifierListSyntaxImpl(node) + case .unexpectedCodeDecl: + self.visitUnexpectedCodeDeclSyntaxImpl(node) case .unexpectedNodes: self.visitUnexpectedNodesSyntaxImpl(node) case .unresolvedAsExpr: @@ -7006,6 +7098,8 @@ open class SyntaxVisitor { self.visitUnresolvedTernaryExprSyntaxImpl(node) case .unsafeExpr: self.visitUnsafeExprSyntaxImpl(node) + case .usingDecl: + self.visitUsingDeclSyntaxImpl(node) case .valueBindingPattern: self.visitValueBindingPatternSyntaxImpl(node) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/TokenKind.swift b/Sources/SwiftSyntax/generated/TokenKind.swift index 10589e1b4e4..ab41c2cd4c7 100644 --- a/Sources/SwiftSyntax/generated/TokenKind.swift +++ b/Sources/SwiftSyntax/generated/TokenKind.swift @@ -21,6 +21,7 @@ public enum TokenKind: Hashable, Sendable { case backtick case binaryOperator(String) case colon + case colonColon case comma case dollarIdentifier(String) case ellipsis @@ -82,6 +83,8 @@ public enum TokenKind: Hashable, Sendable { return text case .colon: return ":" + case .colonColon: + return "::" case .comma: return "," case .dollarIdentifier(let text): @@ -187,6 +190,8 @@ public enum TokenKind: Hashable, Sendable { return "`" case .colon: return ":" + case .colonColon: + return "::" case .comma: return "," case .ellipsis: @@ -275,6 +280,8 @@ public enum TokenKind: Hashable, Sendable { return false case .colon: return true + case .colonColon: + return true case .comma: return true case .dollarIdentifier: @@ -382,6 +389,8 @@ extension TokenKind: Equatable { return lhsText == rhsText case (.colon, .colon): return true + case (.colonColon, .colonColon): + return true case (.comma, .comma): return true case (.dollarIdentifier(let lhsText), .dollarIdentifier(let rhsText)): @@ -489,6 +498,7 @@ public enum RawTokenKind: UInt8, Equatable, Hashable { case backtick case binaryOperator case colon + case colonColon case comma case dollarIdentifier case ellipsis @@ -547,6 +557,8 @@ public enum RawTokenKind: UInt8, Equatable, Hashable { return "`" case .colon: return ":" + case .colonColon: + return "::" case .comma: return "," case .ellipsis: @@ -633,6 +645,8 @@ public enum RawTokenKind: UInt8, Equatable, Hashable { return false case .colon: return true + case .colonColon: + return true case .comma: return true case .dollarIdentifier: @@ -747,6 +761,9 @@ extension TokenKind { case .colon: precondition(text.isEmpty || rawKind.defaultText.map(String.init) == text) return .colon + case .colonColon: + precondition(text.isEmpty || rawKind.defaultText.map(String.init) == text) + return .colonColon case .comma: precondition(text.isEmpty || rawKind.defaultText.map(String.init) == text) return .comma @@ -889,6 +906,8 @@ extension TokenKind { return (.binaryOperator, str) case .colon: return (.colon, nil) + case .colonColon: + return (.colonColon, nil) case .comma: return (.comma, nil) case .dollarIdentifier(let str): diff --git a/Sources/SwiftSyntax/generated/Tokens.swift b/Sources/SwiftSyntax/generated/Tokens.swift index 421aa3ed3d9..30ec618c5a4 100644 --- a/Sources/SwiftSyntax/generated/Tokens.swift +++ b/Sources/SwiftSyntax/generated/Tokens.swift @@ -93,6 +93,19 @@ extension TokenSyntax { ) } + public static func colonColonToken( + leadingTrivia: Trivia = [], + trailingTrivia: Trivia = [], + presence: SourcePresence = .present + ) -> TokenSyntax { + return TokenSyntax( + .colonColon, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia, + presence: presence + ) + } + public static func commaToken( leadingTrivia: Trivia = [], trailingTrivia: Trivia = [], diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift index 5c824e33f3c..a04d4c66d1a 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift @@ -13,7 +13,6 @@ // Do not edit directly! // swift-format-ignore-file -@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) public struct RawABIAttributeArgumentsSyntax: RawSyntaxNodeProtocol { public enum Provider: RawSyntaxNodeProtocol { @@ -1606,6 +1605,7 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { case argumentList(RawLabeledExprListSyntax) case availability(RawAvailabilityArgumentListSyntax) case specializeArguments(RawSpecializeAttributeArgumentListSyntax) + case specializedArguments(RawSpecializedAttributeArgumentSyntax) case objCName(RawObjCSelectorPieceListSyntax) case implementsArguments(RawImplementsAttributeArgumentsSyntax) case differentiableArguments(RawDifferentiableAttributeArgumentsSyntax) @@ -1615,12 +1615,10 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { case dynamicReplacementArguments(RawDynamicReplacementAttributeArgumentsSyntax) case effectsArguments(RawEffectsAttributeArgumentListSyntax) case documentationArguments(RawDocumentationAttributeArgumentListSyntax) - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) case abiArguments(RawABIAttributeArgumentsSyntax) public static func isKindOf(_ raw: RawSyntax) -> Bool { - RawLabeledExprListSyntax.isKindOf(raw) || RawAvailabilityArgumentListSyntax.isKindOf(raw) || RawSpecializeAttributeArgumentListSyntax.isKindOf(raw) || RawObjCSelectorPieceListSyntax.isKindOf(raw) || RawImplementsAttributeArgumentsSyntax.isKindOf(raw) || RawDifferentiableAttributeArgumentsSyntax.isKindOf(raw) || RawDerivativeAttributeArgumentsSyntax.isKindOf(raw) || RawBackDeployedAttributeArgumentsSyntax.isKindOf(raw) || RawOriginallyDefinedInAttributeArgumentsSyntax.isKindOf(raw) || RawDynamicReplacementAttributeArgumentsSyntax.isKindOf(raw) || RawEffectsAttributeArgumentListSyntax.isKindOf(raw) || RawDocumentationAttributeArgumentListSyntax.isKindOf(raw) || RawABIAttributeArgumentsSyntax.isKindOf(raw) + RawLabeledExprListSyntax.isKindOf(raw) || RawAvailabilityArgumentListSyntax.isKindOf(raw) || RawSpecializeAttributeArgumentListSyntax.isKindOf(raw) || RawSpecializedAttributeArgumentSyntax.isKindOf(raw) || RawObjCSelectorPieceListSyntax.isKindOf(raw) || RawImplementsAttributeArgumentsSyntax.isKindOf(raw) || RawDifferentiableAttributeArgumentsSyntax.isKindOf(raw) || RawDerivativeAttributeArgumentsSyntax.isKindOf(raw) || RawBackDeployedAttributeArgumentsSyntax.isKindOf(raw) || RawOriginallyDefinedInAttributeArgumentsSyntax.isKindOf(raw) || RawDynamicReplacementAttributeArgumentsSyntax.isKindOf(raw) || RawEffectsAttributeArgumentListSyntax.isKindOf(raw) || RawDocumentationAttributeArgumentListSyntax.isKindOf(raw) || RawABIAttributeArgumentsSyntax.isKindOf(raw) } public var raw: RawSyntax { @@ -1631,6 +1629,8 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { return node.raw case .specializeArguments(let node): return node.raw + case .specializedArguments(let node): + return node.raw case .objCName(let node): return node.raw case .implementsArguments(let node): @@ -1661,6 +1661,8 @@ public struct RawAttributeSyntax: RawSyntaxNodeProtocol { self = .availability(node) } else if let node = node.as(RawSpecializeAttributeArgumentListSyntax.self) { self = .specializeArguments(node) + } else if let node = node.as(RawSpecializedAttributeArgumentSyntax.self) { + self = .specializedArguments(node) } else if let node = node.as(RawObjCSelectorPieceListSyntax.self) { self = .objCName(node) } else if let node = node.as(RawImplementsAttributeArgumentsSyntax.self) { @@ -1825,21 +1827,25 @@ public struct RawAttributedTypeSyntax: RawTypeSyntaxNodeProtocol { specifiers: RawTypeSpecifierListSyntax, _ unexpectedBetweenSpecifiersAndAttributes: RawUnexpectedNodesSyntax? = nil, attributes: RawAttributeListSyntax, - _ unexpectedBetweenAttributesAndBaseType: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenAttributesAndLateSpecifiers: RawUnexpectedNodesSyntax? = nil, + lateSpecifiers: RawTypeSpecifierListSyntax, + _ unexpectedBetweenLateSpecifiersAndBaseType: RawUnexpectedNodesSyntax? = nil, baseType: some RawTypeSyntaxNodeProtocol, _ unexpectedAfterBaseType: RawUnexpectedNodesSyntax? = nil, arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .attributedType, uninitializedCount: 7, arena: arena) { layout in + kind: .attributedType, uninitializedCount: 9, arena: arena) { layout in layout.initialize(repeating: nil) layout[0] = unexpectedBeforeSpecifiers?.raw layout[1] = specifiers.raw layout[2] = unexpectedBetweenSpecifiersAndAttributes?.raw layout[3] = attributes.raw - layout[4] = unexpectedBetweenAttributesAndBaseType?.raw - layout[5] = baseType.raw - layout[6] = unexpectedAfterBaseType?.raw + layout[4] = unexpectedBetweenAttributesAndLateSpecifiers?.raw + layout[5] = lateSpecifiers.raw + layout[6] = unexpectedBetweenLateSpecifiersAndBaseType?.raw + layout[7] = baseType.raw + layout[8] = unexpectedAfterBaseType?.raw } self.init(unchecked: raw) } @@ -1860,16 +1866,24 @@ public struct RawAttributedTypeSyntax: RawTypeSyntaxNodeProtocol { layoutView.children[3].map(RawAttributeListSyntax.init(raw:))! } - public var unexpectedBetweenAttributesAndBaseType: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenAttributesAndLateSpecifiers: RawUnexpectedNodesSyntax? { layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var lateSpecifiers: RawTypeSpecifierListSyntax { + layoutView.children[5].map(RawTypeSpecifierListSyntax.init(raw:))! + } + + public var unexpectedBetweenLateSpecifiersAndBaseType: RawUnexpectedNodesSyntax? { + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var baseType: RawTypeSyntax { - layoutView.children[5].map(RawTypeSyntax.init(raw:))! + layoutView.children[7].map(RawTypeSyntax.init(raw:))! } public var unexpectedAfterBaseType: RawUnexpectedNodesSyntax? { - layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) } } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift index d9f5b337aea..f27b8eaff23 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift @@ -450,7 +450,9 @@ public struct RawDeclReferenceExprSyntax: RawExprSyntaxNodeProtocol { } public init( - _ unexpectedBeforeBaseName: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBeforeModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndBaseName: RawUnexpectedNodesSyntax? = nil, baseName: RawTokenSyntax, _ unexpectedBetweenBaseNameAndArgumentNames: RawUnexpectedNodesSyntax? = nil, argumentNames: RawDeclNameArgumentsSyntax?, @@ -458,35 +460,45 @@ public struct RawDeclReferenceExprSyntax: RawExprSyntaxNodeProtocol { arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .declReferenceExpr, uninitializedCount: 5, arena: arena) { layout in + kind: .declReferenceExpr, uninitializedCount: 7, arena: arena) { layout in layout.initialize(repeating: nil) - layout[0] = unexpectedBeforeBaseName?.raw - layout[1] = baseName.raw - layout[2] = unexpectedBetweenBaseNameAndArgumentNames?.raw - layout[3] = argumentNames?.raw - layout[4] = unexpectedAfterArgumentNames?.raw + layout[0] = unexpectedBeforeModuleSelector?.raw + layout[1] = moduleSelector?.raw + layout[2] = unexpectedBetweenModuleSelectorAndBaseName?.raw + layout[3] = baseName.raw + layout[4] = unexpectedBetweenBaseNameAndArgumentNames?.raw + layout[5] = argumentNames?.raw + layout[6] = unexpectedAfterArgumentNames?.raw } self.init(unchecked: raw) } - public var unexpectedBeforeBaseName: RawUnexpectedNodesSyntax? { + public var unexpectedBeforeModuleSelector: RawUnexpectedNodesSyntax? { layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var moduleSelector: RawModuleSelectorSyntax? { + layoutView.children[1].map(RawModuleSelectorSyntax.init(raw:)) + } + + public var unexpectedBetweenModuleSelectorAndBaseName: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var baseName: RawTokenSyntax { - layoutView.children[1].map(RawTokenSyntax.init(raw:))! + layoutView.children[3].map(RawTokenSyntax.init(raw:))! } public var unexpectedBetweenBaseNameAndArgumentNames: RawUnexpectedNodesSyntax? { - layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) } public var argumentNames: RawDeclNameArgumentsSyntax? { - layoutView.children[3].map(RawDeclNameArgumentsSyntax.init(raw:)) + layoutView.children[5].map(RawDeclNameArgumentsSyntax.init(raw:)) } public var unexpectedAfterArgumentNames: RawUnexpectedNodesSyntax? { - layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) } } @@ -499,7 +511,7 @@ public struct RawDeclSyntax: RawDeclSyntaxNodeProtocol { public static func isKindOf(_ raw: RawSyntax) -> Bool { switch raw.kind { - case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .variableDecl: + case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .unexpectedCodeDecl, .usingDecl, .variableDecl: return true default: return false diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesGHI.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesGHI.swift index dfe5fc4d0e6..76ca3886071 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesGHI.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesGHI.swift @@ -988,7 +988,9 @@ public struct RawIdentifierTypeSyntax: RawTypeSyntaxNodeProtocol { } public init( - _ unexpectedBeforeName: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBeforeModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndName: RawUnexpectedNodesSyntax? = nil, name: RawTokenSyntax, _ unexpectedBetweenNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? = nil, genericArgumentClause: RawGenericArgumentClauseSyntax?, @@ -996,35 +998,45 @@ public struct RawIdentifierTypeSyntax: RawTypeSyntaxNodeProtocol { arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .identifierType, uninitializedCount: 5, arena: arena) { layout in + kind: .identifierType, uninitializedCount: 7, arena: arena) { layout in layout.initialize(repeating: nil) - layout[0] = unexpectedBeforeName?.raw - layout[1] = name.raw - layout[2] = unexpectedBetweenNameAndGenericArgumentClause?.raw - layout[3] = genericArgumentClause?.raw - layout[4] = unexpectedAfterGenericArgumentClause?.raw + layout[0] = unexpectedBeforeModuleSelector?.raw + layout[1] = moduleSelector?.raw + layout[2] = unexpectedBetweenModuleSelectorAndName?.raw + layout[3] = name.raw + layout[4] = unexpectedBetweenNameAndGenericArgumentClause?.raw + layout[5] = genericArgumentClause?.raw + layout[6] = unexpectedAfterGenericArgumentClause?.raw } self.init(unchecked: raw) } - public var unexpectedBeforeName: RawUnexpectedNodesSyntax? { + public var unexpectedBeforeModuleSelector: RawUnexpectedNodesSyntax? { layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var moduleSelector: RawModuleSelectorSyntax? { + layoutView.children[1].map(RawModuleSelectorSyntax.init(raw:)) + } + + public var unexpectedBetweenModuleSelectorAndName: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var name: RawTokenSyntax { - layoutView.children[1].map(RawTokenSyntax.init(raw:))! + layoutView.children[3].map(RawTokenSyntax.init(raw:))! } public var unexpectedBetweenNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) } public var genericArgumentClause: RawGenericArgumentClauseSyntax? { - layoutView.children[3].map(RawGenericArgumentClauseSyntax.init(raw:)) + layoutView.children[5].map(RawGenericArgumentClauseSyntax.init(raw:)) } public var unexpectedAfterGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) } } @@ -2343,7 +2355,6 @@ public struct RawInitializerDeclSyntax: RawDeclSyntaxNodeProtocol { } } -@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) public struct RawInlineArrayTypeSyntax: RawTypeSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift index 8e6766d2c4f..df1142fe80f 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift @@ -1429,7 +1429,9 @@ public struct RawMacroExpansionDeclSyntax: RawDeclSyntaxNodeProtocol { modifiers: RawDeclModifierListSyntax, _ unexpectedBetweenModifiersAndPound: RawUnexpectedNodesSyntax? = nil, pound: RawTokenSyntax, - _ unexpectedBetweenPoundAndMacroName: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPoundAndModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndMacroName: RawUnexpectedNodesSyntax? = nil, macroName: RawTokenSyntax, _ unexpectedBetweenMacroNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? = nil, genericArgumentClause: RawGenericArgumentClauseSyntax?, @@ -1447,7 +1449,7 @@ public struct RawMacroExpansionDeclSyntax: RawDeclSyntaxNodeProtocol { arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .macroExpansionDecl, uninitializedCount: 21, arena: arena) { layout in + kind: .macroExpansionDecl, uninitializedCount: 23, arena: arena) { layout in layout.initialize(repeating: nil) layout[0] = unexpectedBeforeAttributes?.raw layout[1] = attributes.raw @@ -1455,21 +1457,23 @@ public struct RawMacroExpansionDeclSyntax: RawDeclSyntaxNodeProtocol { layout[3] = modifiers.raw layout[4] = unexpectedBetweenModifiersAndPound?.raw layout[5] = pound.raw - layout[6] = unexpectedBetweenPoundAndMacroName?.raw - layout[7] = macroName.raw - layout[8] = unexpectedBetweenMacroNameAndGenericArgumentClause?.raw - layout[9] = genericArgumentClause?.raw - layout[10] = unexpectedBetweenGenericArgumentClauseAndLeftParen?.raw - layout[11] = leftParen?.raw - layout[12] = unexpectedBetweenLeftParenAndArguments?.raw - layout[13] = arguments.raw - layout[14] = unexpectedBetweenArgumentsAndRightParen?.raw - layout[15] = rightParen?.raw - layout[16] = unexpectedBetweenRightParenAndTrailingClosure?.raw - layout[17] = trailingClosure?.raw - layout[18] = unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures?.raw - layout[19] = additionalTrailingClosures.raw - layout[20] = unexpectedAfterAdditionalTrailingClosures?.raw + layout[6] = unexpectedBetweenPoundAndModuleSelector?.raw + layout[7] = moduleSelector?.raw + layout[8] = unexpectedBetweenModuleSelectorAndMacroName?.raw + layout[9] = macroName.raw + layout[10] = unexpectedBetweenMacroNameAndGenericArgumentClause?.raw + layout[11] = genericArgumentClause?.raw + layout[12] = unexpectedBetweenGenericArgumentClauseAndLeftParen?.raw + layout[13] = leftParen?.raw + layout[14] = unexpectedBetweenLeftParenAndArguments?.raw + layout[15] = arguments.raw + layout[16] = unexpectedBetweenArgumentsAndRightParen?.raw + layout[17] = rightParen?.raw + layout[18] = unexpectedBetweenRightParenAndTrailingClosure?.raw + layout[19] = trailingClosure?.raw + layout[20] = unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures?.raw + layout[21] = additionalTrailingClosures.raw + layout[22] = unexpectedAfterAdditionalTrailingClosures?.raw } self.init(unchecked: raw) } @@ -1498,64 +1502,72 @@ public struct RawMacroExpansionDeclSyntax: RawDeclSyntaxNodeProtocol { layoutView.children[5].map(RawTokenSyntax.init(raw:))! } - public var unexpectedBetweenPoundAndMacroName: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenPoundAndModuleSelector: RawUnexpectedNodesSyntax? { layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var moduleSelector: RawModuleSelectorSyntax? { + layoutView.children[7].map(RawModuleSelectorSyntax.init(raw:)) + } + + public var unexpectedBetweenModuleSelectorAndMacroName: RawUnexpectedNodesSyntax? { + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var macroName: RawTokenSyntax { - layoutView.children[7].map(RawTokenSyntax.init(raw:))! + layoutView.children[9].map(RawTokenSyntax.init(raw:))! } public var unexpectedBetweenMacroNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:)) } public var genericArgumentClause: RawGenericArgumentClauseSyntax? { - layoutView.children[9].map(RawGenericArgumentClauseSyntax.init(raw:)) + layoutView.children[11].map(RawGenericArgumentClauseSyntax.init(raw:)) } public var unexpectedBetweenGenericArgumentClauseAndLeftParen: RawUnexpectedNodesSyntax? { - layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:)) } public var leftParen: RawTokenSyntax? { - layoutView.children[11].map(RawTokenSyntax.init(raw:)) + layoutView.children[13].map(RawTokenSyntax.init(raw:)) } public var unexpectedBetweenLeftParenAndArguments: RawUnexpectedNodesSyntax? { - layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:)) } public var arguments: RawLabeledExprListSyntax { - layoutView.children[13].map(RawLabeledExprListSyntax.init(raw:))! + layoutView.children[15].map(RawLabeledExprListSyntax.init(raw:))! } public var unexpectedBetweenArgumentsAndRightParen: RawUnexpectedNodesSyntax? { - layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:)) } public var rightParen: RawTokenSyntax? { - layoutView.children[15].map(RawTokenSyntax.init(raw:)) + layoutView.children[17].map(RawTokenSyntax.init(raw:)) } public var unexpectedBetweenRightParenAndTrailingClosure: RawUnexpectedNodesSyntax? { - layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[18].map(RawUnexpectedNodesSyntax.init(raw:)) } public var trailingClosure: RawClosureExprSyntax? { - layoutView.children[17].map(RawClosureExprSyntax.init(raw:)) + layoutView.children[19].map(RawClosureExprSyntax.init(raw:)) } public var unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: RawUnexpectedNodesSyntax? { - layoutView.children[18].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[20].map(RawUnexpectedNodesSyntax.init(raw:)) } public var additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax { - layoutView.children[19].map(RawMultipleTrailingClosureElementListSyntax.init(raw:))! + layoutView.children[21].map(RawMultipleTrailingClosureElementListSyntax.init(raw:))! } public var unexpectedAfterAdditionalTrailingClosures: RawUnexpectedNodesSyntax? { - layoutView.children[20].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[22].map(RawUnexpectedNodesSyntax.init(raw:)) } } @@ -1591,7 +1603,9 @@ public struct RawMacroExpansionExprSyntax: RawExprSyntaxNodeProtocol { public init( _ unexpectedBeforePound: RawUnexpectedNodesSyntax? = nil, pound: RawTokenSyntax, - _ unexpectedBetweenPoundAndMacroName: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPoundAndModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndMacroName: RawUnexpectedNodesSyntax? = nil, macroName: RawTokenSyntax, _ unexpectedBetweenMacroNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? = nil, genericArgumentClause: RawGenericArgumentClauseSyntax?, @@ -1609,25 +1623,27 @@ public struct RawMacroExpansionExprSyntax: RawExprSyntaxNodeProtocol { arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .macroExpansionExpr, uninitializedCount: 17, arena: arena) { layout in + kind: .macroExpansionExpr, uninitializedCount: 19, arena: arena) { layout in layout.initialize(repeating: nil) layout[0] = unexpectedBeforePound?.raw layout[1] = pound.raw - layout[2] = unexpectedBetweenPoundAndMacroName?.raw - layout[3] = macroName.raw - layout[4] = unexpectedBetweenMacroNameAndGenericArgumentClause?.raw - layout[5] = genericArgumentClause?.raw - layout[6] = unexpectedBetweenGenericArgumentClauseAndLeftParen?.raw - layout[7] = leftParen?.raw - layout[8] = unexpectedBetweenLeftParenAndArguments?.raw - layout[9] = arguments.raw - layout[10] = unexpectedBetweenArgumentsAndRightParen?.raw - layout[11] = rightParen?.raw - layout[12] = unexpectedBetweenRightParenAndTrailingClosure?.raw - layout[13] = trailingClosure?.raw - layout[14] = unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures?.raw - layout[15] = additionalTrailingClosures.raw - layout[16] = unexpectedAfterAdditionalTrailingClosures?.raw + layout[2] = unexpectedBetweenPoundAndModuleSelector?.raw + layout[3] = moduleSelector?.raw + layout[4] = unexpectedBetweenModuleSelectorAndMacroName?.raw + layout[5] = macroName.raw + layout[6] = unexpectedBetweenMacroNameAndGenericArgumentClause?.raw + layout[7] = genericArgumentClause?.raw + layout[8] = unexpectedBetweenGenericArgumentClauseAndLeftParen?.raw + layout[9] = leftParen?.raw + layout[10] = unexpectedBetweenLeftParenAndArguments?.raw + layout[11] = arguments.raw + layout[12] = unexpectedBetweenArgumentsAndRightParen?.raw + layout[13] = rightParen?.raw + layout[14] = unexpectedBetweenRightParenAndTrailingClosure?.raw + layout[15] = trailingClosure?.raw + layout[16] = unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures?.raw + layout[17] = additionalTrailingClosures.raw + layout[18] = unexpectedAfterAdditionalTrailingClosures?.raw } self.init(unchecked: raw) } @@ -1640,64 +1656,72 @@ public struct RawMacroExpansionExprSyntax: RawExprSyntaxNodeProtocol { layoutView.children[1].map(RawTokenSyntax.init(raw:))! } - public var unexpectedBetweenPoundAndMacroName: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenPoundAndModuleSelector: RawUnexpectedNodesSyntax? { layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var moduleSelector: RawModuleSelectorSyntax? { + layoutView.children[3].map(RawModuleSelectorSyntax.init(raw:)) + } + + public var unexpectedBetweenModuleSelectorAndMacroName: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var macroName: RawTokenSyntax { - layoutView.children[3].map(RawTokenSyntax.init(raw:))! + layoutView.children[5].map(RawTokenSyntax.init(raw:))! } public var unexpectedBetweenMacroNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) } public var genericArgumentClause: RawGenericArgumentClauseSyntax? { - layoutView.children[5].map(RawGenericArgumentClauseSyntax.init(raw:)) + layoutView.children[7].map(RawGenericArgumentClauseSyntax.init(raw:)) } public var unexpectedBetweenGenericArgumentClauseAndLeftParen: RawUnexpectedNodesSyntax? { - layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) } public var leftParen: RawTokenSyntax? { - layoutView.children[7].map(RawTokenSyntax.init(raw:)) + layoutView.children[9].map(RawTokenSyntax.init(raw:)) } public var unexpectedBetweenLeftParenAndArguments: RawUnexpectedNodesSyntax? { - layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:)) } public var arguments: RawLabeledExprListSyntax { - layoutView.children[9].map(RawLabeledExprListSyntax.init(raw:))! + layoutView.children[11].map(RawLabeledExprListSyntax.init(raw:))! } public var unexpectedBetweenArgumentsAndRightParen: RawUnexpectedNodesSyntax? { - layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:)) } public var rightParen: RawTokenSyntax? { - layoutView.children[11].map(RawTokenSyntax.init(raw:)) + layoutView.children[13].map(RawTokenSyntax.init(raw:)) } public var unexpectedBetweenRightParenAndTrailingClosure: RawUnexpectedNodesSyntax? { - layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:)) } public var trailingClosure: RawClosureExprSyntax? { - layoutView.children[13].map(RawClosureExprSyntax.init(raw:)) + layoutView.children[15].map(RawClosureExprSyntax.init(raw:)) } public var unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: RawUnexpectedNodesSyntax? { - layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:)) } public var additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax { - layoutView.children[15].map(RawMultipleTrailingClosureElementListSyntax.init(raw:))! + layoutView.children[17].map(RawMultipleTrailingClosureElementListSyntax.init(raw:))! } public var unexpectedAfterAdditionalTrailingClosures: RawUnexpectedNodesSyntax? { - layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[18].map(RawUnexpectedNodesSyntax.init(raw:)) } } @@ -2184,7 +2208,9 @@ public struct RawMemberTypeSyntax: RawTypeSyntaxNodeProtocol { baseType: some RawTypeSyntaxNodeProtocol, _ unexpectedBetweenBaseTypeAndPeriod: RawUnexpectedNodesSyntax? = nil, period: RawTokenSyntax, - _ unexpectedBetweenPeriodAndName: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPeriodAndModuleSelector: RawUnexpectedNodesSyntax? = nil, + moduleSelector: RawModuleSelectorSyntax?, + _ unexpectedBetweenModuleSelectorAndName: RawUnexpectedNodesSyntax? = nil, name: RawTokenSyntax, _ unexpectedBetweenNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? = nil, genericArgumentClause: RawGenericArgumentClauseSyntax?, @@ -2192,17 +2218,19 @@ public struct RawMemberTypeSyntax: RawTypeSyntaxNodeProtocol { arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .memberType, uninitializedCount: 9, arena: arena) { layout in + kind: .memberType, uninitializedCount: 11, arena: arena) { layout in layout.initialize(repeating: nil) layout[0] = unexpectedBeforeBaseType?.raw layout[1] = baseType.raw layout[2] = unexpectedBetweenBaseTypeAndPeriod?.raw layout[3] = period.raw - layout[4] = unexpectedBetweenPeriodAndName?.raw - layout[5] = name.raw - layout[6] = unexpectedBetweenNameAndGenericArgumentClause?.raw - layout[7] = genericArgumentClause?.raw - layout[8] = unexpectedAfterGenericArgumentClause?.raw + layout[4] = unexpectedBetweenPeriodAndModuleSelector?.raw + layout[5] = moduleSelector?.raw + layout[6] = unexpectedBetweenModuleSelectorAndName?.raw + layout[7] = name.raw + layout[8] = unexpectedBetweenNameAndGenericArgumentClause?.raw + layout[9] = genericArgumentClause?.raw + layout[10] = unexpectedAfterGenericArgumentClause?.raw } self.init(unchecked: raw) } @@ -2223,24 +2251,32 @@ public struct RawMemberTypeSyntax: RawTypeSyntaxNodeProtocol { layoutView.children[3].map(RawTokenSyntax.init(raw:))! } - public var unexpectedBetweenPeriodAndName: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenPeriodAndModuleSelector: RawUnexpectedNodesSyntax? { layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var moduleSelector: RawModuleSelectorSyntax? { + layoutView.children[5].map(RawModuleSelectorSyntax.init(raw:)) + } + + public var unexpectedBetweenModuleSelectorAndName: RawUnexpectedNodesSyntax? { + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var name: RawTokenSyntax { - layoutView.children[5].map(RawTokenSyntax.init(raw:))! + layoutView.children[7].map(RawTokenSyntax.init(raw:))! } public var unexpectedBetweenNameAndGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) } public var genericArgumentClause: RawGenericArgumentClauseSyntax? { - layoutView.children[7].map(RawGenericArgumentClauseSyntax.init(raw:)) + layoutView.children[9].map(RawGenericArgumentClauseSyntax.init(raw:)) } public var unexpectedAfterGenericArgumentClause: RawUnexpectedNodesSyntax? { - layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:)) } } @@ -2698,6 +2734,76 @@ public struct RawMissingTypeSyntax: RawTypeSyntaxNodeProtocol { } } +@_spi(RawSyntax) +public struct RawModuleSelectorSyntax: RawSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .moduleSelector + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeModuleName: RawUnexpectedNodesSyntax? = nil, + moduleName: RawTokenSyntax, + _ unexpectedBetweenModuleNameAndColonColon: RawUnexpectedNodesSyntax? = nil, + colonColon: RawTokenSyntax, + _ unexpectedAfterColonColon: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .moduleSelector, uninitializedCount: 5, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeModuleName?.raw + layout[1] = moduleName.raw + layout[2] = unexpectedBetweenModuleNameAndColonColon?.raw + layout[3] = colonColon.raw + layout[4] = unexpectedAfterColonColon?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeModuleName: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var moduleName: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenModuleNameAndColonColon: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var colonColon: RawTokenSyntax { + layoutView.children[3].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedAfterColonColon: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawMultipleTrailingClosureElementListSyntax: RawSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesQRS.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesQRS.swift index e4313ae32a7..3092d21e457 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesQRS.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesQRS.swift @@ -1190,6 +1190,64 @@ public struct RawSpecializeTargetFunctionArgumentSyntax: RawSyntaxNodeProtocol { } } +@_spi(RawSyntax) +public struct RawSpecializedAttributeArgumentSyntax: RawSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .specializedAttributeArgument + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeGenericWhereClause: RawUnexpectedNodesSyntax? = nil, + genericWhereClause: RawGenericWhereClauseSyntax, + _ unexpectedAfterGenericWhereClause: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .specializedAttributeArgument, uninitializedCount: 3, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeGenericWhereClause?.raw + layout[1] = genericWhereClause.raw + layout[2] = unexpectedAfterGenericWhereClause?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeGenericWhereClause: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var genericWhereClause: RawGenericWhereClauseSyntax { + layoutView.children[1].map(RawGenericWhereClauseSyntax.init(raw:))! + } + + public var unexpectedAfterGenericWhereClause: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawStmtSyntax: RawStmtSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift index e4b5a2b69a0..b61e8335b43 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift @@ -1533,6 +1533,49 @@ public struct RawTypeSyntax: RawTypeSyntaxNodeProtocol { } } +@_spi(RawSyntax) +public struct RawUnexpectedCodeDeclSyntax: RawDeclSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .unexpectedCodeDecl + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init(unexpectedCode: RawUnexpectedNodesSyntax, arena: __shared RawSyntaxArena) { + let raw = RawSyntax.makeLayout( + kind: .unexpectedCodeDecl, uninitializedCount: 1, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedCode.raw + } + self.init(unchecked: raw) + } + + public var unexpectedCode: RawUnexpectedNodesSyntax { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:))! + } +} + @_spi(RawSyntax) public struct RawUnexpectedNodesSyntax: RawSyntaxNodeProtocol { @_spi(RawSyntax) @@ -1863,6 +1906,108 @@ public struct RawUnsafeExprSyntax: RawExprSyntaxNodeProtocol { } } +@_spi(ExperimentalLanguageFeatures) +@_spi(RawSyntax) +public struct RawUsingDeclSyntax: RawDeclSyntaxNodeProtocol { + public enum Specifier: RawSyntaxNodeProtocol { + case attribute(RawAttributeSyntax) + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be ``. + case modifier(RawTokenSyntax) + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + RawAttributeSyntax.isKindOf(raw) || RawTokenSyntax.isKindOf(raw) + } + + public var raw: RawSyntax { + switch self { + case .attribute(let node): + return node.raw + case .modifier(let node): + return node.raw + } + } + + public init?(_ node: __shared some RawSyntaxNodeProtocol) { + if let node = node.as(RawAttributeSyntax.self) { + self = .attribute(node) + } else if let node = node.as(RawTokenSyntax.self) { + self = .modifier(node) + } else { + return nil + } + } + } + + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .usingDecl + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeUsingKeyword: RawUnexpectedNodesSyntax? = nil, + usingKeyword: RawTokenSyntax, + _ unexpectedBetweenUsingKeywordAndSpecifier: RawUnexpectedNodesSyntax? = nil, + specifier: Specifier, + _ unexpectedAfterSpecifier: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .usingDecl, uninitializedCount: 5, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeUsingKeyword?.raw + layout[1] = usingKeyword.raw + layout[2] = unexpectedBetweenUsingKeywordAndSpecifier?.raw + layout[3] = specifier.raw + layout[4] = unexpectedAfterSpecifier?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeUsingKeyword: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var usingKeyword: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenUsingKeywordAndSpecifier: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var specifier: RawSyntax { + layoutView.children[3]! + } + + public var unexpectedAfterSpecifier: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawValueBindingPatternSyntax: RawPatternSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 0ca5359f6e7..2592e549bbd 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -264,7 +264,9 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { .keyword("read"), .keyword("_modify"), .keyword("modify"), - .keyword("init") + .keyword("init"), + .keyword("borrow"), + .keyword("mutate") ])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 7, verify(layout[7], as: RawAccessorParametersSyntax?.self)) @@ -422,14 +424,16 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) } func validateAttributedTypeSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 7) + assert(layout.count == 9) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawTypeSpecifierListSyntax.self)) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 3, verify(layout[3], as: RawAttributeListSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 5, verify(layout[5], as: RawTypeSyntax.self)) + assertNoError(kind, 5, verify(layout[5], as: RawTypeSpecifierListSyntax.self)) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 7, verify(layout[7], as: RawTypeSyntax.self)) + assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) } func validateAvailabilityArgumentListSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { @@ -913,9 +917,11 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } func validateDeclReferenceExprSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 5) + assert(layout.count == 7) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [ + assertNoError(kind, 1, verify(layout[1], as: RawModuleSelectorSyntax?.self)) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [ .tokenKind(.identifier), .keyword("self"), .keyword("Self"), @@ -926,9 +932,9 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { .tokenKind(.binaryOperator), .tokenKind(.integerLiteral) ])) - assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 3, verify(layout[3], as: RawDeclNameArgumentsSyntax?.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 5, verify(layout[5], as: RawDeclNameArgumentsSyntax?.self)) + assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } func validateDeferStmtSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 5) @@ -1563,17 +1569,19 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) } func validateIdentifierTypeSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 5) + assert(layout.count == 7) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [ + assertNoError(kind, 1, verify(layout[1], as: RawModuleSelectorSyntax?.self)) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [ .tokenKind(.identifier), .keyword("Self"), .keyword("Any"), .tokenKind(.wildcard) ])) - assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 3, verify(layout[3], as: RawGenericArgumentClauseSyntax?.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 5, verify(layout[5], as: RawGenericArgumentClauseSyntax?.self)) + assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } func validateIfConfigClauseListSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { @@ -1671,7 +1679,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { .tokenKind(.postfixOperator) ])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.period)])) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.period), .tokenKind(.colonColon)])) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) } func validateInOutExprSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { @@ -1748,7 +1756,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 3, verify(layout[3], as: RawGenericArgumentSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.keyword("x")])) + assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.keyword("of")])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 7, verify(layout[7], as: RawGenericArgumentSyntax.self)) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) @@ -1963,7 +1971,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 16, verify(layout[16], as: RawUnexpectedNodesSyntax?.self)) } func validateMacroExpansionDeclSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 21) + assert(layout.count == 23) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawAttributeListSyntax.self)) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) @@ -1971,40 +1979,44 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.pound)])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) + assertNoError(kind, 7, verify(layout[7], as: RawModuleSelectorSyntax?.self)) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 9, verify(layout[9], as: RawGenericArgumentClauseSyntax?.self)) + assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 11, verify(layout[11], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.leftParen)])) + assertNoError(kind, 11, verify(layout[11], as: RawGenericArgumentClauseSyntax?.self)) assertNoError(kind, 12, verify(layout[12], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 13, verify(layout[13], as: RawLabeledExprListSyntax.self)) + assertNoError(kind, 13, verify(layout[13], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.leftParen)])) assertNoError(kind, 14, verify(layout[14], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 15, verify(layout[15], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.rightParen)])) + assertNoError(kind, 15, verify(layout[15], as: RawLabeledExprListSyntax.self)) assertNoError(kind, 16, verify(layout[16], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 17, verify(layout[17], as: RawClosureExprSyntax?.self)) + assertNoError(kind, 17, verify(layout[17], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.rightParen)])) assertNoError(kind, 18, verify(layout[18], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 19, verify(layout[19], as: RawMultipleTrailingClosureElementListSyntax.self)) + assertNoError(kind, 19, verify(layout[19], as: RawClosureExprSyntax?.self)) assertNoError(kind, 20, verify(layout[20], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 21, verify(layout[21], as: RawMultipleTrailingClosureElementListSyntax.self)) + assertNoError(kind, 22, verify(layout[22], as: RawUnexpectedNodesSyntax?.self)) } func validateMacroExpansionExprSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 17) + assert(layout.count == 19) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.pound)])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) + assertNoError(kind, 3, verify(layout[3], as: RawModuleSelectorSyntax?.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 5, verify(layout[5], as: RawGenericArgumentClauseSyntax?.self)) + assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.leftParen)])) + assertNoError(kind, 7, verify(layout[7], as: RawGenericArgumentClauseSyntax?.self)) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 9, verify(layout[9], as: RawLabeledExprListSyntax.self)) + assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.leftParen)])) assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 11, verify(layout[11], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.rightParen)])) + assertNoError(kind, 11, verify(layout[11], as: RawLabeledExprListSyntax.self)) assertNoError(kind, 12, verify(layout[12], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 13, verify(layout[13], as: RawClosureExprSyntax?.self)) + assertNoError(kind, 13, verify(layout[13], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.rightParen)])) assertNoError(kind, 14, verify(layout[14], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 15, verify(layout[15], as: RawMultipleTrailingClosureElementListSyntax.self)) + assertNoError(kind, 15, verify(layout[15], as: RawClosureExprSyntax?.self)) assertNoError(kind, 16, verify(layout[16], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 17, verify(layout[17], as: RawMultipleTrailingClosureElementListSyntax.self)) + assertNoError(kind, 18, verify(layout[18], as: RawUnexpectedNodesSyntax?.self)) } func validateMatchingPatternConditionSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 9) @@ -2060,16 +2072,18 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } func validateMemberTypeSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 9) + assert(layout.count == 11) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawTypeSyntax.self)) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.period)])) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier), .keyword("self")])) + assertNoError(kind, 5, verify(layout[5], as: RawModuleSelectorSyntax?.self)) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 7, verify(layout[7], as: RawGenericArgumentClauseSyntax?.self)) + assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier), .keyword("self")])) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 9, verify(layout[9], as: RawGenericArgumentClauseSyntax?.self)) + assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) } func validateMetatypeTypeSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 7) @@ -2121,6 +2135,14 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) } + func validateModuleSelectorSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 5) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.colonColon)])) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + } func validateMultipleTrailingClosureElementListSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { assertNoError(kind, index, verify(element, as: RawMultipleTrailingClosureElementSyntax.self)) @@ -2638,6 +2660,12 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.comma)])) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) } + func validateSpecializedAttributeArgumentSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 3) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawGenericWhereClauseSyntax.self)) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + } func validateStringLiteralExprSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 11) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) @@ -2978,6 +3006,10 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { verify(element, as: RawNonisolatedTypeSpecifierSyntax.self)]) } } + func validateUnexpectedCodeDeclSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 1) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax.self)) + } func validateUnexpectedNodesSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { assertNoError(kind, index, verify(element, as: RawSyntax.self)) @@ -3015,6 +3047,15 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 3, verify(layout[3], as: RawExprSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) } + func validateUsingDeclSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 5) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("using")])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertAnyHasNoError(kind, 3, [ + verify(layout[3], as: RawSyntax.self)]) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + } func validateValueBindingPatternSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 5) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) @@ -3498,6 +3539,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateMissingSyntax(kind: kind, layout: layout) case .missingType: validateMissingTypeSyntax(kind: kind, layout: layout) + case .moduleSelector: + validateModuleSelectorSyntax(kind: kind, layout: layout) case .multipleTrailingClosureElementList: validateMultipleTrailingClosureElementListSyntax(kind: kind, layout: layout) case .multipleTrailingClosureElement: @@ -3606,6 +3649,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateSpecializeAvailabilityArgumentSyntax(kind: kind, layout: layout) case .specializeTargetFunctionArgument: validateSpecializeTargetFunctionArgumentSyntax(kind: kind, layout: layout) + case .specializedAttributeArgument: + validateSpecializedAttributeArgumentSyntax(kind: kind, layout: layout) case .stringLiteralExpr: validateStringLiteralExprSyntax(kind: kind, layout: layout) case .stringLiteralSegmentList: @@ -3672,6 +3717,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateTypeInitializerClauseSyntax(kind: kind, layout: layout) case .typeSpecifierList: validateTypeSpecifierListSyntax(kind: kind, layout: layout) + case .unexpectedCodeDecl: + validateUnexpectedCodeDeclSyntax(kind: kind, layout: layout) case .unexpectedNodes: validateUnexpectedNodesSyntax(kind: kind, layout: layout) case .unresolvedAsExpr: @@ -3682,6 +3729,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateUnresolvedTernaryExprSyntax(kind: kind, layout: layout) case .unsafeExpr: validateUnsafeExprSyntax(kind: kind, layout: layout) + case .usingDecl: + validateUsingDeclSyntax(kind: kind, layout: layout) case .valueBindingPattern: validateValueBindingPatternSyntax(kind: kind, layout: layout) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift index 1a444b28949..ae96eca8816 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift @@ -17,8 +17,6 @@ /// The arguments of the '@abi' attribute /// -/// - Note: Requires experimental feature `abiAttribute`. -/// /// ### Children /// /// - `provider`: (``AssociatedTypeDeclSyntax`` | ``DeinitializerDeclSyntax`` | ``EnumCaseDeclSyntax`` | ``FunctionDeclSyntax`` | ``InitializerDeclSyntax`` | ``MissingDeclSyntax`` | ``SubscriptDeclSyntax`` | ``TypeAliasDeclSyntax`` | ``VariableDeclSyntax``) @@ -26,7 +24,6 @@ /// ### Contained in /// /// - ``AttributeSyntax``.``AttributeSyntax/arguments`` -@_spi(ExperimentalLanguageFeatures) public struct ABIAttributeArgumentsSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { public enum Provider: SyntaxChildChoices, SyntaxHashable { case associatedType(AssociatedTypeDeclSyntax) @@ -828,7 +825,7 @@ public struct AccessorBlockSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNo /// /// - `attributes`: ``AttributeListSyntax`` /// - `modifier`: ``DeclModifierSyntax``? -/// - `accessorSpecifier`: (`get` | `set` | `didSet` | `willSet` | `unsafeAddress` | `addressWithOwner` | `addressWithNativeOwner` | `unsafeMutableAddress` | `mutableAddressWithOwner` | `mutableAddressWithNativeOwner` | `_read` | `read` | `_modify` | `modify` | `init`) +/// - `accessorSpecifier`: (`get` | `set` | `didSet` | `willSet` | `unsafeAddress` | `addressWithOwner` | `addressWithNativeOwner` | `unsafeMutableAddress` | `mutableAddressWithOwner` | `mutableAddressWithNativeOwner` | `_read` | `read` | `_modify` | `modify` | `init` | `borrow` | `mutate`) /// - `parameters`: ``AccessorParametersSyntax``? /// - `effectSpecifiers`: ``AccessorEffectSpecifiersSyntax``? /// - `body`: ``CodeBlockSyntax``? @@ -1004,6 +1001,8 @@ public struct AccessorDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclS /// - `_modify` /// - `modify` /// - `init` + /// - `borrow` + /// - `mutate` public var accessorSpecifier: TokenSyntax { get { return Syntax(self).child(at: 5)!.cast(TokenSyntax.self) @@ -2296,7 +2295,7 @@ public struct ArrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSynt // MARK: - AsExprSyntax -/// The cast of an expressison to a different type. +/// The cast of an expression to a different type. /// /// ### Examples /// @@ -2600,6 +2599,10 @@ public struct AssignmentExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExp /// - `inheritanceClause`: ``InheritanceClauseSyntax``? /// - `initializer`: ``TypeInitializerClauseSyntax``? /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct AssociatedTypeDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -3097,7 +3100,7 @@ public struct AttributeClauseFileSyntax: SyntaxProtocol, SyntaxHashable, _LeafSy /// - `atSign`: `@` /// - `attributeName`: ``TypeSyntax`` /// - `leftParen`: `(`? -/// - `arguments`: (``LabeledExprListSyntax`` | ``AvailabilityArgumentListSyntax`` | ``SpecializeAttributeArgumentListSyntax`` | ``ObjCSelectorPieceListSyntax`` | ``ImplementsAttributeArgumentsSyntax`` | ``DifferentiableAttributeArgumentsSyntax`` | ``DerivativeAttributeArgumentsSyntax`` | ``BackDeployedAttributeArgumentsSyntax`` | ``OriginallyDefinedInAttributeArgumentsSyntax`` | ``DynamicReplacementAttributeArgumentsSyntax`` | ``EffectsAttributeArgumentListSyntax`` | ``DocumentationAttributeArgumentListSyntax`` | `ABIAttributeArgumentsSyntax`)? +/// - `arguments`: (``LabeledExprListSyntax`` | ``AvailabilityArgumentListSyntax`` | ``SpecializeAttributeArgumentListSyntax`` | ``SpecializedAttributeArgumentSyntax`` | ``ObjCSelectorPieceListSyntax`` | ``ImplementsAttributeArgumentsSyntax`` | ``DifferentiableAttributeArgumentsSyntax`` | ``DerivativeAttributeArgumentsSyntax`` | ``BackDeployedAttributeArgumentsSyntax`` | ``OriginallyDefinedInAttributeArgumentsSyntax`` | ``DynamicReplacementAttributeArgumentsSyntax`` | ``EffectsAttributeArgumentListSyntax`` | ``DocumentationAttributeArgumentListSyntax`` | ``ABIAttributeArgumentsSyntax``)? /// - `rightParen`: `)`? /// /// ### Contained in @@ -3109,6 +3112,7 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr case argumentList(LabeledExprListSyntax) case availability(AvailabilityArgumentListSyntax) case specializeArguments(SpecializeAttributeArgumentListSyntax) + case specializedArguments(SpecializedAttributeArgumentSyntax) case objCName(ObjCSelectorPieceListSyntax) case implementsArguments(ImplementsAttributeArgumentsSyntax) case differentiableArguments(DifferentiableAttributeArgumentsSyntax) @@ -3118,8 +3122,6 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr case dynamicReplacementArguments(DynamicReplacementAttributeArgumentsSyntax) case effectsArguments(EffectsAttributeArgumentListSyntax) case documentationArguments(DocumentationAttributeArgumentListSyntax) - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) case abiArguments(ABIAttributeArgumentsSyntax) public var _syntaxNode: Syntax { @@ -3130,6 +3132,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr return node._syntaxNode case .specializeArguments(let node): return node._syntaxNode + case .specializedArguments(let node): + return node._syntaxNode case .objCName(let node): return node._syntaxNode case .implementsArguments(let node): @@ -3165,6 +3169,10 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr self = .specializeArguments(node) } + public init(_ node: SpecializedAttributeArgumentSyntax) { + self = .specializedArguments(node) + } + public init(_ node: ObjCSelectorPieceListSyntax) { self = .objCName(node) } @@ -3201,8 +3209,6 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr self = .documentationArguments(node) } - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) public init(_ node: ABIAttributeArgumentsSyntax) { self = .abiArguments(node) } @@ -3214,6 +3220,8 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr self = .availability(node) } else if let node = node.as(SpecializeAttributeArgumentListSyntax.self) { self = .specializeArguments(node) + } else if let node = node.as(SpecializedAttributeArgumentSyntax.self) { + self = .specializedArguments(node) } else if let node = node.as(ObjCSelectorPieceListSyntax.self) { self = .objCName(node) } else if let node = node.as(ImplementsAttributeArgumentsSyntax.self) { @@ -3244,6 +3252,7 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr .node(LabeledExprListSyntax.self), .node(AvailabilityArgumentListSyntax.self), .node(SpecializeAttributeArgumentListSyntax.self), + .node(SpecializedAttributeArgumentSyntax.self), .node(ObjCSelectorPieceListSyntax.self), .node(ImplementsAttributeArgumentsSyntax.self), .node(DifferentiableAttributeArgumentsSyntax.self), @@ -3323,6 +3332,28 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr return self.as(SpecializeAttributeArgumentListSyntax.self)! } + /// Checks if the current syntax node can be cast to ``SpecializedAttributeArgumentSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: SpecializedAttributeArgumentSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``SpecializedAttributeArgumentSyntax``. + /// + /// - Returns: An instance of ``SpecializedAttributeArgumentSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: SpecializedAttributeArgumentSyntax.Type) -> SpecializedAttributeArgumentSyntax? { + return SpecializedAttributeArgumentSyntax.init(self) + } + + /// Force-casts the current syntax node to ``SpecializedAttributeArgumentSyntax``. + /// + /// - Returns: An instance of ``SpecializedAttributeArgumentSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: SpecializedAttributeArgumentSyntax.Type) -> SpecializedAttributeArgumentSyntax { + return self.as(SpecializedAttributeArgumentSyntax.self)! + } + /// Checks if the current syntax node can be cast to ``ObjCSelectorPieceListSyntax``. /// /// - Returns: `true` if the node can be cast, `false` otherwise. @@ -3521,30 +3552,24 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr return self.as(DocumentationAttributeArgumentListSyntax.self)! } - /// Checks if the current syntax node can be cast to `ABIAttributeArgumentsSyntax`. + /// Checks if the current syntax node can be cast to ``ABIAttributeArgumentsSyntax``. /// /// - Returns: `true` if the node can be cast, `false` otherwise. - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) public func `is`(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> Bool { return self.as(syntaxType) != nil } - /// Attempts to cast the current syntax node to `ABIAttributeArgumentsSyntax`. + /// Attempts to cast the current syntax node to ``ABIAttributeArgumentsSyntax``. /// - /// - Returns: An instance of `ABIAttributeArgumentsSyntax`, or `nil` if the cast fails. - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) + /// - Returns: An instance of ``ABIAttributeArgumentsSyntax``, or `nil` if the cast fails. public func `as`(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> ABIAttributeArgumentsSyntax? { return ABIAttributeArgumentsSyntax.init(self) } - /// Force-casts the current syntax node to `ABIAttributeArgumentsSyntax`. + /// Force-casts the current syntax node to ``ABIAttributeArgumentsSyntax``. /// - /// - Returns: An instance of `ABIAttributeArgumentsSyntax`. + /// - Returns: An instance of ``ABIAttributeArgumentsSyntax``. /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. - /// - Note: Requires experimental feature `abiAttribute`. - @_spi(ExperimentalLanguageFeatures) public func cast(_ syntaxType: ABIAttributeArgumentsSyntax.Type) -> ABIAttributeArgumentsSyntax { return self.as(ABIAttributeArgumentsSyntax.self)! } @@ -3766,6 +3791,7 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr /// /// - `specifiers`: ``TypeSpecifierListSyntax`` /// - `attributes`: ``AttributeListSyntax`` +/// - `lateSpecifiers`: ``TypeSpecifierListSyntax`` /// - `baseType`: ``TypeSyntax`` public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -3786,6 +3812,7 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. /// - specifiers: A list of specifiers that can be attached to the type, such as `inout`, `isolated`, or `consuming`. /// - attributes: A list of attributes that can be attached to the type, such as `@escaping`. + /// - lateSpecifiers: A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'. /// - baseType: The type to with the specifiers and attributes are applied. /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. public init( @@ -3794,7 +3821,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers: TypeSpecifierListSyntax = [], _ unexpectedBetweenSpecifiersAndAttributes: UnexpectedNodesSyntax? = nil, attributes: AttributeListSyntax = [], - _ unexpectedBetweenAttributesAndBaseType: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenAttributesAndLateSpecifiers: UnexpectedNodesSyntax? = nil, + lateSpecifiers: TypeSpecifierListSyntax = [], + _ unexpectedBetweenLateSpecifiersAndBaseType: UnexpectedNodesSyntax? = nil, baseType: some TypeSyntaxProtocol, _ unexpectedAfterBaseType: UnexpectedNodesSyntax? = nil, trailingTrivia: Trivia? = nil @@ -3806,7 +3835,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers, unexpectedBetweenSpecifiersAndAttributes, attributes, - unexpectedBetweenAttributesAndBaseType, + unexpectedBetweenAttributesAndLateSpecifiers, + lateSpecifiers, + unexpectedBetweenLateSpecifiersAndBaseType, baseType, unexpectedAfterBaseType ))) { (arena, _) in @@ -3815,7 +3846,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers.raw, unexpectedBetweenSpecifiersAndAttributes?.raw, attributes.raw, - unexpectedBetweenAttributesAndBaseType?.raw, + unexpectedBetweenAttributesAndLateSpecifiers?.raw, + lateSpecifiers.raw, + unexpectedBetweenLateSpecifiersAndBaseType?.raw, baseType.raw, unexpectedAfterBaseType?.raw ] @@ -3922,7 +3955,7 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp .cast(AttributedTypeSyntax.self) } - public var unexpectedBetweenAttributesAndBaseType: UnexpectedNodesSyntax? { + public var unexpectedBetweenAttributesAndLateSpecifiers: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) } @@ -3931,17 +3964,17 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } - /// The type to with the specifiers and attributes are applied. - public var baseType: TypeSyntax { + /// A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'. + public var lateSpecifiers: TypeSpecifierListSyntax { get { - return Syntax(self).child(at: 5)!.cast(TypeSyntax.self) + return Syntax(self).child(at: 5)!.cast(TypeSpecifierListSyntax.self) } set(value) { self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) } } - public var unexpectedAfterBaseType: UnexpectedNodesSyntax? { + public var unexpectedBetweenLateSpecifiersAndBaseType: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } @@ -3950,12 +3983,33 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } + /// The type to with the specifiers and attributes are applied. + public var baseType: TypeSyntax { + get { + return Syntax(self).child(at: 7)!.cast(TypeSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) + } + } + + public var unexpectedAfterBaseType: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) + } + } + public static let structure: SyntaxNodeStructure = .layout([ \Self.unexpectedBeforeSpecifiers, \Self.specifiers, \Self.unexpectedBetweenSpecifiersAndAttributes, \Self.attributes, - \Self.unexpectedBetweenAttributesAndBaseType, + \Self.unexpectedBetweenAttributesAndLateSpecifiers, + \Self.lateSpecifiers, + \Self.unexpectedBetweenLateSpecifiersAndBaseType, \Self.baseType, \Self.unexpectedAfterBaseType ]) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift index 6c8b49198a3..235fcba86a2 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift @@ -628,6 +628,7 @@ public struct DeclNameArgumentsSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynt /// ### Children /// +/// - `moduleSelector`: ``ModuleSelectorSyntax``? /// - `baseName`: (`` | `self` | `Self` | `init` | `deinit` | `subscript` | `` | `` | ``) /// - `argumentNames`: ``DeclNameArgumentsSyntax``? /// @@ -658,7 +659,9 @@ public struct DeclReferenceExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. public init( leadingTrivia: Trivia? = nil, - _ unexpectedBeforeBaseName: UnexpectedNodesSyntax? = nil, + _ unexpectedBeforeModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + _ unexpectedBetweenModuleSelectorAndBaseName: UnexpectedNodesSyntax? = nil, baseName: TokenSyntax, _ unexpectedBetweenBaseNameAndArgumentNames: UnexpectedNodesSyntax? = nil, argumentNames: DeclNameArgumentsSyntax? = nil, @@ -668,14 +671,18 @@ public struct DeclReferenceExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf // Extend the lifetime of all parameters so their arenas don't get destroyed // before they can be added as children of the new arena. self = withExtendedLifetime((RawSyntaxArena(), ( - unexpectedBeforeBaseName, + unexpectedBeforeModuleSelector, + moduleSelector, + unexpectedBetweenModuleSelectorAndBaseName, baseName, unexpectedBetweenBaseNameAndArgumentNames, argumentNames, unexpectedAfterArgumentNames ))) { (arena, _) in let layout: [RawSyntax?] = [ - unexpectedBeforeBaseName?.raw, + unexpectedBeforeModuleSelector?.raw, + moduleSelector?.raw, + unexpectedBetweenModuleSelectorAndBaseName?.raw, baseName.raw, unexpectedBetweenBaseNameAndArgumentNames?.raw, argumentNames?.raw, @@ -692,7 +699,7 @@ public struct DeclReferenceExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf } } - public var unexpectedBeforeBaseName: UnexpectedNodesSyntax? { + public var unexpectedBeforeModuleSelector: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) } @@ -701,6 +708,24 @@ public struct DeclReferenceExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf } } + public var moduleSelector: ModuleSelectorSyntax? { + get { + return Syntax(self).child(at: 1)?.cast(ModuleSelectorSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + } + } + + public var unexpectedBetweenModuleSelectorAndBaseName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + } + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: @@ -715,42 +740,44 @@ public struct DeclReferenceExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf /// - `` public var baseName: TokenSyntax { get { - return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) } } public var unexpectedBetweenBaseNameAndArgumentNames: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) } } public var argumentNames: DeclNameArgumentsSyntax? { get { - return Syntax(self).child(at: 3)?.cast(DeclNameArgumentsSyntax.self) + return Syntax(self).child(at: 5)?.cast(DeclNameArgumentsSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) } } public var unexpectedAfterArgumentNames: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(DeclReferenceExprSyntax.self) } } public static let structure: SyntaxNodeStructure = .layout([ - \Self.unexpectedBeforeBaseName, + \Self.unexpectedBeforeModuleSelector, + \Self.moduleSelector, + \Self.unexpectedBetweenModuleSelectorAndBaseName, \Self.baseName, \Self.unexpectedBetweenBaseNameAndArgumentNames, \Self.argumentNames, @@ -893,6 +920,10 @@ public struct DeferStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSynt /// - `deinitKeyword`: `deinit` /// - `effectSpecifiers`: ``DeinitializerEffectSpecifiersSyntax``? /// - `body`: ``CodeBlockSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct DeinitializerDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift index 2c9f89946cf..41c56cd8b1d 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift @@ -312,6 +312,10 @@ public struct EditorPlaceholderExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _ /// - `modifiers`: ``DeclModifierListSyntax`` /// - `caseKeyword`: `case` /// - `elements`: ``EnumCaseElementListSyntax`` +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct EnumCaseDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -3312,6 +3316,10 @@ public struct FunctionCallExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafE /// - `signature`: ``FunctionSignatureSyntax`` /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? /// - `body`: ``CodeBlockSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct FunctionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesGHI.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesGHI.swift index d28ff8b926b..f0882c9acf8 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesGHI.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesGHI.swift @@ -206,6 +206,8 @@ public struct GenericArgumentClauseSyntax: SyntaxProtocol, SyntaxHashable, _Leaf /// ### Contained in /// /// - ``GenericArgumentListSyntax`` +/// - ``InlineArrayTypeSyntax``.``InlineArrayTypeSyntax/count`` +/// - ``InlineArrayTypeSyntax``.``InlineArrayTypeSyntax/element`` public struct GenericArgumentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { public enum Argument: SyntaxChildChoices, SyntaxHashable { case type(TypeSyntax) @@ -1261,6 +1263,7 @@ public struct GenericSpecializationExprSyntax: ExprSyntaxProtocol, SyntaxHashabl /// - ``MacroDeclSyntax``.``MacroDeclSyntax/genericWhereClause`` /// - ``ProtocolDeclSyntax``.``ProtocolDeclSyntax/genericWhereClause`` /// - ``SpecializeAttributeArgumentListSyntax`` +/// - ``SpecializedAttributeArgumentSyntax``.``SpecializedAttributeArgumentSyntax/genericWhereClause`` /// - ``StructDeclSyntax``.``StructDeclSyntax/genericWhereClause`` /// - ``SubscriptDeclSyntax``.``SubscriptDeclSyntax/genericWhereClause`` /// - ``TypeAliasDeclSyntax``.``TypeAliasDeclSyntax/genericWhereClause`` @@ -1706,6 +1709,7 @@ public struct IdentifierPatternSyntax: PatternSyntaxProtocol, SyntaxHashable, _L /// ### Children /// +/// - `moduleSelector`: ``ModuleSelectorSyntax``? /// - `name`: (`` | `Self` | `Any` | `_`) /// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``? public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyntaxNodeProtocol { @@ -1728,7 +1732,9 @@ public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. public init( leadingTrivia: Trivia? = nil, - _ unexpectedBeforeName: UnexpectedNodesSyntax? = nil, + _ unexpectedBeforeModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + _ unexpectedBetweenModuleSelectorAndName: UnexpectedNodesSyntax? = nil, name: TokenSyntax, _ unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -1738,14 +1744,18 @@ public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp // Extend the lifetime of all parameters so their arenas don't get destroyed // before they can be added as children of the new arena. self = withExtendedLifetime((RawSyntaxArena(), ( - unexpectedBeforeName, + unexpectedBeforeModuleSelector, + moduleSelector, + unexpectedBetweenModuleSelectorAndName, name, unexpectedBetweenNameAndGenericArgumentClause, genericArgumentClause, unexpectedAfterGenericArgumentClause ))) { (arena, _) in let layout: [RawSyntax?] = [ - unexpectedBeforeName?.raw, + unexpectedBeforeModuleSelector?.raw, + moduleSelector?.raw, + unexpectedBetweenModuleSelectorAndName?.raw, name.raw, unexpectedBetweenNameAndGenericArgumentClause?.raw, genericArgumentClause?.raw, @@ -1762,7 +1772,7 @@ public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } - public var unexpectedBeforeName: UnexpectedNodesSyntax? { + public var unexpectedBeforeModuleSelector: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) } @@ -1771,6 +1781,24 @@ public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } + public var moduleSelector: ModuleSelectorSyntax? { + get { + return Syntax(self).child(at: 1)?.cast(ModuleSelectorSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + } + } + + public var unexpectedBetweenModuleSelectorAndName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + } + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: @@ -1780,42 +1808,44 @@ public struct IdentifierTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp /// - `_` public var name: TokenSyntax { get { - return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) } } public var unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) } } public var genericArgumentClause: GenericArgumentClauseSyntax? { get { - return Syntax(self).child(at: 3)?.cast(GenericArgumentClauseSyntax.self) + return Syntax(self).child(at: 5)?.cast(GenericArgumentClauseSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) } } public var unexpectedAfterGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(IdentifierTypeSyntax.self) } } public static let structure: SyntaxNodeStructure = .layout([ - \Self.unexpectedBeforeName, + \Self.unexpectedBeforeModuleSelector, + \Self.moduleSelector, + \Self.unexpectedBetweenModuleSelectorAndName, \Self.name, \Self.unexpectedBetweenNameAndGenericArgumentClause, \Self.genericArgumentClause, @@ -3207,7 +3237,7 @@ public struct ImportDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyn /// ### Children /// /// - `name`: (`` | `` | `` | ``) -/// - `trailingPeriod`: `.`? +/// - `trailingPeriod`: (`.` | `::`)? /// /// ### Contained in /// @@ -3302,7 +3332,9 @@ public struct ImportPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSy /// ### Tokens /// - /// For syntax trees generated by the parser, this is guaranteed to be `.`. + /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: + /// - `.` + /// - `::` public var trailingPeriod: TokenSyntax? { get { return Syntax(self).child(at: 3)?.cast(TokenSyntax.self) @@ -4022,6 +4054,10 @@ public struct InitializerClauseSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynt /// - `signature`: ``FunctionSignatureSyntax`` /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? /// - `body`: ``CodeBlockSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct InitializerDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -4368,18 +4404,15 @@ public struct InitializerDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDe // MARK: - InlineArrayTypeSyntax -/// An inline array type `[3 x Int]`, sugar for `InlineArray<3, Int>`. -/// -/// - Note: Requires experimental feature `inlineArrayTypeSugar`. +/// An inline array type `[3 of Int]`, sugar for `InlineArray<3, Int>`. /// /// ### Children /// /// - `leftSquare`: `[` /// - `count`: ``GenericArgumentSyntax`` -/// - `separator`: `x` +/// - `separator`: `of` /// - `element`: ``GenericArgumentSyntax`` /// - `rightSquare`: `]` -@_spi(ExperimentalLanguageFeatures) public struct InlineArrayTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -4407,7 +4440,7 @@ public struct InlineArrayTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTy _ unexpectedBetweenLeftSquareAndCount: UnexpectedNodesSyntax? = nil, count: GenericArgumentSyntax, _ unexpectedBetweenCountAndSeparator: UnexpectedNodesSyntax? = nil, - separator: TokenSyntax = .keyword(.x), + separator: TokenSyntax = .keyword(.of), _ unexpectedBetweenSeparatorAndElement: UnexpectedNodesSyntax? = nil, element: GenericArgumentSyntax, _ unexpectedBetweenElementAndRightSquare: UnexpectedNodesSyntax? = nil, @@ -4486,7 +4519,7 @@ public struct InlineArrayTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTy /// 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 `[_ x Int]`. + /// - Note: In semantically valid Swift code, this is always an integer or a wildcard type, e.g `_` in `[_ of Int]`. public var count: GenericArgumentSyntax { get { return Syntax(self).child(at: 3)!.cast(GenericArgumentSyntax.self) @@ -4507,7 +4540,7 @@ public struct InlineArrayTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTy /// ### Tokens /// - /// For syntax trees generated by the parser, this is guaranteed to be `x`. + /// For syntax trees generated by the parser, this is guaranteed to be `of`. public var separator: TokenSyntax { get { return Syntax(self).child(at: 5)!.cast(TokenSyntax.self) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift index fe4e496d5ab..92bbb9e6f28 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift @@ -2653,6 +2653,7 @@ public struct MacroDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSynt /// - `attributes`: ``AttributeListSyntax`` /// - `modifiers`: ``DeclModifierListSyntax`` /// - `pound`: `#` +/// - `moduleSelector`: ``ModuleSelectorSyntax``? /// - `macroName`: `` /// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``? /// - `leftParen`: `(`? @@ -2687,7 +2688,9 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea modifiers: DeclModifierListSyntax = [], _ unexpectedBetweenModifiersAndPound: UnexpectedNodesSyntax? = nil, pound: TokenSyntax = .poundToken(), - _ unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + _ unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? = nil, macroName: TokenSyntax, _ unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -2713,7 +2716,9 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea modifiers, unexpectedBetweenModifiersAndPound, pound, - unexpectedBetweenPoundAndMacroName, + unexpectedBetweenPoundAndModuleSelector, + moduleSelector, + unexpectedBetweenModuleSelectorAndMacroName, macroName, unexpectedBetweenMacroNameAndGenericArgumentClause, genericArgumentClause, @@ -2736,7 +2741,9 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea modifiers.raw, unexpectedBetweenModifiersAndPound?.raw, pound.raw, - unexpectedBetweenPoundAndMacroName?.raw, + unexpectedBetweenPoundAndModuleSelector?.raw, + moduleSelector?.raw, + unexpectedBetweenModuleSelectorAndMacroName?.raw, macroName.raw, unexpectedBetweenMacroNameAndGenericArgumentClause?.raw, genericArgumentClause?.raw, @@ -2876,7 +2883,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea } } - public var unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? { + public var unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } @@ -2885,42 +2892,60 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea } } + public var moduleSelector: ModuleSelectorSyntax? { + get { + return Syntax(self).child(at: 7)?.cast(ModuleSelectorSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + } + } + + public var unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + } + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be ``. public var macroName: TokenSyntax { get { - return Syntax(self).child(at: 7)!.cast(TokenSyntax.self) + return Syntax(self).child(at: 9)!.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 9, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 10)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 10, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var genericArgumentClause: GenericArgumentClauseSyntax? { get { - return Syntax(self).child(at: 9)?.cast(GenericArgumentClauseSyntax.self) + return Syntax(self).child(at: 11)?.cast(GenericArgumentClauseSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 9, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 11, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 10)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 12)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 10, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 12, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } @@ -2929,28 +2954,28 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea /// For syntax trees generated by the parser, this is guaranteed to be `(`. public var leftParen: TokenSyntax? { get { - return Syntax(self).child(at: 11)?.cast(TokenSyntax.self) + return Syntax(self).child(at: 13)?.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 11, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 13, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 12)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 14)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 12, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 14, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var arguments: LabeledExprListSyntax { get { - return Syntax(self).child(at: 13)!.cast(LabeledExprListSyntax.self) + return Syntax(self).child(at: 15)!.cast(LabeledExprListSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 13, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 15, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } @@ -2965,7 +2990,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea public func addArgument(_ element: LabeledExprSyntax) -> MacroExpansionDeclSyntax { var collection: RawSyntax let arena = RawSyntaxArena() - if let col = raw.layoutView!.children[13] { + if let col = raw.layoutView!.children[15] { collection = col.layoutView!.appending(element.raw, arena: arena) } else { collection = RawSyntax.makeLayout(kind: SyntaxKind.labeledExprList, @@ -2973,7 +2998,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea } return Syntax(self) .replacingChild( - at: 13, + at: 15, with: collection, rawNodeArena: arena, rawAllocationArena: arena @@ -2983,10 +3008,10 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea public var unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 14)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 16)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 14, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 16, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } @@ -2995,46 +3020,46 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea /// For syntax trees generated by the parser, this is guaranteed to be `)`. public var rightParen: TokenSyntax? { get { - return Syntax(self).child(at: 15)?.cast(TokenSyntax.self) + return Syntax(self).child(at: 17)?.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 15, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 17, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 16)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 18)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 16, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 18, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var trailingClosure: ClosureExprSyntax? { get { - return Syntax(self).child(at: 17)?.cast(ClosureExprSyntax.self) + return Syntax(self).child(at: 19)?.cast(ClosureExprSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 17, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 19, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 18)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 20)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 18, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 20, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } public var additionalTrailingClosures: MultipleTrailingClosureElementListSyntax { get { - return Syntax(self).child(at: 19)!.cast(MultipleTrailingClosureElementListSyntax.self) + return Syntax(self).child(at: 21)!.cast(MultipleTrailingClosureElementListSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 19, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 21, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } @@ -3049,7 +3074,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea public func addAdditionalTrailingClosure(_ element: MultipleTrailingClosureElementSyntax) -> MacroExpansionDeclSyntax { var collection: RawSyntax let arena = RawSyntaxArena() - if let col = raw.layoutView!.children[19] { + if let col = raw.layoutView!.children[21] { collection = col.layoutView!.appending(element.raw, arena: arena) } else { collection = RawSyntax.makeLayout(kind: SyntaxKind.multipleTrailingClosureElementList, @@ -3057,7 +3082,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea } return Syntax(self) .replacingChild( - at: 19, + at: 21, with: collection, rawNodeArena: arena, rawAllocationArena: arena @@ -3067,10 +3092,10 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea public var unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 20)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 22)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 20, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) + self = Syntax(self).replacingChild(at: 22, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionDeclSyntax.self) } } @@ -3081,7 +3106,9 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea \Self.modifiers, \Self.unexpectedBetweenModifiersAndPound, \Self.pound, - \Self.unexpectedBetweenPoundAndMacroName, + \Self.unexpectedBetweenPoundAndModuleSelector, + \Self.moduleSelector, + \Self.unexpectedBetweenModuleSelectorAndMacroName, \Self.macroName, \Self.unexpectedBetweenMacroNameAndGenericArgumentClause, \Self.genericArgumentClause, @@ -3106,6 +3133,7 @@ public struct MacroExpansionDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _Lea /// ### Children /// /// - `pound`: `#` +/// - `moduleSelector`: ``ModuleSelectorSyntax``? /// - `macroName`: `` /// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``? /// - `leftParen`: `(`? @@ -3136,7 +3164,9 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea leadingTrivia: Trivia? = nil, _ unexpectedBeforePound: UnexpectedNodesSyntax? = nil, pound: TokenSyntax = .poundToken(), - _ unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + _ unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? = nil, macroName: TokenSyntax, _ unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -3158,7 +3188,9 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea self = withExtendedLifetime((RawSyntaxArena(), ( unexpectedBeforePound, pound, - unexpectedBetweenPoundAndMacroName, + unexpectedBetweenPoundAndModuleSelector, + moduleSelector, + unexpectedBetweenModuleSelectorAndMacroName, macroName, unexpectedBetweenMacroNameAndGenericArgumentClause, genericArgumentClause, @@ -3177,7 +3209,9 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea let layout: [RawSyntax?] = [ unexpectedBeforePound?.raw, pound.raw, - unexpectedBetweenPoundAndMacroName?.raw, + unexpectedBetweenPoundAndModuleSelector?.raw, + moduleSelector?.raw, + unexpectedBetweenModuleSelectorAndMacroName?.raw, macroName.raw, unexpectedBetweenMacroNameAndGenericArgumentClause?.raw, genericArgumentClause?.raw, @@ -3227,7 +3261,7 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea } } - public var unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? { + public var unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) } @@ -3236,42 +3270,60 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea } } + public var moduleSelector: ModuleSelectorSyntax? { + get { + return Syntax(self).child(at: 3)?.cast(ModuleSelectorSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + } + } + + public var unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + } + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be ``. public var macroName: TokenSyntax { get { - return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) + return Syntax(self).child(at: 5)!.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var genericArgumentClause: GenericArgumentClauseSyntax? { get { - return Syntax(self).child(at: 5)?.cast(GenericArgumentClauseSyntax.self) + return Syntax(self).child(at: 7)?.cast(GenericArgumentClauseSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } @@ -3280,28 +3332,28 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea /// For syntax trees generated by the parser, this is guaranteed to be `(`. public var leftParen: TokenSyntax? { get { - return Syntax(self).child(at: 7)?.cast(TokenSyntax.self) + return Syntax(self).child(at: 9)?.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 9, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 10)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 10, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var arguments: LabeledExprListSyntax { get { - return Syntax(self).child(at: 9)!.cast(LabeledExprListSyntax.self) + return Syntax(self).child(at: 11)!.cast(LabeledExprListSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 9, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 11, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } @@ -3316,7 +3368,7 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea public func addArgument(_ element: LabeledExprSyntax) -> MacroExpansionExprSyntax { var collection: RawSyntax let arena = RawSyntaxArena() - if let col = raw.layoutView!.children[9] { + if let col = raw.layoutView!.children[11] { collection = col.layoutView!.appending(element.raw, arena: arena) } else { collection = RawSyntax.makeLayout(kind: SyntaxKind.labeledExprList, @@ -3324,7 +3376,7 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea } return Syntax(self) .replacingChild( - at: 9, + at: 11, with: collection, rawNodeArena: arena, rawAllocationArena: arena @@ -3334,10 +3386,10 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea public var unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 10)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 12)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 10, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 12, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } @@ -3346,46 +3398,46 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea /// For syntax trees generated by the parser, this is guaranteed to be `)`. public var rightParen: TokenSyntax? { get { - return Syntax(self).child(at: 11)?.cast(TokenSyntax.self) + return Syntax(self).child(at: 13)?.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 11, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 13, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 12)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 14)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 12, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 14, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var trailingClosure: ClosureExprSyntax? { get { - return Syntax(self).child(at: 13)?.cast(ClosureExprSyntax.self) + return Syntax(self).child(at: 15)?.cast(ClosureExprSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 13, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 15, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 14)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 16)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 14, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 16, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public var additionalTrailingClosures: MultipleTrailingClosureElementListSyntax { get { - return Syntax(self).child(at: 15)!.cast(MultipleTrailingClosureElementListSyntax.self) + return Syntax(self).child(at: 17)!.cast(MultipleTrailingClosureElementListSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 15, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 17, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } @@ -3400,7 +3452,7 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea public func addAdditionalTrailingClosure(_ element: MultipleTrailingClosureElementSyntax) -> MacroExpansionExprSyntax { var collection: RawSyntax let arena = RawSyntaxArena() - if let col = raw.layoutView!.children[15] { + if let col = raw.layoutView!.children[17] { collection = col.layoutView!.appending(element.raw, arena: arena) } else { collection = RawSyntax.makeLayout(kind: SyntaxKind.multipleTrailingClosureElementList, @@ -3408,7 +3460,7 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea } return Syntax(self) .replacingChild( - at: 15, + at: 17, with: collection, rawNodeArena: arena, rawAllocationArena: arena @@ -3418,17 +3470,19 @@ public struct MacroExpansionExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea public var unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 16)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 18)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 16, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) + self = Syntax(self).replacingChild(at: 18, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MacroExpansionExprSyntax.self) } } public static let structure: SyntaxNodeStructure = .layout([ \Self.unexpectedBeforePound, \Self.pound, - \Self.unexpectedBetweenPoundAndMacroName, + \Self.unexpectedBetweenPoundAndModuleSelector, + \Self.moduleSelector, + \Self.unexpectedBetweenModuleSelectorAndMacroName, \Self.macroName, \Self.unexpectedBetweenMacroNameAndGenericArgumentClause, \Self.genericArgumentClause, @@ -4208,6 +4262,7 @@ public struct MemberBlockSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNode /// /// - `baseType`: ``TypeSyntax`` /// - `period`: `.` +/// - `moduleSelector`: ``ModuleSelectorSyntax``? /// - `name`: (`` | `self`) /// - `genericArgumentClause`: ``GenericArgumentClauseSyntax``? public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyntaxNodeProtocol { @@ -4234,7 +4289,9 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn baseType: some TypeSyntaxProtocol, _ unexpectedBetweenBaseTypeAndPeriod: UnexpectedNodesSyntax? = nil, period: TokenSyntax = .periodToken(), - _ unexpectedBetweenPeriodAndName: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenPeriodAndModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + _ unexpectedBetweenModuleSelectorAndName: UnexpectedNodesSyntax? = nil, name: TokenSyntax, _ unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -4248,7 +4305,9 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn baseType, unexpectedBetweenBaseTypeAndPeriod, period, - unexpectedBetweenPeriodAndName, + unexpectedBetweenPeriodAndModuleSelector, + moduleSelector, + unexpectedBetweenModuleSelectorAndName, name, unexpectedBetweenNameAndGenericArgumentClause, genericArgumentClause, @@ -4259,7 +4318,9 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn baseType.raw, unexpectedBetweenBaseTypeAndPeriod?.raw, period.raw, - unexpectedBetweenPeriodAndName?.raw, + unexpectedBetweenPeriodAndModuleSelector?.raw, + moduleSelector?.raw, + unexpectedBetweenModuleSelectorAndName?.raw, name.raw, unexpectedBetweenNameAndGenericArgumentClause?.raw, genericArgumentClause?.raw, @@ -4315,7 +4376,7 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn } } - public var unexpectedBetweenPeriodAndName: UnexpectedNodesSyntax? { + public var unexpectedBetweenPeriodAndModuleSelector: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) } @@ -4324,6 +4385,24 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn } } + public var moduleSelector: ModuleSelectorSyntax? { + get { + return Syntax(self).child(at: 5)?.cast(ModuleSelectorSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + } + } + + public var unexpectedBetweenModuleSelectorAndName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + } + } + /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: @@ -4331,37 +4410,37 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn /// - `self` public var name: TokenSyntax { get { - return Syntax(self).child(at: 5)!.cast(TokenSyntax.self) + return Syntax(self).child(at: 7)!.cast(TokenSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) } } public var unexpectedBetweenNameAndGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) } } public var genericArgumentClause: GenericArgumentClauseSyntax? { get { - return Syntax(self).child(at: 7)?.cast(GenericArgumentClauseSyntax.self) + return Syntax(self).child(at: 9)?.cast(GenericArgumentClauseSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + self = Syntax(self).replacingChild(at: 9, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) } } public var unexpectedAfterGenericArgumentClause: UnexpectedNodesSyntax? { get { - return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + return Syntax(self).child(at: 10)?.cast(UnexpectedNodesSyntax.self) } set(value) { - self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) + self = Syntax(self).replacingChild(at: 10, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(MemberTypeSyntax.self) } } @@ -4370,7 +4449,9 @@ public struct MemberTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyn \Self.baseType, \Self.unexpectedBetweenBaseTypeAndPeriod, \Self.period, - \Self.unexpectedBetweenPeriodAndName, + \Self.unexpectedBetweenPeriodAndModuleSelector, + \Self.moduleSelector, + \Self.unexpectedBetweenModuleSelectorAndName, \Self.name, \Self.unexpectedBetweenNameAndGenericArgumentClause, \Self.genericArgumentClause, @@ -4536,6 +4617,10 @@ public struct MetatypeTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeS /// - `attributes`: ``AttributeListSyntax`` /// - `modifiers`: ``DeclModifierListSyntax`` /// - `placeholder`: `` +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct MissingDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -5161,6 +5246,134 @@ public struct MissingTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSy public static let structure: SyntaxNodeStructure = .layout([\Self.unexpectedBeforePlaceholder, \Self.placeholder, \Self.unexpectedAfterPlaceholder]) } +// MARK: - ModuleSelectorSyntax + +/// ### Children +/// +/// - `moduleName`: `` +/// - `colonColon`: `::` +/// +/// ### Contained in +/// +/// - ``DeclReferenceExprSyntax``.``DeclReferenceExprSyntax/moduleSelector`` +/// - ``IdentifierTypeSyntax``.``IdentifierTypeSyntax/moduleSelector`` +/// - ``MacroExpansionDeclSyntax``.``MacroExpansionDeclSyntax/moduleSelector`` +/// - ``MacroExpansionExprSyntax``.``MacroExpansionExprSyntax/moduleSelector`` +/// - ``MemberTypeSyntax``.``MemberTypeSyntax/moduleSelector`` +public struct ModuleSelectorSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .moduleSelector else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeModuleName: UnexpectedNodesSyntax? = nil, + moduleName: TokenSyntax, + _ unexpectedBetweenModuleNameAndColonColon: UnexpectedNodesSyntax? = nil, + colonColon: TokenSyntax = .colonColonToken(), + _ unexpectedAfterColonColon: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), ( + unexpectedBeforeModuleName, + moduleName, + unexpectedBetweenModuleNameAndColonColon, + colonColon, + unexpectedAfterColonColon + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeModuleName?.raw, + moduleName.raw, + unexpectedBetweenModuleNameAndColonColon?.raw, + colonColon.raw, + unexpectedAfterColonColon?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.moduleSelector, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeModuleName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(ModuleSelectorSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be ``. + public var moduleName: TokenSyntax { + get { + return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(ModuleSelectorSyntax.self) + } + } + + public var unexpectedBetweenModuleNameAndColonColon: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(ModuleSelectorSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `::`. + public var colonColon: TokenSyntax { + get { + return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(ModuleSelectorSyntax.self) + } + } + + public var unexpectedAfterColonColon: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(ModuleSelectorSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeModuleName, + \Self.moduleName, + \Self.unexpectedBetweenModuleNameAndColonColon, + \Self.colonColon, + \Self.unexpectedAfterColonColon + ]) +} + // MARK: - MultipleTrailingClosureElementSyntax /// ### Children diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift index 2431b585fc8..1ad996a81d0 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift @@ -2062,6 +2062,87 @@ public struct SpecializeTargetFunctionArgumentSyntax: SyntaxProtocol, SyntaxHash ]) } +// MARK: - SpecializedAttributeArgumentSyntax + +/// The generic where clause for the `@specialized` attribute +/// +/// ### Children +/// +/// - `genericWhereClause`: ``GenericWhereClauseSyntax`` +/// +/// ### Contained in +/// +/// - ``AttributeSyntax``.``AttributeSyntax/arguments`` +public struct SpecializedAttributeArgumentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .specializedAttributeArgument else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeGenericWhereClause: UnexpectedNodesSyntax? = nil, + genericWhereClause: GenericWhereClauseSyntax, + _ unexpectedAfterGenericWhereClause: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), (unexpectedBeforeGenericWhereClause, genericWhereClause, unexpectedAfterGenericWhereClause))) { (arena, _) in + let layout: [RawSyntax?] = [unexpectedBeforeGenericWhereClause?.raw, genericWhereClause.raw, unexpectedAfterGenericWhereClause?.raw] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.specializedAttributeArgument, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeGenericWhereClause: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(SpecializedAttributeArgumentSyntax.self) + } + } + + public var genericWhereClause: GenericWhereClauseSyntax { + get { + return Syntax(self).child(at: 1)!.cast(GenericWhereClauseSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(SpecializedAttributeArgumentSyntax.self) + } + } + + public var unexpectedAfterGenericWhereClause: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(SpecializedAttributeArgumentSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([\Self.unexpectedBeforeGenericWhereClause, \Self.genericWhereClause, \Self.unexpectedAfterGenericWhereClause]) +} + // MARK: - StringLiteralExprSyntax /// A string literal. @@ -3108,6 +3189,10 @@ public struct SubscriptCallExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Leaf /// - `returnClause`: ``ReturnClauseSyntax`` /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? /// - `accessorBlock`: ``AccessorBlockSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct SubscriptDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -4345,7 +4430,7 @@ public struct SwitchDefaultLabelSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyn /// } /// ``` /// -/// A switch ecpression may be declared without any cases. +/// A switch expression may be declared without any cases. /// /// ### Children /// diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift index 88aa709709f..40a3bc8890c 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift @@ -1851,6 +1851,10 @@ public struct TupleTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSynt /// - `genericParameterClause`: ``GenericParameterClauseSyntax``? /// - `initializer`: ``TypeInitializerClauseSyntax`` /// - `genericWhereClause`: ``GenericWhereClauseSyntax``? +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct TypeAliasDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -2602,6 +2606,63 @@ public struct TypeInitializerClauseSyntax: SyntaxProtocol, SyntaxHashable, _Leaf ]) } +// MARK: - UnexpectedCodeDeclSyntax + +/// Unexpected code at declaration position +/// +/// ### Children +/// +/// - `unexpectedCode`: ``UnexpectedNodesSyntax`` +public struct UnexpectedCodeDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .unexpectedCodeDecl else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + unexpectedCode: UnexpectedNodesSyntax, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), (unexpectedCode))) { (arena, _) in + let layout: [RawSyntax?] = [unexpectedCode.raw] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.unexpectedCodeDecl, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedCode: UnexpectedNodesSyntax { + get { + return Syntax(self).child(at: 0)!.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UnexpectedCodeDeclSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([\Self.unexpectedCode]) +} + // MARK: - UnresolvedAsExprSyntax /// The `as` keyword without any operands. @@ -3082,6 +3143,222 @@ public struct UnsafeExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn ]) } +// MARK: - UsingDeclSyntax + +/// A `using` declaration, currently used to control actor isolation within the current file. +/// +/// An example of a `using` declaration is +/// +/// ```swift +/// using @MainActor +/// ``` +/// +/// - Note: Requires experimental feature `defaultIsolationPerFile`. +/// +/// ### Children +/// +/// - `usingKeyword`: `using` +/// - `specifier`: (``AttributeSyntax`` | ``) +@_spi(ExperimentalLanguageFeatures) +public struct UsingDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { + public enum Specifier: SyntaxChildChoices, SyntaxHashable { + case attribute(AttributeSyntax) + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be ``. + case modifier(TokenSyntax) + + public var _syntaxNode: Syntax { + switch self { + case .attribute(let node): + return node._syntaxNode + case .modifier(let node): + return node._syntaxNode + } + } + + public init(_ node: AttributeSyntax) { + self = .attribute(node) + } + + public init(_ node: TokenSyntax) { + self = .modifier(node) + } + + public init?(_ node: __shared some SyntaxProtocol) { + if let node = node.as(AttributeSyntax.self) { + self = .attribute(node) + } else if let node = node.as(TokenSyntax.self) { + self = .modifier(node) + } else { + return nil + } + } + + public static var structure: SyntaxNodeStructure { + return .choices([.node(AttributeSyntax.self), .node(TokenSyntax.self)]) + } + + /// Checks if the current syntax node can be cast to ``AttributeSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: AttributeSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``AttributeSyntax``. + /// + /// - Returns: An instance of ``AttributeSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: AttributeSyntax.Type) -> AttributeSyntax? { + return AttributeSyntax.init(self) + } + + /// Force-casts the current syntax node to ``AttributeSyntax``. + /// + /// - Returns: An instance of ``AttributeSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: AttributeSyntax.Type) -> AttributeSyntax { + return self.as(AttributeSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``TokenSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: TokenSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``TokenSyntax``. + /// + /// - Returns: An instance of ``TokenSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: TokenSyntax.Type) -> TokenSyntax? { + return TokenSyntax.init(self) + } + + /// Force-casts the current syntax node to ``TokenSyntax``. + /// + /// - Returns: An instance of ``TokenSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: TokenSyntax.Type) -> TokenSyntax { + return self.as(TokenSyntax.self)! + } + } + + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .usingDecl else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - usingKeyword: The `using` keyword for this declaration. + /// - specifier: The specifier that could be either an attribute or a modifier. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeUsingKeyword: UnexpectedNodesSyntax? = nil, + usingKeyword: TokenSyntax = .keyword(.using), + _ unexpectedBetweenUsingKeywordAndSpecifier: UnexpectedNodesSyntax? = nil, + specifier: Specifier, + _ unexpectedAfterSpecifier: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), ( + unexpectedBeforeUsingKeyword, + usingKeyword, + unexpectedBetweenUsingKeywordAndSpecifier, + specifier, + unexpectedAfterSpecifier + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeUsingKeyword?.raw, + usingKeyword.raw, + unexpectedBetweenUsingKeywordAndSpecifier?.raw, + specifier.raw, + unexpectedAfterSpecifier?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.usingDecl, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeUsingKeyword: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + /// The `using` keyword for this declaration. + /// + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `using`. + public var usingKeyword: TokenSyntax { + get { + return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + public var unexpectedBetweenUsingKeywordAndSpecifier: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + /// The specifier that could be either an attribute or a modifier. + public var specifier: Specifier { + get { + return Syntax(self).child(at: 3)!.cast(Specifier.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + public var unexpectedAfterSpecifier: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeUsingKeyword, + \Self.usingKeyword, + \Self.unexpectedBetweenUsingKeywordAndSpecifier, + \Self.specifier, + \Self.unexpectedAfterSpecifier + ]) +} + // MARK: - ValueBindingPatternSyntax /// ### Children @@ -3219,6 +3496,10 @@ public struct ValueBindingPatternSyntax: PatternSyntaxProtocol, SyntaxHashable, /// - `modifiers`: ``DeclModifierListSyntax`` /// - `bindingSpecifier`: (`let` | `var` | `inout` | `_mutating` | `_borrowing` | `_consuming`) /// - `bindings`: ``PatternBindingListSyntax`` +/// +/// ### Contained in +/// +/// - ``ABIAttributeArgumentsSyntax``.``ABIAttributeArgumentsSyntax/provider`` public struct VariableDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { public let _syntaxNode: Syntax diff --git a/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift b/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift index a7ef8ab2435..c73835c7a84 100644 --- a/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift +++ b/Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift @@ -467,6 +467,10 @@ extension TokenSyntax: SyntaxExpressibleByStringInterpolation { // Silence warning that TokenSyntax has a retroactive conformance to `ExpressibleByStringInterpolation` through // `SyntaxExpressibleByStringInterpolation`. extension TokenSyntax: Swift.ExpressibleByStringInterpolation {} +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension TokenSyntax: Swift.ExpressibleByStringLiteral {} +extension TokenSyntax: Swift.ExpressibleByExtendedGraphemeClusterLiteral {} +extension TokenSyntax: Swift.ExpressibleByUnicodeScalarLiteral {} #endif // MARK: - Trivia expressible as string @@ -515,7 +519,13 @@ extension Trivia { } #if compiler(>=6) +// Silence warning that Trivia has a retroactive conformance to `ExpressibleByStringInterpolation` through +// `SyntaxExpressibleByStringInterpolation`. extension Trivia: Swift.ExpressibleByStringInterpolation {} +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension Trivia: Swift.ExpressibleByStringLiteral {} +extension Trivia: Swift.ExpressibleByExtendedGraphemeClusterLiteral {} +extension Trivia: Swift.ExpressibleByUnicodeScalarLiteral {} #else extension Trivia: ExpressibleByStringInterpolation {} #endif diff --git a/Sources/SwiftSyntaxBuilder/SyntaxParsable+ExpressibleByStringInterpolation.swift b/Sources/SwiftSyntaxBuilder/SyntaxParsable+ExpressibleByStringInterpolation.swift index 3c933396c06..662106b24b7 100644 --- a/Sources/SwiftSyntaxBuilder/SyntaxParsable+ExpressibleByStringInterpolation.swift +++ b/Sources/SwiftSyntaxBuilder/SyntaxParsable+ExpressibleByStringInterpolation.swift @@ -37,9 +37,9 @@ import os /// Only set from `withStringInterpolationParsingErrorsSuppressed`, which is only intended for testing purposes that are /// single-threaded. #if swift(>=6) -fileprivate nonisolated(unsafe) var suppressStringInterpolationParsingErrors = false +private nonisolated(unsafe) var suppressStringInterpolationParsingErrors = false #else -fileprivate var suppressStringInterpolationParsingErrors = false +private var suppressStringInterpolationParsingErrors = false #endif /// Run the body, disabling any runtime warnings about syntax error in string diff --git a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift index 398e1b56af9..22a143f7258 100644 --- a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift +++ b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift @@ -1051,7 +1051,9 @@ extension MacroExpansionDeclSyntax { modifiers: DeclModifierListSyntax = [], unexpectedBetweenModifiersAndPound: UnexpectedNodesSyntax? = nil, pound: TokenSyntax = .poundToken(), - unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? = nil, macroName: TokenSyntax, unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -1076,7 +1078,9 @@ extension MacroExpansionDeclSyntax { modifiers: modifiers, unexpectedBetweenModifiersAndPound, pound: pound, - unexpectedBetweenPoundAndMacroName, + unexpectedBetweenPoundAndModuleSelector, + moduleSelector: moduleSelector, + unexpectedBetweenModuleSelectorAndMacroName, macroName: macroName, unexpectedBetweenMacroNameAndGenericArgumentClause, genericArgumentClause: genericArgumentClause, @@ -1102,7 +1106,9 @@ extension MacroExpansionExprSyntax { leadingTrivia: Trivia? = nil, unexpectedBeforePound: UnexpectedNodesSyntax? = nil, pound: TokenSyntax = .poundToken(), - unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + unexpectedBetweenPoundAndModuleSelector: UnexpectedNodesSyntax? = nil, + moduleSelector: ModuleSelectorSyntax? = nil, + unexpectedBetweenModuleSelectorAndMacroName: UnexpectedNodesSyntax? = nil, macroName: TokenSyntax, unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, genericArgumentClause: GenericArgumentClauseSyntax? = nil, @@ -1123,7 +1129,9 @@ extension MacroExpansionExprSyntax { leadingTrivia: leadingTrivia, unexpectedBeforePound, pound: pound, - unexpectedBetweenPoundAndMacroName, + unexpectedBetweenPoundAndModuleSelector, + moduleSelector: moduleSelector, + unexpectedBetweenModuleSelectorAndMacroName, macroName: macroName, unexpectedBetweenMacroNameAndGenericArgumentClause, genericArgumentClause: genericArgumentClause, diff --git a/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift b/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift index 6629c0a42de..a60a0524b6f 100644 --- a/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift +++ b/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift @@ -599,7 +599,62 @@ extension KeyPathSubscriptComponentSyntax { } extension MacroExpansionDeclSyntax { - @available(*, deprecated, message: "Use an initializer with pound, macroName, genericArgumentClause, arguments argument(s).") + /// A convenience initializer that allows initializing syntax collections using result builders + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeAttributes: UnexpectedNodesSyntax? = nil, + attributes: AttributeListSyntax = [], + unexpectedBetweenAttributesAndModifiers: UnexpectedNodesSyntax? = nil, + modifiers: DeclModifierListSyntax = [], + unexpectedBetweenModifiersAndPound: UnexpectedNodesSyntax? = nil, + pound: TokenSyntax = .poundToken(), + unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + macroName: TokenSyntax, + unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeAttributes, + attributes: attributes, + unexpectedBetweenAttributesAndModifiers, + modifiers: modifiers, + unexpectedBetweenModifiersAndPound, + pound: pound, + unexpectedBetweenPoundAndMacroName, + macroName: macroName, + unexpectedBetweenMacroNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedBetweenGenericArgumentClauseAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } +} + +extension MacroExpansionDeclSyntax { + @available(*, deprecated, message: "Use an initializer with pound, moduleSelector, macroName, genericArgumentClause, arguments argument(s).") @_disfavoredOverload /// A convenience initializer that allows initializing syntax collections using result builders public init( @@ -656,7 +711,54 @@ extension MacroExpansionDeclSyntax { } extension MacroExpansionExprSyntax { - @available(*, deprecated, message: "Use an initializer with pound, macroName, genericArgumentClause, arguments argument(s).") + /// A convenience initializer that allows initializing syntax collections using result builders + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforePound: UnexpectedNodesSyntax? = nil, + pound: TokenSyntax = .poundToken(), + unexpectedBetweenPoundAndMacroName: UnexpectedNodesSyntax? = nil, + macroName: TokenSyntax, + unexpectedBetweenMacroNameAndGenericArgumentClause: UnexpectedNodesSyntax? = nil, + genericArgumentClause: GenericArgumentClauseSyntax? = nil, + unexpectedBetweenGenericArgumentClauseAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforePound, + pound: pound, + unexpectedBetweenPoundAndMacroName, + macroName: macroName, + unexpectedBetweenMacroNameAndGenericArgumentClause, + genericArgumentClause: genericArgumentClause, + unexpectedBetweenGenericArgumentClauseAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } +} + +extension MacroExpansionExprSyntax { + @available(*, deprecated, message: "Use an initializer with pound, moduleSelector, macroName, genericArgumentClause, arguments argument(s).") @_disfavoredOverload /// A convenience initializer that allows initializing syntax collections using result builders public init( diff --git a/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift b/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift index eaab028c7a1..a1985def3d8 100644 --- a/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift +++ b/Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift @@ -23,167 +23,251 @@ extension AccessorBlockFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AccessorBlockFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AccessorBlockFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AccessorBlockSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AccessorBlockSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AccessorBlockSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AccessorDeclSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AccessorDeclSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AccessorDeclSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AttributeClauseFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AttributeClauseFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AttributeClauseFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AttributeSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AttributeSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AttributeSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AvailabilityMacroDefinitionFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AvailabilityMacroDefinitionFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AvailabilityMacroDefinitionFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension CatchClauseSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension CatchClauseSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension CatchClauseSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension ClosureParameterSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension ClosureParameterSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension ClosureParameterSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension CodeBlockFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension CodeBlockFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension CodeBlockFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension CodeBlockItemSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension CodeBlockItemSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension CodeBlockItemSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension CodeBlockSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension CodeBlockSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension CodeBlockSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension DeclSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension DeclSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension DeclSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension EnumCaseParameterSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension EnumCaseParameterSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension EnumCaseParameterSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension ExprSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension ExprSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension ExprSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension FunctionParameterSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension FunctionParameterSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension FunctionParameterSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension GenericParameterClauseSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension GenericParameterClauseSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension GenericParameterClauseSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension MemberBlockItemListFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension MemberBlockItemListFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension MemberBlockItemListFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension MemberBlockSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension MemberBlockSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension MemberBlockSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension PatternSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension PatternSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension PatternSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension SourceFileSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension SourceFileSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension SourceFileSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension StmtSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension StmtSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension StmtSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension SwitchCaseSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension SwitchCaseSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension SwitchCaseSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension TypeSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension TypeSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension TypeSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension VersionTupleSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension VersionTupleSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension VersionTupleSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AccessorDeclListSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AccessorDeclListSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AccessorDeclListSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension AttributeListSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension AttributeListSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension AttributeListSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension CodeBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension CodeBlockItemListSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension CodeBlockItemListSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif extension MemberBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {} #if compiler(>=6) extension MemberBlockItemListSyntax: Swift.ExpressibleByStringInterpolation {} + +// Work around https://github.com/swiftlang/swift/issues/85153 by restating the implicit conformances. +extension MemberBlockItemListSyntax: Swift.ExpressibleByStringLiteral, Swift.ExpressibleByExtendedGraphemeClusterLiteral, Swift.ExpressibleByUnicodeScalarLiteral {} #endif diff --git a/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift b/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift index 013531a1507..56efff677ce 100644 --- a/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift +++ b/Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift @@ -12,11 +12,13 @@ #if compiler(>=6) public import SwiftDiagnostics +public import SwiftIfConfig internal import SwiftOperators public import SwiftSyntax public import SwiftSyntaxMacros #else import SwiftDiagnostics +import SwiftIfConfig import SwiftOperators import SwiftSyntax import SwiftSyntaxMacros @@ -62,6 +64,9 @@ public class BasicMacroExpansionContext { /// /// Used in conjunction with `expansionDiscriminator`. var uniqueNames: [String: Int] = [:] + + /// The build configuration that will be applied to the expanded code. + var buildConfiguration: (any BuildConfiguration)? = nil } /// State shared by different instances of the macro expansion context, @@ -82,12 +87,14 @@ public class BasicMacroExpansionContext { public init( lexicalContext: [Syntax] = [], expansionDiscriminator: String = "__macro_local_", - sourceFiles: [SourceFileSyntax: KnownSourceFile] = [:] + sourceFiles: [SourceFileSyntax: KnownSourceFile] = [:], + buildConfiguration: (any BuildConfiguration)? = nil ) { self.sharedState = SharedState() self.lexicalContext = lexicalContext self.expansionDiscriminator = expansionDiscriminator self.sharedState.sourceFiles = sourceFiles + self.sharedState.buildConfiguration = buildConfiguration } /// Create a new macro evaluation context that shares most of its global diff --git a/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift index 9cdbf16d2b7..b7ddfc6119b 100644 --- a/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift +++ b/Sources/SwiftSyntaxMacroExpansion/IndentationUtils.swift @@ -61,7 +61,7 @@ extension String { // MARK: SyntaxProtocol.stripp -fileprivate class IndentationStripper: SyntaxRewriter { +private class IndentationStripper: SyntaxRewriter { override func visit(_ token: TokenSyntax) -> TokenSyntax { if token.leadingTrivia.contains(where: \.isNewline) || token.trailingTrivia.contains(where: \.isNewline) { return diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift index 0f7a470118e..697b802972d 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroReplacement.swift @@ -103,7 +103,7 @@ extension MacroDefinition { } } -fileprivate class ParameterReplacementVisitor: OnlyLiteralExprChecker { +private class ParameterReplacementVisitor: OnlyLiteralExprChecker { let macro: MacroDeclSyntax var replacements: [MacroDefinition.Replacement] = [] var genericReplacements: [MacroDefinition.GenericArgumentReplacement] = [] diff --git a/Sources/SwiftSyntaxMacros/CMakeLists.txt b/Sources/SwiftSyntaxMacros/CMakeLists.txt index 1d046f2c35a..b89a2b13826 100644 --- a/Sources/SwiftSyntaxMacros/CMakeLists.txt +++ b/Sources/SwiftSyntaxMacros/CMakeLists.txt @@ -29,5 +29,6 @@ add_swift_syntax_library(SwiftSyntaxMacros ) target_link_swift_syntax_libraries(SwiftSyntaxMacros PUBLIC + SwiftIfConfig SwiftSyntaxBuilder ) diff --git a/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift b/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift index 608a5760743..53bee858e3d 100644 --- a/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift +++ b/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift @@ -12,10 +12,12 @@ #if compiler(>=6) public import SwiftDiagnostics +public import SwiftIfConfig public import SwiftSyntax import SwiftSyntaxBuilder #else import SwiftDiagnostics +import SwiftIfConfig import SwiftSyntax import SwiftSyntaxBuilder #endif @@ -70,6 +72,15 @@ public protocol MacroExpansionContext: AnyObject { /// This array can be empty if there is no context, for example when a /// freestanding macro is used at file scope. var lexicalContext: [Syntax] { get } + + /// Returns the build configuration that will be used with the generated + /// source code. Macro implementations can use this information to determine + /// the context into which they are generating code. + /// + /// When the build configuration is not known, for example because the + /// compiler has not provided this information to the macro implementation, + /// this returns nil. + var buildConfiguration: (any BuildConfiguration)? { get } } extension MacroExpansionContext { @@ -187,3 +198,8 @@ public enum SourceLocationFilePathMode { /// e.g., `/home/taylor/alison.swift`. case filePath } + +extension MacroExpansionContext { + /// Default implementation that produces no build configuration. + public var buildConfiguration: (any BuildConfiguration)? { nil } +} diff --git a/Sources/SwiftSyntaxMacros/MacroExpansionDiagnosticMessages.swift b/Sources/SwiftSyntaxMacros/MacroExpansionDiagnosticMessages.swift index eaa1d9526e7..0b13f8b6156 100644 --- a/Sources/SwiftSyntaxMacros/MacroExpansionDiagnosticMessages.swift +++ b/Sources/SwiftSyntaxMacros/MacroExpansionDiagnosticMessages.swift @@ -16,7 +16,7 @@ public import SwiftDiagnostics import SwiftDiagnostics #endif -fileprivate let diagnosticDomain: String = "SwiftSyntaxMacros" +private let diagnosticDomain: String = "SwiftSyntaxMacros" /// An error during macro expansion that is described by its message. /// diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/Macro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/Macro.swift index 6203a6f9127..3045b364475 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/Macro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/Macro.swift @@ -10,9 +10,18 @@ // //===----------------------------------------------------------------------===// +#if compiler(>=6.2) +/// Describes a macro. +public protocol Macro: SendableMetatype { + /// How the resulting expansion should be formatted, `.auto` by default. + /// Use `.disabled` for the expansion to be used as is. + static var formatMode: FormatMode { get } +} +#else /// Describes a macro. public protocol Macro { /// How the resulting expansion should be formatted, `.auto` by default. /// Use `.disabled` for the expansion to be used as is. static var formatMode: FormatMode { get } } +#endif diff --git a/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift b/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift index 7d7e490cbc0..3450dbaac06 100644 --- a/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift +++ b/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift @@ -13,6 +13,7 @@ #if compiler(>=6) import SwiftBasicFormat public import SwiftDiagnostics +public import SwiftIfConfig @_spi(FixItApplier) import SwiftIDEUtils import SwiftParser import SwiftParserDiagnostics @@ -24,6 +25,7 @@ private import _SwiftSyntaxGenericTestSupport import SwiftBasicFormat import SwiftDiagnostics @_spi(FixItApplier) import SwiftIDEUtils +import SwiftIfConfig import SwiftParser import SwiftParserDiagnostics import SwiftSyntax @@ -497,6 +499,7 @@ public func assertMacroExpansion( testModuleName: String = "TestModule", testFileName: String = "test.swift", indentationWidth: Trivia = .spaces(4), + buildConfiguration: (any BuildConfiguration)? = nil, failureHandler: (TestFailureSpec) -> Void, fileID: StaticString = #fileID, filePath: StaticString = #filePath, @@ -509,7 +512,8 @@ public func assertMacroExpansion( // Expand all macros in the source. let context = BasicMacroExpansionContext( - sourceFiles: [origSourceFile: .init(moduleName: testModuleName, fullFilePath: testFileName)] + sourceFiles: [origSourceFile: .init(moduleName: testModuleName, fullFilePath: testFileName)], + buildConfiguration: buildConfiguration ) func contextGenerator(_ syntax: Syntax) -> BasicMacroExpansionContext { @@ -633,7 +637,7 @@ fileprivate extension FixIt.Change { replacement: replacingChildData.newChild.description ) - case .replaceText(range: let range, with: let newText, in: let syntax): + case .replaceText(let range, with: let newText, in: let syntax): let start = expansionContext.position(of: range.lowerBound, anchoredAt: syntax) let end = expansionContext.position(of: range.upperBound, anchoredAt: syntax) return SourceEdit(range: start..=6) +public import SwiftIfConfig public import SwiftSyntax public import SwiftSyntaxMacroExpansion public import SwiftSyntaxMacros @_spi(XCTestFailureLocation) public import SwiftSyntaxMacrosGenericTestSupport private import XCTest #else +import SwiftIfConfig import SwiftSyntax import SwiftSyntaxMacroExpansion import SwiftSyntaxMacros @@ -44,8 +46,9 @@ public typealias DiagnosticSpec = SwiftSyntaxMacrosGenericTestSupport.Diagnostic /// - testModuleName: The name of the test module to use. /// - testFileName: The name of the test file name to use. /// - indentationWidth: The indentation width used in the expansion. -/// -/// - SeeAlso: ``assertMacroExpansion(_:expandedSource:diagnostics:macroSpecs:applyFixIts:fixedSource:testModuleName:testFileName:indentationWidth:file:line:)`` +/// - buildConfiguration: a build configuration that will be made available +/// to the macro implementation +/// - SeeAlso: ``assertMacroExpansion(_:expandedSource:diagnostics:macroSpecs:applyFixIts:fixedSource:testModuleName:testFileName:indentationWidth:buildConfiguration:file:line:)`` /// to also specify the list of conformances passed to the macro expansion. public func assertMacroExpansion( _ originalSource: String, @@ -57,6 +60,7 @@ public func assertMacroExpansion( testModuleName: String = "TestModule", testFileName: String = "test.swift", indentationWidth: Trivia = .spaces(4), + buildConfiguration: (any BuildConfiguration)? = nil, file: StaticString = #filePath, line: UInt = #line ) { @@ -71,6 +75,7 @@ public func assertMacroExpansion( testModuleName: testModuleName, testFileName: testFileName, indentationWidth: indentationWidth, + buildConfiguration: buildConfiguration, file: file, line: line ) @@ -104,6 +109,7 @@ public func assertMacroExpansion( testModuleName: String = "TestModule", testFileName: String = "test.swift", indentationWidth: Trivia = .spaces(4), + buildConfiguration: (any BuildConfiguration)? = nil, file: StaticString = #filePath, line: UInt = #line ) { @@ -117,6 +123,7 @@ public func assertMacroExpansion( testModuleName: testModuleName, testFileName: testFileName, indentationWidth: indentationWidth, + buildConfiguration: buildConfiguration, failureHandler: { XCTFail($0.message, file: $0.location.staticFilePath, line: $0.location.unsignedLine) }, diff --git a/Sources/SwiftWarningControl/CMakeLists.txt b/Sources/SwiftWarningControl/CMakeLists.txt new file mode 100644 index 00000000000..f102e2358b9 --- /dev/null +++ b/Sources/SwiftWarningControl/CMakeLists.txt @@ -0,0 +1,20 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_swift_syntax_library(SwiftWarningControl + DiagnosticGroupInheritanceTree.swift + WarningGroupControl.swift + WarningControlDeclSyntax.swift + WarningControlRegionBuilder.swift + WarningControlRegions.swift + SyntaxProtocol+WarningControl.swift +) + +target_link_swift_syntax_libraries(SwiftWarningControl PUBLIC + SwiftSyntax + SwiftParser) diff --git a/Sources/SwiftWarningControl/DiagnosticGroupInheritanceTree.swift b/Sources/SwiftWarningControl/DiagnosticGroupInheritanceTree.swift new file mode 100644 index 00000000000..c4066dc43f8 --- /dev/null +++ b/Sources/SwiftWarningControl/DiagnosticGroupInheritanceTree.swift @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +/// A struct wrapper for a diagnostic group inheritance tree +/// represented with a dictionary of a group identifier to an array +/// of its sub-group identifiers. +@_spi(ExperimentalLanguageFeatures) +public struct DiagnosticGroupInheritanceTree { + private let subGroups: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]] + public init(subGroups: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]]) throws { + self.subGroups = subGroups + if hasCycle() { + throw WarningControlError.groupInheritanceCycle + } + } + init() { + self.subGroups = [:] + } + + /// Diagnostic groups that inherit from `group`. + func subgroups(of group: DiagnosticGroupIdentifier) -> [DiagnosticGroupIdentifier] { subGroups[group] ?? [] } +} + +extension DiagnosticGroupInheritanceTree { + // Check the subgroup tree for possible cycles + func hasCycle() -> Bool { + var visited: Set = [] + var recursionStack: Set = [] + func hasCycleFromGroup(_ group: DiagnosticGroupIdentifier) -> Bool { + if visited.insert(group).inserted { + recursionStack.insert(group) + let subgroups = self.subgroups(of: group) + for subGroup in subgroups { + if recursionStack.contains(subGroup) { + return true + } else if !visited.contains(subGroup), hasCycleFromGroup(subGroup) { + return true + } + } + } + recursionStack.remove(group) + return false + } + + for group in subGroups.keys { + if !visited.contains(group), hasCycleFromGroup(group) { + return true + } + } + return false + } +} + +/// Describes the kinds of diagnostics that can occur when processing warning +/// group control queries. This is an Error-conforming type so we can throw errors when +/// needed. +@_spi(ExperimentalLanguageFeatures) +public enum WarningControlError: Error, CustomStringConvertible { + case groupInheritanceCycle + + public var description: String { + switch self { + case .groupInheritanceCycle: + return "cycle detected in the warning group inheritance hierarchy" + } + } +} diff --git a/Sources/SwiftWarningControl/SwiftWarningControl.md b/Sources/SwiftWarningControl/SwiftWarningControl.md new file mode 100644 index 00000000000..9d6a78f0045 --- /dev/null +++ b/Sources/SwiftWarningControl/SwiftWarningControl.md @@ -0,0 +1,30 @@ +# SwiftWarningControl + +A library to evaluate `@warn` diagnostic group controls within a Swift syntax tree. + +## Overview + +Swift provides a mechanism to control the behavior of specific diagnostic groups for a given declaration's lexical scope with the `@warn` attribute. + +The syntax tree and its parser do not reason about warning group controls. The syntax tree produced by the parser represents the `@warn` attribute in a generic fashion, as it would any other basic attribute on a declaration. The per-declaration nature of the attribute means that for any given lexical scope, the behavior of a given diagnostic group can be queried by checking for the presence of this attribute in its parent declaration scope. + +```swift +@warn(Deprecate, as: error) +func foo() { + ... + @warn(Deprecate, as: warning) + func bar() { + ... + @warn("Deprecate", as: ignored, reason: "Foo") + func baz() { + ... + } + } +} +``` + +The `SwiftWarningControl` library provides a utility to determine, for a given source location and diagnostic group identifier, whether or not its behavior is affected by an `@warn` attribute of any of its parent declaration scope. + +* `SyntaxProtocol.getWarningGroupControl(for diagnosticGroupIdentifier:)` produces the behavior control specifier (`WarningGroupControl`: `error`, `warning`, `ignored`) which applies at this node. + +* `SyntaxProtocol.warningGroupControlRegionTree` holds a computed `WarningControlRegionTree` data structure value that can be used to efficiently test for the specified `WarningGroupControl` at a given source location and a given diagnostic group. diff --git a/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift b/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift new file mode 100644 index 00000000000..2b058d721a0 --- /dev/null +++ b/Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftDiagnostics +import SwiftSyntax + +extension SyntaxProtocol { + /// Get the warning emission behavior control for the specified diagnostic group + /// by determining its containing `WarningControlRegion`, if one is present. + /// Returns the syntactic control for the given diagnostic group, or `nil` if + /// there is not one. + /// - Parameters: + /// - for diagnosticGroupIdentifier: The identifier of the diagnostic group. + /// - globalControls: The global controls to consider, specified by the client (compiler) + /// representing module-wide diagnostic group emission configuration, for example + /// with `-Wwarning` and `-Werror` flags. These controls can be overriden at + /// finer-grained scopes with the `@warn` attribute. + @_spi(ExperimentalLanguageFeatures) + public func warningGroupControl( + for diagnosticGroupIdentifier: DiagnosticGroupIdentifier, + globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:], + groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil + ) -> WarningGroupControl? { + let warningControlRegions = root.warningGroupControlRegionTreeImpl( + globalControls: globalControls, + groupInheritanceTree: groupInheritanceTree, + containing: self.position + ) + return warningControlRegions.warningGroupControl(at: self.position, for: diagnosticGroupIdentifier) + } +} diff --git a/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift b/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift new file mode 100644 index 00000000000..6480600645a --- /dev/null +++ b/Sources/SwiftWarningControl/WarningControlDeclSyntax.swift @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +extension WithAttributesSyntax { + /// Compute a dictionary of all `@warn` diagnostic group behavior controls + /// specified on this warning control declaration scope. + var allWarningGroupControls: [DiagnosticGroupIdentifier: WarningGroupControl] { + attributes.reduce(into: [DiagnosticGroupIdentifier: WarningGroupControl]()) { result, attr in + // `@warn` attributes + guard case .attribute(let attributeSyntax) = attr, + attributeSyntax.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "warn" + else { + return + } + + // First argument is the unquoted diagnostic group identifier + guard + let diagnosticGroupID = attributeSyntax.arguments? + .as(LabeledExprListSyntax.self)?.first?.expression + .as(DeclReferenceExprSyntax.self)?.baseName.text + else { + return + } + + // Second argument is the `as: ` behavior control specifier + guard + let asParamExprSyntax = attributeSyntax + .arguments?.as(LabeledExprListSyntax.self)? + .dropFirst().first + else { + return + } + guard + asParamExprSyntax.label?.text == "as", + let controlText = asParamExprSyntax + .expression.as(DeclReferenceExprSyntax.self)? + .baseName.text, + let control = WarningGroupControl(rawValue: controlText) + else { + return + } + result[DiagnosticGroupIdentifier(diagnosticGroupID)] = control + } + } +} diff --git a/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift b/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift new file mode 100644 index 00000000000..285864bd2ee --- /dev/null +++ b/Sources/SwiftWarningControl/WarningControlRegionBuilder.swift @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// Compute the full set of warning control regions in this syntax node +extension SyntaxProtocol { + @_spi(ExperimentalLanguageFeatures) + public func warningGroupControlRegionTree( + globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:], + groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil + ) -> WarningControlRegionTree { + return warningGroupControlRegionTreeImpl( + globalControls: globalControls, + groupInheritanceTree: groupInheritanceTree + ) + } + + /// Implementation of constructing a region tree with an optional parameter + /// to specify that the constructed tree must only contain nodes which contain + /// a specific absolute position - meant to speed up tree generation for individual + /// queries. + func warningGroupControlRegionTreeImpl( + globalControls: [DiagnosticGroupIdentifier: WarningGroupControl], + groupInheritanceTree: DiagnosticGroupInheritanceTree?, + containing position: AbsolutePosition? = nil + ) -> WarningControlRegionTree { + let visitor = WarningControlRegionVisitor( + self.range, + containing: position, + groupInheritanceTree: groupInheritanceTree + ) + visitor.tree.addWarningGroupControls(range: self.range, controls: globalControls) + visitor.walk(self) + return visitor.tree + } +} + +/// Add this warning control decl syntax node warning group controls (as specified with `@warn`) +/// to the tree. +extension WarningControlRegionTree { + mutating func addWarningControlRegions(for syntax: some WithAttributesSyntax) { + addWarningGroupControls( + range: syntax.range, + controls: syntax.allWarningGroupControls + ) + } +} + +/// Helper class that walks a syntax tree looking for warning behavior control regions. +private class WarningControlRegionVisitor: SyntaxAnyVisitor { + /// The tree of warning control regions we have found so far + var tree: WarningControlRegionTree + let containingPosition: AbsolutePosition? + + init( + _ topLevelRange: Range, + containing position: AbsolutePosition?, + groupInheritanceTree: DiagnosticGroupInheritanceTree? + ) { + self.tree = WarningControlRegionTree(range: topLevelRange, groupInheritanceTree: groupInheritanceTree) + containingPosition = position + super.init(viewMode: .fixedUp) + } + + override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { + if let containingPosition, + !node.range.contains(containingPosition) + { + return .skipChildren + } + if let withAttributesSyntax = node.asProtocol(WithAttributesSyntax.self) { + tree.addWarningControlRegions(for: withAttributesSyntax) + } + return .visitChildren + } +} diff --git a/Sources/SwiftWarningControl/WarningControlRegions.swift b/Sources/SwiftWarningControl/WarningControlRegions.swift new file mode 100644 index 00000000000..8faf4c5901b --- /dev/null +++ b/Sources/SwiftWarningControl/WarningControlRegions.swift @@ -0,0 +1,326 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +/// A single warning control region, consisting of a start and end positions, +/// a diagnostic group identifier, and an emission behavior control specifier. +@_spi(ExperimentalLanguageFeatures) +public struct WarningControlRegion { + public let range: Range + public let diagnosticGroupIdentifier: DiagnosticGroupIdentifier + public let control: WarningGroupControl + + init( + range: Range, + diagnosticGroupIdentifier: DiagnosticGroupIdentifier, + control: WarningGroupControl + ) { + self.range = range + self.diagnosticGroupIdentifier = diagnosticGroupIdentifier + self.control = control + } +} + +/// A struct representing a diagnostic group identifier string +@_spi(ExperimentalLanguageFeatures) +public struct DiagnosticGroupIdentifier: Hashable, Sendable, ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.identifier = value + } + public init(_ value: String) { + self.identifier = value + } + public let identifier: String +} + +/// Describes all of the `@warn` diagnostic group behavior controls within the +/// given syntax node, indicating each group's active behavior at a given position. +/// +/// For example, given code like the following: +/// +/// ``` +/// 1 | @warn(Deprecate, as: error) +/// 2 | func foo() { +/// 3 | let a = dep +/// 4 | @warn(Deprecate, as: warning) +/// 5 | func bar() { +/// 6 | let b = dep +/// 7 | @warn(Deprecate, as: ignored) +/// 8 | func baz() { +/// 9 | let c = dep +/// 10 | } +/// 11 | @warn(Deprecate, as: error) +/// 12 | @warn(OtherGroup, as: error) +/// 13 | func qux() { +/// 14 | let d = dep +/// 15 | @warn(SomeOtherGroup, as: warning) +/// 16 | func corge() { +/// 17 | let e = dep +/// 18 | } +/// 19 | } +/// 20 | } +/// 21 | } +/// 22 | func grault() { +/// 23 | let f = dep +/// 24 | } +/// ``` +/// +/// the result will be: +/// - [`Deprecate`:`error`] region within `foo` lexical scope (lines 1-22) +/// - [`Deprecate`:`warning`] region within `bar` lexical scope (lines 4-20) +/// - [`Deprecate`:`ignored`] region within `baz` lexical scope (lines 7-10) +/// - [`Deprecate`:`error`, `OtherGroup`:`error`] region within `qux` lexical scope (lines 12-19) +/// - [`SomeOtherGroup`:`warning`] region within `corge` lexical scope (lines 15-18) +/// +/// The data structure represents these regions and their nesting relationships +/// as an interval tree where interval nodes can only be nested or disjoint, +/// and where a given interval corresponds to a diagnostic control region for one +/// or more diagnostic group, with a behavior specifier for each. +/// +/// Intervals cannot partially overlap, and each node's child intervals are always kept +/// sorted. The tree has multiple top-level nodes (roots) representing file-level warning +/// control regions. +/// +/// Once the tree is computed, lookup of a diagnostic group behavior at a given position +/// is performed by recursively descending into the child node containing +/// the given position (located with a binary search of child nodes of a given parent node). +/// Once the position's depth in the interval tree is reached, we walk back the +/// traversal until we find the first containing region which specifies warning +/// behavior control for the given diagnostic group id. +/// +@_spi(ExperimentalLanguageFeatures) +public struct WarningControlRegionTree { + /// Root region representing top-level (file) scope + private var rootRegionNode: WarningControlRegionNode + + /// All of the diagnostic group identifiers contained in this tree + /// which have at least one occurence with a non-`ignored` behavior + /// specifier + @_spi(ExperimentalLanguageFeatures) + public private(set) var enabledGroups: Set = [] + + /// Inheritance tree among diagnostic group identifiers + private let groupInheritanceTree: DiagnosticGroupInheritanceTree + + init( + range: Range, + groupInheritanceTree: DiagnosticGroupInheritanceTree? + ) { + self.rootRegionNode = WarningControlRegionNode(range: range) + self.groupInheritanceTree = groupInheritanceTree ?? DiagnosticGroupInheritanceTree() + } + + /// Add a warning control region to the tree + mutating func addWarningGroupControls( + range: Range, + controls: [DiagnosticGroupIdentifier: WarningGroupControl] + ) { + guard !controls.isEmpty else { return } + let newNode = WarningControlRegionNode(range: range) + for (diagnosticGroupIdentifier, control) in controls { + // Handle the control for the added diagnostic group + // and propagate it to all of its subgroups. + var groups: [DiagnosticGroupIdentifier] = [diagnosticGroupIdentifier] + var processedGroups: Set = [] + while !groups.isEmpty { + let groupIdentifier = groups.removeFirst() + processedGroups.insert(groupIdentifier) + newNode.addWarningGroupControl(for: groupIdentifier, control: control) + if control != .ignored { + enabledGroups.insert(diagnosticGroupIdentifier) + } + + let newSubGroups = groupInheritanceTree.subgroups(of: groupIdentifier).filter { !processedGroups.contains($0) } + // Ensure we add a corresponding control to each direct and + // transitive sub-group of the one specified on this control. + groups.append(contentsOf: newSubGroups) + } + } + insertIntoSubtree(newNode, parent: rootRegionNode) + } + + /// Insert a region node into the appropriate position in a subtree. + /// During top-down traversal of the syntax tree, nodes that are visited + /// later should never contain any of the previously visited nodes, + /// so it is sufficient to either find an existing child to insert this node + /// into, or add this node as a new child itself. + private func insertIntoSubtree( + _ node: WarningControlRegionNode, + parent: WarningControlRegionNode + ) { + // Check if the new region has the same boundaries as the parent + if parent.range == node.range { + for (diagnosticGroupIdentifier, control) in node.warningGroupControls { + parent.addWarningGroupControl(for: diagnosticGroupIdentifier, control: control) + } + return + } + + // Check if this should be a child of one of parent's children + if let containingChild = parent.findChildContaining(node.range.lowerBound) { + insertIntoSubtree(node, parent: containingChild) + return + } + + // Add as direct child of parent + parent.addChild(node) + } +} + +extension WarningControlRegionTree: CustomDebugStringConvertible { + public var debugDescription: String { + var result = "Warning Group Control Region Tree:\n" + func printNode(_ node: WarningControlRegionNode, indent: Int) { + let spacing = String(repeating: " ", count: indent) + result += "\(spacing)[\(node.range.lowerBound), \(node.range.upperBound)]" + if !node.warningGroupControls.isEmpty { + result += + " control(s): \(node.warningGroupControls.map { $0.key.identifier + ": " + $0.value.rawValue }.joined(separator: ", "))\n" + } else { + result += "\n" + } + for child in node.children { + printNode(child, indent: indent + 1) + } + } + + printNode(rootRegionNode, indent: 0) + return result + } +} + +extension WarningControlRegionTree { + /// Determine the warning group behavior control at a specified position + /// for a given diagnostic group. + @_spi(ExperimentalLanguageFeatures) + public func warningGroupControl( + at position: AbsolutePosition, + for diagnosticGroupIdentifier: DiagnosticGroupIdentifier + ) -> WarningGroupControl? { + return rootRegionNode.innermostContainingRegion(at: position, for: diagnosticGroupIdentifier)?.control + } +} + +/// A node in the warning control region tree, representing a collection of warning +/// group controls and references to its nested child regions. +private class WarningControlRegionNode { + let range: Range + var warningGroupControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:] + var children: [WarningControlRegionNode] = [] + + init( + range: Range, + for diagnosticGroupIdentifier: DiagnosticGroupIdentifier, + control: WarningGroupControl + ) { + self.range = range + self.warningGroupControls = [diagnosticGroupIdentifier: control] + } + + init(range: Range) { + self.range = range + self.warningGroupControls = [:] + } + + /// Add a region with the same bounds as this node + func addWarningGroupControl( + for diagnosticGroupIdentifier: DiagnosticGroupIdentifier, + control: WarningGroupControl + ) { + warningGroupControls[diagnosticGroupIdentifier] = control + } + + /// Get region with specific identifier if it exists + func getWarningGroupControl(for diagnosticGroupIdentifier: DiagnosticGroupIdentifier) -> WarningControlRegion? { + guard let groupControl = warningGroupControls[diagnosticGroupIdentifier] else { + return nil + } + return WarningControlRegion( + range: range, + diagnosticGroupIdentifier: diagnosticGroupIdentifier, + control: groupControl + ) + } + + /// Add a child region that is directly nested within this region + func addChild(_ node: WarningControlRegionNode) { + precondition(range.lowerBound <= node.range.lowerBound && node.range.upperBound <= range.upperBound) + children.append(node) + children.sort() + } +} + +extension WarningControlRegionNode { + /// Find the most deeply-nested warning control region with a given diagnostic group identifier + /// containing the specified position. + /// + /// Lookup complexity is `O(log n + d)` where `n` is the branching factor and `d` is the warning + /// control node depth of the queried position. + func innermostContainingRegion( + at position: AbsolutePosition, + for diagnosticGroupIdentifier: DiagnosticGroupIdentifier + ) -> WarningControlRegion? { + guard range.contains(position) else { + return nil + } + if let childRegionContainingPosition = findChildContaining(position), + let nestedResult = childRegionContainingPosition.innermostContainingRegion( + at: position, + for: diagnosticGroupIdentifier + ) + { + return nestedResult + } + // If no child region has this warning group, check self + return getWarningGroupControl(for: diagnosticGroupIdentifier) + } + + /// Binary search to find the exact child containing the specified position, + /// or `nil` if one is not found, meaning it is only the `self` node itself that contains the position. + func findChildContaining(_ position: AbsolutePosition) -> WarningControlRegionNode? { + guard range.contains(position) && !children.isEmpty else { return nil } + + var left = 0 + var right = children.count - 1 + + while left <= right { + let mid = (left + right) / 2 + let region = children[mid] + if region.range.lowerBound > position { + right = mid - 1 + } else if region.range.upperBound <= position { + left = mid + 1 + } else { + return region + } + } + return nil + } +} + +extension WarningControlRegionNode: Comparable { + static func == (lhs: WarningControlRegionNode, rhs: WarningControlRegionNode) -> Bool { + return lhs.range == rhs.range + } + + static func < (lhs: WarningControlRegionNode, rhs: WarningControlRegionNode) -> Bool { + if lhs.range.lowerBound != rhs.range.lowerBound { + return lhs.range.lowerBound < rhs.range.lowerBound + } + // For same start, larger interval comes first (reverse order by end) + // This can happen, for example, when the top-level region (source-file scope) + // also contains a warning control scope at the very beginning which + // also begins at position 0. + return lhs.range.upperBound > rhs.range.upperBound + } +} diff --git a/Sources/SwiftWarningControl/WarningGroupControl.swift b/Sources/SwiftWarningControl/WarningGroupControl.swift new file mode 100644 index 00000000000..d0e9a4d2b7b --- /dev/null +++ b/Sources/SwiftWarningControl/WarningGroupControl.swift @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax + +// Describes the emission behavior state of a particular warning diagnostic group. +@_spi(ExperimentalLanguageFeatures) +public enum WarningGroupControl: String { + /// Emitted as a fatal error, halting compilation + case error + /// Emitted as a warning + case warning + /// Fully suppressed, i.e. diagnostic not emitted + case ignored +} diff --git a/Sources/_SwiftSyntaxCShims/include/Atomics.h b/Sources/_SwiftSyntaxCShims/include/Atomics.h index cdbc4b68f7b..e5111b81682 100644 --- a/Sources/_SwiftSyntaxCShims/include/Atomics.h +++ b/Sources/_SwiftSyntaxCShims/include/Atomics.h @@ -16,6 +16,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { _Atomic(bool) value; } AtomicBool; @@ -50,4 +54,8 @@ static inline void swiftsyntax_atomic_pointer_set(AtomicPointer *_Nonnull atomic atomic->value = newValue; } +#ifdef __cplusplus +} +#endif + #endif // SWIFTSYNTAX_ATOMICBOOL_H diff --git a/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h b/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h index 3288a0f1746..184368f2c4e 100644 --- a/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h +++ b/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h @@ -15,6 +15,10 @@ #include "_bridging.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct PlatformMutex { void *opaque; } PlatformMutex; @@ -31,4 +35,8 @@ void swiftsyntax_platform_mutex_unlock(PlatformMutex m); SWIFT_NAME_S("PlatformMutex.destroy(self:)") void swiftsyntax_platform_mutex_destroy(PlatformMutex m); +#ifdef __cplusplus +} +#endif + #endif // SWIFTSYNTAX_PLATFORMMUTEX_H diff --git a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h index 1a6c7cb1dd8..756574e4cb1 100644 --- a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h +++ b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h @@ -17,9 +17,17 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + SWIFT_NAME_S("getter:swift_syntax_errno()") static inline int swiftsyntax_errno(void) { return errno; } +#ifdef __cplusplus +} +#endif + #endif // SWIFTSYNTAX_ERRNO_H diff --git a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h index 207fda17818..fe28242a261 100644 --- a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h +++ b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h @@ -17,6 +17,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + SWIFT_NAME_S("getter:swift_syntax_stdout()") static inline FILE *swiftsyntax_stdout(void) { return stdout; @@ -32,4 +36,8 @@ static inline FILE *swiftsyntax_stderr(void) { return stderr; } +#ifdef __cplusplus +} +#endif + #endif // SWIFTSYNTAX_STDIO_H diff --git a/Sources/_SwiftSyntaxTestSupport/LocationMarkers.swift b/Sources/_SwiftSyntaxTestSupport/LocationMarkers.swift index 4bb741c3d97..4cc67ff1319 100644 --- a/Sources/_SwiftSyntaxTestSupport/LocationMarkers.swift +++ b/Sources/_SwiftSyntaxTestSupport/LocationMarkers.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// /// Finds all marked ranges in the given text, see `Marker`. -fileprivate func findMarkedRanges(text: String) -> [Marker] { +private func findMarkedRanges(text: String) -> [Marker] { var markers = [Marker]() while let marker = nextMarkedRange(text: text, from: markers.last?.range.upperBound ?? text.startIndex) { markers.append(marker) @@ -29,7 +29,7 @@ extension Character { } } -fileprivate func nextMarkedRange(text: String, from: String.Index) -> Marker? { +private func nextMarkedRange(text: String, from: String.Index) -> Marker? { guard let start = text[from...].firstIndex(where: { $0.isMarkerEmoji }) else { return nil } @@ -41,7 +41,7 @@ fileprivate func nextMarkedRange(text: String, from: String.Index) -> Marker? { return Marker(name: name, range: markerRange) } -fileprivate struct Marker { +private struct Marker { /// The name of the marker. let name: Substring /// The range of the marker. diff --git a/Sources/_SwiftSyntaxTestSupport/Syntax+Assertions.swift b/Sources/_SwiftSyntaxTestSupport/Syntax+Assertions.swift index b6d71b611f8..d3088107f8f 100644 --- a/Sources/_SwiftSyntaxTestSupport/Syntax+Assertions.swift +++ b/Sources/_SwiftSyntaxTestSupport/Syntax+Assertions.swift @@ -170,7 +170,7 @@ public enum SubtreeError: Error, CustomStringConvertible { } } -fileprivate class SyntaxTypeFinder: SyntaxAnyVisitor { +private class SyntaxTypeFinder: SyntaxAnyVisitor { private let offset: Int private let type: SyntaxProtocol.Type private var found: Syntax? diff --git a/SwiftParserCLI/Package.swift b/SwiftParserCLI/Package.swift index c59ec321d32..ef12c957302 100644 --- a/SwiftParserCLI/Package.swift +++ b/SwiftParserCLI/Package.swift @@ -25,6 +25,7 @@ let package = Package( "InstructionCounter", .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftDiagnostics", package: "swift-syntax"), + .product(name: "SwiftIfConfig", package: "swift-syntax"), .product(name: "SwiftOperators", package: "swift-syntax"), .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftParserDiagnostics", package: "swift-syntax"), diff --git a/SwiftParserCLI/Sources/swift-parser-cli/BasicFormat.swift b/SwiftParserCLI/Sources/swift-parser-cli/BasicFormat.swift index de4e41047c2..0ac916276c4 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/BasicFormat.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/BasicFormat.swift @@ -23,7 +23,7 @@ struct BasicFormat: ParsableCommand, ParseCommand { var description: String { switch self { - case .unknownSyntaxNodeType(nodeType: let nodeType, parsableTypes: let parsableTypes): + case .unknownSyntaxNodeType(let nodeType, let parsableTypes): return """ '\(nodeType)' is not a SwiftSyntax node type that conforms to SyntaxParsable. Possible options are: \(parsableTypes.map {" - \($0)" }.joined(separator: "\n")) diff --git a/SwiftParserCLI/Sources/swift-parser-cli/Commands/Print.swift b/SwiftParserCLI/Sources/swift-parser-cli/Commands/Print.swift new file mode 100644 index 00000000000..8f47fc99d5c --- /dev/null +++ b/SwiftParserCLI/Sources/swift-parser-cli/Commands/Print.swift @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import SwiftParser +import SwiftSyntax + +struct Print: ParsableCommand, ParseCommand { + static var configuration = CommandConfiguration( + commandName: "print", + abstract: "Print the parsed source file after applying any other arguments" + ) + + @OptionGroup + var arguments: ParseArguments + + func run() throws { + let (tree, _) = try parsedSourceFile(wantDiagnostics: false) + print(tree.description) + } +} diff --git a/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintDiags.swift b/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintDiags.swift index 048ff69880c..a98c139ccdf 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintDiags.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintDiags.swift @@ -28,25 +28,18 @@ struct PrintDiags: ParsableCommand, ParseCommand { var colorize: Bool = false func run() throws { - try sourceFileContents.withUnsafeBufferPointer { sourceBuffer in - let tree = Parser.parse(source: sourceBuffer) - var diags = ParseDiagnosticsGenerator.diagnostics(for: tree) - if foldSequences { - diags += foldAllSequences(tree).1 - } - - var group = GroupedDiagnostics() - group.addSourceFile(tree: tree, displayName: sourceFileName, diagnostics: diags) - let annotatedSource = DiagnosticsFormatter.annotateSources( - in: group, - colorize: colorize || TerminalHelper.isConnectedToTerminal - ) - - print(annotatedSource) - - if diags.isEmpty { - print("No diagnostics produced") - } + let (tree, diags) = try parsedSourceFile() + var group = GroupedDiagnostics() + group.addSourceFile(tree: tree, displayName: sourceFileName, diagnostics: diags) + let annotatedSource = DiagnosticsFormatter.annotateSources( + in: group, + colorize: colorize || TerminalHelper.isConnectedToTerminal + ) + + print(annotatedSource) + + if diags.isEmpty { + print("No diagnostics produced") } } } diff --git a/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintTree.swift b/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintTree.swift index fa35da89b62..52467f8f29d 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintTree.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/Commands/PrintTree.swift @@ -27,17 +27,7 @@ struct PrintTree: ParsableCommand, ParseCommand { var includeTrivia: Bool = false func run() throws { - try sourceFileContents.withUnsafeBufferPointer { sourceBuffer in - let tree = Parser.parse(source: sourceBuffer) - - let resultTree: Syntax - if foldSequences { - resultTree = foldAllSequences(tree).0 - } else { - resultTree = Syntax(tree) - } - - print(resultTree.debugDescription(includeTrivia: includeTrivia)) - } + let (tree, _) = try parsedSourceFile(wantDiagnostics: false) + print(tree.debugDescription(includeTrivia: includeTrivia)) } } diff --git a/SwiftParserCLI/Sources/swift-parser-cli/Commands/Reduce.swift b/SwiftParserCLI/Sources/swift-parser-cli/Commands/Reduce.swift index aeb7301a546..ce1d229e1cc 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/Commands/Reduce.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/Commands/Reduce.swift @@ -17,7 +17,7 @@ import Foundation import WinSDK #endif -fileprivate func withTemporaryFile(contents: [UInt8], body: (URL) throws -> T) throws -> T { +private func withTemporaryFile(contents: [UInt8], body: (URL) throws -> T) throws -> T { var tempFileURL = FileManager.default.temporaryDirectory tempFileURL.appendPathComponent("swift-parser-cli-\(UUID().uuidString).swift") try Data(contents).write(to: tempFileURL) diff --git a/SwiftParserCLI/Sources/swift-parser-cli/ParseCommand.swift b/SwiftParserCLI/Sources/swift-parser-cli/ParseCommand.swift index db9a5f4a7cd..61210c97108 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/ParseCommand.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/ParseCommand.swift @@ -11,6 +11,12 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Foundation +import SwiftDiagnostics +import SwiftIfConfig +import SwiftParser +import SwiftParserDiagnostics +import SwiftSyntax struct ParseArguments: ParsableArguments { @Argument(help: "The source file that should be parsed; if omitted, use stdin") @@ -21,6 +27,9 @@ struct ParseArguments: ParsableArguments { @Flag(name: .long, help: "Perform sequence folding with the standard operators") var foldSequences: Bool = false + + @Option(help: "Apply the given static build configuration to the source text before further processing") + var buildConfiguration: String? } /// A command that has arguments to parse source code @@ -50,4 +59,51 @@ extension ParseCommand { /// Whether sequence folding using standard operators should be performed var foldSequences: Bool { arguments.foldSequences } + + /// Parse the source file, applying any additional configuration options + /// such as sequence folding, and return it with diagnostics. + func parsedSourceFile( + wantDiagnostics: Bool = true + ) throws -> (SourceFileSyntax, [Diagnostic]) { + return try sourceFileContents.withUnsafeBufferPointer { sourceBuffer in + // Parse the sources + var tree = Parser.parse(source: sourceBuffer) + + // If we want diagnostics, gather them from the parser. + var diags: [Diagnostic] = [] + if wantDiagnostics { + diags += ParseDiagnosticsGenerator.diagnostics(for: tree) + } + + // If we are supposed to fold sequences, do it now. + if foldSequences { + let (folded, foldDiags) = foldAllSequences(tree) + + tree = folded.cast(SourceFileSyntax.self) + if wantDiagnostics { + diags += foldDiags + } + } + + // If we are supposed to apply a build configuration, do it now. + if let buildConfiguration = arguments.buildConfiguration { + // Load the build configuration. + let buildConfigurationText = try Data(contentsOf: URL(fileURLWithPath: buildConfiguration)) + let staticBuildConfiguration = try JSONDecoder().decode( + StaticBuildConfiguration.self, + from: buildConfigurationText + ) + + // Apply the build configuration. + let (configured, configuredDiags) = tree.removingInactive(in: staticBuildConfiguration) + + tree = configured.cast(SourceFileSyntax.self) + if wantDiagnostics { + diags += configuredDiags + } + } + + return (tree, diags) + } + } } diff --git a/SwiftParserCLI/Sources/swift-parser-cli/swift-parser-cli.swift b/SwiftParserCLI/Sources/swift-parser-cli/swift-parser-cli.swift index 21f02d924c0..e1266525973 100644 --- a/SwiftParserCLI/Sources/swift-parser-cli/swift-parser-cli.swift +++ b/SwiftParserCLI/Sources/swift-parser-cli/swift-parser-cli.swift @@ -32,6 +32,7 @@ class SwiftParserCli: ParsableCommand { subcommands: [ BasicFormat.self, PerformanceTest.self, + Print.self, PrintDiags.self, PrintTree.self, Reduce.self, diff --git a/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/VerifySourceCode.swift b/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/VerifySourceCode.swift index 02c66d15e2f..3393061b7c2 100644 --- a/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/VerifySourceCode.swift +++ b/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/VerifySourceCode.swift @@ -14,7 +14,7 @@ import ArgumentParser import Foundation import RegexBuilder -fileprivate let modules: [String] = [ +private let modules: [String] = [ "SwiftParser", "SwiftParserDiagnostics", "SwiftSyntax", diff --git a/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/common/Paths.swift b/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/common/Paths.swift index 36d6d198cc6..49bdfccad2c 100644 --- a/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/common/Paths.swift +++ b/SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/common/Paths.swift @@ -114,7 +114,7 @@ enum Paths { var description: String { switch self { - case .notFound(executableName: let executableName): + case .notFound(let executableName): return "Executable \(executableName) not found in PATH" } } diff --git a/Tests/PerformanceTest/InstructionsCountAssertion.swift b/Tests/PerformanceTest/InstructionsCountAssertion.swift index 250addfc71e..8c665a124e7 100644 --- a/Tests/PerformanceTest/InstructionsCountAssertion.swift +++ b/Tests/PerformanceTest/InstructionsCountAssertion.swift @@ -13,7 +13,7 @@ import XCTest import _InstructionCounter -fileprivate var baselineURL: URL { +private var baselineURL: URL { if let baselineFile = ProcessInfo.processInfo.environment["BASELINE_FILE"] { return URL(fileURLWithPath: baselineFile) } else { diff --git a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift index 801c5e5e7ff..e6f08022546 100644 --- a/Tests/SwiftBasicFormatTest/BasicFormatTests.swift +++ b/Tests/SwiftBasicFormatTest/BasicFormatTests.swift @@ -12,12 +12,12 @@ import SwiftBasicFormat import SwiftParser -import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax @_spi(Testing) import SwiftSyntaxBuilder import XCTest import _SwiftSyntaxTestSupport -fileprivate func assertFormatted( +private func assertFormatted( tree: T, expected: String, using format: BasicFormat = BasicFormat(indentationWidth: .spaces(4)), @@ -27,7 +27,7 @@ fileprivate func assertFormatted( assertStringsEqualWithDiff(tree.formatted(using: format).description, expected, file: file, line: line) } -fileprivate func assertFormatted( +private func assertFormatted( source: String, expected: String, using format: BasicFormat = BasicFormat(indentationWidth: .spaces(4)), @@ -43,7 +43,7 @@ fileprivate func assertFormatted( ) } -fileprivate func assertFormattingRoundTrips( +private func assertFormattingRoundTrips( _ source: String, using format: BasicFormat = BasicFormat(indentationWidth: .spaces(4)), file: StaticString = #filePath, @@ -695,4 +695,17 @@ final class BasicFormatTest: XCTestCase { expected: "let x: (Int) -> Void" ) } + + func testColonColon() { + assertFormatted( + tree: DeclReferenceExprSyntax( + moduleSelector: ModuleSelectorSyntax( + moduleName: .identifier("Swift"), + colonColon: .colonColonToken() + ), + baseName: TokenSyntax.identifier("print") + ), + expected: "Swift::print" + ) + } } diff --git a/Tests/SwiftBasicFormatTest/IndentTests.swift b/Tests/SwiftBasicFormatTest/IndentTests.swift index a60f5215d18..075cfa2778b 100644 --- a/Tests/SwiftBasicFormatTest/IndentTests.swift +++ b/Tests/SwiftBasicFormatTest/IndentTests.swift @@ -17,7 +17,7 @@ import SwiftSyntax import XCTest import _SwiftSyntaxTestSupport -fileprivate func assertIndented( +private func assertIndented( by indentation: Trivia = .tab, indentFirstLine: Bool = true, source: String, diff --git a/Tests/SwiftBasicFormatTest/InferIndentationTests.swift b/Tests/SwiftBasicFormatTest/InferIndentationTests.swift index 915e6a9bd15..15c9e67f200 100644 --- a/Tests/SwiftBasicFormatTest/InferIndentationTests.swift +++ b/Tests/SwiftBasicFormatTest/InferIndentationTests.swift @@ -15,7 +15,7 @@ import SwiftSyntax import SwiftSyntaxBuilder import XCTest -fileprivate func assertIndentation( +private func assertIndentation( of sourceFile: SourceFileSyntax, _ expected: Trivia?, file: StaticString = #filePath, diff --git a/Tests/SwiftCompilerPluginTest/JSONTests.swift b/Tests/SwiftCompilerPluginTest/JSONTests.swift index 2018d831662..71f95538ea9 100644 --- a/Tests/SwiftCompilerPluginTest/JSONTests.swift +++ b/Tests/SwiftCompilerPluginTest/JSONTests.swift @@ -254,29 +254,29 @@ final class JSONTests: XCTestCase { // MARK: - Test Types -fileprivate struct EmptyStruct: Codable, Equatable { +private struct EmptyStruct: Codable, Equatable { static func == (_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { return true } } -fileprivate class EmptyClass: Codable, Equatable { +private class EmptyClass: Codable, Equatable { static func == (_ lhs: EmptyClass, _ rhs: EmptyClass) -> Bool { return true } } -fileprivate enum Direction: Codable { +private enum Direction: Codable { case right case left } -fileprivate enum Animal: String, Codable { +private enum Animal: String, Codable { case dog case cat } -fileprivate enum Switch: Codable { +private enum Switch: Codable { case off case on @@ -297,14 +297,14 @@ fileprivate enum Switch: Codable { } } -fileprivate enum Tree: Codable, Equatable { - indirect case int(Int) - indirect case string(String) - indirect case array([Self]) - indirect case dictionary([String: Self]) +private indirect enum Tree: Codable, Equatable { + case int(Int) + case string(String) + case array([Self]) + case dictionary([String: Self]) } -fileprivate struct ComplexStruct: Codable, Equatable { +private struct ComplexStruct: Codable, Equatable { struct Diagnostic: Codable, Equatable { var message: String var animal: Animal diff --git a/Tests/SwiftDiagnosticsTest/ParserDiagnosticsFormatterIntegrationTests.swift b/Tests/SwiftDiagnosticsTest/ParserDiagnosticsFormatterIntegrationTests.swift index 5f717422309..d40353270c0 100644 --- a/Tests/SwiftDiagnosticsTest/ParserDiagnosticsFormatterIntegrationTests.swift +++ b/Tests/SwiftDiagnosticsTest/ParserDiagnosticsFormatterIntegrationTests.swift @@ -161,7 +161,7 @@ final class ParserDiagnosticsFormatterIntegrationTests: XCTestCase { let expectedOutput = """ 1 | func o() { 2 | }👨‍👩‍👧‍👦} - | |`- error: extraneous braces at top level + | |`- error: unexpected braces in source file | `- error: consecutive statements on a line must be separated by newline or ';' 3 | } diff --git a/Tests/SwiftIDEUtilsTest/Assertions.swift b/Tests/SwiftIDEUtilsTest/Assertions.swift index c29a3442fe7..ff21d8cde4b 100644 --- a/Tests/SwiftIDEUtilsTest/Assertions.swift +++ b/Tests/SwiftIDEUtilsTest/Assertions.swift @@ -32,7 +32,7 @@ func assertClassification( ) { let tree = Parser.parse(source: source) - var classifications: Array + var classifications: [SyntaxClassifiedRange] if let range { classifications = Array(tree.classifications(in: range)) } else { diff --git a/Tests/SwiftIDEUtilsTest/FixItApplierTests.swift b/Tests/SwiftIDEUtilsTest/FixItApplierTests.swift new file mode 100644 index 00000000000..d45be14f3c5 --- /dev/null +++ b/Tests/SwiftIDEUtilsTest/FixItApplierTests.swift @@ -0,0 +1,418 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +@_spi(FixItApplier) import SwiftIDEUtils +import SwiftSyntax +import XCTest + +private extension SourceEdit { + init(range: Range, replacement: String) { + self.init( + range: AbsolutePosition(utf8Offset: range.lowerBound)..) { + if subrange.isEmpty { return } + var lower = subrange.lowerBound + var upper = subrange.upperBound + while lower < upper { + formIndex(before: &upper) + swapAt(lower, upper) + formIndex(after: &lower) + } + } +} + +private extension MutableCollection where Self: BidirectionalCollection, Element: Comparable { + /// Permutes this collection's elements through all the lexical orderings. + /// + /// Call `nextPermutation()` repeatedly starting with the collection in sorted + /// order. When the full cycle of all permutations has been completed, the + /// collection will be back in sorted order and this method will return + /// `false`. + /// + /// - Returns: A Boolean value indicating whether the collection still has + /// remaining permutations. When this method returns `false`, the collection + /// is in ascending order according to `areInIncreasingOrder`. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + mutating func nextPermutation(upperBound: Index? = nil) -> Bool { + // Ensure we have > 1 element in the collection. + guard !isEmpty else { return false } + var i = index(before: endIndex) + if i == startIndex { return false } + + let upperBound = upperBound ?? endIndex + + while true { + let ip1 = i + formIndex(before: &i) + + // Find the last ascending pair (ie. ..., a, b, ... where a < b) + if self[i] < self[ip1] { + // Find the last element greater than self[i] + // swift-format-ignore: NeverForceUnwrap + // This is _always_ at most `ip1` due to if statement above + let j = lastIndex(where: { self[i] < $0 })! + + // At this point we have something like this: + // 0, 1, 4, 3, 2 + // ^ ^ + // i j + swapAt(i, j) + self.reverse(subrange: ip1.. Bool { + func isActiveTargetObjectFormat(name: String) throws -> Bool { name == "ELF" } diff --git a/Tests/SwiftIfConfigTest/VisitorTests.swift b/Tests/SwiftIfConfigTest/VisitorTests.swift index 0a4ea2fbd3c..bcd0ea4eb12 100644 --- a/Tests/SwiftIfConfigTest/VisitorTests.swift +++ b/Tests/SwiftIfConfigTest/VisitorTests.swift @@ -45,8 +45,8 @@ class AllActiveVisitor: ActiveSyntaxAnyVisitor { } } -class NameCheckingVisitor: ActiveSyntaxAnyVisitor { - let configuration: TestingBuildConfiguration +class NameCheckingVisitor: ActiveSyntaxAnyVisitor { + let configuration: Configuration /// The set of names we are expected to visit. Any syntax nodes with /// names that aren't here will be rejected, and each of the names listed @@ -54,7 +54,7 @@ class NameCheckingVisitor: ActiveSyntaxAnyVisitor { var expectedNames: Set init( - configuration: TestingBuildConfiguration, + configuration: Configuration, expectedNames: Set, configuredRegions: ConfiguredRegions? = nil ) { @@ -399,7 +399,7 @@ extension VisitorTests { /// Assert that removing any inactive code according to the given build /// configuration returns the expected source and diagnostics. -fileprivate func assertRemoveInactive( +private func assertRemoveInactive( _ source: String, configuration: some BuildConfiguration, retainFeatureCheckIfConfigs: Bool = false, diff --git a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift index 9594ec5e350..477105951b7 100644 --- a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift +++ b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift @@ -15,7 +15,7 @@ import SwiftLexicalLookup import SwiftSyntax import XCTest -final class testNameLookup: XCTestCase { +final class TestNameLookup: XCTestCase { func testCodeBlockSimpleCase() { assertLexicalNameLookup( source: """ @@ -717,7 +717,7 @@ final class testNameLookup: XCTestCase { ) } - func testTypeDeclAvaialabilityInSequentialScope() { + func testTypeDeclAvailabilityInSequentialScope() { let declExpectation: [ResultExpectation] = [ .fromScope( CodeBlockSyntax.self, diff --git a/Tests/SwiftLexicalLookupTest/SimpleQueryTests.swift b/Tests/SwiftLexicalLookupTest/SimpleQueryTests.swift index 6ef7497a7d8..b8102ba5d21 100644 --- a/Tests/SwiftLexicalLookupTest/SimpleQueryTests.swift +++ b/Tests/SwiftLexicalLookupTest/SimpleQueryTests.swift @@ -15,7 +15,7 @@ import Foundation import SwiftSyntax import XCTest -final class testSimpleQueries: XCTestCase { +final class TestSimpleQueries: XCTestCase { func testLabeledStmtLookupThreeNested() { assertLexicalScopeQuery( source: """ diff --git a/Tests/SwiftParserTest/Assertions.swift b/Tests/SwiftParserTest/Assertions.swift index cb537012a3c..da6f7008056 100644 --- a/Tests/SwiftParserTest/Assertions.swift +++ b/Tests/SwiftParserTest/Assertions.swift @@ -190,6 +190,7 @@ private func assertTokens( func assertLexemes( _ markedSource: String, lexemes expectedLexemes: [LexemeSpec], + experimentalFeatures: Parser.ExperimentalFeatures = [], file: StaticString = #filePath, line: UInt = #line ) { @@ -206,7 +207,12 @@ func assertLexemes( lookaheadTracker.initialize(to: LookaheadTracker()) source.withUTF8 { buf in var lexemes = [Lexer.Lexeme]() - for token in Lexer.tokenize(buf, from: 0, lookaheadTracker: lookaheadTracker) { + for token in Lexer.tokenize( + buf, + from: 0, + lookaheadTracker: lookaheadTracker, + experimentalFeatures: experimentalFeatures + ) { lexemes.append(token) if token.rawTokenKind == .endOfFile { diff --git a/Tests/SwiftParserTest/AttributeTests.swift b/Tests/SwiftParserTest/AttributeTests.swift index 1bc34070d1b..3193a750ccf 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -39,6 +39,30 @@ final class AttributeTests: ParserTestCase { } """ ) + + assertParse( + """ + @specializedℹ️(1️⃣ + func 2️⃣foo() { + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected argument for '@specialized' attribute", + fixIts: ["insert attribute argument"] + ), + DiagnosticSpec( + message: "expected ')' to end attribute", + notes: [NoteSpec(message: "to match this opening '('")], + fixIts: ["insert ')'"] + ), + ], + fixedSource: """ + @specialized(where <#type#> == <#type#>) + func foo() { + } + """ + ) } func testMissingGenericTypeToAttribute() { @@ -87,6 +111,12 @@ final class AttributeTests: ParserTestCase { func foo(_ t: T) {} """ ) + assertParse( + """ + @specialized(where T : Int) + func foo(_ t: T) {} + """ + ) } func testMissingClosingParenToAttribute() { @@ -158,7 +188,7 @@ final class AttributeTests: ParserTestCase { @objc(zeroArg) class A { } - @objc(:::::) + @objc(:::x::) func f(_: Int, _: Int, _: Int, _: Int, _: Int) { } """ ) @@ -171,6 +201,21 @@ final class AttributeTests: ParserTestCase { ) } + func testObjCAttributeNewlineParen() { + assertParse( + """ + @objc + 1️⃣(foo) func foo() + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '(foo)' in function" + ) + ] + ) + } + func testRethrowsAttribute() { assertParse( """ @@ -522,6 +567,12 @@ final class AttributeTests: ParserTestCase { """ ) + assertParse( + """ + @_expose(!Cxx) func foo() {} + """ + ) + assertParse( """ @_expose(Cxx, "baz") func foo() {} @@ -832,7 +883,7 @@ final class AttributeTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "unexpected code '))' before macro" + message: "unexpected code '))' in class" ), DiagnosticSpec( locationMarker: "3️⃣", @@ -960,23 +1011,20 @@ final class AttributeTests: ParserTestCase { parameterClause: FunctionParameterClauseSyntax {}, returnClause: ReturnClauseSyntax(type: TypeSyntax("Int")) ) - ) {}, - experimentalFeatures: [.abiAttribute] + ) {} ) assertParse( """ @abi(associatedtype AssocTy) associatedtype AssocTy - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(deinit) deinit {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -984,50 +1032,43 @@ final class AttributeTests: ParserTestCase { @abi(case someCase) case someCase } - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(func fn()) func fn() - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(init()) init() {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(subscript(i: Int) -> Element) subscript(i: Int) -> Element {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(typealias Typealias = @escaping () -> Void) typealias Typealias = () -> Void - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(let c1, c2) let c1, c2 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(var v1, v2) var v1, v2 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( @@ -1042,8 +1083,7 @@ final class AttributeTests: ParserTestCase { ), diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file") - ], - experimentalFeatures: [.abiAttribute] + ] ) assertParse( @@ -1067,8 +1107,7 @@ final class AttributeTests: ParserTestCase { ), diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "import is not permitted as ABI-providing declaration") - ], - experimentalFeatures: [.abiAttribute] + ] ) // @@ -1079,15 +1118,13 @@ final class AttributeTests: ParserTestCase { """ @abi(associatedtype AssocTy = T) associatedtype AssocTy - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(deinit {}) deinit {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -1095,50 +1132,43 @@ final class AttributeTests: ParserTestCase { @abi(case someCase = 42) case someCase } - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(func fn() {}) func fn() - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(init() {}) init() {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(subscript(i: Int) -> Element { get {} set {} }) subscript(i: Int) -> Element {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(let c1 = 1, c2 = 2) let c1, c2 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(var v1 = 1, v2 = 2) var v1, v2 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @abi(var v3 { get {} set {} }) var v3 - """, - experimentalFeatures: [.abiAttribute] + """ ) // @@ -1160,8 +1190,7 @@ final class AttributeTests: ParserTestCase { fixedSource: """ @abi(var <#pattern#>) var v1 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -1184,8 +1213,7 @@ final class AttributeTests: ParserTestCase { fixedSource: """ @abi(var v2) var v2 - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -1203,8 +1231,7 @@ final class AttributeTests: ParserTestCase { fixedSource: """ @abi(<#declaration#>) func fn2() {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -1221,8 +1248,7 @@ final class AttributeTests: ParserTestCase { fixedSource: """ @abi(<#declaration#>) func fn3() {} - """, - experimentalFeatures: [.abiAttribute] + """ ) assertParse( """ @@ -1238,14 +1264,13 @@ final class AttributeTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "7️⃣", - message: "unexpected code ')' before function" + message: "unexpected code ')' in source file" ), ], fixedSource: """ @abi(<#declaration#>) func fn4_abi()) func fn4() {} - """, - experimentalFeatures: [.abiAttribute] + """ ) // `#if` is banned inside an `@abi` attribute. @@ -1278,51 +1303,7 @@ final class AttributeTests: ParserTestCase { func _fn() throws(E) ) func fn() throws(E) {} - """, - experimentalFeatures: [.abiAttribute] - ) - } - - func testABIAttributeWithoutFeature() throws { - assertParse( - """ - @abi(1️⃣func fn() -> Int2️⃣) - func fn1() -> Int { } - """, - substructure: FunctionDeclSyntax( - attributes: [ - .attribute( - AttributeSyntax( - attributeName: TypeSyntax("abi"), - leftParen: .leftParenToken(), - [Syntax(try FunctionDeclSyntax("func fn() -> Int"))], - arguments: .argumentList([]), - rightParen: .rightParenToken() - ) - ) - ], - name: "fn1", - signature: FunctionSignatureSyntax( - parameterClause: FunctionParameterClauseSyntax {}, - returnClause: ReturnClauseSyntax(type: TypeSyntax("Int")) - ) - ) {}, - diagnostics: [ - DiagnosticSpec( - locationMarker: "1️⃣", - message: "unexpected code 'func fn() -> Int' in attribute" - ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "expected argument for '@abi' attribute", - fixIts: ["insert attribute argument"] - ), - ], - fixedSource: """ - @abi(func fn() -> Int) - func fn1() -> Int { } - """, - experimentalFeatures: [] + """ ) } @@ -1440,4 +1421,111 @@ final class AttributeTests: ParserTestCase { """ ) } + + func testAttributeParsable() { + assertParse( + """ + 1️⃣#if true + @discardableResult + #endif + """, + { AttributeSyntax.parse(from: &$0) }, + substructure: AttributeSyntax( + atSign: .atSignToken(presence: .missing), + attributeName: TypeSyntax(MissingTypeSyntax(placeholder: .identifier("<#type#>", presence: .missing))), + UnexpectedNodesSyntax([ + TokenSyntax.poundIfToken(), + TokenSyntax.keyword(.true), + TokenSyntax.atSignToken(), + TokenSyntax.identifier("discardableResult"), + TokenSyntax.poundEndifToken(), + ]) + ), + diagnostics: [ + DiagnosticSpec(message: "expected '@' and name in attribute", fixIts: ["insert '@' and name"]), + DiagnosticSpec( + message: "conditional compilation not permitted in attribute", + fixIts: [ + """ + remove '#if true + @discardableResult + #endif' + """ + ] + ), + ], + fixedSource: """ + @<#type#> + """ + ) + } + + func testAttributeWithSpace() { + assertParse( + """ + @1️⃣ FooBar2️⃣ (arg) func foo() {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "extraneous whitespace after '@' is not permitted", + fixIts: ["remove whitespace"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "extraneous whitespace before '(' is not permitted", + fixIts: ["remove whitespace"] + ), + ], + fixedSource: """ + @FooBar(arg) func foo() {} + """ + ) + } + + func testAttributeMainActorClosure() { + assertParse( + """ + { @MainActor (arg) in } + """, + substructure: ClosureExprSyntax( + leftBrace: .leftBraceToken(), + signature: ClosureSignatureSyntax( + attributes: AttributeListSyntax([ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: TypeSyntax(IdentifierTypeSyntax(name: .identifier("MainActor"))) + ) + ) + ]), + parameterClause: ClosureSignatureSyntax.ParameterClause( + ClosureParameterClauseSyntax( + leftParen: .leftParenToken(), + parameters: ClosureParameterListSyntax([ + ClosureParameterSyntax( + attributes: [], + modifiers: [], + firstName: .identifier("arg") + ) + ]), + rightParen: .rightParenToken() + ) + ), + inKeyword: .keyword(.in) + ), + statements: [], + rightBrace: .rightBraceToken() + ) + ) + } + + func testTypeAttributeInExprContext() { + assertParse( + """ + var _ = [@Sendable () -> Void]() + """, + swiftVersion: .v5 + ) + } } diff --git a/Tests/SwiftParserTest/AvailabilityTests.swift b/Tests/SwiftParserTest/AvailabilityTests.swift index 74c0e78e856..612c78a83c5 100644 --- a/Tests/SwiftParserTest/AvailabilityTests.swift +++ b/Tests/SwiftParserTest/AvailabilityTests.swift @@ -204,4 +204,33 @@ final class AvailabilityTests: ParserTestCase { ] ) } + + func testAvailableNewlineParen() { + assertParse( + """ + @available1️⃣ + 2️⃣(*, unavailable) func foo() {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '()' in attribute", + fixIts: ["insert '()'"] + ), + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected argument for '@available' attribute", + fixIts: ["insert attribute argument"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '(*, unavailable)' in function" + ), + ], + fixedSource: """ + @available() + (*, unavailable) func foo() {} + """ + ) + } } diff --git a/Tests/SwiftParserTest/ClosureMissingInTests.swift b/Tests/SwiftParserTest/ClosureMissingInTests.swift new file mode 100644 index 00000000000..7775bc04147 --- /dev/null +++ b/Tests/SwiftParserTest/ClosureMissingInTests.swift @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftParser +@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax +import XCTest + +final class ClosureMissingInTests: ParserTestCase { + + func testMissingInAfterSignature() { + assertParse( + """ + _ = { (x: Int) -> Int 1️⃣0 } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected 'in' in closure signature", + fixIts: ["insert 'in'"] + ) + ], + fixedSource: """ + _ = { (x: Int) -> Int in 0 } + """ + ) + } + + func testArrayLiteralNotMisparsedAsSignature() { + assertParse( + """ + _ = { [x, y] } + """ + ) + } + + func testAsyncIsNotASignatureGate() { + assertParse( + """ + _ = { async } + """ + ) + } + + func testShorthandParamsWithReturnType() { + assertParse( + """ + _ = { x, _ -> Int 1️⃣x } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected 'in' in closure signature", + fixIts: ["insert 'in'"] + ) + ], + fixedSource: """ + _ = { x, _ -> Int in x } + """ + ) + } + + func testResyncTokensBeforeIn() { + assertParse( + """ + _ = { () -> Int + 1️⃣0 + in + 1 + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '0' in closure signature" + ) + ] + ) + } + + func testMissingInInFunctionArgument() { + assertParse( + """ + test(make: { () -> [Int] 1️⃣ + return [3] + }, consume: { _ in + print("Test") + }) + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected 'in' in closure signature", + fixIts: ["insert 'in'"] + ) + ], + fixedSource: + """ + test(make: { () -> [Int] in + return [3] + }, consume: { _ in + print("Test") + }) + """ + ) + } +} diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 7ec097ec5bd..44c95f9ed38 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -12,7 +12,7 @@ import SwiftBasicFormat @_spi(Testing) @_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftParser -@_spi(RawSyntax) import SwiftSyntax +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax import SwiftSyntaxBuilder import XCTest @@ -163,7 +163,7 @@ final class DeclarationTests: ParserTestCase { func foo() {} """, diagnostics: [ - DiagnosticSpec(message: "unexpected brace before function") + DiagnosticSpec(message: "unexpected brace in source file") ] ) } @@ -243,7 +243,7 @@ final class DeclarationTests: ParserTestCase { actor Foo {} """, diagnostics: [ - DiagnosticSpec(message: "unexpected brace before actor") + DiagnosticSpec(message: "unexpected brace in source file") ] ) } @@ -327,7 +327,7 @@ final class DeclarationTests: ParserTestCase { diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "unexpected code '{}' before enum case" + message: "unexpected code '{}' in protocol" ), DiagnosticSpec( locationMarker: "2️⃣", @@ -567,11 +567,15 @@ final class DeclarationTests: ParserTestCase { assertParse( """ - 1️⃣private( + private(1️⃣ """, diagnostics: [ - DiagnosticSpec(message: "extraneous code 'private(' at top level") - ] + DiagnosticSpec(message: "expected 'set)' to end modifier", fixIts: ["insert 'set)'"]), + DiagnosticSpec(message: "expected declaration after 'private' modifier", fixIts: ["insert declaration"]), + ], + fixedSource: """ + private(set) <#declaration#> + """ ) assertParse( @@ -607,10 +611,27 @@ final class DeclarationTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(message: "expected 'set)' to end modifier", fixIts: ["insert 'set)'"]), - DiagnosticSpec(message: "unexpected code 'get, didSet' in variable"), + DiagnosticSpec(message: "expected 'var' in variable", fixIts: ["insert 'var'"]), + ], + fixedSource: """ + private(set) var get, didSet var a = 0 + """ + ) + + assertParse( + """ + public 1️⃣{ {} } + open + """, + diagnostics: [ + DiagnosticSpec( + message: "expected declaration and ';' after 'public' modifier", + fixIts: ["insert declaration and ';'"] + ) ], fixedSource: """ - private(set) get, didSet var a = 0 + public <#declaration#>; { {} } + open """ ) } @@ -770,6 +791,15 @@ final class DeclarationTests: ParserTestCase { } """ ) + assertParse( + """ + @specialized(where Array == Int) + @specialized(where T.Element == Int) + public func funcWithComplexSpecializeRequirements(t: T) -> Int { + return 55555 + } + """ + ) } func testParseRetroactiveExtension() { @@ -807,6 +837,17 @@ final class DeclarationTests: ParserTestCase { """ ) + assertParse( + """ + extension Int: @preconcurrency nonisolated Q {} + """ + ) + + assertParse( + """ + extension Int: @unsafe nonisolated Q {} + """ + ) } func testParseDynamicReplacement() { @@ -1221,7 +1262,7 @@ final class DeclarationTests: ParserTestCase { 1️⃣} """, diagnostics: [ - DiagnosticSpec(message: "extraneous brace at top level") + DiagnosticSpec(message: "unexpected brace in source file") ] ) } @@ -1291,6 +1332,15 @@ final class DeclarationTests: ParserTestCase { } """ ) + + assertParse( + """ + public var foo: Swift.Int { + get + @inlinable set {} + } + """ + ) } func testInitAccessor() { @@ -1408,7 +1458,7 @@ final class DeclarationTests: ParserTestCase { message: "consecutive statements on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"] ), - DiagnosticSpec(locationMarker: "5️⃣", message: "extraneous code ', consectetur adipiscing elit' at top level"), + DiagnosticSpec(locationMarker: "5️⃣", message: "unexpected code ', consectetur adipiscing elit' in source file"), ], applyFixIts: ["insert newline"], fixedSource: """ @@ -1447,7 +1497,7 @@ final class DeclarationTests: ParserTestCase { message: "consecutive statements on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"] ), - DiagnosticSpec(locationMarker: "5️⃣", message: "extraneous code ', consectetur adipiscing elit' at top level"), + DiagnosticSpec(locationMarker: "5️⃣", message: "unexpected code ', consectetur adipiscing elit' in source file"), ], applyFixIts: ["insert ';'"], fixedSource: """ @@ -1625,7 +1675,11 @@ final class DeclarationTests: ParserTestCase { @3️⃣ """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in struct", fixIts: ["insert '{'"]), + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '{' in struct", + fixIts: ["insert '{'"] + ), DiagnosticSpec( locationMarker: "2️⃣", message: "expected condition in conditional compilation clause", @@ -1642,7 +1696,12 @@ final class DeclarationTests: ParserTestCase { message: "expected '#endif' in conditional compilation block", fixIts: ["insert '#endif'"] ), - DiagnosticSpec(locationMarker: "3️⃣", message: "expected '}' to end struct", fixIts: ["insert '}'"]), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "expected '}' to end struct", + fixIts: ["insert '}'"] + ), + ], fixedSource: """ struct n { @@ -1714,7 +1773,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}class C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before class"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected member block in class", fixIts: ["insert member block"]), ], fixedSource: """ @@ -1725,7 +1784,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}enum C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before enum"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected member block in enum", fixIts: ["insert member block"]), ], fixedSource: """ @@ -1736,7 +1795,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}protocol C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before protocol"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected member block in protocol", @@ -1751,7 +1810,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}actor C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before actor"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected member block in actor", fixIts: ["insert member block"]), ], fixedSource: """ @@ -1762,7 +1821,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}struct C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before struct"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected member block in struct", @@ -1777,7 +1836,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}func C2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before function"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected parameter clause in function signature", @@ -1791,7 +1850,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}init2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before initializer"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected parameter clause in function signature", @@ -1805,7 +1864,7 @@ final class DeclarationTests: ParserTestCase { assertParse( "1️⃣}subscript2️⃣", diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before subscript"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected parameter clause in subscript", @@ -2387,8 +2446,13 @@ final class DeclarationTests: ParserTestCase { name: .identifier("A"), memberBlock: MemberBlockSyntax( leftBrace: .leftBraceToken(), - members: MemberBlockItemListSyntax(), - UnexpectedNodesSyntax([TokenSyntax.binaryOperator("^")]), + members: MemberBlockItemListSyntax([ + MemberBlockItemSyntax( + decl: DeclSyntax( + UnexpectedCodeDeclSyntax(unexpectedCode: UnexpectedNodesSyntax([TokenSyntax.binaryOperator("^")])) + ) + ) + ]), rightBrace: .rightBraceToken() ) ) @@ -2443,13 +2507,13 @@ final class DeclarationTests: ParserTestCase { message: "expected parameter clause in function signature", fixIts: ["insert parameter clause"] ), - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code ': Int = A.M1' before macro"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code ': Int = A.M1' in source file"), DiagnosticSpec( locationMarker: "2️⃣", message: "expected parameter clause in function signature", fixIts: ["insert parameter clause"] ), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code ': T = A.M4 where T.Assoc: P' before macro"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code ': T = A.M4 where T.Assoc: P' in source file"), ], fixedSource: """ macro m1(): Int = A.M1 @@ -2880,15 +2944,15 @@ final class DeclarationTests: ParserTestCase { class A ℹ️{ 1️⃣^ } - unowned 2️⃣B { + unowned2️⃣ B { } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code before modifier"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '^' in class"), DiagnosticSpec( locationMarker: "2️⃣", - message: "expected declaration and '}' after 'unowned' modifier", - fixIts: ["insert declaration and '}'"] + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] ), ], fixedSource: @@ -2896,8 +2960,8 @@ final class DeclarationTests: ParserTestCase { class A { ^ } - unowned <#declaration#> - }B { + unowned + B { } """ ) @@ -3449,6 +3513,28 @@ final class DeclarationTests: ParserTestCase { ) } + func testBorrowAndMutateAccessors() { + assertParse( + """ + public class Klass {} + + public struct Wrapper { + var _otherK: Klass + + var k1: Klass { + borrow { + return _otherK + } + mutate { + return &_otherK + } + } + } + """, + experimentalFeatures: .borrowAndMutateAccessors + ) + } + func testMissingCommaInParameters() { assertParse( "func a(foo: Bar1️⃣ foo2: Bar2) {}", @@ -3529,4 +3615,232 @@ final class DeclarationTests: ParserTestCase { ] ) } + + func testTrailingCommas() { + assertParse( + """ + protocol Baaz< + Foo, + Bar, + > { + associatedtype Foo + associatedtype Bar + } + """ + ) + + assertParse( + """ + struct Foo< + T1, + T2, + T3, + >: Baaz< + T1, + T2, + > {} + """ + ) + } +} + +final class UsingDeclarationTests: ParserTestCase { + override var experimentalFeatures: Parser.ExperimentalFeatures { + [.defaultIsolationPerFile] + } + + func testUsing() { + assertParse( + "using @MainActor", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .attribute( + AttributeSyntax( + attributeName: IdentifierTypeSyntax( + name: .identifier("MainActor") + ) + ) + ) + ) + ) + assertParse( + "using nonisolated", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .modifier(.identifier("nonisolated")) + ) + ) + + assertParse( + "using @Test", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .attribute( + AttributeSyntax( + attributeName: IdentifierTypeSyntax( + name: .identifier("Test") + ) + ) + ) + ) + ) + + assertParse( + "using test", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .modifier(.identifier("test")) + ) + ) + + assertParse( + """ + nonisolated + using + """, + substructure: CodeBlockSyntax( + DeclReferenceExprSyntax(baseName: .identifier("using")) + ) + ) + + assertParse( + """ + 1️⃣@MainActor + using2️⃣ + """, + substructure: CodeBlockItemSyntax( + item: CodeBlockItemSyntax.Item( + UsingDeclSyntax( + UnexpectedNodesSyntax([ + Syntax( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: TypeSyntax(IdentifierTypeSyntax(name: .identifier("MainActor"))) + ) + ) + ]), + usingKeyword: .keyword(.using), + specifier: UsingDeclSyntax.Specifier(.identifier("", presence: .missing)) + ) + ) + ), + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '@MainActor' before using"), + DiagnosticSpec(locationMarker: "2️⃣", message: "expected identifier in using", fixIts: ["insert identifier"]), + ], + fixedSource: """ + @MainActor + using <#identifier#> + """ + ) + + assertParse( + """ + using + @MainActor 1️⃣ + """, + diagnostics: [ + DiagnosticSpec( + message: "expected declaration after attribute", + fixIts: ["insert declaration"] + ) + ], + fixedSource: + """ + using + @MainActor <#declaration#> + """ + ) + + assertParse( + """ + using + nonisolated + """, + substructure: CodeBlockSyntax( + DeclReferenceExprSyntax(baseName: .identifier("using")) + ) + ) + + assertParse( + """ + func + using (x: Int) {} + """ + ) + + assertParse( + """ + func + using + (x: Int) {} + """ + ) + + assertParse( + """ + let + using = 42 + """ + ) + + assertParse("let (x: Int, using: String) = (x: 42, using: \"\")") + + assertParse( + """ + do { + using @MainActor + } + """ + ) + } + + func testAccessorBlockDisambiguationMarker() { + assertParse( + """ + var value = initialValue { @_accessorBlock + get + } + """, + substructure: VariableDeclSyntax( + bindingSpecifier: .keyword(.var), + bindings: [ + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: .identifier("value")), + initializer: InitializerClauseSyntax( + value: DeclReferenceExprSyntax(baseName: .identifier("initialValue")) + ), + accessorBlock: AccessorBlockSyntax( + leftBrace: .leftBraceToken(), + accessors: .accessors([ + AccessorDeclSyntax( + attributes: [ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: IdentifierTypeSyntax(name: .identifier("_accessorBlock")) + ) + ) + ], + accessorSpecifier: .keyword(.get) + ) + ]) + ) + ) + ] + ) + ) + } + + func testAccessorBlockAfterPatternBindingDeclWithAttribute() { + assertParse( + """ + var x: Int = foo() + { + @available(*, deprecated) + didSet {} + } + """ + ) + } } diff --git a/Tests/SwiftParserTest/DirectiveTests.swift b/Tests/SwiftParserTest/DirectiveTests.swift index d89d1431d1f..35848f2db25 100644 --- a/Tests/SwiftParserTest/DirectiveTests.swift +++ b/Tests/SwiftParserTest/DirectiveTests.swift @@ -151,12 +151,48 @@ final class DirectiveTests: ParserTestCase { #endif """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace before conditional compilation clause"), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in conditional compilation block"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in conditional compilation clause"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in conditional compilation clause"), ] ) } + func testPoundIfNestedStructure() { + assertParse( + """ + #if true + #if true + @frozen + #endif + #endif + public struct S {} + """, + substructure: AttributeListSyntax([ + .ifConfigDecl( + IfConfigDeclSyntax(clauses: [ + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("true"), + elements: .attributes([ + .ifConfigDecl( + IfConfigDeclSyntax(clauses: [ + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("true"), + elements: .attributes([ + .attribute("@frozen") + ]) + ) + ]) + ) + ]) + ) + ]) + ) + ]) + ) + } + func testHasAttribute() { assertParse( """ @@ -321,4 +357,257 @@ final class DirectiveTests: ParserTestCase { ] ) } + + func testIfConfigAfterAttribute() { + assertParse( + """ + @frozen1️⃣ + + #if true + func foo() {} + #endif + """, + substructure: CodeBlockItemListSyntax([ + CodeBlockItemSyntax( + item: .init( + MissingDeclSyntax( + attributes: [ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: TypeSyntax(IdentifierTypeSyntax(name: .identifier("frozen"))) + ) + ) + ], + modifiers: [], + placeholder: .identifier("<#declaration#>", presence: .missing) + ) + ) + ), + CodeBlockItemSyntax( + item: .init( + IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax([ + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax(BooleanLiteralExprSyntax(literal: .keyword(.true))), + elements: .statements( + CodeBlockItemListSyntax([ + CodeBlockItemSyntax( + item: CodeBlockItemSyntax.Item( + FunctionDeclSyntax( + attributes: [], + modifiers: [], + funcKeyword: .keyword(.func), + name: .identifier("foo"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax( + leftParen: .leftParenToken(), + parameters: FunctionParameterListSyntax([]), + rightParen: .rightParenToken() + ) + ), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: CodeBlockItemListSyntax([]), + rightBrace: .rightBraceToken() + ) + ) + ) + ) + ]) + ) + ) + ]), + poundEndif: .poundEndifToken() + ) + ) + ), + ]), + diagnostics: [ + DiagnosticSpec(message: "expected declaration after attribute", fixIts: ["insert declaration"]) + ], + fixedSource: """ + @frozen <#declaration#> + + #if true + func foo() {} + #endif + """ + ) + } + + func testSourcelocationDirectiveNewlineParen() { + assertParse( + """ + #sourceLocation1️⃣ + (file: "other.swift", line: 1) + var someName: Int + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '(', arguments, and ')' in '#sourceLocation' directive", + fixIts: ["insert '(', arguments, and ')'"] + ) + ], + fixedSource: """ + #sourceLocation(file: "", line: <#integer literal#>) + (file: "other.swift", line: 1) + var someName: Int + """ + ) + } + + func testOrphanEndifInMember() { + assertParse( + """ + struct S ℹ️{1️⃣ + 2️⃣#endif + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '}' to end struct", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"), + ], + fixedSource: """ + struct S { + } + #endif + } + """ + ) + } + + func testOrphanEndifInCodeBlock() { + assertParse( + """ + func foo() ℹ️{1️⃣ + 2️⃣#endif + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '}' to end function", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"), + ], + fixedSource: """ + func foo() { + } + #endif + } + """ + ) + } + + func testOrphanEndifInSwitch() { + assertParse( + """ + switch subject ℹ️{1️⃣ + 2️⃣#endif + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '}' to end 'switch' statement", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"), + ], + fixedSource: """ + switch subject { + } + #endif + } + """ + ) + } + + func testOrphanEndifInSwitchCase() { + assertParse( + """ + switch subject ℹ️{ + case foo: + print()1️⃣ + 2️⃣#endif + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '}' to end 'switch' statement", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"), + ], + fixedSource: """ + switch subject { + case foo: + print() + } + #endif + } + """ + ) + } + + func testRightBraceInIfConfig() { + assertParse( + """ + struct S { + #if true + 1️⃣} + #endif + } + func foo() { + #if true + 2️⃣} + #endif + } + """, + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected brace in conditional compilation clause"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in conditional compilation clause"), + ] + ) + } + + func testMismatchedPoundIfAndCodeBlock() { + assertParse( + """ + #if FOO + func foo() ℹ️{1️⃣ + #endif + 2️⃣} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '}' to end function", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in source file"), + ], + fixedSource: """ + #if FOO + func foo() { + } + #endif + } + """ + ) + } } diff --git a/Tests/SwiftParserTest/DoExpressionTests.swift b/Tests/SwiftParserTest/DoExpressionTests.swift index 3dc5b4efd83..b34e85ba79e 100644 --- a/Tests/SwiftParserTest/DoExpressionTests.swift +++ b/Tests/SwiftParserTest/DoExpressionTests.swift @@ -332,7 +332,7 @@ final class DoExpressionTests: ParserTestCase { } ), diagnostics: [ - DiagnosticSpec(message: "extraneous code 'as Int' at top level") + DiagnosticSpec(message: "unexpected code 'as Int' in source file") ], experimentalFeatures: [] ) diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index e63d753d2da..41ac9978167 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -233,7 +233,7 @@ final class ExpressionTests: ParserTestCase { #""" \String?.!.count1️⃣.? """#, - diagnostics: [DiagnosticSpec(message: "extraneous code '.?' at top level")] + diagnostics: [DiagnosticSpec(message: "unexpected code '.?' in source file")] ) assertParse( @@ -246,7 +246,7 @@ final class ExpressionTests: ParserTestCase { #""" \Optional.?!?!?!?1️⃣.??! """#, - diagnostics: [DiagnosticSpec(message: "extraneous code '.??!' at top level")] + diagnostics: [DiagnosticSpec(message: "unexpected code '.??!' in source file")] ) assertParse( @@ -678,14 +678,14 @@ final class ExpressionTests: ParserTestCase { func testChainedOptionalUnwrapsWithDot() { assertParse( #"\T.?1️⃣.!"#, - diagnostics: [DiagnosticSpec(message: "extraneous code '.!' at top level")] + diagnostics: [DiagnosticSpec(message: "unexpected code '.!' in source file")] ) } func testChainedOptionalUnwrapsAfterSubscript() { assertParse( #"\T.abc[2]1️⃣.?"#, - diagnostics: [DiagnosticSpec(message: "extraneous code '.?' at top level")] + diagnostics: [DiagnosticSpec(message: "unexpected code '.?' in source file")] ) } @@ -1214,6 +1214,31 @@ final class ExpressionTests: ParserTestCase { DiagnosticSpec(message: "invalid escape sequence in literal") ] ) + + // QoI: Bad error message when using Objective-C literals (@"Hello") + assertParse( + """ + _ = 1️⃣@"a" + """, + diagnostics: [ + DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"]) + ], + fixedSource: """ + _ = "a" + """ + ) + + assertParse( + """ + 1️⃣@"a" + """, + diagnostics: [ + DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"]) + ], + fixedSource: """ + "a" + """ + ) } func testAdjacentRawStringLiterals() { @@ -1739,7 +1764,7 @@ final class ExpressionTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: #"extraneous code ')"' at top level"# + message: #"unexpected code ')"' in source file"# ), ], fixedSource: #""" @@ -1882,6 +1907,62 @@ final class ExpressionTests: ParserTestCase { ) } + func testInvalidMultiLineClosingDelimiter() { + assertParse( + #""" + "a"1️⃣""2️⃣ a3️⃣ a4️⃣ℹ️"""5️⃣ + """#, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: [ + "insert newline", "insert ';'", + ] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: [ + "insert newline", "insert ';'", + ] + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: [ + "insert newline", "insert ';'", + ] + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: [ + "insert newline", "insert ';'", + ] + ), + DiagnosticSpec( + locationMarker: "5️⃣", + message: #"expected '"""' to end string literal"#, + notes: [ + NoteSpec(message: #"to match this opening '"""'"#) + ], + fixIts: [ + #"insert '"""'"# + ] + ), + ], + fixedSource: #""" + "a" + "" + a + a + """ + """ + """# + ) + } + func testEmptyLineInMultilineStringLiteral() { assertParse( #""" @@ -2125,6 +2206,94 @@ final class ExpressionTests: ParserTestCase { """ ) } + + func testTrailingCommasInTypeExpressions() { + assertParse( + """ + let _ = Foo2.self + """ + ) + + assertParse( + """ + let _ = Foo2() + """ + ) + + assertParse( + """ + let _ = ((Int, Bool, String,) -> Void).self + """ + ) + + assertParse( + """ + let _ = Array<( + bar: String, + baaz: String, + )>() + """, + substructure: FunctionCallExprSyntax( + calledExpression: GenericSpecializationExprSyntax( + expression: DeclReferenceExprSyntax(baseName: .identifier("Array")), + genericArgumentClause: GenericArgumentClauseSyntax( + leftAngle: .leftAngleToken(), + arguments: GenericArgumentListSyntax([ + GenericArgumentSyntax( + argument: .type( + TypeSyntax( + TupleTypeSyntax( + leftParen: .leftParenToken(), + elements: TupleTypeElementListSyntax([ + TupleTypeElementSyntax( + firstName: .identifier("bar"), + colon: .colonToken(), + type: TypeSyntax(IdentifierTypeSyntax(name: .identifier("String"))), + trailingComma: .commaToken() + ), + TupleTypeElementSyntax( + firstName: .identifier("baaz"), + colon: .colonToken(), + type: TypeSyntax(IdentifierTypeSyntax(name: .identifier("String"))), + trailingComma: .commaToken() + ), + ]), + rightParen: .rightParenToken() + ) + ) + ) + ) + ]), + rightAngle: .rightAngleToken() + ) + ), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([]), + rightParen: .rightParenToken() + ) + ) + } + + func testSecondaryArgumentLabelDollarIdentifierInClosure() { + assertParse( + """ + ℹ️{ a1️⃣: (a $2️⃣ + """, + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code ': (a $' in closure"), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected '}' to end closure", + notes: [NoteSpec(message: "to match this opening '{'")], + fixIts: ["insert '}'"] + ), + ], + fixedSource: """ + { a: (a $ + } + """ + ) + } } final class MemberExprTests: ParserTestCase { @@ -2531,7 +2700,7 @@ final class StatementExpressionTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(message: "unexpected 'is' keyword in 'switch' statement") + DiagnosticSpec(message: "unexpected 'is' keyword in switch case") ] ) } @@ -2540,10 +2709,28 @@ final class StatementExpressionTests: ParserTestCase { assertParse( """ switch x { - 1️⃣@case + @1️⃣case2️⃣ } """, - diagnostics: [DiagnosticSpec(message: "unexpected code '@case' in 'switch' statement")] + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + // FIXME: "expected attribute name after '@'". https://github.com/swiftlang/swift-syntax/issues/3159 + message: "expected type in attribute", + fixIts: ["insert type"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + // FIXME: "expected pattern and ':' in switch case". https://github.com/swiftlang/swift-syntax/issues/3158 + message: "expected expression and ':' in switch case", + fixIts: ["insert expression and ':'"] + ), + ], + fixedSource: """ + switch x { + @<#identifier#> case <#expression#>: + } + """ ) } @@ -2881,7 +3068,7 @@ final class StatementExpressionTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: #"extraneous code ')"' at top level"# + message: #"unexpected code ')"' in source file"# ), ], fixedSource: #""" @@ -2911,7 +3098,7 @@ final class StatementExpressionTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "3️⃣", - message: #"extraneous code ')"' at top level"# + message: #"unexpected code ')"' in source file"# ), ], fixedSource: #""" @@ -3405,7 +3592,7 @@ final class StatementExpressionTests: ParserTestCase { #endif """, diagnostics: [ - DiagnosticSpec(message: "unexpected brace before class") + DiagnosticSpec(message: "unexpected brace in conditional compilation clause") ] ) } diff --git a/Tests/SwiftParserTest/ExpressionTypeTests.swift b/Tests/SwiftParserTest/ExpressionTypeTests.swift index 5def6ec1241..2c2f45b1ce0 100644 --- a/Tests/SwiftParserTest/ExpressionTypeTests.swift +++ b/Tests/SwiftParserTest/ExpressionTypeTests.swift @@ -111,26 +111,26 @@ final class ExpressionTypeTests: ParserTestCase { // Make sure we can handle cases where the type is spelled first in // an InlineArray sugar type. let cases: [UInt: String] = [ - #line: "[3 x Int]", - #line: "[[3 x Int]]", - #line: "[[Int x 3]]", - #line: "[_ x Int]", - #line: "[Int x Int]", - #line: "[@escaping () -> Int x Int]", - #line: "[Int.Type x Int]", - #line: "[sending P & Q x Int]", - #line: "[(some P & Q) -> Int x Int]", - #line: "[~P x Int]", - #line: "[(Int, String) x Int]", - #line: "[G x Int]", - #line: "[[3 x Int] x Int]", - #line: "[[Int] x Int]", - #line: "[_ x Int]", - #line: "[_? x Int]", - #line: "[_?x Int]", - #line: "[_! x Int]", - #line: "[_!x Int]", - #line: "[Int?x Int]", + #line: "[3 of Int]", + #line: "[[3 of Int]]", + #line: "[[Int of 3]]", + #line: "[_ of Int]", + #line: "[Int of Int]", + #line: "[@escaping () -> Int of Int]", + #line: "[Int.Type of Int]", + #line: "[sending P & Q of Int]", + #line: "[(some P & Q) -> Int of Int]", + #line: "[~P of Int]", + #line: "[(Int, String) of Int]", + #line: "[G of Int]", + #line: "[[3 of Int] of Int]", + #line: "[[Int] of Int]", + #line: "[_ of Int]", + #line: "[_? of Int]", + #line: "[_?of Int]", + #line: "[_! of Int]", + #line: "[_!of Int]", + #line: "[Int?of Int]", ] for (line, type) in cases { assertParse( @@ -138,7 +138,6 @@ final class ExpressionTypeTests: ParserTestCase { { ExprSyntax.parse(from: &$0) }, substructure: IdentifierTypeSyntax(name: .identifier("X")), substructureAfterMarker: "1️⃣", - experimentalFeatures: [.inlineArrayTypeSugar], line: line ) } diff --git a/Tests/SwiftParserTest/LexerTests.swift b/Tests/SwiftParserTest/LexerTests.swift index 910d61c4510..16ffd75a5a8 100644 --- a/Tests/SwiftParserTest/LexerTests.swift +++ b/Tests/SwiftParserTest/LexerTests.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -@_spi(RawSyntax) @_spi(Testing) import SwiftParser -@_spi(RawSyntax) import SwiftSyntax +@_spi(RawSyntax) @_spi(Testing) @_spi(ExperimentalLanguageFeatures) import SwiftParser +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax import XCTest -fileprivate func lex(_ sourceBytes: [UInt8], body: ([Lexer.Lexeme]) throws -> Void) rethrows { +private func lex(_ sourceBytes: [UInt8], body: ([Lexer.Lexeme]) throws -> Void) rethrows { let lookaheadTracker = UnsafeMutablePointer.allocate(capacity: 1) defer { lookaheadTracker.deallocate() @@ -22,7 +22,7 @@ fileprivate func lex(_ sourceBytes: [UInt8], body: ([Lexer.Lexeme]) throws -> Vo lookaheadTracker.initialize(to: LookaheadTracker()) try sourceBytes.withUnsafeBufferPointer { (buf) in var lexemes = [Lexer.Lexeme]() - for token in Lexer.tokenize(buf, from: 0, lookaheadTracker: lookaheadTracker) { + for token in Lexer.tokenize(buf, from: 0, lookaheadTracker: lookaheadTracker, experimentalFeatures: []) { lexemes.append(token) if token.rawTokenKind == .endOfFile { @@ -37,7 +37,7 @@ fileprivate func lex(_ sourceBytes: [UInt8], body: ([Lexer.Lexeme]) throws -> Vo /// values for trivia and text. While this is good for most cases, string /// literals can't contain invalid UTF-8. Thus, we need a different assert /// function working on byte arrays to test source code containing invalid UTF-8. -fileprivate func assertRawBytesLexeme( +private func assertRawBytesLexeme( _ lexeme: Lexer.Lexeme, kind: RawTokenKind, leadingTrivia: [UInt8] = [], @@ -579,6 +579,16 @@ class LexerTests: ParserTestCase { LexemeSpec(.binaryOperator, text: "^", trailing: "/*/", diagnostic: "unterminated '/*' comment") ] ) + assertLexemes( + "(Foo::/)", + lexemes: [ + LexemeSpec(.leftParen, text: "("), + LexemeSpec(.identifier, text: "Foo"), + LexemeSpec(.colonColon, text: "::"), + LexemeSpec(.binaryOperator, text: "/"), + LexemeSpec(.rightParen, text: ")"), + ] + ) } func testUnexpectedLexing() { @@ -834,6 +844,54 @@ class LexerTests: ParserTestCase { ) } + func testTwoColons() { + assertLexemes( + "Foo::bar", + lexemes: [ + LexemeSpec(.identifier, text: "Foo"), + LexemeSpec(.colonColon, text: "::"), + LexemeSpec(.identifier, text: "bar"), + ] + ) + + assertLexemes( + "Foo ::bar", + lexemes: [ + LexemeSpec(.identifier, text: "Foo", trailing: " "), + LexemeSpec(.colonColon, text: "::"), + LexemeSpec(.identifier, text: "bar"), + ] + ) + + assertLexemes( + "Foo:: bar", + lexemes: [ + LexemeSpec(.identifier, text: "Foo"), + LexemeSpec(.colonColon, text: "::", trailing: " "), + LexemeSpec(.identifier, text: "bar"), + ] + ) + + assertLexemes( + "Foo :: bar", + lexemes: [ + LexemeSpec(.identifier, text: "Foo", trailing: " "), + LexemeSpec(.colonColon, text: "::", trailing: " "), + LexemeSpec(.identifier, text: "bar"), + ] + ) + + assertLexemes( + "Foo: :bar", + lexemes: [ + LexemeSpec(.identifier, text: "Foo"), + LexemeSpec(.colon, text: ":", trailing: " "), + LexemeSpec(.colon, text: ":"), + LexemeSpec(.identifier, text: "bar"), + ] + ) + } + func testEditorPlaceholders() { assertLexemes( "!1️⃣<#b1#> && !2️⃣<#b2#>", diff --git a/Tests/SwiftParserTest/Parser+EntryTests.swift b/Tests/SwiftParserTest/Parser+EntryTests.swift index c0b84809f86..91ca7a12e2c 100644 --- a/Tests/SwiftParserTest/Parser+EntryTests.swift +++ b/Tests/SwiftParserTest/Parser+EntryTests.swift @@ -37,6 +37,72 @@ class EntryTests: ParserTestCase { ) } + func testDeclSyntaxParseIfConfig() throws { + assertParse( + """ + #if FLAG + func test() {} + #endif + """, + { DeclSyntax.parse(from: &$0) }, + substructure: IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax([ + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax(DeclReferenceExprSyntax(baseName: .identifier("FLAG"))), + elements: .init([ + MemberBlockItemSyntax( + decl: DeclSyntax( + FunctionDeclSyntax( + funcKeyword: .keyword(.func), + name: .identifier("test"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax(parameters: []) + ), + body: CodeBlockSyntax(statements: []) + ) + ) + ) + ]) + ) + ]) + ) + ) + } + + func testDeclSyntaxParseIfConfigAttr() throws { + assertParse( + """ + #if FLAG + @attr + #endif + func test() {} + """, + { DeclSyntax.parse(from: &$0) }, + substructure: FunctionDeclSyntax( + attributes: [ + .ifConfigDecl( + IfConfigDeclSyntax(clauses: [ + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: DeclReferenceExprSyntax(baseName: .identifier("FLAG")), + elements: .attributes([ + .attribute(AttributeSyntax(TypeSyntax(IdentifierTypeSyntax(name: .identifier("attr"))))) + ]) + ) + ]) + ) + ], + funcKeyword: .keyword(.func), + name: .identifier("test"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax(parameters: []) + ), + body: CodeBlockSyntax(statements: []) + ) + ) + } + func testRemainderUnexpected() throws { assertParse( "func test() {} 1️⃣other tokens", diff --git a/Tests/SwiftParserTest/RegexLiteralTests.swift b/Tests/SwiftParserTest/RegexLiteralTests.swift index 56cf4d85dd7..3c84cb49c28 100644 --- a/Tests/SwiftParserTest/RegexLiteralTests.swift +++ b/Tests/SwiftParserTest/RegexLiteralTests.swift @@ -1388,7 +1388,7 @@ final class RegexLiteralTests: ParserTestCase { """, substructure: BinaryOperatorExprSyntax(operator: .binaryOperator("/")), diagnostics: [ - DiagnosticSpec(message: "extraneous code ':/def/' at top level") + DiagnosticSpec(message: "unexpected code ':/def/' in source file") ] ) } diff --git a/Tests/SwiftParserTest/StatementTests.swift b/Tests/SwiftParserTest/StatementTests.swift index 39872342bbe..228aec6d6ee 100644 --- a/Tests/SwiftParserTest/StatementTests.swift +++ b/Tests/SwiftParserTest/StatementTests.swift @@ -257,6 +257,20 @@ final class StatementTests: ParserTestCase { ) } + func testUnknownDefaultAtStatement() { + assertParse( + """ + func test() { + 1️⃣@unknown default: + return + } + """, + diagnostics: [ + DiagnosticSpec(message: "'default' label can only appear inside a 'switch' statement") + ] + ) + } + func testMissingIfClauseIntroducer() { assertParse("if _ = 42 {}") } @@ -265,16 +279,32 @@ final class StatementTests: ParserTestCase { assertParse( """ func test1() { - 1️⃣@s return + @s 1️⃣return } func test2() { - 2️⃣@unknown return + @unknown 2️⃣return } """, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '@s' before 'return' statement"), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '@unknown' before 'return' statement"), - ] + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected declaration and ';' after attribute", + fixIts: ["insert declaration and ';'"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected declaration and ';' after attribute", + fixIts: ["insert declaration and ';'"] + ), + ], + fixedSource: """ + func test1() { + @s <#declaration#>; return + } + func test2() { + @unknown <#declaration#>; return + } + """ ) } @@ -335,7 +365,7 @@ final class StatementTests: ParserTestCase { assertParse( "LABEL1️⃣:", diagnostics: [ - DiagnosticSpec(message: "extraneous code ':' at top level") + DiagnosticSpec(message: "unexpected code ':' in source file") ] ) } @@ -703,7 +733,7 @@ final class StatementTests: ParserTestCase { func testRecoveryInFrontOfAccessorIntroducer() { assertParse( """ - subscript1️⃣(2️⃣{3️⃣@self _modify + subscript1️⃣(2️⃣{@attr _modify3️⃣ """, diagnostics: [ DiagnosticSpec( @@ -723,14 +753,10 @@ final class StatementTests: ParserTestCase { notes: [NoteSpec(locationMarker: "2️⃣", message: "to match this opening '{'")], fixIts: ["insert '}'"] ), - DiagnosticSpec( - locationMarker: "3️⃣", - message: "extraneous code '@self _modify' at top level" - ), ], fixedSource: """ - subscript() -> <#type#> { - }@self _modify + subscript() -> <#type#> {@attr _modify + } """ ) } @@ -830,7 +856,7 @@ final class StatementTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "missing condition in 'if' statement"), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in 'if' statement"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected 'in' keyword in 'if' statement"), ] ) } @@ -867,7 +893,7 @@ final class StatementTests: ParserTestCase { "guard test 1️⃣{ $0 } 2️⃣else {}", diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'else' in 'guard' statement", fixIts: ["insert 'else'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'else {}' at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'else {}' in source file"), ], fixedSource: "guard test else { $0 } else {}" ) @@ -880,7 +906,7 @@ final class StatementTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'else' in 'guard' statement", fixIts: ["insert 'else'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'else {}' at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'else {}' in source file"), ], fixedSource: """ @@ -897,7 +923,7 @@ final class StatementTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'else' in 'guard' statement", fixIts: ["insert 'else'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'else {}' at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'else {}' in source file"), ], fixedSource: """ guard test else { $0 @@ -913,8 +939,8 @@ final class StatementTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'else' in 'guard' statement", fixIts: ["insert 'else'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in 'guard' statement"), - DiagnosticSpec(locationMarker: "3️⃣", message: "extraneous code 'else {}' at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected 'in' keyword in 'guard' statement"), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'else {}' in source file"), ], fixedSource: """ guard test else { x in diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index 6e49d5fa75d..362c8ebb404 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -734,27 +734,61 @@ final class TypeTests: ParserTestCase { fixedSource: "func foo(test: nonisolated(nonsendinghello) () async -> Void)" ) } -} -final class InlineArrayTypeTests: ParserTestCase { - override var experimentalFeatures: Parser.ExperimentalFeatures { - [.inlineArrayTypeSugar] + func testTrailingCommas() { + assertParse( + """ + let foo: ( + bar: String, + quux: String, + ) + """ + ) + + assertParse( + """ + let closure: ( + String, + String, + ) -> ( + bar: String, + quux: String, + ) + """ + ) + + assertParse( + """ + struct Foo {} + + typealias Bar< + T1, + T2, + > = Foo< + T1, + T2, + Bool, + > + """ + ) } +} +final class InlineArrayTypeTests: ParserTestCase { func testBasic() { assertParse( - "[3 x Int]", + "[3 of Int]", substructure: InlineArrayTypeSyntax( count: .init(argument: .expr("3")), - separator: .keyword(.x), + separator: .keyword(.of), element: .init(argument: .type(TypeSyntax("Int"))) ) ) assertParse( - "[Int x _]", + "[Int of _]", substructure: InlineArrayTypeSyntax( count: .init(argument: .type(TypeSyntax("Int"))), - separator: .keyword(.x), + separator: .keyword(.of), element: .init(argument: .type(TypeSyntax("_"))) ) ) @@ -766,7 +800,7 @@ final class InlineArrayTypeTests: ParserTestCase { """ S<[ 3 - 1️⃣x + 1️⃣of Int ]>() """, @@ -777,7 +811,7 @@ final class InlineArrayTypeTests: ParserTestCase { assertParse( """ S<[3 - 1️⃣x + 1️⃣of Int ]>() """, @@ -788,23 +822,23 @@ final class InlineArrayTypeTests: ParserTestCase { assertParse( """ S<[3 - 1️⃣x Int]>() + 1️⃣of Int]>() """, diagnostics: [ - DiagnosticSpec(message: "unexpected code 'x Int' in array") + DiagnosticSpec(message: "unexpected code 'of Int' in array") ] ) // These are okay. assertParse( """ - S<[3 x + S<[3 of Int]>() """ ) assertParse( """ S<[ - 3 x Int + 3 of Int ]>() """ ) @@ -812,17 +846,17 @@ final class InlineArrayTypeTests: ParserTestCase { func testDiagnostics() { assertParse( - "2️⃣[3 x1️⃣", + "2️⃣[3 of1️⃣", diagnostics: [ DiagnosticSpec( message: "expected element type and ']' to end inline array type", fixIts: ["insert element type and ']'"] ) ], - fixedSource: "[3 x <#type#>]" + fixedSource: "[3 of <#type#>]" ) assertParse( - "ℹ️[3 x Int1️⃣", + "ℹ️[3 of Int1️⃣", diagnostics: [ DiagnosticSpec( message: "expected ']' to end inline array type", @@ -830,12 +864,12 @@ final class InlineArrayTypeTests: ParserTestCase { fixIts: ["insert ']'"] ) ], - fixedSource: "[3 x Int]" + fixedSource: "[3 of Int]" ) } func testEllipsis() { - // Make sure this isn't parsed as ' x ' - assertParse("[x...x]") + // Make sure this isn't parsed as ' of ' + assertParse("[x...of]") } } diff --git a/Tests/SwiftParserTest/ValueGenericsTests.swift b/Tests/SwiftParserTest/ValueGenericsTests.swift index 7350ca0b749..0075a3b0a64 100644 --- a/Tests/SwiftParserTest/ValueGenericsTests.swift +++ b/Tests/SwiftParserTest/ValueGenericsTests.swift @@ -71,7 +71,7 @@ final class ValueGenericsTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "extraneous code '>() {}' at top level" + message: "unexpected code '>() {}' in source file" ), ], fixedSource: """ diff --git a/Tests/SwiftParserTest/translated/EnumTests.swift b/Tests/SwiftParserTest/translated/EnumTests.swift index 946169ae67c..d199ed13f1e 100644 --- a/Tests/SwiftParserTest/translated/EnumTests.swift +++ b/Tests/SwiftParserTest/translated/EnumTests.swift @@ -1393,7 +1393,7 @@ final class EnumTests: ParserTestCase { assertParse( """ enum E_53662_PatternMatching { - case 1️⃣let 2️⃣.foo(x, y): + case 1️⃣let 2️⃣.3️⃣foo(x4️⃣, y5️⃣)6️⃣: } """, diagnostics: [ @@ -1404,12 +1404,32 @@ final class EnumTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "unexpected code '.foo(x, y):' in enum" + message: "unexpected code '.' in enum" ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "expected 'func' in function", + fixIts: ["insert 'func'"] + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected ':' and type in parameter", + fixIts: ["insert ':' and type"] + ), + DiagnosticSpec( + locationMarker: "5️⃣", + message: "expected ':' and type in parameter", + fixIts: ["insert ':' and type"] + ), + DiagnosticSpec( + locationMarker: "6️⃣", + message: "unexpected code ':' in enum" + ), + ], fixedSource: """ enum E_53662_PatternMatching { - case `let` .foo(x, y): + case `let` .func foo(x: <#type#>, y: <#type#>): } """ ) diff --git a/Tests/SwiftParserTest/translated/ErrorsTests.swift b/Tests/SwiftParserTest/translated/ErrorsTests.swift index 0f86ff82a38..cb79adc7483 100644 --- a/Tests/SwiftParserTest/translated/ErrorsTests.swift +++ b/Tests/SwiftParserTest/translated/ErrorsTests.swift @@ -300,9 +300,15 @@ final class ErrorsTests: ParserTestCase { ) ) ) - ) - ]), - UnexpectedNodesSyntax([TokenSyntax.keyword(.throws)]) + ), + CodeBlockItemSyntax( + item: .decl( + DeclSyntax( + UnexpectedCodeDeclSyntax(unexpectedCode: UnexpectedNodesSyntax([TokenSyntax.keyword(.throws)])) + ) + ) + ), + ]) ), diagnostics: [ DiagnosticSpec(message: "unexpected 'throws' keyword in function") diff --git a/Tests/SwiftParserTest/translated/EscapedIdentifiersTests.swift b/Tests/SwiftParserTest/translated/EscapedIdentifiersTests.swift index 939beb8b972..c47d921ec53 100644 --- a/Tests/SwiftParserTest/translated/EscapedIdentifiersTests.swift +++ b/Tests/SwiftParserTest/translated/EscapedIdentifiersTests.swift @@ -205,14 +205,22 @@ final class EscapedIdentifiersTests: ParserTestCase { assertParse( """ 1️⃣`multiline is - not allowed` = 5 + not2️⃣ allowed3️⃣` = 5 """, diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '`multiline is' in source file"), DiagnosticSpec( - locationMarker: "1️⃣", - message: "extraneous code at top level" - ) - ] + locationMarker: "2️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code '` = 5' in source file"), + ], + fixedSource: """ + `multiline is + not + allowed` = 5 + """ ) } diff --git a/Tests/SwiftParserTest/translated/ForwardSlashRegexSkippingInvalidTests.swift b/Tests/SwiftParserTest/translated/ForwardSlashRegexSkippingInvalidTests.swift index 3959dc9d2bf..1c25a07b872 100644 --- a/Tests/SwiftParserTest/translated/ForwardSlashRegexSkippingInvalidTests.swift +++ b/Tests/SwiftParserTest/translated/ForwardSlashRegexSkippingInvalidTests.swift @@ -156,7 +156,7 @@ final class ForwardSlashRegexSkippingInvalidTests: ParserTestCase { 1️⃣} """, diagnostics: [ - DiagnosticSpec(message: "extraneous brace at top level") + DiagnosticSpec(message: "unexpected brace in source file") ] ) } @@ -322,7 +322,7 @@ final class ForwardSlashRegexSkippingInvalidTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "3️⃣", - message: "extraneous brace at top level" + message: "unexpected brace in source file" ), ], applyFixIts: ["insert '/'"], @@ -365,11 +365,12 @@ final class ForwardSlashRegexSkippingInvalidTests: ParserTestCase { 0 /x}}1️⃣} / 2 - } + 2️⃣} } """, diagnostics: [ - DiagnosticSpec(message: "extraneous code at top level") + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '} /' in source file"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected braces in source file"), ] ) } @@ -381,10 +382,11 @@ final class ForwardSlashRegexSkippingInvalidTests: ParserTestCase { _ = 2 /x} 1️⃣/ .bitWidth - } + 2️⃣} """, diagnostics: [ - DiagnosticSpec(message: "extraneous code at top level") + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '/' in source file"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in source file"), ] ) } diff --git a/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift b/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift index 25e94d39f4a..b916ad09c02 100644 --- a/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift +++ b/Tests/SwiftParserTest/translated/ForwardSlashRegexTests.swift @@ -1423,7 +1423,7 @@ final class ForwardSlashRegexTests: ParserTestCase { _ = /\()1️⃣/ """#, diagnostics: [ - DiagnosticSpec(message: "extraneous code '/' at top level") + DiagnosticSpec(message: "unexpected code '/' in source file") ] ) } diff --git a/Tests/SwiftParserTest/translated/IfconfigExprTests.swift b/Tests/SwiftParserTest/translated/IfconfigExprTests.swift index 9307e79271e..423b586003e 100644 --- a/Tests/SwiftParserTest/translated/IfconfigExprTests.swift +++ b/Tests/SwiftParserTest/translated/IfconfigExprTests.swift @@ -105,7 +105,7 @@ final class IfconfigExprTests: ParserTestCase { } """#, diagnostics: [ - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '+ otherExpr' in conditional compilation block"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '+ otherExpr' in conditional compilation clause"), DiagnosticSpec( locationMarker: "2️⃣", message: #"unexpected code 'print("debug")' in conditional compilation block"# diff --git a/Tests/SwiftParserTest/translated/ModuleSelectorTests.swift b/Tests/SwiftParserTest/translated/ModuleSelectorTests.swift new file mode 100644 index 00000000000..1f2114b0ca8 --- /dev/null +++ b/Tests/SwiftParserTest/translated/ModuleSelectorTests.swift @@ -0,0 +1,2350 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// This test file has been translated from swift/test/Parse/module_selector.swift + +@_spi(ExperimentalLanguageFeatures) import SwiftParser +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax +import XCTest + +final class ModuleSelectorTests: ParserTestCase { + func testModuleSelectorImports() { + assertParse( + """ + import struct ModuleSelectorTestingKit::A + """, + substructure: ImportDeclSyntax( + importKindSpecifier: .keyword(.struct), + path: [ + ImportPathComponentSyntax( + name: .identifier("ModuleSelectorTestingKit"), + trailingPeriod: .colonColonToken() + ), + ImportPathComponentSyntax( + name: .identifier("A") + ), + ] + ) + ) + + assertParse( + """ + import struct 1️⃣_::A + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + + assertParse( + """ + import struct ModuleSelectorTestingKit::1️⃣Submodule::A + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'Submodule::' in import") + ] + ) + + assertParse( + """ + import struct ModuleSelectorTestingKit.Submodule1️⃣::A + """, + diagnostics: [ + DiagnosticSpec( + message: "submodule cannot be imported using module selector", + fixIts: ["replace '::' with '.'"] + ) + ], + fixedSource: """ + import struct ModuleSelectorTestingKit.Submodule.A + """ + ) + + assertParse( + """ + import ctypes1️⃣::bits + """, + diagnostics: [ + DiagnosticSpec( + message: "submodule cannot be imported using module selector", + fixIts: ["replace '::' with '.'"] + ) + ], + fixedSource: """ + import ctypes.bits + """ + ) + } + + func testModuleSelectorCorrectCode() { + assertParse( + """ + extension ModuleSelectorTestingKit::A {} + """, + substructure: ExtensionDeclSyntax( + extendedType: makeType(moduleSelector: "ModuleSelectorTestingKit", name: "A"), + memberBlock: MemberBlockSyntax {} + ) + ) + + assertParse( + """ + extension A: @retroactive Swift::Equatable {} + """, + substructure: ExtensionDeclSyntax( + extendedType: IdentifierTypeSyntax(name: .identifier("A")), + inheritanceClause: InheritanceClauseSyntax( + inheritedTypes: [ + InheritedTypeSyntax( + type: AttributedTypeSyntax( + specifiers: [], + attributes: [ + .attribute( + AttributeSyntax( + attributeName: makeType(name: "retroactive"), + arguments: nil + ) + ) + ], + baseType: makeType(moduleSelector: "Swift", name: "Equatable") + ) + ) + ] + ), + memberBlock: MemberBlockSyntax {} + ) + ) + + assertParse( + """ + @_implements(Swift::Equatable, ==(_:_:)) + public static func equals(_: ModuleSelectorTestingKit::A, _: ModuleSelectorTestingKit::A) -> Swift::Bool { + Swift::fatalError() + } + """, + substructure: FunctionDeclSyntax( + attributes: [ + .attribute( + AttributeSyntax( + attributeName: makeType(name: "_implements"), + leftParen: .leftParenToken(), + arguments: .implementsArguments( + ImplementsAttributeArgumentsSyntax( + type: makeType(moduleSelector: "Swift", name: "Equatable"), + declName: makeDeclRef( + baseName: .binaryOperator("=="), + argumentLabels: [nil, nil] + ) + ) + ), + rightParen: .rightParenToken() + ) + ) + ], + modifiers: [ + DeclModifierSyntax(name: .keyword(.public)), + DeclModifierSyntax(name: .keyword(.static)), + ], + name: .identifier("equals"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax { + FunctionParameterSyntax( + firstName: .wildcardToken(), + type: makeType(moduleSelector: "ModuleSelectorTestingKit", name: "A") + ) + FunctionParameterSyntax( + firstName: .wildcardToken(), + type: makeType(moduleSelector: "ModuleSelectorTestingKit", name: "A") + ) + }, + returnClause: ReturnClauseSyntax( + type: makeType(moduleSelector: "Swift", name: "Bool") + ) + ), + body: CodeBlockSyntax { + makeCall( + callee: ExprSyntax(makeDeclRef(moduleSelector: "Swift", baseName: "fatalError")), + arguments: [:] + ) + } + ) + ) + + // FIXME: Add tests with autodiff @_differentiable(jvp:vjp:) and + // @_derivative(of:) + + assertParse( + """ + @_dynamicReplacement(for: ModuleSelectorTestingKit::negate()) + mutating func myNegate() {} + """, + substructure: FunctionDeclSyntax( + attributes: [ + .attribute( + AttributeSyntax( + attributeName: makeType(name: "_dynamicReplacement"), + leftParen: .leftParenToken(), + arguments: .dynamicReplacementArguments( + DynamicReplacementAttributeArgumentsSyntax( + declName: makeDeclRef( + moduleSelector: "ModuleSelectorTestingKit", + baseName: "negate", + argumentLabels: [] + ) + ) + ), + rightParen: .rightParenToken() + ) + ) + ], + modifiers: [ + DeclModifierSyntax(name: .keyword(.mutating)) + ], + name: .identifier("myNegate"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax {} + ), + body: CodeBlockSyntax { + } + ) + ) + + // FIXME: Can we test @convention(witness_method:)? + + assertParse( + """ + let fn: (Swift::Int, Swift::Int) -> Swift::Int = (Swift::+) + """, + substructure: VariableDeclSyntax(bindingSpecifier: .keyword(.let)) { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: "fn"), + typeAnnotation: TypeAnnotationSyntax( + type: FunctionTypeSyntax( + parameters: TupleTypeElementListSyntax { + TupleTypeElementSyntax(type: makeType(moduleSelector: "Swift", name: "Int")) + TupleTypeElementSyntax(type: makeType(moduleSelector: "Swift", name: "Int")) + }, + returnClause: ReturnClauseSyntax( + type: makeType(moduleSelector: "Swift", name: "Int") + ) + ) + ), + initializer: InitializerClauseSyntax( + value: TupleExprSyntax { + LabeledExprSyntax(expression: makeDeclRef(moduleSelector: "Swift", baseName: .binaryOperator("+"))) + } + ) + ) + } + ) + + assertParse( + """ + let magnitude: Int.Swift::Magnitude = main::magnitude + """, + substructure: VariableDeclSyntax(bindingSpecifier: .keyword(.let)) { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: "magnitude"), + typeAnnotation: TypeAnnotationSyntax( + type: makeMember( + of: makeType(name: "Int"), + moduleSelector: "Swift", + name: "Magnitude" + ) + ), + initializer: InitializerClauseSyntax( + value: makeDeclRef(moduleSelector: "main", baseName: "magnitude") + ) + ) + } + ) + + assertParse( + """ + if Swift::Bool.Swift::random() { + } + """, + substructure: IfExprSyntax( + conditions: ConditionElementListSyntax { + ConditionElementSyntax( + condition: .expression( + ExprSyntax( + makeCall( + callee: MemberAccessExprSyntax( + base: makeDeclRef(moduleSelector: "Swift", baseName: "Bool"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "random") + ), + arguments: [:] + ) + ) + ) + ) + } + ) {} + ) + + assertParse( + """ + self.ModuleSelectorTestingKit::negate() + """, + substructure: makeCall( + callee: MemberAccessExprSyntax( + base: makeDeclRef(baseName: .keyword(.self)), + declName: makeDeclRef(moduleSelector: "ModuleSelectorTestingKit", baseName: "negate") + ), + arguments: [:] + ) + ) + + assertParse( + """ + self = ModuleSelectorTestingKit::A(value: .Swift::min) + """, + substructure: SequenceExprSyntax { + makeDeclRef(baseName: .keyword(.self)) + AssignmentExprSyntax() + makeCall( + callee: makeDeclRef(moduleSelector: "ModuleSelectorTestingKit", baseName: "A"), + arguments: [ + "value": ExprSyntax( + MemberAccessExprSyntax( + declName: makeDeclRef(moduleSelector: "Swift", baseName: "min") + ) + ) + ] + ) + } + ) + + assertParse( + """ + self = A.ModuleSelectorTestingKit::init(value: .min) + """, + substructure: SequenceExprSyntax { + makeDeclRef(baseName: .keyword(.self)) + AssignmentExprSyntax() + makeCall( + callee: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "A"), + declName: makeDeclRef(moduleSelector: "ModuleSelectorTestingKit", baseName: .keyword(.`init`)) + ), + arguments: [ + "value": ExprSyntax( + MemberAccessExprSyntax( + declName: makeDeclRef(baseName: "min") + ) + ) + ] + ) + } + ) + + assertParse( + """ + self.main::myNegate() + """, + substructure: makeCall( + callee: MemberAccessExprSyntax( + base: makeDeclRef(baseName: .keyword(.self)), + declName: makeDeclRef(moduleSelector: "main", baseName: "myNegate") + ), + arguments: [:] + ) + ) + } + + func testModuleSelectorIncorrectAttrNames() { + // An attribute with a module selector *must* be a custom attribute and should be parsed as such. + assertParse( + """ + @2️⃣main::available(macOS 1️⃣10.15, *) var use1: String { "foo" } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '10.15, *' in attribute" + ) + ] + ) + + assertParse( + """ + @main::available var use2 + """, + substructure: AttributeSyntax( + attributeName: makeType(moduleSelector: "main", name: "available") + ) + ) + + assertParse( + """ + @main::available(foo: bar) var use3 + """, + substructure: AttributeSyntax( + attributeName: makeType(moduleSelector: "main", name: "available"), + leftParen: .leftParenToken(), + arguments: .argumentList( + [ + LabeledExprSyntax( + label: "foo", + colon: .colonToken(), + expression: makeDeclRef(baseName: "bar") + ) + ] + ), + rightParen: .rightParenToken() + ) + ) + + assertParse( + """ + func builderUser2(@main::MyBuilder fn: () -> Void) {} + """, + substructure: FunctionParameterSyntax( + attributes: [ + .attribute( + AttributeSyntax( + attributeName: makeType(moduleSelector: "main", name: "MyBuilder") + ) + ) + ], + firstName: "fn", + type: TypeSyntax("() -> Void") + ) + ) + } + + func testModuleSelectorWhitespace() { + assertParse( + """ + _ = Swift::print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift:: print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift ::print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift :: print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift::1️⃣ + print + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: """ + _ = Swift::<#identifier#> + print + """ + ) + assertParse( + """ + _ = Swift + ::print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift ::1️⃣ + print + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: """ + _ = Swift ::<#identifier#> + print + """ + ) + assertParse( + """ + _ = Swift + :: print + """, + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "print") + ) + assertParse( + """ + _ = Swift + ::1️⃣ + print + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: """ + _ = Swift + ::<#identifier#> + print + """ + ) + } + + func testModuleSelectorIncorrectFuncSignature() { + assertParse( + """ + func main1️⃣::decl1() {} + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::decl1' before parameter clause") + ] + ) + assertParse( + """ + func decl1( + main1️⃣::p1: Swift::A + ) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::p1' in parameter" + ) + ] + ) + + // Round-tripping failures: + assertParse( + """ + func decl1( + main1️⃣::p1: 2️⃣::A + ) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::p1' in parameter" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + func decl1( + main::p1: <#identifier#>::A + ) {} + """ + ) + assertParse( + """ + func decl1( + main1️⃣::p1: Swift::2️⃣ + ) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::p1' in parameter" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected identifier in type", + fixIts: ["insert identifier"] + ), + ], + fixedSource: """ + func decl1( + main::p1: Swift::<#identifier#> + ) {} + """ + ) + + assertParse( + """ + func decl1( + main1️⃣::label p2: Swift::inout2️⃣ 3️⃣A + ) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::label p2' in parameter" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected ',' in parameter", + fixIts: ["insert ','"] + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "expected identifier and ':' in parameter", + fixIts: ["insert identifier and ':'"] + ), + ], + fixedSource: """ + func decl1( + main::label p2: Swift::inout, <#identifier#>: A + ) {} + """ + ) + assertParse( + """ + func decl1( + label main1️⃣::p3: @Swift::escaping () -> A + ) {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::p3' in parameter" + ) + ] + ) + } + + func testModuleSelectorIncorrectBindingDecls() { + assertParse( + """ + let main1️⃣::decl1a = "a" + """, + diagnostics: [ + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + let main = <#identifier#>::decl1a = "a" + """ + ) + + // Found by mutation testing: + assertParse( + """ + let let1️⃣::decl1a = "a" + """, + diagnostics: [ + DiagnosticSpec( + message: "expected pattern in value binding pattern", + fixIts: ["insert pattern"] + ), + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + let let<#pattern#> = <#identifier#>::decl1a = "a" + """ + ) + + assertParse( + """ + var main1️⃣::decl1b = "b" + """, + diagnostics: [ + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + var main = <#identifier#>::decl1b = "b" + """ + ) + assertParse( + """ + let (main1️⃣::decl1c, Swift::decl1d) = ("c", "d") + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1c, Swift::decl1d' in tuple pattern" + ) + ] + ) + assertParse( + """ + if let (main1️⃣::decl1e, Swift::decl1f) = Optional(("e", "f")) {} + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1e, Swift::decl1f' in tuple" + ) + ] + ) + assertParse( + """ + guard let (main1️⃣::decl1g, Swift::decl1h) = Optional(("g", "h")) else { return } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1g, Swift::decl1h' in tuple" + ) + ] + ) + assertParse( + """ + switch Optional(main::decl1g) { + case Optional.some(let Swift1️⃣::decl1i): + break + case .none: + break + } + """, + diagnostics: [ + // FIXME: Function call??? + DiagnosticSpec( + message: "unexpected code '::decl1i' in function call" + ) + ] + ) + assertParse( + """ + switch Optional(main::decl1g) { + case let Optional.some(Swift1️⃣::decl1j): + break + case .none: + break + } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1j' in function call" + ) + ] + ) + assertParse( + """ + switch Optional(main::decl1g) { + case let Swift1️⃣::decl1k?: + break + case .none: + break + } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1k?' in switch case" + ) + ] + ) + assertParse( + """ + for main1️⃣::decl1l in "lll" {} + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl1l' in 'for' statement" + ) + ] + ) + } + + func testModuleSelectorIncorrectClosureDecls() { + assertParse( + // This gets radically misinterpreted as two statements followed by some invalid code. + """ + "lll".forEach { [2️⃣Swift::magnitude] + main::elem 1️⃣in print(elem) + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code 'in print(elem)' in closure" + ) + ] + ) + assertParse( + """ + "lll".forEach { (main1️⃣::elem) in print(elem) } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::elem' in parameter clause" + ) + ] + ) + assertParse( + """ + "lll".forEach { (main1️⃣::elem) -> Void in print(elem) } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::elem' in parameter clause" + ) + ] + ) + assertParse( + """ + "lll".forEach { (main1️⃣::elem: Character) -> Void in print(elem) } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::elem: Character' in parameter clause" + ) + ] + ) + } + + func testModuleSelectorIncorrectTypeDecls() { + assertParse( + """ + enum main1️⃣::decl2 { + case Swift2️⃣::decl2a + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::decl2' in enum" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '::decl2a' in enum" + ), + ] + ) + assertParse( + """ + struct main1️⃣::decl3 {} + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::decl3' in struct") + ] + ) + assertParse( + """ + class main1️⃣::decl4 {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::decl4' in class" + ) + ] + ) + assertParse( + """ + typealias main1️⃣::decl5 = Swift::Bool + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::decl5' in typealias declaration" + ) + ] + ) + assertParse( + """ + protocol main1️⃣::decl6 { + associatedtype Swift2️⃣::decl6a + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::decl6' in protocol" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '::decl6a' in protocol" + ), + ] + ) + } + + func testModuleSelectorIncorrectGlobalVarDecls() { + assertParse( + """ + let main1️⃣::decl7 = 7 + """, + diagnostics: [ + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + let main = <#identifier#>::decl7 = 7 + """ + ) + assertParse( + """ + var main1️⃣::decl8 = 8 { + willSet(Swift2️⃣::newValue) {} + didSet(Foo3️⃣::oldValue) {} + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '::newValue' in accessor" + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "unexpected code '::oldValue' in accessor" + ), + ], + fixedSource: """ + var main = <#identifier#>::decl8 = 8 { + willSet(Swift::newValue) {} + didSet(Foo::oldValue) {} + } + """ + ) + } + + func testModuleSelectorIncorrectNestedDecls() { + assertParse( + """ + struct Parent { + func main1️⃣::decl1() {} + } + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::decl1' before parameter clause") + ] + ) + assertParse( + """ + struct Parent { + enum main1️⃣::decl2 { + case Swift2️⃣::decl2a + } + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::decl2' in enum" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '::decl2a' in enum" + ), + ] + ) + assertParse( + """ + struct Parent { + struct main1️⃣::decl3 {} + } + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::decl3' in struct") + ] + ) + assertParse( + """ + struct Parent { + class main1️⃣::decl4 {} + } + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::decl4' in class") + ] + ) + assertParse( + """ + struct Parent { + typealias main1️⃣::decl5 = Swift::Bool + } + """, + diagnostics: [ + DiagnosticSpec( + message: "unexpected code '::decl5' in typealias declaration" + ) + ] + ) + } + + func testModuleSelectorMacroDecls() { + assertParse( + """ + struct CreatesDeclExpectation { + #main::myMacro() + } + """, + substructure: MacroExpansionDeclSyntax( + moduleSelector: ModuleSelectorSyntax(moduleName: "main"), + macroName: "myMacro", + leftParen: .leftParenToken(), + arguments: [], + rightParen: .rightParenToken() + ) + ) + } + + func testModuleSelectorIncorrectRuntimeBaseAttr() { + // Should be diagnosed in ASTGen: + assertParse( + """ + @_swift_native_objc_runtime_base(1️⃣main::BaseClass) + class C1 {} + """, + substructure: AttributeSyntax( + attributeName: makeType(name: "_swift_native_objc_runtime_base"), + leftParen: .leftParenToken(), + arguments: .argumentList( + [ + LabeledExprSyntax(expression: makeDeclRef(moduleSelector: "main", baseName: "BaseClass")) + ] + ), + rightParen: .rightParenToken() + ) + ) + } + + func testModuleSelectorOperatorDecls() { + assertParse( + """ + infix operator <<<<< : Swift1️⃣::AdditionPrecedence + """, + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + infix operator <<<<< : Swift + <#identifier#>::AdditionPrecedence + """ + ) + assertParse( + """ + precedencegroup main1️⃣::PG1 { + higherThan: Swift2️⃣::AdditionPrecedence + } + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '::PG1' in precedencegroup" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '::AdditionPrecedence' in precedencegroup" + ), + ] + ) + } + + func testModuleSelectorIllFormedModuleNames() { + assertParse( + """ + var a: 1️⃣::Int + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + var a: <#identifier#>::Int + """ + ) + assertParse( + """ + var b: (1️⃣::Int) + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + var b: (<#identifier#>::Int) + """ + ) + assertParse( + """ + var c: 1️⃣*::Int + """, + diagnostics: [ + DiagnosticSpec(message: "expected type in type annotation", fixIts: ["insert type"]), + DiagnosticSpec(message: "unexpected code '*::Int' in source file"), + ], + fixedSource: """ + var c: <#type#>*::Int + """ + ) + assertParse( + """ + var d: 1️⃣_::Int + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + assertParse( + """ + var e: 1️⃣Self::Int + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var e: `Self`::Int + """ + ) + assertParse( + """ + var f: 1️⃣self::Int + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var f: `self`::Int + """ + ) + assertParse( + """ + var g: 1️⃣inout2️⃣::Int + """, + diagnostics: [ + DiagnosticSpec( + message: "expected type in type annotation", + fixIts: ["insert type"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected pattern in variable", + fixIts: ["insert pattern"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + var g: <#type#>inout<#pattern#> = <#identifier#>::Int + """ + ) + assertParse( + """ + var h: 1️⃣Any::Int + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Any' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var h: `Any`::Int + """ + ) + assertParse( + """ + var aArray: [1️⃣::Int] + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + var aArray: [<#identifier#>::Int] + """ + ) + assertParse( + """ + var bArray: [(1️⃣::Int)] + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + var bArray: [(<#identifier#>::Int)] + """ + ) + assertParse( + """ + var cArray: [1️⃣*::Int] + """, + diagnostics: [ + DiagnosticSpec(message: "expected type in array type", fixIts: ["insert type"]), + DiagnosticSpec(message: "unexpected code '*::Int' in array type"), + ], + fixedSource: """ + var cArray: [<#type#>*::Int] + """ + ) + assertParse( + """ + var dArray: [1️⃣_::Int] + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + assertParse( + """ + var eArray: [1️⃣Self::Int] + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var eArray: [`Self`::Int] + """ + ) + assertParse( + """ + var fArray: [1️⃣self::Int] + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var fArray: [`self`::Int] + """ + ) + assertParse( + """ + var gArray: [1️⃣inout2️⃣::Int3️⃣] + """, + diagnostics: [ + DiagnosticSpec( + message: "expected type and ']' to end array type", + fixIts: ["insert type and ']'"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected pattern in variable", + fixIts: ["insert pattern"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "unexpected code ']' in source file" + ), + ], + fixedSource: """ + var gArray: [<#type#>]inout<#pattern#> = <#identifier#>::Int] + """ + ) + assertParse( + """ + var hArray: [1️⃣Any::Int] + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Any' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var hArray: [`Any`::Int] + """ + ) + assertParse( + """ + var aIndex: String.1️⃣::Index + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + var aIndex: String.<#identifier#>::Index + """ + ) + // FIXME: This gets interpreted as a single `.*` operator; may not be ideal. + assertParse( + """ + var cIndex: String1️⃣.*::Index + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '.*::Index' in source file") + ] + ) + assertParse( + """ + var dIndex: String.1️⃣_::Index + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + assertParse( + """ + var eIndex: String.1️⃣Self::Index + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var eIndex: String.`Self`::Index + """ + ) + assertParse( + """ + var fIndex: String.1️⃣self::Index + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var fIndex: String.`self`::Index + """ + ) + assertParse( + """ + var gIndex: String.inout1️⃣::Index + """, + diagnostics: [ + DiagnosticSpec( + message: "expected '=' in variable", + fixIts: ["insert '='"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + var gIndex: String.inout = <#identifier#>::Index + """ + ) + assertParse( + """ + var hIndex: String.1️⃣Any::Index + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Any' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + var hIndex: String.`Any`::Index + """ + ) + assertParse( + """ + func inExpr() { + 1️⃣::print() + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + func inExpr() { + <#identifier#>::print() + } + """ + ) + assertParse( + """ + func inExpr() { + (1️⃣::print()) + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + func inExpr() { + (<#identifier#>::print()) + } + """ + ) + assertParse( + """ + func inExpr() { + 1️⃣*::print() + } + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '*::print()' in function") + ] + ) + assertParse( + """ + func inExpr() { + 1️⃣_::print() + } + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + assertParse( + """ + func inExpr() { + 1️⃣Self::print() + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + `Self`::print() + } + """ + ) + assertParse( + """ + func inExpr() { + 1️⃣self::print() + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + `self`::print() + } + """ + ) + assertParse( + """ + func inExpr() { + inout1️⃣::print() + } + """, + diagnostics: [ + DiagnosticSpec(message: "expected pattern in variable", fixIts: ["insert pattern"]), + DiagnosticSpec(message: "expected '=' in variable", fixIts: ["insert '='"]), + DiagnosticSpec(message: "expected module name in module selector", fixIts: ["insert module name"]), + ], + fixedSource: """ + func inExpr() { + inout<#pattern#> = <#identifier#>::print() + } + """ + ) + assertParse( + """ + func inExpr() { + 1️⃣Any::print() + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Any' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + `Any`::print() + } + """ + ) + assertParse( + """ + func inExpr() { + _ = 1.1️⃣::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + func inExpr() { + _ = 1.<#identifier#>::magnitude + } + """ + ) + assertParse( + """ + func inExpr() { + _ = (1.1️⃣::magnitude) + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ) + ], + fixedSource: """ + func inExpr() { + _ = (1.<#identifier#>::magnitude) + } + """ + ) + // FIXME: This gets interpreted as a single `.*` operator; may not be ideal. + assertParse( + """ + func inExpr() { + _ = 1.*1️⃣::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + func inExpr() { + _ = 1.* + <#identifier#>::magnitude + } + """ + ) + assertParse( + """ + func inExpr() { + _ = 1.1️⃣_::magnitude + } + """, + diagnostics: [ + DiagnosticSpec(message: "'_' cannot be used as an identifier here") + ] + ) + assertParse( + """ + func inExpr() { + _ = 1.1️⃣Self::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + _ = 1.`Self`::magnitude + } + """ + ) + assertParse( + """ + func inExpr() { + _ = 1.1️⃣self::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'self' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + _ = 1.`self`::magnitude + } + """ + ) + assertParse( + """ + func inExpr() { + _ = 1.inout1️⃣::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + message: "expected module name in module selector", + fixIts: ["insert module name"] + ), + ], + fixedSource: """ + func inExpr() { + _ = 1.inout + <#identifier#>::magnitude + } + """ + ) + assertParse( + """ + func inExpr() { + _ = 1.1️⃣Any::magnitude + } + """, + diagnostics: [ + DiagnosticSpec( + message: "keyword 'Any' cannot be used as an identifier here", + fixIts: ["if this name is unavoidable, use backticks to escape it"] + ) + ], + fixedSource: """ + func inExpr() { + _ = 1.`Any`::magnitude + } + """ + ) + } + + func testModuleSelectorAttrs() { + // 'main::Private' should be diagnosed in ASTGen + assertParse( + """ + @_spi(main::Private) + public struct BadImplementsAttr: CustomStringConvertible {} + """, + substructure: AttributeSyntax( + attributeName: makeType(name: "_spi"), + leftParen: .leftParenToken(), + arguments: .argumentList( + [ + LabeledExprSyntax(expression: makeDeclRef(moduleSelector: "main", baseName: "Private")) + ] + ), + rightParen: .rightParenToken() + ) + ) + // 'Swift::description' should be diagnosed in ASTGen + assertParse( + """ + @_implements(main::CustomStringConvertible, Swift::description) + public var stringValue: String { fatalError() } + """, + substructure: AttributeSyntax( + attributeName: makeType(name: "_implements"), + leftParen: .leftParenToken(), + arguments: .implementsArguments( + ImplementsAttributeArgumentsSyntax( + type: makeType(moduleSelector: "main", name: "CustomStringConvertible"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "description") + ) + ), + rightParen: .rightParenToken() + ) + ) + assertParse( + """ + @_specialize(target: main::fn(), spi: Swift1️⃣::Private, where T == Swift::Int) + public func fn() -> T { fatalError() } + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::Private, where T == Swift::Int' in attribute") + ] + ) + assertParse( + """ + func fn(_: @isolated(Swift1️⃣::any) () -> Void) {} + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::any' in attribute") + ] + ) + assertParse( + """ + @_documentation(metadata: Swift1️⃣::GroupName) + func fn() {} + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code '::GroupName' in attribute") + ] + ) + assertParse( + """ + @derivative(of: Swift::Foo.Swift::Bar.Swift::baz(), wrt: quux) + func fn() {} + """, + substructure: AttributeSyntax( + attributeName: makeType(name: "derivative"), + leftParen: .leftParenToken(), + arguments: .derivativeRegistrationArguments( + DerivativeAttributeArgumentsSyntax( + originalDeclName: MemberAccessExprSyntax( + base: TypeExprSyntax( + type: makeMember( + of: makeType(moduleSelector: "Swift", name: "Foo"), + moduleSelector: "Swift", + name: "Bar" + ) + ), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "baz", argumentLabels: []) + ), + comma: .commaToken(), + arguments: DifferentiabilityWithRespectToArgumentSyntax( + arguments: .argument( + DifferentiabilityArgumentSyntax(argument: "quux") + ) + ) + ) + ), + rightParen: .rightParenToken() + ) + ) + } + + func testModuleSelectorExpr() { + assertParse( + "let x = Swift::1️⃣do { 1 }", + substructure: FunctionCallExprSyntax( + calledExpression: makeDeclRef(moduleSelector: "Swift", baseName: "do"), + leftParen: nil, + arguments: [], + rightParen: nil, + trailingClosure: ClosureExprSyntax { + CodeBlockItemSyntax(item: .expr(ExprSyntax(IntegerLiteralExprSyntax(integerLiteral: 1)))) + } + ), + experimentalFeatures: [.doExpressions] + ) + assertParse( + """ + let x = Swift::1️⃣ + do { 1 } + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier in variable", fixIts: ["insert identifier"]) + ], + fixedSource: """ + let x = Swift::<#identifier#> + do { 1 } + """, + experimentalFeatures: [.doExpressions] + ) + assertParse( + "let x = Swift::if1️⃣ y { 1 } 2️⃣else { 0 }", + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code 'else { 0 }' in source file" + ), + ], + fixedSource: """ + let x = Swift::if + y { 1 } else { 0 } + """ + ) + assertParse( + """ + let x = Swift::1️⃣ + if y { 1 } else { 0 } + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier in variable", fixIts: ["insert identifier"]) + ], + fixedSource: """ + let x = Swift::<#identifier#> + if y { 1 } else { 0 } + """ + ) + assertParse( + """ + let x = Swift::switch1️⃣ y2️⃣ { + 3️⃣case true: 1 + 4️⃣case false: 0 + } + """, + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: ["insert newline", "insert ';'"] + ), + DiagnosticSpec( + locationMarker: "3️⃣", + message: "'case' can only appear inside a 'switch' statement or 'enum' declaration" + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "'case' can only appear inside a 'switch' statement or 'enum' declaration" + ), + ], + fixedSource: """ + let x = Swift::switch + y + { + case true: 1 + case false: 0 + } + """ + ) + assertParse( + """ + let x = Swift::1️⃣ + switch y { + case true: 1 + case false: 0 + } + """, + diagnostics: [ + DiagnosticSpec(message: "expected identifier in variable", fixIts: ["insert identifier"]) + ], + fixedSource: """ + let x = Swift::<#identifier#> + switch y { + case true: 1 + case false: 0 + } + """ + ) + assertParse( + "fn(Swift::1️⃣&x)", + diagnostics: [ + DiagnosticSpec( + message: "expected identifier in function call", + fixIts: ["insert identifier"] + ), + DiagnosticSpec(message: "unexpected code '&x' in function call"), + ], + fixedSource: "fn(Swift::<#identifier#>&x)" + ) + assertParse( + #"_ = Swift::1️⃣\main::Foo.BarKit::bar"#, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: #"_ = Swift::<#identifier#>\main::Foo.BarKit::bar"# + ) + assertParse( + #"_ = \main::Foo.BarKit::bar"#, + substructure: KeyPathExprSyntax( + root: makeType(moduleSelector: "main", name: "Foo"), + components: [ + KeyPathComponentSyntax( + period: .periodToken(), + component: .property( + KeyPathPropertyComponentSyntax( + declName: makeDeclRef(moduleSelector: "BarKit", baseName: "bar") + ) + ) + ) + ] + ) + ) + assertParse( + "_ = Swift::1️⃣-x", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>-x" + ) + assertParse( + "_ = Swift::1️⃣1", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>1" + ) + assertParse( + "_ = Swift::1️⃣1.0", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>1.0" + ) + assertParse( + #"_ = Swift::1️⃣@"fnord""#, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]), + DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"]), + ], + fixedSource: #"_ = Swift::<#identifier#>"fnord""# + ) + assertParse( + #"_ = Swift::1️⃣"fnord""#, + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: #"_ = Swift::<#identifier#>"fnord""# + ) + assertParse( + "_ = Swift::1️⃣/fnord/", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>/fnord/" + ) + assertParse( + "_ = Swift::nil", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "nil") + ) + assertParse( + "_ = Swift::1️⃣true", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "true") + ) + assertParse( + "_ = Swift::identifier", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "identifier") + ) + assertParse( + "_ = Swift::self", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "self") + ) + assertParse( + "_ = Swift::init", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: .keyword(.`init`)) + ) + assertParse( + "@attached(extension, names: 1️⃣Swift::deinit) macro m()", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: .keyword(.`deinit`)), + substructureAfterMarker: "1️⃣" + ) + assertParse( + "@attached(extension, names: 1️⃣Swift::subscript) macro m()", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: .keyword(.subscript)), + substructureAfterMarker: "1️⃣" + ) + assertParse( + "_ = Swift::Self", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "Self") + ) + assertParse( + "_ = Swift::Any", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "Any") + ) + assertParse( + "_ = Swift::1️⃣$0", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>$0" + ) + assertParse( + "_ = Swift::$foo", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: .identifier("$foo")) + ) + assertParse( + "_ = Swift::1️⃣_", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>_" + ) + assertParse( + "Swift::1️⃣_ = 1", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "Swift::<#identifier#>_ = 1" + ) + assertParse( + "_ = Swift::1️⃣#foo", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>#foo" + ) + assertParse( + "_ = #Swift::foo", + substructure: MacroExpansionExprSyntax( + moduleSelector: ModuleSelectorSyntax(moduleName: "Swift"), + macroName: "foo", + arguments: [] + ) + ) + assertParse( + "_ = Swift::1️⃣{ 1 }", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in function call", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#> { 1 }" + ) + assertParse( + "_ = Swift::1️⃣.random()", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in member access", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>.random()" + ) + assertParse( + "_ = Swift::1️⃣.main::random()", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in member access", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>.main::random()" + ) + assertParse( + "_ = .main::random()", + substructure: MemberAccessExprSyntax( + declName: makeDeclRef(moduleSelector: "main", baseName: "random") + ) + ) + assertParse( + "_ = Swift::super.foo()", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: "super") + ) + assertParse( + "_ = Swift::1️⃣(a, b)", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in function call", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>(a, b)" + ) + assertParse( + "_ = Swift::1️⃣[a, b]", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in subscript", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>[a, b]" + ) + assertParse( + "_ = Swift::1️⃣", + diagnostics: [ + DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = Swift::<#identifier#>" + ) + + assertParse( + "_ = x.Swift::y", + substructure: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "x"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "y") + ) + ) + assertParse( + "_ = x.Swift::1️⃣1", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in member access", fixIts: ["insert identifier"]) + ], + fixedSource: "_ = x.Swift::<#identifier#>1" + ) + assertParse( + "_ = x.Swift::self", + substructure: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "x"), + declName: makeDeclRef( + moduleSelector: "Swift", + baseName: "self" + ) + ) + ) + assertParse( + "_ = x.Swift::Self.self", + substructure: MemberAccessExprSyntax( + base: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "x"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "Self") + ), + declName: makeDeclRef(baseName: .keyword(.`self`)) + ) + ) + assertParse( + "_ = x.Swift::Type.self", + substructure: MemberAccessExprSyntax( + base: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "x"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "Type") + ), + declName: makeDeclRef(baseName: .keyword(.`self`)) + ) + ) + assertParse( + "_ = x.Swift::Protocol.self", + substructure: MemberAccessExprSyntax( + base: MemberAccessExprSyntax( + base: makeDeclRef(baseName: "x"), + declName: makeDeclRef(moduleSelector: "Swift", baseName: "Protocol") + ), + declName: makeDeclRef(baseName: .keyword(.`self`)) + ) + ) + assertParse( + "_ = myArray.reduce(0, 1️⃣Swift::+)", + substructure: makeDeclRef(moduleSelector: "Swift", baseName: .binaryOperator("+")), + substructureAfterMarker: "1️⃣" + ) + } + + func testModuleSelectorStmt() { + assertParse( + "if Swift::1️⃣#available(macOS 15, *) {}", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in 'if' statement", fixIts: ["insert identifier"]), + DiagnosticSpec(message: "unexpected code '#available(macOS 15, *)' in 'if' statement"), + ], + fixedSource: "if Swift::<#identifier#> #available(macOS 15, *) {}" + ) + } + + func testModuleSelectorType() { + assertParse( + "func fn(_: Swift::Self) {}", + substructure: makeType(moduleSelector: "Swift", name: "Self") + ) + assertParse( + "func fn(_: Swift::Any) {}", + substructure: makeType(moduleSelector: "Swift", name: "Any") + ) + assertParse( + "func fn(_: Swift::Foo) {}", + substructure: makeType(moduleSelector: "Swift", name: "Foo") + ) + assertParse( + "func fn(_: Swift::1️⃣(Int, String)) {}", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in type", fixIts: ["insert identifier"]), + DiagnosticSpec(message: "unexpected code '(Int, String)' in parameter clause"), + ], + fixedSource: "func fn(_: Swift::<#identifier#>(Int, String)) {}" + ) + assertParse( + "func fn(_: Swift::1️⃣[Int]) {}", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in type", fixIts: ["insert identifier"]), + DiagnosticSpec(message: "unexpected code '[Int]' in parameter clause"), + ], + fixedSource: "func fn(_: Swift::<#identifier#>[Int]) {}" + ) + assertParse( + "func fn(_: Swift::1️⃣_) {}", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in type", fixIts: ["insert identifier"]), + DiagnosticSpec(message: "unexpected code '_' in parameter clause"), + ], + fixedSource: "func fn(_: Swift::<#identifier#>_) {}" + ) + assertParse( + "func fn(_: Swift::1️⃣) {}", + diagnostics: [ + DiagnosticSpec(message: "expected identifier in type", fixIts: ["insert identifier"]) + ], + fixedSource: "func fn(_: Swift::<#identifier#>) {}" + ) + // TODO: Add equivalent test case for legacy parser + assertParse( + "func fn(_: Foo.1️⃣Swift::Type) {}", + substructure: makeMember( + of: makeType(name: "Foo"), + moduleSelector: "Swift", + name: "Type" + ) + ) + // TODO: Add equivalent test case for legacy parser + assertParse( + "func fn(_: Foo.1️⃣Swift::Protocol) {}", + substructure: makeMember( + of: makeType(name: "Foo"), + moduleSelector: "Swift", + name: "Protocol" + ) + ) + assertParse( + "func fn(_: Foo.Swift::Bar) {}", + substructure: makeMember( + of: makeType(name: "Foo"), + moduleSelector: "Swift", + name: "Bar" + ) + ) + assertParse( + "func fn(_: Foo.Swift::self) {}", + substructure: makeMember( + of: makeType(name: "Foo"), + moduleSelector: "Swift", + name: "self" + ) + ) + } + + func testModuleSelectorSubmodule() { + assertParse( + "_ = Foundation::1️⃣NSData::NSData()", + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'NSData::' in module selector") + ] + ) + + assertParse( + "_ = Foundation::1️⃣NSData::Fnord::NSData()", + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'NSData::Fnord::' in module selector") + ] + ) + + assertParse( + """ + _ = Foundation::1️⃣NSData::2️⃣ + Fnord::NSData() + """, + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'NSData::' in module selector"), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected identifier", + fixIts: ["insert identifier"] + ), + ], + fixedSource: """ + _ = Foundation::NSData::<#identifier#> + Fnord::NSData() + """ + ) + } +} + +// MARK: - Syntax tree construction helpers + +private func makeType(moduleSelector: TokenSyntax? = nil, name: TokenSyntax) -> IdentifierTypeSyntax { + return IdentifierTypeSyntax( + moduleSelector: moduleSelector.map { ModuleSelectorSyntax(moduleName: $0) }, + name: name + ) +} + +private func makeDeclRef( + moduleSelector: TokenSyntax? = nil, + baseName: TokenSyntax, + argumentLabels: [TokenSyntax?]? = nil +) -> DeclReferenceExprSyntax { + return DeclReferenceExprSyntax( + moduleSelector: moduleSelector.map { ModuleSelectorSyntax(moduleName: $0) }, + baseName: baseName, + argumentNames: argumentLabels.map { argumentLabels in + DeclNameArgumentsSyntax( + arguments: DeclNameArgumentListSyntax( + argumentLabels.map { DeclNameArgumentSyntax(name: $0 ?? .wildcardToken()) } + ) + ) + } + ) +} + +private func makeMember( + of base: some TypeSyntaxProtocol, + moduleSelector: TokenSyntax? = nil, + name: TokenSyntax +) -> MemberTypeSyntax { + return MemberTypeSyntax( + baseType: base, + moduleSelector: moduleSelector.map { ModuleSelectorSyntax(moduleName: $0) }, + name: name + ) +} + +private func makeCall( + callee: some ExprSyntaxProtocol, + arguments: KeyValuePairs +) -> FunctionCallExprSyntax { + return FunctionCallExprSyntax( + calledExpression: callee, + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax( + arguments.map { labelAndExpr in + LabeledExprSyntax( + label: labelAndExpr.0, + colon: labelAndExpr.0 == nil ? nil : .colonToken(), + expression: labelAndExpr.1 + ) + } + ), + rightParen: .rightParenToken() + ) +} diff --git a/Tests/SwiftParserTest/translated/MultilineErrorsTests.swift b/Tests/SwiftParserTest/translated/MultilineErrorsTests.swift index e9f57e14bac..56e6ffea3d7 100644 --- a/Tests/SwiftParserTest/translated/MultilineErrorsTests.swift +++ b/Tests/SwiftParserTest/translated/MultilineErrorsTests.swift @@ -563,7 +563,7 @@ final class MultilineErrorsTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: #"extraneous code ')!"' at top level"# + message: #"unexpected code ')!"' in source file"# ), ], fixedSource: ##""" @@ -597,7 +597,7 @@ final class MultilineErrorsTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: #"extraneous code ')!"' at top level"# + message: #"unexpected code ')!"' in source file"# ), ], fixedSource: ##""" diff --git a/Tests/SwiftParserTest/translated/OperatorDeclTests.swift b/Tests/SwiftParserTest/translated/OperatorDeclTests.swift index a8e79ba960f..2ddb23d46b1 100644 --- a/Tests/SwiftParserTest/translated/OperatorDeclTests.swift +++ b/Tests/SwiftParserTest/translated/OperatorDeclTests.swift @@ -214,7 +214,7 @@ final class OperatorDeclTests: ParserTestCase { prefix operator %%+ """, diagnostics: [ - DiagnosticSpec(message: "unexpected ';' separator", fixIts: ["remove ';'"]) + DiagnosticSpec(message: "standalone ';' statements are not allowed", fixIts: ["remove ';'"]) ], fixedSource: """ diff --git a/Tests/SwiftParserTest/translated/OptionalTests.swift b/Tests/SwiftParserTest/translated/OptionalTests.swift index f29ee9511b3..542021fb26d 100644 --- a/Tests/SwiftParserTest/translated/OptionalTests.swift +++ b/Tests/SwiftParserTest/translated/OptionalTests.swift @@ -33,7 +33,7 @@ final class OptionalTests: ParserTestCase { var b : A 1️⃣? """, diagnostics: [ - DiagnosticSpec(message: "extraneous code '?' at top level") + DiagnosticSpec(message: "unexpected code '?' in source file") ] ) } diff --git a/Tests/SwiftParserTest/translated/PoundAssertTests.swift b/Tests/SwiftParserTest/translated/PoundAssertTests.swift index f2b57c8b173..4ee9fa6a2fc 100644 --- a/Tests/SwiftParserTest/translated/PoundAssertTests.swift +++ b/Tests/SwiftParserTest/translated/PoundAssertTests.swift @@ -41,7 +41,7 @@ final class PoundAssertTests: ParserTestCase { message: "consecutive statements on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"] ), - DiagnosticSpec(locationMarker: "2️⃣", message: #"extraneous code ', "error message")' at top level"#), + DiagnosticSpec(locationMarker: "2️⃣", message: #"unexpected code ', "error message")' in source file"#), ], applyFixIts: ["insert newline"], fixedSource: #""" @@ -61,7 +61,7 @@ final class PoundAssertTests: ParserTestCase { message: "consecutive statements on a line must be separated by newline or ';'", fixIts: ["insert newline", "insert ';'"] ), - DiagnosticSpec(locationMarker: "2️⃣", message: #"extraneous code ', "error message")' at top level"#), + DiagnosticSpec(locationMarker: "2️⃣", message: #"unexpected code ', "error message")' in source file"#), ], applyFixIts: ["insert ';'"], fixedSource: #""" diff --git a/Tests/SwiftParserTest/translated/RecoveryLibraryTests.swift b/Tests/SwiftParserTest/translated/RecoveryLibraryTests.swift index a6f511902ab..e9311734db4 100644 --- a/Tests/SwiftParserTest/translated/RecoveryLibraryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryLibraryTests.swift @@ -35,15 +35,15 @@ final class RecoveryLibraryTests: ParserTestCase { diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "unexpected braces before function", + message: "unexpected braces in source file", highlight: """ // Check that we handle multiple consecutive right braces. } } """ ), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected braces before function"), - DiagnosticSpec(locationMarker: "3️⃣", message: "extraneous braces at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected braces in source file"), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected braces in source file"), ] ) } diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index aba6d85f9ea..4bb52f7411c 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -28,7 +28,7 @@ final class RecoveryTests: ParserTestCase { diagnostics: [ DiagnosticSpec( message: - "unexpected code ') this line is invalid, but we will stop at the keyword below...' before 'return' statement" + "unexpected code ') this line is invalid, but we will stop at the keyword below...' in function" ) ] ) @@ -45,7 +45,7 @@ final class RecoveryTests: ParserTestCase { """#, diagnostics: [ DiagnosticSpec( - message: "unexpected code ') this line is invalid, but we will stop at the declaration...' before function" + message: "unexpected code ') this line is invalid, but we will stop at the declaration...' in function" ) ] ) @@ -93,7 +93,7 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: "unexpected code in function" + message: "unexpected code ', b : Int' in function" ), ], applyFixIts: ["insert '>'", "insert expression"], @@ -140,7 +140,7 @@ final class RecoveryTests: ParserTestCase { diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "unexpected brace before function" + message: "unexpected brace in source file" ), DiagnosticSpec( locationMarker: "2️⃣", @@ -174,7 +174,7 @@ final class RecoveryTests: ParserTestCase { diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "unexpected brace before function" + message: "unexpected brace in source file" ), DiagnosticSpec( locationMarker: "2️⃣", @@ -975,14 +975,16 @@ final class RecoveryTests: ParserTestCase { func testRecovery54() { assertParse( """ - struct NoBracesStruct11️⃣() + struct NoBracesStruct11️⃣()2️⃣ """, diagnostics: [ - DiagnosticSpec(message: "expected member block in struct", fixIts: ["insert member block"]) + DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in struct", fixIts: ["insert '{'"]), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '()' in struct"), + DiagnosticSpec(locationMarker: "2️⃣", message: "expected '}' to end struct", fixIts: ["insert '}'"]), ], fixedSource: """ - struct NoBracesStruct1 { - }() + struct NoBracesStruct1 {() + } """ ) } @@ -993,33 +995,31 @@ final class RecoveryTests: ParserTestCase { enum NoBracesUnion11️⃣() class NoBracesClass12️⃣() protocol NoBracesProtocol13️⃣() - extension NoBracesStruct14️⃣() + extension NoBracesStruct14️⃣()5️⃣ """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in enum", fixIts: ["insert '{'"]), - DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '()' before class"), + DiagnosticSpec(locationMarker: "1️⃣", message: "unexpected code '()' in enum"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected '{' in class", fixIts: ["insert '{'"]), - DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '()' before protocol"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '()' in class"), DiagnosticSpec(locationMarker: "3️⃣", message: "expected '{' in protocol", fixIts: ["insert '{'"]), - DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code '()' before extension"), - DiagnosticSpec( - locationMarker: "4️⃣", - message: "expected member block in extension", - fixIts: ["insert member block"] - ), - DiagnosticSpec(locationMarker: "4️⃣", message: "expected '}' to end protocol", fixIts: ["insert '}'"]), - DiagnosticSpec(locationMarker: "4️⃣", message: "expected '}' to end class", fixIts: ["insert '}'"]), - DiagnosticSpec(locationMarker: "4️⃣", message: "expected '}' to end enum", fixIts: ["insert '}'"]), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code '()' in protocol"), + DiagnosticSpec(locationMarker: "4️⃣", message: "expected '{' in extension", fixIts: ["insert '{'"]), + DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code '()' in extension"), + DiagnosticSpec(locationMarker: "5️⃣", message: "expected '}' to end extension", fixIts: ["insert '}'"]), + DiagnosticSpec(locationMarker: "5️⃣", message: "expected '}' to end protocol", fixIts: ["insert '}'"]), + DiagnosticSpec(locationMarker: "5️⃣", message: "expected '}' to end class", fixIts: ["insert '}'"]), + DiagnosticSpec(locationMarker: "5️⃣", message: "expected '}' to end enum", fixIts: ["insert '}'"]), ], fixedSource: """ enum NoBracesUnion1 {() class NoBracesClass1 {() protocol NoBracesProtocol1 {() - extension NoBracesStruct1 { + extension NoBracesStruct1 {() + } } } } - }() """ ) } @@ -1112,7 +1112,7 @@ final class RecoveryTests: ParserTestCase { assertParse( """ enum EE 1️⃣EE where T : Multi { - case a2️⃣ 3️⃣a + case a 2️⃣a case b } """, @@ -1122,18 +1122,12 @@ final class RecoveryTests: ParserTestCase { message: "found an unexpected second identifier in enum; is there an accidental break?", fixIts: ["join the identifiers together"] ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "consecutive declarations on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'a' before enum case"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'a' in enum"), ], applyFixIts: ["join the identifiers together", "insert newline"], fixedSource: """ enum EEEE where T : Multi { - case a - a + case a a case b } """ @@ -1144,7 +1138,7 @@ final class RecoveryTests: ParserTestCase { assertParse( """ enum EE 1️⃣EE where T : Multi { - case a2️⃣ 3️⃣a + case a 2️⃣a case b } """, @@ -1154,17 +1148,11 @@ final class RecoveryTests: ParserTestCase { message: "found an unexpected second identifier in enum; is there an accidental break?", fixIts: ["join the identifiers together"] ), - DiagnosticSpec( - locationMarker: "2️⃣", - message: "consecutive declarations on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'a' before enum case"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code 'a' in enum"), ], - applyFixIts: ["join the identifiers together", "insert ';'"], fixedSource: """ enum EEEE where T : Multi { - case a;a + case a a case b } """ @@ -1733,7 +1721,7 @@ final class RecoveryTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec( - message: "extraneous code ']' at top level" + message: "unexpected code ']' in source file" ) ] ) @@ -1750,7 +1738,7 @@ final class RecoveryTests: ParserTestCase { notes: [NoteSpec(message: "to match this opening '<'")], fixIts: ["insert '>'"] ), - DiagnosticSpec(message: "extraneous code ']>' at top level"), + DiagnosticSpec(message: "unexpected code ']>' in source file"), ], fixedSource: """ let a2: Set]> @@ -1772,12 +1760,7 @@ final class RecoveryTests: ParserTestCase { diagnostics: [ DiagnosticSpec( locationMarker: "1️⃣", - message: "consecutive declarations on a line must be separated by newline or ';'", - fixIts: ["insert newline", "insert ';'"] - ), - DiagnosticSpec( - locationMarker: "1️⃣", - message: "unexpected code ':' before variable" + message: "unexpected code ':' in struct" ), DiagnosticSpec( locationMarker: "2️⃣", @@ -1812,8 +1795,7 @@ final class RecoveryTests: ParserTestCase { ], fixedSource: """ struct ErrorTypeInVarDeclDictionaryType { - let a1: String - : + let a1: String: let a2: [String: Int] let a3: [String: [Int]] let a4: [String: Int] @@ -1829,7 +1811,7 @@ final class RecoveryTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec( - message: "extraneous code ']' at top level" + message: "unexpected code ']' in source file" ) ] ) @@ -2076,7 +2058,7 @@ final class RecoveryTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(message: "unexpected code before typealias declaration") + DiagnosticSpec(message: "unexpected code in struct") ] ) } @@ -2430,7 +2412,7 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "2️⃣", - message: "unexpected code '> {}' in 'switch' statement" + message: "unexpected code '> {}' in switch case" ), ], fixedSource: """ @@ -2463,6 +2445,11 @@ final class RecoveryTests: ParserTestCase { locationMarker: "3️⃣", message: "unexpected code '[' in function" ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected 'in' in closure signature", + fixIts: ["insert 'in'"] + ), DiagnosticSpec( locationMarker: "4️⃣", message: "unexpected code ') -> Int {}' in closure" @@ -2471,7 +2458,7 @@ final class RecoveryTests: ParserTestCase { fixedSource: """ #if true struct Foo19605164 { - func a(s: S) [{{g) -> Int {} + func a(s: S) [{{g in) -> Int {} }}} #endif """ @@ -2502,7 +2489,7 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: "unexpected code ')}' before struct" + message: "unexpected code ')}' in source file" ), DiagnosticSpec( locationMarker: "5️⃣", @@ -2597,7 +2584,7 @@ final class RecoveryTests: ParserTestCase { } """, diagnostics: [ - DiagnosticSpec(message: "extraneous code at top level") + DiagnosticSpec(message: "unexpected code in source file") ] ) } @@ -2932,7 +2919,7 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: "extraneous brace at top level" + message: "unexpected brace in source file" ), ], applyFixIts: ["remove operator body", "insert newline"], @@ -2969,7 +2956,7 @@ final class RecoveryTests: ParserTestCase { ), DiagnosticSpec( locationMarker: "4️⃣", - message: "extraneous brace at top level" + message: "unexpected brace in source file" ), ], applyFixIts: ["remove operator body", "insert ';'"], @@ -3251,8 +3238,196 @@ final class RecoveryTests: ParserTestCase { assertParse( "func foo() -> Int1️⃣:", diagnostics: [ - DiagnosticSpec(message: "extraneous code ':' at top level") + DiagnosticSpec(message: "unexpected code ':' in source file") ] ) } + + func testStatementAfterAttribute() { + assertParse( + """ + @attr1️⃣ + guard foo else {} + struct S {} + """, + substructure: CodeBlockItemListSyntax([ + CodeBlockItemSyntax( + item: .decl( + DeclSyntax( + MissingDeclSyntax( + attributes: [ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: IdentifierTypeSyntax(name: .identifier("attr")) + ) + ) + ], + modifiers: [], + placeholder: .identifier("<#declaration#>", presence: .missing) + ) + ) + ) + ), + CodeBlockItemSyntax( + item: .init( + GuardStmtSyntax( + guardKeyword: .keyword(.guard), + conditions: ConditionElementListSyntax([ + ConditionElementSyntax( + condition: ConditionElementSyntax.Condition(DeclReferenceExprSyntax(baseName: .identifier("foo"))) + ) + ]), + elseKeyword: .keyword(.else), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: CodeBlockItemListSyntax([]), + rightBrace: .rightBraceToken() + ) + ) + ) + ), + CodeBlockItemSyntax( + item: .init( + StructDeclSyntax( + attributes: AttributeListSyntax([]), + modifiers: DeclModifierListSyntax([]), + structKeyword: .keyword(.struct), + name: .identifier("S"), + memberBlock: MemberBlockSyntax( + leftBrace: .leftBraceToken(), + members: MemberBlockItemListSyntax([]), + rightBrace: .rightBraceToken() + ) + ) + ) + ), + ]), + diagnostics: [ + DiagnosticSpec(message: "expected declaration after attribute", fixIts: ["insert declaration"]) + ], + fixedSource: """ + @attr <#declaration#> + guard foo else {} + struct S {} + """ + ) + } + + func testUnexpectedBeforeAttributeInMemberBlock() { + assertParse( + """ + struct S { + 1️⃣do {} + @attr func foo() + } + """, + substructure: StructDeclSyntax( + name: .identifier("S"), + memberBlock: MemberBlockSyntax( + members: [ + MemberBlockItemSyntax( + decl: UnexpectedCodeDeclSyntax( + unexpectedCode: UnexpectedNodesSyntax([ + TokenSyntax.keyword(.do), + TokenSyntax.leftBraceToken(), + TokenSyntax.rightBraceToken(), + ]) + ) + ), + MemberBlockItemSyntax( + decl: FunctionDeclSyntax( + attributes: [ + .attribute(AttributeSyntax(attributeName: IdentifierTypeSyntax(name: .identifier("attr")))) + ], + name: .identifier("foo"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax( + leftParen: .leftParenToken(), + parameters: [], + rightParen: .rightParenToken() + ) + ) + ) + ), + ] + ) + ), + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'do {}' in struct") + ] + ) + } + + func testAttrInCodeBlock() { + assertParse( + """ + func foo() { + @attr1️⃣ + } + struct S {} + """, + substructure: CodeBlockItemListSyntax([ + CodeBlockItemSyntax( + item: .init( + FunctionDeclSyntax( + funcKeyword: .keyword(.func), + name: .identifier("foo"), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax( + leftParen: .leftParenToken(), + parameters: FunctionParameterListSyntax([]), + rightParen: .rightParenToken() + ) + ), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: CodeBlockItemListSyntax([ + CodeBlockItemSyntax( + item: .init( + MissingDeclSyntax( + attributes: AttributeListSyntax([ + .attribute( + AttributeSyntax( + atSign: .atSignToken(), + attributeName: TypeSyntax(IdentifierTypeSyntax(name: .identifier("attr"))) + ) + ) + ]), + placeholder: .identifier("<#declaration#>", presence: .missing) + ) + ) + ) + ]), + rightBrace: .rightBraceToken() + ) + ) + ) + ), + CodeBlockItemSyntax( + item: CodeBlockItemSyntax.Item( + StructDeclSyntax( + structKeyword: .keyword(.struct), + name: .identifier("S"), + memberBlock: MemberBlockSyntax( + leftBrace: .leftBraceToken(), + members: MemberBlockItemListSyntax([]), + rightBrace: .rightBraceToken() + ) + ) + ) + ), + ]), + diagnostics: [ + // FIXME: expected *declaration* after attribute + DiagnosticSpec(message: "expected statements after attribute", fixIts: ["insert statements"]) + ], + fixedSource: """ + func foo() { + @attr <#declaration#> + } + struct S {} + """ + ) + } } diff --git a/Tests/SwiftParserTest/translated/StringLiteralEofTests.swift b/Tests/SwiftParserTest/translated/StringLiteralEofTests.swift index 00dc1127bb8..ae17fe465fd 100644 --- a/Tests/SwiftParserTest/translated/StringLiteralEofTests.swift +++ b/Tests/SwiftParserTest/translated/StringLiteralEofTests.swift @@ -243,4 +243,37 @@ final class StringLiteralEofTests: ParserTestCase { """## ) } + + func testSimpleMultilineStringLiteralWith() { + assertParse( + #""" + #sourceLocation1️⃣(file: 2️⃣"""3️⃣\(4️⃣"5️⃣ + """#, + diagnostics: [ + DiagnosticSpec(locationMarker: "3️⃣", message: "argument cannot be an interpolated string literal"), + DiagnosticSpec( + locationMarker: "4️⃣", + message: #"expected '"""' to end simple string literal"#, + notes: [NoteSpec(locationMarker: "2️⃣", message: #"to match this opening '"""'"#)], + fixIts: [#"insert '"""'"#] + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected ', line:' and line number in '#sourceLocation' arguments", + fixIts: ["insert ', line:' and line number"] + ), + DiagnosticSpec( + locationMarker: "4️⃣", + message: "expected ')' in '#sourceLocation' directive", + notes: [NoteSpec(locationMarker: "1️⃣", message: "to match this opening '('")], + fixIts: ["insert ')'"] + ), + DiagnosticSpec(locationMarker: "4️⃣", message: "extra tokens following the #sourceLocation directive"), + ], + fixedSource: #""" + #sourceLocation(file: """\( + """, line: <#integer literal#>)" + """# + ) + } } diff --git a/Tests/SwiftParserTest/translated/TypealiasTests.swift b/Tests/SwiftParserTest/translated/TypealiasTests.swift index 05e769667ac..32ebbb88e5a 100644 --- a/Tests/SwiftParserTest/translated/TypealiasTests.swift +++ b/Tests/SwiftParserTest/translated/TypealiasTests.swift @@ -168,7 +168,7 @@ final class TypealiasTests: ParserTestCase { message: "expected '=' in typealias declaration", fixIts: ["replace ':' with '='"] ), - DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code ', Float' at top level"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code ', Float' in source file"), ], fixedSource: """ typealias Recovery5 = Int, Float @@ -183,7 +183,7 @@ final class TypealiasTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec(message: "expected type in typealias declaration", fixIts: ["insert type"]), - DiagnosticSpec(message: "extraneous code '=' at top level"), + DiagnosticSpec(message: "unexpected code '=' in source file"), ], fixedSource: """ typealias Recovery6 = <#type#>= diff --git a/Tests/SwiftRefactorTest/CallToTrailingClosureTests.swift b/Tests/SwiftRefactorTest/CallToTrailingClosureTests.swift index 06cc7984da4..9f457e8edbc 100644 --- a/Tests/SwiftRefactorTest/CallToTrailingClosureTests.swift +++ b/Tests/SwiftRefactorTest/CallToTrailingClosureTests.swift @@ -249,7 +249,7 @@ final class CallToTrailingClosuresTest: XCTestCase { } } -fileprivate func assertRefactorCall( +private func assertRefactorCall( _ callExpr: ExprSyntax, startAtArgument: Int = 0, expected: ExprSyntax?, diff --git a/Tests/SwiftRefactorTest/ConvertComputedPropertyToStoredTest.swift b/Tests/SwiftRefactorTest/ConvertComputedPropertyToStoredTest.swift index 328675ec7be..e87913a565c 100644 --- a/Tests/SwiftRefactorTest/ConvertComputedPropertyToStoredTest.swift +++ b/Tests/SwiftRefactorTest/ConvertComputedPropertyToStoredTest.swift @@ -146,7 +146,7 @@ final class ConvertComputedPropertyToStoredTest: XCTestCase { } } -fileprivate func assertRefactorConvert( +private func assertRefactorConvert( _ callDecl: DeclSyntax, expected: DeclSyntax?, file: StaticString = #filePath, diff --git a/Tests/SwiftRefactorTest/ConvertComputedPropertyToZeroParameterFunctionTests.swift b/Tests/SwiftRefactorTest/ConvertComputedPropertyToZeroParameterFunctionTests.swift index c75c4784b87..20a823c0b65 100644 --- a/Tests/SwiftRefactorTest/ConvertComputedPropertyToZeroParameterFunctionTests.swift +++ b/Tests/SwiftRefactorTest/ConvertComputedPropertyToZeroParameterFunctionTests.swift @@ -301,7 +301,7 @@ final class ConvertComputedPropertyToZeroParameterFunctionTests: XCTestCase { } } -fileprivate func assertRefactorConvert( +private func assertRefactorConvert( _ callDecl: DeclSyntax, expected: DeclSyntax?, file: StaticString = #filePath, diff --git a/Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift b/Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift index 1127b037b33..c948dbbc3c7 100644 --- a/Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift +++ b/Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift @@ -172,7 +172,7 @@ final class ConvertStoredPropertyToComputedTest: XCTestCase { } } -fileprivate func assertRefactorConvert( +private func assertRefactorConvert( _ callDecl: DeclSyntax, expected: DeclSyntax?, file: StaticString = #filePath, diff --git a/Tests/SwiftRefactorTest/ConvertZeroParameterFunctionToComputedPropertyTests.swift b/Tests/SwiftRefactorTest/ConvertZeroParameterFunctionToComputedPropertyTests.swift index a55e8610849..997236c6d9c 100644 --- a/Tests/SwiftRefactorTest/ConvertZeroParameterFunctionToComputedPropertyTests.swift +++ b/Tests/SwiftRefactorTest/ConvertZeroParameterFunctionToComputedPropertyTests.swift @@ -140,7 +140,7 @@ final class ConvertZeroParameterFunctionToComputedPropertyTests: XCTestCase { } } -fileprivate func assertRefactorConvert( +private func assertRefactorConvert( _ callDecl: DeclSyntax, expected: DeclSyntax?, file: StaticString = #filePath, diff --git a/Tests/SwiftRefactorTest/ExpandEditorPlaceholderTests.swift b/Tests/SwiftRefactorTest/ExpandEditorPlaceholderTests.swift index 859963a737c..33fe96cd399 100644 --- a/Tests/SwiftRefactorTest/ExpandEditorPlaceholderTests.swift +++ b/Tests/SwiftRefactorTest/ExpandEditorPlaceholderTests.swift @@ -18,16 +18,16 @@ import SwiftSyntaxBuilder import XCTest import _SwiftSyntaxTestSupport -fileprivate let closurePlaceholder = wrapInPlaceholder("T##closure##() -> Void") -fileprivate let closureWithArgPlaceholder = wrapInPlaceholder( +private let closurePlaceholder = wrapInPlaceholder("T##closure##() -> Void") +private let closureWithArgPlaceholder = wrapInPlaceholder( "T##(Int) -> String##(Int) -> String##(_ someInt: Int) -> String" ) -fileprivate let closureCombinedTypeDisplayPlaceholder = wrapInPlaceholder( +private let closureCombinedTypeDisplayPlaceholder = wrapInPlaceholder( "T##(Int) -> String" ) -fileprivate let voidPlaceholder = wrapInPlaceholder("T##code##Void") -fileprivate let intPlaceholder = wrapInPlaceholder("T##Int##Int") -fileprivate let stringPlaceholder = wrapInPlaceholder("T##String##String") +private let voidPlaceholder = wrapInPlaceholder("T##code##Void") +private let intPlaceholder = wrapInPlaceholder("T##Int##Int") +private let stringPlaceholder = wrapInPlaceholder("T##String##String") final class ExpandEditorPlaceholderTests: XCTestCase { func testSimple() throws { @@ -451,9 +451,61 @@ final class ExpandEditorPlaceholderTests: XCTestCase { format: .testCustom() ) } + + func testMacroTrailingClosureExpansion1() throws { + try assertRefactorPlaceholderToken( + "#foo(\(closurePlaceholder), \(intPlaceholder))", + expected: """ + { + \(voidPlaceholder) + } + """ + ) + } + + func testMacroTrailingClosureExpansion2() throws { + let call = "#foo(fn: \(closureWithArgPlaceholder))" + let expanded = """ + #foo { someInt in + \(stringPlaceholder) + } + """ + + try assertRefactorPlaceholderCall(call, expected: expanded) + try assertExpandEditorPlaceholdersToClosures(call, expected: expanded) + } + + func testMacroTrailingClosureExpansion3() throws { + let call = "#foo(fn1: \(closurePlaceholder), fn2: \(closureWithArgPlaceholder))" + let expanded = """ + #foo { + \(voidPlaceholder) + } fn2: { someInt in + \(stringPlaceholder) + } + """ + + try assertRefactorPlaceholderCall(call, expected: expanded) + try assertExpandEditorPlaceholdersToClosures(call, expected: expanded) + } + + func testMacroTrailingClosureExpansion4() throws { + try assertExpandEditorPlaceholdersToClosures( + decl: """ + #foo(fn1: \(closurePlaceholder), fn2: \(closurePlaceholder)) + """, + expected: """ + #foo { + \(voidPlaceholder) + } fn2: { + \(voidPlaceholder) + } + """ + ) + } } -fileprivate func assertRefactorPlaceholder( +private func assertRefactorPlaceholder( _ placeholder: String, wrap: Bool = true, expected: String, @@ -480,7 +532,7 @@ fileprivate func assertRefactorPlaceholder( ) } -fileprivate func assertRefactorPlaceholderCall( +private func assertRefactorPlaceholderCall( _ expr: String, placeholder: Int = 0, expected: String, @@ -489,7 +541,7 @@ fileprivate func assertRefactorPlaceholderCall( line: UInt = #line ) throws { var parser = Parser(expr) - let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).as(FunctionCallExprSyntax.self), file: file, line: line) + let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).asProtocol(CallLikeSyntax.self), file: file, line: line) let arg = call.arguments[call.arguments.index(at: placeholder)] let token: TokenSyntax = try XCTUnwrap(arg.expression.as(DeclReferenceExprSyntax.self), file: file, line: line) .baseName @@ -504,7 +556,7 @@ fileprivate func assertRefactorPlaceholderCall( ) } -fileprivate func assertRefactorPlaceholderToken( +private func assertRefactorPlaceholderToken( _ expr: String, placeholder: Int = 0, expected: String, @@ -513,7 +565,7 @@ fileprivate func assertRefactorPlaceholderToken( line: UInt = #line ) throws { var parser = Parser(expr) - let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).as(FunctionCallExprSyntax.self), file: file, line: line) + let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).asProtocol(CallLikeSyntax.self), file: file, line: line) let arg = call.arguments[call.arguments.index(at: placeholder)] let token: TokenSyntax = try XCTUnwrap(arg.expression.as(DeclReferenceExprSyntax.self), file: file, line: line) .baseName @@ -528,16 +580,13 @@ fileprivate func assertRefactorPlaceholderToken( ) } -fileprivate func assertExpandEditorPlaceholdersToClosures( - _ expr: String, +private func assertExpandEditorPlaceholdersToClosures( + _ call: some CallLikeSyntax, expected: String, format: ExpandEditorPlaceholdersToLiteralClosures.Context.Format = .trailing(indentationWidth: nil), file: StaticString = #filePath, line: UInt = #line ) throws { - var parser = Parser(expr) - let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).as(FunctionCallExprSyntax.self), file: file, line: line) - try assertRefactor( call, context: ExpandEditorPlaceholdersToLiteralClosures.Context(format: format), @@ -548,13 +597,49 @@ fileprivate func assertExpandEditorPlaceholdersToClosures( ) } +private func assertExpandEditorPlaceholdersToClosures( + _ expr: String, + expected: String, + format: ExpandEditorPlaceholdersToLiteralClosures.Context.Format = .trailing(indentationWidth: nil), + file: StaticString = #filePath, + line: UInt = #line +) throws { + var parser = Parser(expr) + let call = try XCTUnwrap(ExprSyntax.parse(from: &parser).asProtocol(CallLikeSyntax.self), file: file, line: line) + try assertExpandEditorPlaceholdersToClosures( + call, + expected: expected, + format: format, + file: file, + line: line + ) +} + +private func assertExpandEditorPlaceholdersToClosures( + decl: String, + expected: String, + format: ExpandEditorPlaceholdersToLiteralClosures.Context.Format = .trailing(indentationWidth: nil), + file: StaticString = #filePath, + line: UInt = #line +) throws { + var parser = Parser(decl) + let call = try XCTUnwrap(DeclSyntax.parse(from: &parser).asProtocol(CallLikeSyntax.self), file: file, line: line) + try assertExpandEditorPlaceholdersToClosures( + call, + expected: expected, + format: format, + file: file, + line: line + ) +} + fileprivate extension ExpandEditorPlaceholdersToLiteralClosures.Context.Format { static func testCustom(indentationWidth: Trivia? = nil) -> Self { .custom(CustomClosureFormat(indentationWidth: indentationWidth), allowNestedPlaceholders: true) } } -fileprivate class CustomClosureFormat: BasicFormat { +private class CustomClosureFormat: BasicFormat { override func requiresNewline(between _: TokenSyntax?, and _: TokenSyntax?) -> Bool { return false } diff --git a/Tests/SwiftRefactorTest/ManifestEditTests.swift b/Tests/SwiftRefactorTest/ManifestEditTests.swift new file mode 100644 index 00000000000..eb53f384f90 --- /dev/null +++ b/Tests/SwiftRefactorTest/ManifestEditTests.swift @@ -0,0 +1,1012 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +@_spi(FixItApplier) import SwiftIDEUtils +import SwiftParser +@_spi(PackageRefactor) import SwiftRefactor +import SwiftSyntax +import SwiftSyntaxBuilder +import XCTest +import _SwiftSyntaxTestSupport + +final class ManifestEditTests: XCTestCase { + static let swiftSystemURL: String = "https://github.com/apple/swift-system.git" + + static let swiftSystemPackageDependency: PackageDependency = .sourceControl( + .init( + location: swiftSystemURL, + requirement: .branch("main") + ) + ) + + func testAddPackageDependencyExistingComma() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", branch: "main"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .branch("main") + ) + ) + ) + ) + } + + func testAddPackageDependencyExistingNoComma() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", exact: "510.0.0"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .exact("510.0.0") + ) + ) + ) + ) + + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + /* test */ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + /* test */ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", exact: "510.0.0"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .exact("510.0.0") + ) + ) + ) + ) + + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + /* test */ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + /* test */ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", exact: "510.0.0"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .exact("510.0.0") + ) + ) + ) + ) + } + + func testAddPackageDependencyDuplicates() throws { + XCTAssertThrowsError( + try AddPackageDependency.textRefactor( + syntax: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-system.git", from: "510.0.1") + ] + ) + """, + in: .init(dependency: Self.swiftSystemPackageDependency) + ) + ) { (error: any Error) in + guard let error = error as? ManifestEditError, + case .existingDependency("https://github.com/apple/swift-system.git") = error + else { + XCTFail("unexpected error thrown: \(error)") + return + } + } + } + + func testAddPackageDependencyExistingAppended() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") + ] + [] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), + .package(url: "https://github.com/apple/swift-system.git", from: "510.0.0"), + ] + [] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .rangeFrom("510.0.0") + ) + ) + ) + ) + } + + func testAddPackageDependencyExistingOneLine() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), .package(url: "https://github.com/apple/swift-system.git", from: "510.0.0"),] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .rangeFrom("510.0.0") + ) + ) + ) + ) + + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ /*test*/ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1") ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ /*test*/ .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "510.0.1"), .package(url: "https://github.com/apple/swift-system.git", from: "510.0.0"),] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .rangeFrom("510.0.0") + ) + ) + ) + ) + } + + func testAddPackageDependencyExistingEmpty() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-system.git", "508.0.0" ..< "510.0.0"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: .sourceControl( + .init( + location: Self.swiftSystemURL, + requirement: .range(lowerBound: "508.0.0", upperBound: "510.0.0") + ) + ) + ) + ) + } + + func testAddPackageDependencyNoExistingAtEnd() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-system.git", branch: "main"), + ] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: Self.swiftSystemPackageDependency + ) + ) + } + + func testAddPackageDependencyNoExistingMiddle() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/apple/swift-system.git", branch: "main"), + ], + targets: [] + ) + """, + provider: AddPackageDependency.self, + context: .init( + dependency: Self.swiftSystemPackageDependency + ) + ) + } + + func testAddPackageDependencyErrors() { + XCTAssertThrowsError( + try AddPackageDependency.textRefactor( + syntax: """ + // swift-tools-version: 5.5 + let package: Package = .init( + name: "packages" + ) + """, + in: .init(dependency: Self.swiftSystemPackageDependency) + ) + ) { error in + XCTAssertEqual( + error as? ManifestEditError, + .cannotFindPackage + ) + } + + XCTAssertThrowsError( + try AddPackageDependency.textRefactor( + syntax: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: blah + ) + """, + in: .init(dependency: Self.swiftSystemPackageDependency) + ) + ) { (error: any Error) in + XCTAssertEqual( + error as? ManifestEditError, + .cannotFindArrayLiteralArgument(argumentName: "dependencies") + ) + } + } + + func testAddLibraryProduct() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + .target(name: "MyLib"), + ], + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + products: [ + .library( + name: "MyLib", + type: .dynamic, + targets: [ "MyLib" ] + ), + ], + targets: [ + .target(name: "MyLib"), + ], + ) + """, + provider: AddProduct.self, + context: .init( + product: + ProductDescription( + name: "MyLib", + type: .library(.dynamic), + targets: ["MyLib"] + ) + ) + ) + } + + func testAddLibraryTarget() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + .target(name: "MyLib"), + ] + ) + """, + provider: AddPackageTarget.self, + context: .init( + target: PackageTarget(name: "MyLib") + ) + ) + } + + func testAddLibraryTargetWithDependencies() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + .target( + name: "MyLib", + dependencies: [ + "OtherLib", + .product(name: "SwiftSyntax", package: "swift-syntax"), + .target(name: "TargetLib") + ] + ), + ] + ) + """, + provider: AddPackageTarget.self, + context: .init( + target: PackageTarget( + name: "MyLib", + dependencies: [ + .byName(name: "OtherLib"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .target(name: "TargetLib"), + ] + ) + ) + ) + } + + func testAddExecutableTargetWithDependencies() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + // These are the targets + .target(name: "MyLib") + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + // These are the targets + .target(name: "MyLib"), + .executableTarget( + name: "MyProgram target-name", + dependencies: [ + .product(name: "SwiftSyntax", package: "swift-syntax"), + .target(name: "TargetLib"), + "MyLib" + ] + ), + ] + ) + """, + provider: AddPackageTarget.self, + context: .init( + target: PackageTarget( + name: "MyProgram target-name", + type: .executable, + dependencies: [ + .product(name: "SwiftSyntax", package: "swift-syntax"), + .target(name: "TargetLib"), + .byName(name: "MyLib"), + ] + ) + ) + ) + } + + func testAddMacroTarget() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + import PackageDescription + + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + import CompilerPluginSupport + import PackageDescription + + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "<#version#>"), + ], + targets: [ + .macro( + name: "MyMacro target-name", + dependencies: [ + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacros", package: "swift-syntax") + ] + ), + ] + ) + """, + provider: AddPackageTarget.self, + context: .init( + target: PackageTarget( + name: "MyMacro target-name", + type: .macro + ) + ) + ) + } + + func testAddSwiftTestingTestTarget() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages" + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + targets: [ + .testTarget(name: "MyTest target-name"), + ] + ) + """, + provider: AddPackageTarget.self, + context: .init( + target: PackageTarget( + name: "MyTest target-name", + type: .test + ), + testHarness: .swiftTesting + ) + ) + } + + func testAddTargetDependency() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-example.git", from: "1.2.3"), + ], + targets: [ + .testTarget( + name: "MyTest" + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.5 + let package = Package( + name: "packages", + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-example.git", from: "1.2.3"), + ], + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + .product(name: "SomethingOrOther", package: "swift-example"), + ] + ), + ] + ) + """, + provider: AddTargetDependency.self, + context: .init( + dependency: .product(name: "SomethingOrOther", package: "swift-example"), + targetName: "MyTest" + ) + ) + } + + func testAddJava2SwiftPlugin() throws { + try assertManifestRefactor( + """ + // swift-tools-version: 5.7 + let package = Package( + name: "packages", + targets: [ + .target( + name: "MyLib" + ) + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.7 + let package = Package( + name: "packages", + targets: [ + .target( + name: "MyLib", + plugins: [ + .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + ] + ) + ] + ) + """, + provider: AddPluginUsage.self, + context: .init( + targetName: "MyLib", + pluginUsage: .plugin(name: "Java2SwiftPlugin", package: "swift-java") + ) + ) + } + + func testAddSwiftSettings() throws { + XCTAssertThrowsError( + try AddSwiftSetting.upcomingFeature( + to: "OtherTest", + name: "ExistentialAny", + manifest: """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .executableTarget( + name: "MyTest" + ) + ] + ) + """ + ) + ) { error in + XCTAssertEqual( + error as? ManifestEditError, + .cannotFindTarget(targetName: "OtherTest") + ) + } + + XCTAssertThrowsError( + try AddSwiftSetting.upcomingFeature( + to: "MyPlugin", + name: "ExistentialAny", + manifest: """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .plugin( + name: "MyPlugin", + capability: .buildTool + ) + ] + ) + """ + ) + ) { error in + XCTAssertEqual( + error as? ManifestEditError, + .cannotAddSettingsToPluginTarget + ) + } + + try assertManifestRefactor( + """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ] + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .enableUpcomingFeature("ExistentialAny:migratable"), + ] + ), + ] + ) + """ + ) { manifest in + try AddSwiftSetting.upcomingFeature( + to: "MyTest", + name: "ExistentialAny:migratable", + manifest: manifest + ) + } + + try assertManifestRefactor( + """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ] + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .enableExperimentalFeature("Extern"), + .enableExperimentalFeature("TrailingComma"), + ] + ), + ] + ) + """ + ) { manifest in + try AddSwiftSetting.experimentalFeature( + to: "MyTest", + name: "TrailingComma", + manifest: manifest + ) + } + + try assertManifestRefactor( + """ + // swift-tools-version: 6.2 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ] + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 6.2 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .strictMemorySafety(), + ] + ), + ] + ) + """ + ) { manifest in + try AddSwiftSetting.strictMemorySafety( + to: "MyTest", + manifest: manifest + ) + } + + try assertManifestRefactor( + """ + // swift-tools-version: 6.0 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ] + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 6.0 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .swiftLanguageMode(.v5), + ] + ), + ] + ) + """ + ) { manifest in + try AddSwiftSetting.languageMode( + to: "MyTest", + mode: "5", + manifest: manifest + ) + } + + // Custom language mode + try assertManifestRefactor( + """ + // swift-tools-version: 6.0 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ] + ), + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 6.0 + let package = Package( + name: "packages", + targets: [ + .testTarget( + name: "MyTest", + dependencies: [ + ], + swiftSettings: [ + .swiftLanguageMode(.version("6.2")), + ] + ), + ] + ) + """ + ) { manifest in + try AddSwiftSetting.languageMode( + to: "MyTest", + mode: "6.2", + manifest: manifest + ) + } + + try assertManifestRefactor( + """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .target( + name: "MyTest", + dependencies: [ + .byName(name: "Dependency") + ] + ), + .target( + name: "Dependency" + ) + ] + ) + """, + expectedManifest: """ + // swift-tools-version: 5.8 + let package = Package( + name: "packages", + targets: [ + .target( + name: "MyTest", + dependencies: [ + .byName(name: "Dependency") + ] + ), + .target( + name: "Dependency", + swiftSettings: [ + .enableUpcomingFeature("ExistentialAny"), + ] + ) + ] + ) + """ + ) { manifest in + try AddSwiftSetting.upcomingFeature( + to: "Dependency", + name: "ExistentialAny", + manifest: manifest + ) + } + } +} + +/// Assert that applying the given edit/refactor operation to the manifest +/// produces the expected manifest source file. +func assertManifestRefactor( + _ originalManifest: Provider.Input, + expectedManifest: Provider.Input, + provider: Provider.Type, + context: Provider.Context, + file: StaticString = #filePath, + line: UInt = #line +) throws where Provider.Input == SourceFileSyntax { + return try assertManifestRefactor( + originalManifest, + expectedManifest: expectedManifest, + file: file, + line: line + ) { (manifest) in + try provider.textRefactor(syntax: manifest, in: context) + } +} + +/// Assert that applying the given edit/refactor operation to the manifest +/// produces the expected manifest source file. +func assertManifestRefactor( + _ originalManifest: SourceFileSyntax, + expectedManifest: SourceFileSyntax, + file: StaticString = #filePath, + line: UInt = #line, + operation: (SourceFileSyntax) throws -> [SourceEdit] +) rethrows { + let edits = try operation(originalManifest) + let editedManifestSource = FixItApplier.apply( + edits: edits, + to: originalManifest + ) + + let editedManifest = Parser.parse(source: editedManifestSource) + assertStringsEqualWithDiff( + editedManifest.description, + expectedManifest.description, + file: file, + line: line + ) +} diff --git a/Tests/SwiftRefactorTest/RefactorTestUtils.swift b/Tests/SwiftRefactorTest/RefactorTestUtils.swift index 8650d03e323..721327dca61 100644 --- a/Tests/SwiftRefactorTest/RefactorTestUtils.swift +++ b/Tests/SwiftRefactorTest/RefactorTestUtils.swift @@ -25,7 +25,7 @@ func assertRefactor( file: StaticString = #filePath, line: UInt = #line ) throws { - let edits = R.textRefactor(syntax: input, in: context) + let edits = try R.textRefactor(syntax: input, in: context) guard !edits.isEmpty else { if !expected.isEmpty { XCTFail( @@ -83,12 +83,12 @@ func assertRefactor( file: StaticString = #filePath, line: UInt = #line ) throws { - let refactored = R.refactor(syntax: input, in: context) + let refactored = try? R.refactor(syntax: input, in: context) guard let refactored = refactored else { if expected != nil { XCTFail( """ - Refactoring produced nil result, expected: + Refactoring failed, expected: \(expected?.description ?? "") """, file: file, diff --git a/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift b/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift index 4194894c0e8..cbf1f3ac9c2 100644 --- a/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift +++ b/Tests/SwiftSyntaxBuilderTest/StringInterpolationTests.swift @@ -214,8 +214,8 @@ final class StringInterpolationTests: XCTestCase { } func testInterpolationLiteralOptional() { - let some: Optional = 42 - let none: Optional = nil + let some: Int? = 42 + let none: Int? = nil let a: ExprSyntax = "print(\(literal: some))" assertStringsEqualWithDiff(a.description, #"print(42)"#) diff --git a/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift index cf1c0b6267a..3560cca6174 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift @@ -25,7 +25,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct ConstantOneGetter: AccessorMacro { +private struct ConstantOneGetter: AccessorMacro { static func expansion( of node: AttributeSyntax, providingAccessorsOf declaration: some DeclSyntaxProtocol, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/AttributeRemoverTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/AttributeRemoverTests.swift index fe8a41e6fbe..a3ab09c2d18 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/AttributeRemoverTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/AttributeRemoverTests.swift @@ -16,7 +16,7 @@ import SwiftSyntax import XCTest import _SwiftSyntaxTestSupport -fileprivate func assertSyntaxRemovingTestAttributes( +private func assertSyntaxRemovingTestAttributes( _ originalSource: String, reduction expectedReducedSource: String, file: StaticString = #filePath, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/CodeItemMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/CodeItemMacroTests.swift index 1df8d0fda63..9f3f69936be 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/CodeItemMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/CodeItemMacroTests.swift @@ -25,7 +25,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct DeclsFromStringsMacro: DeclarationMacro { +private struct DeclsFromStringsMacro: DeclarationMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext diff --git a/Tests/SwiftSyntaxMacroExpansionTest/DeclarationMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/DeclarationMacroTests.swift index 20ac099d3c6..e397b669e55 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/DeclarationMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/DeclarationMacroTests.swift @@ -25,7 +25,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct DeclsFromStringsMacro: DeclarationMacro { +private struct DeclsFromStringsMacro: DeclarationMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext diff --git a/Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift index b79397f2649..609128b2475 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift @@ -24,7 +24,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct StringifyMacro: ExpressionMacro { +private struct StringifyMacro: ExpressionMacro { static func expansion( of macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext diff --git a/Tests/SwiftSyntaxMacroExpansionTest/ExtensionMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/ExtensionMacroTests.swift index eb6dae3773f..28373c7fe54 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/ExtensionMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/ExtensionMacroTests.swift @@ -253,7 +253,7 @@ final class ExtensionMacroTests: XCTestCase { } } -fileprivate struct SendableExtensionMacro: ExtensionMacro { +private struct SendableExtensionMacro: ExtensionMacro { static func expansion( of node: AttributeSyntax, attachedTo: some DeclGroupSyntax, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MemberAttributeMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MemberAttributeMacroTests.swift index 32a48cabd88..6f8c05a2c04 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MemberAttributeMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MemberAttributeMacroTests.swift @@ -25,7 +25,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct WrapAllProperties: MemberAttributeMacro { +private struct WrapAllProperties: MemberAttributeMacro { static func expansion( of node: AttributeSyntax, attachedTo decl: some DeclGroupSyntax, diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift index 48f1fcc7aaf..4a191c69079 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift @@ -25,7 +25,7 @@ import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest -fileprivate struct NoOpMemberMacro: MemberMacro { +private struct NoOpMemberMacro: MemberMacro { static func expansion( of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, diff --git a/Tests/SwiftSyntaxTest/DebugDescriptionTests.swift b/Tests/SwiftSyntaxTest/DebugDescriptionTests.swift index 9094f44e396..dde2045381c 100644 --- a/Tests/SwiftSyntaxTest/DebugDescriptionTests.swift +++ b/Tests/SwiftSyntaxTest/DebugDescriptionTests.swift @@ -157,8 +157,7 @@ class DebugDescriptionTests: XCTestCase { }(), ] - testCases.forEach { keyAndValue in - let (key:line, value:testCase) = keyAndValue + for (line, testCase) in testCases { var actualDumped = "" dump(testCase.syntax, to: &actualDumped) assertStringsEqualWithDiff( diff --git a/Tests/SwiftSyntaxTest/RawSyntaxTests.swift b/Tests/SwiftSyntaxTest/RawSyntaxTests.swift index 4a6eaf2130e..925b6842837 100644 --- a/Tests/SwiftSyntaxTest/RawSyntaxTests.swift +++ b/Tests/SwiftSyntaxTest/RawSyntaxTests.swift @@ -13,7 +13,7 @@ @_spi(RawSyntax) import SwiftSyntax import XCTest -fileprivate func cannedStructDecl(arena: ParsingRawSyntaxArena) -> RawStructDeclSyntax { +private func cannedStructDecl(arena: ParsingRawSyntaxArena) -> RawStructDeclSyntax { let structKW = RawTokenSyntax( kind: .keyword, text: arena.intern("struct"), diff --git a/Tests/SwiftSyntaxTest/SourceLocationConverterTests.swift b/Tests/SwiftSyntaxTest/SourceLocationConverterTests.swift index 7db28ec5390..dd2095e628b 100644 --- a/Tests/SwiftSyntaxTest/SourceLocationConverterTests.swift +++ b/Tests/SwiftSyntaxTest/SourceLocationConverterTests.swift @@ -15,7 +15,7 @@ import SwiftSyntaxBuilder import XCTest import _SwiftSyntaxTestSupport -fileprivate func assertPresumedSourceLocation( +private func assertPresumedSourceLocation( _ source: SourceFileSyntax, inspectionItemFilter: (CodeBlockItemSyntax.Item) -> (some SyntaxProtocol)? = { $0.as(VariableDeclSyntax.self) }, presumedFile: String, diff --git a/Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift b/Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift index 24e390117f3..d031b592e2a 100644 --- a/Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxCollectionsTests.swift @@ -14,7 +14,7 @@ import SwiftSyntax import XCTest import _SwiftSyntaxTestSupport -fileprivate func intElement(_ int: Int) -> ArrayElementSyntax { +private func intElement(_ int: Int) -> ArrayElementSyntax { let literal = TokenSyntax.integerLiteral("\(int)") return ArrayElementSyntax( expression: IntegerLiteralExprSyntax(literal: literal), @@ -22,7 +22,7 @@ fileprivate func intElement(_ int: Int) -> ArrayElementSyntax { ) } -fileprivate func assertSyntaxCollectionManipulation( +private func assertSyntaxCollectionManipulation( initialElements: [Int], transformation: (_ array: inout ArrayExprSyntax) -> Void, expectedElements: [Int], diff --git a/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift b/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift index a355341c245..367ea60d398 100644 --- a/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift @@ -13,7 +13,7 @@ import SwiftSyntax import XCTest -fileprivate func cannedStructDecl() -> StructDeclSyntax { +private func cannedStructDecl() -> StructDeclSyntax { let structKW = TokenSyntax.keyword(.struct, trailingTrivia: .space) let fooID = TokenSyntax.identifier("Foo", trailingTrivia: .space) let rBrace = TokenSyntax.rightBraceToken(leadingTrivia: .newline) @@ -210,7 +210,7 @@ class SyntaxCreationTests: XCTestCase { literal: .integerLiteral("1") ) let operatorNames = ["==", "!=", "+", "-", "*", "/", "<", ">", "<=", ">="] - operatorNames.forEach { operatorName in + for operatorName in operatorNames { let operatorToken = TokenSyntax.binaryOperator(operatorName, leadingTrivia: .space, trailingTrivia: .space) let operatorExpr = BinaryOperatorExprSyntax(operator: operatorToken) let exprList = ExprListSyntax([ diff --git a/Tests/SwiftSyntaxTest/SyntaxTreeModifierTests.swift b/Tests/SwiftSyntaxTest/SyntaxTreeModifierTests.swift index f8838043912..0c6974a1f25 100644 --- a/Tests/SwiftSyntaxTest/SyntaxTreeModifierTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxTreeModifierTests.swift @@ -13,7 +13,7 @@ import SwiftSyntax import XCTest -fileprivate func cannedVarDecl() -> VariableDeclSyntax { +private func cannedVarDecl() -> VariableDeclSyntax { let identifierPattern = IdentifierPatternSyntax( identifier: .identifier("a") ) diff --git a/Tests/SwiftWarningControlTest/WarningControlTests.swift b/Tests/SwiftWarningControlTest/WarningControlTests.swift new file mode 100644 index 00000000000..acf23b2012d --- /dev/null +++ b/Tests/SwiftWarningControlTest/WarningControlTests.swift @@ -0,0 +1,402 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 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 +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) import SwiftWarningControl +import XCTest +import _SwiftSyntaxGenericTestSupport +import _SwiftSyntaxTestSupport + +public class WarningGroupControlTests: XCTestCase { + func testSimpleFunctionWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + func foo() { + 1️⃣let x = 1 + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error + ] + ) + } + + func testNestedFunctionWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(SomeOtherGroup, as: warning) + @warn(GroupID, as: error) + @warn(YetAnotherGroup, as: warning) + func foo() { + 1️⃣let x = 1 + @warn(GroupID, as: warning) + func bar() { + 2️⃣let x = 1 + @warn(GroupID, as: ignored) + @warn(SomeOtherGroup, as: ignored) + func baz() { + 3️⃣let x = 1 + } + @warn(GroupID, as: error) + func qux() { + 4️⃣let x = 1 + @warn(SomeOtherGroup, as: warning) + func corge() { + 5️⃣let x = 1 + } + } + } + } + func grault() { + 6️⃣let x = 1 + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .warning, + "3️⃣": .ignored, + "4️⃣": .error, + "5️⃣": .error, + "6️⃣": nil, + ] + ) + } + + func testEnabledGroupIdentifiers() throws { + let source = + """ + @warn(Group1, as: warning) + @warn(Group2, as: error) + @warn(Group3, as: ignored) + func foo() { + @warn(Group4, as: warning) + func bar() { + @warn(Group5, as: ignored) + @warn(Group6, as: ignored) + func baz() {} + @warn(Group7, as: error) + func qux() { + @warn(Group8, as: warning) + func corge() {} + } + } + } + """ + var parser = Parser(source) + let parseTree = SourceFileSyntax.parse(from: &parser) + let warningControlTree = parseTree.warningGroupControlRegionTree() + let enabledDiagnosticGroups = warningControlTree.enabledGroups + XCTAssertTrue(enabledDiagnosticGroups.contains("Group1")) + XCTAssertTrue(enabledDiagnosticGroups.contains("Group2")) + XCTAssertFalse(enabledDiagnosticGroups.contains("Group3")) + XCTAssertTrue(enabledDiagnosticGroups.contains("Group4")) + XCTAssertFalse(enabledDiagnosticGroups.contains("Group5")) + XCTAssertFalse(enabledDiagnosticGroups.contains("Group6")) + XCTAssertTrue(enabledDiagnosticGroups.contains("Group7")) + XCTAssertTrue(enabledDiagnosticGroups.contains("Group8")) + } + + func testNominalDeclWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + class Foo { + 1️⃣let x = 1 + } + @warn(GroupID, as: ignored) + struct Bar { + 2️⃣let x = 1 + } + @warn(GroupID, as: warning) + enum Baz { + 3️⃣let x = 1 + } + @warn(GroupID, as: error) + actor Qux { + 4️⃣let x = 1 + @warn(GroupID, as: ignored) + struct Quux { + 5️⃣let x = 1 + } + } + @warn(GroupID, as: warning) + protocol Proto { + 6️⃣let x = 1 + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .ignored, + "3️⃣": .warning, + "4️⃣": .error, + "5️⃣": .ignored, + "6️⃣": .warning, + ] + ) + } + + func testInitializerWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + struct Foo { + 1️⃣let x = 1 + @warn(GroupID, as: ignored) + init { + 2️⃣let x = 1 + } + @warn(GroupID, as: warning) + deinit { + 3️⃣let x = 1 + } + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .ignored, + "3️⃣": .warning, + ] + ) + } + + func testExtensionWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + extension Foo { + 1️⃣let x = 1 + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error + ] + ) + } + + func testImportWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: ignored) + import1️⃣ Foo + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .ignored + ] + ) + } + + func testSubscriptWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: ignored) + struct Foo { + @warn(GroupID, as: error) + subscript(index: Int) -> Value { + 1️⃣let x = 1 + } + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error + ] + ) + } + + func testComputedPropertyWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: ignored) + struct Foo { + @warn(GroupID, as: error) + var property: Int { + 1️⃣return 11 + } + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error + ] + ) + } + + func testAccessorWarningGroupControl() throws { + try assertWarningGroupControl( + """ + @warn(GroupID, as: ignored) + struct Foo { + var property: Int { + @warn(GroupID, as: error) + get { + 1️⃣return 11 + } + @warn(GroupID, as: warning) + set { + 2️⃣let x = 1 + } + } + } + """, + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .warning, + ] + ) + } + + func testEnclosingGlobalControlOverride() throws { + // Global control does not override syntactic control + try assertWarningGroupControl( + """ + @warn(GroupID, as: error) + func foo() { + 1️⃣let x = 1 + } + """, + globalControls: ["GroupID": .warning], + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error + ] + ) + + try assertWarningGroupControl( + """ + func foo() { + 1️⃣let x = 1 + @warn(GroupID, as: ignored) + func bar() { + 2️⃣let x = 1 + } + } + """, + globalControls: ["GroupID": .error], + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .ignored, + ] + ) + } + + func testEnclosingGlobalControlOnly() throws { + // Global control used in absense of a syntactic control + try assertWarningGroupControl( + """ + func foo() { + 1️⃣let x = 1 + } + """, + globalControls: ["GroupID": .warning], + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .warning + ] + ) + } + + func testSubGroupInheritance() throws { + try assertWarningGroupControl( + """ + @warn(SuperGroupID, as: error) + func foo() { + 1️⃣let x = 1 + } + @warn(SuperSuperGroupID, as: ignored) + func bar() { + 2️⃣let x = 1 + } + """, + groupInheritanceTree: DiagnosticGroupInheritanceTree(subGroups: [ + "SuperGroupID": ["GroupID"], + "SuperSuperGroupID": ["SuperGroupID"], + ]), + diagnosticGroupID: "GroupID", + states: [ + "1️⃣": .error, + "2️⃣": .ignored, + ] + ) + } + + func testInheritanceTreeCycle() throws { + XCTAssertThrowsError( + try DiagnosticGroupInheritanceTree(subGroups: [ + "SuperGroupID": ["GroupID"], + "GroupID": ["SuperGroupID"], + ]) + ) { (error: any Error) in + XCTAssertEqual( + error as? WarningControlError, + .groupInheritanceCycle + ) + } + } +} + +/// Assert that the various marked positions in the source code have the +/// expected warning behavior controls. +private func assertWarningGroupControl( + _ markedSource: String, + globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:], + groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil, + diagnosticGroupID: DiagnosticGroupIdentifier, + states: [String: WarningGroupControl?], + file: StaticString = #filePath, + line: UInt = #line +) throws { + // Pull out the markers that we'll use to dig out nodes to query. + let (markerLocations, source) = extractMarkers(markedSource) + + var parser = Parser(source) + let tree = SourceFileSyntax.parse(from: &parser) + for (marker, location) in markerLocations { + guard let expectedState = states[marker] else { + XCTFail("Missing marker \(marker) in expected states", file: file, line: line) + continue + } + + let absolutePosition = AbsolutePosition(utf8Offset: location) + guard let token = tree.token(at: absolutePosition) else { + XCTFail("Unable to find token at location \(location)", file: file, line: line) + continue + } + + let warningControlRegions = tree.warningGroupControlRegionTree( + globalControls: globalControls, + groupInheritanceTree: groupInheritanceTree + ) + + let groupControl = token.warningGroupControl( + for: diagnosticGroupID, + globalControls: globalControls, + groupInheritanceTree: groupInheritanceTree + ) + XCTAssertEqual(groupControl, expectedState) + + let groupControlViaRegions = warningControlRegions.warningGroupControl( + at: absolutePosition, + for: diagnosticGroupID + ) + XCTAssertEqual(groupControlViaRegions, expectedState) + } +} diff --git a/cmake/modules/AddSwiftHostLibrary.cmake b/cmake/modules/AddSwiftHostLibrary.cmake index d2f7bc69eb9..b482d976c45 100644 --- a/cmake/modules/AddSwiftHostLibrary.cmake +++ b/cmake/modules/AddSwiftHostLibrary.cmake @@ -56,6 +56,9 @@ function(add_swift_syntax_library name) # Create the library target. add_library(${target} ${ASHL_SOURCES}) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_link_libraries(${target} PUBLIC swiftSwiftOnoneSupport) + endif() if(SWIFTSYNTAX_EMIT_MODULE) # Determine where Swift modules will be built and installed.