Skip to content

Commit 59c3eaa

Browse files
authored
Incremented numeric-string should change to int/float
1 parent 23fa0f8 commit 59c3eaa

File tree

8 files changed

+98
-12
lines changed

8 files changed

+98
-12
lines changed

phpstan-baseline.neon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,8 +1528,8 @@ parameters:
15281528
path: src/Type/Php/SscanfFunctionDynamicReturnTypeExtension.php
15291529

15301530
-
1531-
message: "#^Casting to string something that's already string\\.$#"
1532-
count: 1
1531+
message: "#^Cannot access offset int\\<0, max\\> on \\(float\\|int\\)\\.$#"
1532+
count: 2
15331533
path: src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php
15341534

15351535
-

resources/functionMap_php80delta.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
'str_ends_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
103103
'str_starts_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'],
104104
'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'],
105-
'stripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
105+
'stripos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
106106
'stristr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'],
107107
'strpos' => ['positive-int|0|false', 'haystack'=>'string', 'needle'=>'string', 'offset='=>'int'],
108108
'strrchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string'],
@@ -241,7 +241,7 @@
241241
'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['?string|?false', 'confidential_message'=>'string', 'public_message'=>'string', 'nonce'=>'string', 'key'=>'string'],
242242
'SplFileObject::fgetss' => ['string|false', 'allowable_tags='=>'string'],
243243
'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string|int', 'before_needle='=>'bool'],
244-
'stripos' => ['int|false', 'haystack'=>'string', 'needle'=>'string|int', 'offset='=>'int'],
244+
'stripos' => ['0|positive-int|false', 'haystack'=>'string', 'needle'=>'string|int', 'offset='=>'int'],
245245
'stristr' => ['string|false', 'haystack'=>'string', 'needle'=>'string|int', 'before_needle='=>'bool'],
246246
'strpos' => ['int|false', 'haystack'=>'string', 'needle'=>'string|int', 'offset='=>'int'],
247247
'strrchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string|int'],

src/Analyser/MutatingScope.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
use PHPStan\Type\DynamicReturnTypeExtensionRegistry;
9292
use PHPStan\Type\ErrorType;
9393
use PHPStan\Type\ExpressionTypeResolverExtensionRegistry;
94+
use PHPStan\Type\FloatType;
9495
use PHPStan\Type\GeneralizePrecision;
9596
use PHPStan\Type\Generic\GenericClassStringType;
9697
use PHPStan\Type\Generic\GenericObjectType;
@@ -1461,7 +1462,7 @@ private function resolveType(string $exprString, Expr $node): Type
14611462
} elseif ($node instanceof Expr\PreInc || $node instanceof Expr\PreDec) {
14621463
$varType = $this->getType($node->var);
14631464
$varScalars = $varType->getConstantScalarValues();
1464-
$stringType = new StringType();
1465+
14651466
if (count($varScalars) > 0) {
14661467
$newTypes = [];
14671468

@@ -1477,9 +1478,24 @@ private function resolveType(string $exprString, Expr $node): Type
14771478
return TypeCombinator::union(...$newTypes);
14781479
} elseif ($varType->isString()->yes()) {
14791480
if ($varType->isLiteralString()->yes()) {
1480-
return new IntersectionType([$stringType, new AccessoryLiteralStringType()]);
1481+
return new IntersectionType([
1482+
new StringType(),
1483+
new AccessoryLiteralStringType(),
1484+
]);
1485+
}
1486+
1487+
if ($varType->isNumericString()->yes()) {
1488+
return new BenevolentUnionType([
1489+
new IntegerType(),
1490+
new FloatType(),
1491+
]);
14811492
}
1482-
return $stringType;
1493+
1494+
return new BenevolentUnionType([
1495+
new StringType(),
1496+
new IntegerType(),
1497+
new FloatType(),
1498+
]);
14831499
}
14841500

14851501
if ($node instanceof Expr\PreInc) {

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,19 +2956,19 @@ public function dataBinaryOperations(): array
29562956
'$string--',
29572957
],
29582958
[
2959-
'string',
2959+
'(float|int|string)',
29602960
'++$string',
29612961
],
29622962
[
2963-
'string',
2963+
'(float|int|string)',
29642964
'--$string',
29652965
],
29662966
[
2967-
'string',
2967+
'(float|int|string)',
29682968
'$incrementedString',
29692969
],
29702970
[
2971-
'string',
2971+
'(float|int|string)',
29722972
'$decrementedString',
29732973
],
29742974
[

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,7 @@ public function dataFileAsserts(): iterable
14471447
yield from $this->gatherAssertTypes(__DIR__ . '/data/sort.php');
14481448
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3312.php');
14491449
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5961.php');
1450+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10122.php');
14501451
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10189.php');
14511452
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10317.php');
14521453
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10302-interface-extends.php');
@@ -1459,6 +1460,7 @@ public function dataFileAsserts(): iterable
14591460
yield from $this->gatherAssertTypes(__DIR__ . '/data/set-type-type-specifying.php');
14601461
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10468.php');
14611462
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6613.php');
1463+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10187.php');
14621464
}
14631465

14641466
/**
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Bug10122;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFoo():void
8+
{
9+
function(string $s) {
10+
assertType('(float|int|string)', ++$s);
11+
};
12+
function(string $s) {
13+
assertType('(float|int|string)', --$s);
14+
};
15+
function(string $s) {
16+
assertType('string', $s++);
17+
assertType('(float|int|string)', $s);
18+
};
19+
function(string $s) {
20+
assertType('string', $s--);
21+
assertType('(float|int|string)', $s);
22+
};
23+
24+
function(float $f) {
25+
assertType('float', ++$f);
26+
};
27+
function(float $f) {
28+
assertType('float', --$f);
29+
};
30+
function(float $f) {
31+
assertType('float', $f++);
32+
};
33+
function(float $f) {
34+
assertType('float', $f--);
35+
};
36+
}
37+
38+
/** @param numeric-string $ns */
39+
function doNumericString(string $ns) {
40+
function() use ($ns) {
41+
assertType('(float|int)', ++$ns);
42+
};
43+
function() use ($ns) {
44+
assertType('(float|int)', --$ns);
45+
};
46+
function() use ($ns) {
47+
assertType('numeric-string', $ns++);
48+
assertType('(float|int)', $ns);
49+
};
50+
function() use ($ns) {
51+
assertType('numeric-string', $ns--);
52+
assertType('(float|int)', $ns);
53+
};
54+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug10187;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function inc(string $n): string
8+
{
9+
$before = $n;
10+
$after = ++$n;
11+
assertType('array{n: (float|int|string), before: string, after: (float|int|string)}', compact('n', 'before', 'after'));
12+
13+
return (string)$after; // No warnings expected here
14+
}

tests/PHPStan/Analyser/data/literal-string.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function increment($literalString, string $string)
5757
assertType('literal-string', $literalString);
5858

5959
$string++;
60-
assertType('string', $string);
60+
assertType('(float|int|string)', $string);
6161
}
6262

6363
}

0 commit comments

Comments
 (0)