Skip to content

Commit

Permalink
Update with Swift 6
Browse files Browse the repository at this point in the history
  • Loading branch information
khlopko committed Aug 6, 2024
1 parent aad58a0 commit c66e735
Show file tree
Hide file tree
Showing 18 changed files with 502 additions and 479 deletions.
7 changes: 4 additions & 3 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"originHash" : "ac7a8b3c89189306fafff79842be1c9addc0a3faf5b97bab411a81122122615a",
"pins" : [
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "f1e9245226002bb134884345d4809b9543da3666",
"version" : "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-17-a"
"revision" : "06b5cdc432e93b60e3bdf53aff2857c6b312991a",
"version" : "600.0.0-prerelease-2024-07-30"
}
}
],
"version" : 2
"version" : 3
}
45 changes: 26 additions & 19 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,49 +1,56 @@
// swift-tools-version: 5.9
// swift-tools-version: 6.0

import PackageDescription
import CompilerPluginSupport
import PackageDescription

let package = Package(
name: "SweetDeclarations",
name: "swift-util-macros",
platforms: [
.macOS(.v10_15),
.iOS(.v13),
.tvOS(.v13),
.watchOS(.v6),
.macCatalyst(.v13)
.macCatalyst(.v13),
],
products: [
.library(
name: "SweetDeclarationsLib",
targets: ["SweetDeclarationsLib"]
name: "SwiftUtilMacros",
targets: ["SwiftUtilMacros"]
),
.executable(
name: "SweetDeclarationsClient",
targets: ["SweetDeclarationsClient"]
name: "SwiftUtilMacrosClient",
targets: ["SwiftUtilMacrosClient"]
),
],
dependencies: [
.package(
url: "https://github.com/apple/swift-syntax.git",
from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"
),
.package(url: "https://github.com/apple/swift-syntax.git", from: "600.0.0-latest")
],
targets: [
.macro(
name: "SweetDeclarationsPlugin",
name: "SwiftUtilMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
]
),
.target(
name: "SwiftUtilMacros",
dependencies: [
.target(name: "SwiftUtilMacrosPlugin")
]
),
.executableTarget(
name: "SwiftUtilMacrosClient",
dependencies: [
.target(name: "SwiftUtilMacros")
]
),
.target(name: "SweetDeclarationsLib", dependencies: ["SweetDeclarationsPlugin"]),
.executableTarget(name: "SweetDeclarationsClient", dependencies: ["SweetDeclarationsLib"]),
.testTarget(
name: "SweetDeclarationsLibTests",
name: "SwiftUtilMacrosPluginTests",
dependencies: [
"SweetDeclarationsLib",
.target(name: "SwiftUtilMacrosPlugin"),
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]
)
),
]
)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SweetDeclarations
# swift-util-macros

A set of Swift macros to provide convenient initialization / modification over structs and classes
declarations.
Expand Down
15 changes: 0 additions & 15 deletions Sources/SweetDeclarationsLib/Macros.swift

This file was deleted.

20 changes: 20 additions & 0 deletions Sources/SwiftUtilMacros/Macros.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Macros.swift
//

@attached(member, names: arbitrary)
public macro PublicInit() = #externalMacro(module: "SwiftUtilMacrosPlugin", type: "PublicInitMacro")

@attached(member, names: arbitrary)
public macro PublicInit(escaping: [Any.Type]) = #externalMacro(module: "SwiftUtilMacrosPlugin", type: "PublicInitMacro")

@attached(member, names: arbitrary)
public macro GranularUpdate() = #externalMacro(module: "SwiftUtilMacrosPlugin", type: "GranularUpdateMacro")

@attached(member, names: arbitrary)
public macro TestStub() = #externalMacro(module: "SwiftUtilMacrosPlugin", type: "TestStubMacro")

@attached(member)
@attached(extension, conformances: OptionSet)
public macro BitMask() = #externalMacro(module: "SwiftUtilMacrosPlugin", type: "BitMaskOptionSetMacro")

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@

import Foundation

import SweetDeclarationsLib
import SwiftUtilMacros

public typealias GetConnections = () -> [User]

