Skip to content

Commit bb21fec

Browse files
committed
Merge pull request #1449 from kimdv/kimdv/add-diagnostic-for-wrong-type
Add diagnostic for array type
1 parent 2774987 commit bb21fec

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
@@ -1052,7 +1052,37 @@ 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+
var loopProgress = LoopProgressCondition()
1075+
while loopProgress.evaluate(currentToken) {
1076+
if self.at(TokenSpec(.postfixQuestionMark, allowAtStartOfLine: false)) {
1077+
result = RawTypeSyntax(self.parseOptionalType(result))
1078+
} else if self.at(TokenSpec(.exclamationMark, allowAtStartOfLine: false)) {
1079+
result = RawTypeSyntax(self.parseImplicitlyUnwrappedOptionalType(result))
1080+
} else {
1081+
break
1082+
}
1083+
}
1084+
1085+
return result
10561086
}
10571087
}
10581088
}

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
@@ -137,6 +137,9 @@ extension DiagnosticMessage where Self == StaticParserError {
137137
public static var expectedSequenceExpressionInForEachLoop: Self {
138138
.init("expected Sequence expression for for-each loop")
139139
}
140+
public static var extraRightBracket: Self {
141+
.init("unexpected ']' in type; did you mean to write an array type?")
142+
}
140143
public static var initializerInPattern: Self {
141144
.init("unexpected initializer in pattern; did you mean to use '='?")
142145
}
@@ -542,6 +545,14 @@ extension FixItMessage where Self == StaticParserFixIt {
542545
}
543546
}
544547

548+
public struct InsertFixIt: ParserFixIt {
549+
public let tokenToBeInserted: TokenSyntax
550+
551+
public var message: String {
552+
"insert '\(tokenToBeInserted.text)'"
553+
}
554+
}
555+
545556
public struct MoveTokensAfterFixIt: ParserFixIt {
546557
/// The token that should be moved
547558
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
@@ -1084,15 +1084,27 @@ final class RecoveryTests: XCTestCase {
10841084
)
10851085
}
10861086

1087-
func testRecovery98a() {
1088-
assertParse(
1089-
"""
1090-
let a1: Swift.Int1️⃣]
1091-
""",
1092-
diagnostics: [
1093-
DiagnosticSpec(message: "extraneous code ']' at top level")
1094-
]
1095-
)
1087+
func testRecovery98() {
1088+
let testCases: [UInt: (testCase: String, fixedSource: String)] = [
1089+
#line: ("let a1: Swift.Int1️⃣]", "let a1: [Swift.Int]"),
1090+
#line: ("let a3: Set<Int>1️⃣]", "let a3: [Set<Int>]"),
1091+
#line: ("let a4: Int1️⃣]?", "let a4: [Int]?"),
1092+
#line: ("let a5: Int?1️⃣]", "let a5: [Int?]"),
1093+
#line: ("let a6: [Int]1️⃣]", "let a6: [[Int]]"),
1094+
#line: ("let a7: [String: Int]1️⃣]", "let a7: [[String: Int]]"),
1095+
#line: ("func foo() -> Int1️⃣]??", "func foo() -> [Int]??"),
1096+
]
1097+
1098+
for (line, testCase) in testCases {
1099+
assertParse(
1100+
testCase.testCase,
1101+
diagnostics: [
1102+
DiagnosticSpec(message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"], line: line)
1103+
],
1104+
fixedSource: testCase.fixedSource,
1105+
line: line
1106+
)
1107+
}
10961108
}
10971109

10981110
func testRecovery98b() {
@@ -1107,66 +1119,6 @@ final class RecoveryTests: XCTestCase {
11071119
)
11081120
}
11091121

1110-
func testRecovery98c() {
1111-
assertParse(
1112-
"""
1113-
let a3: Set<Int>1️⃣]
1114-
""",
1115-
diagnostics: [
1116-
// TODO: Old parser expected error on line 4: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1117-
DiagnosticSpec(message: "extraneous code ']' at top level")
1118-
]
1119-
)
1120-
}
1121-
1122-
func testRecovery98d() {
1123-
assertParse(
1124-
"""
1125-
let a4: Int1️⃣]?
1126-
""",
1127-
diagnostics: [
1128-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1129-
DiagnosticSpec(message: "extraneous code ']?' at top level")
1130-
]
1131-
)
1132-
}
1133-
1134-
func testRecovery98e() {
1135-
assertParse(
1136-
"""
1137-
let a5: Int?1️⃣]
1138-
""",
1139-
diagnostics: [
1140-
// TODO: Old parser expected error on line 6: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1141-
DiagnosticSpec(message: "extraneous code ']' at top level")
1142-
]
1143-
)
1144-
}
1145-
1146-
func testRecovery98f() {
1147-
assertParse(
1148-
"""
1149-
let a6: [Int]1️⃣]
1150-
""",
1151-
diagnostics: [
1152-
// TODO: Old parser expected error on line 7: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1153-
DiagnosticSpec(message: "extraneous code ']' at top level")
1154-
]
1155-
)
1156-
}
1157-
1158-
func testRecovery98g() {
1159-
assertParse(
1160-
"""
1161-
let a7: [String: Int]1️⃣]
1162-
""",
1163-
diagnostics: [
1164-
// TODO: Old parser expected error on line 8: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 11 - 11 = '['
1165-
DiagnosticSpec(message: "extraneous code ']' at top level")
1166-
]
1167-
)
1168-
}
1169-
11701122
func testRecovery99() {
11711123
assertParse(
11721124
"""
@@ -1204,10 +1156,9 @@ final class RecoveryTests: XCTestCase {
12041156
4️⃣}
12051157
""",
12061158
diagnostics: [
1207-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct"),
1208-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array"),
1209-
// TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 17 - 17 = '['
1210-
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code ']' in function"),
1159+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct", fixIts: ["insert '}'"]),
1160+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array", fixIts: ["insert ']'"]),
1161+
DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected ']' in type; did you mean to write an array type?", fixIts: ["insert '['"]),
12111162
DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous brace at top level"),
12121163
]
12131164
)

0 commit comments

Comments
 (0)