Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum support #6425

Closed
15 tasks done
Tracked by #6395
weirdan opened this issue Sep 5, 2021 · 13 comments
Closed
15 tasks done
Tracked by #6395

Enum support #6425

weirdan opened this issue Sep 5, 2021 · 13 comments

Comments

@psalm-github-bot
Copy link

psalm-github-bot bot commented Sep 5, 2021

I found these snippets:

https://psalm.dev/r/d153c9d4f0
<?php

enum Status: int
{
    case FOO = 1;
    case BAR = 2;
}

$statusA = Status::FOO;
$statusB = Status::FOO;
$statusC = Status::BAR;

$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true
Psalm output (using commit 916b098):

ERROR: ParseError - 3:6 - Syntax error, unexpected T_STRING on line 3

ERROR: ParseError - 5:5 - Syntax error, unexpected T_CASE on line 5

@weirdan weirdan added this to the PHP 8.1 milestone Sep 5, 2021
@weirdan
Copy link
Collaborator Author

weirdan commented Sep 5, 2021

@niconoe- mind elaborating on those two non-linked points?

@niconoe-
Copy link

niconoe- commented Sep 5, 2021

About Enum are objects I was thinking about the good behavior of using instanceof like described in the RFC. I didn't know this was already developped as I tested in the default PHP version, not forcing it to PHP 8.1.
You can remove this point, or check it to say it's already done.

About Attributes are allowed on Enums and TARGET_CLASS also references Enums I was thinking about something like https://psalm.dev/r/c31b9b8e81?php=8.1 or like this: https://psalm.dev/r/36779dc1b9?php=8.1

In the first case, the attribute usage is triggered by Psalm because Status is not a class (while enums are actually a subset of classes, if I understand correctly the concept in the RFC).

In the 2nd case, the attribute usage should be triggered by Psalm but not for the given reason, as currently it's the fact that Status is not a class, but it should trigger the fact that the target is not respected.

Maybe there are still other cases to discover when mixing attributes with enums, but I'm not familiar enough with both of them to go deeper.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/c31b9b8e81
<?php

#[Attribute]
enum Status: int
{
    case FOO = 1;
    case BAR = 2;
}

#[Status]
class HelloWorld
{}
Psalm output (using commit 916b098):

INFO: UndefinedAttributeClass - 10:3 - Attribute class Status does not exist

INFO: UnusedClass - 11:7 - Class HelloWorld is never used
https://psalm.dev/r/36779dc1b9
<?php

#[Attribute(Attribute::TARGET_CLASS)]
enum Status: int
{
    case FOO = 1;
    case BAR = 2;
}

class HelloWorld
{
    #[Status]
	public function _() {}
}
Psalm output (using commit 916b098):

INFO: UndefinedAttributeClass - 12:7 - Attribute class Status does not exist

INFO: MissingReturnType - 13:18 - Method HelloWorld::_ does not have a return type, expecting void

INFO: UnusedClass - 10:7 - Class HelloWorld is never used

@orklah orklah added the enum label Nov 6, 2021
@orklah
Copy link
Collaborator

orklah commented Nov 6, 2021

@jpresutti
Copy link

jpresutti commented Nov 23, 2021

Enums in attributes are not properly detected for their types
https://psalm.dev/r/aa57812a88?php=8.1

@psalm-github-bot
Copy link

psalm-github-bot bot commented Nov 23, 2021

I found these snippets:

https://psalm.dev/r/aa57812a88
<?php
class CreateController 
{

