-
Notifications
You must be signed in to change notification settings - Fork 50
Implement DataProviderDataRule #238
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
base: 2.0.x
Are you sure you want to change the base?
Conversation
Still work in progress. Will separate into more PRs after its mostly done |
4402d12
to
1453c20
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.
Also I pushed a bunch of failing tests 😊
} | ||
|
||
/** | ||
* @return array<ReflectionMethod> |
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 is a weird type. I'd prefer our own ExtendedMethodReflection to be returned. You can get ClassReflection::getNativeMethod()
once you make sure this is a test method from the current $reflectionMethod
(to save time from instantiating all methods which is expensive).
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 noticed this because I was looking for how you're handling variadic methods and the $testMethod->getNumberOfParameters()
method call seemed unfamiliar to me.
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.
from ExtendedMethodReflection
I cannot getStartLine
, which I need in DataProviderHelper
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.
You mean there isn't getStartLine
on AttributeReflection? I don't see getStartLine
being called on ReflectionMethod
.
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.
see
$startLine = $node->getStartLine(); |
} | ||
|
||
$maxNumberOfParameters = 0; | ||
$trimArgs = count($testsWithProvider) > 1; |
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 in correct implementation, we don't need this condition at all. A foreach over methods that just iterates once should be enough to set everything correctly.
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.
What about code like
#[DataProvider('aProvider')]
public function testTrim(string $expectedResult, string $input): void
{
}
/** @return array<array{string, string|int|false}> */
public function aProvider(): array
{
return [
[
'Hello World',
" Hello World \n",
],
[
'Hello World',
123,
],
[
'Hello World',
false,
],
];
}
@staabm ?
I would expect:
- To not parse the implementation of the provider because I added a return type (which improves performance of the rule).
/** @return array<array{string, string|int|false}> */
to be reported because it's not compatible with the testTrim signature.
This way:
- I fix the phpdoc to
/** @return array<array{string, string}> */
- And then, the
123
andfalse
will be reported because they don't respect the@return
type.
I think with the new rule, you no longer need a return type on the provider (which is now redundant). having to define the signature at the test and another time at the provider feels like unnecessary work IMO at best we would stop reporting "return type has no value type specified in iterable type array." for data-providers which can be analyzed by this PR. |
I feel like it's a really opinionated decision, and might conflict with some other static analysis tool.
Couldn't the DataProviderDataRule either
This rule will Cause some code like
Is not reported by the rule currently, but technically wrong (and reported by Psalm) |
@VincentLanglet I disagree with you but I'm on a phone and might write a proper response latee. |
$method = $scope->getFunction(); | ||
$classReflection = $scope->getClassReflection(); | ||
if ( | ||
$classReflection === null | ||
|| !$classReflection->is(TestCase::class) | ||
) { | ||
return []; | ||
} |
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.
Shouldn't this condition be done before buildArrayTypesFromNode to be faster ?
We might also prefer to check
if (count($testsWithProvider) === 0) {
return [];
}
before the buildArrayTypesFromNode
call, it will avoid to inspect methods which are not dataprovider and improves performances.
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 am not sure whether AST traversing + type retrieval is faster or slower then runtime reflection.
do you have a test at hand which to prove your thesis about what is faster?
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 am not sure whether AST traversing + type retrieval is faster or slower then runtime reflection.
do you have a cast at hand which to prove your thesis about what is faster?
Not at all, it might be a wrong intuition.
I thought type manipulation was something slow, with possible union/intersection etc.
$testsWithProvider = []; | ||
$testMethods = $this->testMethodsHelper->getTestMethods($classReflection, $scope); | ||
foreach ($testMethods as $testMethod) { | ||
foreach ($this->dataProviderHelper->getDataProviderMethods($scope, $testMethod, $classReflection) as [, $providerMethodName]) { |
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.
Do you think it would be worth having a cache on getDataProviderMethods
(and maybe getTestMethods ?) ?
Cause we will compute this for every possible return of a TestCase class, and the result will always be the same
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 am fine/willing to add a cache in case we find a case where it turns into a measurable improvement
@VincentLanglet To reply to your earlier comment about this code: #[DataProvider('aProvider')]
public function testTrim(string $expectedResult, string $input): void
{
}
/** @return array<array{string, string|int}> */
public function aProvider(): array
{
return [
[
'Hello World',
" Hello World \n",
],
];
} Currently this code will be reported with return.nestedUnusedType (https://phpstan.org/r/3b73fe7f-361e-4f8e-a752-0cf8827db30e). Provided it's in a final class or you turn on |
fixed, thank you 🙏 |
I played as Infection for a bit and found some code that could be removed from the algorithm 😊 I still think there's something fishy about it but I didn't prove it yet. In my gut I think we should be interesting in the min number of parameters when deciding how to trim, but maybe I'm wrong, or maybe I will find another failing test :) We'll see. Thank you so far! |
thats what you deleted with f083e63 ...? :) |
understood. finished the infection package, so we can setup it :) |
Proof of concept
leads to
Closes #70
utilizes phpstan/phpstan-src#4429 phpstan/phpstan-src#4438
Scope
Todos
@test
#[Test]
@dataProvider
#[DataProvider]
static
data-providerstatic
data-providerreturn []
data-providersyield []
data-providersyield from []
data-providersCompositionRule
into phpstan-src