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

Generic abstract classes don't respect type annotations #4843

Open
nikeee opened this issue Mar 6, 2024 · 0 comments
Open

Generic abstract classes don't respect type annotations #4843

nikeee opened this issue Mar 6, 2024 · 0 comments

Comments

@nikeee
Copy link

nikeee commented Mar 6, 2024

I tried to build a generic interface that has a parse method that returns an instance of T. However, the implementation seems to expect to have T in the place of the return type. This is not possible because it doesn't exist at run time and will throw an error.

Examples:

/** @template T */
interface SomeInterface {
	/** @return ?T */
	function parse(string $value): mixed;
}
/** @implements SomeInterface<bool> */
class SomeClass0 implements SomeInterface {
	function parse(string $value): ?bool { return false; }
}
/** @implements SomeInterface<int> */
class SomeClass1 implements SomeInterface {
	function parse(string $value): ?int { return 0; }
}
a.php PhanTypeMismatchReturn Returning false of type false but parse(string $value) is declared to return ?T
a.php PhanTypeMismatchReturn Returning 0 of type 0 but parse(string $value) is declared to return ?T

...did not work, so I suspected that phan has issues with the nullability here. That doesn't seem to be the case either:

/** @template T */
interface SomeInterface {
	/** @return T */
	function parse(string $value): mixed;
}
/** @implements SomeInterface<?bool> */
class SomeClass0 implements SomeInterface {
	/** @return ?bool */
	function parse(string $value): ?bool { return false; }
}
/** @implements SomeInterface<?int> */
class SomeClass1 implements SomeInterface {
	/** @return ?int */
	function parse(string $value): ?int { return 0; }
}
a.php PhanTypeMismatchReturn Returning false of type false but parse(string $value) is declared to return T
a.php PhanTypeMismatchReturn Returning 0 of type 0 but parse(string $value) is declared to return T

Next, I've read in the documentation that generic interfaces don't seem to be supported, so I tried using an abstract class. It seems to yield the same result:

/** @template T */
abstract class SomeClass {
	/** @return T */
	abstract function doWork();
}
/** @inherits SomeClass<?bool> */
class SomeClass0 extends SomeClass {
	public function __construct() { }
	function doWork(): ?bool { return false; }
}
/** @inherits SomeClass<?int> */
class SomeClass1 extends SomeClass {
	public function __construct() { }
	/** @return ?int */
	function doWork(): ?int { return 0; }
}
a.php:17 PhanParamSignatureMismatch Declaration of function doWork() : T should be compatible with function doWork() : T defined in a.php:12
a.php:17 PhanTypeMismatchReturn Returning false of type false but doWork() is declared to return T

It seems that SomeClass1 works without any issues here, the error only applies to ?bool. When adding an additional /** @return ?bool */, it seems to work. My guess is that phan sometimes ignores the types that are written in actual PHP code, not in the PHPDoc.

Is this the intended behavior? How to I type this correctly?

$ ./vendor/bin/phan --version
Phan 5.4.3
php-ast version 1.1.1
PHP version used to run Phan: 8.3.3-1+ubuntu22.04.1+deb.sury.org+1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant