Skip to content

Commit 1b1ae9a

Browse files
committed
Add diagnostic for missing space around =
1 parent 33e4b33 commit 1b1ae9a

File tree

5 files changed

+68
-13
lines changed

5 files changed

+68
-13
lines changed

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1967,7 +1967,23 @@ extension Lexer.Cursor {
19671967
if self.input.baseAddress! - tokStart.input.baseAddress! == 1 {
19681968
switch tokStart.peek() {
19691969
case UInt8(ascii: "="):
1970-
return Lexer.Result(.equal)
1970+
if leftBound != rightBound {
1971+
var errorPos = tokStart
1972+
1973+
if rightBound {
1974+
_ = errorPos.advance()
1975+
}
1976+
1977+
return Lexer.Result(
1978+
.equal,
1979+
error: LexingDiagnostic(
1980+
.equalMustHaveConsistentWhitespaceOnBothSides,
1981+
position: errorPos
1982+
)
1983+
)
1984+
} else {
1985+
return Lexer.Result(.equal)
1986+
}
19711987
case UInt8(ascii: "&"):
19721988
if leftBound == rightBound || leftBound {
19731989
break

Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public extension TokenError {
3939
/// Please order the cases in this enum alphabetically by case name.
4040
public enum StaticTokenError: String, DiagnosticMessage {
4141
case editorPlaceholder = "editor placeholder in source file"
42+
case equalMustHaveConsistentWhitespaceOnBothSides = "'=' must have consistent whitespace on both sides"
4243
case expectedBinaryExponentInHexFloatLiteral = "hexadecimal floating point literal must end with an exponent"
4344
case expectedClosingBraceInUnicodeEscape = #"expected '}' in \u{...} escape sequence"#
4445
case expectedDigitInFloatLiteral = "expected a digit in floating point exponent"
@@ -134,6 +135,7 @@ public extension SwiftSyntax.TokenDiagnostic {
134135

135136
switch self.kind {
136137
case .editorPlaceholder: return StaticTokenError.editorPlaceholder
138+
case .equalMustHaveConsistentWhitespaceOnBothSides: return StaticTokenError.equalMustHaveConsistentWhitespaceOnBothSides
137139
case .expectedBinaryExponentInHexFloatLiteral: return StaticTokenError.expectedBinaryExponentInHexFloatLiteral
138140
case .expectedClosingBraceInUnicodeEscape: return StaticTokenError.expectedClosingBraceInUnicodeEscape
139141
case .expectedDigitInFloatLiteral: return StaticTokenError.expectedDigitInFloatLiteral
@@ -200,6 +202,26 @@ public extension SwiftSyntax.TokenDiagnostic {
200202
return [
201203
FixIt(message: .replaceCurlyQuoteByNormalQuote, changes: [[.replace(oldNode: Syntax(token), newNode: Syntax(fixedToken))]])
202204
]
205+
case .equalMustHaveConsistentWhitespaceOnBothSides:
206+
let hasLeadingSpace = token.previousToken(viewMode: .all)?.trailingTrivia.contains(where: { $0.isSpaceOrTab }) ?? false
207+
let hasTrailingSpace = token.trailingTrivia.contains { $0.isSpaceOrTab }
208+
var changes: [FixIt.Change] = []
209+
210+
if !hasLeadingSpace {
211+
changes += [
212+
.replaceLeadingTrivia(token: token, newTrivia: .space)
213+
]
214+
}
215+
216+
if !hasTrailingSpace {
217+
changes += [
218+
.replaceTrailingTrivia(token: token, newTrivia: .space)
219+
]
220+
}
221+
222+
return [
223+
FixIt(message: .insertWhitespace, changes: FixIt.Changes(changes: changes))
224+
]
203225
default:
204226
return []
205227
}

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,9 @@ extension FixItMessage where Self == StaticParserFixIt {
504504
public static var insertNewline: Self {
505505
.init("insert newline")
506506
}
507+
public static var insertWhitespace: Self {
508+
.init("insert whitespace")
509+
}
507510
public static var joinIdentifiers: Self {
508511
.init("join the identifiers together")
509512
}

Sources/SwiftSyntax/TokenDiagnostic.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public struct TokenDiagnostic: Hashable {
2323
// Please order these alphabetically
2424

2525
case editorPlaceholder
26+
case equalMustHaveConsistentWhitespaceOnBothSides
2627
case expectedBinaryExponentInHexFloatLiteral
2728
case expectedClosingBraceInUnicodeEscape
2829
case expectedDigitInFloatLiteral
@@ -96,6 +97,7 @@ public struct TokenDiagnostic: Hashable {
9697
public var severity: Severity {
9798
switch kind {
9899
case .editorPlaceholder: return .error
100+
case .equalMustHaveConsistentWhitespaceOnBothSides: return .error
99101
case .expectedBinaryExponentInHexFloatLiteral: return .error
100102
case .expectedClosingBraceInUnicodeEscape: return .error
101103
case .expectedDigitInFloatLiteral: return .error

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,34 +1958,46 @@ final class RecoveryTests: XCTestCase {
19581958
}
19591959

19601960
func testRecovery163() {
1961+
// <rdar://problem/22387625> QoI: Common errors: 'let x= 5' and 'let x =5' could use Fix-its
19611962
assertParse(
19621963
"""
1963-
// <rdar://problem/22387625> QoI: Common errors: 'let x= 5' and 'let x =5' could use Fix-its
19641964
func r22387625() {
1965-
let _= 5
1966-
let _ =5
1965+
let _1️⃣= 5
1966+
let _ =2️⃣5
19671967
}
19681968
""",
19691969
diagnostics: [
1970-
// TODO: Old parser expected error on line 3: '=' must have consistent whitespace on both sides, Fix-It replacements: 8 - 8 = ' '
1971-
// TODO: Old parser expected error on line 4: '=' must have consistent whitespace on both sides, Fix-It replacements: 10 - 10 = ' '
1972-
]
1970+
DiagnosticSpec(locationMarker: "1️⃣", message: "'=' must have consistent whitespace on both sides", fixIts: ["insert whitespace"]),
1971+
DiagnosticSpec(locationMarker: "2️⃣", message: "'=' must have consistent whitespace on both sides", fixIts: ["insert whitespace"]),
1972+
],
1973+
fixedSource: """
1974+
func r22387625() {
1975+
let _ = 5
1976+
let _ = 5
1977+
}
1978+
"""
19731979
)
19741980
}
19751981

19761982
func testRecovery164() {
1983+
// https://github.com/apple/swift/issues/45723
19771984
assertParse(
19781985
"""
1979-
// https://github.com/apple/swift/issues/45723
19801986
do {
1981-
let _: Int= 5
1982-
let _: Array<Int>= []
1987+
let _: Int1️⃣= 5
1988+
let _: Array<Int>2️⃣= []
19831989
}
19841990
""",
19851991
diagnostics: [
1986-
// TODO: Old parser expected error on line 3: '=' must have consistent whitespace on both sides, Fix-It replacements: 13 - 13 = ' '
1987-
// TODO: Old parser expected error on line 4: '=' must have consistent whitespace on both sides, Fix-It replacements: 20 - 20 = ' '
1988-
]
1992+
DiagnosticSpec(locationMarker: "1️⃣", message: "'=' must have consistent whitespace on both sides", fixIts: ["insert whitespace"]),
1993+
DiagnosticSpec(locationMarker: "2️⃣", message: "'=' must have consistent whitespace on both sides", fixIts: ["insert whitespace"]),
1994+
],
1995+
fixedSource: """
1996+
do {
1997+
let _: Int = 5
1998+
let _: Array<Int> = []
1999+
}
2000+
"""
19892001
)
19902002
}
19912003

0 commit comments

Comments
 (0)