Skip to content

Commit 4738296

Browse files
authored
Merge pull request #940 from ahoppen/ahoppen/recovery-in-precedencegroup
Improve recovery in precedence groups
2 parents 1237f87 + e496f30 commit 4738296

File tree

8 files changed

+92
-53
lines changed

8 files changed

+92
-53
lines changed

CodeGeneration/Sources/SyntaxSupport/gyb_generated/DeclNodes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,7 @@ public let DECL_NODES: [Node] = [
12581258
]),
12591259

12601260
Node(name: "OperatorDecl",
1261-
nameForDiagnostics: "operator",
1261+
nameForDiagnostics: "operator declaration",
12621262
description: "A Swift `operator` declaration.",
12631263
kind: "Decl",
12641264
traits: [

Sources/SwiftParser/Declarations.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,12 +2153,19 @@ extension Parser {
21532153
let assignmentKeyword = self.eat(handle)
21542154
let (unexpectedBeforeColon, colon) = self.expect(.colon)
21552155
let (unexpectedBeforeFlag, flag) = self.expectAny([.trueKeyword, .falseKeyword], default: .trueKeyword)
2156+
let unexpectedAfterFlag: RawUnexpectedNodesSyntax?
2157+
if flag.isMissing, let unexpectedIdentifier = self.consume(if: .identifier, where: { !$0.isAtStartOfLine }) {
2158+
unexpectedAfterFlag = RawUnexpectedNodesSyntax([unexpectedIdentifier], arena: self.arena)
2159+
} else {
2160+
unexpectedAfterFlag = nil
2161+
}
21562162
elements.append(RawSyntax(RawPrecedenceGroupAssignmentSyntax(
21572163
assignmentKeyword: assignmentKeyword,
21582164
unexpectedBeforeColon,
21592165
colon: colon,
21602166
unexpectedBeforeFlag,
21612167
flag: flag,
2168+
unexpectedAfterFlag,
21622169
arena: self.arena
21632170
)))
21642171
case (.higherThan, let handle)?,

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
436436
return .visitChildren
437437
}
438438

439+
public override func visit(_ node: PrecedenceGroupAssignmentSyntax) -> SyntaxVisitorContinueKind {
440+
if shouldSkip(node) {
441+
return .skipChildren
442+
}
443+
if let unexpected = node.unexpectedBetweenColonAndFlag ?? node.unexpectedAfterFlag, node.flag.presence == .missing {
444+
addDiagnostic(unexpected, .invalidFlagAfterPrecedenceGroupAssignment, handledNodes: [unexpected.id, node.flag.id])
445+
}
446+
return .visitChildren
447+
}
448+
439449
public override func visit(_ node: ReturnStmtSyntax) -> SyntaxVisitorContinueKind {
440450
if shouldSkip(node) {
441451
return .skipChildren

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public enum StaticParserError: String, DiagnosticMessage {
7575
case defaultOutsideOfSwitch = "'default' label can only appear inside a 'switch' statement"
7676
case editorPlaceholderInSourceFile = "editor placeholder in source file"
7777
case expectedExpressionAfterTry = "expected expression after 'try'"
78+
case invalidFlagAfterPrecedenceGroupAssignment = "expected 'true' or 'false' after 'assignment'"
7879
case missingColonInTernaryExprDiagnostic = "expected ':' after '? ...' in ternary expression"
7980
case operatorShouldBeDeclaredWithoutBody = "operator should no longer be declared with body"
8081
case standaloneSemicolonStatement = "standalone ';' statements are not allowed"

Sources/SwiftSyntax/gyb_generated/SyntaxEnum.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ public enum SyntaxEnum {
598598
case .enumDecl:
599599
return "enum"
600600
case .operatorDecl:
601-
return "operator"
601+
return "operator declaration"
602602
case .designatedTypeList:
603603
return nil
604604
case .designatedTypeElement:

Sources/SwiftSyntax/gyb_generated/syntax_nodes/SyntaxNodes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10172,7 +10172,7 @@ public struct OperatorPrecedenceAndTypesSyntax: SyntaxProtocol, SyntaxHashable {
1017210172
case 2:
1017310173
return nil
1017410174
case 3:
10175-
return nil
10175+
return "precedence group"
1017610176
case 4:
1017710177
return nil
1017810178
case 5:

Tests/SwiftParserTest/translated/OperatorDeclTests.swift

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ final class OperatorDeclTests: XCTestCase {
142142
prefix operator1️⃣
143143
""",
144144
diagnostics: [
145-
DiagnosticSpec(message: "expected name in operator")
145+
DiagnosticSpec(message: "expected name in operator declaration")
146146
]
147147
)
148148
}
@@ -154,7 +154,7 @@ final class OperatorDeclTests: XCTestCase {
154154
prefix operator %%+
155155
""",
156156
diagnostics: [
157-
DiagnosticSpec(message: "unexpected code before operator"),
157+
DiagnosticSpec(message: "unexpected code before operator declaration"),
158158
]
159159
)
160160
}
@@ -208,7 +208,6 @@ final class OperatorDeclTests: XCTestCase {
208208
infix operator --1️⃣aa
209209
""",
210210
diagnostics: [
211-
// TODO: Old parser expected error on line 1: 'aa' is considered an identifier and must not appear within an operator name
212211
DiagnosticSpec(message: "'aa' is considered an identifier and must not appear within an operator name", fixIts: ["remove 'aa'"])
213212
], fixedSource: "infix operator --"
214213
)
@@ -220,7 +219,6 @@ final class OperatorDeclTests: XCTestCase {
220219
infix operator 1️⃣aa--: A
221220
""",
222221
diagnostics: [
223-
// TODO: Old parser expected error on line 1: 'aa' is considered an identifier and must not appear within an operator name
224222
DiagnosticSpec(message: "'aa' is considered an identifier and must not appear within an operator name", highlight: "aa", fixIts: ["remove 'aa'"]),
225223
], fixedSource: "infix operator --: A"
226224
)
@@ -302,51 +300,91 @@ final class OperatorDeclTests: XCTestCase {
302300
infix operator --- : 1️⃣;
303301
""",
304302
diagnostics: [
305-
// TODO: Old parser expected error on line 3: expected precedence group name after ':' in operator declaration
306-
DiagnosticSpec(message: "expected identifier in operator"),
303+
DiagnosticSpec(message: "expected precedence group in operator declaration"),
307304
]
308305
)
309306
}
310307

311-
func testOperatorDecl15() {
308+
func testOperatorDecl15a() {
312309
AssertParse(
313310
"""
314311
precedencegroup 1️⃣{
315312
associativity: right
316313
}
314+
""",
315+
diagnostics: [
316+
DiagnosticSpec(message: "expected name in precedencegroup"),
317+
]
318+
)
319+
}
320+
321+
func testOperatorDecl15b() {
322+
AssertParse(
323+
"""
317324
precedencegroup A {
318-
associativity 2️⃣right
325+
associativity 1️⃣right
319326
}
327+
""",
328+
diagnostics: [
329+
DiagnosticSpec(message: "expected ':' in 'associativity' property of precedencegroup"),
330+
]
331+
)
332+
}
333+
334+
335+
func testOperatorDecl15c() {
336+
AssertParse(
337+
"""
320338
precedencegroup B {
321-
3️⃣precedence 123
339+
1️⃣precedence 123
322340
}
341+
""",
342+
diagnostics: [
343+
DiagnosticSpec(message: "unexpected code 'precedence 123' in precedencegroup"),
344+
]
345+
)
346+
}
347+
348+
349+
func testOperatorDecl15d() {
350+
AssertParse(
351+
"""
323352
precedencegroup C {
324-
associativity: sinister
353+
associativity: sinister
325354
}
355+
"""
356+
)
357+
}
358+
359+
360+
func testOperatorDecl15e() {
361+
AssertParse(
362+
"""
326363
precedencegroup D {
327-
assignment: 4️⃣no
364+
assignment: 1️⃣no
328365
}
366+
""",
367+
diagnostics: [
368+
DiagnosticSpec(message: "expected 'true' or 'false' after 'assignment'"),
369+
]
370+
)
371+
}
372+
373+
374+
func testOperatorDecl15() {
375+
AssertParse(
376+
"""
329377
precedencegroup E {
330-
higherThan:5️⃣
378+
higherThan:1️⃣
331379
}
332380
""",
333381
diagnostics: [
334-
// TODO: Old parser expected error on line 1: expected identifier after 'precedencegroup'
335-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected name in precedencegroup"),
336-
// TODO: Old parser expected error on line 5: expected colon after attribute name in precedence group
337-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ':' in 'associativity' property of precedencegroup"),
338-
// TODO: Old parser expected error on line 8: 'precedence' is not a valid precedence group attribute
339-
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code 'precedence 123' in precedencegroup"),
340-
// TODO: Old parser expected error on line 11: expected 'none', 'left', or 'right' after 'associativity'
341-
// TODO: Old parser expected error on line 14: expected 'true' or 'false' after 'assignment'
342-
DiagnosticSpec(locationMarker: "4️⃣", message: "expected 'true' in 'assignment' property of precedencegroup"),
343-
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code 'no' in precedencegroup"),
344-
DiagnosticSpec(locationMarker: "5️⃣", message: "expected name in 'relation' property of precedencegroup"),
345-
// TODO: Old parser expected error on line 18: expected name of related precedence group after 'higherThan'
382+
DiagnosticSpec(message: "expected name in 'relation' property of precedencegroup"),
346383
]
347384
)
348385
}
349386

387+
350388
func testOperatorDecl16() {
351389
AssertParse(
352390
"""
@@ -364,10 +402,7 @@ final class OperatorDeclTests: XCTestCase {
364402
associativity: none
365403
associativity: left
366404
}
367-
""",
368-
diagnostics: [
369-
// TODO: Old parser expected error on line 3: 'associativity' attribute for precedence group declared multiple times
370-
]
405+
"""
371406
)
372407
}
373408

@@ -378,10 +413,7 @@ final class OperatorDeclTests: XCTestCase {
378413
assignment: true
379414
assignment: false
380415
}
381-
""",
382-
diagnostics: [
383-
// TODO: Old parser expected error on line 3: 'assignment' attribute for precedence group declared multiple times
384-
]
416+
"""
385417
)
386418
}
387419

@@ -391,34 +423,25 @@ final class OperatorDeclTests: XCTestCase {
391423
class Foo {
392424
infix operator |||
393425
}
394-
""",
395-
diagnostics: [
396-
// TODO: Old parser expected error on line 2: 'operator' may only be declared at file scope
397-
]
426+
"""
398427
)
399428
}
400429

401430
func testOperatorDecl20() {
402431
AssertParse(
403432
"""
404433
infix operator **<< : UndeclaredPrecedenceGroup
405-
""",
406-
diagnostics: [
407-
// TODO: Old parser expected error on line 1: unknown precedence group 'UndeclaredPrecedenceGroup'
408-
]
434+
"""
409435
)
410436
}
411437

412438
func testOperatorDecl21() {
439+
// TODO: We should not allow specification of multiple precedence groups
413440
AssertParse(
414441
"""
415442
protocol Proto {}
416443
infix operator *<*< : F, Proto
417-
""",
418-
diagnostics: [
419-
// TODO: Old parser expected error on line 2: consecutive statements on a line must be separated by ';'
420-
// TODO: Old parser expected error on line 2: expected expression
421-
]
444+
"""
422445
)
423446
}
424447

@@ -436,9 +459,7 @@ final class OperatorDeclTests: XCTestCase {
436459
postfix operator ++:1️⃣
437460
""",
438461
diagnostics: [
439-
// TODO: Old parser expected error on line 1: only infix operators may declare a precedence, Fix-It replacements: 20 - 21 = ''
440-
DiagnosticSpec(message: "expected identifier in operator"),
441-
// TODO: Old parser expected error on line 2: expected precedence group name after ':' in operator declaration
462+
DiagnosticSpec(message: "expected precedence group in operator declaration"),
442463
]
443464
)
444465
}

gyb_syntax_support/DeclNodes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@
726726
]),
727727

728728
# operator-decl -> attribute? modifiers? 'operator' operator
729-
Node('OperatorDecl', name_for_diagnostics='operator', kind='Decl',
729+
Node('OperatorDecl', name_for_diagnostics='operator declaration', kind='Decl',
730730
traits=['IdentifiedDecl'],
731731
description='A Swift `operator` declaration.',
732732
children=[
@@ -773,7 +773,7 @@
773773
''',
774774
children=[
775775
Child('Colon', kind='ColonToken'),
776-
Child('PrecedenceGroup', kind='IdentifierToken',
776+
Child('PrecedenceGroup', name_for_diagnostics='precedence group', kind='IdentifierToken',
777777
description='''
778778
The precedence group for this operator
779779
'''),

0 commit comments

Comments
 (0)