phpdoc-parser icon indicating copy to clipboard operation
phpdoc-parser copied to clipboard

Parentheses in callable syntax are not interpreted correctly

Open mabar opened this issue 3 years ago • 11 comments

Parentheses in callables in some cases create unexpected results, like (Closure(): int|null) changing to (Closure(): int)|null https://phpstan.org/r/f0db1360-c01a-4884-8c17-1caa7d464852

mabar avatar Apr 14 '21 08:04 mabar

Parentheses in callable syntax are not interpreted correctly

That's nonsence. The syntax is ambigous, there cannot be a correct way to interpret it.

Maybe related https://github.com/phpstan/phpdoc-parser/pull/18 (it was a long time ago, so I'm not 100 % sure)

JanTvrdik avatar Apr 14 '21 17:04 JanTvrdik

Also note that top-level parentheses always have no effect, i.e. (Closure(): int|null) will aways be identical to Closure(): int|null. It's the same as in math, 1 equals to (1) equals to ((1))

JanTvrdik avatar Apr 14 '21 17:04 JanTvrdik

Please check the f() and g() examples. Closure(): (int|null)|null is shown by phpstan as (Closure(): int|null)|null. If you use same syntax as is displayed by phpstan, result is (Closure(): int)|null.

I don't mind if parentheses are supported (even useless) or forbidden, but now it fails silently and phpstan is using in errors syntax which is not supported by phpstan parser.

mabar avatar Apr 14 '21 18:04 mabar

So the issue in how we print the type?

JanTvrdik avatar Apr 14 '21 18:04 JanTvrdik

And failing silently in case of not supported syntax. (Closure(): int|null)|null -> (Closure(): int)|null

Ideally Closure():int|null should fail too, because it's unclear whether user wanted Closure(): (int|null) or Closure(): (int)|null

mabar avatar Apr 14 '21 18:04 mabar

And failing silently in case of not supported syntax.

I don't understand. (Closure(): int|null)|null is supported and parsed well. What would you expect? It's parsed as (((Closure(): int)|null)|null) and further normalized by removing the duplicated null in union type.

should fail too, because it's unclear

I think ut's allowed mostly for null|Closure():int. Anyway I think that's was #18 was supposed to solve.

JanTvrdik avatar Apr 14 '21 18:04 JanTvrdik

I don't think so. (Closure(): int|string)|null turns into (Closure(): int)|string|null

https://phpstan.org/r/04ea77ae-c31d-4036-807a-9de7274fe15e

mabar avatar Apr 14 '21 18:04 mabar

Yes, that is correct, even though it can be confusing. What would you expect? Note that the parentheses here are useless.

(A|B)|C is the same as A|B|C and also same as (A)|B|C. Here the A is Closure(): int.

JanTvrdik avatar Apr 14 '21 18:04 JanTvrdik

(Closure(): int|string)|null turns into (Closure(): int)|string|null

One is closure which returns int|string or null, second is closure which returns int or string or null.

In following example is expected closure which returns int|string or null and as such is the code treated. If it was the same, this code would not fail. https://phpstan.org/r/58aa23f2-e350-4760-b8f0-2c5cc1bd66b8 http://sandbox.onlinephpfunctions.com/code/108b92d91ec897d01054c530ab461f47c0ef515a

Also thanks, just found another bug due to testing the Closure(): (int|string)|null syntax https://phpstan.org/r/fe407cba-17cf-4f56-8a0a-07d68cb41712 http://sandbox.onlinephpfunctions.com/code/3072c54f802558ace446341a9326c44a7a37b794

mabar avatar Apr 14 '21 18:04 mabar

One is closure which returns int|string or null

Based on what? Is that you wish? Again parsing Closure(): int|string as (Closure(): int)|string is not a bug. It's very much intentional. See https://github.com/phpstan/phpdoc-parser/blob/c37ba6f7b3d30406f70f1416f18380bb0c6890f1/tests/PHPStan/Parser/TypeParserTest.php#L611-L620

JanTvrdik avatar Apr 14 '21 19:04 JanTvrdik

I've run this through typescript

(a: (() => number)|string) => {
    if (typeof a === 'string') {
        return;
    }

    const x:never = a;
}

and it interprets (Closure(): int)|string not as Closure(): int|string but as Closure(): int or string. Isn't the intention here based on wrong assumption?

simPod avatar Apr 14 '21 19:04 simPod