Repository: symfony/symfony
File: src/Symfony/Component/Console/Attribute/AskChoice.php (PHP)

Code before:
        $this->validator = $validator ? $validator(...) : null;
        $this->choices = \is_callable($choices) ? $choices(...) : $choices;
    }

    /**
     * @internal
     */
    public static function tryFrom(\ReflectionParameter|\ReflectionProperty $member, string $name): ?self
    {
        $reflection = new ReflectionMember($member);

        if (!$self = $reflection->getAttribute(self::class)) {
            return null;
        }

        $type = $reflection->getType();

        if (!$type instanceof \ReflectionNamedType) {
            throw new LogicException(\sprintf('The %s "$%s" of "%s" must have a named type. Untyped, Union or Intersection types are not supported for choice questions.', $reflection->getMemberName(), $name, $reflection->getSourceName()));
        }

        $isBackedEnum = is_subclass_of($type->getName(), \BackedEnum::class);

        // Validate that choices are provided or can be derived from enum
        if (!$self->choices && !$isBackedEnum) {
            throw new LogicException(\sprintf('The #[Choice] attribute for the %s "$%s" of "%s" requires either explicit choices or a BackedEnum type.', $reflection->getMemberName(), $name, $reflection->getSourceName()));
        }

        $self->closure = function (SymfonyStyle $io, InputInterface $input) use ($self, $reflection, $name, $type, $isBackedEnum) {
            if ($reflection->isProperty() && isset($this->{$reflection->getName()})) {
                return;
            }

            if ($reflection->isParameter() && !\in_array($input->getArgument($name), [null, []], true)) {
                return;
            }

            $choices = $self->choices instanceof \Closure ? ($self->choices)() : $self->choices;

            // Derive choices from enum cases if not provided
            if (!$choices && $isBackedEnum) {
                /** @var class-string<\BackedEnum> $enumClass */
                $enumClass = $type->getName();
                $choices = array_column($enumClass::cases(), 'value');
            }

            $question = new ChoiceQuestion($self->question, $choices, $self->default);
            $question->setMultiselect('array' === $type->getName());
            $question->setErrorMessage($self->errorMessage);
            $question->setPrompt($self->prompt);
            $question->setMaxAttempts($self->maxAttempts);

Review comment (suggestion):
```suggestion
            throw new LogicException(\sprintf('The #[AskChoice] attribute for the %s "$%s" of "%s" requires either explicit choices or a BackedEnum type.', $reflection->getMemberName(), $name, $reflection->getSourceName()));
```

Code after:
        $this->validator = $validator ? $validator(...) : null;
        $this->choices = \is_callable($choices) ? $choices(...) : $choices;
    }

    /**
     * @internal
     */
    public static function tryFrom(\ReflectionParameter|\ReflectionProperty $member, string $name): ?self
    {
        $reflection = new ReflectionMember($member);

        if (!$self = $reflection->getAttribute(self::class)) {
            return null;
        }

        $type = $reflection->getType();

        if (!$type instanceof \ReflectionNamedType) {
            throw new LogicException(\sprintf('The %s "$%s" of "%s" must have a named type. Untyped, Union or Intersection types are not supported for choice questions.', $reflection->getMemberName(), $name, $reflection->getSourceName()));
        }

        $isBackedEnum = is_subclass_of($type->getName(), \BackedEnum::class);

        // Validate that choices are provided or can be derived from enum
        if (!$self->choices && !$isBackedEnum) {
            throw new LogicException(\sprintf('The #[AskChoice] attribute for the %s "$%s" of "%s" requires either explicit choices or a BackedEnum type.', $reflection->getMemberName(), $name, $reflection->getSourceName()));
        }

        $self->closure = function (SymfonyStyle $io, InputInterface $input) use ($self, $reflection, $name, $type, $isBackedEnum) {
            if ($reflection->isProperty() && isset($this->{$reflection->getName()})) {
                return;
            }

            if ($reflection->isParameter() && !\in_array($input->getArgument($name), [null, []], true)) {
                return;
            }

            $choices = $self->choices instanceof \Closure ? ($self->choices)() : $self->choices;

            // Derive choices from enum cases if not provided
            if (!$choices && $isBackedEnum) {
                /** @var class-string<\BackedEnum> $enumClass */
                $enumClass = $type->getName();
                $choices = array_column($enumClass::cases(), 'value');
            }

            $question = new ChoiceQuestion($self->question, $choices, $self->default);
            $question->setMultiselect('array' === $type->getName());
            $question->setErrorMessage($self->errorMessage);
            $question->setPrompt($self->prompt);
            $question->setMaxAttempts($self->maxAttempts);