Repository: swiftlang/swift-syntax
Pull request #3018 (2025-03-16 21:14:35)

[SwiftParser] Experimental feature flags for  _borrow and _move
_borrow and _move should be parsed as an ownership operator only when a feature flag is enabled.

rintaro: https://github.com/swiftlang/swift/pull/80046
@swift-ci Please test
rintaro: https://github.com/swiftlang/swift/pull/80046
@swift-ci Please test
rintaro: https://github.com/swiftlang/swift/pull/80046
@swift-ci Please test

Patch:
diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift
index d807c71c..a62d4224 100644
--- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift
+++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift
@@ -22,6 +22,7 @@ public enum ExperimentalFeature: String, CaseIterable {
   case valueGenerics
   case abiAttribute
   case keypathWithMethodMembers
+  case oldOwnershipOperatorSpellings
 
   /// The name of the feature as it is written in the compiler's `Features.def` file.
   public var featureName: String {
@@ -44,6 +45,8 @@ public enum ExperimentalFeature: String, CaseIterable {
       return "ABIAttribute"
     case .keypathWithMethodMembers:
       return "KeypathWithMethodMembers"
+    case .oldOwnershipOperatorSpellings:
+      return "OldOwnershipOperatorSpellings"
     }
   }
 
@@ -68,6 +71,8 @@ public enum ExperimentalFeature: String, CaseIterable {
       return "@abi attribute"
     case .keypathWithMethodMembers:
       return "keypaths with method members"
+    case .oldOwnershipOperatorSpellings:
+      return "`_move` and `_borrow` as ownership operators"
     }
   }
 
diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift
index ee101e60..1aad77b9 100644
--- a/Sources/SwiftParser/Expressions.swift
+++ b/Sources/SwiftParser/Expressions.swift
@@ -481,20 +481,9 @@ extension Parser {
           arena: self.arena
         )
       )
-    case (._move, let handle)?:
-      let moveKeyword = self.eat(handle)
-      let sub = self.parseSequenceExpressionElement(
-        flavor: flavor,
-        pattern: pattern
-      )
-      return RawExprSyntax(
-        RawConsumeExprSyntax(
-          consumeKeyword: moveKeyword,
-          expression: sub,
-          arena: self.arena
-        )
-      )
+
     case (._borrow, let handle)?:
+      assert(self.experimentalFeatures.contains(.oldOwnershipOperatorSpellings))
       fallthrough
     case (.borrow, let handle)?:
       if !atContextualExpressionModifier() {
@@ -531,6 +520,9 @@ extension Parser {
         )
       )
 
