Skip to content

Commit ec4efb1

Browse files
authored
Added global actor preference extraction (#12)
1 parent b2671db commit ec4efb1

12 files changed

Lines changed: 193 additions & 69 deletions

File tree

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let package = Package(
2222
dependencies: [
2323
.package(
2424
url: "https://github.com/swiftlang/swift-syntax",
25-
"600.0.0" ..< "602.0.0"
25+
"600.0.0" ..< "604.0.0"
2626
)
2727
],
2828
targets: [

Sources/PrincipleMacros/Builders/Declarations/Common/DeclBuilder.swift

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,40 @@ extension DeclBuilder {
2020

2121
public var inheritedAccessControlLevel: TokenSyntax? {
2222
let settings = settings.accessControlLevel
23-
return basicDeclaration.accessControlLevel(
24-
inheritedBy: settings.inheritingDeclaration,
25-
maxAllowed: settings.maxAllowed
26-
)
23+
guard let accessControlLevel = basicDeclaration.accessControlLevel,
24+
let index = TokenKind.accessControlLevels.firstIndex(of: accessControlLevel.tokenKind),
25+
let maxAllowedIndex = Keyword.accessControlLevels.firstIndex(of: settings.maxAllowed)
26+
else {
27+
return nil
28+
}
29+
30+
guard index <= maxAllowedIndex else {
31+
let tokenKind = TokenKind.accessControlLevels[maxAllowedIndex]
32+
return TokenSyntax(tokenKind, presence: .present).withTrailingSpace
33+
}
34+
35+
switch settings.inheritingDeclaration {
36+
case .member:
37+
if let internalIndex = Keyword.accessControlLevels.firstIndex(of: .internal),
38+
index <= internalIndex {
39+
return nil
40+
}
41+
case .peer:
42+
break
43+
}
44+
45+
return accessControlLevel.trimmed.withTrailingSpace
46+
}
47+
48+
public var inheritedGlobalActorIsolation: AttributeSyntax? {
49+
let globalActor: AttributeSyntax? = switch settings.globalActorIsolationPreference {
50+
case .nonisolated:
51+
nil
52+
case let .isolated(globalActor):
53+
"@\(globalActor)"
54+
case .none:
55+
basicDeclaration.globalActor?.trimmed
56+
}
57+
return globalActor?.withTrailingSpace
2758
}
2859
}

Sources/PrincipleMacros/Builders/Declarations/Common/DeclBuilderSettings.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ import SwiftSyntax
1111
public struct DeclBuilderSettings {
1212

1313
public var accessControlLevel: AccessControlLevel
14+
public var globalActorIsolationPreference: GlobalActorIsolationPreference?
1415

15-
public init(accessControlLevel: AccessControlLevel) {
16+
public init(
17+
accessControlLevel: AccessControlLevel,
18+
globalActorIsolationPreference: GlobalActorIsolationPreference? = nil
19+
) {
1620
self.accessControlLevel = accessControlLevel
21+
self.globalActorIsolationPreference = globalActorIsolationPreference
1722
}
1823
}
1924

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// ActorDeclBuilder.swift
3+
// PrincipleMacros
4+
//
5+
// Created by Kamil Strzelecki on 18/08/2025.
6+
// Copyright © 2025 Kamil Strzelecki. All rights reserved.
7+
//
8+
9+
import SwiftSyntax
10+
11+
public protocol ActorDeclBuilder: TypeDeclBuilder {
12+
13+
var declaration: ActorDeclSyntax { get }
14+
}
15+
16+
extension ActorDeclBuilder {
17+
18+
public var typeDeclaration: any TypeDeclSyntax {
19+
declaration
20+
}
21+
}

Sources/PrincipleMacros/Parameters/ParameterExtractor.swift

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,50 @@ import SwiftSyntax
1010

1111
public struct ParameterExtractor {
1212

13-
private let arguments: LabeledExprListSyntax
13+
private let arguments: LabeledExprListSyntax?
1414
private let trailingClosure: ClosureExprSyntax?
1515

1616
public init(from node: some FreestandingMacroExpansionSyntax) {
1717
self.arguments = node.arguments
1818
self.trailingClosure = node.trailingClosure
1919
}
2020

21-
public func expression(withLabel label: TokenSyntax?) throws -> ExprSyntax {
22-
let match = arguments.first { element in
23-
element.label?.trimmedDescription == label?.trimmedDescription
21+
public init(from node: AttributeSyntax) {
22+
self.arguments = switch node.arguments {
23+
case let .argumentList(arguments):
24+
arguments
25+
default:
26+
nil
2427
}
28+
self.trailingClosure = nil
29+
}
2530

26-
guard let match else {
27-
throw ParameterExtractionError.notFound
31+
public func expression(
32+
withLabel label: TokenSyntax?
33+
) -> ExprSyntax? {
34+
let match = arguments?.first { element in
35+
element.label?.trimmedDescription == label?.trimmedDescription
2836
}
37+
return match?.expression.trimmed
38+
}
2939

30-
return match.expression.trimmed
40+
public func trailingClosure(
41+
withLabel label: TokenSyntax?
42+
) throws -> ExprSyntax? {
43+
if let trailingClosure {
44+
return ExprSyntax(trailingClosure)
45+
}
46+
return expression(withLabel: label)
3147
}
3248

33-
public func rawString(withLabel label: TokenSyntax?) throws -> String {
34-
let rawString = try expression(withLabel: label)
49+
public func rawString(
50+
withLabel label: TokenSyntax?
51+
) throws -> String? {
52+
guard let expression = expression(withLabel: label) else {
53+
return nil
54+
}
55+
56+
let rawString = expression
3557
.as(StringLiteralExprSyntax.self)?
3658
.representedLiteralValue
3759

@@ -42,10 +64,23 @@ public struct ParameterExtractor {
4264
return rawString
4365
}
4466

45-
public func trailingClosure(withLabel label: TokenSyntax?) throws -> ExprSyntax {
46-
if let trailingClosure {
47-
return ExprSyntax(trailingClosure)
67+
public func globalActorIsolationPreference(
68+
withLabel label: TokenSyntax?
69+
) throws -> GlobalActorIsolationPreference? {
70+
guard let expression = expression(withLabel: label) else {
71+
return nil
72+
}
73+
74+
if NilLiteralExprSyntax(expression) != nil {
75+
return .nonisolated
76+
}
77+
78+
if let memberAccessExpression = MemberAccessExprSyntax(expression),
79+
memberAccessExpression.declName.baseName.tokenKind == .keyword(.self),
80+
let baseType = memberAccessExpression.base {
81+
return .isolated("\(baseType)")
4882
}
49-
return try expression(withLabel: label)
83+
84+
throw ParameterExtractionError.unexpectedSyntaxType
5085
}
5186
}

Sources/PrincipleMacros/Syntax/Custom/BasicDeclSyntax.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
import SwiftSyntax
1010

1111
public typealias BasicDeclSyntax = DeclSyntaxProtocol
12+
& WithAttributesSyntax
1213
& WithModifiersSyntax

Sources/PrincipleMacros/Syntax/Custom/TypeDeclSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import SwiftSyntax
1010

11-
public protocol TypeDeclSyntax: DeclGroupSyntax, NamedDeclSyntax, WithModifiersSyntax {
11+
public protocol TypeDeclSyntax: DeclGroupSyntax, NamedDeclSyntax, BasicDeclSyntax {
1212

1313
var isFinal: Bool { get }
1414
}

Sources/PrincipleMacros/Syntax/Extensions/WithModifiersSyntax.swift

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,55 +41,23 @@ extension WithModifiersSyntax {
4141
}
4242
}
4343

44-
extension WithModifiersSyntax {
45-
46-
public func accessControlLevel(
47-
inheritedBy inheritingDeclaration: InheritingDeclaration,
48-
maxAllowed: Keyword
49-
) -> TokenSyntax? {
50-
guard let accessControlLevel,
51-
let index = TokenKind.accessControlLevels.firstIndex(of: accessControlLevel.tokenKind),
52-
let maxAllowedIndex = Keyword.accessControlLevels.firstIndex(of: maxAllowed)
53-
else {
54-
return nil
55-
}
56-
57-
guard index <= maxAllowedIndex else {
58-
let tokenKind = TokenKind.accessControlLevels[maxAllowedIndex]
59-
return TokenSyntax(tokenKind, presence: .present)
60-
}
61-
62-
switch inheritingDeclaration {
63-
case .member:
64-
if let internalIndex = Keyword.accessControlLevels.firstIndex(of: .internal),
65-
index <= internalIndex {
66-
return nil
67-
}
68-
case .peer:
69-
break
70-
}
71-
72-
return accessControlLevel.trimmed.withTrailingSpace
73-
}
74-
}
75-
7644
extension TokenKind {
7745

78-
fileprivate static let typeScopeSpecifiers = Keyword.typeScopeSpecifiers
46+
static let typeScopeSpecifiers = Keyword.typeScopeSpecifiers
7947
.map(TokenKind.keyword)
8048

81-
fileprivate static let accessControlLevels = Keyword.accessControlLevels
49+
static let accessControlLevels = Keyword.accessControlLevels
8250
.map(TokenKind.keyword)
8351
}
8452

8553
extension Keyword {
8654

87-
fileprivate static let typeScopeSpecifiers: [Keyword] = [
55+
static let typeScopeSpecifiers: [Keyword] = [
8856
.static,
8957
.class
9058
]
9159

92-
fileprivate static let accessControlLevels: [Keyword] = [
60+
static let accessControlLevels: [Keyword] = [
9361
.private,
9462
.fileprivate,
9563
.internal,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// GlobalActorIsolationPreference.swift
3+
// PrincipleMacros
4+
//
5+
// Created by Kamil Strzelecki on 18/08/2025.
6+
// Copyright © 2025 Kamil Strzelecki. All rights reserved.
7+
//
8+
9+
import SwiftSyntax
10+
11+
public enum GlobalActorIsolationPreference: Hashable {
12+
13+
case nonisolated
14+
case isolated(TypeSyntax)
15+
}

0 commit comments

Comments
 (0)