Skip to content

Commit bcc9bb1

Browse files
authored
Merge pull request #11 from wickwirew/release-bug
Fix release only lexer bug
2 parents 82c97aa + 8edc6de commit bcc9bb1

2 files changed

Lines changed: 92 additions & 4 deletions

File tree

Sources/Compiler/Parse/Lexer.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,20 @@ struct Lexer {
178178
currentColumn += 1
179179
}
180180
}
181+
182+
/// Gets `currentIndex` through a noninlined call. Used to work around
183+
/// an optimizer bug using a stale value of `currentIndex`.
184+
@inline(never)
185+
private func currentIndexOptimizerWorkaround() -> String.Index {
186+
return currentIndex
187+
}
181188

182189
// SQLite does not seem to really care what goes between the escape delimiters.
183190
// Table names will gladly take newlines and such.
184191
private mutating func parseEscapedIdentifier(closing: Character) -> Token {
185192
let tokenStart = startLocation()
186193
advance() // Opening
187-
let identifierStart = currentIndex
194+
let identifierStart = currentIndexOptimizerWorkaround()
188195

189196
while let current, current != closing {
190197
advance()
@@ -323,7 +330,7 @@ struct Lexer {
323330
advance() // 0
324331
advance() // x or X
325332

326-
let numberStart = currentIndex
333+
let numberStart = currentIndexOptimizerWorkaround()
327334

328335
while let current, Lexer.hexDigits.contains(current) || current == "_" {
329336
advance()
@@ -345,8 +352,8 @@ struct Lexer {
345352
private mutating func parseStringContents() -> (Substring, SourceLocation) {
346353
let tokenStart = startLocation()
347354
advance()
348-
let stringStart = currentIndex
349-
355+
let stringStart = currentIndexOptimizerWorkaround()
356+
350357
while let current, current != "'" {
351358
advance()
352359
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// LexerSliceReleaseTests.swift
3+
//
4+
//
5+
// Created by Wes Wickwire on 6/27/26.
6+
//
7+
8+
import Testing
9+
@testable import Compiler
10+
11+
/// These tests only failed in a release build.
12+
@Suite
13+
struct LexerSliceReleaseTests {
14+
@Test func escapedIdentifierStripsDelimiters() {
15+
#expect(firstIdentifier("\"MyModel.ID\"") == "MyModel.ID")
16+
#expect(firstIdentifier("[MyModel.ID]") == "MyModel.ID")
17+
#expect(firstIdentifier("`MyModel.ID`") == "MyModel.ID")
18+
}
19+
20+
@Test func stringLiteralStripsQuotes() {
21+
#expect(firstString("'hello world'") == "hello world")
22+
}
23+
24+
@Test func hexLiteral() {
25+
#expect(firstHex("0xFF") == 255)
26+
}
27+
28+
@Test func scientificNotation() {
29+
#expect(firstDouble("1e3") == 1000)
30+
#expect(firstDouble("1e-2") == 0.01)
31+
}
32+
33+
@Test func aliasedColumnTypeGeneratesUnquotedName() {
34+
var compiler = Compiler()
35+
let (_, diags) = compiler.compile(migration: """
36+
CREATE TABLE myTable (
37+
id INTEGER AS "MyModel.ID"
38+
);
39+
""")
40+
#expect(diags.isEmpty)
41+
let table = compiler.schema.tables.values.first!
42+
let col = table.columns.values.first!
43+
#expect(!String(describing: col.type).contains("\""))
44+
}
45+
46+
private func firstIdentifier(_ src: String) -> String? {
47+
var lexer = Lexer(source: src)
48+
while true {
49+
let t = lexer.next()
50+
if case .eof = t.kind { return nil }
51+
if case let .identifier(v) = t.kind { return String(v) }
52+
}
53+
}
54+
55+
private func firstString(_ src: String) -> String? {
56+
var lexer = Lexer(source: src)
57+
while true {
58+
let t = lexer.next()
59+
if case .eof = t.kind { return nil }
60+
if case let .string(v) = t.kind { return String(v) }
61+
}
62+
}
63+
64+
private func firstHex(_ src: String) -> Int? {
65+
var lexer = Lexer(source: src)
66+
while true {
67+
let t = lexer.next()
68+
if case .eof = t.kind { return nil }
69+
if case let .hex(v) = t.kind { return v }
70+
}
71+
}
72+
73+
private func firstDouble(_ src: String) -> Double? {
74+
var lexer = Lexer(source: src)
75+
while true {
76+
let t = lexer.next()
77+
if case .eof = t.kind { return nil }
78+
if case let .double(v) = t.kind { return v }
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)