Skip to content

Commit eb4c06d

Browse files
committed
Update typhoon/type
1 parent 03b504d commit eb4c06d

File tree

6 files changed

+74
-53
lines changed

6 files changed

+74
-53
lines changed

composer.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/CustomTypeParsers.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
/**
1010
* @api
1111
*/
12-
final class CustomTypeParsers implements CustomTypeParser
12+
final readonly class CustomTypeParsers implements CustomTypeParser
1313
{
1414
/**
1515
* @param iterable<CustomTypeParser> $customTypeParsers
1616
*/
1717
public function __construct(
18-
private readonly iterable $customTypeParsers = [],
18+
private iterable $customTypeParsers = [],
1919
) {}
2020

2121
public function parseCustomType(string $unresolvedName, array $templateArguments, TypeContext $context): ?Type

src/Internal/ContextualTypeParser.php

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@
2020
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
2121
use Typhoon\PHPStanTypeParser\CustomTypeParser;
2222
use Typhoon\PHPStanTypeParser\TypeContext;
23+
use Typhoon\Type\ArrayT;
2324
use Typhoon\Type\ObjectT;
2425
use Typhoon\Type\Type;
2526
use function Typhoon\Type\andT;
26-
use function Typhoon\Type\arrayT;
2727
use function Typhoon\Type\floatRangeT;
2828
use function Typhoon\Type\floatT;
2929
use function Typhoon\Type\intRangeT;
3030
use function Typhoon\Type\intT;
31-
use function Typhoon\Type\nonEmptyArrayT;
3231
use function Typhoon\Type\nullOrT;
3332
use function Typhoon\Type\orT;
3433
use function Typhoon\Type\stringT;
@@ -60,11 +59,11 @@
6059
* @internal
6160
* @psalm-internal Typhoon\PHPStanTypeParser
6261
*/
63-
final class ContextualTypeParser
62+
final readonly class ContextualTypeParser
6463
{
6564
public function __construct(
66-
private readonly CustomTypeParser $customTypeParser,
67-
private readonly TypeContext $context,
65+
private CustomTypeParser $customTypeParser,
66+
private TypeContext $context,
6867
) {}
6968

7069
public function parseTypeNode(TypeNode $node): Type
@@ -87,7 +86,7 @@ private static function parseConstExpr(ConstExprNode $node): Type
8786
$node instanceof ConstExprFalseNode => falseT,
8887
$node instanceof ConstExprTrueNode => trueT,
8988
$node instanceof ConstExprIntegerNode => match (true) {
90-
is_numeric($node->value) => intT($node->value),
89+
is_numeric($node->value) => intT((int) $node->value),
9190
default => throw new \LogicException(),
9291
},
9392
$node instanceof ConstExprFloatNode => match (true) {
@@ -151,17 +150,17 @@ private function parseIdentifier(string $name, array $genericNodes = []): Type
151150
if ($name === 'array') {
152151
return match ($number = \count($templateArguments)) {
153152
0 => arrayT,
154-
1 => arrayT(valueType: $templateArguments[0]),
155-
2 => arrayT($templateArguments[0], $templateArguments[1]),
153+
1 => new ArrayT(valueType: $templateArguments[0]),
154+
2 => new ArrayT($templateArguments[0], $templateArguments[1]),
156155
default => throw new \LogicException(\sprintf('array type should have at most 2 type arguments, got %d', $number)),
157156
};
158157
}
159158

160159
if ($name === 'non-empty-array') {
161160
return match ($number = \count($templateArguments)) {
162-
0 => nonEmptyArrayT(),
163-
1 => nonEmptyArrayT(valueType: $templateArguments[0]),
164-
2 => nonEmptyArrayT($templateArguments[0], $templateArguments[1]),
161+
0 => new ArrayT(isNonEmpty: true),
162+
1 => new ArrayT(valueType: $templateArguments[0], isNonEmpty: true),
163+
2 => new ArrayT($templateArguments[0], $templateArguments[1], isNonEmpty: true),
165164
default => throw new \LogicException(\sprintf('non-empty-array type should have at most 2 type arguments, got %d', $number)),
166165
};
167166
}
@@ -178,8 +177,8 @@ private function parseInt(array $genericNodes): Type
178177
return match (\count($genericNodes)) {
179178
0 => intT,
180179
2 => intRangeT(
181-
min: self::parseRangeLimit($genericNodes[0], 'min', float: false),
182-
max: self::parseRangeLimit($genericNodes[1], 'max', float: false),
180+
min: self::parseIntRangeLimit($genericNodes[0], 'min'),
181+
max: self::parseIntRangeLimit($genericNodes[1], 'max'),
183182
),
184183
default => throw new \LogicException(\sprintf(
185184
'Int range type should have 2 type arguments, got %d',
@@ -196,8 +195,8 @@ private function parseFloat(array $genericNodes): Type
196195
return match (\count($genericNodes)) {
197196
0 => floatT,
198197
2 => floatRangeT(
199-
min: self::parseRangeLimit($genericNodes[0], 'min', float: true),
200-
max: self::parseRangeLimit($genericNodes[1], 'max', float: true),
198+
min: self::parseFloatRangeLimit($genericNodes[0], 'min'),
199+
max: self::parseFloatRangeLimit($genericNodes[1], 'max'),
201200
),
202201
default => throw new \LogicException(\sprintf(
203202
'Float range type should have 2 type arguments, got %d',
@@ -208,9 +207,8 @@ private function parseFloat(array $genericNodes): Type
208207

209208
/**
210209
* @param 'min'|'max' $name
211-
* @return ?numeric-string
212210
*/
213-
private function parseRangeLimit(TypeNode $type, string $name, bool $float): ?string
211+
private function parseIntRangeLimit(TypeNode $type, string $name): ?int
214212
{
215213
if ($type instanceof IdentifierTypeNode) {
216214
if ($type->name === $name) {
@@ -226,11 +224,34 @@ private function parseRangeLimit(TypeNode $type, string $name, bool $float): ?st
226224

227225
$expr = $type->constExpr;
228226

229-
if (($float && $expr instanceof ConstExprFloatNode) || $expr instanceof ConstExprIntegerNode) {
230-
if (!is_numeric($expr->value)) {
231-
throw new \LogicException();
227+
if ($expr instanceof ConstExprIntegerNode && is_numeric($expr->value)) {
228+
return (int) $expr->value;
229+
}
230+
231+
throw new \LogicException();
232+
}
233+
234+
/**
235+
* @param 'min'|'max' $name
236+
* @return ?numeric-string
237+
*/
238+
private function parseFloatRangeLimit(TypeNode $type, string $name): ?string
239+
{
240+
if ($type instanceof IdentifierTypeNode) {
241+
if ($type->name === $name) {
242+
return null;
232243
}
233244

245+
throw new \LogicException();
246+
}
247+
248+
if (!$type instanceof ConstTypeNode) {
249+
throw new \LogicException();
250+
}
251+
252+
$expr = $type->constExpr;
253+
254+
if (($expr instanceof ConstExprFloatNode || $expr instanceof ConstExprIntegerNode) && is_numeric($expr->value)) {
234255
return $expr->value;
235256
}
236257

src/PHPStanTypeParser.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616
/**
1717
* @api
1818
*/
19-
final class PHPStanTypeParser
19+
final readonly class PHPStanTypeParser
2020
{
21-
private readonly CustomTypeParser $customTypeParser;
21+
private CustomTypeParser $customTypeParser;
2222

2323
/**
2424
* @param iterable<CustomTypeParser> $customTypeParsers
2525
*/
2626
public function __construct(
2727
iterable $customTypeParsers = [],
28-
private readonly Lexer $lexer = new Lexer(new ParserConfig([])),
29-
private readonly TypeParser $typeParser = new TypeParser(
28+
private Lexer $lexer = new Lexer(new ParserConfig([])),
29+
private TypeParser $typeParser = new TypeParser(
3030
new ParserConfig([]),
3131
new ConstExprParser(new ParserConfig([])),
3232
),

src/RuntimeTypeContext.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
namespace Typhoon\PHPStanTypeParser;
66

77
use Typhoon\Type\Type;
8-
use function Typhoon\Type\objectT;
8+
use function Typhoon\Type\namedObjectT;
99

1010
/**
1111
* @api
1212
*/
13-
final class RuntimeTypeContext implements TypeContext
13+
final readonly class RuntimeTypeContext implements TypeContext
1414
{
1515
public function resolveConstantName(string $unresolvedName): array
1616
{
@@ -32,6 +32,6 @@ public function resolveClassName(string $unresolvedName): string
3232

3333
public function resolveNameAsType(string $unresolvedName, array $templateArguments = []): Type
3434
{
35-
return objectT($this->resolveClassName($unresolvedName), $templateArguments);
35+
return namedObjectT($this->resolveClassName($unresolvedName), $templateArguments);
3636
}
3737
}

tests/PHPStanTypeParserTest.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
use function Typhoon\Type\floatT;
1616
use function Typhoon\Type\intRangeT;
1717
use function Typhoon\Type\intT;
18+
use function Typhoon\Type\namedObjectT;
1819
use function Typhoon\Type\nonEmptyArrayT;
1920
use function Typhoon\Type\nullOrT;
20-
use function Typhoon\Type\objectT;
2121
use function Typhoon\Type\orT;
2222
use function Typhoon\Type\stringT;
2323
use const Typhoon\Type\arrayKeyT;
@@ -79,7 +79,7 @@ private static function cases(): \Generator
7979
yield '0.5' => floatT(0.5);
8080
yield '-4.67' => floatT(-4.67);
8181
yield 'float' => floatT;
82-
yield 'float<10.0002, 231.00002>' => floatRangeT(10.0002, 231.00002);
82+
yield 'float<10.0002, 231.00002>' => floatRangeT(10.000_2, 231.000_02);
8383
yield 'float<min, 123>' => floatRangeT(max: 123);
8484
yield 'float<-99, max>' => floatRangeT(min: -99);
8585
yield '"0"' => stringT('0');
@@ -100,33 +100,21 @@ private static function cases(): \Generator
100100
yield 'int&string' => andT(intT, stringT);
101101
yield '(int&string)&float' => andT(andT(intT, stringT), floatT);
102102
yield 'array' => arrayT;
103-
yield 'array<string>' => arrayT(valueType: stringT);
103+
yield 'array<string>' => arrayT(value: stringT);
104104
yield 'array<int, string>' => arrayT(intT, stringT);
105105
yield 'non-empty-array' => nonEmptyArrayT();
106-
yield 'non-empty-array<string>' => nonEmptyArrayT(valueType: stringT);
106+
yield 'non-empty-array<string>' => nonEmptyArrayT(value: stringT);
107107
yield 'non-empty-array<int, string>' => nonEmptyArrayT(intT, stringT);
108108
yield 'mixed' => mixedT;
109-
yield \stdClass::class => objectT(\stdClass::class);
110-
yield \Stringable::class => objectT(\Stringable::class);
111-
yield 'Traversable<int, string>' => objectT(\Traversable::class, [intT, stringT]);
109+
yield \stdClass::class => namedObjectT(\stdClass::class);
110+
yield \Stringable::class => namedObjectT(\Stringable::class);
111+
yield 'Traversable<int, string>' => namedObjectT(\Traversable::class, [intT, stringT]);
112112
// todo yield 'stdClass|Iterator&Throwable' https://github.com/phpstan/phpdoc-parser/issues/271
113113
}
114114

115-
/**
116-
* @return \Generator<non-empty-string, array{non-empty-string, Type}>
117-
*/
118-
public static function provider(): \Generator
119-
{
120-
foreach (self::cases() as $string => $type) {
121-
$name = is_numeric($string) ? "`{$string}`" : $string;
122-
123-
yield $name => [$string, $type];
124-
}
125-
}
126-
127115
private ?PHPStanTypeParser $parser = null;
128116

129-
#[DataProvider('provider')]
117+
#[DataProvider('provideCases')]
130118
public function test(string $string, Type $expectedType): void
131119
{
132120
$this->parser ??= new PHPStanTypeParser();
@@ -135,4 +123,16 @@ public function test(string $string, Type $expectedType): void
135123

136124
self::assertEquals($expectedType, $type);
137125
}
126+
127+
/**
128+
* @return \Generator<non-empty-string, array{non-empty-string, Type}>
129+
*/
130+
public static function provideCases(): iterable
131+
{
132+
foreach (self::cases() as $string => $type) {
133+
$name = is_numeric($string) ? "`{$string}`" : $string;
134+
135+
yield $name => [$string, $type];
136+
}
137+
}
138138
}

0 commit comments

Comments
 (0)