Skip to content

Commit 802ea1a

Browse files
committed
Add diagnostic for wrong inheritance
1 parent aa65da5 commit 802ea1a

File tree

6 files changed

+158
-70
lines changed

6 files changed

+158
-70
lines changed

Sources/SwiftParser/Nominals.swift

Lines changed: 36 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,18 @@ 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+
// Parse python style inheritance clause and replace parentheses with a colon
280+
if let leftParen = self.consume(if: .leftParen) {
281+
unexpectedBeforeColon = RawUnexpectedNodesSyntax([leftParen], arena: self.arena)
282+
colon = missingToken(.colon)
283+
} else {
284+
unexpectedBeforeColon = nil
285+
colon = self.consume(if: .colon) ?? missingToken(.colon)
286+
}
287+
277288
var elements = [RawInheritedTypeSyntax]()
278289
do {
279290
var keepGoing: RawTokenSyntax? = nil
@@ -306,10 +317,22 @@ extension Parser {
306317
)
307318
} while keepGoing != nil && loopProgress.evaluate(currentToken)
308319
}
320+
321+
let unexpectedAfterInheritedTypeCollection: RawUnexpectedNodesSyntax?
322+
323+
// If it contains a left paren, then it might be a python style inheritance clause.
324+
// Left paren have been replaced with colon
325+
if unexpectedBeforeColon?.containsToken(where: { $0.tokenKind == .leftParen }) == true, let rightBrace = self.consume(if: .rightParen) {
326+
unexpectedAfterInheritedTypeCollection = RawUnexpectedNodesSyntax(elements: [RawSyntax(rightBrace)], arena: self.arena)
327+
} else {
328+
unexpectedAfterInheritedTypeCollection = nil
329+
}
330+
309331
return RawTypeInheritanceClauseSyntax(
310332
unexpectedBeforeColon,
311333
colon: colon,
312334
inheritedTypeCollection: RawInheritedTypeListSyntax(elements: elements, arena: self.arena),
335+
unexpectedAfterInheritedTypeCollection,
313336
arena: self.arena
314337
)
315338
}
@@ -352,3 +375,14 @@ extension Parser {
352375
)
353376
}
354377
}
378+
379+
extension Parser {
380+
private mutating func isAtPythonStyleInheritanceClause() -> Bool {
381+
guard self.at(.leftParen) else { return false }
382+
return self.withLookahead {
383+
$0.consume(if: .leftParen)
384+
guard $0.canParseType() else { return false }
385+
return $0.at(.rightParen, .keyword(.where), .leftBrace) || $0.at(.eof)
386+
}
387+
}
388+
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,50 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
12691269
return handleEffectSpecifiers(node)
12701270
}
12711271

