Skip to content

Commit 24224fb

Browse files
authored
Merge branch refs/heads/1.12.x into 2.0.x
2 parents 124705a + 7d49f52 commit 24224fb

6 files changed

+75
-13
lines changed

src/Type/IntegerRangeType.php

+5
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ public function toAbsoluteNumber(): Type
465465

466466
public function toString(): Type
467467
{
468+
$finiteTypes = $this->getFiniteTypes();
469+
if ($finiteTypes !== []) {
470+
return TypeCombinator::union(...$finiteTypes)->toString();
471+
}
472+
468473
$isZero = (new ConstantIntegerType(0))->isSuperTypeOf($this);
469474
if ($isZero->no()) {
470475
return new IntersectionType([

src/Type/Php/SprintfFunctionDynamicReturnTypeExtension.php

+21-1
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,29 @@ public function getTypeFromFunctionCall(
105105
$checkArgType = $scope->getType($args[$checkArg]->value);
106106
if (
107107
$matches['specifier'] === 's'
108-
&& ($checkArgType->isConstantValue()->no() || $matches['width'] === '')
109108
&& ($checkArgType->isString()->yes() || $checkArgType->isInteger()->yes())
110109
) {
110+
if ($checkArgType instanceof IntegerRangeType) {
111+
$constArgTypes = $checkArgType->getFiniteTypes();
112+
} else {
113+
$constArgTypes = $checkArgType->getConstantScalarTypes();
114+
}
115+
if ($constArgTypes !== []) {
116+
$result = [];
117+
$printfArgs = array_fill(0, count($args) - 1, '');
118+
foreach ($constArgTypes as $constArgType) {
119+
$printfArgs[$checkArg - 1] = $constArgType->getValue();
120+
try {
121+
$result[] = new ConstantStringType(@sprintf($constantString->getValue(), ...$printfArgs));
122+
} catch (Throwable) {
123+
continue 2;
124+
}
125+
}
126+
$singlePlaceholderEarlyReturn = TypeCombinator::union(...$result);
127+
128+
continue;
129+
}
130+
111131
$singlePlaceholderEarlyReturn = $checkArgType->toString();
112132
} elseif ($matches['specifier'] !== 's') {
113133
$singlePlaceholderEarlyReturn = new IntersectionType([

tests/PHPStan/Analyser/nsrt/bug-7387.php

+25-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
class HelloWorld
88
{
9-
public function inputTypes(int $i, float $f, string $s) {
9+
/**
10+
* @param int<-1, 5> $intRange
11+
*/
12+
public function inputTypes(int $i, float $f, string $s, int $intRange) {
1013
// https://3v4l.org/iXaDX
1114
assertType('numeric-string', sprintf('%.14F', $i));
1215
assertType('numeric-string', sprintf('%.14F', $f));
@@ -19,6 +22,9 @@ public function inputTypes(int $i, float $f, string $s) {
1922
assertType('numeric-string', sprintf('%14F', $i));
2023
assertType('numeric-string', sprintf('%14F', $f));
2124
assertType('numeric-string', sprintf('%14F', $s));
25+
26+
assertType("'-1'|'0'|'1'|'2'|'3'|'4'|'5'", sprintf('%s', $intRange));
27+
assertType("' 0'|' 1'|' 2'|' 3'|' 4'|' 5'|'-1'", sprintf('%2s', $intRange));
2228
}
2329

2430
public function specifiers(int $i) {
@@ -53,18 +59,27 @@ public function specifiers(int $i) {
5359
*/
5460
public function positionalArgs($mixed, int $i, float $f, string $s, int $posInt, int $negInt, int $nonZeroIntRange, int $intRange) {
5561
// https://3v4l.org/vVL0c
56-
assertType('numeric-string', sprintf('%2$14s', $mixed, $i));
57-
assertType('non-falsy-string&numeric-string', sprintf('%2$14s', $mixed, $posInt));
58-
assertType('non-falsy-string&numeric-string', sprintf('%2$14s', $mixed, $negInt));
59-
assertType('numeric-string', sprintf('%2$14s', $mixed, $intRange));
60-
assertType('non-falsy-string&numeric-string', sprintf('%2$14s', $mixed, $nonZeroIntRange));
61-
62-
assertType("non-falsy-string", sprintf('%2$14s', $mixed, 1));
63-
assertType("non-falsy-string", sprintf('%2$14s', $mixed, '1'));
64-
assertType("non-falsy-string", sprintf('%2$14s', $mixed, 'abc'));
62+
assertType('numeric-string', sprintf('%2$6s', $mixed, $i));
63+
assertType('non-falsy-string&numeric-string', sprintf('%2$6s', $mixed, $posInt));
64+
assertType('non-falsy-string&numeric-string', sprintf('%2$6s', $mixed, $negInt));
65+
assertType("' 1'|' 2'|' 3'|' 4'|' 5'", sprintf('%2$6s', $mixed, $nonZeroIntRange));
66+
67+
// https://3v4l.org/1ECIq
68+
assertType('non-falsy-string', sprintf("%2$'#6s", $mixed, 1));
69+
assertType('non-falsy-string', sprintf("%2$'#6s", $mixed, $i));
70+
assertType('non-falsy-string', sprintf("%2$'#6s", $mixed, $posInt));
71+
assertType('non-falsy-string', sprintf("%2$'#6s", $mixed, $negInt));
72+
assertType("' 0'|' 1'|' 2'|' 3'|' 4'|' 5'|' -1'", sprintf('%2$6s', $mixed, $intRange));
73+
assertType('non-falsy-string', sprintf("%2$'#6s", $mixed, $nonZeroIntRange));
74+
75+
assertType("' 1'", sprintf('%2$6s', $mixed, 1));
76+
assertType("' 1'", sprintf('%2$6s', $mixed, '1'));
77+
assertType("' abc'", sprintf('%2$6s', $mixed, 'abc'));
78+
assertType("' 0'|' 1'|' 2'|' 3'|' 4'|' 5'|' -1'", sprintf('%2$6s', $mixed, $intRange));
6579
assertType("'1'", sprintf('%2$s', $mixed, 1));
6680
assertType("'1'", sprintf('%2$s', $mixed, '1'));
6781
assertType("'abc'", sprintf('%2$s', $mixed, 'abc'));
82+
assertType("'-1'|'0'|'1'|'2'|'3'|'4'|'5'", sprintf('%2$s', $mixed, $intRange));
6883

6984
assertType('numeric-string', sprintf('%2$.14F', $mixed, $i));
7085
assertType('numeric-string', sprintf('%2$.14F', $mixed, $f));

tests/PHPStan/Analyser/nsrt/filter-var.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public function scalars(bool $bool, float $float, int $int, string $string, int
159159
assertType("'17.1'", filter_var(17.1));
160160
assertType("'1.0E-50'", filter_var(1e-50));
161161
assertType('numeric-string', filter_var($int));
162-
assertType('numeric-string', filter_var($intRange));
162+
assertType("'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'", filter_var($intRange));
163163
assertType("'17'", filter_var(17));
164164
assertType('string', filter_var($string));
165165
assertType('non-empty-string', filter_var($nonEmptyString));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace IntRangeToString;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param int<5, 10> $i
11+
* @param int<-10, 10> $ii
12+
* @param int<0, 128> $maxlong
13+
* @param int<0, 129> $toolong
14+
*/
15+
public function sayHello($i, $ii, $maxlong, $toolong): void
16+
{
17+
assertType("'10'|'5'|'6'|'7'|'8'|'9'", (string) $i);
18+
assertType("'-1'|'-10'|'-2'|'-3'|'-4'|'-5'|'-6'|'-7'|'-8'|'-9'|'0'|'1'|'10'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'", (string) $ii);
19+
assertType("'0'|'1'|'10'|'100'|'101'|'102'|'103'|'104'|'105'|'106'|'107'|'108'|'109'|'11'|'110'|'111'|'112'|'113'|'114'|'115'|'116'|'117'|'118'|'119'|'12'|'120'|'121'|'122'|'123'|'124'|'125'|'126'|'127'|'128'|'13'|'14'|'15'|'16'|'17'|'18'|'19'|'2'|'20'|'21'|'22'|'23'|'24'|'25'|'26'|'27'|'28'|'29'|'3'|'30'|'31'|'32'|'33'|'34'|'35'|'36'|'37'|'38'|'39'|'4'|'40'|'41'|'42'|'43'|'44'|'45'|'46'|'47'|'48'|'49'|'5'|'50'|'51'|'52'|'53'|'54'|'55'|'56'|'57'|'58'|'59'|'6'|'60'|'61'|'62'|'63'|'64'|'65'|'66'|'67'|'68'|'69'|'7'|'70'|'71'|'72'|'73'|'74'|'75'|'76'|'77'|'78'|'79'|'8'|'80'|'81'|'82'|'83'|'84'|'85'|'86'|'87'|'88'|'89'|'9'|'90'|'91'|'92'|'93'|'94'|'95'|'96'|'97'|'98'|'99'", (string) $maxlong);
20+
assertType("numeric-string", (string) $toolong);
21+
}
22+
}

tests/PHPStan/Analyser/nsrt/unset-conditional-expressions.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function doBaz(): void
4242
}
4343
}
4444

45-
assertType('array{a?: bool, b?: numeric-string, c?: int<-1, 1>, d?: int<0, 1>}', $breakdowns);
45+
assertType("array{a?: bool, b?: '0'|'1', c?: int<-1, 1>, d?: int<0, 1>}", $breakdowns);
4646
}
4747

4848
}

0 commit comments

Comments
 (0)