Skip to content

Commit 439287b

Browse files
committed
Add diagnostic for wrong inheritance
1 parent 92178c0 commit 439287b

File tree

5 files changed

+122
-35
lines changed

5 files changed

+122
-35
lines changed

Sources/SwiftParser/Nominals.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,13 @@ extension Parser {
180180
}
181181

182182
let inheritance: RawTypeInheritanceClauseSyntax?
183-
if self.at(.colon) {
183+
if self.at(.colon)
184+
|| (self.at(.leftParen) && self.withLookahead {
185+
$0.consume(if: .leftParen)
186+
return $0.canParseType() && ($0.at(.rightParen, .keyword(.where), .leftBrace) || $0.at(.eof))
187+
}
188+
)
189+
{
184190
inheritance = self.parseInheritance()
185191
} else {
186192
inheritance = nil
@@ -213,7 +219,17 @@ extension Parser {
213219
/// Parse an inheritance clause.
214220
@_spi(RawSyntax)
215221
public mutating func parseInheritance() -> RawTypeInheritanceClauseSyntax {
216-
let (unexpectedBeforeColon, colon) = self.expect(.colon)
222+
let unexpectedBeforeColon: RawUnexpectedNodesSyntax?
223+
let colon: RawTokenSyntax
224+
225+
if self.at(.colon) {
226+
unexpectedBeforeColon = nil
227+
colon = consumeAnyToken(remapping: .colon)
228+
} else {
229+
unexpectedBeforeColon = RawUnexpectedNodesSyntax([self.consumeAnyToken()], arena: self.arena)
230+
colon = missingToken(.colon)
231+
}
232+
217233
var elements = [RawInheritedTypeSyntax]()
218234
do {
219235
var keepGoing: RawTokenSyntax? = nil
@@ -241,10 +257,20 @@ extension Parser {
241257
)
242258
} while keepGoing != nil && loopProgress.evaluate(currentToken)
243259
}
260+
261+
let unexpectedAfterInheritedTypeCollection: RawUnexpectedNodesSyntax?
262+
263+
if let rightBrace = self.consume(if: .rightParen) {
264+
unexpectedAfterInheritedTypeCollection = RawUnexpectedNodesSyntax(elements: [RawSyntax(rightBrace)], arena: self.arena)
265+
} else {
266+
unexpectedAfterInheritedTypeCollection = nil
267+
}
268+
244269
return RawTypeInheritanceClauseSyntax(
245270
unexpectedBeforeColon,
246271
colon: colon,
247272
inheritedTypeCollection: RawInheritedTypeListSyntax(elements: elements, arena: self.arena),
273+
unexpectedAfterInheritedTypeCollection,
248274
arena: self.arena
249275
)
250276
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,48 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
11711171
return handleEffectSpecifiers(node)
11721172
}
11731173

1174+
public override func visit(_ node: TypeInheritanceClauseSyntax) -> SyntaxVisitorContinueKind {
1175+
if shouldSkip(node) {
1176+
return .skipChildren
1177+
}
1178+
1179+
if let unexpected = node.unexpectedBeforeColon,
1180+
let leftParen = unexpected.tokens(satisfying: { $0.tokenKind == .leftParen }).first
1181+
{
1182+
1183+
var handledNodes: [SyntaxIdentifier] = [
1184+
leftParen.id, node.colon.id,
1185+
]
1186+
1187+
var changes: [FixIt.Changes] = [
1188+
.makePresent(node.colon, trailingTrivia: .space),
1189+
.makeMissing(unexpected),
1190+
]
1191+
1192+
if let rightParen = node.unexpectedAfterInheritedTypeCollection?.tokens(satisfying: { $0.tokenKind == .rightParen }).first {
1193+
handledNodes += [rightParen.id]
1194+
changes += [
1195+
.makeMissing(rightParen),
1196+
[.replaceTrailingTrivia(token: node.inheritedTypeCollection.lastToken!, newTrivia: .space)],
1197+
]
1198+
}
1199+
1200+
addDiagnostic(
1201+
unexpected,
1202+
.expectedColonClass,
1203+
fixIts: [
1204+
FixIt(
1205+
message: ReplaceTokensFixIt(replaceTokens: [], replacement: .colonToken()),
1206+
changes: changes
1207+
)
1208+
],
1209+
handledNodes: handledNodes
1210+
)
1211+
}
1212+
1213+
return .visitChildren
1214+
}
1215+
11741216
public override func visit(_ node: TypeInitializerClauseSyntax) -> SyntaxVisitorContinueKind {
11751217
if shouldSkip(node) {
11761218
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 13 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
}
@@ -608,3 +611,13 @@ public struct ReplaceTokensFixIt: ParserFixIt {
608611
"replace \(nodesDescription(replaceTokens, format: false)) with '\(replacement.text)'"
609612
}
610613
}
614+
615+
public struct TypeInheritanceClauseSyntaxFixIt: ParserFixIt {
616+
public let node: TypeInheritanceClauseSyntax
617+
618+
public let replacement: TokenSyntax
619+
620+
public var message: String {
621+
"replace \(node.description) with '\(replacement.text)'"
622+
}
623+
}

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ final class DeclarationTests: XCTestCase {
847847
DiagnosticSpec(locationMarker: "1️⃣", message: "expected ':' in parameter"),
848848
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end parameter clause"),
849849
DiagnosticSpec(locationMarker: "3️⃣", message: "expected identifier in struct"),
850-
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code ')' in struct"),
850+
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code ')' in inheritance clause"),
851851
]
852852
)
853853
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,9 +1518,11 @@ final class RecoveryTests: XCTestCase {
15181518
class WrongInheritanceClause11️⃣(Int) {}
15191519
""",
15201520
diagnostics: [
1521-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 34 - 35 = ''
1522-
DiagnosticSpec(message: "unexpected code '(Int)' in class")
1523-
]
1521+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1522+
],
1523+
fixedSource: """
1524+
class WrongInheritanceClause1: Int {}
1525+
"""
15241526
)
15251527
}
15261528

@@ -1530,9 +1532,11 @@ final class RecoveryTests: XCTestCase {
15301532
class WrongInheritanceClause21️⃣(Base2<Int>) {}
15311533
""",
15321534
diagnostics: [
1533-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 41 - 42 = ''
1534-
DiagnosticSpec(message: "unexpected code '(Base2<Int>)' in class")
1535-
]
1535+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1536+
],
1537+
fixedSource: """
1538+
class WrongInheritanceClause2: Base2<Int> {}
1539+
"""
15361540
)
15371541
}
15381542

