diff --git a/src/Reflector/PhpDocTypeReflector.php b/src/Reflector/PhpDocTypeReflector.php index 92b70ae..87d93a6 100644 --- a/src/Reflector/PhpDocTypeReflector.php +++ b/src/Reflector/PhpDocTypeReflector.php @@ -101,6 +101,14 @@ private function reflectIdentifier(string $name, array $genericTypes = []): Type return $this->reflectInt($genericTypes); } + if ($name === 'int-mask') { + return $this->reflectIntMask($genericTypes); + } + + if ($name === 'int-mask-of') { + return $this->reflectIntMaskOf($genericTypes); + } + if ($name === 'list') { return $this->reflectList($genericTypes); } @@ -121,6 +129,8 @@ private function reflectIdentifier(string $name, array $genericTypes = []): Type return $this->reflectIterable($genericTypes); } + // todo check no generics? + return match ($name) { 'null' => types::null, 'true' => types::true, @@ -134,6 +144,7 @@ private function reflectIdentifier(string $name, array $genericTypes = []): Type 'numeric' => types::numeric, 'string' => types::string, 'non-empty-string' => types::nonEmptyString, + 'non-falsy-string', 'truthy-string' => types::truthyString, 'numeric-string' => types::numericString, 'array-key' => types::arrayKey, 'literal-int' => types::literalInt, @@ -203,6 +214,57 @@ private function reflectIntLimit(TypeNode $type, string $unlimitedName): ?int throw new ReflectionException(sprintf('%s cannot be used as int range limit.', TypeStringifier::stringify($type))); } + /** + * @param list $templateArguments + */ + private function reflectIntMask(array $templateArguments): Type\IntMaskType + { + if ($templateArguments === []) { + throw new \LogicException(); + } + + return types::intMask(...array_map($this->reflectIntMaskInt(...), $templateArguments)); + } + + /** + * @return int<0, max> + */ + private function reflectIntMaskInt(TypeNode $node): int + { + if (!$node instanceof ConstTypeNode) { + throw new ReflectionException('TODO'); + } + + if (!$node->constExpr instanceof ConstExprIntegerNode) { + throw new ReflectionException('TODO'); + } + + $int = (int) $node->constExpr->value; + + if ((string) $int !== $node->constExpr->value) { + throw new ReflectionException('TODO'); + } + + if ($int < 0) { + throw new ReflectionException('TODO'); + } + + return $int; + } + + /** + * @param list $templateArguments + */ + private function reflectIntMaskOf(array $templateArguments): Type\IntMaskOfType + { + if (\count($templateArguments) !== 1) { + throw new \LogicException(); + } + + /** @psalm-suppress MixedArgumentTypeCoercion */ + return types::intMaskOf($this->doReflect($templateArguments[0])); + } + /** * @param list $templateArguments */