-
-
Notifications
You must be signed in to change notification settings - Fork 15
Add Wrappers for Class, Actor, Extension #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
78d29e2
fc363e2
473a533
a3551fb
95b8c29
f0d003e
9b12009
3a18d77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,48 @@ | ||
import SwiftSyntax | ||
|
||
/// A protocol for declaration groups (e.g., `struct`, `class`, `enum`) that can contain members and properties. | ||
/// Declaration groups are higher-level constructs compared to regular declarations such as `var`. | ||
public protocol DeclGroupProtocol: RawRepresentable { | ||
/// The underlying syntax node for the declaration group. | ||
var rawValue: RawValue { get } | ||
|
||
/// The declaration's identifier. | ||
/// | ||
/// Note: SwiftSyntax's `DeclGroupSyntax` protocol does not include the declaration's identifier. | ||
/// This must be implemented manually for each declaration wrapper. This omission might be due to | ||
/// the fact that extensions technically do not have a name, even though they are always attached to a specific identifier. | ||
/// A protocol that represents a declaration group, such as a `struct`, `class`, `enum`, or `protocol`. | ||
/// This protocol defines common properties that all declaration groups should have. | ||
/// | ||
/// Conforming types should provide the following properties: | ||
/// - `identifier`: The identifier of the declaration group. | ||
/// - `members`: The members of the declaration group. | ||
/// - `properties`: The properties declared within the declaration group. | ||
/// - `inheritedTypes`: The types that the declaration group inherits from or conforms to. | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public protocol AnyDeclGroupProtocol { | ||
/// The identifier of the declaration group. | ||
var identifier: String { get } | ||
|
||
/// The members of the declaration group. | ||
/// This array contains all the members declared within the declaration group. | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var members: [Decl] { get } | ||
|
||
/// The properties declared in the declaration group. | ||
/// This array contains all the properties declared within the declaration group. | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var properties: [Property] { get } | ||
|
||
/// The inherited types of the declaration group. | ||
/// This array contains all the types that the declaration group inherits from or conforms to. | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var inheritedTypes: [Type] { get } | ||
|
||
/// Initializes the declaration group with the given syntax node. | ||
/// | ||
/// - Parameter syntax: The underlying syntax node representing the declaration group. | ||
init(_ syntax: RawValue) | ||
} | ||
|
||
/// Default implementations and helper initializers for `DeclGroupProtocol`. | ||
extension DeclGroupProtocol { | ||
/// The underlying syntax node for the declaration group. | ||
public var _syntax: RawValue { | ||
rawValue | ||
} | ||
/// A protocol that represents a declaration group with an underlying syntax node. | ||
/// This protocol extends `AnyDeclGroupProtocol` and `RepresentableBySyntax` to provide additional | ||
/// functionality specific to declaration groups in Swift syntax. | ||
/// | ||
/// Conforming types must: | ||
/// - Conform to `AnyDeclGroupProtocol`, providing properties for `identifier`, `members`, `properties`, and `inheritedTypes`. | ||
/// - Conform to `RepresentableBySyntax`, providing an underlying syntax node of type `DeclGroupSyntax`. | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public protocol DeclGroupProtocol: AnyDeclGroupProtocol, RepresentableBySyntax | ||
where UnderlyingSyntax: DeclGroupSyntax {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the requirement in this where clause gets removed then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue lies with the Another approach could be to use a type-erased syntax, similar to the following: public struct AnySyntax: SyntaxProtocol {
public static var structure: SyntaxNodeStructure {
// Returning a default or dummy structure, indicating this shouldn't be used directly
return SyntaxNodeStructure.choices([.node(Syntax.self)])
}
public var _syntaxNode: Syntax { _syntaxNodeClosure() }
private let _syntaxNodeClosure: () -> Syntax
public init<S: SyntaxProtocol>(_ syntax: S) {
self._syntaxNodeClosure = { syntax._syntaxNode }
}
} But perhaps it wouldn’t make sense for something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok, strange that |
||
|
||
/// Initializes the declaration group with the given raw value. | ||
/// | ||
/// - Parameter rawValue: The raw value representing the declaration group. | ||
public init?(rawValue: RawValue) { | ||
self.init(rawValue) | ||
} | ||
} | ||
|
||
/// Additional functionality for `DeclGroupProtocol` where the raw value conforms to `DeclGroupSyntax`. | ||
extension DeclGroupProtocol where RawValue: DeclGroupSyntax { | ||
extension DeclGroupProtocol { | ||
/// Attempts to initialize the wrapper from an arbitrary declaration group. | ||
/// | ||
/// - Parameter syntax: The syntax node representing the declaration group. | ||
/// - Note: This initializer will return `nil` if the syntax node does not match the expected type. | ||
public init?(_ syntax: any DeclGroupSyntax) { | ||
guard let rawValue = syntax as? RawValue else { return nil } | ||
self.init(rawValue: rawValue) | ||
guard let syntax = syntax as? UnderlyingSyntax else { return nil } | ||
self.init(syntax) | ||
} | ||
|
||
/// The members of the declaration group. | ||
|
@@ -89,11 +80,11 @@ extension DeclGroupProtocol where RawValue: DeclGroupSyntax { | |
|
||
/// The access level of the declaration group. | ||
public var accessLevel: AccessModifier? { | ||
AccessModifier(modifiers: _syntax.modifiers) | ||
AccessModifier(firstModifierOfKindIn: _syntax.modifiers) | ||
} | ||
|
||
/// The context-specific modifiers of the declaration group. | ||
public var declarationContext: DeclarationContextModifier? { | ||
DeclarationContextModifier(modifiers: _syntax.modifiers) | ||
DeclarationContextModifier(firstModifierOfKindIn: _syntax.modifiers) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import SwiftSyntax | ||
|
||
/// A protocol that provides a consistent interface for types that are represented by an underlying syntax node. | ||
/// This protocol is useful for working with various SwiftSyntax types in a unified manner. | ||
/// | ||
/// Types conforming to this protocol must define an associated `UnderlyingSyntax` type that conforms to `SyntaxProtocol`. | ||
/// They must also provide a `_syntax` property to access the underlying syntax node and an initializer to create an instance from the syntax node. | ||
public protocol RepresentableBySyntax { | ||
ajkolean marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The type of the underlying syntax node that this type represents. | ||
associatedtype UnderlyingSyntax: SyntaxProtocol | ||
|
||
/// The underlying syntax node for this type. | ||
var _syntax: UnderlyingSyntax { get set } | ||
|
||
/// Initializes an instance with the given underlying syntax node. | ||
/// | ||
/// - Parameter syntax: The underlying syntax node to represent. | ||
init(_ syntax: UnderlyingSyntax) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason for renaming this to
AnyDeclGroup
? I can see a few upsides and would be happy to discuss, but best to apply this convention to the other root-level enums as well if we decide it's a good ideaEdit: I've now gone through the rest of the changes and have seen that it was so that
DeclGroupProtocol
can have a hard requirement on the type of its wrapped syntax, I've left a more open question/remark at the where clause. Not sure what the best design would be, and would love to hear your thoughts since you've been trying out a few different approaches and likely have a bit more insight into the problem than I do.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The goal of renaming to
AnyDeclGroup
was to align with Swift's conventions for type-erased types, likeAnyView
,AnyHashable
, andAnyCancellable
, making the distinction between concrete types and their type-erased counterparts clear and intuitive. This consistency with the standard library and clarity in differentiation helps in understanding the purpose ofAnyDeclGroup
. The aim was to allowDeclGroupProtocol
to have a hard requirement on the type of its wrapped syntax while maintaining flexibility, and type erasure withAnyDeclGroup
achieves this by holding any type conforming toDeclGroupProtocol
. If we adoptAnyDeclGroup
, similar updates should be applied to other root-level enums for consistency, but this is beyond the scope of the current PR and should be handled separately. For now, I've reverted toDeclGroup
to match the existing naming convention, and if we decide to use theAny
prefix, we should plan a broader refactor for consistency. I appreciate your feedback and am open to further discussion to find the best design approach.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I can see why the
Any
prefix made sense cause ofDeclGroupSyntax
being a protocol for whatever weird reason. The reason that I don't have theAny
prefix on any of the others (e.g.Decl
) is because they're still concrete and not erasing any info (since they're enums), so I guess in this case it depends what's more important to devs; the fact that it's erasing the_syntax
escape hatch, or the fact that it's not erasing the underlying decl group information.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In-fact,
DeclGroup
can still conform toRepresentableBySyntax
, but just with the underlying syntax type beingSyntax
instead (the main use would just be for using the syntax node in diagnostics, since all other useful decl group syntax info would unfortunately be erased)