Skip to content

Commit 71af420

Browse files
authored
Merge pull request #892 from ahoppen/ahoppen/colon-in-typealias
Add diagnostic if ':' is used in a typealias declaration instead of '='
2 parents 01e4bba + aae84e8 commit 71af420

File tree

4 files changed

+49
-35
lines changed

4 files changed

+49
-35
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,14 @@ extension Parser {
19091909
}
19101910

19111911
// Parse the binding alias.
1912-
let (unexpectedBeforeEqual, equal) = self.expect(.equal)
1912+
let unexpectedBeforeEqual: RawUnexpectedNodesSyntax?
1913+
let equal: RawTokenSyntax
1914+
if let colon = self.consume(if: .colon) {
1915+
unexpectedBeforeEqual = RawUnexpectedNodesSyntax(elements: [RawSyntax(colon)], arena: self.arena)
1916+
equal = missingToken(.equal, text: nil)
1917+
} else {
1918+
(unexpectedBeforeEqual, equal) = self.expect(.equal)
1919+
}
19131920
let value = self.parseType()
19141921
let initializer = RawTypeInitializerClauseSyntax(
19151922
unexpectedBeforeEqual,

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,22 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
304304
return .visitChildren
305305
}
306306

307+
public override func visit(_ node: TypeInitializerClauseSyntax) -> SyntaxVisitorContinueKind {
308+
if shouldSkip(node) {
309+
return .skipChildren
310+
}
311+
if node.equal.presence == .missing {
312+
exchangeTokens(
313+
unexpected: node.unexpectedBeforeEqual,
314+
unexpectedTokenCondition: { $0.tokenKind == .colon },
315+
correctTokens: [node.equal],
316+
message: { _ in MissingNodesError(missingNodes: [Syntax(node.equal)]) },
317+
fixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: node.equal) }
318+
)
319+
}
320+
return .visitChildren
321+
}
322+
307323
public override func visit(_ node: UnresolvedTernaryExprSyntax) -> SyntaxVisitorContinueKind {
308324
if shouldSkip(node) {
309325
return .skipChildren

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,12 @@ public struct MoveTokensInFrontOfFixIt: ParserFixIt {
169169
}
170170
}
171171

172+
public struct ReplaceTokensFixIt: ParserFixIt {
173+
public let replaceTokens: [TokenSyntax]
174+
175+
public let replacement: TokenSyntax
176+
177+
public var message: String {
178+
"replace \(missingNodesDescription(missingNodes: replaceTokens.map(Syntax.init), commonParent: nil)) by '\(replacement.text)'"
179+
}
180+
}

Tests/SwiftParserTest/translated/TypealiasTests.swift

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,15 @@ final class TypealiasTests: XCTestCase {
3535
)
3636
}
3737

38+
// <rdar://problem/13339798> QoI: poor diagnostic in malformed typealias
3839
func testTypealias3a() {
3940
AssertParse(
4041
"""
41-
// <rdar://problem/13339798> QoI: poor diagnostic in malformed typealias
4242
typealias Foo1 1️⃣: Int
4343
""",
4444
diagnostics: [
45-
// TODO: Old parser expected error on line 2: expected '=' in type alias declaration, Fix-It replacements: 16 - 17 = '='
46-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
47-
DiagnosticSpec(message: "extraneous ': Int' at top level"),
48-
]
45+
DiagnosticSpec(message: "expected '=' in typealias declaration"),
46+
], fixedSource: "typealias Foo1 = Int"
4947
)
5048
}
5149

@@ -55,10 +53,8 @@ final class TypealiasTests: XCTestCase {
5553
typealias Foo21️⃣: Int
5654
""",
5755
diagnostics: [
58-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration, Fix-It replacements: 15 - 16 = ' ='
59-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
60-
DiagnosticSpec(message: "extraneous ': Int' at top level"),
61-
]
56+
DiagnosticSpec(message: "expected '=' in typealias declaration"),
57+
], fixedSource: "typealias Foo2= Int"
6258
)
6359
}
6460

@@ -68,10 +64,8 @@ final class TypealiasTests: XCTestCase {
6864
typealias Foo3 1️⃣:Int
6965
""",
7066
diagnostics: [
71-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration, Fix-It replacements: 16 - 17 = '= '
72-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
73-
DiagnosticSpec(message: "extraneous ':Int' at top level"),
74-
]
67+
DiagnosticSpec(message: "expected '=' in typealias declaration"),
68+
], fixedSource: "typealias Foo3 =Int"
7569
)
7670
}
7771

