Skip to content

Chaining class methods breaks type information in prior closure #436

Description

@florenceboettger

Verified on latest main

  • I have confirmed this bug still occurs when built from the latest main branch.

PHPantom version / commit

33718a6

Installation method

Built from source

Operating system

Linux x86_64

Editor

VS Code

Bug description

Happens both with the prebuilt VSCode version and with the latest main version via cargo.

If you have a class with a method that returns $this and uses a closure as an argument, chaining further methods afterwards completely disables type recognition within the closure.

Steps to reproduce

  1. Create a file with the following content:
<?php

class ClosureTest
{
	public function useClosure(\Closure $closure): static
	{
		return $this;
	}

	public function return(): string
	{
		return 'test';
	}
}

$closure = new ClosureTest();
$closure->useClosure(function (string $s) {
	echo $s;
	$test = 'a';
});
  1. When hovering over $s within the closure body, it shows $s = string as expected. When hovering over $test, it shows $test = 'a' as expected.
  2. Chain another class method like so:
<?php

class ClosureTest
{
	public function useClosure(\Closure $closure): static
	{
		return $this;
	}

	public function return(): string
	{
		return 'test';
	}
}

$closure = new ClosureTest();
$closure->useClosure(function (string $s) {
	echo $s;
	$test = 'a';
})->return();
  1. Expected: Same behavior as in 2.
  2. Actual: When hovering over $s within the closure body now, it only shows $s. The type information is lost within the closure body, even though the closure definition defines it as string. Likewise, when hovering over $test, it now also only shows $test and loses the type information.

When explicitly adding type information with phpstan, it works for variables:

  1. Create a file with the following content:
<?php

class ClosureTest
{
	public function useClosure(\Closure $closure): static
	{
		return $this;
	}

	public function return(): string
	{
		return 'test';
	}
}

$closure = new ClosureTest();
/** @var string $s */
$closure->useClosure(function (string $s) {
	echo $s;
	/** @var string $test */
	$test = 'a';
})->return();
  1. Hovering over $s and $test in the closure body yields type info of string for both.

However, typing the closure more strictly with phpstan does not change anything from the initially-described case:

<?php

class ClosureTest
{
	/**
	 * @param (\Closure(string $s): void) $closure
	 *
	 * @return $this
	 */
	public function useClosure(\Closure $closure): static
	{
		return $this;
	}

	public function return(): string
	{
		return 'test';
	}
}

$closure = new ClosureTest();
$closure->useClosure(function (string $s) {
	echo $s;
	$test = 'a';
})->return();

Error output or panic trace


.phpantom.toml

default / no config file

Additional context

No response

Activity

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions