Skip to content

Commit a4a4159

Browse files
committed
[SwiftParser] Parse nonisolated as modifier only if it has nonsending in expression context
1 parent 768b987 commit a4a4159

File tree

2 files changed

+140
-16
lines changed

2 files changed

+140
-16
lines changed

Sources/SwiftParser/Expressions.swift

+28-2
Original file line numberDiff line numberDiff line change
@@ -381,13 +381,39 @@ extension Parser {
381381
}
382382
}
383383

384+
/// Make sure that we only accept `nonisolated(nonsending)` as a valid type specifier
385+
/// in expression context to minimize source compatibility impact.
386+
func canParseNonisolatedAsSpecifierInExpressionContext() -> Bool {
387+
return withLookahead {
388+
guard $0.consume(if: .keyword(.nonisolated)) != nil else {
389+
return false
390+
}
391+
392+
if $0.currentToken.isAtStartOfLine {
393+
return false
394+
}
395+
396+
guard $0.consume(if: .leftParen) != nil else {
397+
return false
398+
}
399+
400+
guard $0.consume(if: TokenSpec(.nonsending, allowAtStartOfLine: false)) != nil else {
401+
return false
402+
}
403+
404+
return $0.at(TokenSpec(.rightParen, allowAtStartOfLine: false))
405+
}
406+
}
407+
384408
/// Parse an expression sequence element.
385409
mutating func parseSequenceExpressionElement(
386410
flavor: ExprFlavor,
387411
pattern: PatternContext = .none
388412
) -> RawExprSyntax {
389-
// Try to parse '@' sign or 'inout' as an attributed typerepr.
390-
if self.at(.atSign, .keyword(.inout)) {
413+
// Try to parse '@' sign, 'inout', or 'nonisolated' as an attributed typerepr.
414+
if self.at(.atSign, .keyword(.inout))
415+
|| self.canParseNonisolatedAsSpecifierInExpressionContext()
416+
{
391417
var lookahead = self.lookahead()
392418
if lookahead.canParseType() {
393419
let type = self.parseType()

Tests/SwiftParserTest/TypeTests.swift

+112-14
Original file line numberDiff line numberDiff line change
@@ -553,38 +553,136 @@ final class TypeTests: ParserTestCase {
553553
}
554554

555555
func testNonisolatedSpecifier() {
556-
assertParse("let _: nonisolated(nonsending) () async -> Void = {}")
557-
assertParse("let _: [nonisolated(nonsending) () async -> Void]")
558-
assertParse("let _ = [String: (nonisolated(nonsending) () async -> Void)?].self")
559-
assertParse("let _ = Array<nonisolated(nonsending) () async -> Void>()")
560-
assertParse("func foo(test: nonisolated(nonsending) () async -> Void)")
561-
assertParse("func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}")
562-
assertParse("test(S<nonisolated(nonsending) () async -> Void>(), type(of: concurrentTest))")
563-
assertParse("S<nonisolated(nonsending) @Sendable (Int) async -> Void>()")
564-
assertParse("let _ = S<nonisolated(nonsending) consuming @Sendable (Int) async -> Void>()")
565-
assertParse("struct S : nonisolated P {}")
566-
assertParse("let _ = [nonisolated()]")
556+
assertParse(
557+
"""
558+
let x = nonisolated
559+
print("hello")
560+
""",
561+
substructure: DeclReferenceExprSyntax(
562+
baseName: .identifier("nonisolated")
563+
)
564+
)
565+
566+
assertParse(
567+
"let _: nonisolated(nonsending) () async -> Void = {}",
568+
substructure: NonisolatedTypeSpecifierSyntax(
569+
nonisolatedKeyword: .keyword(.nonisolated),
570+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
571+
)
572+
)
573+
assertParse(
574+
"let _: [nonisolated(nonsending) () async -> Void]",
575+
substructure: NonisolatedTypeSpecifierSyntax(
576+
nonisolatedKeyword: .keyword(.nonisolated),
577+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
578+
)
579+
)
580+
581+
assertParse(
582+
"let _ = [String: (nonisolated(nonsending) () async -> Void)?].self",
583+
substructure: NonisolatedTypeSpecifierSyntax(
584+
nonisolatedKeyword: .keyword(.nonisolated),
585+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
586+
)
587+
)
588+
589+
assertParse(
590+
"let _ = Array<nonisolated(nonsending) () async -> Void>()",
591+
substructure: NonisolatedTypeSpecifierSyntax(
592+
nonisolatedKeyword: .keyword(.nonisolated),
593+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
594+
)
595+
)
596+
assertParse(
597+
"func foo(test: nonisolated(nonsending) () async -> Void)",
598+
substructure: NonisolatedTypeSpecifierSyntax(
599+
nonisolatedKeyword: .keyword(.nonisolated),
600+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
601+
)
602+
)
603+
assertParse(
604+
"func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}",
605+
substructure: NonisolatedTypeSpecifierSyntax(
606+
nonisolatedKeyword: .keyword(.nonisolated),
607+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
608+
)
609+
)
610+
assertParse(
611+
"test(S<nonisolated(nonsending) () async -> Void>(), type(of: concurrentTest))",
612+
substructure: NonisolatedTypeSpecifierSyntax(
613+
nonisolatedKeyword: .keyword(.nonisolated),
614+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
615+
)
616+
)
617+
assertParse(
618+
"S<nonisolated(nonsending) @Sendable (Int) async -> Void>()",
619+
substructure: NonisolatedTypeSpecifierSyntax(
620+
nonisolatedKeyword: .keyword(.nonisolated),
621+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
622+
)
623+
)
624+
assertParse(
625+
"let _ = S<nonisolated(nonsending) consuming @Sendable (Int) async -> Void>()",
626+
substructure: NonisolatedTypeSpecifierSyntax(
627+
nonisolatedKeyword: .keyword(.nonisolated),
628+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
629+
)
630+
)
631+
632+
assertParse(
633+
"struct S : nonisolated P {}",
634+
substructure: NonisolatedTypeSpecifierSyntax(
635+
nonisolatedKeyword: .keyword(.nonisolated)
636+
)
637+
)
638+
639+
assertParse(
640+
"let _ = [nonisolated()]",
641+
substructure: DeclReferenceExprSyntax(
642+
baseName: .identifier("nonisolated")
643+
)
644+
)
645+
646+
assertParse(
647+
"let _ = [nonisolated(nonsending) () async -> Void]()",
648+
substructure: NonisolatedTypeSpecifierSyntax(
649+
nonisolatedKeyword: .keyword(.nonisolated),
650+
argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending))
651+
)
652+
)
653+
654+
assertParse(
655+
"_ = S<nonisolated>()",
656+
substructure: GenericArgumentSyntax.Argument(
657+
IdentifierTypeSyntax(name: .identifier("nonisolated"))
658+
)
659+
)
567660

568661
assertParse(
569662
"""
570663
let x: nonisolated
571664
(hello)
572-
"""
665+
""",
666+
substructure: IdentifierTypeSyntax(name: .identifier("nonisolated"))
573667
)
574668

575669
assertParse(
576670
"""
577671
struct S: nonisolated
578672
P {
579673
}
580-
"""
674+
""",
675+
substructure: NonisolatedTypeSpecifierSyntax(
676+
nonisolatedKeyword: .keyword(.nonisolated)
677+
)
581678
)
582679

583680
assertParse(
584681
"""
585682
let x: nonisolated
586683
(Int) async -> Void = {}
587-
"""
684+
""",
685+
substructure: IdentifierTypeSyntax(name: .identifier("nonisolated"))
588686
)
589687

590688
assertParse(

0 commit comments

Comments
 (0)