Skip to content

Commit 0d813c3

Browse files
authored
Merge pull request #1809 from TTOzzi/remove-unexpected-node-between-argument-and-right-paren-in-attribute-syntax
Improve diagnostics for invalid version tuples
2 parents b0f8e70 + 8ae11b1 commit 0d813c3

File tree

5 files changed

+86
-49
lines changed

5 files changed

+86
-49
lines changed

Sources/SwiftParser/Availability.swift

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,23 @@ extension Parser {
253253
return self.consumeAnyToken()
254254
}
255255

256+
/// Consume the unexpected version token(e.g. integerLiteral, floatingLiteral, identifier) until the period no longer appears.
257+
private mutating func parseUnexpectedVersionTokens() -> RawUnexpectedNodesSyntax? {
258+
var unexpectedTokens: [RawTokenSyntax] = []
259+
var keepGoing: RawTokenSyntax? = nil
260+
var loopProgress = LoopProgressCondition()
261+
repeat {
262+
if let keepGoing {
263+
unexpectedTokens.append(keepGoing)
264+
}
265+
if let unexpectedVersion = self.consume(if: .integerLiteral, .floatingLiteral, .identifier) {
266+
unexpectedTokens.append(unexpectedVersion)
267+
}
268+
keepGoing = self.consume(if: .period)
269+
} while keepGoing != nil && loopProgress.evaluate(currentToken)
270+
return RawUnexpectedNodesSyntax(unexpectedTokens, arena: self.arena)
271+
}
272+
256273
/// Parse a dot-separated list of version numbers.
257274
///
258275
/// Grammar
@@ -286,24 +303,29 @@ extension Parser {
286303
} else {
287304
trailingComponents.append(versionComponent)
288305
}
289-
}
290-
291-
var unexpectedTrailingComponents: RawUnexpectedNodesSyntax?
292306

293-
if !trailingComponents.isEmpty {
294-
unexpectedTrailingComponents = RawUnexpectedNodesSyntax(elements: trailingComponents.compactMap { $0.as(RawSyntax.self) }, arena: self.arena)
307+
if version.isMissing {
308+
break
309+
}
295310
}
296311

312+
let unexpectedTrailingComponents = RawUnexpectedNodesSyntax(trailingComponents, arena: self.arena)
313+
let unexpectedAfterComponents = self.parseUnexpectedVersionTokens()
297314
return RawVersionTupleSyntax(
298315
major: major,
299316
components: RawVersionComponentListSyntax(elements: components, arena: self.arena),
300-
unexpectedTrailingComponents,
317+
RawUnexpectedNodesSyntax(combining: unexpectedTrailingComponents, unexpectedAfterComponents, arena: self.arena),
301318
arena: self.arena
302319
)
303-
304320
} else {
305321
let major = self.expectDecimalIntegerWithoutRecovery()
306-
return RawVersionTupleSyntax(major: major, components: nil, arena: self.arena)
322+
let unexpectedAfterComponents = self.parseUnexpectedVersionTokens()
323+
return RawVersionTupleSyntax(
324+
major: major,
325+
components: nil,
326+
unexpectedAfterComponents,
327+
arena: self.arena
328+
)
307329
}
308330
}
309331
}

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,14 +1938,22 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
19381938
return .skipChildren
19391939
}
19401940

1941-
if let trailingComponents = node.unexpectedAfterComponents,
1942-
let components = node.components
1943-
{
1944-
addDiagnostic(
1945-
trailingComponents,
1946-
TrailingVersionAreIgnored(major: node.major, components: components),
1947-
handledNodes: [trailingComponents.id]
1948-
)
1941+
if let unexpectedAfterComponents = node.unexpectedAfterComponents {
1942+
if let components = node.components,
1943+
unexpectedAfterComponents.allSatisfy({ $0.is(VersionComponentSyntax.self) })
1944+
{
1945+
addDiagnostic(
1946+
unexpectedAfterComponents,
1947+
TrailingVersionAreIgnored(major: node.major, components: components),
1948+
handledNodes: [unexpectedAfterComponents.id]
1949+
)
1950+
} else {
1951+
addDiagnostic(
1952+
unexpectedAfterComponents,
1953+
CannotParseVersionTuple(versionTuple: unexpectedAfterComponents),
1954+
handledNodes: [node.major.id, node.components?.id, unexpectedAfterComponents.id].compactMap { $0 }
1955+
)
1956+
}
19491957
}
19501958

19511959
return .visitChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ public struct CannotParseVersionTuple: ParserError {
292292
public let versionTuple: UnexpectedNodesSyntax
293293

294294
public var message: String {
295-
return "cannot parse version \(versionTuple)"
295+
return "cannot parse version component \(versionTuple.shortSingleLineContentDescription)"
296296
}
297297
}
298298

Tests/SwiftParserTest/AvailabilityTests.swift

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,21 @@ final class AvailabilityTests: XCTestCase {
142142
)
143143
)
144144

