-
Notifications
You must be signed in to change notification settings - Fork 439
[NFC-ish] Rework compatibility layer generation #2888
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
Conversation
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.
Looks like a great clean-up to the somewhat ad-hoc deprecation design we had before.
@@ -40,7 +40,8 @@ public enum ChildKind { | |||
/// The child always contains a node of the given `kind`. | |||
case node(kind: SyntaxNodeKind) | |||
/// The child always contains a node that matches one of the `choices`. | |||
case nodeChoices(choices: [Child]) | |||
case nodeChoices(choices: [Child], childHistory: Child.History = []) | |||
// FIXME: We don't appear to have ever generated compatibility layers for children of node choices! |
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.
I think you resolved this comment, right?
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.
nodeChoices
still doesn't have compatibility layers. I think largely because Swift can't alias enum cases—you can use a deprecated static method to construct the replacement case, but not to pattern-match it.
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.
Instead of having a FIXME
for it (which is very easy to miss), could we raise a fatalError
if you try to add a compatibility layer of node choices? That way you can’t miss it if you try to add one.
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.
So, there actually are existing cases where people have specified a deprecatedName:
for a child of a ChildKind.nodeChoices
; this information was simply ignored until now. Rather than deleting it, I thought it'd be better to preserve the info in the node definition files, but if we'd rather just delete it (which—again—would be an NFC change), I can remove the childHistory
value here entirely.
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.
I haven't done anything with this yet, but I think I've addressed everything else you suggested.
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.
Thanks for checking. Could you file an issue for the FIXME? Then we can take a look at it after this PR is merged.
CodeGeneration/Sources/generate-swift-syntax/InitSignature+Extensions.swift
Outdated
Show resolved
Hide resolved
...n/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift
Outdated
Show resolved
Hide resolved
a461ff2
to
75f6c3f
Compare
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.
Thanks for addressing my comments, @beccadax.
public var deprecatedMembersByTrait: [String: DeprecatedMemberInfo] = [:] | ||
|
||
/// Cache for `replacementChild(for:)`. Ensures that we don't create | ||
/// two different replacement nodes even if we refactor the same child twice. |
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.
I would just add
/// two different replacement nodes even if we refactor the same child twice. | |
/// two different replacement nodes even if we refactor the same child twice. | |
/// This is important because we use pointer equality of `Child` to decide if two children are the same. |
} | ||
|
||
internal init(nodes: [Node], traits: [Trait]) { | ||
// Note that we compute these up-front, greedily, so that all of the mutation is isolated by the lazy var's state. |
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.
Which lazy var? Is this a leftover from a previous version?
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.
I was referring to the SYNTAX_COMPATIBILITY_LAYER
global, which like all Swift globals, is implicitly lazily initialized with synchronization.
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.
Would this be a better way to describe the situation?
// Note that we compute these up-front, greedily, so that all of the mutation is isolated by the lazy var's state. | |
// This instance will be stored in a global that's used from multiple threads simultaneously, so it won't be safe | |
// to mutate once the initializer returns. We therefore do all the work to populate its tables up front, rather | |
// than computing it lazily on demand. |
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.
Sounds great to me. Thanks!
75f6c3f
to
e458bdf
Compare
@swift-ci please test |
e458bdf
to
e5bde56
Compare
Turned out this was happening with base nodes, since they don't have any children (but their unexpected nodes are ignored anyway). Pulled in a fix that avoids trying to create unexpected children for base nodes. |
@swift-ci please test |
Introduce and factor out a type that represents a list of children for which an initializer ought to exist. This changes some `@available(renamed:)` values, but nothing that should change diagnostics or behavior.
A new CompatibilityLayer type computes information about deprecated vars and inits, presenting it in a new way: by creating additional `Child` objects with the deprecated information, plus sufficient info to map it back to the current name. This causes minor non-functional changes to the bodies of compatibility builder inits, but otherwise has no real effect in this commit. However, this new setup with separate `Child` nodes for deprecated children and a global table of information about them prepares us for further changes.
Introduce a much more sophisticated way of describing past revisions to a node’s children. This allows: • Multiple past changes (for instance, a property that has been renamed more than once). • The ability to represent the order of multiple changes and group together simultaneous changes. • Multiple kinds of changes, including changes that introduce more than one member. Note that this commit does not use any of these new capabilities; it just ports existing behavior to the new representation.
It’s always been possible to specify deprecated children, but nothing was actually done with the info until now. Turns some manaully-generated decls into automatically-generated ones, but doesn’t change anything user-facing.
e5bde56
to
f1fcb10
Compare
Needed to placate swift-format. |
@swift-ci please smoke test |
@swift-ci please test |
This PR replaces the
deprecatedName
property onChild
with a new, more sophisticated mechanism which supports:• Multiple past changes (for instance, a property that has been renamed more than once) with a different initializer for each past state.
• The ability to represent the order of multiple changes and group together simultaneous changes.
• Multiple kinds of changes, including an
extracted
change where a group of adjacent nodes are moved into a newly-created child node.This new
childHistory
mechanism also works onTrait
s, where it generates compatibility properties as one would expect. (We previously silently ignoreddeprecatedName
on a child of a trait.)This PR does not add or remove any SwiftSyntax APIs, but it does cause small, functionally equivalent changes to the generated code.
Extracted from #2882.