Skip to content

Commit 46b8901

Browse files
committed
Add diagnostic for wrong inheritance
1 parent aa65da5 commit 46b8901

File tree

6 files changed

+168
-70
lines changed

6 files changed

+168
-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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,53 @@ 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+
var replaceTokens = [leftParen]
1291+
1292+
if let rightParen = node.unexpectedAfterInheritedTypeCollection?.tokens(satisfying: { $0.tokenKind == .rightParen }).first,
1293+
let lastToken = node.inheritedTypeCollection.lastToken(viewMode: .sourceAccurate)
1294+
{
1295+
handledNodes += [rightParen.id]
1296+
changes += [
1297+
.makeMissing(rightParen),
1298+
FixIt.MultiNodeChange(.replaceTrailingTrivia(token: lastToken, newTrivia: .space)),
1299+
]
1300+
1301+
replaceTokens += [rightParen]
1302+
}
1303+
1304+
addDiagnostic(
1305+
unexpected,
1306+
.expectedColonClass,
1307+
fixIts: [
1308+
FixIt(
1309+
message: ReplaceTokensFixIt(replaceTokens: replaceTokens, replacements: [.colonToken()]),
1310+
changes: changes
1311+
)
1312+
],
1313+
handledNodes: handledNodes
1314+
)
1315+
}
1316+
return .visitChildren
1317+
}
1318+
12721319
public override func visit(_ node: TypeInitializerClauseSyntax) -> SyntaxVisitorContinueKind {
12731320
if shouldSkip(node) {
12741321
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: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,9 +1570,12 @@ 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+
fixIts: ["replace '()' with ':'"])
1575+
],
1576+
fixedSource: """
1577+
class WrongInheritanceClause1: Int {}
1578+
"""
15761579
)
15771580
}
15781581

@@ -1582,9 +1585,12 @@ final class RecoveryTests: XCTestCase {
15821585
class WrongInheritanceClause21️⃣(Base2<Int>) {}
15831586
""",
15841587
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-
]
1588+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1589+
fixIts: ["replace '()' with ':'"])
1590+
],
1591+
fixedSource: """
1592+
class WrongInheritanceClause2: Base2<Int> {}
1593+
"""
15881594
)
15891595
}
15901596

@@ -1594,9 +1600,12 @@ final class RecoveryTests: XCTestCase {
15941600
class WrongInheritanceClause3<T>1️⃣(SubModule.Base1) where T:AnyObject {}
15951601
""",
15961602
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-
]
1603+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1604+
fixIts: ["replace '()' with ':'"])
1605+
],
1606+
fixedSource: """
1607+
class WrongInheritanceClause3<T>: SubModule.Base1 where T:AnyObject {}
1608+
"""
16001609
)
16011610
}
16021611

@@ -1606,9 +1615,12 @@ final class RecoveryTests: XCTestCase {
16061615
class WrongInheritanceClause41️⃣(SubModule.Base2<Int>) {}
16071616
""",
16081617
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-
]
1618+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1619+
fixIts: ["replace '()' with ':'"])
1620+
],
1621+
fixedSource: """
1622+
class WrongInheritanceClause4: SubModule.Base2<Int> {}
1623+
"""
16121624
)
16131625
}
16141626

@@ -1618,47 +1630,49 @@ final class RecoveryTests: XCTestCase {
16181630
class WrongInheritanceClause5<T>1️⃣(SubModule.Base2<Int>) where T:AnyObject {}
16191631
""",
16201632
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-
]
1633+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1634+
fixIts: ["replace '()' with ':'"])
1635+
],
1636+
fixedSource: """
1637+
class WrongInheritanceClause5<T>: SubModule.Base2<Int> where T:AnyObject {}
1638+
"""
16241639
)
16251640
}
16261641

16271642
func testRecovery130() {
16281643
assertParse(
16291644
"""
1630-
class WrongInheritanceClause61️⃣(Int 2️⃣{}3️⃣
1645+
class WrongInheritanceClause61️⃣(Int {}
16311646
""",
16321647
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-
]
1648+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1649+
fixIts: ["replace '(' with ':'"])
1650+
],
1651+
fixedSource: """
1652+
class WrongInheritanceClause6: Int {}
1653+
"""
16391654
)
16401655
}
16411656

16421657
func testRecovery131() {
16431658
assertParse(
16441659
"""
1645-
class WrongInheritanceClause7<T>1️⃣(Int 2️⃣where T:AnyObject {}
1660+
class WrongInheritanceClause7<T>1️⃣(Int where T:AnyObject {}
16461661
""",
16471662
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-
]
1663+
DiagnosticSpec(message: "expected ':' to begin inheritance clause",
1664+
fixIts: ["replace '(' with ':'"])
1665+
],
1666+
fixedSource: """
1667+
class WrongInheritanceClause7<T>: Int where T:AnyObject {}
1668+
"""
16551669
)
16561670
}
16571671

16581672
func testRecovery132() {
1673+
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16591674
assertParse(
16601675
"""
1661-
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16621676
Base=1 as Base=1
16631677
"""
16641678
)

0 commit comments

Comments
 (0)