Add new features to ConvertStoredPropertyToComputed#3229
Add new features to ConvertStoredPropertyToComputed#3229ahoppen merged 1 commit intoswiftlang:mainfrom
Conversation
|
@ahoppen, Thanks for your review! |
ahoppen
left a comment
There was a problem hiding this comment.
Looking at this again, I noticed that the = { … }() should already be handled.
Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift
Outdated
Show resolved
Hide resolved
Tests/SwiftRefactorTest/ConvertStoredPropertyToComputedTest.swift
Outdated
Show resolved
Hide resolved
ahoppen
left a comment
There was a problem hiding this comment.
Sorry, looks like the trivia handling is going to be a little more complex than I initially thought. But nothing you won’t be able to do 😉
There was a problem hiding this comment.
I’m not sure it should be in this module
|
|
||
| /// Removes attributes from a syntax tree while maintaining their surrounding trivia. | ||
| @_spi(Testing) | ||
| public class NodeRemover: SyntaxRewriter { |
There was a problem hiding this comment.
Let’s change this to the package access level. I think we didn’t use to have it when this was written.
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import Foundation |
There was a problem hiding this comment.
swift-syntax must not depend on Foundation because it’s lower in the stack than Foundation. Could you remove the Foundation dependency?
| return AttributeListSyntax(filteredAttributes) | ||
| } | ||
|
|
||
| public override func visit(_ node: DeclModifierListSyntax) -> DeclModifierListSyntax { |
There was a problem hiding this comment.
Hmm, I was hoping that we could extend NodeRemover to be able to remove any arbitrary node. This doesn’t seem to be the case right now and I don’t want to have a semi-general type that promises to remove arbitrary nodes but isn’t actually able to do so. Maybe the better solution in that case is to extract all the common functionality (like the trivia convenience accessors) to the SwiftSyntax module and write another dedicated rewriter to remove decl modifiers.
There was a problem hiding this comment.
I’ll try to work on this idea
There was a problem hiding this comment.
I’d like to clarify — is the current purpose to extract the base logic into a NodeRemover class and have AttributeRemover and DeclModifierRemover inherit from it, with only strictly typed visit methods remaining in them?
There was a problem hiding this comment.
No, I’m not proposing a type hierarchy. Essentially just create two separate classes and whatever you can factor out, factor out. I was mostly talking about at least sharing Trivia.trimmingPrefix etc.
There was a problem hiding this comment.
Hope I got it right this time, could you please check and sorry for the previous misunderstanding
| var filteredModifiers: [DeclModifierListSyntax.Element] = [] | ||
|
|
||
| for modifier in node { | ||
| if self.predicate(Syntax(modifier)) { |
There was a problem hiding this comment.
Would this read easier as a guard self.predicate(Syntax(modifier)) else {? At least it saves an indentation level.
|
|
||
| for modifier in node { | ||
| if self.predicate(Syntax(modifier)) { | ||
| let trailingTrivia = modifier.trailingTrivia.trimmingPrefix(while: \.isSpaceOrTab) |
There was a problem hiding this comment.
Do we need to trim the spaces and tabs here. Shouldn’t that be handled already when merging triviaToAttachToNextToken into existing trivia? Or am I missing something?
There was a problem hiding this comment.
When lazy is first before the comment, removing lazy leaves the space before the comment – it doesn't merge with trivia after comment.
case handled in test testRefactoringStoredPropertyWithModifiersAndComment
There was a problem hiding this comment.
Oh, I see. That makes sense. Thanks for the explanation. Can you add that as a comment for future reference?
| if triviaToAttachToNextToken.isEmpty, | ||
| let previousToken = modifier.previousToken(viewMode: .sourceAccurate), | ||
| previousToken.trailingTrivia.isEmpty | ||
| { | ||
| triviaToAttachToNextToken = modifier.trailingTrivia | ||
| } |
There was a problem hiding this comment.
Why is this check necessary? Could you maybe add a doc comment that explains it?
| defer { triviaToAttachToNextToken = Trivia() } | ||
| return syntaxNode.with(\.leadingTrivia, triviaToAttachToNextToken + syntaxNode.leadingTrivia) |
There was a problem hiding this comment.
I think we should get a little better performance if we don’t modify the node if triviaToAttachToNextToken is empty (that way we don’t need to allocate a new node), ie. just do an early exist if triviaToAttachToNextToken is empty.
| guard let keyword = $0.as(DeclModifierSyntax.self) else { return false } | ||
| return keyword.name.tokenKind == lazyKeyword.name.tokenKind |
There was a problem hiding this comment.
Shouldn’t we be able to use the identity on Syntax to find the token to remove, ie. do something like the following
| guard let keyword = $0.as(DeclModifierSyntax.self) else { return false } | |
| return keyword.name.tokenKind == lazyKeyword.name.tokenKind | |
| $0.id == lazyKeyword.id |
ahoppen
left a comment
There was a problem hiding this comment.
Nice. Just a few small comments, otherwise LGTM.
|
|
||
| return DeclModifierListSyntax(filteredModifiers) | ||
| } | ||
|
|
There was a problem hiding this comment.
Do we also need a visit function for Token that calls prependAndClearAccumulatedTrivia to transfer trivia to the token after the last modifier, eg. in lazy /* comment */ var test: Int?
There was a problem hiding this comment.
Could you add a test case for this so we don’t regress it in the future?
|
Just #3229 (comment) left, after that this is good to go 🚀 |
ahoppen
left a comment
There was a problem hiding this comment.
Nice. I just realized that we didn’t report this refactoring action in SourceKit-LSP so far, fixed that in swiftlang/sourcekit-lsp#2458 because I was touching related code anyway.
|
@swift-ci Please test |
1 similar comment
|
@swift-ci Please test |
|
@swift-ci Please test Windows |
|
@myaumura We can ignore the Bazel build failure, it is because of #3232 (comment). |
|
@ahoppen, Thank you for the thorough review and detailed explanations—they really helped clarify the issues! Should I squash commits? |
|
Yes, squashing would be appreciated, it makes for a cleaner git history. And it appears that the CMake build doesn’t support the |
|
Sure, doing it now! |
|
@swift-ci Please test |
ce91e33 to
4b2774d
Compare
|
Also FWIW, adding support for the |
|
@swift-ci Please test |
|
@swift-ci Please test Windows |
|
Congrats to your first contribution to Swift, @myaumura 🚀 |
As part of swiftlang/sourcekit-lsp#2424.
Add new functionality to the existing code action analogous to ConvertToComputedProperty.cpp and tryComputedPropertyFixIts.
lazyattribute if present.=sign if variable likevar foo: Int = { return 0 }().I'm happy to make changes if needed