Skip to content

Commit 8587fc3

Browse files
committed
Merge branch 'develop'
2 parents 13f62cc + 42ea7a3 commit 8587fc3

File tree

13 files changed

+236
-164
lines changed

13 files changed

+236
-164
lines changed

Package.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@ import CompilerPluginSupport
55
let package = Package(
66
name: "ManagedModels",
77

8-
// For now :-)
9-
// macOS v13 needed to build the macro on macOS 13! (for iOS)
10-
platforms: [ .macOS(.v11), .iOS(.v14), .tvOS(.v15), .watchOS(.v8) ],
8+
platforms: [ .macOS(.v11), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) ],
119
products: [
1210
.library(name: "ManagedModels", targets: [ "ManagedModels" ])
1311
],
1412
dependencies: [
15-
// Depend on the latest Swift 5.9 prerelease of SwiftSyntax
16-
.package(url: "https://github.com/apple/swift-syntax.git",
17-
from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"),
13+
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0")
1814
],
1915
targets: [
2016
.target(name: "ManagedModels", dependencies: [ "ManagedModelMacros" ]),
@@ -27,13 +23,11 @@ let package = Package(
2723
]
2824
),
2925

30-
// A test target used to develop the macro implementation.
3126
.testTarget(
3227
name: "ManagedModelTests",
3328
dependencies: [ "ManagedModels" ]
3429
),
3530

36-
// A test target used to develop the macro implementation.
3731
.testTarget(
3832
name: "ManagedModelMacrosTests",
3933
dependencies: [

Sources/ManagedModelMacros/ModelMacro/ModelMacro.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,4 @@
44
//
55

66
public enum ModelMacro {
7-
/*
8-
@attached(member, ...)
9-
@attached(memberAttribute)
10-
@attached(extension, conformances: // the protocols we add automagically
11-
PersistentModel
12-
)
13-
14-
Note: @Model does not support `originalName`/`renamingIdentifier`?
15-
We could. Some for that versionHash thing.
16-
*/
177
}

Sources/ManagedModelMacros/ModelMacro/ModelMembers.swift

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,10 @@ extension ModelMacro: MemberMacro { // @attached(member, names:...)
4949
initializers: findInitializers(in: classDecl)
5050
)
5151

52-
let needsFR : Bool = {
53-
guard let f = classDecl
54-
.findFunctionWithName("fetchRequest", isStatic: true) else
55-
{
56-
return true
57-
}
58-
return f.parameterCount != 0
59-
}()
60-
if needsFR {
52+
if classDecl.findFunctionWithName("fetchRequest", isStaticOrClass: true,
53+
numberOfParametersWithoutDefaults: 0)
54+
== nil
55+
{
6156
let modelClassName = modelClassName.text
6257
newMembers.append(
6358
"""
@@ -78,13 +73,9 @@ extension ModelMacro: MemberMacro { // @attached(member, names:...)
7873
)
7974
newMembers.append(DeclSyntax(metadata))
8075

81-
let needsEntity : Bool = {
82-
guard let f = classDecl.findFunctionWithName("entity", isStatic: true) else {
83-
return true
84-
}
85-
return f.parameterCount != 0
86-
}()
87-
if needsEntity {
76+
if classDecl.findFunctionWithName("entity", isStaticOrClass: true,
77+
parameterCount: 0) == nil
78+
{
8879
newMembers.append(
8980
"""
9081
/// Returns the `NSEntityDescription` associated w/ the `PersistentModel`.

Sources/ManagedModelMacros/Models/ModelInitializers.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ struct ModelInitializer {
1313

1414
/// Just the keyword parts of the selector, empty for wildcard.
1515
let parameterKeywords : [ String ]
16+
17+
let numberOfParametersWithoutDefaults : Int
1618
}
1719

1820
extension Collection where Element == ModelInitializer {
1921

22+
/// Either has a plain `init()` or an init that has all parameters w/ a
23+
/// default (e.g. `init(title: String = "")`) which can be called w/o
24+
/// specifying parameters.
2025
var hasNoArgumentInitializer: Bool {
21-
// TBD: should also check for default arguments!!
2226
guard !self.isEmpty else { return false }
23-
return self.contains(where: { $0.parameterKeywords.isEmpty })
27+
return self.contains(where: { $0.numberOfParametersWithoutDefaults == 0 })
2428
}
2529

2630
var hasDesignatedInitializers: Bool {
@@ -54,17 +58,23 @@ extension ModelMacro {
5458
continue
5559
}
5660

61+
var numberOfParametersWithoutDefaults = 0
5762
var keywords = [ String ]()
5863
for parameter : FunctionParameterSyntax
5964
in initDecl.signature.parameterClause.parameters
6065
{
6166
if parameter.firstName.tokenKind == .wildcard { keywords.append("") }
6267
else { keywords.append(parameter.firstName.trimmedDescription) }
68+
69+
if parameter.defaultValue == nil {
70+
numberOfParametersWithoutDefaults += 1
71+
}
6372
}
6473

6574
initializers.append(ModelInitializer(
6675
isConvenience: initDecl.isConvenience,
67-
parameterKeywords: keywords
76+
parameterKeywords: keywords,
77+
numberOfParametersWithoutDefaults: numberOfParametersWithoutDefaults
6878
))
6979
}
7080
return initializers
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2023 ZeeZide GmbH.
4+
//
5+
6+
import SwiftSyntax
7+
8+
extension ClassDeclSyntax {
9+
10+
var isPublicOrOpen : Bool { modifiers.containsPublicOrOpen }
11+
12+
var publicOrOpenModifier : DeclModifierListSyntax.Element? {
13+
modifiers.publicOrOpenModifier
14+
}
15+
}
16+
17+
extension ClassDeclSyntax {
18+
19+
var inheritedTypeNames : Set<String> {
20+
// The protocol types in like (as a workaround for protocols below)
21+
// `class Model: Codable, CustomStringConvertible`
22+
var inheritedTypeNames = Set<String>()
23+
if let inheritedTypes = inheritanceClause?.inheritedTypes {
24+
for type : InheritedTypeSyntax in inheritedTypes {
25+
let typeSyntax : TypeSyntax = type.type
26+
if let id = typeSyntax.as(IdentifierTypeSyntax.self) {
27+
inheritedTypeNames.insert(id.name.trimmed.text)
28+
}
29+
}
30+
}
31+
return inheritedTypeNames
32+
}
33+
}
34+
35+
extension ClassDeclSyntax {
36+
37+
func findFunctionWithName(_ name: String, isStaticOrClass: Bool,
38+
parameterCount: Int? = nil,
39+
numberOfParametersWithoutDefaults: Int? = nil)
40+
-> FunctionDeclSyntax?
41+
{
42+
for member : MemberBlockItemSyntax in memberBlock.members {
43+
guard let funcDecl = member.decl.as(FunctionDeclSyntax.self) else {
44+
continue
45+
}
46+
let hasStatic = funcDecl.modifiers.containsStaticOrClass
47+
guard hasStatic == isStaticOrClass else { continue }
48+
49+
if let parameterCount {
50+
guard parameterCount == funcDecl.parameterCount else { continue }
51+
}
52+
if let numberOfParametersWithoutDefaults {
53+
guard numberOfParametersWithoutDefaults ==
54+
funcDecl.numberOfParametersWithoutDefaults else { continue }
55+
}
56+
57+
// filter out operators and different names
58+
guard case .identifier(let idName) = funcDecl.name.tokenKind,
59+
idName == name else
60+
{
61+
continue
62+
}
63+
64+
// Found it
65+
return funcDecl
66+
}
67+
return nil
68+
}
69+
}

Sources/ManagedModelMacros/Utilities/DeclHelpers.swift

Lines changed: 20 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,109 +7,40 @@ import SwiftSyntax
77

88
extension VariableDeclSyntax {
99

10-
var isStaticOrClass : Bool {
11-
modifiers.contains {
12-
switch $0.name.tokenKind {
13-
case .keyword(.static) : return true
14-
case .keyword(.class) : return true
15-
default: return false
16-
}
17-
}
18-
}
19-
20-
var isPublicOrOpen : Bool { modifiers.containsPublicOrOpen }
10+
var isStaticOrClass : Bool { modifiers.containsStaticOrClass }
11+
var isPublicOrOpen : Bool { modifiers.containsPublicOrOpen }
2112
}
2213

2314
extension InitializerDeclSyntax {
24-
var isConvenience : Bool { modifiers.convenienceModifier != nil }
25-
}
26-
27-
extension FunctionDeclSyntax {
2815

29-
var parameterCount : Int {
30-
signature.parameterClause.parameters.count
16+
var isConvenience : Bool {
17+
modifiers.contains { $0.name.tokenKind == .keyword(.convenience) }
3118
}
32-
}
33-
34-
extension ClassDeclSyntax {
3519

36-
var isPublicOrOpen : Bool { modifiers.containsPublicOrOpen }
37-
var publicOrOpenModifier : DeclModifierListSyntax.Element? {
38-
modifiers.publicOrOpenModifier
39-
}
20+
var parameterCount : Int { signature.parameterClause.parameters.count }
4021

41-
var inheritedTypeNames : Set<String> {
42-
// The protocol types in like (as a workaround for protocols below)
43-
// `class Model: Codable, CustomStringConvertible`
44-
var inheritedTypeNames = Set<String>()
45-
if let inheritedTypes = inheritanceClause?.inheritedTypes {
46-
for type : InheritedTypeSyntax in inheritedTypes {
47-
let typeSyntax : TypeSyntax = type.type
48-
if let id = typeSyntax.as(IdentifierTypeSyntax.self) {
49-
inheritedTypeNames.insert(id.name.trimmed.text)
50-
}
51-
}
52-
}
53-
return inheritedTypeNames
54-
}
55-
56-
func findFunctionWithName(_ name: String, isStatic: Bool)
57-
-> FunctionDeclSyntax?
58-
{
59-
for member : MemberBlockItemSyntax in memberBlock.members {
60-
guard let funcDecl = member.decl.as(FunctionDeclSyntax.self) else {
61-
continue
62-
}
63-
let hasStatic = funcDecl.modifiers.contains(.static)
64-
guard hasStatic == isStatic else { continue }
65-
66-
switch funcDecl.name.tokenKind {
67-
case .identifier(_):
68-
return funcDecl
69-
default: // operator
70-
return nil
71-
}
72-
}
73-
return nil
22+
var numberOfParametersWithoutDefaults : Int {
23+
signature.parameterClause.parameters.numberOfParametersWithoutDefaults
7424
}
7525
}
7626

77-
extension DeclModifierListSyntax {
27+
extension FunctionDeclSyntax {
7828

79-
var modifiers : Set<Keyword> {
80-
var modifiers = Set<Keyword>()
81-
for modifier in self {
82-
guard case .keyword(let keyword) = modifier.name.tokenKind else {
83-
continue
84-
}
85-
modifiers.insert(keyword)
86-
}
87-
return modifiers
88-
}
29+
var parameterCount : Int { signature.parameterClause.parameters.count }
8930

90-
func contains(_ keyword: Keyword) -> Bool {
91-
for modifier in self {
92-
guard case .keyword(let keywordMember) = modifier.name.tokenKind else {
93-
continue
94-
}
95-
if keyword == keywordMember { return true }
96-
}
97-
return false
31+
var numberOfParametersWithoutDefaults : Int {
32+
signature.parameterClause.parameters.numberOfParametersWithoutDefaults
9833
}
34+
}
35+
36+
extension FunctionParameterListSyntax {
9937

100-
var publicOrOpenModifier : Element? {
101-
self.first {
102-
switch $0.name.tokenKind {
103-
case .keyword(.public) : return true
104-
case .keyword(.open) : return true
105-
default: return false
106-
}
38+
var numberOfParametersWithoutDefaults : Int {
39+
var count = 0
40+
for parameter : FunctionParameterSyntax in self {
41+
guard parameter.defaultValue == nil else { continue }
42+
count += 1
10743
}
44+
return count
10845
}
109-
110-
var convenienceModifier : Element? {
111-
self.first { $0.name.tokenKind == .keyword(.convenience) }
112-
}
113-
114-
var containsPublicOrOpen : Bool { publicOrOpenModifier != nil }
11546
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2023 ZeeZide GmbH.
4+
//
5+
6+
import SwiftSyntax
7+
8+
extension DeclModifierListSyntax {
9+
10+
var modifiers : Set<Keyword> {
11+
var modifiers = Set<Keyword>()
12+
for modifier in self {
13+
guard case .keyword(let keyword) = modifier.name.tokenKind else {
14+
continue
15+
}
16+
modifiers.insert(keyword)
17+
}
18+
return modifiers
19+
}
20+
21+
func contains(_ keyword: Keyword) -> Bool {
22+
for modifier in self {
23+
guard case .keyword(let keywordMember) = modifier.name.tokenKind else {
24+
continue
25+
}
26+
if keyword == keywordMember { return true }
27+
}
28+
return false
29+
}
30+
31+
var publicOrOpenModifier : Element? {
32+
self.first {
33+
switch $0.name.tokenKind {
34+
case .keyword(.public) : return true
35+
case .keyword(.open) : return true
36+
default: return false
37+
}
38+
}
39+
}
40+
41+
var containsPublicOrOpen : Bool { publicOrOpenModifier != nil }
42+
43+
var containsStaticOrClass : Bool {
44+
contains {
45+
switch $0.name.tokenKind {
46+
case .keyword(.static) : return true
47+
case .keyword(.class) : return true
48+
default: return false
49+
}
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)