Skip to content

Commit 8acf58a

Browse files
committed
Make erroneous @DatabaseFunction applications easier to debug
The macro has to make assumptions about the types involved in the function, _i.e._ that they are "query representable." We rely on a `_columnWidth` property that produces the following, unhelpful error when dealing with types that aren't representable in queries: > Type 'CKShare' has no member '_columnWidth' By moving things to a function, the error provides better breadcrumbs: > Global function '_columnWidth' requires that 'CKShare' conform to > 'QueryExpression' The compiler generates more instructions here, but `_columnWidth` is only called a single time for database functions at the time of installation. The other fix is for `decode`, which is heavily overloaded, so the existing error: > No exact matches in call to instance method 'decode' Gets deemphasized for: > Global function '_requireQueryRepresentable' requires that 'Foo' > conform to 'QueryRepresentable'
1 parent 1447ea2 commit 8acf58a

File tree

3 files changed

+67
-45
lines changed

3 files changed

+67
-45
lines changed

Sources/StructuredQueriesSQLiteCore/DatabaseFunction.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,21 @@ extension AggregateDatabaseFunction {
115115
}
116116
}
117117
}
118+
119+
// NB: Provides better error diagnostics for '@DatabaseFunction' macro-generated code.
120+
//
121+
// - Type 'CKShare' has no member '_columnWidth'
122+
// + Global function '_columnWidth' requires that 'CKShare' conform to 'QueryExpression'
123+
@_transparent
124+
public func _columnWidth<T: QueryExpression>(_: T.Type) -> Int {
125+
T._columnWidth
126+
}
127+
128+
// NB: Provides better error diagnostics for '@DatabaseFunction' macro-generated code.
129+
//
130+
// - No exact matches in call to instance method 'decode'
131+
// + Global function '_requireQueryRepresentable' requires that 'Foo' conform to 'QueryRepresentable'
132+
@_transparent
133+
public func _requireQueryRepresentable<T: QueryRepresentable>(_: T.Type) -> T.Type {
134+
T.self
135+
}

Sources/StructuredQueriesSQLiteMacros/DatabaseFunctionMacro.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ extension DatabaseFunctionMacro: PeerMacro {
188188
argumentBindings.append(secondName)
189189

190190
argumentCounts.append("\(type)")
191-
decodings.append("let \(secondName) = try decoder.decode(\(type).self)")
191+
decodings.append(
192+
"let \(secondName) = try decoder.decode(_requireQueryRepresentable(\(type).self))"
193+
)
192194
decodingUnwrappings.append(
193195
"guard let \(secondName) else { throw InvalidInvocation() }"
194196
)
@@ -270,7 +272,9 @@ extension DatabaseFunctionMacro: PeerMacro {
270272
argumentBindings.append(parameterName)
271273

272274
argumentCounts.append("\(type)")
273-
decodings.append("let \(parameterName) = try decoder.decode(\(type).self)")
275+
decodings.append(
276+
"let \(parameterName) = try decoder.decode(_requireQueryRepresentable(\(type).self))"
277+
)
274278
decodingUnwrappings.append("guard let \(parameterName) else { throw InvalidInvocation() }")
275279
canThrowInvalidInvocation = true
276280
}
@@ -321,7 +325,7 @@ extension DatabaseFunctionMacro: PeerMacro {
321325
? "0"
322326
: """
323327
var argumentCount = 0
324-
\(argumentCounts.map { "argumentCount += \($0)._columnWidth\n" }.joined())\
328+
\(argumentCounts.map { "argumentCount += _columnWidth(\($0).self)\n" }.joined())\
325329
return argumentCount
326330
"""
327331

Tests/StructuredQueriesMacrosTests/DatabaseFunctionMacroTests.swift

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ extension SnapshotTests {
129129
public let name = "jsonCapitalize"
130130
public var argumentCount: Int? {
131131
var argumentCount = 0
132-
argumentCount += [String].JSONRepresentation._columnWidth
132+
argumentCount += _columnWidth([String].JSONRepresentation.self)
133133
return argumentCount
134134
}
135135
public let isDeterministic = false
@@ -147,7 +147,7 @@ extension SnapshotTests {
147147
public func invoke(
148148
_ decoder: inout some QueryDecoder
149149
) throws -> StructuredQueriesCore.QueryBinding {
150-
let strings = try decoder.decode([String].JSONRepresentation.self)
150+
let strings = try decoder.decode(_requireQueryRepresentable([String].JSONRepresentation.self))
151151
guard let strings else {
152152
throw InvalidInvocation()
153153
}
@@ -237,7 +237,7 @@ extension SnapshotTests {
237237
public let name = "currentDate"
238238
public var argumentCount: Int? {
239239
var argumentCount = 0
240-
argumentCount += String._columnWidth
240+
argumentCount += _columnWidth(String.self)
241241
return argumentCount
242242
}
243243
public let isDeterministic = false
@@ -255,7 +255,7 @@ extension SnapshotTests {
255255
public func invoke(
256256
_ decoder: inout some QueryDecoder
257257
) throws -> StructuredQueriesCore.QueryBinding {
258-
let format = try decoder.decode(String.self)
258+
let format = try decoder.decode(_requireQueryRepresentable(String.self))
259259
guard let format else {
260260
throw InvalidInvocation()
261261
}
@@ -295,7 +295,7 @@ extension SnapshotTests {
295295
public let name = "currentDate"
296296
public var argumentCount: Int? {
297297
var argumentCount = 0
298-
argumentCount += String._columnWidth
298+
argumentCount += _columnWidth(String.self)
299299
return argumentCount
300300
}
301301
public let isDeterministic = false
@@ -313,7 +313,7 @@ extension SnapshotTests {
313313
public func invoke(
314314
_ decoder: inout some QueryDecoder
315315
) throws -> StructuredQueriesCore.QueryBinding {
316-
let format = try decoder.decode(String.self)
316+
let format = try decoder.decode(_requireQueryRepresentable(String.self))
317317
guard let format else {
318318
throw InvalidInvocation()
319319
}
@@ -353,7 +353,7 @@ extension SnapshotTests {
353353
public let name = "currentDate"
354354
public var argumentCount: Int? {
355355
var argumentCount = 0
356-
argumentCount += String._columnWidth
356+
argumentCount += _columnWidth(String.self)
357357
return argumentCount
358358
}
359359
public let isDeterministic = false
@@ -371,7 +371,7 @@ extension SnapshotTests {
371371
public func invoke(
372372
_ decoder: inout some QueryDecoder
373373
) throws -> StructuredQueriesCore.QueryBinding {
374-
let format = try decoder.decode(String.self)
374+
let format = try decoder.decode(_requireQueryRepresentable(String.self))
375375
guard let format else {
376376
throw InvalidInvocation()
377377
}
@@ -411,7 +411,7 @@ extension SnapshotTests {
411411
public let name = "currentDate"
412412
public var argumentCount: Int? {
413413
var argumentCount = 0
414-
argumentCount += String._columnWidth
414+
argumentCount += _columnWidth(String.self)
415415
return argumentCount
416416
}
417417
public let isDeterministic = false
@@ -429,7 +429,7 @@ extension SnapshotTests {
429429
public func invoke(
430430
_ decoder: inout some QueryDecoder
431431
) throws -> StructuredQueriesCore.QueryBinding {
432-
let format = try decoder.decode(String.self)
432+
let format = try decoder.decode(_requireQueryRepresentable(String.self))
433433
guard let format else {
434434
throw InvalidInvocation()
435435
}
@@ -469,8 +469,8 @@ extension SnapshotTests {
469469
public let name = "concat"
470470
public var argumentCount: Int? {
471471
var argumentCount = 0
472-
argumentCount += String._columnWidth
473-
argumentCount += String._columnWidth
472+
argumentCount += _columnWidth(String.self)
473+
argumentCount += _columnWidth(String.self)
474474
return argumentCount
475475
}
476476
public let isDeterministic = false
@@ -488,8 +488,8 @@ extension SnapshotTests {
488488
public func invoke(
489489
_ decoder: inout some QueryDecoder
490490
) throws -> StructuredQueriesCore.QueryBinding {
491-
let first = try decoder.decode(String.self)
492-
let second = try decoder.decode(String.self)
491+
let first = try decoder.decode(_requireQueryRepresentable(String.self))
492+
let second = try decoder.decode(_requireQueryRepresentable(String.self))
493493
guard let first else {
494494
throw InvalidInvocation()
495495
}
@@ -549,7 +549,7 @@ extension SnapshotTests {
549549
public let name = "currentDate"
550550
public var argumentCount: Int? {
551551
var argumentCount = 0
552-
argumentCount += String?._columnWidth
552+
argumentCount += _columnWidth(String?.self)
553553
return argumentCount
554554
}
555555
public let isDeterministic = false
@@ -567,7 +567,7 @@ extension SnapshotTests {
567567
public func invoke(
568568
_ decoder: inout some QueryDecoder
569569
) throws -> StructuredQueriesCore.QueryBinding {
570-
let format = try decoder.decode(String?.self)
570+
let format = try decoder.decode(_requireQueryRepresentable(String?.self))
571571
guard let format else {
572572
throw InvalidInvocation()
573573
}
@@ -1041,8 +1041,8 @@ extension SnapshotTests {
10411041
public let name = "min"
10421042
public var argumentCount: Int? {
10431043
var argumentCount = 0
1044-
argumentCount += Int._columnWidth
1045-
argumentCount += Int._columnWidth
1044+
argumentCount += _columnWidth(Int.self)
1045+
argumentCount += _columnWidth(Int.self)
10461046
return argumentCount
10471047
}
10481048
public let isDeterministic = false
@@ -1063,8 +1063,8 @@ extension SnapshotTests {
10631063
public func invoke(
10641064
_ decoder: inout some QueryDecoder
10651065
) throws -> StructuredQueriesCore.QueryBinding {
1066-
let x = try decoder.decode(Int.self)
1067-
let y = try decoder.decode(Int.self)
1066+
let x = try decoder.decode(_requireQueryRepresentable(Int.self))
1067+
let y = try decoder.decode(_requireQueryRepresentable(Int.self))
10681068
guard let x else {
10691069
throw InvalidInvocation()
10701070
}
@@ -1108,8 +1108,8 @@ extension SnapshotTests {
11081108
public let name = "min"
11091109
public var argumentCount: Int? {
11101110
var argumentCount = 0
1111-
argumentCount += Int._columnWidth
1112-
argumentCount += Int._columnWidth
1111+
argumentCount += _columnWidth(Int.self)
1112+
argumentCount += _columnWidth(Int.self)
11131113
return argumentCount
11141114
}
11151115
public let isDeterministic = false
@@ -1130,8 +1130,8 @@ extension SnapshotTests {
11301130
public func invoke(
11311131
_ decoder: inout some QueryDecoder
11321132
) throws -> StructuredQueriesCore.QueryBinding {
1133-
let x = try decoder.decode(Int.self)
1134-
let y = try decoder.decode(Int.self)
1133+
let x = try decoder.decode(_requireQueryRepresentable(Int.self))
1134+
let y = try decoder.decode(_requireQueryRepresentable(Int.self))
11351135
guard let x else {
11361136
throw InvalidInvocation()
11371137
}
@@ -1172,8 +1172,8 @@ extension SnapshotTests {
11721172
public let name = "isValid"
11731173
public var argumentCount: Int? {
11741174
var argumentCount = 0
1175-
argumentCount += Reminder._columnWidth
1176-
argumentCount += Bool._columnWidth
1175+
argumentCount += _columnWidth(Reminder.self)
1176+
argumentCount += _columnWidth(Bool.self)
11771177
return argumentCount
11781178
}
11791179
public let isDeterministic = false
@@ -1191,8 +1191,8 @@ extension SnapshotTests {
11911191
public func invoke(
11921192
_ decoder: inout some QueryDecoder
11931193
) throws -> StructuredQueriesCore.QueryBinding {
1194-
let reminder = try decoder.decode(Reminder.self)
1195-
let override = try decoder.decode(Bool.self)
1194+
let reminder = try decoder.decode(_requireQueryRepresentable(Reminder.self))
1195+
let override = try decoder.decode(_requireQueryRepresentable(Bool.self))
11961196
guard let reminder else {
11971197
throw InvalidInvocation()
11981198
}
@@ -1238,7 +1238,7 @@ extension SnapshotTests {
12381238
public let name = "sum"
12391239
public var argumentCount: Int? {
12401240
var argumentCount = 0
1241-
argumentCount += Int._columnWidth
1241+
argumentCount += _columnWidth(Int.self)
12421242
return argumentCount
12431243
}
12441244
public let isDeterministic = false
@@ -1256,7 +1256,7 @@ extension SnapshotTests {
12561256
public func step(
12571257
_ decoder: inout some QueryDecoder
12581258
) throws -> Int {
1259-
let xs = try decoder.decode(Int.self)
1259+
let xs = try decoder.decode(_requireQueryRepresentable(Int.self))
12601260
guard let xs else {
12611261
throw InvalidInvocation()
12621262
}
@@ -1298,7 +1298,7 @@ extension SnapshotTests {
12981298
public let name = "sum"
12991299
public var argumentCount: Int? {
13001300
var argumentCount = 0
1301-
argumentCount += Int._columnWidth
1301+
argumentCount += _columnWidth(Int.self)
13021302
return argumentCount
13031303
}
13041304
public let isDeterministic = false
@@ -1316,7 +1316,7 @@ extension SnapshotTests {
13161316
public func step(
13171317
_ decoder: inout some QueryDecoder
13181318
) throws -> Int {
1319-
let xs = try decoder.decode(Int.self)
1319+
let xs = try decoder.decode(_requireQueryRepresentable(Int.self))
13201320
guard let xs else {
13211321
throw InvalidInvocation()
13221322
}
@@ -1370,8 +1370,8 @@ extension SnapshotTests {
13701370
public let name = "joined"
13711371
public var argumentCount: Int? {
13721372
var argumentCount = 0
1373-
argumentCount += String._columnWidth
1374-
argumentCount += String._columnWidth
1373+
argumentCount += _columnWidth(String.self)
1374+
argumentCount += _columnWidth(String.self)
13751375
return argumentCount
13761376
}
13771377
public let isDeterministic = false
@@ -1389,8 +1389,8 @@ extension SnapshotTests {
13891389
public func step(
13901390
_ decoder: inout some QueryDecoder
13911391
) throws -> (String, separator: String) {
1392-
let p0 = try decoder.decode(String.self)
1393-
let separator = try decoder.decode(String.self)
1392+
let p0 = try decoder.decode(_requireQueryRepresentable(String.self))
1393+
let separator = try decoder.decode(_requireQueryRepresentable(String.self))
13941394
guard let p0 else {
13951395
throw InvalidInvocation()
13961396
}
@@ -1437,7 +1437,7 @@ extension SnapshotTests {
14371437
public let name = "joined"
14381438
public var argumentCount: Int? {
14391439
var argumentCount = 0
1440-
argumentCount += [String].JSONRepresentation._columnWidth
1440+
argumentCount += _columnWidth([String].JSONRepresentation.self)
14411441
return argumentCount
14421442
}
14431443
public let isDeterministic = false
@@ -1455,7 +1455,7 @@ extension SnapshotTests {
14551455
public func step(
14561456
_ decoder: inout some QueryDecoder
14571457
) throws -> [String] {
1458-
let arrays = try decoder.decode([String].JSONRepresentation.self)
1458+
let arrays = try decoder.decode(_requireQueryRepresentable([String].JSONRepresentation.self))
14591459
guard let arrays else {
14601460
throw InvalidInvocation()
14611461
}
@@ -1501,7 +1501,7 @@ extension SnapshotTests {
15011501
public let name = "print"
15021502
public var argumentCount: Int? {
15031503
var argumentCount = 0
1504-
argumentCount += Int._columnWidth
1504+
argumentCount += _columnWidth(Int.self)
15051505
return argumentCount
15061506
}
15071507
public let isDeterministic = false
@@ -1519,7 +1519,7 @@ extension SnapshotTests {
15191519
public func step(
15201520
_ decoder: inout some QueryDecoder
15211521
) throws -> Int {
1522-
let xs = try decoder.decode(Int.self)
1522+
let xs = try decoder.decode(_requireQueryRepresentable(Int.self))
15231523
guard let xs else {
15241524
throw InvalidInvocation()
15251525
}
@@ -1570,7 +1570,7 @@ extension SnapshotTests {
15701570
public let name = "validatePositive"
15711571
public var argumentCount: Int? {
15721572
var argumentCount = 0
1573-
argumentCount += Int._columnWidth
1573+
argumentCount += _columnWidth(Int.self)
15741574
return argumentCount
15751575
}
15761576
public let isDeterministic = false
@@ -1588,7 +1588,7 @@ extension SnapshotTests {
15881588
public func step(
15891589
_ decoder: inout some QueryDecoder
15901590
) throws -> Int {
1591-
let xs = try decoder.decode(Int.self)
1591+
let xs = try decoder.decode(_requireQueryRepresentable(Int.self))
15921592
guard let xs else {
15931593
throw InvalidInvocation()
15941594
}

0 commit comments

Comments
 (0)