Skip to content

Commit d167613

Browse files
authored
Merge pull request #1592 from kimdv/kimdv/add-missing-diagnostic-for-missing-´0´-in-float-literal
Add diagnostic for missing `0` in RawFloatLiteralExprSyntax
2 parents 088fc49 + 79f7837 commit d167613

File tree

6 files changed

+76
-5
lines changed

6 files changed

+76
-5
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,31 @@ extension Parser {
12631263
return RawExprSyntax(self.parseClosureExpression())
12641264
case (.period, let handle)?: // .foo
12651265
let dot = self.eat(handle)
1266+
1267+
// Special case ".<integer_literal>" like ".4". This isn't valid, but the
1268+
// developer almost certainly meant to use "0.4". Diagnose this, and
1269+
// recover as if they wrote that.
1270+
if let integerLiteral = self.consume(if: .integerLiteral) {
1271+
let text = arena.intern("0" + String(syntaxText: dot.tokenText) + String(syntaxText: integerLiteral.tokenText))
1272+
return RawExprSyntax(
1273+
RawFloatLiteralExprSyntax(
1274+
floatingDigits: RawTokenSyntax(
1275+
missing: .floatingLiteral,
1276+
text: text,
1277+
arena: self.arena
1278+
),
1279+
RawUnexpectedNodesSyntax(
1280+
elements: [
1281+
RawSyntax(dot),
1282+
RawSyntax(integerLiteral),
1283+
],
1284+
arena: self.arena
1285+
),
1286+
arena: self.arena
1287+
)
1288+
)
1289+
}
1290+
12661291
let (name, args) = self.parseDeclNameRef([.keywords, .compoundNames])
12671292
return RawExprSyntax(
12681293
RawMemberAccessExprSyntax(

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,32 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
678678
return .visitChildren
679679
}
680680

681+
public override func visit(_ node: FloatLiteralExprSyntax) -> SyntaxVisitorContinueKind {
682+
if shouldSkip(node) {
683+
return .skipChildren
684+
}
685+
if node.floatingDigits.presence == .missing,
686+
let (period, integerLiteral) = node.unexpectedAfterFloatingDigits?.twoTokens(firstSatisfying: { $0.tokenKind == .period }, secondSatisfying: { $0.tokenKind.isIntegerLiteral })
687+
{
688+
addDiagnostic(
689+
node,
690+
InvalidFloatLiteralMissingLeadingZero(decimalDigits: integerLiteral),
691+
fixIts: [
692+
FixIt(
693+
message: InsertFixIt(tokenToBeInserted: .integerLiteral("0")),
694+
changes: [
695+
.makePresent(node.floatingDigits),
696+
.makeMissing(period),
697+
.makeMissing(integerLiteral),
698+
]
699+
)
700+
],
701+
handledNodes: [node.floatingDigits.id, period.id, integerLiteral.id]
702+
)
703+
}
704+
return .visitChildren
705+
}
706+
681707
public override func visit(_ node: ForInStmtSyntax) -> SyntaxVisitorContinueKind {
682708
if shouldSkip(node) {
683709
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,14 @@ public struct IdentifierNotAllowedInOperatorName: ParserError {
306306
}
307307
}
308308

309+
public struct InvalidFloatLiteralMissingLeadingZero: ParserError {
310+
public let decimalDigits: TokenSyntax
311+
312+
public var message: String {
313+
return "'.\(decimalDigits.text)' is not a valid floating point literal; it must be written '0.\(decimalDigits.text)'"
314+
}
315+
}
316+
309317
public struct InvalidIdentifierError: ParserError {
310318
public let invalidIdentifier: TokenSyntax
311319
public let missingIdentifier: TokenSyntax

Sources/SwiftParserDiagnostics/SyntaxExtensions.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ extension TokenKind {
149149
return false
150150
}
151151
}
152+
153+
var isIntegerLiteral: Bool {
154+
switch self {
155+
case .integerLiteral:
156+
return true
157+
default:
158+
return false
159+
}
160+
}
152161
}
153162

154163
public extension TriviaPiece {

Sources/SwiftSyntax/SyntaxArena.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ public class SyntaxArena {
7979

8080
/// Copies a UTF8 sequence of `String` to the memory this arena manages, and
8181
/// returns the copied string as a `SyntaxText`
82-
func intern(_ value: String) -> SyntaxText {
82+
@_spi(RawSyntax)
83+
public func intern(_ value: String) -> SyntaxText {
8384
if value.isEmpty { return SyntaxText() }
8485
var value = value
8586
return value.withUTF8 { utf8 in

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,16 +1658,18 @@ final class RecoveryTests: XCTestCase {
16581658
assertParse(
16591659
"""
16601660
func exprPostfix2() {
1661-
_ = .1️⃣42
1661+
_ = 1️⃣.42
16621662
}
16631663
""",
16641664
diagnostics: [
1665-
// TODO: Old parser expected error on line 2: '.42' is not a valid floating point literal; it must be written '0.42', Fix-It replacements: 7 - 7 = '0'
1666-
DiagnosticSpec(message: "expected name in member access", fixIts: ["insert name"])
1665+
DiagnosticSpec(
1666+
message: "'.42' is not a valid floating point literal; it must be written '0.42'",
1667+
fixIts: ["insert '0'"]
1668+
)
16671669
],
16681670
fixedSource: """
16691671
func exprPostfix2() {
1670-
_ = .<#identifier#>42
1672+
_ = 0.42
16711673
}
16721674
"""
16731675
)

0 commit comments

Comments
 (0)