44
55use Countable ;
66use PhpParser \Node ;
7+ use PhpParser \Node \Expr \MethodCall ;
78use PhpParser \NodeAbstract ;
89use PHPStan \Analyser \Scope ;
910use PHPStan \Rules \Rule ;
1718class AssertSameWithCountRule implements Rule
1819{
1920
21+ /** @var bool */
22+ private $ bleedingEdge ;
23+ 24+ public function __construct (bool $ bleedingEdge )
25+ {
26+ $ this ->bleedingEdge = $ bleedingEdge ;
27+ }
28+ 2029 public function getNodeType (): string
2130 {
2231 return NodeAbstract::class;
@@ -37,36 +46,89 @@ public function processNode(Node $node, Scope $scope): array
3746
3847 $ right = $ node ->getArgs ()[1 ]->value ;
3948
40- if (
41- $ right instanceof Node \Expr \FuncCall
42- && $ right ->name instanceof Node \Name
43- && $ right ->name ->toLowerString () === 'count '
44- ) {
45- return [
46- RuleErrorBuilder::message ('You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, count($variable)). ' )
47- ->identifier ('phpunit.assertCount ' )
48- ->build (),
49- ];
49+ $ rightIsCountFuncCall = $ this ->isCountFuncCall ($ right );
50+ $ rightIsCountMethodCall = $ this ->isCountMethodCall ($ right ) && $ this ->argIsCountable ($ right , $ scope );
51+ if (!($ rightIsCountFuncCall || $ rightIsCountMethodCall )) {
52+ return [];
5053 }
5154
52- if (
53- $ right instanceof Node \Expr \MethodCall
54- && $ right ->name instanceof Node \Identifier
55- && $ right ->name ->toLowerString () === 'count '
56- && count ($ right ->getArgs ()) === 0
57- ) {
58- $ type = $ scope ->getType ($ right ->var );
55+ $ leftIsCountFuncCall = $ leftIsCountMethodCall = false ;
56+ if ($ this ->bleedingEdge ) {
57+ $ left = $ node ->getArgs ()[0 ]->value ;
58+ $ leftIsCountFuncCall = $ this ->isCountFuncCall ($ left );
59+ $ leftIsCountMethodCall = $ this ->isCountMethodCall ($ left ) && $ this ->argIsCountable ($ left , $ scope );
60+ }
5961
60- if ((new ObjectType (Countable::class))->isSuperTypeOf ($ type )->yes ()) {
62+ if ($ rightIsCountFuncCall ) {
63+ if ($ leftIsCountFuncCall ) {
6164 return [
62- RuleErrorBuilder::message ('You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, $variable->count()). ' )
63- ->identifier ('phpunit.assertCount ' )
65+ RuleErrorBuilder::message ('You should use assertSameSize($expected, $variable) instead of assertSame(count($expected), count($variable)). ' )
66+ ->identifier ('phpunit.assertSameSize ' )
67+ ->build (),
68+ ];
69+ } elseif ($ leftIsCountMethodCall ) {
70+ return [
71+ RuleErrorBuilder::message ('You should use assertSameSize($expected, $variable) instead of assertSame($expected->count(), count($variable)). ' )
72+ ->identifier ('phpunit.assertSameSize ' )
6473 ->build (),
6574 ];
6675 }
76+ 77+ return [
78+ RuleErrorBuilder::message ('You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, count($variable)). ' )
79+ ->identifier ('phpunit.assertCount ' )
80+ ->build (),
81+ ];
82+ }
83+ 84+ if ($ leftIsCountFuncCall ) {
85+ return [
86+ RuleErrorBuilder::message ('You should use assertSameSize($expected, $variable) instead of assertSame(count($expected), $variable->count()). ' )
87+ ->identifier ('phpunit.assertSameSize ' )
88+ ->build (),
89+ ];
90+ } elseif ($ leftIsCountMethodCall ) {
91+ return [
92+ RuleErrorBuilder::message ('You should use assertSameSize($expected, $variable) instead of assertSame($expected->count(), $variable->count()). ' )
93+ ->identifier ('phpunit.assertSameSize ' )
94+ ->build (),
95+ ];
6796 }
6897
69- return [];
98+ return [
99+ RuleErrorBuilder::message ('You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, $variable->count()). ' )
100+ ->identifier ('phpunit.assertCount ' )
101+ ->build (),
102+ ];
103+ }
104+ 105+ /**
106+ * @phpstan-assert-if-true Node\Expr\FuncCall $expr
107+ */
108+ private function isCountFuncCall (Node \Expr $ expr ): bool
109+ {
110+ return $ expr instanceof Node \Expr \FuncCall
111+ && $ expr ->name instanceof Node \Name
112+ && $ expr ->name ->toLowerString () === 'count ' ;
113+ }
114+ 115+ /**
116+ * @phpstan-assert-if-true Node\Expr\MethodCall $expr
117+ */
118+ private function isCountMethodCall (Node \Expr $ expr ): bool
119+ {
120+ return $ expr instanceof Node \Expr \MethodCall
121+ && $ expr ->name instanceof Node \Identifier
122+ && $ expr ->name ->toLowerString () === 'count '
123+ && count ($ expr ->getArgs ()) === 0 ;
124+ }
125+ 126+ private function argIsCountable (MethodCall $ methodCall , Scope $ scope ): bool
127+ {
128+ $ type = $ scope ->getType ($ methodCall ->var );
129+ $ countableType = new ObjectType (Countable::class);
130+ 131+ return $ countableType ->isSuperTypeOf ($ type )->yes ();
70132 }
71133
72134}
0 commit comments