Skip to content
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ let package = Package(
.testTarget(
name: "MockoloTests",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
"MockoloFramework",
"MockoloTestSupportMacros",
],
Expand Down
53 changes: 53 additions & 0 deletions Sources/MockoloFramework/Models/Attribute.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright (c) 2026. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// Represents an attribute attached to a declaration
struct Attribute: Hashable, CustomStringConvertible {
var description: String

enum KnownKind: Hashable {
enum AvailableKind {
/// `@available(*, deprecated)`, `@available(*, noasync)`
case behavioral

/// `@available(iOS 26.0, *)`, `@available(iOS, introduced: 26.0)`
case platform
}
case available(AvailableKind)
}
var kind: KnownKind?

var isAvailable: Bool {
if case .available = kind {
return true
}
return false
}

var isBehavioralAvailable: Bool {
if case .available(.behavioral) = kind {
return true
}
return false
}

var isPlatformAvailable: Bool {
if case .available(.platform) = kind {
return true
}
return false
}
}
3 changes: 3 additions & 0 deletions Sources/MockoloFramework/Models/MethodModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ final class MethodModel: Model {
let isStatic: Bool
let isAsync: Bool
let throwing: ThrowingKind
let attributes: [Attribute]
let funcsWithArgsHistory: [String]
let customModifiers: [String : Modifier]
var modelType: ModelType {
Expand Down Expand Up @@ -177,6 +178,7 @@ final class MethodModel: Model {
isStatic: Bool,
offset: Int64,
length: Int64,
attributes: [Attribute] = [],
funcsWithArgsHistory: [String],
customModifiers: [String: Modifier],
modelDescription: String?,
Expand All @@ -193,6 +195,7 @@ final class MethodModel: Model {
self.genericTypeParams = genericTypeParams
self.genericWhereClause = genericWhereClause
self.processed = processed
self.attributes = attributes
self.funcsWithArgsHistory = funcsWithArgsHistory
self.customModifiers = customModifiers
self.modelDescription = modelDescription
Expand Down
7 changes: 3 additions & 4 deletions Sources/MockoloFramework/Models/NominalModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final class NominalModel: Model {
let inheritedTypeName: String
let genericWhereConstraints: [String]
let type: SwiftType
let attribute: String
let attributes: [Attribute]
let accessLevel: String
let declKindOfMockAnnotatedBaseType: NominalTypeDeclKind
let entities: [(String, Model)]
Expand All @@ -39,7 +39,7 @@ final class NominalModel: Model {
acl: String,
declKindOfMockAnnotatedBaseType: NominalTypeDeclKind,
declKind: NominalTypeDeclKind,
attributes: [String],
attributes: [Attribute],
offset: Int64,
inheritedTypeName: String,
genericWhereConstraints: [String],
Expand All @@ -58,7 +58,7 @@ final class NominalModel: Model {
self.offset = offset
self.inheritedTypeName = inheritedTypeName
self.genericWhereConstraints = genericWhereConstraints
self.attribute = Set(attributes.filter {$0.contains(String.available)}).joined(separator: " ")
self.attributes = attributes
self.accessLevel = acl
self.requiresSendable = requiresSendable
}
Expand All @@ -70,7 +70,6 @@ final class NominalModel: Model {
return applyNominalTemplate(
name: name,
accessLevel: accessLevel,
attribute: attribute,
arguments: arguments,
initParamCandidates: initParamCandidates,
declaredInits: declaredInits,
Expand Down
12 changes: 7 additions & 5 deletions Sources/MockoloFramework/Models/ParsedEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct ResolvedEntity {
var key: String
var entity: Entity
var uniqueModels: [(String, Model)]
var attributes: [String]
var attributes: [Attribute]
var inheritedTypes: [String]

var declaredInits: [MethodModel] {
Expand Down Expand Up @@ -65,13 +65,15 @@ struct ResolvedEntity {

func model() -> Model {
let metadata = entity.metadata
let combinedAttributes = entity.entityNode.attributeDescriptions + attributes
let combinedAttributes = (entity.entityNode.parsedAttributes + attributes)
.filter(\.isAvailable)
.uniqued()
return NominalModel(selfType: .init(name: metadata?.nameOverride ?? (key + "Mock")),
namespaces: entity.entityNode.namespaces,
acl: entity.entityNode.accessLevel,
declKindOfMockAnnotatedBaseType: entity.entityNode.declKind,
declKind: inheritsActorProtocol ? .actor : .class,
attributes: combinedAttributes,
attributes: Array(combinedAttributes),
offset: entity.entityNode.offset,
inheritedTypeName: (entity.metadata?.module?.withDot ?? "") + key,
genericWhereConstraints: entity.entityNode.genericWhereConstraints,
Expand All @@ -92,7 +94,7 @@ protocol EntityNode {
var nameText: String { get }
var mayHaveGlobalActor: Bool { get }
var accessLevel: String { get }
var attributeDescriptions: [String] { get }
var parsedAttributes: [Attribute] { get }
var declKind: NominalTypeDeclKind { get }
var inheritedTypes: [String] { get }
var genericWhereConstraints: [String] { get }
Expand All @@ -102,7 +104,7 @@ protocol EntityNode {
}

struct EntityNodeSubContainer {
var attributes: [String]
var attributes: [Attribute]
var members: [Model]
var hasInit: Bool
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/MockoloFramework/Models/VariableModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class VariableModel: Model {
let type: SwiftType?
let offset: Int64
let accessLevel: String
let attributes: [String]?
let attributes: [Attribute]
/// Indicates whether this model can be used as a parameter to an initializer
let canBeInitParam: Bool
let processed: Bool
Expand Down Expand Up @@ -50,6 +50,7 @@ final class VariableModel: Model {
storageKind: MockStorageKind,
canBeInitParam: Bool,
offset: Int64,
attributes: [Attribute] = [],
rxTypes: [String: String]?,
customModifiers: [String: Modifier]?,
modelDescription: String?,
Expand All @@ -65,7 +66,7 @@ final class VariableModel: Model {
self.rxTypes = rxTypes
self.customModifiers = customModifiers
self.accessLevel = acl ?? ""
self.attributes = nil
self.attributes = attributes
self.modelDescription = modelDescription
self.combineType = combineType
}
Expand Down
Loading