@@ -81,10 +75,8 @@ final class TypealiasTests: XCTestCase {
8175
typealias Foo41️⃣:/*comment*/Int
8276
""",
8377
diagnostics: [
84-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration, Fix-It replacements: 15 - 16 = ' = '
85-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
86-
DiagnosticSpec(message: "extraneous ':/*comment*/Int' at top level"),
87-
]
78+
DiagnosticSpec(message: "expected '=' in typealias declaration"),
79+
], fixedSource: "typealias Foo4=/*comment*/Int"
8880
)
8981
}
9082

@@ -94,7 +86,6 @@ final class TypealiasTests: XCTestCase {
9486
typealias Recovery11️⃣
9587
""",
9688
diagnostics: [
97-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration
9889
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
9990
]
10091
)
@@ -103,13 +94,11 @@ final class TypealiasTests: XCTestCase {
10394
func testTypealias6() {
10495
AssertParse(
10596
"""
106-
typealias Recovery2 1️⃣:
97+
typealias Recovery2 1️⃣:2️⃣
10798
""",
10899
diagnostics: [
109-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration
110-
// TODO: Old parser expected error on line 1: expected type in type alias declaration
111-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
112-
DiagnosticSpec(message: "extraneous ':' at top level"),
100+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '=' in typealias declaration", fixIts: ["replace ':' by '='"]),
101+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected value in typealias declaration"),
113102
]
114103
)
115104
}
@@ -120,7 +109,6 @@ final class TypealiasTests: XCTestCase {
120109
typealias Recovery3 =1️⃣
121110
""",
122111
diagnostics: [
123-
// TODO: Old parser expected error on line 1: expected type in type alias declaration
124112
DiagnosticSpec(message: "expected value in typealias declaration"),
125113
]
126114
)
@@ -132,24 +120,19 @@ final class TypealiasTests: XCTestCase {
132120
typealias Recovery4 1️⃣: Int
133121
""",
134122
diagnostics: [
135-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration
136-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
137-
DiagnosticSpec(message: "extraneous ': Int' at top level"),
123+
DiagnosticSpec(message: "expected '=' in typealias declaration", fixIts: ["replace ':' by '='"]),
138124
]
139125
)
140126
}
141127

142128
func testTypealias9() {
143129
AssertParse(
144130
"""
145-
typealias Recovery5 1️⃣: Int, Float
131+
typealias Recovery5 1️⃣: Int2️⃣, Float
146132
""",
147133
diagnostics: [
148-
// TODO: Old parser expected error on line 1: expected '=' in type alias declaration
149-
// TODO: Old parser expected error on line 1: consecutive statements on a line must be separated by ';'
150-
// TODO: Old parser expected error on line 1: expected expression
151-
DiagnosticSpec(message: "expected '=' and value in typealias declaration"),
152-
DiagnosticSpec(message: "extraneous ': Int, Float' at top level"),
134+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '=' in typealias declaration", fixIts: ["replace ':' by '='"]),
135+
DiagnosticSpec(locationMarker: "2️⃣", message: "extraneous ', Float' at top level"),
153136
]
154137
)
155138
}
@@ -160,7 +143,6 @@ final class TypealiasTests: XCTestCase {
160143
typealias Recovery6 = 1️⃣=
161144
""",
162145
diagnostics: [
163-
// TODO: Old parser expected error on line 1: expected type in type alias declaration
164146
DiagnosticSpec(message: "expected value in typealias declaration"),
165147
DiagnosticSpec(message: "extraneous '=' at top level"),
166148
]

0 commit comments

Comments
 (0)