1272+
public override func visit(_ node: TypeInheritanceClauseSyntax) -> SyntaxVisitorContinueKind {
1273+
if shouldSkip(node) {
1274+
return .skipChildren
1275+
}
1276+
1277+
if let unexpected = node.unexpectedBeforeColon,
1278+
let leftParen = unexpected.tokens(satisfying: { $0.tokenKind == .leftParen }).first
1279+
{
1280+
1281+
var handledNodes: [SyntaxIdentifier] = [
1282+
leftParen.id, node.colon.id,
1283+
]
1284+
1285+
var changes: [FixIt.MultiNodeChange] = [
1286+
.makePresent(node.colon, trailingTrivia: .space),
1287+
.makeMissing(unexpected),
1288+
]
1289+
1290+
if let rightParen = node.unexpectedAfterInheritedTypeCollection?.tokens(satisfying: { $0.tokenKind == .rightParen }).first,
1291+
let lastToken = node.inheritedTypeCollection.lastToken(viewMode: .sourceAccurate)
1292+
{
1293+
handledNodes += [rightParen.id]
1294+
changes += [
1295+
.makeMissing(rightParen),
1296+
FixIt.MultiNodeChange(.replaceTrailingTrivia(token: lastToken, newTrivia: .space)),
1297+
]
1298+
}
1299+
1300+
addDiagnostic(
1301+
unexpected,
1302+
.expectedColonClass,
1303+
fixIts: [
1304+
FixIt(
1305+
message: ReplaceTokensFixIt(replaceTokens: [], replacements: [.colonToken()]),
1306+
changes: changes
1307+
)
1308+
],
1309+
handledNodes: handledNodes
1310+
)
1311+
}
1312+
1313+
return .visitChildren
1314+
}
1315+
12721316
public override func visit(_ node: TypeInitializerClauseSyntax) -> SyntaxVisitorContinueKind {
12731317
if shouldSkip(node) {
12741318
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
}

Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
118118
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
119119
case \ActorDeclSyntax.genericWhereClause:
120120
return "genericWhereClause"
121-
case \ActorDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
122-
return "unexpectedBetweenGenericWhereClauseAndMembers"
123-
case \ActorDeclSyntax.members:
124-
return "members"
125-
case \ActorDeclSyntax.unexpectedAfterMembers:
126-
return "unexpectedAfterMembers"
121+
case \ActorDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
122+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
123+
case \ActorDeclSyntax.memberBlock:
124+
return "memberBlock"
125+
case \ActorDeclSyntax.unexpectedAfterMemberBlock:
126+
return "unexpectedAfterMemberBlock"
127127
case \ArrayElementSyntax.unexpectedBeforeExpression:
128128
return "unexpectedBeforeExpression"
129129
case \ArrayElementSyntax.expression:
@@ -468,12 +468,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
468468
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
469469
case \ClassDeclSyntax.genericWhereClause:
470470
return "genericWhereClause"
471-
case \ClassDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
472-
return "unexpectedBetweenGenericWhereClauseAndMembers"
473-
case \ClassDeclSyntax.members:
474-
return "members"
475-
case \ClassDeclSyntax.unexpectedAfterMembers:
476-
return "unexpectedAfterMembers"
471+
case \ClassDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
472+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
473+
case \ClassDeclSyntax.memberBlock:
474+
return "memberBlock"
475+
case \ClassDeclSyntax.unexpectedAfterMemberBlock:
476+
return "unexpectedAfterMemberBlock"
477477
case \ClassRestrictionTypeSyntax.unexpectedBeforeClassKeyword:
478478
return "unexpectedBeforeClassKeyword"
479479
case \ClassRestrictionTypeSyntax.classKeyword:
@@ -1168,12 +1168,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
11681168
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
11691169
case \EnumDeclSyntax.genericWhereClause:
11701170
return "genericWhereClause"
1171-
case \EnumDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
1172-
return "unexpectedBetweenGenericWhereClauseAndMembers"
1173-
case \EnumDeclSyntax.members:
1174-
return "members"
1175-
case \EnumDeclSyntax.unexpectedAfterMembers:
1176-
return "unexpectedAfterMembers"
1171+
case \EnumDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
1172+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
1173+
case \EnumDeclSyntax.memberBlock:
1174+
return "memberBlock"
1175+
case \EnumDeclSyntax.unexpectedAfterMemberBlock:
1176+
return "unexpectedAfterMemberBlock"
11771177
case \ExposeAttributeArgumentsSyntax.unexpectedBeforeLanguage:
11781178
return "unexpectedBeforeLanguage"
11791179
case \ExposeAttributeArgumentsSyntax.language:
@@ -1246,12 +1246,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
12461246
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
12471247
case \ExtensionDeclSyntax.genericWhereClause:
12481248
return "genericWhereClause"
1249-
case \ExtensionDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
1250-
return "unexpectedBetweenGenericWhereClauseAndMembers"
1251-
case \ExtensionDeclSyntax.members:
1252-
return "members"
1253-
case \ExtensionDeclSyntax.unexpectedAfterMembers:
1254-
return "unexpectedAfterMembers"
1249+
case \ExtensionDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
1250+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
1251+
case \ExtensionDeclSyntax.memberBlock:
1252+
return "memberBlock"
1253+
case \ExtensionDeclSyntax.unexpectedAfterMemberBlock:
1254+
return "unexpectedAfterMemberBlock"
12551255
case \FallthroughStmtSyntax.unexpectedBeforeFallthroughKeyword:
12561256
return "unexpectedBeforeFallthroughKeyword"
12571257
case \FallthroughStmtSyntax.fallthroughKeyword:
@@ -2586,12 +2586,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
25862586
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
25872587
case \ProtocolDeclSyntax.genericWhereClause:
25882588
return "genericWhereClause"
2589-
case \ProtocolDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
2590-
return "unexpectedBetweenGenericWhereClauseAndMembers"
2591-
case \ProtocolDeclSyntax.members:
2592-
return "members"
2593-
case \ProtocolDeclSyntax.unexpectedAfterMembers:
2594-
return "unexpectedAfterMembers"
2589+
case \ProtocolDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
2590+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
2591+
case \ProtocolDeclSyntax.memberBlock:
2592+
return "memberBlock"
2593+
case \ProtocolDeclSyntax.unexpectedAfterMemberBlock:
2594+
return "unexpectedAfterMemberBlock"
25952595
case \QualifiedDeclNameSyntax.unexpectedBeforeBaseType:
25962596
return "unexpectedBeforeBaseType"
25972597
case \QualifiedDeclNameSyntax.baseType:
@@ -2776,12 +2776,12 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
27762776
return "unexpectedBetweenInheritanceClauseAndGenericWhereClause"
27772777
case \StructDeclSyntax.genericWhereClause:
27782778
return "genericWhereClause"
2779-
case \StructDeclSyntax.unexpectedBetweenGenericWhereClauseAndMembers:
2780-
return "unexpectedBetweenGenericWhereClauseAndMembers"
2781-
case \StructDeclSyntax.members:
2782-
return "members"
2783-
case \StructDeclSyntax.unexpectedAfterMembers:
2784-
return "unexpectedAfterMembers"
2779+
case \StructDeclSyntax.unexpectedBetweenGenericWhereClauseAndMemberBlock:
2780+
return "unexpectedBetweenGenericWhereClauseAndMemberBlock"
2781+
case \StructDeclSyntax.memberBlock:
2782+
return "memberBlock"
2783+
case \StructDeclSyntax.unexpectedAfterMemberBlock:
2784+
return "unexpectedAfterMemberBlock"
27852785
case \SubscriptDeclSyntax.unexpectedBeforeAttributes:
27862786
return "unexpectedBeforeAttributes"
27872787
case \SubscriptDeclSyntax.attributes:

Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
791791
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))
792792
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.leftParen)]))
793793
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
794-
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier), .keyword("set")]))
794+
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.identifier)]))
795795
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
796796
assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.rightParen)]))
797797
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,9 +1570,11 @@ final class RecoveryTests: XCTestCase {
15701570
class WrongInheritanceClause11️⃣(Int) {}
15711571
""",
15721572
diagnostics: [
1573-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 34 - 35 = ''
1574-
DiagnosticSpec(message: "unexpected code '(Int)' in class")
1575-
]
1573+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1574+
],
1575+
fixedSource: """
1576+
class WrongInheritanceClause1: Int {}
1577+
"""
15761578
)
15771579
}
15781580

@@ -1582,9 +1584,11 @@ final class RecoveryTests: XCTestCase {
15821584
class WrongInheritanceClause21️⃣(Base2<Int>) {}
15831585
""",
15841586
diagnostics: [
1585-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 41 - 42 = ''
1586-
DiagnosticSpec(message: "unexpected code '(Base2<Int>)' in class")
1587-
]
1587+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1588+
],
1589+
fixedSource: """
1590+
class WrongInheritanceClause2: Base2<Int> {}
1591+
"""
15881592
)
15891593
}
15901594

