-
Notifications
You must be signed in to change notification settings - Fork 424
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
Add keypath method and initializer syntax under an experimental feature flag keypathWithMethodMembers
#2950
base: main
Are you sure you want to change the base?
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.
The implementation looks good to me. I have a few small comments inline.
Child( | ||
name: "leftParen", | ||
kind: .token(choices: [.token(.leftParen)]), | ||
isOptional: true | ||
), |
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.
Why is the left parenthesis optional? Wouldn’t we just have a normal member component if we don’t have parentheses?
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 modeled this after .funcCallExpr
but you're right and I will make this non optional. @ahoppen I also have 2 related questions:
.keyPathPropertyComponent
has a .genericArgumentClause
but I don't see any examples of it being used in the tests. Do you happen to know when that is used? It seems like a generic on the root (eg: \Box<Int>.item
) is already being handled by KeyPathExpr
.
Is there a way to make .labeledExprList
optional? I forgot to account for this - partially applied methods parse their method name and arg list into DeclReferenceExpr
and do not have a LabeledExprList
. I'm inclined to create a second node to handle this (eg: .keyPathPartiallyAppliedComponent
).
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.
.keyPathPropertyComponent
has a.genericArgumentClause
but I don't see any examples of it being used in the tests. Do you happen to know when that is used? It seems like a generic on the root (eg:\Box<Int>.item
) is already being handled byKeyPathExpr
.
I don’t think a generic clause is actually valid in key path properties and maybe we should remove it. @rintaro Can you think of a reason why KeyPathPropertyComponentSyntax
should have a generic arguments clause?
Is there a way to make
.labeledExprList
optional? I forgot to account for this - partially applied methods parse their method name and arg list intoDeclReferenceExpr
and do not have aLabeledExprList
. I'm inclined to create a second node to handle this (eg:.keyPathPartiallyAppliedComponent
).
I’m not sure I understand. If there are no parentheses, we should be able to parse the component as a KeyPathPropertyComponentSyntax
. If there are parentheses and no arguments, arguments
can just be an empty list. And if there are arguments … then it doesn’t need to be optional.
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’m not sure I understand. If there are no parentheses, we should be able to parse the component as a KeyPathPropertyComponentSyntax. If there are parentheses and no arguments, arguments can just be an empty list. And if there are arguments … then it doesn’t need to be optional.
Should partial arguments should also be KeyPathPropertyComponentSyntax? method(_:)
and method(arg:)
for unapplied keypath methods are being parsed as DeclReferenceExpr
with DeclNameArgumentListSyntax
arguments instead of LabeledExprList
. I have pushed changes that reflect this, unsure if there are any ramifications of doing it this way, but the Swift and c++ parser outputs match.
e0c6ccb
to
7762c01
Compare
dfa57bf
to
dd259f6
Compare
Added additional tests to ensure Swift and c++ parsers match. |
while true { | ||
let token = lookahead.peek().rawTokenKind | ||
if token == .endOfFile || lookahead.atStartOfLine { | ||
break | ||
} | ||
if token == .leftParen { | ||
hasLParen = true | ||
} | ||
if token == .colon { | ||
lookahead.consumeAnyToken() | ||
// If there's a colon followed by a right parenthesis, it is | ||
// a partial application and should be parsed as a property. | ||
if lookahead.peek().rawTokenKind == .rightParen { | ||
return false | ||
} | ||
} | ||
if token == .rightParen { | ||
hasRParen = true | ||
} | ||
lookahead.consumeAnyToken() | ||
} | ||
// If parentheses exist with no partial application pattern, | ||
// parse as a key path method. | ||
return hasLParen && hasRParen ? true : false |
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.
This seems problematic to me. I haven’t fully though it through but it has the potential for an unbounded lookahead, which could be very bad for parser performance and the use of consumeAnyToken
to eat any uncovered token also seems like a pitfall to me.
What I would suggest is that in your actual parse*
method, you call parseDottedExpressionSuffix
outside of the if
. That would parse any compound names, ie. unapplied function references. If we are at a parenthesis after that, continue parsing arguments for an applied key path reference. That way, you can also parse key paths like \Foo.t(a:)(2)
, mirroring the following valid Swift code
struct Foo {
func t(a: Int) {
self.t(a:)(2)
}
}
Or am I missing something with that 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.
This makes sense and I've updated the code to reflect this. For reasons I can't reproduce now, parseDottedExpressionSuffix
did not work for me initially and so I wrote this new method. Regardless it works now. All good things to know about Lookahead
as well. Thank you!
6cf481b
to
6790783
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.
That looks great and is much simpler as well. Just two minor comments, everything else is great.
6790783
to
fa3596e
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.
Looks good to me 🚀
@swift-ci Please test |
Not sure why this test is failing - is it due to my imports in |
Oh, I just didn’t test it with your PR in the compiler repo. Let’s try again. @swift-ci Please test |
Looks like you need to rebase this PR + re-generate the sources to adjust it for #2926. |
fa3596e
to
5c3310a
Compare
Rebased and regenerated, but running into this failure locally:
|
Could you try if deleting |
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
Yes, deleting Examples/.build fixed the issue locally. |
5c3310a
to
e0e888e
Compare
e0e888e
to
fa70433
Compare
Yes, rebased and regenerated! |
This accompanies the compiler implementation for Method and Initializer Keypaths which extends keypath usage to include references to methods and initializers: