Skip to content

Method call remembered as true after && when the other operand comes from an is_string($x) && $x !== '' ternary (regression in 2.3.0) #15483

Open
phpstan/phpstan-src
#6830

Description

@bartrail

Bug report

Since 2.3.0, a method call that is only evaluated on one branch of $a === null && $obj->method() is remembered as true on every path that survives a later $a === null early return. This only happens when $a comes from a ternary whose condition is is_string($x) && $x !== ''. With is_string($x) alone, or with $a as a parameter, the type stays bool.

On the path where $title was already a non-empty string, $input->isInteractive() is never called, so nothing is known about it there.

function f(Input $input, mixed $option): void
{
    $title = \is_string($option) && $option !== '' ? $option : null;
    if ($title === null && $input->isInteractive()) {
        $title = prompt();
    }
    if ($title === null) {
        return;
    }
    \PHPStan\dumpType($input->isInteractive()); // 2.3.1: true, 2.2.12: bool
}

In real code this shows up as booleanAnd.rightAlwaysTrue / booleanAnd.leftAlwaysTrue on later isInteractive() checks in a Symfony console command.

Version f() g() (only is_string)
2.2.12 bool bool
2.3.1 true bool

Code snippet that reproduces the problem

https://phpstan.org/r/175d129b-c316-4473-a49c-fa077551ea07

Expected output

Dumped type: bool on line 24, as in 2.2.12.

Did PHPStan help you today? Did it make you happy in any way?

We run level 10 on a large Symfony codebase, and the same upgrade pointed out a few redundant assertions in our tests. Thanks for the tool.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions