Skip to content

Commit 731e46a

Browse files
committed
Merge pull request #1450 from kimdv/kimdv/add-diagnostic-for-wrong-inheritance
Add diagnostic for wrong inheritance
1 parent 83c2be9 commit 731e46a

File tree

4 files changed

+144
-33
lines changed

4 files changed

+144
-33
lines changed

Sources/SwiftParser/Nominals.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ extension Parser {
240240
}
241241

242242
let inheritance: RawTypeInheritanceClauseSyntax?
243-
if self.at(.colon) {
243+
if self.at(.colon) || self.isAtPythonStyleInheritanceClause() {
244244
inheritance = self.parseInheritance()
245245
} else {
246246
inheritance = nil
@@ -273,7 +273,20 @@ extension Parser {
273273
/// Parse an inheritance clause.
274274
@_spi(RawSyntax)
275275
public mutating func parseInheritance() -> RawTypeInheritanceClauseSyntax {
276-
let (unexpectedBeforeColon, colon) = self.expect(.colon)
276+
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
277+
let colon: RawTokenSyntax
278+
279+
let isPythonStyleInheritanceClause: Bool
280+
// Parse python style inheritance clause and replace parentheses with a colon
281+
if let leftParen = self.consume(if: .leftParen) {
282+
unexpectedBeforeColon = RawUnexpectedNodesSyntax([leftParen], arena: self.arena)
283+
colon = missingToken(.colon)
284+
isPythonStyleInheritanceClause = true
285+
} else {
286+
(unexpectedBeforeColon, colon) = self.expect(.colon)
287+
isPythonStyleInheritanceClause = false
288+
}
289+
277290
var elements = [RawInheritedTypeSyntax]()
278291
do {
279292
var keepGoing: RawTokenSyntax? = nil
@@ -301,10 +314,21 @@ extension Parser {
301314
)
302315
} while keepGoing != nil && loopProgress.evaluate(currentToken)
303316
}
317+
318+
let unexpectedAfterInheritedTypeCollection: RawUnexpectedNodesSyntax?
319+
320+
// If it is a Python style inheritance clause, then consume a right paren if there is one.
321+
if isPythonStyleInheritanceClause, let rightParen = self.consume(if: .rightParen) {
322+
unexpectedAfterInheritedTypeCollection = RawUnexpectedNodesSyntax(elements: [RawSyntax(rightParen)], arena: self.arena)
323+
} else {
324+
unexpectedAfterInheritedTypeCollection = nil
325+
}
326+
304327
return RawTypeInheritanceClauseSyntax(
305328
unexpectedBeforeColon,
306329
colon: colon,
307330
inheritedTypeCollection: RawInheritedTypeListSyntax(elements: elements, arena: self.arena),
331+
unexpectedAfterInheritedTypeCollection,
308332
arena: self.arena
309333
)
310334
}
@@ -347,3 +371,14 @@ extension Parser {
347371
)
348372
}
349373
}
374+
375+
extension Parser {
376+
private mutating func isAtPythonStyleInheritanceClause() -> Bool {
377+
guard self.at(.leftParen) else { return false }
378+
return self.withLookahead {
379+
$0.consume(if: .leftParen)
380+
guard $0.canParseType() else { return false }
381+
return $0.at(.rightParen, .keyword(.where), .leftBrace) || $0.at(.eof)
382+
}
383+
}
384+
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,51 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
12071207
return handleEffectSpecifiers(node)
12081208
}
12091209

1210+
public override func visit(_ node: TypeInheritanceClauseSyntax) -> SyntaxVisitorContinueKind {
1211+
if shouldSkip(node) {
1212+
return .skipChildren
1213+
}
1214+
1215+
if let unexpected = node.unexpectedBeforeColon,
1216+
let leftParen = unexpected.onlyToken(where: { $0.tokenKind == .leftParen })
1217+
{
1218+
1219+
var handledNodes: [SyntaxIdentifier] = [
1220+
leftParen.id,
1221+
node.colon.id,
1222+
]
1223+
1224+
var changes: [FixIt.MultiNodeChange] = [
1225+
.makePresent(node.colon),
1226+
.makeMissing(unexpected),
1227+
]
1228+
1229+
var replaceTokens = [leftParen]
1230+
1231+
if let rightParen = node.unexpectedAfterInheritedTypeCollection?.onlyToken(where: { $0.tokenKind == .rightParen }) {
1232+
handledNodes += [rightParen.id]
1233+
changes += [
1234+
.makeMissing(rightParen)
1235+
]
1236+
1237+
replaceTokens += [rightParen]
1238+
}
1239+
1240+
addDiagnostic(
1241+
unexpected,
1242+
.expectedColonClass,
1243+
fixIts: [
1244+
FixIt(
1245+
message: ReplaceTokensFixIt(replaceTokens: replaceTokens, replacements: [.colonToken()]),
1246+
changes: changes
1247+
)
1248+
],
1249+
handledNodes: handledNodes
1250+
)
1251+
}
1252+
return .visitChildren
1253+
}
1254+
12101255
public override func visit(_ node: TypeInitializerClauseSyntax) -> SyntaxVisitorContinueKind {
12111256
if shouldSkip(node) {
12121257
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ extension DiagnosticMessage where Self == StaticParserError {
125125
public static var escapedNewlineAtLatlineOfMultiLineStringLiteralNotAllowed: Self {
126126
.init("escaped newline at the last line of a multi-line string literal is not allowed")
127127
}
128+
public static var expectedColonClass: Self {
129+
.init("expected ':' to begin inheritance clause")
130+
}
128131
public static var expectedExpressionAfterTry: Self {
129132
.init("expected expression after 'try'")
130133
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,9 +1551,14 @@ final class RecoveryTests: XCTestCase {
15511551
class WrongInheritanceClause11️⃣(Int) {}
15521552
""",
15531553
diagnostics: [
1554-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 34 - 35 = ''
1555-
DiagnosticSpec(message: "unexpected code '(Int)' in class")
1556-
]
1554+
DiagnosticSpec(
1555+
message: "expected ':' to begin inheritance clause",
1556+
fixIts: ["replace '()' with ':'"]
1557+
)
1558+
],
1559+
fixedSource: """
1560+
class WrongInheritanceClause1: Int {}
1561+
"""
15571562
)
15581563
}
15591564

@@ -1563,9 +1568,14 @@ final class RecoveryTests: XCTestCase {
15631568
class WrongInheritanceClause21️⃣(Base2<Int>) {}
15641569
""",
15651570
diagnostics: [
1566-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 41 - 42 = ''
1567-
DiagnosticSpec(message: "unexpected code '(Base2<Int>)' in class")
1568-
]
1571+
DiagnosticSpec(
1572+
message: "expected ':' to begin inheritance clause",
1573+
fixIts: ["replace '()' with ':'"]
1574+
)
1575+
],
1576+
fixedSource: """
1577+
class WrongInheritanceClause2: Base2<Int>{}
1578+
"""
15691579
)
15701580
}
15711581

@@ -1575,9 +1585,14 @@ final class RecoveryTests: XCTestCase {
15751585
class WrongInheritanceClause3<T>1️⃣(SubModule.Base1) where T:AnyObject {}
15761586
""",
15771587
diagnostics: [
1578-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 49 - 50 = ''
1579-
DiagnosticSpec(message: "unexpected code '(SubModule.Base1) where T:AnyObject' in class")
1580-
]
1588+
DiagnosticSpec(
1589+
message: "expected ':' to begin inheritance clause",
1590+
fixIts: ["replace '()' with ':'"]
1591+
)
1592+
],
1593+
fixedSource: """
1594+
class WrongInheritanceClause3<T>: SubModule.Base1 where T:AnyObject {}
1595+
"""
15811596
)
15821597
}
15831598

@@ -1587,9 +1602,14 @@ final class RecoveryTests: XCTestCase {
15871602
class WrongInheritanceClause41️⃣(SubModule.Base2<Int>) {}
15881603
""",
15891604
diagnostics: [
1590-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 51 - 52 = ''
1591-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>)' in class")
1592-
]
1605+
DiagnosticSpec(
1606+
message: "expected ':' to begin inheritance clause",
1607+
fixIts: ["replace '()' with ':'"]
1608+
)
1609+
],
1610+
fixedSource: """
1611+
class WrongInheritanceClause4: SubModule.Base2<Int>{}
1612+
"""
15931613
)
15941614
}
15951615

@@ -1599,47 +1619,55 @@ final class RecoveryTests: XCTestCase {
15991619
class WrongInheritanceClause5<T>1️⃣(SubModule.Base2<Int>) where T:AnyObject {}
16001620
""",
16011621
diagnostics: [
1602-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 54 - 55 = ''
1603-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>) where T:AnyObject' in class")
1604-
]
1622+
DiagnosticSpec(
1623+
message: "expected ':' to begin inheritance clause",
1624+
fixIts: ["replace '()' with ':'"]
1625+
)
1626+
],
1627+
fixedSource: """
1628+
class WrongInheritanceClause5<T>: SubModule.Base2<Int>where T:AnyObject {}
1629+
"""
16051630
)
16061631
}
16071632

16081633
func testRecovery130() {
16091634
assertParse(
16101635
"""
1611-
class WrongInheritanceClause61️⃣(Int 2️⃣{}3️⃣
1636+
class WrongInheritanceClause61️⃣(Int {}
16121637
""",
16131638
diagnostics: [
1614-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': '
1615-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class"),
1616-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable"),
1617-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern"),
1618-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '}' to end class"),
1619-
]
1639+
DiagnosticSpec(
1640+
message: "expected ':' to begin inheritance clause",
1641+
fixIts: ["replace '(' with ':'"]
1642+
)
1643+
],
1644+
fixedSource: """
1645+
class WrongInheritanceClause6: Int {}
1646+
"""
16201647
)
16211648
}
16221649

16231650
func testRecovery131() {
16241651
assertParse(
16251652
"""
1626-
class WrongInheritanceClause7<T>1️⃣(Int 2️⃣where T:AnyObject {}
1653+
class WrongInheritanceClause7<T>1️⃣(Int where T:AnyObject {}
16271654
""",
16281655
diagnostics: [
1629-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': '
1630-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class"),
1631-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable"),
1632-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern"),
1633-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected '}' to end class"),
1634-
DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'where T:AnyObject {}' at top level"),
1635-
]
1656+
DiagnosticSpec(
1657+
message: "expected ':' to begin inheritance clause",
1658+
fixIts: ["replace '(' with ':'"]
1659+
)
1660+
],
1661+
fixedSource: """
1662+
class WrongInheritanceClause7<T>: Int where T:AnyObject {}
1663+
"""
16361664
)
16371665
}
16381666

16391667
func testRecovery132() {
1668+
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16401669
assertParse(
16411670
"""
1642-
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16431671
Base=1 as Base=1
16441672
"""
16451673
)

0 commit comments

Comments
 (0)