Skip to content

Commit 3c2f1e4

Browse files
authored
Merge pull request #34396 from rjmccall/tentative-custom-attr-parsing
Fix isStartOfSwiftDecl to allow qualified types and generic arguments in attributes
2 parents b094758 + 223d7b9 commit 3c2f1e4

File tree

2 files changed

+70
-15
lines changed

2 files changed

+70
-15
lines changed

lib/Parse/ParseDecl.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3616,6 +3616,30 @@ static bool isParenthesizedUnowned(Parser &P) {
36163616
(P.Tok.getText() == "safe" || P.Tok.getText() == "unsafe");
36173617
}
36183618

3619+
static void skipAttribute(Parser &P) {
3620+
// Consider unexpected tokens to be incomplete attributes.
3621+
3622+
// Parse the attribute name, which can be qualified, have
3623+
// generic arguments, and so on.
3624+
do {
3625+
if (!P.consumeIf(tok::identifier) && !P.consumeIf(tok::code_complete))
3626+
return;
3627+
3628+
if (P.startsWithLess(P.Tok)) {
3629+
P.consumeStartingLess();
3630+
P.skipUntilGreaterInTypeList();
3631+
}
3632+
} while (P.consumeIf(tok::period));
3633+
3634+
// Skip an argument clause after the attribute name.
3635+
if (P.consumeIf(tok::l_paren)) {
3636+
while (P.Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) {
3637+
if (P.consumeIf(tok::r_paren)) break;
3638+
P.skipSingle();
3639+
}
3640+
}
3641+
}
3642+
36193643
bool Parser::isStartOfSwiftDecl() {
36203644
// If this is obviously not the start of a decl, then we're done.
36213645
if (!isKeywordPossibleDeclStart(Tok)) return false;
@@ -3645,23 +3669,14 @@ bool Parser::isStartOfSwiftDecl() {
36453669
if (Tok.is(tok::kw_try))
36463670
return peekToken().isAny(tok::kw_let, tok::kw_var);
36473671

3648-
// Look through attribute list, because it may be an *type* attribute list.
3672+
// Skip an attribute, since it might be a type attribute. This can't
3673+
// happen at the top level of a scope, but we do use isStartOfSwiftDecl()
3674+
// in positions like generic argument lists.
36493675
if (Tok.is(tok::at_sign)) {
36503676
BacktrackingScope backtrack(*this);
3651-
while (consumeIf(tok::at_sign)) {
3652-
// If not identifier or code complete token, consider '@' as an incomplete
3653-
// attribute.
3654-
if (Tok.isNot(tok::identifier, tok::code_complete))
3655-
continue;
3656-
consumeToken();
3657-
// Eat paren after attribute name; e.g. @foo(x)
3658-
if (consumeIf(tok::l_paren)) {
3659-
while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) {
3660-
if (consumeIf(tok::r_paren)) break;
3661-
skipSingle();
3662-
}
3663-
}
3664-
}
3677+
while (consumeIf(tok::at_sign))
3678+
skipAttribute(*this);
3679+
36653680
// If this attribute is the last element in the block,
36663681
// consider it is a start of incomplete decl.
36673682
if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif))

test/Parse/result-builder.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// rdar://70158735
4+
5+
@resultBuilder
6+
struct A<T> {
7+
static func buildBlock(_ values: Int...) -> Int { return 0 }
8+
}
9+
10+
struct B<T> {}
11+
12+
extension B {
13+
@resultBuilder
14+
struct Generic<U> {
15+
static func buildBlock(_ values: Int...) -> Int { return 0 }
16+
}
17+
18+
@resultBuilder
19+
struct NonGeneric {
20+
static func buildBlock(_ values: Int...) -> Int { return 0 }
21+
}
22+
}
23+
24+
@A<Float> var test0: Int {
25+
1
26+
2
27+
3
28+
}
29+
30+
@B<Float>.NonGeneric var test1: Int {
31+
1
32+
2
33+
3
34+
}
35+
36+
@B<Float>.Generic<Float> var test2: Int {
37+
1
38+
2
39+
3
40+
}

0 commit comments

Comments
 (0)