Skip to content
Draft
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
37 changes: 37 additions & 0 deletions Sources/SwiftLexicalLookup/SimpleLookupQueries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,43 @@
import SwiftSyntax

extension SyntaxProtocol {

@_spi(Experimental) public func lookupControlStructure() -> Syntax? {
Copy link
Member

@rintaro rintaro Dec 22, 2025

Choose a reason for hiding this comment

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

I don't feel comfortable with having this method on SyntaxProtocol.
Instead, how about making

@_spi(Experimental) public protocol ControlTransferStmtSyntax {
  var controlTransferTarget: Syntax? { get }
}

And make break, continue, return, throw, and fallthrough conform to it?

let syntax = Syntax(self)

switch syntax.as(SyntaxEnum.self) {
case .throwStmt, .returnStmt:
Copy link
Member

Choose a reason for hiding this comment

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

The target of throw can be do-catch statements.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, SyntaxProtoco.lookupCatchNode() should be usable for throw case.

return lookupEnclosingControlStructure(for: [
FunctionDeclSyntax.self,
InitializerDeclSyntax.self,
DeinitializerDeclSyntax.self,
AccessorDeclSyntax.self,
ClosureExprSyntax.self,
])
case .breakStmt, .continueStmt:
return lookupEnclosingControlStructure(for: [
Comment on lines +29 to +30
Copy link
Member

@rintaro rintaro Dec 22, 2025

Choose a reason for hiding this comment

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

Unfortunately, break and continue target is not that simple. You'd need to handle labels e.g. break LOOP.
Also, they can target other non-loop labeled statements , e.g.

OUTER: do {
  for val in values {
    if val.condition {
      break OUTER
    }
  }
}

In the actual compiler, the rule is implemented in swift::findBreakOrContinueStmtTarget

WhileStmtSyntax.self,
RepeatStmtSyntax.self,
ForStmtSyntax.self,
])
default:
return nil
}
}

private func lookupEnclosingControlStructure(for types: [SyntaxProtocol.Type]) -> Syntax? {
var nextSyntax: Syntax? = Syntax(self).parent

while let currentSyntax = nextSyntax {
if types.contains(where: { currentSyntax.is($0) }) {
return currentSyntax
}
nextSyntax = currentSyntax.parent
}

return nil
}

/// Returns all labeled statements available at a particular syntax node.
///
/// - Returns: Available labeled statements at a particular syntax node
Expand Down