Initial commit

This commit is contained in:
David Skrundz 2024-09-17 16:06:48 -06:00
commit b10e75dcd9
Signed by: DavidSkrundz
GPG Key ID: D7006AAB6214A600
8 changed files with 268 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

15
Package.resolved Normal file
View File

@ -0,0 +1,15 @@
{
"originHash" : "e51397b3369f3d57e433298148acc6f90a2116f05c1952cc811f11150b1ce7c1",
"pins" : [
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "cb53fa1bd3219b0b23ded7dfdd3b2baff266fd25",
"version" : "600.0.0"
}
}
],
"version" : 3
}

40
Package.swift Normal file
View File

@ -0,0 +1,40 @@
// swift-tools-version: 6.0
import PackageDescription
import CompilerPluginSupport
let package = Package(
name: "Superman",
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)],
products: [
.library(
name: "Superman",
targets: ["Superman"]
),
.executable(
name: "SupermanSample",
targets: ["SupermanSample"]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", from: "600.0.0"),
],
targets: [
.macro(
name: "SupermanMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
.target(name: "Superman", dependencies: ["SupermanMacros"]),
.executableTarget(name: "SupermanSample", dependencies: ["Superman"]),
.testTarget(
name: "SupermanTests",
dependencies: [
"SupermanMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]
),
]
)

View File

@ -0,0 +1,5 @@
@attached(body)
public macro SuperFirst() = #externalMacro(module: "SupermanMacros", type: "SuperFirstMacro")
@attached(body)
public macro SuperLast() = #externalMacro(module: "SupermanMacros", type: "SuperLastMacro")

View File

@ -0,0 +1,9 @@
enum Error: Swift.Error, CustomStringConvertible {
case message(String)
var description: String {
switch self {
case .message(let text): return text
}
}
}

View File

@ -0,0 +1,62 @@
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxMacros
@main
struct SupermanPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
SuperFirstMacro.self,
SuperLastMacro.self,
]
}
public struct SuperFirstMacro: BodyMacro {
public static func expansion(of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext) throws -> [CodeBlockItemSyntax] {
// Only allow Functions
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw Error.message("@SuperFirst only works on functions")
}
// Only allow Void Functions
guard funcDecl.signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text != "Void" else {
throw Error.message("@SuperFirst only works on Void functions")
}
// Requires Function Body
guard let funcBody = funcDecl.body else {
throw Error.message("@SuperFirst only works on functions with bodies")
}
let arguments = funcDecl.signature.parameterClause.parameters
let callArgs = arguments.map { argument in
"\(argument.firstName): \(argument.secondName ?? argument.firstName)"
}.joined(separator: ", ")
return ["super.\(funcDecl.name)(\(raw: callArgs))"] + Array(funcBody.statements)
}
}
public struct SuperLastMacro: BodyMacro {
public static func expansion(of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext) throws -> [CodeBlockItemSyntax] {
// Only allow Functions
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw Error.message("@SuperLast only works on functions")
}
// Requires Function Body
guard let funcBody = funcDecl.body else {
throw Error.message("@SuperLast only works on functions with bodies")
}
let arguments = funcDecl.signature.parameterClause.parameters
let callArgs = arguments.map { argument in
"\(argument.firstName): \(argument.secondName ?? argument.firstName)"
}.joined(separator: ", ")
return Array(funcBody.statements) + ["return super.\(funcDecl.name)(\(raw: callArgs))"]
}
}

View File

@ -0,0 +1,50 @@
import Superman
class Base {
func superFirst() {
print("Base superFirst")
}
func superFirstArgs(_ b: Bool, i: Int, double d: Double) {
print("Base superFirstArgs (\(b), \(i), \(d))")
}
func superLast() {
print("Base superLast")
}
func superLastArgs(_ b: Bool, i: Int, double d: Double) {
print("Base superLastArgs (\(b), \(i), \(d))")
}
}
class Child: Base {
@SuperFirst
override func superFirst() {
print("Child superFirst")
}
@SuperFirst
override func superFirstArgs(_ b: Bool, i: Int, double d: Double) {
print("Child superFirstArgs (\(b), \(i), \(d))")
}
@SuperLast
override func superLast() {
print("Child superLast")
}
@SuperLast
override func superLastArgs(_ b: Bool, i: Int, double d: Double) {
print("Child superLastArgs (\(b), \(i), \(d))")
}
}
func main() {
let child = Child()
child.superFirst()
child.superLast()
child.superFirstArgs(true, i: 1, double: 0.1)
child.superLastArgs(false, i: 0, double: 1.0)
}
main()

View File

@ -0,0 +1,79 @@
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import XCTest
#if canImport(SupermanMacros)
import SupermanMacros
let testMacros: [String: Macro.Type] = [
"SuperFirst": SuperFirstMacro.self,
"SuperLast": SuperLastMacro.self,
]
final class QSLibsTests: XCTestCase {
func testSuperFirst() throws {
assertMacroExpansion("""
@SuperFirst
func function() {
print()
}
""",
expandedSource: """
func function() {
super.function()
print()
}
""",
macros: testMacros)
}
func testSuperLast() throws {
assertMacroExpansion("""
@SuperLast
func function() {
print()
}
""",
expandedSource: """
func function() {
print()
return super.function()
}
""",
macros: testMacros)
}
func testSuperFirstArgs() throws {
assertMacroExpansion("""
@SuperFirst
func function(_ b: Bool, i: Int, double d: Double) {
print()
}
""",
expandedSource: """
func function(_ b: Bool, i: Int, double d: Double) {
super.function(_ : b, i: i, double : d)
print()
}
""",
macros: testMacros)
}
func testSuperLastArgs() throws {
assertMacroExpansion("""
@SuperLast
func function(_ b: Bool, i: Int, double d: Double) {
print()
}
""",
expandedSource: """
func function(_ b: Bool, i: Int, double d: Double) {
print()
return super.function(_ : b, i: i, double : d)
}
""",
macros: testMacros)
}
}
#endif