Skip to content

Commit edc2150

Browse files
Macro fixes for release/archive builds and Swift 6.2 (#71)
* Add member macro attribute. * wip * wip * wip * wip * wip * wip * wip * wip * wip * clean up * clean up * clean up * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent a4f0516 commit edc2150

File tree

12 files changed

+751
-184
lines changed

12 files changed

+751
-184
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ DerivedData/
77
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
88
.netrc
99
Package.resolved
10-
db.sqlite
10+
*.sqlite

Sources/StructuredQueries/Macros.swift

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,19 @@ import StructuredQueriesCore
99
conformances: Table,
1010
PartialSelectStatement,
1111
PrimaryKeyedTable,
12-
names: named(TableColumns),
13-
named(From),
14-
named(Draft),
12+
names: named(From),
1513
named(columns),
1614
named(init(_:)),
1715
named(init(decoder:)),
1816
named(QueryValue),
1917
named(schemaName),
2018
named(tableName)
2119
)
22-
@attached(
23-
memberAttribute
24-
)
20+
@attached(member, names: named(Draft), named(TableColumns))
21+
@attached(memberAttribute)
2522
public macro Table(
26-
_ name: String? = nil,
27-
schema schemaName: String? = nil
23+
_ name: String = "",
24+
schema schemaName: String = ""
2825
) =
2926
#externalMacro(
3027
module: "StructuredQueriesMacros",
@@ -38,9 +35,9 @@ public macro Table(
3835
/// - representableType: A type that represents the property type in a query expression. For types
3936
/// that don't have a single representation in SQL, like `Date` and `UUID`.
4037
/// - primaryKey: The column is its table's auto-incrementing primary key.
41-
@attached(accessor, names: named(willSet))
38+
@attached(peer)
4239
public macro Column(
43-
_ name: String? = nil,
40+
_ name: String = "",
4441
as representableType: (any QueryRepresentable.Type)? = nil,
4542
primaryKey: Bool = false
4643
) =
@@ -52,7 +49,7 @@ public macro Column(
5249
/// Tells Structured Queries not to consider the annotated property a column of the table
5350
///
5451
/// Like SwiftData's `@Transient` macro, but for SQL.
55-
@attached(accessor, names: named(willSet))
52+
@attached(peer)
5653
public macro Ephemeral() =
5754
#externalMacro(
5855
module: "StructuredQueriesMacros",
@@ -95,6 +92,7 @@ public macro Ephemeral() =
9592
names: named(Columns),
9693
named(init(decoder:))
9794
)
95+
@attached(member, names: named(Columns))
9896
public macro Selection() =
9997
#externalMacro(
10098
module: "StructuredQueriesMacros",
@@ -134,22 +132,3 @@ public macro sql<QueryValue>(
134132
as queryValueType: QueryValue.Type = QueryValue.self
135133
) -> SQLQueryExpression<QueryValue> =
136134
#externalMacro(module: "StructuredQueriesMacros", type: "SQLMacro")
137-
138-
// NB: Due to a bug in Swift, this macro is expanded internally by the '@Table' macro.
139-
// @attached(
140-
// memberAttribute
141-
// )
142-
// @attached(
143-
// extension,
144-
// conformances: Table,
145-
// names: named(TableColumns),
146-
// named(columns),
147-
// named(init(_:)),
148-
// named(init(decoder:)),
149-
// named(tableName)
150-
// )
151-
// public macro _Draft<T: Table>(_: T.Type) =
152-
// #externalMacro(
153-
// module: "StructuredQueriesMacros",
154-
// type: "TableMacro"
155-
// )

Sources/StructuredQueriesCore/Optional.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
public protocol _OptionalProtocol<Wrapped> {
22
associatedtype Wrapped
33
var _wrapped: Wrapped? { get }
4-
static var none: Self { get }
5-
static func some(_ wrapped: Wrapped) -> Self
4+
static var _none: Self { get }
5+
static func _some(_ wrapped: Wrapped) -> Self
66
}
77

88
extension Optional: _OptionalProtocol {
99
public var _wrapped: Wrapped? { self }
10+
public static var _none: Self { .none }
11+
public static func _some(_ wrapped: Wrapped) -> Self { .some(wrapped) }
1012
}
1113

1214
public protocol _OptionalPromotable<_Optionalized> {

Sources/StructuredQueriesCore/Statements/Insert.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ extension Table {
458458
conflictTargetColumnNames: conflictTargetColumnNames,
459459
conflictTargetFilter: targetFilter(Self.columns),
460460
values: values,
461-
updates: updates.map(Updates.init),
461+
updates: updates.map { Updates($0) },
462462
updateFilter: updateFilter(Self.columns),
463463
returning: []
464464
)
Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import SwiftSyntax
22
import SwiftSyntaxMacros
33

4-
public enum ColumnMacro: AccessorMacro {
5-
public static func expansion<
6-
Context: MacroExpansionContext,
7-
Declaration: DeclSyntaxProtocol
8-
>(
4+
public enum ColumnMacro: PeerMacro {
5+
public static func expansion<D: DeclSyntaxProtocol, C: MacroExpansionContext>(
96
of node: AttributeSyntax,
10-
providingAccessorsOf declaration: Declaration,
11-
in context: Context
12-
) throws -> [AccessorDeclSyntax] {
13-
return []
7+
providingPeersOf declaration: D,
8+
in context: C
9+
) throws -> [DeclSyntax] {
10+
[]
1411
}
1512
}
Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import SwiftSyntax
22
import SwiftSyntaxMacros
33

4-
public enum EphemeralMacro: AccessorMacro {
5-
public static func expansion<
6-
Context: MacroExpansionContext,
7-
Declaration: DeclSyntaxProtocol
8-
>(
4+
public enum EphemeralMacro: PeerMacro {
5+
public static func expansion<D: DeclSyntaxProtocol, C: MacroExpansionContext>(
96
of node: AttributeSyntax,
10-
providingAccessorsOf declaration: Declaration,
11-
in context: Context
12-
) throws -> [AccessorDeclSyntax] {
13-
return []
7+
providingPeersOf declaration: D,
8+
in context: C
9+
) throws -> [DeclSyntax] {
10+
[]
1411
}
1512
}

Sources/StructuredQueriesMacros/SelectionMacro.swift

Lines changed: 153 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ extension SelectionMacro: ExtensionMacro {
157157

158158
var conformances: [TypeSyntax] = []
159159
let protocolNames: [TokenSyntax] = ["QueryRepresentable"]
160-
let schemaConformances: [ExprSyntax] = ["\(moduleName).QueryExpression"]
161160
if let inheritanceClause = declaration.inheritanceClause {
162161
for type in protocolNames {
163162
if !inheritanceClause.inheritedTypes.contains(where: {
@@ -175,16 +174,7 @@ extension SelectionMacro: ExtensionMacro {
175174
return []
176175
}
177176

178-
let initArguments =
179-
allColumns
180-
.map { "\($0): some \(moduleName).QueryExpression\($1.map { "<\($0)>" } ?? "")" }
181-
.joined(separator: ",\n")
182-
let initAssignment: [String] =
183-
allColumns
184-
.map { #"\(\#($0.name).queryFragment) AS \#($0.name.text.quoted())"# }
185-
186177
let initDecoder: DeclSyntax = """
187-
188178
public init(decoder: inout some \(moduleName).QueryDecoder) throws {
189179
\(raw: (decodings + decodingUnwrappings + decodingAssignments).joined(separator: "\n"))
190180
}
@@ -194,17 +184,7 @@ extension SelectionMacro: ExtensionMacro {
194184
"""
195185
\(declaration.attributes.availability)extension \(type)\
196186
\(conformances.isEmpty ? "" : ": \(conformances, separator: ", ")") {
197-
public struct Columns: \(schemaConformances, separator: ", ") {
198-
public typealias QueryValue = \(type.trimmed)
199-
public let queryFragment: \(moduleName).QueryFragment
200-
public init(
201-
\(raw: initArguments)
202-
) {
203-
self.queryFragment = \"\"\"
204-
\(raw: initAssignment.joined(separator: ", "))
205-
\"\"\"
206-
}
207-
}\(initDecoder)
187+
\(initDecoder)
208188
}
209189
"""
210190
)
@@ -213,6 +193,158 @@ extension SelectionMacro: ExtensionMacro {
213193
}
214194
}
215195

196+
extension SelectionMacro: MemberMacro {
197+
public static func expansion<D: DeclGroupSyntax, C: MacroExpansionContext>(
198+
of node: AttributeSyntax,
199+
providingMembersOf declaration: D,
200+
conformingTo protocols: [TypeSyntax],
201+
in context: C
202+
) throws -> [DeclSyntax] {
203+
guard
204+
let declaration = declaration.as(StructDeclSyntax.self)
205+
else {
206+
return []
207+
}
208+
let type = IdentifierTypeSyntax(name: declaration.name.trimmed)
209+
var allColumns: [(name: TokenSyntax, type: TypeSyntax?)] = []
210+
var decodings: [String] = []
211+
var decodingUnwrappings: [String] = []
212+
var decodingAssignments: [String] = []
213+
var expansionFailed = false
214+
215+
let selfRewriter = SelfRewriter(selfEquivalent: type.name)
216+
for member in declaration.memberBlock.members {
217+
guard
218+
let property = member.decl.as(VariableDeclSyntax.self),
219+
!property.isStatic,
220+
!property.isComputed,
221+
property.bindings.count == 1,
222+
let binding = property.bindings.first,
223+
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.trimmed
224+
else { continue }
225+
226+
var columnQueryValueType =
227+
(binding.typeAnnotation?.type.trimmed
228+
?? binding.initializer?.value.literalType)
229+
.map { selfRewriter.rewrite($0).cast(TypeSyntax.self) }
230+
231+
for attribute in property.attributes {
232+
guard
233+
let attribute = attribute.as(AttributeSyntax.self),
234+
let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text,
235+
attributeName == "Column",
236+
case .argumentList(let arguments) = attribute.arguments
237+
else { continue }
238+
239+
for argumentIndex in arguments.indices {
240+
let argument = arguments[argumentIndex]
241+
242+
switch argument.label {
243+
case nil:
244+
var newArguments = arguments
245+
newArguments.remove(at: argumentIndex)
246+
expansionFailed = true
247+
248+
case .some(let label) where label.text == "as":
249+
guard
250+
let memberAccess = argument.expression.as(MemberAccessExprSyntax.self),
251+
memberAccess.declName.baseName.tokenKind == .keyword(.self),
252+
let base = memberAccess.base
253+
else {
254+
expansionFailed = true
255+
continue
256+
}
257+
258+
columnQueryValueType = "\(raw: base.trimmedDescription)"
259+
260+
case .some(let label) where label.text == "primaryKey":
261+
var newArguments = arguments
262+
newArguments.remove(at: argumentIndex)
263+
expansionFailed = true
264+
265+
case let argument?:
266+
fatalError("Unexpected argument: \(argument)")
267+
}
268+
}
269+
}
270+
271+
var assignedType: String? {
272+
binding
273+
.initializer?
274+
.value
275+
.as(FunctionCallExprSyntax.self)?
276+
.calledExpression
277+
.as(DeclReferenceExprSyntax.self)?
278+
.baseName
279+
.text
280+
}
281+
282+
allColumns.append((identifier, columnQueryValueType))
283+
let decodedType = columnQueryValueType?.asNonOptionalType()
284+
decodings.append(
285+
"""
286+
let \(identifier) = try decoder.decode(\(decodedType.map { "\($0).self" } ?? ""))
287+
"""
288+
)
289+
if columnQueryValueType.map({ !$0.isOptionalType }) ?? true {
290+
decodingUnwrappings.append(
291+
"""
292+
guard let \(identifier) else { throw QueryDecodingError.missingRequiredColumn }
293+
"""
294+
)
295+
}
296+
decodingAssignments.append(
297+
"""
298+
self.\(identifier) = \(identifier)
299+
"""
300+
)
301+
}
302+
303+
var conformances: [TypeSyntax] = []
304+
let protocolNames: [TokenSyntax] = ["QueryRepresentable"]
305+
let schemaConformances: [ExprSyntax] = ["\(moduleName).QueryExpression"]
306+
if let inheritanceClause = declaration.inheritanceClause {
307+
for type in protocolNames {
308+
if !inheritanceClause.inheritedTypes.contains(where: {
309+
[type.text, "\(moduleName).\(type)"].contains($0.type.trimmedDescription)
310+
}) {
311+
conformances.append("\(moduleName).\(type)")
312+
}
313+
}
314+
} else {
315+
conformances = protocolNames.map { "\(moduleName).\($0)" }
316+
}
317+
318+
guard !expansionFailed else {
319+
return []
320+
}
321+
322+
let initArguments =
323+
allColumns
324+
.map { "\($0): some \(moduleName).QueryExpression\($1.map { "<\($0)>" } ?? "")" }
325+
.joined(separator: ",\n")
326+
let initAssignment: [String] =
327+
allColumns
328+
.map { #"\(\#($0.name).queryFragment) AS \#($0.name.text.quoted())"# }
329+
330+
return [
331+
"""
332+
public struct Columns: \(schemaConformances, separator: ", ") {
333+
public typealias QueryValue = \(type.trimmed)
334+
public let queryFragment: \(moduleName).QueryFragment
335+
public init(
336+
\(raw: initArguments)
337+
) {
338+
self.queryFragment = \"\"\"
339+
\(raw: initAssignment.joined(separator: ", "))
340+
\"\"\"
341+
}
342+
}
343+
"""
344+
]
345+
}
346+
}
347+
216348
extension SelectionMacro: MemberAttributeMacro {
217349
public static func expansion<D: DeclGroupSyntax, T: DeclSyntaxProtocol, C: MacroExpansionContext>(
218350
of node: AttributeSyntax,

0 commit comments

Comments
 (0)