@@ -1594,9 +1598,11 @@ final class RecoveryTests: XCTestCase {
15941598
class WrongInheritanceClause3<T>1️⃣(SubModule.Base1) where T:AnyObject {}
15951599
""",
15961600
diagnostics: [
1597-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 49 - 50 = ''
1598-
DiagnosticSpec(message: "unexpected code '(SubModule.Base1) where T:AnyObject' in class")
1599-
]
1601+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1602+
],
1603+
fixedSource: """
1604+
class WrongInheritanceClause3<T>: SubModule.Base1 where T:AnyObject {}
1605+
"""
16001606
)
16011607
}
16021608

@@ -1606,9 +1612,11 @@ final class RecoveryTests: XCTestCase {
16061612
class WrongInheritanceClause41️⃣(SubModule.Base2<Int>) {}
16071613
""",
16081614
diagnostics: [
1609-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 51 - 52 = ''
1610-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>)' in class")
1611-
]
1615+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1616+
],
1617+
fixedSource: """
1618+
class WrongInheritanceClause4: SubModule.Base2<Int> {}
1619+
"""
16121620
)
16131621
}
16141622

@@ -1618,47 +1626,46 @@ final class RecoveryTests: XCTestCase {
16181626
class WrongInheritanceClause5<T>1️⃣(SubModule.Base2<Int>) where T:AnyObject {}
16191627
""",
16201628
diagnostics: [
1621-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 54 - 55 = ''
1622-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>) where T:AnyObject' in class")
1623-
]
1629+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1630+
],
1631+
fixedSource: """
1632+
class WrongInheritanceClause5<T>: SubModule.Base2<Int> where T:AnyObject {}
1633+
"""
16241634
)
16251635
}
16261636

16271637
func testRecovery130() {
16281638
assertParse(
16291639
"""
1630-
class WrongInheritanceClause61️⃣(Int 2️⃣{}3️⃣
1640+
class WrongInheritanceClause61️⃣(Int {}
16311641
""",
16321642
diagnostics: [
1633-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': '
1634-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class", fixIts: ["insert '{'"]),
1635-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable", fixIts: ["insert 'var'"]),
1636-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern", fixIts: ["insert ')'"]),
1637-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '}' to end class", fixIts: ["insert '}'"]),
1638-
]
1643+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1644+
],
1645+
fixedSource: """
1646+
class WrongInheritanceClause6: Int {}
1647+
"""
16391648
)
16401649
}
16411650

16421651
func testRecovery131() {
16431652
assertParse(
16441653
"""
1645-
class WrongInheritanceClause7<T>1️⃣(Int 2️⃣where T:AnyObject {}
1654+
class WrongInheritanceClause7<T>1️⃣(Int where T:AnyObject {}
16461655
""",
16471656
diagnostics: [
1648-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': '
1649-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class", fixIts: ["insert '{'"]),
1650-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable", fixIts: ["insert 'var'"]),
1651-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern", fixIts: ["insert ')'"]),
1652-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected '}' to end class", fixIts: ["insert '}'"]),
1653-
DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'where T:AnyObject {}' at top level"),
1654-
]
1657+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1658+
],
1659+
fixedSource: """
1660+
class WrongInheritanceClause7<T>: Int where T:AnyObject {}
1661+
"""
16551662
)
16561663
}
16571664

16581665
func testRecovery132() {
1666+
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16591667
assertParse(
16601668
"""
1661-
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16621669
Base=1 as Base=1
16631670
"""
16641671
)

0 commit comments

Comments
 (0)