diff --git a/Examples/Sources/MacroExamples/Implementation/Accessor/EnvironmentValueMacro.swift b/Examples/Sources/MacroExamples/Implementation/Accessor/EnvironmentValueMacro.swift new file mode 100644 index 00000000000..01510a12726 --- /dev/null +++ b/Examples/Sources/MacroExamples/Implementation/Accessor/EnvironmentValueMacro.swift @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxMacros + +public struct EnvironmentValueMacro: AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + guard + case let .argumentList(arguments) = node.arguments, + let argument = arguments.first + else { return [] } + + return [ + """ + get { self[\(argument.expression)] } + """, + """ + set { self[\(argument.expression)] = newValue } + """, + ] + } +} diff --git a/Examples/Sources/MacroExamples/Implementation/Plugin.swift b/Examples/Sources/MacroExamples/Implementation/Plugin.swift index e5960e80712..cd157e62baf 100644 --- a/Examples/Sources/MacroExamples/Implementation/Plugin.swift +++ b/Examples/Sources/MacroExamples/Implementation/Plugin.swift @@ -26,6 +26,7 @@ struct MyPlugin: CompilerPlugin { DefaultFatalErrorImplementationMacro.self, DictionaryStorageMacro.self, DictionaryStoragePropertyMacro.self, + EnvironmentValueMacro.self, EquatableExtensionMacro.self, FontLiteralMacro.self, FuncUniqueMacro.self, diff --git a/Examples/Sources/MacroExamples/Interface/AccessorMacros.swift b/Examples/Sources/MacroExamples/Interface/AccessorMacros.swift new file mode 100644 index 00000000000..17b6f16244e --- /dev/null +++ b/Examples/Sources/MacroExamples/Interface/AccessorMacros.swift @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if canImport(SwiftUI) + +import SwiftUI + +// MARK: - EnvironmentValue Accessor + +/// Adds getter / setter to an attached environment value with specified EnvironmentKey +@attached(accessor) +public macro EnvironmentValue(for key: any EnvironmentKey.Type) = + #externalMacro(module: "MacroExamplesImplementation", type: "EnvironmentValueMacro") + +#endif diff --git a/Examples/Sources/MacroExamples/Playground/AccessorMacrosPlayground.swift b/Examples/Sources/MacroExamples/Playground/AccessorMacrosPlayground.swift new file mode 100644 index 00000000000..e100945a573 --- /dev/null +++ b/Examples/Sources/MacroExamples/Playground/AccessorMacrosPlayground.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// MARK: - EnvironmentValue Accessor + +import MacroExamplesInterface + +#if canImport(SwiftUI) + +import SwiftUI + +private struct MyEnvironmentKey: EnvironmentKey { + static let defaultValue: String = "Default value" +} + +extension EnvironmentValues { + @EnvironmentValue(for: MyEnvironmentKey.self) + var myCustomValue: String +} + +func runEnvironmentValueAccessorMacroPlayground() { + var environmentValues = EnvironmentValues() + print("Default myCustomValue: \(environmentValues.myCustomValue)") + environmentValues.myCustomValue = "New value" + print("New myCustomValue: \(environmentValues.myCustomValue)") +} + +#endif diff --git a/Examples/Sources/MacroExamples/Playground/main.swift b/Examples/Sources/MacroExamples/Playground/main.swift index d9af26d3e0e..674b9b9b745 100644 --- a/Examples/Sources/MacroExamples/Playground/main.swift +++ b/Examples/Sources/MacroExamples/Playground/main.swift @@ -47,3 +47,11 @@ runMemberMacrosPlayground() // MARK: - Peer Macros runPeerMacrosPlayground() + +// MARK: - Accessor Macros + +#if canImport(SwiftUI) + +runEnvironmentValueAccessorMacroPlayground() + +#endif diff --git a/Examples/Tests/MacroExamples/Implementation/Accessor/EnvironmentValueMacroTests.swift b/Examples/Tests/MacroExamples/Implementation/Accessor/EnvironmentValueMacroTests.swift new file mode 100644 index 00000000000..73749476ebb --- /dev/null +++ b/Examples/Tests/MacroExamples/Implementation/Accessor/EnvironmentValueMacroTests.swift @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import MacroExamplesImplementation +import SwiftSyntaxMacros +import SwiftSyntaxMacrosTestSupport +import XCTest + +final class EnvironmentValueMacroMacroTests: XCTestCase { + private let macros = ["EnvironmentValue": EnvironmentValueMacro.self] + + func testEnvironmentValue() { + assertMacroExpansion( + """ + extension EnvironmentValues { + @EnvironmentValue(for: MyEnvironmentKey.self) + var myCustomValue: String + } + """, + expandedSource: """ + extension EnvironmentValues { + var myCustomValue: String { + get { + self[MyEnvironmentKey.self] + } + set { + self[MyEnvironmentKey.self] = newValue + } + } + } + """, + macros: macros + ) + } +}