swiftlang/swift-syntax

Closure arguments parsed as attribute parameters

Closed this issue · 4 comments

Issue Kind

Parse of Valid Source Produced Invalid Syntax Tree

Source Code

f { @Sendable (e: Int) in }

Description

Parsing the code snippet, swift-parser-cli print-tree produces the tree

SourceFileSyntax
├─statements: CodeBlockItemListSyntax
│ ╰─[0]: CodeBlockItemSyntax
│   ╰─item: FunctionCallExprSyntax
│     ├─calledExpression: DeclReferenceExprSyntax
│     │ ╰─baseName: identifier("f")
│     ├─arguments: LabeledExprListSyntax
│     ├─trailingClosure: ClosureExprSyntax
│     │ ├─leftBrace: leftBrace
│     │ ├─signature: ClosureSignatureSyntax
│     │ │ ├─attributes: AttributeListSyntax
│     │ │ │ ╰─[0]: AttributeSyntax
│     │ │ │   ├─atSign: atSign
│     │ │ │   ├─attributeName: IdentifierTypeSyntax
│     │ │ │   │ ╰─name: identifier("Sendable")
│     │ │ │   ├─leftParen: leftParen
│     │ │ │   ├─arguments: LabeledExprListSyntax
│     │ │ │   │ ╰─[0]: LabeledExprSyntax
│     │ │ │   │   ├─label: identifier("e")
│     │ │ │   │   ├─colon: colon
│     │ │ │   │   ╰─expression: DeclReferenceExprSyntax
│     │ │ │   │     ╰─baseName: identifier("Int")
│     │ │ │   ╰─rightParen: rightParen
│     │ │ ╰─inKeyword: keyword(SwiftSyntax.Keyword.in)
│     │ ├─statements: CodeBlockItemListSyntax
│     │ ╰─rightBrace: rightBrace
│     ╰─additionalTrailingClosures: MultipleTrailingClosureElementListSyntax
╰─endOfFileToken: endOfFile

which shows that (e: Int) is interpreted as the parameter list of @Sendable while it is actually meant to be the argument list for the closure.

Is this an ambiguity only the compiler can resolve with full type information available?

Tracked in Apple’s issue tracker as rdar://118608697

@ahoppen: I think @Sendable is not the primary reason for the behavior. I see f { @A (e: Int) in } is parsed in the same way, namely (e: Int) as an argument list.

AFAIK @A (e: Int) isn’t valid at the moment either.

_ = { @MainActor (e: Int) in 
  print(e) // error: /tmp/a.swift:2:9: error: cannot find 'e' in scope
}

You need to write this as

_ = { @MainActor
  (e: Int) in 
  print(e)
}

I’m open to discussing whether the first variant should be allowed but that would be a separate issue since it’s not a discrepancy between the new and the old parser.

Okay. I see the difference. What behaves like the current parser is not a bug.