From 387ebd5059990086444e993cc0fb40e167cc71c6 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 4 Apr 2023 09:19:09 +0200 Subject: [PATCH] Some useful advanced PHPDoc types --- src/PhpDoc/TypeNodeResolver.php | 30 ++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 1 + tests/PHPStan/Analyser/data/more-types.php | 41 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/PHPStan/Analyser/data/more-types.php diff --git a/src/PhpDoc/TypeNodeResolver.php b/src/PhpDoc/TypeNodeResolver.php index 32ea661534..2065f6575b 100644 --- a/src/PhpDoc/TypeNodeResolver.php +++ b/src/PhpDoc/TypeNodeResolver.php @@ -198,6 +198,7 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco case 'class-string': case 'interface-string': case 'trait-string': + case 'enum-string': return new ClassStringType(); case 'callable-string': @@ -215,6 +216,18 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco return new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]); + case 'empty-scalar': + return TypeCombinator::intersect( + new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]), + StaticTypeFactory::falsey(), + ); + + case 'non-empty-scalar': + return TypeCombinator::remove( + new UnionType([new IntegerType(), new FloatType(), new StringType(), new BooleanType()]), + StaticTypeFactory::falsey(), + ); + case 'number': $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope); @@ -260,6 +273,13 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco new AccessoryNonFalsyStringType(), ]); + case 'non-empty-literal-string': + return new IntersectionType([ + new StringType(), + new AccessoryNonEmptyStringType(), + new AccessoryLiteralStringType(), + ]); + case 'bool': return new BooleanType(); @@ -307,6 +327,7 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco return new IterableType(new MixedType(), new MixedType()); case 'callable': + case 'pure-callable': return new CallableType(); case 'resource': @@ -318,9 +339,15 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco return new ResourceType(); + case 'closed-resource': + return new ResourceType(); + case 'mixed': return new MixedType(true); + case 'non-empty-mixed': + return new MixedType(true, StaticTypeFactory::falsey()); + case 'void': return new VoidType(); @@ -330,6 +357,9 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco case 'callable-object': return new IntersectionType([new ObjectWithoutClassType(), new CallableType()]); + case 'callable-array': + return new IntersectionType([new ArrayType(new MixedType(), new MixedType()), new CallableType()]); + case 'never': case 'noreturn': $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope); diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index e2319e9f3b..0f9c6336dc 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1220,6 +1220,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8609.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/PhpDoc/data/bug-8609-function.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9131.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/more-types.php'); } /** diff --git a/tests/PHPStan/Analyser/data/more-types.php b/tests/PHPStan/Analyser/data/more-types.php new file mode 100644 index 0000000000..33581a6272 --- /dev/null +++ b/tests/PHPStan/Analyser/data/more-types.php @@ -0,0 +1,41 @@ +|int<1, max>|non-falsy-string|true', $nonEmptyScalar); + assertType("0|0.0|''|'0'|false", $emptyScalar); + assertType("mixed~0|0.0|''|'0'|array{}|false|null", $nonEmptyMixed); + } + +}