Skip to content

Commit 3fd77d6

Browse files
committed
Merge pull request swiftlang#1449 from kimdv/kimdv/add-diagnostic-for-wrong-type
Add diagnostic for array type
1 parent 83c2be9 commit 3fd77d6

File tree

6 files changed

+93
-76
lines changed

6 files changed

+93
-76
lines changed

Sources/SwiftParser/Types.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,37 @@ extension Parser {
10641064
)
10651065
)
10661066
} else {
1067-
return self.parseType()
1067+
var result = self.parseType()
1068+
1069+
guard !result.hasError else {
1070+
return result
1071+
}
1072+
1073+
if self.at(.rightSquareBracket) {
1074+
let (unexpectedBeforeRSquareBracket, rightSquareBracket) = self.expect(.rightSquareBracket)
1075+
result = RawTypeSyntax(
1076+
RawArrayTypeSyntax(
1077+
leftSquareBracket: missingToken(.leftSquareBracket),
1078+
elementType: result,
1079+
unexpectedBeforeRSquareBracket,
1080+
rightSquareBracket: rightSquareBracket,
1081+
arena: self.arena
1082+
)
1083+
)
1084+
}
1085+
1086+
var loopProgress = LoopProgressCondition()
1087+
while loopProgress.evaluate(currentToken) {
1088+
if self.at(TokenSpec(.postfixQuestionMark, allowAtStartOfLine: false)) {
1089+
result = RawTypeSyntax(self.parseOptionalType(result))
1090+
} else if self.at(TokenSpec(.exclamationMark, allowAtStartOfLine: false)) {
1091+
result = RawTypeSyntax(self.parseImplicitlyUnwrappedOptionalType(result))
1092+
} else {
1093+
break
1094+
}
1095+
}
1096+
1097+
return result
10681098
}
10691099
}
10701100
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,23 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
410410
return .visitChildren
411411
}
412412

413+
public override func visit(_ node: ArrayTypeSyntax) -> SyntaxVisitorContinueKind {
414+
if shouldSkip(node) {
415+
return .skipChildren
416+
}
417+
418+
if node.leftSquareBracket.presence == .missing && node.rightSquareBracket.presence == .present {
419+
addDiagnostic(
420+
node.rightSquareBracket,
421+
.extraRightBracket,
422+
fixIts: [.init(message: InsertFixIt(tokenToBeInserted: node.leftSquareBracket), changes: .makePresent(node.leftSquareBracket))],
423+
handledNodes: [node.leftSquareBracket.id]
424+
)
425+
}
426+
427+
return .visitChildren
428+
}
429+
413430
public override func visit(_ node: AttributeSyntax) -> SyntaxVisitorContinueKind {
414431
if shouldSkip(node) {
415432
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
}
@@ -545,6 +548,14 @@ extension FixItMessage where Self == StaticParserFixIt {
545548
}
546549
}
547550

551+
public struct InsertFixIt: ParserFixIt {
552+
public let tokenToBeInserted: TokenSyntax
553+
554+
public var message: String {
555+
"insert '\(tokenToBeInserted.text)'"
556+
}
557+
}
558+
548559
public struct MoveTokensAfterFixIt: ParserFixIt {
549560
/// The token that should be moved
550561
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

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 24 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,15 +1119,27 @@ final class RecoveryTests: XCTestCase {
11191119
)
11201120
}
11211121

1122-
func testRecovery98a() {
1123-
assertParse(
1124-
"""
1125-
let a1: Swift.Int1️⃣]
1126-
""",
1127-
diagnostics: [
1128-
DiagnosticSpec(message: "extraneous code ']' at top level")
1129-
]
1130-
)
1122+
func testRecovery98() {
1123+
let testCases: [UInt: (testCase: String, fixedSource: String)] = [
1124+
#line: ("let a1: Swift.Int1️⃣]", "let a1: [Swift.Int]"),
1125+
#line: ("let a3: Set<Int>1️⃣]", "let a3: [Set<Int>]"),
1126+
#line: ("let a4: Int1️⃣]?", "let a4: [Int]?"),
1127+
#line: ("let a5: Int?1️⃣]", "let a5: [Int?]"),
1128+
#line: ("let a6: [Int]1️⃣]", "let a6: [[Int]]"),
1129+
#line: ("let a7: [String: Int]1️⃣]", "let a7: [[String: Int]]"),
1130+
#line: ("func foo() -> Int1️⃣]??", "func foo() -> [Int]??"),
1131+
]
1132+
1133+
for (line, testCase) in testCases {
1134+
assertParse(
1135+
testCase.testCase,
1136+
diagnostics: [
1137+
DiagnosticSpec(message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"], line: line)
1138+
],
1139+
fixedSource: testCase.fixedSource,
1140+
line: line
1141+
)
1142+
}
11311143
}
11321144

11331145
func testRecovery98b() {
@@ -1142,66 +1154,6 @@ final class RecoveryTests: XCTestCase {
11421154
)
11431155
}
11441156

1145-
func testRecovery98c() {
1146-
assertParse(
1147-
"""
1148-
let a3: Set<Int>1️⃣]
1149-
""",
1150-
diagnostics: [
1151-
// TODO: Old parser expected error on line 4: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1152-
DiagnosticSpec(message: "extraneous code ']' at top level")
1153-
]
1154-
)
1155-
}
1156-
1157-
func testRecovery98d() {
1158-
assertParse(
1159-
"""
1160-
let a4: Int1️⃣]?
1161-
""",
1162-
diagnostics: [
1163-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1164-
DiagnosticSpec(message: "extraneous code ']?' at top level")
1165-
]
1166-
)
1167-
}
1168-
1169-
func testRecovery98e() {
1170-
assertParse(
1171-
"""
1172-
let a5: Int?1️⃣]
1173-
""",
1174-
diagnostics: [
1175-
// TODO: Old parser expected error on line 6: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1176-
DiagnosticSpec(message: "extraneous code ']' at top level")
1177-
]
1178-
)
1179-
}
1180-
1181-
func testRecovery98f() {
1182-
assertParse(
1183-
"""
1184-
let a6: [Int]1️⃣]
1185-
""",
1186-
diagnostics: [
1187-
// TODO: Old parser expected error on line 7: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1188-
DiagnosticSpec(message: "extraneous code ']' at top level")
1189-
]
1190-
)
1191-
}
1192-
1193-
func testRecovery98g() {
1194-
assertParse(
1195-
"""
1196-
let a7: [String: Int]1️⃣]
1197-
""",
1198-
diagnostics: [
1199-
// TODO: Old parser expected error on line 8: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1200-
DiagnosticSpec(message: "extraneous code ']' at top level")
1201-
]
1202-
)
1203-
}
1204-
12051157
func testRecovery99() {
12061158
assertParse(
12071159
"""
@@ -1239,10 +1191,9 @@ final class RecoveryTests: XCTestCase {
12391191
4️⃣}
12401192
""",
12411193
diagnostics: [
1242-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct"),
1243-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array"),
1244-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 17 - 17 = '['
1245-
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code ']' in function"),
1194+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct", fixIts: ["insert '}'"]),
1195+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array", fixIts: ["insert ']'"]),
1196+
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"]),
12461197
DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous brace at top level"),
12471198
]
12481199
)

0 commit comments

Comments
 (0)