Skip to content

[Php70] add a rector to pass only variables as arguments by reference#2546

Merged
TomasVotruba merged 1 commit intorectorphp:masterfrom
Lctrs:argument_by_reference
Jan 11, 2020
Merged

[Php70] add a rector to pass only variables as arguments by reference#2546
TomasVotruba merged 1 commit intorectorphp:masterfrom
Lctrs:argument_by_reference

Conversation

@Lctrs
Copy link
Copy Markdown
Contributor

@Lctrs Lctrs commented Jan 3, 2020

Still WIP.

Creating it to gather first reviews.

@TomasVotruba
Copy link
Copy Markdown
Member

Hi, is this ready for review or do you plan more work first?

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 4, 2020

I'm planning to handle a few more cases and simplify code a little bit, but main structure/flow is here.

Copy link
Copy Markdown
Member

@TomasVotruba TomasVotruba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job with covergin with test 👍
I've left few mostly readability comments.

Have you use bin/rector create command?

Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 4, 2020

Yes I have used the create command.

@Lctrs Lctrs marked this pull request as ready for review January 6, 2020 16:14
@Lctrs Lctrs changed the title WIP: [Php70] add a rector to pass only variables as arguments by reference [Php70] add a rector to pass only variables as arguments by reference Jan 6, 2020
@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 6, 2020

@TomasVotruba Ready for review. Build failure is unrelated I guess.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 6, 2020

@TomasVotruba Finally still WIP. Tried it on a real project. Got errors.

@TomasVotruba
Copy link
Copy Markdown
Member

No troubles. Just ping me when you need me

@TomasVotruba
Copy link
Copy Markdown
Member

CI should pass completely now. Just fixed the master

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 6, 2020

@TomasVotruba Any idea what leaveNode() may only return an array if the parent structure is an array means ?

@TomasVotruba
Copy link
Copy Markdown
Member

That happens when you get node, but return an array of nodes.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 6, 2020

How/Where can this happen ? I don't return an array in my rector.

@TomasVotruba
Copy link
Copy Markdown
Member

There can be like dozens of cases where this can happen.

I'd have to see the test case.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 7, 2020

@TomasVotruba It was caused by a return statement. When I add something like return reset(bar()); for example, the node was inserted between the return and the reset, hence the error. I handled this case on line 100-101 in my rector, but maybe this can be generalized ?

Otherwise, it's finally ready for review. It worked well in one of my project. I tried to be as extensive as possible on test.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 7, 2020

Only the PHAR compilation fails ATM. Will take a look at it.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 7, 2020

@TomasVotruba All good 🎉

Copy link
Copy Markdown
Member

@TomasVotruba TomasVotruba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for a great job!

I've added few notes. Mostly about decopuling Reflection from Rector

Comment thread packages/Php70/src/Dto/VariableAssignPair.php Outdated
Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
}
}

private function processArgument(Expr $expr, Scope $scope): VariableAssignPair
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like VariableAssignPairFactory.

process/refactor method should change nodes in some way