    #[Action(usage: '--type={(get-post-put-delete-patch)} --module={module} --noview={true|false} {controller} {action}', description: 'Create a new controller action from the template file.')]
    #[Param(type: 'string', name: 'controller', description: 'Name of controller to create action in.' . "\n" . '  Controller file is created if it does not exist.')]
    #[Param(type: 'string', name: 'action', description: 'Name of action to create.' . "\n" . '  Creates associated view file unless noview is true')]
    #[Param(type: 'string', name: 'type', description: 'HTTP Methods for Controller Action, dash separated. Defaults to get', paramType: ParamType::FLAG)]
    #[Param(type: 'string', name: 'module', description: 'Optional - Module to create controller in (CLI for command line)', paramType: ParamType::FLAG)]
    #[Param(type: 'bool', name: 'noview', description: 'True to skip creating view file', paramType: ParamType::FLAG)]
    public function actionGet(
        ?string $controller = null,
        ?string $action = null,
        string $type = 'get',
        ?string $module = null,
        bool $noview = false
    ): void {
    }
}

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class Action
{
    public function __construct(public string $usage = '', public string $description = '')
    {
    }

    /**
     * Get pretty format usage text
     *
     * @param string $usage
     * @param string $name
     * @return string
     */
    public function getHelpText(string $usage, string $name): string
    {
        return trim(implode(' ', ['Usage:', $usage, $name, $this->usage]));
    }
}

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Param
{
    public function __construct(
        public string $type = '',
        public string $name = '',
        public string $description = '',
        public ParamType $paramType = ParamType::PARAM
    ) {
    }

   
}


enum ParamType
{
    case FLAG;
    case PARAM;
}
Psalm output (using commit fdf3a8d):

ERROR: InvalidArgument - 8:138 - Argument 4 of Param::__construct expects ParamType, "Psalm could not infer this type" provided

ERROR: InvalidArgument - 9:137 - Argument 4 of Param::__construct expects ParamType, "Psalm could not infer this type" provided

ERROR: InvalidArgument - 10:102 - Argument 4 of Param::__construct expects ParamType, "Psalm could not infer this type" provided

@simPod
Copy link
Contributor

simPod commented Jan 10, 2022

  • Enums have properties probably only internally, not on interface?
  • Interface StringBackedEnum exists
  • Interface IntBackedEnum exists
  • type of IntBackedEnum->value is int (same for StringBackedEnum)

https://psalm.dev/r/9df7604507
https://psalm.dev/r/461a456ffb

@psalm-github-bot
Copy link

psalm-github-bot bot commented Jan 10, 2022

I found these snippets:

https://psalm.dev/r/9df7604507
<?php

function value(BackedEnum $e): int|string {
    return $e->value;
}

function name(BackedEnum $e): string {
    return $e->name;
}


function intValue(IntBackedEnum $e): int {
    return $e->value;
}

function stringValue(StringBackedEnum $e): string {
    return $e->value;
}
Psalm output (using commit 206332b):

ERROR: UndefinedClass - 3:16 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 3:32 - Could not verify return type 'int|string' for value

ERROR: UndefinedClass - 7:15 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 7:31 - Could not verify return type 'string' for name

ERROR: UndefinedClass - 12:19 - Class, interface or enum named IntBackedEnum does not exist

INFO: MixedInferredReturnType - 12:38 - Could not verify return type 'int' for intValue

ERROR: UndefinedClass - 16:22 - Class, interface or enum named StringBackedEnum does not exist

INFO: MixedInferredReturnType - 16:44 - Could not verify return type 'string' for stringValue
https://psalm.dev/r/461a456ffb
<?php

function value(BackedEnum $e): int|string {
    return $e->value;
}

function name(BackedEnum $e): string {
    return $e->name;
}
Psalm output (using commit 206332b):

ERROR: UndefinedClass - 3:16 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 3:32 - Could not verify return type 'int|string' for value

ERROR: UndefinedClass - 7:15 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 7:31 - Could not verify return type 'string' for name

@weirdan
Copy link
Collaborator Author

weirdan commented Jan 10, 2022

Interface StringBackedEnum exists
Interface IntBackedEnum exists

Do they? https://3v4l.org/3piBE#v8.1.1

@simPod
Copy link
Contributor

simPod commented Jan 10, 2022

Hm, my bad, looked into wrong stubs. So only this one should not give any error https://psalm.dev/r/461a456ffb.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/461a456ffb
<?php

function value(BackedEnum $e): int|string {
    return $e->value;
}

function name(BackedEnum $e): string {
    return $e->name;
}
Psalm output (using commit 206332b):

ERROR: UndefinedClass - 3:16 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 3:32 - Could not verify return type 'int|string' for value

ERROR: UndefinedClass - 7:15 - Class, interface or enum named BackedEnum does not exist

INFO: MixedInferredReturnType - 7:31 - Could not verify return type 'string' for name

@orklah
Copy link
Collaborator

orklah commented Jan 24, 2022

As far as I can tell, Enum are now fully supported. Please open new issues if bugs are found

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants