Skip to content

Commit ce2e743

Browse files
phpstan-botclaude
andcommitted
Convert bug-14550 test from NSRT to AnalyserIntegrationTest
Since this is a crash bug, an AnalyserIntegrationTest is more appropriate than an NSRT test. The test verifies the analysis completes without crashing on first-class callables in various TypeSpecifier code paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dff735b commit ce2e743

2 files changed

Lines changed: 7 additions & 30 deletions

File tree

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,13 @@ public function testBug14542(): void
15501550
$this->assertNoErrors($errors);
15511551
}
15521552

1553+
public function testBug14550(): void
1554+
{
1555+
// crash
1556+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-14550.php');
1557+
$this->assertNotEmpty($errors);
1558+
}
1559+
15531560
/**
15541561
* @param string[]|null $allAnalysedFiles
15551562
* @return list<Error>
Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,157 +4,127 @@
44

55
namespace Bug14550;
66

7-
use function PHPStan\Testing\assertType;
8-
9-
// Standalone assignments trigger TypeSpecifier via NodeScopeResolver null-context call
107
function testArrayKeyFirstAssign(): void
118
{
129
$fn = array_key_first(...);
13-
assertType('Closure(array): (int|string|null)', $fn);
1410
}
1511

1612
function testArrayKeyLastAssign(): void
1713
{
1814
$fn = array_key_last(...);
19-
assertType('Closure(array): (int|string|null)', $fn);
2015
}
2116

2217
function testArrayRandAssign(): void
2318
{
2419
$fn = array_rand(...);
25-
assertType('(Closure(non-empty-array): (int|string))|(Closure(non-empty-array, int<1, max>): (array<int, int|string>|int|string))', $fn);
2620
}
2721

2822
function testCountMinusOneAssign(): void
2923
{
3024
$idx = count(...) - 1;
31-
assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...));
3225
}
3326

34-
// array_search guard needs true context, so it must be in a condition
3527
function testArraySearchInCondition(): void
3628
{
3729
if ($key = array_search(...)) {
38-
assertType('Closure(mixed, array, bool=): (int|string|false)', $key);
3930
}
4031
}
4132

42-
// Comparison guards in TypeSpecifier (Smaller/SmallerOrEqual)
4333
function testCountInComparisons(): void
4434
{
4535
if (count(...) < 1) {}
4636
if (0 < count(...)) {}
47-
assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...));
4837
}
4938

5039
function testSizeofInComparisons(): void
5140
{
5241
if (sizeof(...) < 1) {}
5342
if (0 < sizeof(...)) {}
54-
assertType('Closure(array|Countable, int=): int', sizeof(...));
5543
}
5644

5745
function testCountMinusOneInComparison(): void
5846
{
5947
$i = 0;
6048
if ($i < count(...) - 1) {}
61-
assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...));
6249
}
6350

6451
function testStrlenInComparisons(): void
6552
{
6653
if (strlen(...) < 1) {}
6754
if (0 < strlen(...)) {}
68-
assertType('Closure(string): int<0, max>', strlen(...));
6955
}
7056

7157
function testMbStrlenInComparisons(): void
7258
{
7359
if (mb_strlen(...) < 1) {}
7460
if (0 < mb_strlen(...)) {}
75-
assertType('Closure(string, string|null=): int<0, max>', mb_strlen(...));
7661
}
7762

7863
function testPregMatchInComparisons(): void
7964
{
8065
if (preg_match(...) < 1) {}
8166
if (0 < preg_match(...)) {}
82-
assertType('Closure(string, string, array<string>|null=, TFlags=, int=): (0|1|false)', preg_match(...));
8367
}
8468

85-
// Identical/NotIdentical guards in resolveNormalizedIdentical
8669
function testCountIdentical(): void
8770
{
8871
if (count(...) === 0) {}
89-
assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...));
9072
}
9173

9274
function testStrlenIdentical(): void
9375
{
9476
if (strlen(...) === 0) {}
9577
if (mb_strlen(...) === 0) {}
96-
assertType('Closure(string): int<0, max>', strlen(...));
9778
}
9879

9980
function testArrayKeyFirstNullComparison(): void
10081
{
10182
if (array_key_first(...) !== null) {}
10283
if (array_key_last(...) !== null) {}
103-
assertType('Closure(array): (int|string|null)', array_key_first(...));
10484
}
10585

10686
function testGetClassIdentical(): void
10787
{
10888
if (get_class(...) === 'stdClass') {}
10989
if (get_debug_type(...) === 'string') {}
110-
assertType('Closure(object=): class-string', get_class(...));
11190
}
11291

11392
function testStringFuncIdentical(): void
11493
{
11594
if (strtolower(...) === 'test') {}
116-
assertType('Closure(string): lowercase-string', strtolower(...));
11795
}
11896

119-
// String equality guards in specifyTypesForConstantStringBinaryExpression
12097
function testGettypeEquality(): void
12198
{
12299
if (gettype(...) === 'string') {}
123100
if (gettype(...) == 'string') {}
124-
assertType('Closure(mixed): string', gettype(...));
125101
}
126102

127103
function testGetClassEquality(): void
128104
{
129105
if (get_class(...) == 'stdClass') {}
130106
if (get_debug_type(...) == 'string') {}
131-
assertType('Closure(object=): class-string', get_class(...));
132107
}
133108

134109
function testGetParentClassEquality(): void
135110
{
136111
if (get_parent_class(...) === 'stdClass') {}
137-
assertType('Closure(object|string=): (class-string|false)', get_parent_class(...));
138112
}
139113

140114
function testTrimEquality(): void
141115
{
142116
if (trim(...) !== '') {}
143117
if (ltrim(...) !== '') {}
144118
if (rtrim(...) !== '') {}
145-
assertType('Closure(string, string=): string', trim(...));
146119
}
147120

148-
// NodeScopeResolver guards
149121
function testArrayKeysInForeach(): void
150122
{
151123
foreach (array_keys(...) as $key) {}
152-
assertType('Closure(array, mixed=, bool=): list<int|string>', array_keys(...));
153124
}
154125

155126
function testCountInForLoop(): void
156127
{
157128
for ($i = 0; $i < count(...); $i++) {}
158129
for ($i = 0; count(...) > $i; $i++) {}
159-
assertType('Closure(array|Countable, 0|1=): int<0, max>', count(...));
160130
}

0 commit comments

Comments
 (0)