diff --git a/Sources/SwiftParser/Lexer/Cursor.swift b/Sources/SwiftParser/Lexer/Cursor.swift index adf4687aac7..16f5deb09b1 100644 --- a/Sources/SwiftParser/Lexer/Cursor.swift +++ b/Sources/SwiftParser/Lexer/Cursor.swift @@ -2044,6 +2044,7 @@ extension Lexer.Cursor { extension Lexer.Cursor { mutating func tryLexEditorPlaceholder(sourceBufferStart: Lexer.Cursor) -> Lexer.Result { precondition(self.is(at: "<") && self.is(offset: 1, at: "#")) + let start = self var ptr = self let leftAngleConsumed = ptr.advance(matching: "<") let poundConsumed = ptr.advance(matching: "#") @@ -2058,7 +2059,10 @@ extension Lexer.Cursor { let closingAngleConsumed = ptr.advance(matching: ">") precondition(closingAngleConsumed) self = ptr - return Lexer.Result(.identifier) + return Lexer.Result( + .identifier, + error: LexingDiagnostic(.editorPlaceholder, position: start) + ) default: break } diff --git a/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift index be345934a03..751cbe71806 100644 --- a/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/LexerDiagnosticMessages.swift @@ -38,6 +38,7 @@ public extension TokenError { /// Please order the cases in this enum alphabetically by case name. public enum StaticTokenError: String, DiagnosticMessage { + case editorPlaceholder = "editor placeholder in source file" case expectedBinaryExponentInHexFloatLiteral = "hexadecimal floating point literal must end with an exponent" case expectedClosingBraceInUnicodeEscape = #"expected '}' in \u{...} escape sequence"# case expectedDigitInFloatLiteral = "expected a digit in floating point exponent" @@ -132,6 +133,7 @@ public extension SwiftSyntax.TokenDiagnostic { } switch self.kind { + case .editorPlaceholder: return StaticTokenError.editorPlaceholder case .expectedBinaryExponentInHexFloatLiteral: return StaticTokenError.expectedBinaryExponentInHexFloatLiteral case .expectedClosingBraceInUnicodeEscape: return StaticTokenError.expectedClosingBraceInUnicodeEscape case .expectedDigitInFloatLiteral: return StaticTokenError.expectedDigitInFloatLiteral diff --git a/Sources/SwiftSyntax/TokenDiagnostic.swift b/Sources/SwiftSyntax/TokenDiagnostic.swift index 9d28defb11c..3cca78022ca 100644 --- a/Sources/SwiftSyntax/TokenDiagnostic.swift +++ b/Sources/SwiftSyntax/TokenDiagnostic.swift @@ -22,6 +22,7 @@ public struct TokenDiagnostic: Hashable { public enum Kind { // Please order these alphabetically + case editorPlaceholder case expectedBinaryExponentInHexFloatLiteral case expectedClosingBraceInUnicodeEscape case expectedDigitInFloatLiteral @@ -94,6 +95,7 @@ public struct TokenDiagnostic: Hashable { public var severity: Severity { switch kind { + case .editorPlaceholder: return .error case .expectedBinaryExponentInHexFloatLiteral: return .error case .expectedClosingBraceInUnicodeEscape: return .error case .expectedDigitInFloatLiteral: return .error diff --git a/Tests/SwiftParserTest/Assertions.swift b/Tests/SwiftParserTest/Assertions.swift index f2020e15ffc..252af0dbb88 100644 --- a/Tests/SwiftParserTest/Assertions.swift +++ b/Tests/SwiftParserTest/Assertions.swift @@ -87,7 +87,7 @@ private func assertTokens( var lexemeStartOffset = 0 for (actualLexeme, expectedLexeme) in zip(actual, expected) { defer { - lexemeStartOffset = actualLexeme.byteLength + lexemeStartOffset += actualLexeme.byteLength } if actualLexeme.rawTokenKind != expectedLexeme.rawTokenKind { XCTFail( diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 185715a7e7b..bdb881b1707 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -1379,12 +1379,15 @@ final class DeclarationTests: XCTestCase { assertParse( """ class Foo { - <#code#> + 1️⃣<#code#> } """, substructure: Syntax( MemberDeclListItemSyntax(decl: EditorPlaceholderDeclSyntax(identifier: .identifier("<#code#>"))) - ) + ), + diagnostics: [ + DiagnosticSpec(message: "editor placeholder in source file") + ] ) } diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index f62673be7c4..938fab92af3 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -838,9 +838,28 @@ final class ExpressionTests: XCTestCase { } func testCodeCompletionExpressions() { - assertParse("if !<#b1#> && !<#b2#> {}") - assertParse("if <#test#> {}") - assertParse("if <#b1#>, <#b2#> {}") + assertParse( + "if !1️⃣<#b1#> && !2️⃣<#b2#> {}", + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file"), + DiagnosticSpec(locationMarker: "2️⃣", message: "editor placeholder in source file"), + ] + ) + + assertParse( + "if 1️⃣<#test#> {}", + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file") + ] + ) + + assertParse( + "if 1️⃣<#b1#>, 2️⃣<#b2#> {}", + diagnostics: [ + DiagnosticSpec(locationMarker: "1️⃣", message: "editor placeholder in source file"), + DiagnosticSpec(locationMarker: "2️⃣", message: "editor placeholder in source file"), + ] + ) } func testKeywordApplyExpression() { diff --git a/Tests/SwiftParserTest/LexerTests.swift b/Tests/SwiftParserTest/LexerTests.swift index e3274f9a3c2..c386b840344 100644 --- a/Tests/SwiftParserTest/LexerTests.swift +++ b/Tests/SwiftParserTest/LexerTests.swift @@ -663,20 +663,20 @@ public class LexerTests: XCTestCase { func testEditorPlaceholders() { assertLexemes( - "!<#b1#> && !<#b2#>", + "!1️⃣<#b1#> && !2️⃣<#b2#>", lexemes: [ LexemeSpec(.prefixOperator, text: "!"), - LexemeSpec(.identifier, text: "<#b1#>", trailing: " "), + LexemeSpec(.identifier, text: "<#b1#>", trailing: " ", errorLocationMarker: "1️⃣", diagnostic: "editor placeholder in source file"), LexemeSpec(.binaryOperator, text: "&&", trailing: " "), LexemeSpec(.prefixOperator, text: "!"), - LexemeSpec(.identifier, text: "<#b2#>"), + LexemeSpec(.identifier, text: "<#b2#>", errorLocationMarker: "2️⃣", diagnostic: "editor placeholder in source file"), ] ) assertLexemes( - "<##>", + "1️⃣<##>", lexemes: [ - LexemeSpec(.identifier, text: "<##>", trailing: "") + LexemeSpec(.identifier, text: "<##>", trailing: "", diagnostic: "editor placeholder in source file") ] ) } diff --git a/Tests/SwiftParserTest/translated/IdentifiersTests.swift b/Tests/SwiftParserTest/translated/IdentifiersTests.swift index 499a4751f62..0e5a9667a5e 100644 --- a/Tests/SwiftParserTest/translated/IdentifiersTests.swift +++ b/Tests/SwiftParserTest/translated/IdentifiersTests.swift @@ -83,10 +83,10 @@ final class IdentifiersTests: XCTestCase { assertParse( """ // Placeholders are recognized as identifiers but with error. - func <#some name#>() {} + func 1️⃣<#some name#>() {} """, diagnostics: [ - // TODO: (good first issue) Old parser expected error on line 2: editor placeholder in source file + DiagnosticSpec(message: "editor placeholder in source file") ] ) }