Skip to content

[SE-0458] Implement "unsafe" effect for the for-in loop #2971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ public let STMT_NODES: [Node] = [
kind: .token(choices: [.keyword(.await)]),
isOptional: true
),
Child(
name: "unsafeKeyword",
kind: .token(choices: [.keyword(.unsafe)]),
experimentalFeature: .unsafeExpression,
isOptional: true
),
Child(
name: "caseKeyword",
kind: .token(choices: [.keyword(.case)]),
Expand Down
11 changes: 11 additions & 0 deletions Sources/SwiftParser/Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,16 @@ extension Parser {
let tryKeyword = self.consume(if: .keyword(.try))
let awaitKeyword = self.consume(if: .keyword(.await))

let unsafeKeyword: RawTokenSyntax?
if let modifierKeyword = ExpressionModifierKeyword(
lexeme: self.currentToken,
experimentalFeatures: self.experimentalFeatures
), modifierKeyword == .unsafe, !self.peek(isAt: .keyword(.in)) {
unsafeKeyword = self.consumeAnyToken(remapping: .keyword)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need to check at least for : to make sure the following continues to parse:

for unsafe: Int in [1, 2] {
  print(unsafe)
}

Also, I’d prefer to do self.expect(.keyword(.unsafe)) instead of consumeAnyToken. It just adds more context to what kind of keyword unsafeKeyword is and another layer of safety if the current token should not be unsafe for any reason in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, I read your comments out of order. We indeed need to deal with the :. Thanks for the self.expect(.keyword(.unsafe)) hint.

} else {
unsafeKeyword = nil
}

// Parse the pattern. This is either 'case <refutable pattern>' or just a
// normal pattern.
let caseKeyword = self.consume(if: .keyword(.case))
Expand Down Expand Up @@ -624,6 +634,7 @@ extension Parser {
forKeyword: forKeyword,
tryKeyword: tryKeyword,
awaitKeyword: awaitKeyword,
unsafeKeyword: unsafeKeyword,
caseKeyword: caseKeyword,
pattern: pattern,
typeAnnotation: type,
Expand Down
8 changes: 6 additions & 2 deletions Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1373,8 +1373,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? {
return "unexpectedBetweenTryKeywordAndAwaitKeyword"
case \ForStmtSyntax.awaitKeyword:
return "awaitKeyword"
case \ForStmtSyntax.unexpectedBetweenAwaitKeywordAndCaseKeyword:
return "unexpectedBetweenAwaitKeywordAndCaseKeyword"
case \ForStmtSyntax.unexpectedBetweenAwaitKeywordAndUnsafeKeyword:
return "unexpectedBetweenAwaitKeywordAndUnsafeKeyword"
case \ForStmtSyntax.unsafeKeyword:
return "unsafeKeyword"
case \ForStmtSyntax.unexpectedBetweenUnsafeKeywordAndCaseKeyword:
return "unexpectedBetweenUnsafeKeywordAndCaseKeyword"
case \ForStmtSyntax.caseKeyword:
return "caseKeyword"
case \ForStmtSyntax.unexpectedBetweenCaseKeywordAndPattern:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2818,7 +2818,7 @@ extension ForStmtSyntax {
}
}

@available(*, deprecated, renamed: "init(leadingTrivia:_:forKeyword:_:tryKeyword:_:awaitKeyword:_:caseKeyword:_:pattern:_:typeAnnotation:_:inKeyword:_:sequence:_:whereClause:_:body:_:trailingTrivia:)")
@available(*, deprecated, renamed: "init(leadingTrivia:_:forKeyword:_:tryKeyword:_:awaitKeyword:_:unsafeKeyword:_:caseKeyword:_:pattern:_:typeAnnotation:_:inKeyword:_:sequence:_:whereClause:_:body:_:trailingTrivia:)")
@_disfavoredOverload
public init(
leadingTrivia: Trivia? = nil,
Expand All @@ -2828,7 +2828,9 @@ extension ForStmtSyntax {
tryKeyword: TokenSyntax? = nil,
_ unexpectedBetweenTryKeywordAndAwaitKeyword: UnexpectedNodesSyntax? = nil,
awaitKeyword: TokenSyntax? = nil,
_ unexpectedBetweenAwaitKeywordAndCaseKeyword: UnexpectedNodesSyntax? = nil,
_ unexpectedBetweenAwaitKeywordAndUnsafeKeyword: UnexpectedNodesSyntax? = nil,
unsafeKeyword: TokenSyntax? = nil,
_ unexpectedBetweenUnsafeKeywordAndCaseKeyword: UnexpectedNodesSyntax? = nil,
caseKeyword: TokenSyntax? = nil,
_ unexpectedBetweenCaseKeywordAndPattern: UnexpectedNodesSyntax? = nil,
pattern: some PatternSyntaxProtocol,
Expand All @@ -2853,7 +2855,9 @@ extension ForStmtSyntax {
tryKeyword: tryKeyword,
unexpectedBetweenTryKeywordAndAwaitKeyword,
awaitKeyword: awaitKeyword,
unexpectedBetweenAwaitKeywordAndCaseKeyword,
unexpectedBetweenAwaitKeywordAndUnsafeKeyword,
unsafeKeyword: unsafeKeyword,
unexpectedBetweenUnsafeKeywordAndCaseKeyword,
caseKeyword: caseKeyword,
unexpectedBetweenCaseKeywordAndPattern,
pattern: pattern,
Expand Down
76 changes: 44 additions & 32 deletions Sources/SwiftSyntax/generated/raw/RawSyntaxNodesEF.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1522,7 +1522,9 @@ public struct RawForStmtSyntax: RawStmtSyntaxNodeProtocol {
tryKeyword: RawTokenSyntax?,
_ unexpectedBetweenTryKeywordAndAwaitKeyword: RawUnexpectedNodesSyntax? = nil,
awaitKeyword: RawTokenSyntax?,
_ unexpectedBetweenAwaitKeywordAndCaseKeyword: RawUnexpectedNodesSyntax? = nil,
_ unexpectedBetweenAwaitKeywordAndUnsafeKeyword: RawUnexpectedNodesSyntax? = nil,
unsafeKeyword: RawTokenSyntax?,
_ unexpectedBetweenUnsafeKeywordAndCaseKeyword: RawUnexpectedNodesSyntax? = nil,
caseKeyword: RawTokenSyntax?,
_ unexpectedBetweenCaseKeywordAndPattern: RawUnexpectedNodesSyntax? = nil,
pattern: some RawPatternSyntaxNodeProtocol,
Expand All @@ -1540,29 +1542,31 @@ public struct RawForStmtSyntax: RawStmtSyntaxNodeProtocol {
arena: __shared RawSyntaxArena
) {
let raw = RawSyntax.makeLayout(
kind: .forStmt, uninitializedCount: 21, arena: arena) { layout in
kind: .forStmt, uninitializedCount: 23, arena: arena) { layout in
layout.initialize(repeating: nil)
layout[0] = unexpectedBeforeForKeyword?.raw
layout[1] = forKeyword.raw
layout[2] = unexpectedBetweenForKeywordAndTryKeyword?.raw
layout[3] = tryKeyword?.raw
layout[4] = unexpectedBetweenTryKeywordAndAwaitKeyword?.raw
layout[5] = awaitKeyword?.raw
layout[6] = unexpectedBetweenAwaitKeywordAndCaseKeyword?.raw
layout[7] = caseKeyword?.raw
layout[8] = unexpectedBetweenCaseKeywordAndPattern?.raw
layout[9] = pattern.raw
layout[10] = unexpectedBetweenPatternAndTypeAnnotation?.raw
layout[11] = typeAnnotation?.raw
layout[12] = unexpectedBetweenTypeAnnotationAndInKeyword?.raw
layout[13] = inKeyword.raw
layout[14] = unexpectedBetweenInKeywordAndSequence?.raw
layout[15] = sequence.raw
layout[16] = unexpectedBetweenSequenceAndWhereClause?.raw
layout[17] = whereClause?.raw
layout[18] = unexpectedBetweenWhereClauseAndBody?.raw
layout[19] = body.raw
layout[20] = unexpectedAfterBody?.raw
layout[6] = unexpectedBetweenAwaitKeywordAndUnsafeKeyword?.raw
layout[7] = unsafeKeyword?.raw
layout[8] = unexpectedBetweenUnsafeKeywordAndCaseKeyword?.raw
layout[9] = caseKeyword?.raw
layout[10] = unexpectedBetweenCaseKeywordAndPattern?.raw
layout[11] = pattern.raw
layout[12] = unexpectedBetweenPatternAndTypeAnnotation?.raw
layout[13] = typeAnnotation?.raw
layout[14] = unexpectedBetweenTypeAnnotationAndInKeyword?.raw
layout[15] = inKeyword.raw
layout[16] = unexpectedBetweenInKeywordAndSequence?.raw
layout[17] = sequence.raw
layout[18] = unexpectedBetweenSequenceAndWhereClause?.raw
layout[19] = whereClause?.raw
layout[20] = unexpectedBetweenWhereClauseAndBody?.raw
layout[21] = body.raw
layout[22] = unexpectedAfterBody?.raw
}
self.init(unchecked: raw)
}
Expand Down Expand Up @@ -1591,64 +1595,72 @@ public struct RawForStmtSyntax: RawStmtSyntaxNodeProtocol {
layoutView.children[5].map(RawTokenSyntax.init(raw:))
}

public var unexpectedBetweenAwaitKeywordAndCaseKeyword: RawUnexpectedNodesSyntax? {
public var unexpectedBetweenAwaitKeywordAndUnsafeKeyword: RawUnexpectedNodesSyntax? {
layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var caseKeyword: RawTokenSyntax? {
public var unsafeKeyword: RawTokenSyntax? {
layoutView.children[7].map(RawTokenSyntax.init(raw:))
}

public var unexpectedBetweenCaseKeywordAndPattern: RawUnexpectedNodesSyntax? {
public var unexpectedBetweenUnsafeKeywordAndCaseKeyword: RawUnexpectedNodesSyntax? {
layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var caseKeyword: RawTokenSyntax? {
layoutView.children[9].map(RawTokenSyntax.init(raw:))
}

public var unexpectedBetweenCaseKeywordAndPattern: RawUnexpectedNodesSyntax? {
layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var pattern: RawPatternSyntax {
layoutView.children[9].map(RawPatternSyntax.init(raw:))!
layoutView.children[11].map(RawPatternSyntax.init(raw:))!
}

public var unexpectedBetweenPatternAndTypeAnnotation: RawUnexpectedNodesSyntax? {
layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var typeAnnotation: RawTypeAnnotationSyntax? {
layoutView.children[11].map(RawTypeAnnotationSyntax.init(raw:))
layoutView.children[13].map(RawTypeAnnotationSyntax.init(raw:))
}

public var unexpectedBetweenTypeAnnotationAndInKeyword: RawUnexpectedNodesSyntax? {
layoutView.children[12].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var inKeyword: RawTokenSyntax {
layoutView.children[13].map(RawTokenSyntax.init(raw:))!
layoutView.children[15].map(RawTokenSyntax.init(raw:))!
}

public var unexpectedBetweenInKeywordAndSequence: RawUnexpectedNodesSyntax? {
layoutView.children[14].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var sequence: RawExprSyntax {
layoutView.children[15].map(RawExprSyntax.init(raw:))!
layoutView.children[17].map(RawExprSyntax.init(raw:))!
}

public var unexpectedBetweenSequenceAndWhereClause: RawUnexpectedNodesSyntax? {
layoutView.children[16].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[18].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var whereClause: RawWhereClauseSyntax? {
layoutView.children[17].map(RawWhereClauseSyntax.init(raw:))
layoutView.children[19].map(RawWhereClauseSyntax.init(raw:))
}

public var unexpectedBetweenWhereClauseAndBody: RawUnexpectedNodesSyntax? {
layoutView.children[18].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[20].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var body: RawCodeBlockSyntax {
layoutView.children[19].map(RawCodeBlockSyntax.init(raw:))!
layoutView.children[21].map(RawCodeBlockSyntax.init(raw:))!
}

public var unexpectedAfterBody: RawUnexpectedNodesSyntax? {
layoutView.children[20].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[22].map(RawUnexpectedNodesSyntax.init(raw:))
}
}

Expand Down
18 changes: 10 additions & 8 deletions Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1341,28 +1341,30 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
}
func validateForStmtSyntax(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: RawTokenSyntax.self, tokenChoices: [.keyword("for")]))
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax?.self, tokenChoices: [.keyword("try")]))
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax?.self, tokenChoices: [.keyword("await")]))
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self, tokenChoices: [.keyword("case")]))
assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self, tokenChoices: [.keyword("unsafe")]))
assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 9, verify(layout[9], as: RawPatternSyntax.self))
assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self, tokenChoices: [.keyword("case")]))
assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 11, verify(layout[11], as: RawTypeAnnotationSyntax?.self))
assertNoError(kind, 11, verify(layout[11], as: RawPatternSyntax.self))
assertNoError(kind, 12, verify(layout[12], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 13, verify(layout[13], as: RawTokenSyntax.self, tokenChoices: [.keyword("in")]))
assertNoError(kind, 13, verify(layout[13], as: RawTypeAnnotationSyntax?.self))
assertNoError(kind, 14, verify(layout[14], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 15, verify(layout[15], as: RawExprSyntax.self))
assertNoError(kind, 15, verify(layout[15], as: RawTokenSyntax.self, tokenChoices: [.keyword("in")]))
assertNoError(kind, 16, verify(layout[16], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 17, verify(layout[17], as: RawWhereClauseSyntax?.self))
assertNoError(kind, 17, verify(layout[17], as: RawExprSyntax.self))
assertNoError(kind, 18, verify(layout[18], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 19, verify(layout[19], as: RawCodeBlockSyntax.self))
assertNoError(kind, 19, verify(layout[19], as: RawWhereClauseSyntax?.self))
assertNoError(kind, 20, verify(layout[20], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 21, verify(layout[21], as: RawCodeBlockSyntax.self))
assertNoError(kind, 22, verify(layout[22], as: RawUnexpectedNodesSyntax?.self))
}
func validateForceUnwrapExprSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) {
assert(layout.count == 5)
Expand Down
Loading