Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ugommirikwe committed Mar 7, 2024
0 parents commit ab14d16
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
52 changes: 52 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
import CompilerPluginSupport

let package = Package(
name: "CopyableMacro",
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "CopyableMacro",
targets: ["CopyableMacro"]
),
.executable(
name: "CopyableMacroClient",
targets: ["CopyableMacroClient"]
),
],
dependencies: [
// Depend on the Swift 5.9 release of SwiftSyntax
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
// Macro implementation that performs the source transformation of a macro.
.macro(
name: "CopyableMacroMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),

// Library that exposes a macro as part of its API, which is used in client programs.
.target(name: "CopyableMacro", dependencies: ["CopyableMacroMacros"]),

// A client of the library, which is able to use the macro in its own code.
.executableTarget(name: "CopyableMacroClient", dependencies: ["CopyableMacro"]),

// A test target used to develop the macro implementation.
.testTarget(
name: "CopyableMacroTests",
dependencies: [
"CopyableMacroMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]
),
]
)
11 changes: 11 additions & 0 deletions Sources/CopyableMacro/CopyableMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book

/// A macro that produces both a value and a string containing the
/// source code that generated the value. For example,
///
/// #stringify(x + y)
///
/// produces a tuple `(x + y, "x + y")`.
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "CopyableMacroMacros", type: "StringifyMacro")
8 changes: 8 additions & 0 deletions Sources/CopyableMacroClient/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import CopyableMacro

let a = 17
let b = 25

let (result, code) = #stringify(a + b)

print("The value \(result) was produced by the code \"\(code)\"")
33 changes: 33 additions & 0 deletions Sources/CopyableMacroMacros/CopyableMacroMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

/// Implementation of the `stringify` macro, which takes an expression
/// of any type and produces a tuple containing the value of that expression
/// and the source code that produced the value. For example
///
/// #stringify(x + y)
///
/// will expand to
///
/// (x + y, "x + y")
public struct StringifyMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = node.argumentList.first?.expression else {
fatalError("compiler bug: the macro does not have any arguments")
}

return "(\(argument), \(literal: argument.description))"
}
}

@main
struct CopyableMacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
StringifyMacro.self,
]
}
46 changes: 46 additions & 0 deletions Tests/CopyableMacroTests/CopyableMacroTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import XCTest

// Macro implementations build for the host, so the corresponding module is not available when cross-compiling. Cross-compiled tests may still make use of the macro itself in end-to-end tests.
#if canImport(CopyableMacroMacros)
import CopyableMacroMacros

let testMacros: [String: Macro.Type] = [
"stringify": StringifyMacro.self,
]
#endif

final class CopyableMacroTests: XCTestCase {
func testMacro() throws {
#if canImport(CopyableMacroMacros)
assertMacroExpansion(
"""
#stringify(a + b)
""",
expandedSource: """
(a + b, "a + b")
""",
macros: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
#endif
}

func testMacroWithStringLiteral() throws {
#if canImport(CopyableMacroMacros)
assertMacroExpansion(
#"""
#stringify("Hello, \(name)")
"""#,
expandedSource: #"""
("Hello, \(name)", #""Hello, \(name)""#)
"""#,
macros: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
#endif
}
}

0 comments on commit ab14d16

Please sign in to comment.