|
6 | 6 |
|
7 | 7 | use function PHPStan\Testing\assertType; |
8 | 8 |
|
9 | | -/** |
10 | | - * @param list<string> $list |
11 | | - */ |
12 | | -function crashArrayKeyFirst(array $list): void |
| 9 | +// Standalone assignments trigger TypeSpecifier via NodeScopeResolver null-context call |
| 10 | +function testArrayKeyFirstAssign(): void |
13 | 11 | { |
14 | 12 | $fn = array_key_first(...); |
15 | 13 | assertType('Closure(array): (int|string|null)', $fn); |
16 | 14 | } |
17 | 15 |
|
18 | | -/** |
19 | | - * @param list<string> $list |
20 | | - */ |
21 | | -function crashArrayKeyLast(array $list): void |
| 16 | +function testArrayKeyLastAssign(): void |
22 | 17 | { |
23 | 18 | $fn = array_key_last(...); |
24 | 19 | assertType('Closure(array): (int|string|null)', $fn); |
25 | 20 | } |
26 | 21 |
|
27 | | -/** |
28 | | - * @param list<string> $list |
29 | | - */ |
30 | | -function crashArrayRand(array $list): void |
| 22 | +function testArrayRandAssign(): void |
31 | 23 | { |
32 | 24 | $fn = array_rand(...); |
33 | 25 | assertType('(Closure(non-empty-array): (int|string))|(Closure(non-empty-array, int<1, max>): (array<int, int|string>|int|string))', $fn); |
34 | 26 | } |
35 | 27 |
|
36 | | -/** |
37 | | - * @param list<string> $list |
38 | | - */ |
39 | | -function crashArraySearch(array $list, string $s): void |
| 28 | +function testCountMinusOneAssign(): void |
40 | 29 | { |
41 | | - $fn = array_search(...); |
42 | | - assertType('Closure(mixed, array, bool=): (int|string|false)', $fn); |
| 30 | + $idx = count(...) - 1; |
| 31 | + assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...)); |
43 | 32 | } |
44 | 33 |
|
45 | | -function testStrlen(): void |
| 34 | +// array_search guard needs true context, so it must be in a condition |
| 35 | +function testArraySearchInCondition(): void |
46 | 36 | { |
47 | | - $fn = strlen(...); |
48 | | - assertType('Closure(string): int<0, max>', $fn); |
| 37 | + if ($key = array_search(...)) { |
| 38 | + assertType('Closure(mixed, array, bool=): (int|string|false)', $key); |
| 39 | + } |
49 | 40 | } |
50 | 41 |
|
51 | | -function testMbStrlen(): void |
| 42 | +// Comparison guards in TypeSpecifier (Smaller/SmallerOrEqual) |
| 43 | +function testCountInComparisons(): void |
52 | 44 | { |
53 | | - $fn = mb_strlen(...); |
54 | | - assertType('Closure(string, string|null=): int<0, max>', $fn); |
| 45 | + if (count(...) < 1) {} |
| 46 | + if (0 < count(...)) {} |
| 47 | + assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...)); |
55 | 48 | } |
56 | 49 |
|
57 | | -function testCount(): void |
| 50 | +function testSizeofInComparisons(): void |
58 | 51 | { |
59 | | - $fn = count(...); |
60 | | - assertType('Closure(array|Countable, 0|1=): int<0, max>', $fn); |
| 52 | + if (sizeof(...) < 1) {} |
| 53 | + if (0 < sizeof(...)) {} |
| 54 | + assertType('Closure(array|Countable, int=): int', sizeof(...)); |
61 | 55 | } |
62 | 56 |
|
63 | | -function testSizeof(): void |
| 57 | +function testCountMinusOneInComparison(): void |
64 | 58 | { |
65 | | - $fn = sizeof(...); |
66 | | - assertType('Closure(array|Countable, int=): int', $fn); |
| 59 | + $i = 0; |
| 60 | + if ($i < count(...) - 1) {} |
| 61 | + assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...)); |
67 | 62 | } |
68 | 63 |
|
69 | | -function testPregMatch(): void |
| 64 | +function testStrlenInComparisons(): void |
70 | 65 | { |
71 | | - $fn = preg_match(...); |
72 | | - assertType('Closure(string, string, array<string>|null=, TFlags=, int=): (0|1|false)', $fn); |
| 66 | + if (strlen(...) < 1) {} |
| 67 | + if (0 < strlen(...)) {} |
| 68 | + assertType('Closure(string): int<0, max>', strlen(...)); |
73 | 69 | } |
74 | 70 |
|
75 | | -function testGettype(): void |
| 71 | +function testMbStrlenInComparisons(): void |
76 | 72 | { |
77 | | - $fn = gettype(...); |
78 | | - assertType('Closure(mixed): string', $fn); |
| 73 | + if (mb_strlen(...) < 1) {} |
| 74 | + if (0 < mb_strlen(...)) {} |
| 75 | + assertType('Closure(string, string|null=): int<0, max>', mb_strlen(...)); |
79 | 76 | } |
80 | 77 |
|
81 | | -function testGetClass(): void |
| 78 | +function testPregMatchInComparisons(): void |
82 | 79 | { |
83 | | - $fn = get_class(...); |
84 | | - assertType('Closure(object=): class-string', $fn); |
| 80 | + if (preg_match(...) < 1) {} |
| 81 | + if (0 < preg_match(...)) {} |
| 82 | + assertType('Closure(string, string, array<string>|null=, TFlags=, int=): (0|1|false)', preg_match(...)); |
85 | 83 | } |
86 | 84 |
|
87 | | -function testGetDebugType(): void |
| 85 | +// Identical/NotIdentical guards in resolveNormalizedIdentical |
| 86 | +function testCountIdentical(): void |
88 | 87 | { |
89 | | - $fn = get_debug_type(...); |
90 | | - assertType('Closure(mixed): string', $fn); |
| 88 | + if (count(...) === 0) {} |
| 89 | + assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...)); |
91 | 90 | } |
92 | 91 |
|
93 | | -function testGetParentClass(): void |
| 92 | +function testStrlenIdentical(): void |
94 | 93 | { |
95 | | - $fn = get_parent_class(...); |
96 | | - assertType('Closure(object|string=): (class-string|false)', $fn); |
| 94 | + if (strlen(...) === 0) {} |
| 95 | + if (mb_strlen(...) === 0) {} |
| 96 | + assertType('Closure(string): int<0, max>', strlen(...)); |
97 | 97 | } |
98 | 98 |
|
99 | | -function testTrim(): void |
| 99 | +function testArrayKeyFirstNullComparison(): void |
100 | 100 | { |
101 | | - $fn = trim(...); |
102 | | - assertType('Closure(string, string=): string', $fn); |
| 101 | + if (array_key_first(...) !== null) {} |
| 102 | + if (array_key_last(...) !== null) {} |
| 103 | + assertType('Closure(array): (int|string|null)', array_key_first(...)); |
103 | 104 | } |
104 | 105 |
|
105 | | -function testLtrim(): void |
| 106 | +function testGetClassIdentical(): void |
106 | 107 | { |
107 | | - $fn = ltrim(...); |
108 | | - assertType('Closure(string, string=): string', $fn); |
| 108 | + if (get_class(...) === 'stdClass') {} |
| 109 | + if (get_debug_type(...) === 'string') {} |
| 110 | + assertType('Closure(object=): class-string', get_class(...)); |
109 | 111 | } |
110 | 112 |
|
111 | | -function testRtrim(): void |
| 113 | +function testStringFuncIdentical(): void |
112 | 114 | { |
113 | | - $fn = rtrim(...); |
114 | | - assertType('Closure(string, string=): string', $fn); |
| 115 | + if (strtolower(...) === 'test') {} |
| 116 | + assertType('Closure(string): lowercase-string', strtolower(...)); |
115 | 117 | } |
116 | 118 |
|
117 | | -/** |
118 | | - * @param list<string> $list |
119 | | - */ |
120 | | -function testArrayKeysInForeach(array $list): void |
| 119 | +// String equality guards in specifyTypesForConstantStringBinaryExpression |
| 120 | +function testGettypeEquality(): void |
121 | 121 | { |
122 | | - foreach (array_keys(...) as $key) { |
123 | | - } |
124 | | - $fn = array_keys(...); |
125 | | - assertType('Closure(array, mixed=, bool=): list<int|string>', $fn); |
| 122 | + if (gettype(...) === 'string') {} |
| 123 | + if (gettype(...) == 'string') {} |
| 124 | + assertType('Closure(mixed): string', gettype(...)); |
| 125 | +} |
| 126 | + |
| 127 | +function testGetClassEquality(): void |
| 128 | +{ |
| 129 | + if (get_class(...) == 'stdClass') {} |
| 130 | + if (get_debug_type(...) == 'string') {} |
| 131 | + assertType('Closure(object=): class-string', get_class(...)); |
| 132 | +} |
| 133 | + |
| 134 | +function testGetParentClassEquality(): void |
| 135 | +{ |
| 136 | + if (get_parent_class(...) === 'stdClass') {} |
| 137 | + assertType('Closure(object|string=): (class-string|false)', get_parent_class(...)); |
| 138 | +} |
| 139 | + |
| 140 | +function testTrimEquality(): void |
| 141 | +{ |
| 142 | + if (trim(...) !== '') {} |
| 143 | + if (ltrim(...) !== '') {} |
| 144 | + if (rtrim(...) !== '') {} |
| 145 | + assertType('Closure(string, string=): string', trim(...)); |
| 146 | +} |
| 147 | + |
| 148 | +// NodeScopeResolver guards |
| 149 | +function testArrayKeysInForeach(): void |
| 150 | +{ |
| 151 | + foreach (array_keys(...) as $key) {} |
| 152 | + assertType('Closure(array, mixed=, bool=): list<int|string>', array_keys(...)); |
| 153 | +} |
| 154 | + |
| 155 | +function testCountInForLoop(): void |
| 156 | +{ |
| 157 | + for ($i = 0; $i < count(...); $i++) {} |
| 158 | + for ($i = 0; count(...) > $i; $i++) {} |
| 159 | + assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...)); |
126 | 160 | } |
0 commit comments