Skip to content

Commit 5d455f4

Browse files
committed
Fix a bug that caused the lexer’s cursor to end up in an incorrect state after resetForSplit
1 parent 6143e9d commit 5d455f4

File tree

5 files changed

+25
-56
lines changed

5 files changed

+25
-56
lines changed

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -695,16 +695,6 @@ extension Lexer.Cursor {
695695
mutating func advanceValidatingUTF8Character() -> Unicode.Scalar? {
696696
return Unicode.Scalar.lexing(advance: { self.advance() }, peek: { self.peek(at: 0) })
697697
}
698-
699-
/// Rever the lexer by `offset` bytes. This should only be used by `resetForSplit`.
700-
/// This must not back up by more bytes than the last token because that would
701-
/// require us to also update `previousTokenKind`, which we don't do in this
702-
/// function
703-
mutating func backUp(by offset: Int) {
704-
assert(!self.isAtStartOfFile)
705-
self.previous = self.input.baseAddress!.advanced(by: -(offset + 1)).pointee
706-
self.input = UnsafeBufferPointer(start: self.input.baseAddress!.advanced(by: -offset), count: self.input.count + offset)
707-
}
708698
}
709699

710700
// MARK: - Boundness of operators

Sources/SwiftParser/Lexer/LexemeSequence.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,14 @@ extension Lexer {
6161
return self.nextToken
6262
}
6363

64+
/// Reset the lexeme sequence to the state we were in when lexing `splitToken`
65+
/// but after we consumed `consumedPrefix` bytes from `splitToken`.
6466
/// - Warning: Do not add more usages of this function.
65-
mutating func resetForSplit(of bytes: Int) -> Lexer.Lexeme {
66-
guard bytes > 0 else {
67-
return self.advance()
67+
mutating func resetForSplit(splitToken: Lexeme, consumedPrefix: Int) -> Lexer.Lexeme {
68+
self.cursor = splitToken.cursor
69+
for _ in 0..<consumedPrefix {
70+
_ = self.cursor.advance()
6871
}
69-
70-
// FIXME: This is kind of ridiculous. We shouldn't have to look backwards
71-
// in the token stream. We should be fusing together runs of operator and
72-
// identifier characters in the parser, not splitting and backing up
73-
// again in the lexer.
74-
let backUpLength = self.nextToken.byteLength + bytes
75-
self.cursor.backUp(by: backUpLength)
7672
self.nextToken = self.cursor.nextToken(sourceBufferStart: self.sourceBufferStart, stateAllocator: lexerStateAllocator)
7773
return self.advance()
7874
}

Sources/SwiftParser/Lookahead.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,10 @@ extension Parser.Lookahead {
122122
}
123123
assert(tokenText.hasPrefix(prefix))
124124

125-
// See also: Parser.consumePrefix(_:as:)
126-
let offset =
127-
(self.currentToken.trailingTriviaByteLength
128-
+ tokenText.count
129-
- prefix.count)
130-
self.currentToken = self.lexemes.resetForSplit(of: offset)
125+
self.currentToken = self.lexemes.resetForSplit(
126+
splitToken: self.currentToken,
127+
consumedPrefix: self.currentToken.leadingTriviaByteLength + prefix.count
128+
)
131129
}
132130
}
133131

Sources/SwiftParser/Parser.swift

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -555,36 +555,10 @@ extension Parser {
555555

556556
self.adjustNestingLevel(for: tokenKind)
557557

558-
// ... or a multi-character token with the first N characters being the one
559-
// that we want to consume as a separate token.
560-
// Careful: We need to reset the lexer to a point just before it saw the
561-
// current token, plus the split point. That means we need to take trailing
562-
// trivia into account for the current token, but we only need to take the
563-
// number of UTF-8 bytes of the text of the split - no trivia necessary.
564-
//
565-
// <TOKEN<trailing trivia>> <NEXT TOKEN> ... -> <T> <OKEN<trailing trivia>> <NEXT TOKEN>
566-
//
567-
// The current calculation is:
568-
//
569-
// <<leading trivia>TOKEN<trailing trivia>>
570-
// CURSOR ^
571-
// + trailing trivia length
572-
//
573-
// <<leading trivia>TOKEN<trailing trivia>>
574-
// CURSOR ^
575-
// + content length
576-
//
577-
// <<leading trivia>TOKEN<trailing trivia>>
578-
// CURSOR ^
579-
// - split point length
580-
//
581-
// <<leading trivia>TOKEN<trailing trivia>>
582-
// CURSOR ^
583-
let offset =
584-
(self.currentToken.trailingTriviaByteLength
585-
+ tokenText.count
586-
- prefix.count)
587-
self.currentToken = self.lexemes.resetForSplit(of: offset)
558+
self.currentToken = self.lexemes.resetForSplit(
559+
splitToken: self.currentToken,
560+
consumedPrefix: self.currentToken.leadingTriviaByteLength + prefix.count
561+
)
588562
return tok
589563
}
590564
}

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,4 +1666,15 @@ final class StatementExpressionTests: XCTestCase {
16661666
]
16671667
)
16681668
}
1669+
1670+
func testStringLiteralAfterKeyPath() {
1671+
AssertParse(
1672+
#"""
1673+
\String.?1️⃣""
1674+
"""#,
1675+
diagnostics: [
1676+
DiagnosticSpec(message: "consecutive statements on a line must be separated by ';'")
1677+
]
1678+
)
1679+
}
16691680
}

0 commit comments

Comments
 (0)