-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
PHP 8.0 | Add support for match expressions #3037
Comments
FYI: I've edited the above issue to add a third question about the default case. |
@gsherwood Any thoughts on the above ? |
Initial inventory of affected sniffs:
I have commits ready for all sniffs with a checkmark and will pull those once the initial tokenizer changes have been pulled and merged. Not claiming completeness, there may be more sniffs which need changes, but this should take care of the bulk of it. Edit: Some more sniffs found in the mean time which I have adjusted and/or added tests to related to match structures:
Edit: And yet some more:
|
If it makes life easier for the sniffs, then a new token for I normally don't add new tokens until I start repeating code in the sniffs and you've done a lot of work on them already, so I'm happy to go with whatever you think it most helpful for sniff developers. If you are unsure, leave it as
For the case where an arrow function ends with scope closer, and there is a semicolon directly after that, I think we could safely say that the semicolon ends the scope. It gets a bit more strange when the arrow function is followed by a comma when used as a function argument, but that's already regarded as the scope closer for arrow functions (not technically correct, but close enough) so I think there is clear precedent for what you're suggesting. So agree, the match expression should be the scope owner, and the semicolon/comma/whatever should close the arrow function.
If I was personally working on this one, I'd try and get the scope closer for the Given you're working on this, I'll go with whatever you think is easiest. Although if you think a |
@gsherwood Thanks for responding. I agree that if the scope condition setting, especially in combination with arrow functions, can be sorted properly, we may not need the For the |
Otherwise I'm seeing PHPCS work (as in not fail) with Example code that gives me error: $value = match ($value) {
'' => null,
'false' => false,
'true' => true,
default => $value, # this line is indented 4 spaces too deep according to PHPCS
}; The following code is linted and gives no error: $value = match (true) {
true => 1,
false => 2,
}; |
Finally back to working on the PR to fix this and the longer I'm working on, the more I can only conclude that a separate So for now, I'm moving forward with this by adding the |
Nearly there... got the scope setting working completely, including in combination with arrow functions, which was the hardest part. Only thing left to do is finalize the |
PR #3226 addresses this issue. Once that PR has been merged, I will pull follow-up PRs to address supporting match expressions in individual sniffs. |
Pulled the first round of sniff specific PRs. Will pull a second round after I've had some sleep ;-) |
Thanking you here for all these incoming PRs instead of on each one. I'll also put something in the changelog when they are done as well. |
Sounds like a plan. A few more PRs coming up, aside from the six currently still open. I'm looking at the Given this piece of test/example code: $match = match ($a) {
1, 2, => $a * $b,
default, => 'something'
}; For anything within this @gsherwood Do you agree ? If so, I'll see what I can come up with, but basically, it looks like we'd need to check if this is within a Will probably need to set up something similar then for |
Status: aside from the above notes about the The only two sniffs which are still on the to do list are:
|
Sorry, just got back to this. I absolutely agree - it makes it consistent with how a single case (and default with no trialing comma) works, and how arrow functions are working. I've written some test cases for this but haven't tried your suggested change yet. Will give it a go and see if I can get these tests passing. |
I think that's working fine. All I do is detect that the start token is inside a match expression, and then advance the start token to the match arrow and let it continue from there. |
I'm going to take a look at findStartOfStatement as well, but need to write some tests for it first. |
What about if the start token given is in the "return" part of the match case expression ? |
The code I've got looks backwards for a previous arrow, then forwards from there, looking for a comma. If there was a previous arrow but no comma found, we must be after the arrow. Otherwise, we must be before it, so apply that skip logic. The actual code I've added to findEndOfStatement is: // If the start token is inside the case part of a match expression,
// advance to the match arrow and continue looking for the
// end of the statement from there so that we skip over commas.
$matchExpression = $this->getCondition($start, T_MATCH);
if ($matchExpression !== false) {
$beforeArrow = true;
$prevMatchArrow = $this->findPrevious(T_MATCH_ARROW, ($start - 1), $this->tokens[$matchExpression]['scope_opener']);
if ($prevMatchArrow !== false) {
$prevComma = $this->findNext(T_COMMA, ($prevMatchArrow + 1), $start);
if ($prevComma === false) {
// No comma between this token and the last match arrow,
// so this token exists after the arrow and we can continue
// checking as normal.
$beforeArrow = false;
}
}
if ($beforeArrow === true) {
$nextMatchArrow = $this->findNext(T_MATCH_ARROW, ($start + 1), $this->tokens[$matchExpression]['scope_closer']);
if ($nextMatchArrow !== false) {
$start = $nextMatchArrow;
}
}
}//end if I've got a few test cases that are working but I likely need more before I commit anything. I also haven't tried any alternative methods of getting that logic working. |
Just an update that I've had to make some changes in I'm pretty sure I'll need to change the find prev/next code to skip over parenthesis to support function calls in match arms and conditions properly, but I haven't tested this yet. |
I haven't looked at the code yet (not even sure if it's public yet), but yes, both before the arrow as well as after the arrow, there can be an expression, be it a function call, array declaration or closure declaration to name but a few, so any kind of brackets would need to be jumped over. |
I've committed the tests and changes to support match expressions in findStart/EndOfStatement here: ef80e53 I've got some test cases in there, but very happy for people to provide more. |
PHP 8.0 introduces a new type of control structure: match expressions.
Ref: https://wiki.php.net/rfc/match_expression_v2
I've been working on adding support for these to PHP_CodeSniffer and believe I am nearly finished, but, no surprise, I'm left with
twothree questions for which I'd like a second opinion from @gsherwood and whoever else has an opinion on it.Double arrow handling/tokenizing
As can be seen in the above code sample, match expressions introduce yet another use for the double arrow.
I've ran some tests and code like the below appears to be supported:
While I honestly and truly hope that I will never in my lifetime come across code such as the second sample in a real life codebase, it is valid in PHP 8.0+.
So, that brings me to my question: to prevent issues with array arrow alignment sniffs, should the double arrow when used in match expressions be tokenized differently ? I imagine a
T_MATCH_ARROW
custom token could be used.Mind: if so, code like the below would need to tokenize correctly:
Scope closer sharing with arrow functions
For the above code sample, which would be valid in PHP 8, the scope closer of the arrow function and the scope closer of the match expression would both be the
}
, as things are at the moment.So what should be the
scope_condition
for the close curly ? And what should be in theconditions
array for the code between the match curly braces ?I would personally find it more intuitive for the
match
to be the scope owner when writing sniffs.So, what about if for the arrow function, the
;
after the match expression is seen as the scope closer (if available) ?That would line up with simpler arrow function code, like the below where the
;
is also the scope closer for the arrow function.Default case scope start/end
As the cases in a
match
expression have nobreak
orreturn
statement anddefault
is treated as a scope and the tokenizer tries to assign it scope openers/closers, this becomes interesting.The scope opener is clearly the
T_DOUBLE_ARROW
(orT_MATCH_ARROW
if it would be named separately), the scope closer aT_COMMA
orT_CLOSE_CURLY_BRACKET
(shared withT_MATCH
), where in both cases, we need to make sure that the scope closer doesn't get confused with comma's or curly braces from, for instance, a short array being returned or a nested match expression or other construct which also uses curly braces.This also begs the question whether the
T_DEFAULT
in aT_MATCH
should have its own token to prevent the scope setting for a "normal"switch
default
case getting confused by the different scope openers/closers allowed for theswitch
default
and thematch
default
.Either way, I'd appreciate some input on this so I can finish off the PR.
The text was updated successfully, but these errors were encountered: