Skip to content

Commit 1421a90

Browse files
committed
Add diagnostic for array type
1 parent aa65da5 commit 1421a90

File tree

8 files changed

+129
-111
lines changed

8 files changed

+129
-111
lines changed

Sources/SwiftParser/Types.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,38 @@ extension Parser {
10521052
)
10531053
)
10541054
} else {
1055-
return self.parseType()
1055+
var result = self.parseType()
1056+
1057+
guard !result.hasError else {
1058+
return result
1059+
}
1060+
1061+
if self.at(.rightSquareBracket) {
1062+
let (unexpectedBeforeRSquareBracket, rightSquareBracket) = self.expect(.rightSquareBracket)
1063+
result = RawTypeSyntax(
1064+
RawArrayTypeSyntax(
1065+
leftSquareBracket: missingToken(.leftSquareBracket),
1066+
elementType: result,
1067+
unexpectedBeforeRSquareBracket,
1068+
rightSquareBracket: rightSquareBracket,
1069+
arena: self.arena
1070+
)
1071+
)
1072+
}
1073+
1074+
// After recovering, we continue parse the remaing `?` or `!` if they are present.
1075+
var continueParsingQuestionOrExclamationMark = true
1076+
while continueParsingQuestionOrExclamationMark {
1077+
if self.at(TokenSpec(.postfixQuestionMark, allowAtStartOfLine: false)) {
1078+
result = RawTypeSyntax(self.parseOptionalType(result))
1079+
} else if self.at(TokenSpec(.exclamationMark, allowAtStartOfLine: false)) {
1080+
result = RawTypeSyntax(self.parseImplicitlyUnwrappedOptionalType(result))
1081+
} else {
1082+
continueParsingQuestionOrExclamationMark = false
1083+
}
1084+
}
1085+
1086+
return result
10561087
}
10571088
}
10581089
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,23 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
426426
return .visitChildren
427427
}
428428

429+
public override func visit(_ node: ArrayTypeSyntax) -> SyntaxVisitorContinueKind {
430+
if shouldSkip(node) {
431+
return .skipChildren
432+
}
433+
434+
if node.leftSquareBracket.presence == .missing && node.rightSquareBracket.presence == .present {
435+
addDiagnostic(
436+
node.rightSquareBracket,
437+
.extraRightBracket,
438+
fixIts: [.init(message: InsertFixIt(tokenToBeInserted: node.leftSquareBracket), changes: .makePresent(node.leftSquareBracket))],
439+
handledNodes: [node.leftSquareBracket.id]
440+
)
441+
}
442+
443+
return .visitChildren
444+
}
445+
429446
public override func visit(_ node: AttributeSyntax) -> SyntaxVisitorContinueKind {
430447
if shouldSkip(node) {
431448
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ extension DiagnosticMessage where Self == StaticParserError {
140140
public static var expectedSequenceExpressionInForEachLoop: Self {
141141
.init("expected Sequence expression for for-each loop")
142142
}
143+
public static var extraRightBracket: Self {
144+
.init("unexpected ']' in type; did you mean to write an array type?")
145+
}
143146
public static var initializerInPattern: Self {
144147
.init("unexpected initializer in pattern; did you mean to use '='?")
145148
}
@@ -557,6 +560,14 @@ extension FixItMessage where Self == StaticParserFixIt {
557560
}
558561
}
559562

563+
public struct InsertFixIt: ParserFixIt {
564+
public let tokenToBeInserted: TokenSyntax
565+
566+
public var message: String {
567+
"insert '\(tokenToBeInserted.text)'"
568+
}
569+
}
570+
560571
public struct MoveTokensAfterFixIt: ParserFixIt {
561572
/// The token that should be moved
562573
public let movedTokens: [TokenSyntax]

Sources/SwiftSyntax/Raw/RawSyntaxNodeProtocol.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public extension RawSyntaxNodeProtocol {
4646
var isEmpty: Bool {
4747
return raw.byteLength == 0
4848
}
49+
50+
/// Whether the tree contained by this layout has any
51+
/// - missing nodes or
52+
/// - unexpected nodes or
53+
/// - tokens with a `TokenDiagnostic` of severity `error`
54+
var hasError: Bool {
55+
return raw.recursiveFlags.contains(.hasError)
56+
}
4957
}
5058

5159
/// `RawSyntax` itself conforms to `RawSyntaxNodeProtocol`.

Sources/SwiftSyntax/Syntax.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@ public extension SyntaxProtocol {
238238
/// Whether the tree contained by this layout has any
239239
/// - missing nodes or
240240
/// - unexpected nodes or
241-
/// - tokens with a `TokenDiagnostic` of severity `error`
241+
/// - tokens with a ``TokenDiagnostic`` of severity ``TokenDiagnostic/Severity-swift.enum/error``.
242242
var hasError: Bool {
243-
return raw.recursiveFlags.contains(.hasError)
243+
return raw.hasError
244244
}
245245

246246
/// Whether the tree contained by this layout has any tokens with a

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: 22 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,15 +1138,27 @@ final class RecoveryTests: XCTestCase {
11381138
)
11391139
}
11401140

1141-
func testRecovery98a() {
1142-
assertParse(
1143-
"""
1144-
let a1: Swift.Int1️⃣]
1145-
""",
1146-
diagnostics: [
1147-
DiagnosticSpec(message: "extraneous code ']' at top level")
1148-
]
1149-
)
1141+
func testRecovery98() {
1142+
let testCases: [UInt: (testCase: String, fixedSource: String)] = [
1143+
#line: ("let a1: Swift.Int1️⃣]", "let a1: [Swift.Int]"),
1144+
#line: ("let a3: Set<Int>1️⃣]", "let a3: [Set<Int>]"),
1145+
#line: ("let a4: Int1️⃣]?", "let a4: [Int]?"),
1146+
#line: ("let a5: Int?1️⃣]", "let a5: [Int?]"),
1147+
#line: ("let a6: [Int]1️⃣]", "let a6: [[Int]]"),
1148+
#line: ("let a7: [String: Int]1️⃣]", "let a7: [[String: Int]]"),
1149+
#line: ("func foo() -> Int1️⃣]??", "func foo() -> [Int]??"),
1150+
]
1151+
1152+
for (line, testCase) in testCases {
1153+
assertParse(
1154+
testCase.testCase,
1155+
diagnostics: [
1156+
DiagnosticSpec(message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"], line: line)
1157+
],
1158+
fixedSource: testCase.fixedSource,
1159+
line: line
1160+
)
1161+
}
11501162
}
11511163

11521164
func testRecovery98b() {
@@ -1161,66 +1173,6 @@ final class RecoveryTests: XCTestCase {
11611173
)
11621174
}
11631175

1164-
func testRecovery98c() {
1165-
assertParse(
1166-
"""
1167-
let a3: Set<Int>1️⃣]
1168-
""",
1169-
diagnostics: [
1170-
// TODO: Old parser expected error on line 4: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1171-
DiagnosticSpec(message: "extraneous code ']' at top level")
1172-
]
1173-
)
1174-
}
1175-
1176-
func testRecovery98d() {
1177-
assertParse(
1178-
"""
1179-
let a4: Int1️⃣]?
1180-
""",
1181-
diagnostics: [
1182-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1183-
DiagnosticSpec(message: "extraneous code ']?' at top level")
1184-
]
1185-
)
1186-
}
1187-
1188-
func testRecovery98e() {
1189-
assertParse(
1190-
"""
1191-
let a5: Int?1️⃣]
1192-
""",
1193-
diagnostics: [
1194-
// TODO: Old parser expected error on line 6: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1195-
DiagnosticSpec(message: "extraneous code ']' at top level")
1196-
]
1197-
)
1198-
}
1199-
1200-
func testRecovery98f() {
1201-
assertParse(
1202-
"""
1203-
let a6: [Int]1️⃣]
1204-
""",
1205-
diagnostics: [
1206-
// TODO: Old parser expected error on line 7: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1207-
DiagnosticSpec(message: "extraneous code ']' at top level")
1208-
]
1209-
)
1210-
}
1211-
1212-
func testRecovery98g() {
1213-
assertParse(
1214-
"""
1215-
let a7: [String: Int]1️⃣]
1216-
""",
1217-
diagnostics: [
1218-
// TODO: Old parser expected error on line 8: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1219-
DiagnosticSpec(message: "extraneous code ']' at top level")
1220-
]
1221-
)
1222-
}
1223-
12241176
func testRecovery99() {
12251177
assertParse(
12261178
"""
@@ -1260,8 +1212,7 @@ final class RecoveryTests: XCTestCase {
12601212
diagnostics: [
12611213
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct", fixIts: ["insert '}'"]),
12621214
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array", fixIts: ["insert ']'"]),
1263-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 17 - 17 = '['
1264-
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code ']' in function"),
1215+
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"]),
12651216
DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous brace at top level"),
12661217
]
12671218
)

0 commit comments

Comments
 (0)