Skip to content

Commit 13aa62e

Browse files
phpstan-botclaude
andcommitted
Move the printf format parser change to its own pull request
PrintfFormatParser::parse() reading the analysed PHP version from the call site is a self-contained change with its own test, so it is reviewed separately. The parser keeps its injected PhpVersion here. PrintfFunctionThrowTypeExtension still resolves its own throwsValueErrorForInternalFunctions() check from Scope::getPhpVersion(), which is what this pull request is about. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 40551b1 commit 13aa62e

10 files changed

Lines changed: 34 additions & 39 deletions

‎src/Php/PhpVersions.php‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,6 @@ public function supportsAllUnicodeScalarCodePointsInMbSubstituteCharacter(): Tri
248248
return IntegerRangeType::fromInterval(70200, null)->isSuperTypeOf($this->phpVersions)->result;
249249
}
250250

251-
public function supportsHhPrintfSpecifier(): TrinaryLogic
252-
{
253-
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
254-
}
255-
256251
public function supportsPassNoneEncodings(): TrinaryLogic
257252
{
258253
return IntegerRangeType::fromInterval(null, 70299)->isSuperTypeOf($this->phpVersions)->result;

‎src/Rules/Functions/PrintfArrayParametersRule.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function processNode(Node $node, Scope $scope): array
6767
foreach ($formatArgType->getConstantStrings() as $formatString) {
6868
$format = $formatString->getValue();
6969

70-
$uses = $this->printfFormatParser->parse($format, $scope->getPhpVersion());
70+
$uses = $this->printfFormatParser->parse($format);
7171
if ($uses === null) {
7272
return [
7373
RuleErrorBuilder::message(sprintf(

‎src/Rules/Functions/PrintfParameterTypeRule.php‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7-
use PHPStan\Php\PhpVersions;
87
use PHPStan\Reflection\ReflectionProvider;
98
use PHPStan\Rules\Rule;
109
use PHPStan\Rules\RuleErrorBuilder;
@@ -95,7 +94,7 @@ public function processNode(Node $node, Scope $scope): array
9594

9695
$formatString = $formatArgTypeStrings[0];
9796
$format = $formatString->getValue();
98-
$placeholderMap = $this->getPlaceholders($format, $scope->getPhpVersion());
97+
$placeholderMap = $this->getPlaceholders($format);
9998
if ($placeholderMap === null) {
10099
// Already reported by PrintfParametersRule.
101100
return [];
@@ -177,9 +176,9 @@ public function processNode(Node $node, Scope $scope): array
177176
/**
178177
* @return array<int, non-empty-list<PrintfPlaceholder>>|null parameter index => placeholders
179178
*/
180-
private function getPlaceholders(string $format, PhpVersions $phpVersions): ?array
179+
private function getPlaceholders(string $format): ?array
181180
{
182-
$uses = $this->printfFormatParser->parse($format, $phpVersions);
181+
$uses = $this->printfFormatParser->parse($format);
183182
if ($uses === null) {
184183
return null;
185184
}

‎src/Rules/Functions/PrintfParametersRule.php‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use PhpParser\Node\Expr\FuncCall;
77
use PHPStan\Analyser\Scope;
88
use PHPStan\DependencyInjection\RegisteredRule;
9-
use PHPStan\Php\PhpVersions;
109
use PHPStan\Reflection\ReflectionProvider;
1110
use PHPStan\Rules\Rule;
1211
use PHPStan\Rules\RuleErrorBuilder;
@@ -85,7 +84,7 @@ public function processNode(Node $node, Scope $scope): array
8584
$format = $formatString->getValue();
8685

8786
if (in_array($name, ['sprintf', 'printf'], true)) {
88-
$tempPlaceHoldersCount = $this->getPrintfPlaceholdersCount($format, $scope->getPhpVersion());
87+
$tempPlaceHoldersCount = $this->getPrintfPlaceholdersCount($format);
8988
} else {
9089
$tempPlaceHoldersCount = $this->getScanfPlaceholdersCount($format);
9190
}
@@ -130,9 +129,9 @@ public function processNode(Node $node, Scope $scope): array
130129
return [];
131130
}
132131

133-
private function getPrintfPlaceholdersCount(string $format, PhpVersions $phpVersions): ?int
132+
private function getPrintfPlaceholdersCount(string $format): ?int
134133
{
135-
$uses = $this->printfFormatParser->parse($format, $phpVersions);
134+
$uses = $this->printfFormatParser->parse($format);
136135
if ($uses === null) {
137136
return null;
138137
}

‎src/Type/Php/PrintfFormatParser.php‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace PHPStan\Type\Php;
44

55
use PHPStan\DependencyInjection\AutowiredService;
6-
use PHPStan\Php\PhpVersions;
6+
use PHPStan\Php\PhpVersion;
77
use function in_array;
88
use function ltrim;
99
use function strlen;
@@ -29,6 +29,10 @@ final class PrintfFormatParser
2929

3030
private const ARG_NUM_NEXT = -1;
3131

32+
public function __construct(private PhpVersion $phpVersion)
33+
{
34+
}
35+
3236
/**
3337
* Returns the argument uses in the order the format consumes them, or null
3438
* when the format throws ValueError for any arguments. Index 0 is the first
@@ -37,7 +41,7 @@ final class PrintfFormatParser
3741
*
3842
* @return list<array{index: int, kind: 'value'|'width'|'precision', specifier: string, placeholder: string, number: int}>|null
3943
*/
40-
public function parse(string $format, PhpVersions $phpVersions): ?array
44+
public function parse(string $format): ?array
4145
{
4246
$length = strlen($format);
4347
$i = 0;
@@ -134,7 +138,7 @@ public function parse(string $format, PhpVersions $phpVersions): ?array
134138
}
135139

136140
$specifier = $format[$i];
137-
if (!self::isSpecifier($specifier, $phpVersions)) {
141+
if (!$this->isSpecifier($specifier)) {
138142
return null;
139143
}
140144

@@ -210,13 +214,13 @@ private static function parseNumber(string $format, int $length, int &$i): int
210214
return (int) $digits;
211215
}
212216

213-
private static function isSpecifier(string $char, PhpVersions $phpVersions): bool
217+
private function isSpecifier(string $char): bool
214218
{
215219
if (strpos(self::SPECIFIERS, $char) !== false) {
216220
return true;
217221
}
218222

219-
return !$phpVersions->supportsHhPrintfSpecifier()->no() && strpos(self::HH_SPECIFIERS, $char) !== false;
223+
return $this->phpVersion->supportsHhPrintfSpecifier() && strpos(self::HH_SPECIFIERS, $char) !== false;
220224
}
221225

222226
private static function isDigit(string $char): bool

‎src/Type/Php/PrintfFunctionThrowTypeExtension.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function getThrowTypeFromFunctionCall(FunctionReflection $functionReflect
8484
$uses = [];
8585
$requiredArgumentsCount = 0;
8686
foreach ($formats as $format) {
87-
$formatUses = $this->formatParser->parse($format->getValue(), $scope->getPhpVersion());
87+
$formatUses = $this->formatParser->parse($format->getValue());
8888
if ($formatUses === null) {
8989
return $functionReflection->getThrowType();
9090
}

‎tests/PHPStan/Rules/Functions/PrintfArrayParametersRuleTest.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace PHPStan\Rules\Functions;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Testing\RuleTestCase;
78
use PHPStan\Type\Php\PrintfFormatParser;
9+
use const PHP_VERSION_ID;
810

911
/**
1012
* @extends RuleTestCase<PrintfArrayParametersRule>
@@ -15,7 +17,7 @@ class PrintfArrayParametersRuleTest extends RuleTestCase
1517
protected function getRule(): Rule
1618
{
1719
return new PrintfArrayParametersRule(
18-
new PrintfFormatParser(),
20+
new PrintfFormatParser(new PhpVersion(PHP_VERSION_ID)),
1921
self::createReflectionProvider(),
2022
);
2123
}

‎tests/PHPStan/Rules/Functions/PrintfParameterTypeRuleTest.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace PHPStan\Rules\Functions;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Rules\RuleLevelHelper;
78
use PHPStan\Testing\RuleTestCase;
89
use PHPStan\Type\Php\PrintfFormatParser;
10+
use const PHP_VERSION_ID;
911

1012
/**
1113
* @extends RuleTestCase<PrintfParameterTypeRule>
@@ -19,7 +21,7 @@ protected function getRule(): Rule
1921
{
2022
$reflectionProvider = $this->createReflectionProvider();
2123
return new PrintfParameterTypeRule(
22-
new PrintfFormatParser(),
24+
new PrintfFormatParser(new PhpVersion(PHP_VERSION_ID)),
2325
$reflectionProvider,
2426
new RuleLevelHelper(
2527
$reflectionProvider,

‎tests/PHPStan/Rules/Functions/PrintfParametersRuleTest.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Functions;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Testing\RuleTestCase;
78
use PHPStan\Type\Php\PrintfFormatParser;
@@ -16,7 +17,7 @@ class PrintfParametersRuleTest extends RuleTestCase
1617
protected function getRule(): Rule
1718
{
1819
return new PrintfParametersRule(
19-
new PrintfFormatParser(),
20+
new PrintfFormatParser(new PhpVersion(PHP_VERSION_ID)),
2021
self::createReflectionProvider(),
2122
);
2223
}

‎tests/PHPStan/Type/Php/PrintfFormatParserTest.php‎

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
namespace PHPStan\Type\Php;
44

5-
use PHPStan\Php\PhpVersions;
6-
use PHPStan\Type\Constant\ConstantIntegerType;
5+
use PHPStan\Php\PhpVersion;
76
use PHPUnit\Framework\Attributes\DataProvider;
87
use PHPUnit\Framework\TestCase;
98

@@ -80,8 +79,8 @@ public static function dataRequiredArgumentsCount(): iterable
8079
#[DataProvider('dataRequiredArgumentsCount')]
8180
public function testRequiredArgumentsCount(string $format, ?int $expectedCount): void
8281
{
83-
$parser = new PrintfFormatParser();
84-
$uses = $parser->parse($format, self::phpVersions(80000));
82+
$parser = new PrintfFormatParser(new PhpVersion(80000));
83+
$uses = $parser->parse($format);
8584
if ($expectedCount === null) {
8685
$this->assertNull($uses);
8786
return;
@@ -93,29 +92,23 @@ public function testRequiredArgumentsCount(string $format, ?int $expectedCount):
9392

9493
public function testUses(): void
9594
{
96-
$parser = new PrintfFormatParser();
95+
$parser = new PrintfFormatParser(new PhpVersion(80000));
9796
$this->assertSame([
9897
['index' => 0, 'kind' => 'width', 'specifier' => 'g', 'placeholder' => '%*.*g', 'number' => 1],
9998
['index' => 1, 'kind' => 'precision', 'specifier' => 'g', 'placeholder' => '%*.*g', 'number' => 1],
10099
['index' => 2, 'kind' => 'value', 'specifier' => 'g', 'placeholder' => '%*.*g', 'number' => 1],
101100
['index' => 0, 'kind' => 'value', 'specifier' => 's', 'placeholder' => '%1$s', 'number' => 2],
102101
['index' => 4, 'kind' => 'width', 'specifier' => 'd', 'placeholder' => '%*5$d', 'number' => 3],
103102
['index' => 3, 'kind' => 'value', 'specifier' => 'd', 'placeholder' => '%*5$d', 'number' => 3],
104-
], $parser->parse('%*.*g %1$s %*5$d', self::phpVersions(80000)));
103+
], $parser->parse('%*.*g %1$s %*5$d'));
105104
}
106105

107106
public function testHhSpecifiersBeforePhp8(): void
108107
{
109-
$parser = new PrintfFormatParser();
110-
$phpVersions = self::phpVersions(70400);
111-
$this->assertNull($parser->parse('%h', $phpVersions));
112-
$this->assertNull($parser->parse('%H', $phpVersions));
113-
$this->assertNotNull($parser->parse('%g', $phpVersions));
114-
}
115-
116-
private static function phpVersions(int $versionId): PhpVersions
117-
{
118-
return new PhpVersions(new ConstantIntegerType($versionId));
108+
$parser = new PrintfFormatParser(new PhpVersion(70400));
109+
$this->assertNull($parser->parse('%h'));
110+
$this->assertNull($parser->parse('%H'));
111+
$this->assertNotNull($parser->parse('%g'));
119112
}
120113

121114
}

0 commit comments

Comments
 (0)