Comment on lines +240 to +177
private function createCountedValueName(?string $valueName, Scope $scope): string
{
$countedValueName = $valueName ?? self::DEFAULT_VARIABLE_NAME;

// make sure variable name is unique
if (! $scope->hasVariableType($countedValueName)->yes()) {
return $countedValueName;
}

// we need to add number suffix until the variable is unique
$i = 2;
$countedValueNamePart = $countedValueName;
while ($scope->hasVariableType($countedValueName)->yes()) {
$countedValueName = $countedValueNamePart . $i;
++$i;
}

return $countedValueName;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be great if abstracted in AbstractRector, as this logic is used in multiple places now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomasVotruba How would you handle things like suffix ? (ForRepeatedCountToOwnVariableRector uses a suffix)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? Suffix of what?

I'd just move this to protected method to abstract class, so it can be re-used in multiple Rector rules.

Comment on lines +9 to +21

?>
-----
<?php

namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;

function propertyFetch()
{
reset((new \stdClass())->dummy);
}

?>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed, as nothing changes

Comment thread phpstan.neon Outdated
public function refactor(Node $node): ?Node
{
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof MutatingScope) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scope is good enough as interface

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scope doesn't contains the method assignExpression sadly. See (upcoming) explanation below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. I'm affraid that this will go in mess like "which one to use when", since instanceof Scope is used everywhere now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will see if I can do something with the ScopeFactory instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But a simple rule could be if you need to mutate scope, go with MutatingScope, otherwise use Scope.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might, but classes with more public method than interfaces they implement always create crappy code. It's PHPStan design flaw we cannot cover here I'm affraid.

Let's keep it your way for now, not that important.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, MutatingScope seems to be the only available implementation of Scope.

$this->addNodeBeforeNode($replacements->assign(), $current instanceof Return_ ? $current : $node);
$node->args[$key]->value = $replacements->variable();

$scope = $scope->assignExpression($replacements->variable(), $scope->getType($replacements->variable()));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never used this before. What is it for? When should we use it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns a new Scope from the previous Scope + the assigned expression (for example, if you pass a variable to it, the new scope will be aware of this variable).

I did this so that Scope is aware of newly created variables, so that createCountedValueName() returns a truly unique variable name.

Copy link
Copy Markdown
Member

@TomasVotruba TomasVotruba Jan 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks!

Maybe it would be more consistent to add type here:

/** @var MutatingScope $scope */
$scope = $scope->assignExpression($replacements->variable(), $scope->getType($replacements->variable()));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assignExpression() always returns a MutatingScope. No need to add a phpDoc IMHO.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant it to have instanceof Scope above and here make the annotation for variable on the right:

/** @var MutatingScope $scope */
$whatever = **$scope**->assignExpression(...);

Comment thread packages/Php70/src/Rector/FuncCall/NonVariableToVariableOnFunctionCallRector.php Outdated
@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 7, 2020

@TomasVotruba New version with most of your comments adressed. Only things missing is extracting createCountedValueName() to AbstractRector. I don't know yet how to handle properly things such as suffix, etc. If you have any idea...

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 8, 2020

@TomasVotruba Should be good.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 9, 2020

@TomasVotruba FYI, I have a working version locally that uses the core reflection API (well, and also roave/better-reflection for one case). So no more Broker use. Just need to split it into more functions, Should be up tomorrow.

@TomasVotruba
Copy link
Copy Markdown
Member

All rigth. Maybe look at PHPStan static reflection there is since 0.12.4

Ping me when ready for review

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 9, 2020

Should I understand that you prefer to use the Reflection API from PHPStan ?

@TomasVotruba
Copy link
Copy Markdown
Member

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 9, 2020

Yeah but if we use a reflection provider from PHPStan, we'll get objects implementing the reflection API from PHPStan, not from the core. Or maybe I'm missing something ?

@TomasVotruba
Copy link
Copy Markdown
Member

It's just for inspiration, I don't personally use it

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 10, 2020

@TomasVotruba WDYT ? Yeah I know that roave/better-reflection is required as dev-master, but due to version conflict, I had no other choice.

@TomasVotruba
Copy link
Copy Markdown
Member

TomasVotruba commented Jan 10, 2020

Yeah I know that roave/better-reflection is required as dev-master, but due to version conflict, I had no other choice.

That would disable composer require rector/rector, not possible.

I think we should first finish the rule, then add more dependencies. This PR is already mixing many various problems. One step at a time.

@Lctrs
Copy link
Copy Markdown
Contributor Author

Lctrs commented Jan 10, 2020

Okay, I reverted my last commit for now, I think I covered all your remarks. Maybe I'm missing something ?

@TomasVotruba
Copy link
Copy Markdown
Member

I'm looking at it

@TomasVotruba TomasVotruba self-requested a review January 11, 2020 10:07
@TomasVotruba
Copy link
Copy Markdown
Member

TomasVotruba commented Jan 11, 2020

All looks good! It's like reading a well written story.

Thanks for this aweseome feature. I think you're the most skilled first contritbutor Rector ever had 🙇‍♂️

@TomasVotruba TomasVotruba merged commit 2e83fe7 into rectorphp:master Jan 11, 2020
@Lctrs Lctrs deleted the argument_by_reference branch January 11, 2020 12:16
TomasVotruba added a commit that referenced this pull request Jun 21, 2022
rectorphp/rector-src@dd4064e cleanup php-version bound tests, now under one version no need to separate (#2546)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants