diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 16088765c7d..a9172ce9150 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -39,4 +39,5 @@ add_subdirectory(SwiftParserDiagnostics) add_subdirectory(SwiftOperators) add_subdirectory(SwiftSyntaxBuilder) add_subdirectory(SwiftSyntaxMacros) +add_subdirectory(SwiftCompilerPluginMessageHandling) add_subdirectory(IDEUtils) diff --git a/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt new file mode 100644 index 00000000000..5a838463580 --- /dev/null +++ b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt @@ -0,0 +1,22 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_swift_host_library(SwiftCompilerPluginMessageHandling + CompilerPluginMessageHandler.swift + Diagnostics.swift + Macros.swift + PluginMacroExpansionContext.swift + PluginMessages.swift +) + +target_link_libraries(SwiftCompilerPluginMessageHandling PUBLIC + SwiftSyntax + SwiftDiagnostics + SwiftParser + SwiftSyntaxMacros + SwiftOperators) diff --git a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift index 9d7f04d2a44..c01ad205b27 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift @@ -12,9 +12,23 @@ import SwiftSyntaxMacros +/// Optional features. +public enum PluginFeature: String { + case loadPluginLibrary = "load-plugin-library" +} + /// A type that provides the actual plugin functions. public protocol PluginProvider { + /// Resolve macro type by the module name and the type name. func resolveMacro(moduleName: String, typeName: String) -> Macro.Type? + + /// Load dynamic link library at `libraryPath`. Implementations can use + /// `moduleName` to associate the loaded library with it. + func loadPluginLibrary(libraryPath: String, moduleName: String) throws + + /// Optional plugin features. This is sent to the host so the it can decide + /// the behavior depending on these. + var features: [PluginFeature] { get } } /// Low level message connection to the plugin host. @@ -67,9 +81,11 @@ extension CompilerPluginMessageHandler { fileprivate func handleMessage(_ message: HostToPluginMessage) throws { switch message { case .getCapability: - try self.sendMessage( - .getCapabilityResult(capability: PluginMessage.capability) + let capability = PluginMessage.PluginCapability( + protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER, + features: provider.features.map({ $0.rawValue }) ) + try self.sendMessage(.getCapabilityResult(capability: capability)) case .expandFreestandingMacro(let macro, let discriminator, let expandingSyntax): try expandFreestandingMacro( @@ -87,6 +103,42 @@ extension CompilerPluginMessageHandler { declSyntax: declSyntax, parentDeclSyntax: parentDeclSyntax ) + + case .loadPluginLibrary(let libraryPath, let moduleName): + var diags: [PluginMessage.Diagnostic] = [] + do { + try provider.loadPluginLibrary(libraryPath: libraryPath, moduleName: moduleName) + } catch { + diags.append( + PluginMessage.Diagnostic( + message: String(describing: error), + severity: .error, + position: .invalid, + highlights: [], + notes: [], + fixIts: [] + ) + ) + } + try self.sendMessage(.loadPluginLibraryResult(loaded: diags.isEmpty, diagnostics: diags)); } } } + +struct UnimplementedError: Error, CustomStringConvertible { + var description: String { "unimplemented" } +} + +/// Default implementation of 'PluginProvider' requirements. +public extension PluginProvider { + var features: [PluginFeature] { + // No optional features by default. + return [] + } + + func loadPluginLibrary(libraryPath: String, moduleName: String) throws { + // This should be unreachable. The host should not call 'loadPluginLibrary' + // unless the feature is not declared. + throw UnimplementedError() + } +} diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index de5e44f0cfa..df67a178f43 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -14,14 +14,17 @@ // NOTE: Types in this file should be self-contained and should not depend on any non-stdlib types. internal enum HostToPluginMessage: Codable { + /// Get capability of this plugin. case getCapability + /// Expand a '@freestanding' macro. case expandFreestandingMacro( macro: PluginMessage.MacroReference, discriminator: String, syntax: PluginMessage.Syntax ) + /// Expand an '@attached' macro. case expandAttachedMacro( macro: PluginMessage.MacroReference, macroRole: PluginMessage.MacroRole, @@ -30,9 +33,21 @@ internal enum HostToPluginMessage: Codable { declSyntax: PluginMessage.Syntax, parentDeclSyntax: PluginMessage.Syntax? ) + + /// Optionally implemented message to load a dynamic link library. + /// 'moduleName' can be used as a hint indicating that the library + /// provides the specified module. + case loadPluginLibrary( + libraryPath: String, + moduleName: String + ) } internal enum PluginToHostMessage: Codable { + case getCapabilityResult( + capability: PluginMessage.PluginCapability + ) + case expandFreestandingMacroResult( expandedSource: String?, diagnostics: [PluginMessage.Diagnostic] @@ -43,18 +58,21 @@ internal enum PluginToHostMessage: Codable { diagnostics: [PluginMessage.Diagnostic] ) - case getCapabilityResult(capability: PluginMessage.PluginCapability) + case loadPluginLibraryResult( + loaded: Bool, + diagnostics: [PluginMessage.Diagnostic] + ) } /*namespace*/ internal enum PluginMessage { - static var PROTOCOL_VERSION_NUMBER: Int { 3 } // Renamed 'customAttributeSyntax' to 'attributeSyntax'. + static var PROTOCOL_VERSION_NUMBER: Int { 4 } // Added 'loadPluginLibrary'. struct PluginCapability: Codable { var protocolVersion: Int - } - static var capability: PluginCapability { - PluginCapability(protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER) + /// Optional features this plugin provides. + /// * 'load-plugin-library': 'loadPluginLibrary' message is implemented. + var features: [String]? } struct MacroReference: Codable {