diff --git a/Package.resolved b/Package.resolved index c7c09f8a6..fce7dd30c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,12 +1,12 @@ { - "originHash" : "a1b1e06fbdf5f90d2817b4e4ba491ff6fc177e87dad20844f45d67caa0b58193", + "originHash" : "0d71c67b1c80ef5579aa062c1d31bee0895d941e2b3d8283f5523ed15a0479fd", "pins" : [ { "identity" : "liveview-native-core", "kind" : "remoteSourceControl", "location" : "https://github.com/liveview-native/liveview-native-core", "state" : { - "revision" : "2f0e874960ee93b5b76daff2ecfb6d8e445e15bc", + "revision" : "0e3475a96ebf9528be3645e476738f0b8181c0b2", "version" : "0.4.1-rc-5" } }, @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { - "revision" : "0687f71944021d616d34d922343dcef086855920", - "version" : "600.0.1" + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" } } ], diff --git a/Package.swift b/Package.swift index 5d85bb652..eec0791fe 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -29,7 +29,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"), - .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.1"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "601.0.1"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/Foundation.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/Foundation.swift index 9596efbeb..db9342a74 100644 --- a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/Foundation.swift +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/Foundation.swift @@ -8,6 +8,7 @@ import Foundation import LiveViewNativeStylesheet import LiveViewNativeCore +import CoreText extension Calendar { @ASTDecodable("Calendar") @@ -238,6 +239,24 @@ public extension AttributedString.Resolvable { } } +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +extension AttributedString.LineHeight { + @ASTDecodable("LineHeight") + public enum Resolvable: StylesheetResolvable, @preconcurrency Decodable { + case __constant(AttributedString.LineHeight) + } +} + +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +public extension AttributedString.LineHeight.Resolvable { + @MainActor func resolve(on element: ElementNode, in context: LiveContext) -> AttributedString.LineHeight { + switch self { + case let .__constant(value): + return value + } + } +} + public struct StylesheetResolvableLocalizedError: @preconcurrency Decodable, StylesheetResolvable, LocalizedError, @preconcurrency AttributeDecodable { public let errorDescription: String? diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/StandardLibrary.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/StandardLibrary.swift index b1b78afe0..2f659a944 100644 --- a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/StandardLibrary.swift +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/StandardLibrary.swift @@ -96,3 +96,40 @@ extension Double { } } } + +extension Float { + public enum Resolvable: StylesheetResolvable, @preconcurrency Decodable { + case __constant(Float) + case reference(AttributeReference) + + @ASTDecodable("Float") + enum Member: @preconcurrency Decodable { + case infinity + case pi + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + + if let member = try? container.decode(Member.self) { + switch member { + case .infinity: + self = .__constant(.infinity) + case .pi: + self = .__constant(.pi) + } + } else { + self = .reference(try container.decode(AttributeReference.self)) + } + } + + public func resolve(on element: ElementNode, in context: LiveContext) -> Float where R : RootRegistry { + switch self { + case let .__constant(constant): + return constant + case let .reference(reference): + return reference.resolve(on: element, in: context) + } + } + } +} diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/AttributedTextFormattingDefinition.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/AttributedTextFormattingDefinition.swift new file mode 100644 index 000000000..0c2272a21 --- /dev/null +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/AttributedTextFormattingDefinition.swift @@ -0,0 +1,6 @@ +// +// AttributedTextFormattingDefinition.swift +// LiveViewNative +// +// Created by Carson Katri on 6/10/25. +// diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/NamedCoordinateSpace.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/NamedCoordinateSpace.swift index d0c0af590..ee0822fd5 100644 --- a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/NamedCoordinateSpace.swift +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/NamedCoordinateSpace.swift @@ -46,3 +46,14 @@ public extension NamedCoordinateSpace.Resolvable { } } } + +enum StylesheetResolvableCoordinateSpaceProtocol: StylesheetResolvable, AttributeDecodable, @preconcurrency Decodable { + case local + + @MainActor func resolve(on element: ElementNode, in context: LiveContext) -> CoordinateSpace { + switch self { + case .local: + return .local + } + } +} diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/RoundedRectangularShape.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/RoundedRectangularShape.swift new file mode 100644 index 000000000..77004cc9e --- /dev/null +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/RoundedRectangularShape.swift @@ -0,0 +1,141 @@ +// +// RoundedRectangularShape.swift +// LiveViewNative +// +// Created by Carson.Katri on 9/9/25. +// + +import SwiftUI +import LiveViewNativeCore +import LiveViewNativeStylesheet + +@ASTDecodable("RoundedRectangularShape") +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +enum StylesheetResolvableRoundedRectangularShape: InsettableShape, RoundedRectangularShape, StylesheetResolvable, @preconcurrency Decodable, AttributeDecodable { + typealias InsetShape = StylesheetResolvableRoundedRectangularShape + + case _resolved(any SwiftUI.RoundedRectangularShape) + + case rect + case _rectCornerSize(cornerSize: CGSize.Resolvable, style: RoundedCornerStyle.Resolvable) + static func rect(cornerSize: CGSize.Resolvable, style: RoundedCornerStyle.Resolvable = .__constant(.continuous)) -> Self { + ._rectCornerSize(cornerSize: cornerSize, style: style) + } + case _rectCornerRadius(cornerRadius: CGFloat.Resolvable, style: RoundedCornerStyle.Resolvable) + static func rect(cornerRadius: CGFloat.Resolvable, style: RoundedCornerStyle.Resolvable = .__constant(.continuous)) -> Self { + ._rectCornerRadius(cornerRadius: cornerRadius, style: style) + } + case _rectCornerRadii(cornerRadii: RectangleCornerRadii.Resolvable, style: RoundedCornerStyle.Resolvable) + static func rect(cornerRadii: RectangleCornerRadii.Resolvable, style: RoundedCornerStyle.Resolvable = .__constant(.continuous)) -> Self { + ._rectCornerRadii(cornerRadii: cornerRadii, style: style) + } + case _rectRadius( + topLeadingRadius: CGFloat.Resolvable, + bottomLeadingRadius: CGFloat.Resolvable, + bottomTrailingRadius: CGFloat.Resolvable, + topTrailingRadius: CGFloat.Resolvable, + style: RoundedCornerStyle.Resolvable + ) + static func rect( + topLeadingRadius: CGFloat.Resolvable = .__constant(0), + bottomLeadingRadius: CGFloat.Resolvable = .__constant(0), + bottomTrailingRadius: CGFloat.Resolvable = .__constant(0), + topTrailingRadius: CGFloat.Resolvable = .__constant(0), + style: RoundedCornerStyle.Resolvable = .__constant(.continuous) + ) -> Self { + ._rectRadius( + topLeadingRadius: topLeadingRadius, + bottomLeadingRadius: bottomLeadingRadius, + bottomTrailingRadius: bottomTrailingRadius, + topTrailingRadius: topTrailingRadius, + style: style + ) + } + + case capsule + case _capsule(style: RoundedCornerStyle.Resolvable) + static func capsule(style: RoundedCornerStyle.Resolvable) -> Self { + ._capsule(style: style) + } + + case circle +} + +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +extension StylesheetResolvableRoundedRectangularShape { + nonisolated func path(in rect: CGRect) -> Path { + switch self { + case let ._resolved(shape): + return shape.path(in: rect) + default: + fatalError() + } + } + + nonisolated func sizeThatFits(_ proposal: ProposedViewSize) -> CGSize { + switch self { + case let ._resolved(shape): + return shape.sizeThatFits(proposal) + default: + fatalError() + } + } + + func corners(in size: CGSize?) -> Self.Corners? { + switch self { + case let ._resolved(shape): + return shape.corners(in: size) + default: + fatalError() + } + } + + nonisolated func inset(by amount: CGFloat) -> StylesheetResolvableRoundedRectangularShape { + switch self { + case let ._resolved(shape): + return ._resolved(shape.inset(by: amount) as! any RoundedRectangularShape) + default: + fatalError() + } + } +} + +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +extension StylesheetResolvableRoundedRectangularShape { + @MainActor + func resolve(on element: ElementNode, in context: LiveContext) -> Self where R : RootRegistry { + switch self { + case ._resolved(let roundedRectangularShape): + return self + case .rect: + return ._resolved(Rectangle()) + case ._rectCornerSize(let cornerSize, let style): + return ._resolved(RoundedRectangle(cornerSize: cornerSize.resolve(on: element, in: context), style: style.resolve(on: element, in: context))) + case ._rectCornerRadius(let cornerRadius, let style): + return ._resolved(RoundedRectangle(cornerRadius: cornerRadius.resolve(on: element, in: context), style: style.resolve(on: element, in: context))) + case ._rectCornerRadii(let cornerRadii, let style): + return ._resolved(UnevenRoundedRectangle(cornerRadii: cornerRadii.resolve(on: element, in: context), style: style.resolve(on: element, in: context))) + case ._rectRadius(let topLeadingRadius, let bottomLeadingRadius, let bottomTrailingRadius, let topTrailingRadius, let style): + return ._resolved(UnevenRoundedRectangle( + topLeadingRadius: topLeadingRadius.resolve(on: element, in: context), + bottomLeadingRadius: bottomLeadingRadius.resolve(on: element, in: context), + bottomTrailingRadius: bottomTrailingRadius.resolve(on: element, in: context), + topTrailingRadius: topTrailingRadius.resolve(on: element, in: context), + style: style.resolve(on: element, in: context) + )) + case .capsule: + return ._resolved(Capsule()) + case ._capsule(let style): + return ._resolved(Capsule(style: style.resolve(on: element, in: context))) + case .circle: + return ._resolved(Circle()) + } + } +} + +@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) +extension StylesheetResolvableRoundedRectangularShape { + init(from attribute: LiveViewNativeCore.Attribute?, on element: ElementNode) throws { + throw AttributeDecodingError.badValue(Self.self) + } +} diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ButtonStyle.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ButtonStyle.swift index 7bb3bea19..eb59334b4 100644 --- a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ButtonStyle.swift +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ButtonStyle.swift @@ -43,6 +43,8 @@ enum StylesheetResolvablePrimitiveButtonStyle: @preconcurrency PrimitiveButtonSt case link #endif case plain + + case glass } extension StylesheetResolvablePrimitiveButtonStyle { @@ -87,6 +89,12 @@ extension StylesheetResolvablePrimitiveButtonStyle { #endif case .plain: SwiftUI.Button(configuration).buttonStyle(.plain) + case .glass: + if #available(iOS 26, macOS 26, tvOS 26, visionOS 26, watchOS 26, *) { + SwiftUI.Button(configuration).buttonStyle(.glass) + } else { + SwiftUI.Button(configuration).buttonStyle(.automatic) + } } } } @@ -118,6 +126,8 @@ extension StylesheetResolvablePrimitiveButtonStyle: @preconcurrency AttributeDec #endif case "plain": self = .plain + case "glass": + self = .glass default: throw AttributeDecodingError.badValue(Self.self) } diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ShapeStyle.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ShapeStyle.swift index b9e99f44e..0710fd16c 100644 --- a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ShapeStyle.swift +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/Styles/ShapeStyle.swift @@ -621,3 +621,21 @@ extension ShadowStyle { } } } + +@available(iOS 26.0, *) +extension Color.ResolvedHDR { + public enum Resolvable: StylesheetResolvable, @preconcurrency Decodable { + case __constant(SwiftUI.Color.ResolvedHDR) + + public init(from decoder: any Decoder) throws { + fatalError() + } + + public func resolve(on element: ElementNode, in context: LiveContext) -> Color.ResolvedHDR where R : RootRegistry { + switch self { + case let .__constant(resolvedHDR): + return resolvedHDR + } + } + } +} diff --git a/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/TextSelection.swift b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/TextSelection.swift new file mode 100644 index 000000000..7237462b2 --- /dev/null +++ b/Sources/LiveViewNative/Stylesheets/ResolvableTypes/SwiftUI/TextSelection.swift @@ -0,0 +1,24 @@ +// +// TextSelection.swift +// LiveViewNative +// +// Created by Carson.Katri on 9/9/25. +// + +import SwiftUI +import LiveViewNativeCore + +@available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *) +extension TextSelection: @retroactive Codable, AttributeDecodable { + public init(from decoder: any Decoder) throws { + fatalError("not supported") + } + + public func encode(to encoder: any Encoder) throws { + fatalError("not supported") + } + + public init(from attribute: LiveViewNativeCore.Attribute?, on element: ElementNode) throws { + fatalError() + } +} diff --git a/Sources/LiveViewNative/Views/Toolbars/ToolbarItem.swift b/Sources/LiveViewNative/Views/Toolbars/ToolbarItem.swift index 08a46c42f..7c3124f46 100644 --- a/Sources/LiveViewNative/Views/Toolbars/ToolbarItem.swift +++ b/Sources/LiveViewNative/Views/Toolbars/ToolbarItem.swift @@ -66,7 +66,7 @@ import LiveViewNativeCore struct ToolbarItem: ToolbarContent { /// The position of this item in the toolbar. @_documentation(visibility: public) - private var placement: ToolbarItemPlacement = .automatic + private var placement: _ToolbarItemPlacement = .automatic init(element: ElementNode) { self._liveElement = .init(element: element) @@ -83,7 +83,7 @@ struct ToolbarItem: ToolbarContent { @_documentation(visibility: public) @LiveElement struct CustomizableToolbarItem: CustomizableToolbarContent { - var placement: ToolbarItemPlacement = .automatic + var placement: _ToolbarItemPlacement = .automatic /// The unique ID for this customizable item. @_documentation(visibility: public) @@ -131,7 +131,7 @@ struct CustomizableToolbarItem: CustomizableToolbarContent { /// The positioning of a toolbar item. @_documentation(visibility: public) -enum ToolbarItemPlacement: String, AttributeDecodable { +enum _ToolbarItemPlacement: String, AttributeDecodable { @_documentation(visibility: public) case automatic @_documentation(visibility: public) diff --git a/Sources/LiveViewNative/Views/Toolbars/ToolbarItemGroup.swift b/Sources/LiveViewNative/Views/Toolbars/ToolbarItemGroup.swift index 9d12a39d2..16d9290be 100644 --- a/Sources/LiveViewNative/Views/Toolbars/ToolbarItemGroup.swift +++ b/Sources/LiveViewNative/Views/Toolbars/ToolbarItemGroup.swift @@ -32,7 +32,7 @@ import SwiftUI struct ToolbarItemGroup: ToolbarContent { /// The position of this group in the toolbar. @_documentation(visibility: public) - private var placement: ToolbarItemPlacement = .automatic + private var placement: _ToolbarItemPlacement = .automatic init(element: ElementNode) { self._liveElement = .init(element: element) diff --git a/Sources/ModifierGenerator/ModifierGenerator.swift b/Sources/ModifierGenerator/ModifierGenerator.swift index 4ad647343..148af63af 100644 --- a/Sources/ModifierGenerator/ModifierGenerator.swift +++ b/Sources/ModifierGenerator/ModifierGenerator.swift @@ -6,6 +6,9 @@ import SwiftParser @main struct ModifierGenerator: ParsableCommand { +// @Argument(transform: { URL(filePath: $0) }) +// private var input: URL +// /// The path used as the output file. @Argument(transform: { URL(filePath: $0) }) private var output: URL diff --git a/Sources/ModifierGenerator/StyleDefinitionGenerator/StyleDefinitionGenerator.swift b/Sources/ModifierGenerator/StyleDefinitionGenerator/StyleDefinitionGenerator.swift index e210c1563..00bcb200c 100644 --- a/Sources/ModifierGenerator/StyleDefinitionGenerator/StyleDefinitionGenerator.swift +++ b/Sources/ModifierGenerator/StyleDefinitionGenerator/StyleDefinitionGenerator.swift @@ -181,12 +181,39 @@ public final class StyleDefinitionGenerator: SyntaxVisitor { let extensionAvailability = node.attributes.filter(\.isAvailability) + print(node.memberBlock.members.compactMap({ $0.decl.syntaxNodeType })) + for modifier in node.memberBlock.members - .compactMap({ $0.decl.as(FunctionDeclSyntax.self) }) - .filter(\.isValidModifier) - .map({ $0.normalizedParameterTypes() }) - .filter(\.isPublic) - .filter({ !denylist.contains($0.name.text) }) + .reduce(into: [FunctionDeclSyntax](), { functionDecls, member in + if let functionDecl = member.decl.as(FunctionDeclSyntax.self), + functionDecl.isValidModifier, + functionDecl.isPublic, + !denylist.contains(functionDecl.name.text) + { + functionDecls.append(functionDecl.normalizedParameterTypes()) + } else if let ifConfig = member.decl.as(IfConfigDeclSyntax.self) { + // extract clauses from #if blocks, for example: + // #if compiler(>= 5.3) && $NoncopyableTypes + // func modifier(...) -> some SwiftUICore.View + // #endif + for clause in ifConfig.clauses { + switch clause.elements { + case let .decls(decls): + for decl in decls { + if let functionDecl = decl.decl.as(FunctionDeclSyntax.self), + functionDecl.isValidModifier, + functionDecl.isPublic, + !denylist.contains(functionDecl.name.text) + { + functionDecls.append(functionDecl.normalizedParameterTypes()) + } + } + default: + break + } + } + } + }) { // special case for `toolbar` to exclude `ViewReference` clauses. if modifier.name.text == "toolbar" { diff --git a/Sources/ModifierGenerator/denylist.swift b/Sources/ModifierGenerator/denylist.swift index 9850c3f2f..647d4e9fa 100644 --- a/Sources/ModifierGenerator/denylist.swift +++ b/Sources/ModifierGenerator/denylist.swift @@ -51,5 +51,8 @@ let denylist = [ "touchBar", - "pageCommand" + "pageCommand", + + "attributedTextFormattingDefinition", + "navigationSubtitle" ] diff --git a/Sources/ModifierGenerator/typeDenylist.swift b/Sources/ModifierGenerator/typeDenylist.swift index 9367ac6a2..97b9a48a4 100644 --- a/Sources/ModifierGenerator/typeDenylist.swift +++ b/Sources/ModifierGenerator/typeDenylist.swift @@ -3,4 +3,6 @@ let typeDenylist = [ "GraphicsContext", "Foundation.AttributeScopes.SwiftUIAttributes", "Edge.Set", + "TextSelection.Indices", + "AttributedTextSelection" ] diff --git a/Sources/SwiftSyntaxExtensions/NormalizableDeclSyntax.swift b/Sources/SwiftSyntaxExtensions/NormalizableDeclSyntax.swift index 61bfb9ef3..119b20578 100644 --- a/Sources/SwiftSyntaxExtensions/NormalizableDeclSyntax.swift +++ b/Sources/SwiftSyntaxExtensions/NormalizableDeclSyntax.swift @@ -69,9 +69,10 @@ public extension NormalizableDeclSyntax { if let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -99,9 +100,10 @@ public extension NormalizableDeclSyntax { if let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -128,9 +130,10 @@ public extension NormalizableDeclSyntax { if let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -158,9 +161,10 @@ public extension NormalizableDeclSyntax { if let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -225,6 +229,7 @@ public extension NormalizableDeclSyntax { else { fatalError("Unsupported parameter '\(functionType)'. Function parameters must have a 'Void' return value.") } return parameter .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: .identifier("Event")))) + .with(\.attributes, []) } if let optionalType = parameter.type.as(OptionalTypeSyntax.self), let tupleType = optionalType.wrappedType.as(TupleTypeSyntax.self), @@ -237,6 +242,7 @@ public extension NormalizableDeclSyntax { else { fatalError("Unsupported parameter '\(functionType)'. Function parameters must have a 'Void' return value.") } return parameter .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: .identifier("Event")))) + .with(\.attributes, []) } if let attributedType = parameter.type.as(AttributedTypeSyntax.self), let functionType = attributedType.baseType.as(FunctionTypeSyntax.self) @@ -247,6 +253,7 @@ public extension NormalizableDeclSyntax { else { fatalError("Unsupported parameter '\(functionType)'. Function parameters must have a 'Void' return value.") } return parameter .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: .identifier("Event")))) + .with(\.attributes, []) } if let attributedType = parameter.type.as(AttributedTypeSyntax.self), let tupleType = attributedType.baseType.as(TupleTypeSyntax.self), @@ -259,6 +266,7 @@ public extension NormalizableDeclSyntax { else { fatalError("Unsupported parameter '\(functionType)'. Function parameters must have a 'Void' return value.") } return parameter .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: .identifier("Event")))) + .with(\.attributes, []) } // [S] where S == String -> [String] // [S] where S: Protocol -> [StylesheetResolvableProtocol] @@ -268,9 +276,10 @@ public extension NormalizableDeclSyntax { if let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -304,9 +313,10 @@ public extension NormalizableDeclSyntax { let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -340,9 +350,10 @@ public extension NormalizableDeclSyntax { let genericType = genericWhereClause?.requirements.lazy.compactMap({ requirement -> TypeSyntax? in switch requirement.requirement { case let .sameTypeRequirement(sameType): - guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text + guard sameType.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text, + case let .type(rightType) = sameType.rightType else { return nil } - return sameType.rightType + return rightType case let .conformanceRequirement(conformance): guard conformance.leftType.as(IdentifierTypeSyntax.self)?.name.text == identifierType.name.text else { return nil } @@ -392,7 +403,7 @@ public extension NormalizableDeclSyntax { } else if let memberType = parameter.type.as(MemberTypeSyntax.self), memberType.baseType.as(IdentifierTypeSyntax.self)?.name.text == "Swift", memberType.name.text == "Set", - let elementType = memberType.genericArgumentClause?.arguments.first?.argument + case let .type(elementType) = memberType.genericArgumentClause?.arguments.first?.argument { // Swift.Set -> AttributeReference> return parameter .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: .identifier("StylesheetResolvableSet"), genericArgumentClause: GenericArgumentClauseSyntax { @@ -570,10 +581,10 @@ public extension TypeSyntax { } else if let memberType = self.as(MemberTypeSyntax.self), memberType.baseType.as(IdentifierTypeSyntax.self)?.name.text == "Swift", memberType.name.text == "Set", - let elementType = memberType.genericArgumentClause?.arguments.first?.argument + case let .type(elementType) = memberType.genericArgumentClause?.arguments.first?.argument { // Swift.Set -> StylesheetResolvableSet return TypeSyntax(IdentifierTypeSyntax(name: .identifier("StylesheetResolvableSet"), genericArgumentClause: GenericArgumentClauseSyntax { - GenericArgumentSyntax(argument: MemberTypeSyntax(baseType: elementType, name: .identifier("Resolvable"))) + GenericArgumentSyntax(argument: .type(TypeSyntax(MemberTypeSyntax(baseType: elementType, name: .identifier("Resolvable"))))) })) } else if let memberType = self.as(MemberTypeSyntax.self), memberType.baseType.as(IdentifierTypeSyntax.self)?.name.text == "SwiftUICore", diff --git a/Sources/SwiftSyntaxExtensions/Types.swift b/Sources/SwiftSyntaxExtensions/Types.swift index 9ffcc6314..d9ffb5c0c 100644 --- a/Sources/SwiftSyntaxExtensions/Types.swift +++ b/Sources/SwiftSyntaxExtensions/Types.swift @@ -45,12 +45,18 @@ public extension TypeSyntaxProtocol { // generic arguments must be valid (A) if let identifierType = self.as(IdentifierTypeSyntax.self), - !(identifierType.genericArgumentClause?.arguments.allSatisfy(\.argument.isValidModifierType) ?? true) + !(identifierType.genericArgumentClause?.arguments.allSatisfy({ + guard case .type(let type) = $0.argument else { return false } + return type.isValidModifierType + }) ?? true) { return false } if let memberType = self.as(MemberTypeSyntax.self), - !(memberType.genericArgumentClause?.arguments.allSatisfy(\.argument.isValidModifierType) ?? true) + !(memberType.genericArgumentClause?.arguments.allSatisfy({ + guard case .type(let type) = $0.argument else { return false } + return type.isValidModifierType + }) ?? true) { return false } @@ -205,7 +211,10 @@ public extension TypeSyntaxProtocol { case "Bool", "Duration", "Float", "Float16", "Int", "Int128", "Int16", "Int32", "Int64", "Int8", "LocalTestingActorID", "Never", "ObservationRegistrar", "SIMD16", "SIMD2", "SIMD3", "SIMD32", "SIMD4", "SIMD64", "SIMD8", "SIMDMask", "String", "TaskPriority", "UInt", "UInt128", "UInt16", "UInt32", "UInt64", "UInt8": return true case "ClosedRange", "CollectionDifference", "ContiguousArray", "Optional", "PartialRangeFrom", "PartialRangeThrough", "PartialRangeUpTo", "Range", "Set", "Dictionary": - return memberType.genericArgumentClause?.arguments.allSatisfy({ $0.argument.isPrimitiveDecodable }) ?? true + return memberType.genericArgumentClause?.arguments.allSatisfy({ + guard case .type(let type) = $0.argument else { return false } + return type.isPrimitiveDecodable + }) ?? true default: return false } @@ -225,7 +234,10 @@ public extension TypeSyntaxProtocol { case "Bool", "Duration", "Float", "Float16", "Int", "Int128", "Int16", "Int32", "Int64", "Int8", "LocalTestingActorID", "Never", "ObservationRegistrar", "SIMD16", "SIMD2", "SIMD3", "SIMD32", "SIMD4", "SIMD64", "SIMD8", "SIMDMask", "String", "TaskPriority", "UInt", "UInt128", "UInt16", "UInt32", "UInt64", "UInt8": return true case "ClosedRange", "CollectionDifference", "ContiguousArray", "Optional", "PartialRangeFrom", "PartialRangeThrough", "PartialRangeUpTo", "Range", "Set", "Dictionary": - return identifierType.genericArgumentClause?.arguments.allSatisfy({ $0.argument.isPrimitiveDecodable }) ?? true + return identifierType.genericArgumentClause?.arguments.allSatisfy({ + guard case .type(let type) = $0.argument else { return false } + return type.isPrimitiveDecodable + }) ?? true case "TimeInterval", "Date", "URL", "Data": return true default: diff --git a/Sources/SwiftSyntaxExtensions/ValueResolution.swift b/Sources/SwiftSyntaxExtensions/ValueResolution.swift index b1c608a22..ca4f6d985 100644 --- a/Sources/SwiftSyntaxExtensions/ValueResolution.swift +++ b/Sources/SwiftSyntaxExtensions/ValueResolution.swift @@ -61,13 +61,18 @@ public extension FunctionParameterSyntax { ) )) .with(\.defaultValue, self.defaultValue.flatMap({ defaultValue in - InitializerClauseSyntax(value: FunctionCallExprSyntax( - calledExpression: MemberAccessExprSyntax(name: .identifier("constant")), - leftParen: .leftParenToken(), - rightParen: .rightParenToken() - ) { - LabeledExprSyntax(expression: defaultValue.value) - }) + // can't provide a default for a some/any type + if self.type.as(IdentifierTypeSyntax.self)?.name.text.hasPrefix("StylesheetResolvable") == true { + return nil + } else { + return InitializerClauseSyntax(value: FunctionCallExprSyntax( + calledExpression: MemberAccessExprSyntax(name: .identifier("constant")), + leftParen: .leftParenToken(), + rightParen: .rightParenToken() + ) { + LabeledExprSyntax(expression: defaultValue.value) + }) + } })) } @@ -181,13 +186,15 @@ public extension FunctionParameterSyntax { name: .identifier("castProjectedValue") ) ) { - LabeledExprSyntax( - label: "type", - expression: MemberAccessExprSyntax( - base: TypeExprSyntax(type: changeTrackedType.genericArgumentClause!.arguments.first!.argument), - name: .identifier("self") + if case let .type(type) = changeTrackedType.genericArgumentClause!.arguments.first!.argument { + LabeledExprSyntax( + label: "type", + expression: MemberAccessExprSyntax( + base: TypeExprSyntax(type: type), + name: .identifier("self") + ) ) - ) + } }) } else if self.type.isResolvableType { // value.resolve(on: __element, in: __context)