Skip to content

Commit 56e377c

Browse files
committed
Rewrite isStartOfDeclaration to more closesly match the parser implementation
This allows us to share a few token kinds betewen `isStartOfDeclaration` and the actual parser.
1 parent 6a07ac5 commit 56e377c

File tree

9 files changed

+262
-268
lines changed

9 files changed

+262
-268
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 45 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,48 @@
1212

1313
@_spi(RawSyntax) import SwiftSyntax
1414

15+
extension TokenConsumer {
16+
func atStartOfDeclaration() -> Bool {
17+
if self.at(anyIn: PoundDeclarationStart.self) != nil {
18+
return true
19+
}
20+
21+
var subparser = self.lookahead()
22+
23+
var hasAttribute = false
24+
var attributeProgress = LoopProgressCondition()
25+
while attributeProgress.evaluate(subparser.currentToken) && subparser.at(.atSign) {
26+
hasAttribute = true
27+
_ = subparser.eatParseAttributeList()
28+
}
29+
30+
var modifierProgress = LoopProgressCondition()
31+
while let (modifierKind, handle) = subparser.at(anyIn: DeclarationModifier.self),
32+
modifierKind != .classKeyword,
33+
modifierProgress.evaluate(subparser.currentToken) {
34+
subparser.eat(handle)
35+
if subparser.at(.leftParen) {
36+
subparser.consumeAnyToken()
37+
subparser.consume(to: .rightParen)
38+
}
39+
}
40+
41+
if hasAttribute {
42+
if subparser.at(.rightBrace) || subparser.at(.eof) || subparser.at(.poundEndifKeyword) {
43+
return true
44+
}
45+
}
46+
47+
switch subparser.at(anyIn: DeclarationStart.self) {
48+
case (.caseKeyword, _)?, nil:
49+
// When 'case' appears inside a function, it's probably a switch
50+
// case, not an enum case declaration.
51+
return false
52+
default: return true
53+
}
54+
}
55+
}
56+
1557
extension Parser {
1658
@_spi(RawSyntax)
1759
public struct DeclAttributes {
@@ -49,21 +91,7 @@ extension Parser {
4991
/// declarations → declaration declarations?
5092
@_spi(RawSyntax)
5193
public mutating func parseDeclaration() -> RawDeclSyntax {
52-
enum PoundIfTokenKind: RawTokenKindSubset {
53-
case poundIfKeyword
54-
case poundWarningKeyword
55-
case poundErrorKeyword
56-
57-
var rawTokenKind: RawTokenKind {
58-
switch self {
59-
case .poundIfKeyword: return .poundIfKeyword
60-
case .poundWarningKeyword: return .poundWarningKeyword
61-
case .poundErrorKeyword: return .poundErrorKeyword
62-
}
63-
}
64-
}
65-
66-
switch self.at(anyIn: PoundIfTokenKind.self) {
94+
switch self.at(anyIn: PoundDeclarationStart.self) {
6795
case (.poundIfKeyword, _)?:
6896
return RawDeclSyntax(self.parsePoundIfDirective { parser in
6997
let parsedDecl = parser.parseDeclaration()
@@ -82,61 +110,10 @@ extension Parser {
82110
break
83111
}
84112

85-
enum DeclStartTokenKind: RawTokenKindSubset {
86-
case importKeyword
87-
case classKeyword
88-
case enumKeyword
89-
case caseKeyword
90-
case structKeyword
91-
case protocolKeyword
92-
case associatedtypeKeyword
93-
case typealiasKeyword
94-
case extensionKeyword
95-
case funcKeyword
96-
case subscriptKeyword
97-
case letKeyword
98-
case varKeyword
99-
case initKeyword
100-
case deinitKeyword
101-
case operatorKeyword
102-
case precedencegroupKeyword
103-
case actor
104-
105-
var contextualKeyword: SyntaxText? {
106-
switch self {
107-
case .actor: return "actor"
108-
default: return nil
109-
}
110-
}
111-
112-
var rawTokenKind: RawTokenKind {
113-
switch self {
114-
case .importKeyword: return .importKeyword
115-
case .classKeyword: return .classKeyword
116-
case .enumKeyword: return .enumKeyword
117-
case .caseKeyword: return .caseKeyword
118-
case .structKeyword: return .structKeyword
119-
case .protocolKeyword: return .protocolKeyword
120-
case .associatedtypeKeyword: return .associatedtypeKeyword
121-
case .typealiasKeyword: return .typealiasKeyword
122-
case .extensionKeyword: return .extensionKeyword
123-
case .funcKeyword: return .funcKeyword
124-
case .subscriptKeyword: return .subscriptKeyword
125-
case .letKeyword: return .letKeyword
126-
case .varKeyword: return .varKeyword
127-
case .initKeyword: return .initKeyword
128-
case .deinitKeyword: return .deinitKeyword
129-
case .operatorKeyword: return .operatorKeyword
130-
case .precedencegroupKeyword: return .precedencegroupKeyword
131-
case .actor: return .identifier
132-
}
133-
}
134-
}
135-
136113
let attrs = DeclAttributes(
137114
attributes: self.parseAttributeList(),
138115
modifiers: self.parseModifierList())
139-
switch self.at(anyIn: DeclStartTokenKind.self) {
116+
switch self.at(anyIn: DeclarationStart.self) {
140117
case (.importKeyword, _)?:
141118
return RawDeclSyntax(self.parseImportDeclaration(attrs))
142119
case (.classKeyword, _)?:
@@ -170,7 +147,7 @@ extension Parser {
170147
return RawDeclSyntax(self.parseOperatorDeclaration(attrs))
171148
case (.precedencegroupKeyword, _)?:
172149
return RawDeclSyntax(self.parsePrecedenceGroupDeclaration(attrs))
173-
case (.actor, _)?:
150+
case (.actorContextualKeyword, _)?:
174151
return RawDeclSyntax(self.parseActorDeclaration(attrs))
175152
case nil:
176153
return RawDeclSyntax(RawMissingDeclSyntax(

Sources/SwiftParser/Expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1673,7 +1673,7 @@ extension Parser {
16731673
// If The next token is at the beginning of a new line and can never start
16741674
// an element, break.
16751675
if self.currentToken.isAtStartOfLine
1676-
&& (self.at(any: [.rightBrace, .poundEndifKeyword]) || self.lookahead().isStartOfDeclaration() || self.atStartOfStatement()) {
1676+
&& (self.at(any: [.rightBrace, .poundEndifKeyword]) || self.atStartOfDeclaration() || self.atStartOfStatement()) {
16771677
break
16781678
}
16791679
}

Sources/SwiftParser/Lookahead.swift

Lines changed: 16 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,22 @@ extension Parser.Lookahead {
7575
self.consumeAnyToken()
7676
}
7777

78+
/// Consume tokens of lower precedence than `kind` until reaching a token of that kind.
79+
/// The token of `kind` is also consumed.
80+
@_spi(RawSyntax)
81+
public mutating func consume(to kind: RawTokenKind) {
82+
if self.consume(if: kind) != nil {
83+
return
84+
}
85+
var lookahead = self.lookahead()
86+
if lookahead.canRecoverTo([kind]) {
87+
for _ in 0..<lookahead.tokensConsumed {
88+
self.consumeAnyToken()
89+
}
90+
self.consumeAnyToken()
91+
}
92+
}
93+
7894
/// Consumes a given token, or splits the current token into a leading token
7995
/// matching the given token and a trailing token and consumes the leading
8096
/// token.
@@ -220,143 +236,6 @@ extension Parser.Lookahead {
220236
"_opaqueReturnTypeOf",
221237
]
222238

223-
func isStartOfDeclaration() -> Bool {
224-
guard self.at(anyIn: CanBeDeclaratinStart.self) != nil else {
225-
// If this is obviously not the start of a decl, then we're done.
226-
return false
227-
}
228-
229-
/*
230-
// When 'init' appears inside another 'init', it's likely the user wants to
231-
// invoke an initializer but forgets to prefix it with 'self.' or 'super.'
232-
// Otherwise, expect 'init' to be the start of a declaration (and complain
233-
// when the expectation is not fulfilled).
234-
if (Tok.is(tok::kw_init)) {
235-
return !isa<ConstructorDecl>(CurDeclContext);
236-
}
237-
*/
238-
239-
// Similarly, when 'case' appears inside a function, it's probably a switch
240-
// case, not an enum case declaration.
241-
if self.at(.caseKeyword) {
242-
return false
243-
}
244-
245-
/*
246-
// The protocol keyword needs more checking to reject "protocol<Int>".
247-
if (Tok.is(tok::kw_protocol)) {
248-
const Token &Tok2 = peekToken();
249-
return !Tok2.isAnyOperator() || !Tok2.getText().equals("<");
250-
}
251-
252-
// The 'try' case is only for simple local recovery, so we only bother to
253-
// check 'let' and 'var' right now.
254-
if (Tok.is(tok::kw_try))
255-
return peekToken().isAny(tok::kw_let, tok::kw_var);
256-
*/
257-
258-
// Skip an attribute, since it might be a type attribute. This can't
259-
// happen at the top level of a scope, but we do use isStartOfSwiftDecl()
260-
// in positions like generic argument lists.
261-
if self.at(.atSign) {
262-
var subparser = self.lookahead()
263-
_ = subparser.eatParseAttributeList()
264-
// If this attribute is the last element in the block,
265-
// consider it is a start of incomplete decl.
266-
if subparser.at(any: [.rightBrace, .eof, .poundEndifKeyword]) {
267-
return true
268-
}
269-
return subparser.isStartOfDeclaration()
270-
}
271-
272-
// If we have a decl modifying keyword, check if the next token is a valid
273-
// decl start. This is necessary to correctly handle Swift keywords that are
274-
// shared by SIL, e.g 'private' in 'sil private @foo :'. We need to make sure
275-
// this isn't considered a valid Swift decl start.
276-
if self.currentToken.tokenKind.isKeyword {
277-
if self.at(any: [], contextualKeywords: Self.declAttributeNames) {
278-
var subparser = self.lookahead()
279-
subparser.consumeAnyToken()
280-
281-
// Eat paren after modifier name; e.g. private(set)
282-
if subparser.consume(if: .leftParen) != nil {
283-
while !subparser.at(any: [.eof, .rightBrace, .poundEndifKeyword]) {
284-
if subparser.consume(if: .rightParen) != nil {
285-
break
286-
}
287-
288-
// If we found the start of a decl while trying to skip over the
289-
// paren, then we have something incomplete like 'private('. Return
290-
// true for better recovery.
291-
if subparser.isStartOfDeclaration() {
292-
return true
293-
}
294-
295-
subparser.consumeAnyToken()
296-
}
297-
}
298-
return subparser.isStartOfDeclaration()
299-
}
300-
}
301-
302-
// Otherwise, the only hard case left is the identifier case.
303-
guard self.at(.identifier) else {
304-
return true
305-
}
306-
307-
// If this is an operator declaration, handle it.
308-
if case .operatorKeyword = self.peek().tokenKind,
309-
(self.atContextualKeyword("prefix") ||
310-
self.atContextualKeyword("postfix") ||
311-
self.atContextualKeyword("infix")) {
312-
return true
313-
}
314-
315-
// If this can't possibly be a contextual keyword, then this identifier is
316-
// not interesting. Bail out.
317-
guard self.at(anyIn: ContextualDeclKeyword.self) != nil else {
318-
return false
319-
}
320-
321-
// If it might be, we do some more digging.
322-
323-
// If this is 'unowned', check to see if it is valid.
324-
let tok2 = self.peek()
325-
if self.atContextualKeyword("unowned") && tok2.tokenKind == .leftParen &&
326-
self.isParenthesizedUnowned() {
327-
var lookahead = self.lookahead()
328-
lookahead.expectIdentifierWithoutRecovery()
329-
lookahead.eat(.leftParen)
330-
lookahead.expectIdentifierWithoutRecovery()
331-
lookahead.eat(.rightParen)
332-
return lookahead.isStartOfDeclaration()
333-
}
334-
335-
if self.atContextualKeyword("actor") {
336-
if tok2.tokenKind == .identifier {
337-
return true
338-
}
339-
// actor may be somewhere in the modifier list. Eat the tokens until we get
340-
// to something that isn't the start of a decl. If that is an identifier,
341-
// it's an actor declaration, otherwise, it isn't.
342-
var lookahead = self.lookahead()
343-
repeat {
344-
lookahead.consumeAnyToken()
345-
} while lookahead.isStartOfDeclaration()
346-
return lookahead.at(.identifier)
347-
}
348-
349-
// If the next token is obviously not the start of a decl, bail early.
350-
guard CanBeDeclaratinStart(tok2) != nil else {
351-
return false
352-
}
353-
354-
// Otherwise, do a recursive parse.
355-
var next = self.lookahead()
356-
next.expectIdentifierWithoutRecovery()
357-
return next.isStartOfDeclaration()
358-
}
359-
360239
func isParenthesizedUnowned() -> Bool {
361240
assert(self.atContextualKeyword("unowned") && self.peek().tokenKind == .leftParen,
362241
"Invariant violated")

0 commit comments

Comments
 (0)