@PublicInit(escaping: [GetConnections.self])
@PublicInit
@GranularUpdate
public struct User {
var desc: String { "\(id)+\(name)" }
public let id: String
public let name: Name
public let getConnections: GetConnections
public let getConnections: () -> [User]
public let getPublications: (_ startDate: Date) -> [String]
}

Expand Down Expand Up @@ -53,3 +51,4 @@ final class SomeProtocolStub {
return method3Result!
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,15 @@
import SwiftSyntax

extension SwiftSyntax.AttributeSyntax {

internal func macrosEscapingArgs() -> [String] {
let tupleElement = argument?
.as(TupleExprElementListSyntax.self)?.first?
.as(TupleExprElementSyntax.self)
let tupleElement = arguments?.as(LabeledExprListSyntax.self)?.first
guard tupleElement?.label?.text == "escaping" else {
return []
}
return tupleElement?.expression.as(ArrayExprSyntax.self)?.elements.compactMap {
$0
.as(ArrayElementSyntax.self)?.expression
return tupleElement?.expression.as(ArrayExprSyntax.self)?.elements.compactMap { element in
element.expression
.as(MemberAccessExprSyntax.self)?.base?
.as(IdentifierExprSyntax.self)?.identifier.text
.as(DeclReferenceExprSyntax.self)?.baseName.text
} ?? []
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,50 @@
// DeclarationProperty.swift
//

import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftDiagnostics

internal struct DeclarationProperty {
let propertyName: String
let propertyType: String
let isClosure: Bool
let explicitlyEscaping: Bool
}

struct TypeNotFoundMessage: DiagnosticMessage {
let message = "Property type not found or not supported. Specify type explicitly if its missing to fix this error"
let diagnosticID = SwiftDiagnostics.MessageID(domain: "PublicInitMacro", id: "TypeNotFound")
let severity: SwiftDiagnostics.DiagnosticSeverity = .error
extension DeclarationProperty {
func asInitParam(nillable: Bool) -> String {
let propertyType = decoratedType(
for: self,
nillable: nillable,
isExplicitlyEscaping: { _ in explicitlyEscaping }
)
return " \(propertyName): \(propertyType)"
}

internal static func gather(
private func decoratedType(
for property: DeclarationProperty,
nillable: Bool,
isExplicitlyEscaping: (String) -> Bool
) -> String {
var propertyType: String = property.propertyType
let isExplicitlyEscaping = isExplicitlyEscaping(propertyType)
if nillable {
if property.isClosure {
propertyType = "(\(propertyType))"
}
propertyType = "\(propertyType)? = nil"
}
if !nillable && isExplicitlyEscaping {
propertyType = "@escaping \(propertyType)"
}
return propertyType
}
}

extension DeclarationProperty {
static func gather(
from declaration: some SwiftSyntax.DeclGroupSyntax,
in context: some SwiftSyntaxMacros.MacroExpansionContext
) -> [DeclarationProperty] {
Expand All @@ -30,90 +60,73 @@ internal struct DeclarationProperty {
else {
return nil
}
let isComputed = varDecl.bindings.contains { $0.accessor?.is(CodeBlockSyntax.self) == true }
let isComputed = varDecl.bindings.contains { $0.accessorBlock != nil }
guard !isComputed else {
return nil
}
let typeSyntax = patternBindingSyntax?.typeAnnotation?.type
let propertyType: String
let isClosure: Bool
let explicitlyEscaping: Bool
if let optionalType = typeSyntax?.as(OptionalTypeSyntax.self) {
propertyType = optionalType.description
isClosure = optionalType.wrappedType
.as(TupleTypeSyntax.self)?.elements.first?
isClosure =
optionalType.wrappedType
.as(TupleTypeSyntax.self)?.elements.first?.type
.is(FunctionTypeSyntax.self) == true
} else if let simpleType = typeSyntax?.as(SimpleTypeIdentifierSyntax.self)?.name.text {
explicitlyEscaping = false
} else if let simpleType = typeSyntax?.as(IdentifierTypeSyntax.self)?.name.text {
propertyType = simpleType
isClosure = false
explicitlyEscaping = false
} else if let closureType = typeSyntax?.as(FunctionTypeSyntax.self) {
propertyType = closureType.description
isClosure = true
explicitlyEscaping = true
} else {
context.diagnose(Diagnostic(node: varDecl._syntaxNode, message: TypeNotFoundMessage()))
context.diagnose(
Diagnostic(node: varDecl._syntaxNode, message: TypeNotFoundMessage()))
return nil
}
return DeclarationProperty(
propertyName: propertyName,
propertyType: propertyType,
isClosure: isClosure
isClosure: isClosure,
explicitlyEscaping: explicitlyEscaping
)
}
}

let propertyName: String
let propertyType: String
let isClosure: Bool

func asInitParam(nillable: Bool, escapingPropertyTypes: [String]) -> String {
let propertyType = decoratedType(
for: self,
nillable: nillable,
isExplicitlyEscaping: { escapingPropertyTypes.contains($0) }
)
return " \(propertyName): \(propertyType)"
}

private func decoratedType(
for property: DeclarationProperty,
nillable: Bool,
isExplicitlyEscaping: (String) -> Bool
) -> String {
var propertyType: String = property.propertyType
let isExplicitlyEscaping = isExplicitlyEscaping(propertyType)
if nillable {
if property.isClosure {
propertyType = "(\(propertyType))"
}
propertyType = "\(propertyType)? = nil"
}
let isNotNilClosure = property.isClosure && !nillable
if isNotNilClosure || isExplicitlyEscaping {
propertyType = "@escaping \(propertyType)"
}
return propertyType
}

}

extension [DeclarationProperty] {

internal func asInitParams(
escapingPropertyTypes: [String],
nillable: Bool
) -> SwiftSyntax.DeclSyntax {
SwiftSyntax.DeclSyntax(stringLiteral: map {
$0.asInitParam(nillable: nillable, escapingPropertyTypes: escapingPropertyTypes)
}
.joined(separator: ",\n"))
SwiftSyntax.DeclSyntax(
stringLiteral: map {
$0.asInitParam(nillable: nillable)
}
.joined(separator: ",\n")
)
}

internal func asInitBody(
decorateAssignment: (_ propertyName: String) -> String = { $0 }
) -> SwiftSyntax.DeclSyntax {
SwiftSyntax.DeclSyntax(stringLiteral: map {
"self.\($0.propertyName) = \(decorateAssignment($0.propertyName))"
}
.joined(separator: "\n"))
SwiftSyntax.DeclSyntax(
stringLiteral: map {
"self.\($0.propertyName) = \(decorateAssignment($0.propertyName))"
}
.joined(separator: "\n")
)
}
}

extension DeclarationProperty {
struct TypeNotFoundMessage: DiagnosticMessage {
let message =
"Property type not found or not supported. Specify type explicitly if its missing to fix this error"
let diagnosticID = SwiftDiagnostics.MessageID(domain: "PublicInitMacro", id: "TypeNotFound")
let severity: SwiftDiagnostics.DiagnosticSeverity = .error
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct GranularUpdateMacro: MemberMacro {
let result: SwiftSyntax.DeclSyntax = """
public init(
from another: \(raw: typeName),
\(raw: properties.asInitParams(escapingPropertyTypes: [], nillable: true))
\(raw: properties.asInitParams(nillable: true))
) {
\(raw: properties.asInitBody(decorateAssignment: { "\($0) ?? another.\($0)" }))
}
Expand All @@ -36,10 +36,10 @@ public struct GranularUpdateMacro: MemberMacro {
from declaration: some SwiftSyntax.DeclGroupSyntax
) throws -> String {
if let structDecl = declaration.as(StructDeclSyntax.self) {
return structDecl.identifier.text
return structDecl.name.text
}
if let classDecl = declaration.as(ClassDeclSyntax.self) {
return classDecl.identifier.text
return classDecl.name.text
}
throw GenerationError.unsupportedType
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ struct SweetDeclarationsPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
GranularUpdateMacro.self,
PublicInitMacro.self,
TestStubMacro.self
TestStubMacro.self,
]
}
Loading

0 comments on commit c66e735

Please sign in to comment.