+    case (._move, let handle)?:
+      assert(self.experimentalFeatures.contains(.oldOwnershipOperatorSpellings))
+      fallthrough
     case (.consume, let handle)?:
       if !atContextualExpressionModifier() {
         break EXPR_PREFIX
diff --git a/Sources/SwiftParser/TokenSpecSet.swift b/Sources/SwiftParser/TokenSpecSet.swift
index 2519b689..164149d2 100644
--- a/Sources/SwiftParser/TokenSpecSet.swift
+++ b/Sources/SwiftParser/TokenSpecSet.swift
@@ -705,8 +705,8 @@ enum ExpressionModifierKeyword: TokenSpecSet {
   init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
     switch PrepareForKeywordMatch(lexeme) {
     case TokenSpec(.await): self = .await
-    case TokenSpec(._move): self = ._move
-    case TokenSpec(._borrow): self = ._borrow
+    case TokenSpec(._move) where experimentalFeatures.contains(.oldOwnershipOperatorSpellings): self = ._move
+    case TokenSpec(._borrow) where experimentalFeatures.contains(.oldOwnershipOperatorSpellings): self = ._borrow
     case TokenSpec(.try): self = .try
     case TokenSpec(.borrow): self = .borrow
     case TokenSpec(.consume): self = .consume
diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift
index fac23f47..40d3939c 100644
--- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift
+++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift
@@ -52,6 +52,9 @@ extension Parser.ExperimentalFeatures {
   /// Whether to enable the parsing of keypaths with method members.
   public static let keypathWithMethodMembers = Self (rawValue: 1 << 8)
 
+  /// Whether to enable the parsing of `_move` and `_borrow` as ownership operators.
+  public static let oldOwnershipOperatorSpellings = 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.
   public init?(name: String) {
@@ -74,6 +77,8 @@ extension Parser.ExperimentalFeatures {
       self = .abiAttribute
     case "KeypathWithMethodMembers":
       self = .keypathWithMethodMembers
+    case "OldOwnershipOperatorSpellings":
+      self = .oldOwnershipOperatorSpellings
     default:
       return nil
     }


Test patch:
diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift
index 8084f19b..e63d753d 100644
--- a/Tests/SwiftParserTest/ExpressionTests.swift
+++ b/Tests/SwiftParserTest/ExpressionTests.swift
@@ -1557,18 +1557,18 @@ final class ExpressionTests: ParserTestCase {
     )
   }
 
-  func testMoveExpression() {
-    assertParse("_move msg")
-    assertParse("use(_move msg)")
-    assertParse("_move msg")
-    assertParse("let b = (_move self).buffer")
+  func testConsumeExpression() {
+    assertParse("consume msg")
+    assertParse("use(consume msg)")
+    assertParse("consume msg")
+    assertParse("let b = (consume self).buffer")
   }
 
   func testBorrowExpression() {
-    assertParse("_borrow msg")
-    assertParse("use(_borrow msg)")
-    assertParse("_borrow msg")
-    assertParse("let b = (_borrow self).buffer")
+    assertParse("borrow msg")
+    assertParse("use(borrow msg)")
+    assertParse("borrow msg")
+    assertParse("let b = (borrow self).buffer")
     assertParse("borrow msg")
     assertParse("use(borrow msg)")
     assertParse("borrow(msg)")
diff --git a/Tests/SwiftParserTest/translated/BorrowExprTests.swift b/Tests/SwiftParserTest/translated/BorrowExprTests.swift
index 78d07b05..d945782a 100644
--- a/Tests/SwiftParserTest/translated/BorrowExprTests.swift
+++ b/Tests/SwiftParserTest/translated/BorrowExprTests.swift
@@ -12,6 +12,8 @@
 
 // This test file has been translated from swift/test/Parse/borrow_expr.swift
 
+@_spi(ExperimentalLanguageFeatures) import SwiftParser
+import SwiftSyntax
 import XCTest
 
 final class BorrowExprTests: ParserTestCase {
@@ -23,7 +25,8 @@ final class BorrowExprTests: ParserTestCase {
       func testGlobal() {
         useString(_borrow global)
       }
-      """
+      """,
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
     )
   }
 
@@ -36,7 +39,8 @@ final class BorrowExprTests: ParserTestCase {
           t = String()
           useString(_borrow t)
       }
-      """
+      """,
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
     )
   }
 
diff --git a/Tests/SwiftParserTest/translated/MoveExprTests.swift b/Tests/SwiftParserTest/translated/MoveExprTests.swift
index 98d5ad82..18f1bb7e 100644
--- a/Tests/SwiftParserTest/translated/MoveExprTests.swift
+++ b/Tests/SwiftParserTest/translated/MoveExprTests.swift
@@ -12,6 +12,8 @@
 
 // This test file has been translated from swift/test/Parse/move_expr.swift
 
+@_spi(ExperimentalLanguageFeatures) import SwiftParser
+import SwiftSyntax
 import XCTest
 
 final class MoveExprTests: ParserTestCase {
@@ -22,7 +24,8 @@ final class MoveExprTests: ParserTestCase {
       func testGlobal() {
           let _ = _move global
       }
-      """
+      """,
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
     )
   }
 
@@ -33,7 +36,9 @@ final class MoveExprTests: ParserTestCase {
           let t = String()
           let _ = _move t
       }
-      """
+      """,
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
+
     )
   }
 
@@ -45,7 +50,58 @@ final class MoveExprTests: ParserTestCase {
           t = String()
           let _ = _move t
       }
+      """,
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
+    )
+  }
+
+  func testMoveExpr4() {
+    assertParse(
+      """
+      _move(t)
+      """,
+      substructure: FunctionCallExprSyntax(
+        calledExpression: DeclReferenceExprSyntax(baseName: .identifier("_move")),
+        leftParen: .leftParenToken(),
+        arguments: [.init(expression: DeclReferenceExprSyntax(baseName: .identifier("t")))],
+        rightParen: .rightParenToken()
+      ),
+      experimentalFeatures: [.oldOwnershipOperatorSpellings]
+    )
+  }
+
+  func testMoveExpr5() {
+    assertParse(
+      """
+      _move(t)
+      """,
+      substructure: FunctionCallExprSyntax(
+        calledExpression: DeclReferenceExprSyntax(baseName: .identifier("_move")),
+        leftParen: .leftParenToken(),
+        arguments: [.init(expression: DeclReferenceExprSyntax(baseName: .identifier("t")))],
+        rightParen: .rightParenToken()
+      ),
+      experimentalFeatures: []
+    )
+  }
+
+  func testMoveExpr6() {
+    assertParse(
       """
+      _move1️⃣ t 
+      """,
+      diagnostics: [
+        DiagnosticSpec(
+          message: "consecutive statements on a line must be separated by newline or ';'",
+          fixIts: ["insert newline", "insert ';'"]
+        )
+      ],
+      fixedSource:
+        """
+        _move
+        t
+        """,
+      experimentalFeatures: []
     )
   }