@@ -1542,9 +1546,11 @@ final class RecoveryTests: XCTestCase {
15421546
class WrongInheritanceClause3<T>1️⃣(SubModule.Base1) where T:AnyObject {}
15431547
""",
15441548
diagnostics: [
1545-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 49 - 50 = ''
1546-
DiagnosticSpec(message: "unexpected code '(SubModule.Base1) where T:AnyObject' in class")
1547-
]
1549+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1550+
],
1551+
fixedSource: """
1552+
class WrongInheritanceClause3<T>: SubModule.Base1 where T:AnyObject {}
1553+
"""
15481554
)
15491555
}
15501556

@@ -1554,9 +1560,11 @@ final class RecoveryTests: XCTestCase {
15541560
class WrongInheritanceClause41️⃣(SubModule.Base2<Int>) {}
15551561
""",
15561562
diagnostics: [
1557-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': ', 51 - 52 = ''
1558-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>)' in class")
1559-
]
1563+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1564+
],
1565+
fixedSource: """
1566+
class WrongInheritanceClause4: SubModule.Base2<Int> {}
1567+
"""
15601568
)
15611569
}
15621570

@@ -1566,47 +1574,46 @@ final class RecoveryTests: XCTestCase {
15661574
class WrongInheritanceClause5<T>1️⃣(SubModule.Base2<Int>) where T:AnyObject {}
15671575
""",
15681576
diagnostics: [
1569-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': ', 54 - 55 = ''
1570-
DiagnosticSpec(message: "unexpected code '(SubModule.Base2<Int>) where T:AnyObject' in class")
1571-
]
1577+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1578+
],
1579+
fixedSource: """
1580+
class WrongInheritanceClause5<T>: SubModule.Base2<Int> where T:AnyObject {}
1581+
"""
15721582
)
15731583
}
15741584

15751585
func testRecovery130() {
15761586
assertParse(
15771587
"""
1578-
class WrongInheritanceClause61️⃣(Int 2️⃣{}3️⃣
1588+
class WrongInheritanceClause61️⃣(Int {}
15791589
""",
15801590
diagnostics: [
1581-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 30 - 31 = ': '
1582-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class"),
1583-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable"),
1584-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern"),
1585-
DiagnosticSpec(locationMarker: "3️⃣", message: "expected '}' to end class"),
1586-
]
1591+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1592+
],
1593+
fixedSource: """
1594+
class WrongInheritanceClause6: Int {}
1595+
"""
15871596
)
15881597
}
15891598

15901599
func testRecovery131() {
15911600
assertParse(
15921601
"""
1593-
class WrongInheritanceClause7<T>1️⃣(Int 2️⃣where T:AnyObject {}
1602+
class WrongInheritanceClause7<T>1️⃣(Int where T:AnyObject {}
15941603
""",
15951604
diagnostics: [
1596-
// TODO: Old parser expected error on line 1: expected ':' to begin inheritance clause, Fix-It replacements: 33 - 34 = ': '
1597-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '{' in class"),
1598-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'var' in variable"),
1599-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end tuple pattern"),
1600-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected '}' to end class"),
1601-
DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous code 'where T:AnyObject {}' at top level"),
1602-
]
1605+
DiagnosticSpec(message: "expected ':' to begin inheritance clause")
1606+
],
1607+
fixedSource: """
1608+
class WrongInheritanceClause7<T>: Int where T:AnyObject {}
1609+
"""
16031610
)
16041611
}
16051612

16061613
func testRecovery132() {
1614+
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16071615
assertParse(
16081616
"""
1609-
// <rdar://problem/18502220> [swift-crashes 078] parser crash on invalid cast in sequence expr
16101617
Base=1 as Base=1
16111618
"""
16121619
)
@@ -2242,5 +2249,4 @@ final class RecoveryTests: XCTestCase {
22422249
]
22432250
)
22442251
}
2245-
22462252
}

0 commit comments

Comments
 (0)