Skip to content

Commit b3cd386

Browse files
committed
Merge pull request #1380 from kimdv/kimdv/add-diagnostic-message-for-identifier-editor-placeholder
2 parents cf2d4c6 + 0054642 commit b3cd386

File tree

10 files changed

+102
-25
lines changed

10 files changed

+102
-25
lines changed

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 22 additions & 2 deletions
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
@@ -2044,6 +2060,7 @@ extension Lexer.Cursor {
20442060
extension Lexer.Cursor {
20452061
mutating func tryLexEditorPlaceholder(sourceBufferStart: Lexer.Cursor) -> Lexer.Result {
20462062
precondition(self.is(at: "<") && self.is(offset: 1, at: "#"))
2063+
let start = self
20472064
var ptr = self
20482065
let leftAngleConsumed = ptr.advance(matching: "<")
20492066
let poundConsumed = ptr.advance(matching: "#")
@@ -2058,7 +2075,10 @@ extension Lexer.Cursor {
20582075
let closingAngleConsumed = ptr.advance(matching: ">")
20592076
precondition(closingAngleConsumed)
20602077
self = ptr
2061-
return Lexer.Result(.identifier)
2078+
return Lexer.Result(
2079+
.identifier,
2080+
error: LexingDiagnostic(.editorPlaceholder, position: start)
2081+
)
20622082
default:
20632083
break
20642084
}

Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public extension TokenError {
3838

3939
/// Please order the cases in this enum alphabetically by case name.
4040
public enum StaticTokenError: String, DiagnosticMessage {
41+
case editorPlaceholder = "editor placeholder in source file"
42+
case equalMustHaveConsistentWhitespaceOnBothSides = "'=' must have consistent whitespace on both sides"
4143
case expectedBinaryExponentInHexFloatLiteral = "hexadecimal floating point literal must end with an exponent"
4244
case expectedClosingBraceInUnicodeEscape = #"expected '}' in \u{...} escape sequence"#
4345
case expectedDigitInFloatLiteral = "expected a digit in floating point exponent"
@@ -132,6 +134,8 @@ public extension SwiftSyntax.TokenDiagnostic {
132134
}
133135

134136
switch self.kind {
137+
case .editorPlaceholder: return StaticTokenError.editorPlaceholder
138+
case .equalMustHaveConsistentWhitespaceOnBothSides: return StaticTokenError.equalMustHaveConsistentWhitespaceOnBothSides
135139
case .expectedBinaryExponentInHexFloatLiteral: return StaticTokenError.expectedBinaryExponentInHexFloatLiteral
136140
case .expectedClosingBraceInUnicodeEscape: return StaticTokenError.expectedClosingBraceInUnicodeEscape
137141
case .expectedDigitInFloatLiteral: return StaticTokenError.expectedDigitInFloatLiteral
@@ -198,6 +202,15 @@ public extension SwiftSyntax.TokenDiagnostic {
198202
return [
199203
FixIt(message: .replaceCurlyQuoteByNormalQuote, changes: [[.replace(oldNode: Syntax(token), newNode: Syntax(fixedToken))]])
200204
]
205+
case .equalMustHaveConsistentWhitespaceOnBothSides:
206+
let fixedToken =
207+
token
208+
.with(\.leadingTrivia, .space)
209+
.with(\.trailingTrivia, .space)
210+
211+
return [
212+
FixIt(message: .insertWhitespace, changes: [[.replace(oldNode: Syntax(token), newNode: Syntax(fixedToken))]])
213+
]
201214
default:
202215
return []
203216
}

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,9 @@ extension FixItMessage where Self == StaticParserFixIt {
498498
public static var insertNewline: Self {
499499
.init("insert newline")
500500
}
501+
public static var insertWhitespace: Self {
502+
.init("insert whitespace")
503+
}
501504
public static var joinIdentifiers: Self {
502505
.init("join the identifiers together")
503506
}

Sources/SwiftSyntax/TokenDiagnostic.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public struct TokenDiagnostic: Hashable {
2222
public enum Kind {
2323
// Please order these alphabetically
2424

25+
case editorPlaceholder
26+
case equalMustHaveConsistentWhitespaceOnBothSides
2527
case expectedBinaryExponentInHexFloatLiteral
2628
case expectedClosingBraceInUnicodeEscape
2729
case expectedDigitInFloatLiteral
@@ -94,6 +96,8 @@ public struct TokenDiagnostic: Hashable {
9496

9597
public var severity: Severity {
9698
switch kind {
99+
case .editorPlaceholder: return .error
100+
case .equalMustHaveConsistentWhitespaceOnBothSides: return .error
97101
case .expectedBinaryExponentInHexFloatLiteral: return .error
98102
case .expectedClosingBraceInUnicodeEscape: return .error
99103
case .expectedDigitInFloatLiteral: return .error

Tests/SwiftParserTest/Assertions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private func assertTokens(
8787
var lexemeStartOffset = 0
8888
for (actualLexeme, expectedLexeme) in zip(actual, expected) {
8989
defer {
90-
lexemeStartOffset = actualLexeme.byteLength
90+
lexemeStartOffset += actualLexeme.byteLength
9191
}
9292
if actualLexeme.rawTokenKind != expectedLexeme.rawTokenKind {
9393
XCTFail(

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,12 +1385,15 @@ final class DeclarationTests: XCTestCase {
13851385
assertParse(
13861386
"""
13871387
class Foo {
1388-
<#code#>
1388+
1️⃣<#code#>
13891389
}
13901390
""",
13911391
substructure: Syntax(
13921392
MemberDeclListItemSyntax(decl: EditorPlaceholderDeclSyntax(identifier: .identifier("<#code#>")))
1393-
)
1393+
),
1394+
diagnostics: [
1395+
DiagnosticSpec(message: "editor placeholder in source file")
1396+
]
13941397
)
13951398
}
13961399

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -838,9 +838,28 @@ final class ExpressionTests: XCTestCase {
838838
}
839839

840840
func testCodeCompletionExpressions() {
841-
assertParse("if !<#b1#> && !<#b2#> {}")
842-
assertParse("if <#test#> {}")
843-
assertParse("if <#b1#>, <#b2#> {}")
841+
assertParse(
842+
"if !1️⃣<#b1#> && !2️⃣<#b2#> {}",
843+
diagnostics: [
844+
DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file"),
845+
DiagnosticSpec(locationMarker: "2️⃣", message: "editor placeholder in source file"),
846+
]
847+
)
848+
849+
assertParse(
850+
"if 1️⃣<#test#> {}",
851+
diagnostics: [
852+
DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file")
853+
]
854+
)
855+
856+
assertParse(
857+
"if 1️⃣<#b1#>, 2️⃣<#b2#> {}",
858+
diagnostics: [
859+
DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file"),
860+
DiagnosticSpec(locationMarker: "2️⃣", message: "editor placeholder in source file"),
861+
]
862+
)
844863
}
845864

846865
func testKeywordApplyExpression() {

Tests/SwiftParserTest/LexerTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -663,20 +663,20 @@ public class LexerTests: XCTestCase {
663663

664664
func testEditorPlaceholders() {
665665
assertLexemes(
666-
"!<#b1#> && !<#b2#>",
666+
"!1️⃣<#b1#> && !2️⃣<#b2#>",
667667
lexemes: [
668668
LexemeSpec(.prefixOperator, text: "!"),
669-
LexemeSpec(.identifier, text: "<#b1#>", trailing: " "),
669+
LexemeSpec(.identifier, text: "<#b1#>", trailing: " ", errorLocationMarker: "1️⃣", diagnostic: "editor placeholder in source file"),
670670
LexemeSpec(.binaryOperator, text: "&&", trailing: " "),
671671
LexemeSpec(.prefixOperator, text: "!"),
672-
LexemeSpec(.identifier, text: "<#b2#>"),
672+
LexemeSpec(.identifier, text: "<#b2#>", errorLocationMarker: "2️⃣", diagnostic: "editor placeholder in source file"),
673673
]
674674
)
675675

676676
assertLexemes(
677-
"<##>",
677+
"1️⃣<##>",
678678
lexemes: [
679-
LexemeSpec(.identifier, text: "<##>", trailing: "")
679+
LexemeSpec(.identifier, text: "<##>", trailing: "", diagnostic: "editor placeholder in source file")
680680
]
681681
)
682682
}

Tests/SwiftParserTest/translated/IdentifiersTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ final class IdentifiersTests: XCTestCase {
8383
assertParse(
8484
"""
8585
// Placeholders are recognized as identifiers but with error.
86-
func <#some name#>() {}
86+
func 1️⃣<#some name#>() {}
8787
""",
8888
diagnostics: [
89-
// TODO: (good first issue) Old parser expected error on line 2: editor placeholder in source file
89+
DiagnosticSpec(message: "editor placeholder in source file")
9090
]
9191
)
9292
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,14 +1962,21 @@ final class RecoveryTests: XCTestCase {
19621962
"""
19631963
// <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️⃣= 4
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"),
1971+
DiagnosticSpec(locationMarker: "2️⃣", message: "'=' must have consistent whitespace on both sides"),
1972+
],
1973+
fixedSource: """
1974+
// <rdar://problem/22387625> QoI: Common errors: 'let x= 5' and 'let x =5' could use Fix-its
1975+
func r22387625() {
1976+
let _ = 4
1977+
let _ = 5
1978+
}
1979+
"""
19731980
)
19741981
}
19751982

@@ -1978,14 +1985,22 @@ final class RecoveryTests: XCTestCase {
19781985
"""
19791986
// https://github.com/apple/swift/issues/45723
19801987
do {
1981-
let _: Int= 5
1982-
let _: Array<Int>= []
1988+
let _: Int1️⃣= 5
1989+
let _: Array<Int>2️⃣= []
19831990
}
19841991
""",
19851992
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-
]
1993+
DiagnosticSpec(locationMarker: "1️⃣", message: "'=' must have consistent whitespace on both sides"),
1994+
DiagnosticSpec(locationMarker: "2️⃣", message: "'=' must have consistent whitespace on both sides"),
1995+
],
1996+
applyFixIts: ["insert whitespace", "insert whitespace"],
1997+
fixedSource: """
1998+
// https://github.com/apple/swift/issues/45723
1999+
do {
2000+
let _: Int = 5
2001+
let _: Array<Int> = []
2002+
}
2003+
"""
19892004
)
19902005
}
19912006

0 commit comments

Comments
 (0)