Skip to content

Commit 82ea68d

Browse files
committed
Add the TracingMacros module and the @Traced macro
This is defined as a separate product so that users who don't want to pay the compile-time cost of macros don't have to use it, you opt-in to that cost by depending on the TracingMacros module. The @Traced macro is only available in Swift 6.0 compilers since function body macros were introduced in Swift 6.0. This adds minimum OS versions for Apple platforms in order to be able to depend on SwiftSyntax. This applies to the whole package since there are no per-target platform specifications. Most notably: This raises the macOS minimum deployment target from the (implicit) 10.13 to 10.15.
1 parent bd2d421 commit 82ea68d

File tree

4 files changed

+204
-1
lines changed

4 files changed

+204
-1
lines changed

Package.swift

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
// swift-tools-version:5.9
22
import PackageDescription
3+
import CompilerPluginSupport
34

45
let package = Package(
56
name: "swift-distributed-tracing",
7+
platforms: [
8+
.macOS(.v10_15),
9+
.iOS(.v13),
10+
.tvOS(.v13),
11+
.watchOS(.v6),
12+
.macCatalyst(.v13),
13+
],
614
products: [
715
.library(name: "Instrumentation", targets: ["Instrumentation"]),
816
.library(name: "Tracing", targets: ["Tracing"]),
17+
.library(name: "TracingMacros", targets: ["TracingMacros"]),
918
],
1019
dependencies: [
11-
.package(url: "https://github.com/apple/swift-service-context.git", from: "1.1.0")
20+
.package(url: "https://github.com/apple/swift-service-context.git", from: "1.1.0"),
21+
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.0-latest"),
1222
],
1323
targets: [
1424
// ==== --------------------------------------------------------------------------------------------------------
@@ -43,5 +53,32 @@ let package = Package(
4353
.target(name: "Tracing")
4454
]
4555
),
56+
57+
// ==== --------------------------------------------------------------------------------------------------------
58+
// MARK: TracingMacros
59+
60+
.target(
61+
name: "TracingMacros",
62+
dependencies: [
63+
.target(name: "Tracing"),
64+
.target(name: "TracingMacrosImplementation"),
65+
]
66+
),
67+
.macro(
68+
name: "TracingMacrosImplementation",
69+
dependencies: [
70+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
71+
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
72+
]
73+
),
74+
.testTarget(
75+
name: "TracingMacrosTests",
76+
dependencies: [
77+
.target(name: "Tracing"),
78+
.target(name: "TracingMacros"),
79+
.target(name: "TracingMacrosImplementation"),
80+
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
81+
]
82+
),
4683
]
4784
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Distributed Tracing open source project
4+
//
5+
// Copyright (c) 2020-2024 Apple Inc. and the Swift Distributed Tracing project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift Distributed Tracing project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
@_exported import ServiceContextModule
15+
import Tracing
16+
17+
#if compiler(>=6.0)
18+
@attached(body)
19+
public macro Traced(
20+
_ operationName: String? = nil,
21+
context: ServiceContext? = nil,
22+
ofKind kind: SpanKind? = nil,
23+
span spanName: String? = nil
24+
) = #externalMacro(module: "TracingMacrosImplementation", type: "TracedMacro")
25+
#endif
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Distributed Tracing open source project
4+
//
5+
// Copyright (c) 2020-2024 Apple Inc. and the Swift Distributed Tracing project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift Distributed Tracing project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import SwiftCompilerPlugin
15+
import SwiftSyntax
16+
import SwiftSyntaxBuilder
17+
import SwiftSyntaxMacros
18+
19+
#if compiler(>=6.0)
20+
public struct TracedMacro: BodyMacro {
21+
public static func expansion(
22+
of node: AttributeSyntax,
23+
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
24+
in context: some MacroExpansionContext
25+
) throws -> [CodeBlockItemSyntax] {
26+
guard let function = declaration.as(FunctionDeclSyntax.self),
27+
let body = function.body
28+
else {
29+
throw MacroExpansionErrorMessage("expected a function with a body")
30+
}
31+
32+
let operationName = StringLiteralExprSyntax(content: function.name.text)
33+
let withSpanCall: ExprSyntax = "withSpan(\(operationName))"
34+
let withSpanExpr: ExprSyntax = "\(withSpanCall) { span in \(body.statements) }"
35+
36+
return ["\(withSpanExpr)"]
37+
}
38+
}
39+
#endif
40+
41+
@main
42+
struct TracingMacroPlugin: CompilerPlugin {
43+
#if compiler(>=6.0)
44+
let providingMacros: [Macro.Type] = [
45+
TracedMacro.self,
46+
]
47+
#else
48+
let providingMacros: [Macro.Type] = []
49+
#endif
50+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Distributed Tracing open source project
4+
//
5+
// Copyright (c) 2020-2024 Apple Inc. and the Swift Distributed Tracing project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift Distributed Tracing project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import XCTest
15+
import SwiftSyntaxMacrosTestSupport
16+
17+
import Tracing
18+
import TracingMacros
19+
import TracingMacrosImplementation
20+
21+
#if compiler(>=6.0)
22+
23+
final class TracedMacroTests: XCTestCase {
24+
func test_tracedMacro_requires_body() {
25+
assertMacroExpansion(
26+
"""
27+
@Traced
28+
func funcWithoutBody()
29+
""",
30+
expandedSource: """
31+
func funcWithoutBody()
32+
""",
33+
diagnostics: [
34+
.init(message: "expected a function with a body", line: 1, column: 1),
35+
],
36+
macros: ["Traced": TracedMacro.self]
37+
)
38+
}
39+
40+
func test_tracedMacro_sync_nothrow() {
41+
assertMacroExpansion(
42+
"""
43+
@Traced
44+
func syncNonthrowingExample(param: Int) {
45+
print(param)
46+
}
47+
""",
48+
expandedSource: """
49+
func syncNonthrowingExample(param: Int) {
50+
withSpan("syncNonthrowingExample") { span in
51+
print(param)
52+
}
53+
}
54+
""",
55+
macros: ["Traced": TracedMacro.self]
56+
)
57+
}
58+
59+
func test_tracedMacro_accessSpan() {
60+
assertMacroExpansion(
61+
"""
62+
@Traced
63+
func example(param: Int) {
64+
span.attributes["param"] = param
65+
}
66+
""",
67+
expandedSource: """
68+
func example(param: Int) {
69+
withSpan("example") { span in
70+
span.attributes["param"] = param
71+
}
72+
}
73+
""",
74+
macros: ["Traced": TracedMacro.self]
75+
)
76+
}
77+
}
78+
79+
// MARK: Compile tests
80+
81+
@Traced
82+
func syncNonthrowingExample(param: Int) {
83+
print(param)
84+
}
85+
86+
@Traced
87+
func example(param: Int) {
88+
span.attributes["param"] = param
89+
}
90+
91+
#endif

0 commit comments

Comments
 (0)