145+
assertParse(
146+
"""
147+
@available(OSX 10.0.1, *)
148+
func test() {}
149+
"""
150+
)
151+
145152
assertParse(
146153
"""
147154
@available(OSX 1️⃣10e10)
148155
func test() {}
149156
""",
150157
diagnostics: [
151-
DiagnosticSpec(message: "expected version tuple in version restriction", fixIts: ["insert version tuple"]),
152-
DiagnosticSpec(message: "unexpected code '10e10' in attribute"),
153-
],
154-
fixedSource: """
155-
@available(OSX <#integer literal#>10e10)
156-
func test() {}
157-
"""
158+
DiagnosticSpec(message: "cannot parse version component code '10e10'")
159+
]
158160
)
159161

160162
assertParse(
@@ -163,13 +165,8 @@ final class AvailabilityTests: XCTestCase {
163165
func test() {}
164166
""",
165167
diagnostics: [
166-
DiagnosticSpec(message: "expected integer literal in version tuple", fixIts: ["insert integer literal"]),
167-
DiagnosticSpec(message: "unexpected code '0e10' in attribute"),
168-
],
169-
fixedSource: """
170-
@available(OSX 10.<#integer literal#>0e10)
171-
func test() {}
172-
"""
168+
DiagnosticSpec(message: "cannot parse version component code '0e10'")
169+
]
173170
)
174171

175172
assertParse(
@@ -178,13 +175,8 @@ final class AvailabilityTests: XCTestCase {
178175
func test() {}
179176
""",
180177
diagnostics: [
181-
DiagnosticSpec(message: "expected version tuple in version restriction", fixIts: ["insert version tuple"]),
182-
DiagnosticSpec(message: "unexpected code '0xff' in attribute"),
183-
],
184-
fixedSource: """
185-
@available(OSX <#integer literal#>0xff)
186-
func test() {}
187-
"""
178+
DiagnosticSpec(message: "cannot parse version component code '0xff'")
179+
]
188180
)
189181

190182
assertParse(
@@ -193,13 +185,28 @@ final class AvailabilityTests: XCTestCase {
193185
func test() {}
194186
""",
195187
diagnostics: [
196-
DiagnosticSpec(message: "expected integer literal in version tuple", fixIts: ["insert integer literal"]),
197-
DiagnosticSpec(message: "unexpected code '0xff' in attribute"),
198-
],
199-
fixedSource: """
200-
@available(OSX 1.0.<#integer literal#>0xff)
201-
func test() {}
202-
"""
188+
DiagnosticSpec(message: "cannot parse version component code '0xff'")
189+
]
190+
)
191+
192+
assertParse(
193+
"""
194+
@available(OSX 1.0.1️⃣0xff, *)
195+
func test() {}
196+
""",
197+
diagnostics: [
198+
DiagnosticSpec(message: "cannot parse version component code '0xff'")
199+
]
200+
)
201+
202+
assertParse(
203+
"""
204+
@available(OSX 1.0.1️⃣asdf)
205+
func test() {}
206+
""",
207+
diagnostics: [
208+
DiagnosticSpec(message: "cannot parse version component code 'asdf'")
209+
]
203210
)
204211
}
205212
}

Tests/SwiftParserTest/translated/IfconfigExprTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ final class IfconfigExprTests: XCTestCase {
388388
#endif
389389
"""#,
390390
diagnostics: [
391-
DiagnosticSpec(message: "cannot parse version \"\"")
391+
DiagnosticSpec(message: #"cannot parse version component code '""'"#)
392392
]
393393
)
394394
}
@@ -401,7 +401,7 @@ final class IfconfigExprTests: XCTestCase {
401401
#endif
402402
""",
403403
diagnostics: [
404-
DiagnosticSpec(message: "cannot parse version >=2.2")
404+
DiagnosticSpec(message: "cannot parse version component code '>=2.2'")
405405
]
406406
)
407407
}
@@ -414,7 +414,7 @@ final class IfconfigExprTests: XCTestCase {
414414
#endif
415415
""",
416416
diagnostics: [
417-
DiagnosticSpec(message: "cannot parse version 20A301")
417+
DiagnosticSpec(message: "cannot parse version component code '20A301'")
418418
]
419419
)
420420
}
@@ -427,7 +427,7 @@ final class IfconfigExprTests: XCTestCase {
427427
#endif
428428
"""#,
429429
diagnostics: [
430-
DiagnosticSpec(message: "cannot parse version \"20A301\"")
430+
DiagnosticSpec(message: #"cannot parse version component code '"20A301"'"#)
431431
]
432432
)
433433
}

0 commit comments

Comments
 (0)