Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Implement TypeSpecifierContext->getReturnType() #3881

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

Draft
staabm wants to merge 7 commits into phpstan:2.1.x
base: 2.1.x
Choose a base branch
Loading
from staabm:testwith

Conversation

@staabm
Copy link
Contributor

@staabm staabm commented Mar 14, 2025
edited
Loading

trying a minimal approach to see whether the idea works as a whole


for expression like

if (doFoo() > 0)

we want to pass the context specific return-type information into type-specifying extensions so they can decide not just on true/truethy/false/falsy but also on "remaining return type values" (e.g. preg_match returns 0|1|false)

this should help to reduce the number of hardcoded hacks within the TypeSpecifier und move over some cases into extensions (which also allows 3rd party extensions todo similar things, as they can't hard-code hack the TypeSpecifier)

ondrej quoted:

There should be a new method on the context, can be getReturnType

Let’s say we have doFoo(): int
if (doFoo() > 0), in the then branch, this should return int<1, max>, in the else branch it should return int<min, 0>

Comment on lines -21 to -22
/** @var self[] */
private static array $registry;
Copy link
Contributor Author

@staabm staabm Mar 14, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop the caching, as context now contains an additional Type (state)

on my machine this did not make a difference in performance

Comment on lines +433 to +436
&& in_array(strtolower((string) $expr->right->name), ['preg_match', 'strlen', 'mb_strlen'], true)
) {
Copy link
Contributor Author

@staabm staabm Mar 14, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atm I whitelisted the cases which I dropped above to keep my sanity.
I feel we can drop it after we removed the hard-coded case list and moved the logic into extensions

Copy link
Contributor Author

staabm commented Mar 14, 2025
edited
Loading

the case failling in webmozart-assert is effectively

<?php
declare(strict_types=1);
class HelloWorld
{
	public function sayHello($m): void
	{
		\PHPStan\dumpType(
			is_string($m)
			&& strlen($m) >= 1
			&& strlen($m) <= 0
		);
	}
}

not sure what todo with it atm.

edit: fixed with 19749e8

Copy link
Contributor Author

staabm commented Mar 15, 2025
edited
Loading

debugging some more, I think more precisely the problem is here:

<?php
function sayHello(string $m): void
{
	if (strlen($m) >= 1) {
		if (strlen($m) <= 0) {
			\PHPStan\dumpType(
				$m
			);
		}
	}
}

it dumps non-empty-string instead of *NEVER*

edit: fixed with 19749e8

Comment on lines +52 to +57
$returnType = $context->getReturnType();
if ($returnType instanceof NeverType) {
return $this->typeSpecifier->create($args[0]->value, $returnType, $context->negate(), $scope);
}
Copy link
Contributor Author

@staabm staabm Mar 15, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might move this into TypeSpecifier instead.

when $callType instanceof NeverType turn all non-by-ref-args into *NEVER*.
not sure its required though.

@staabm staabm marked this pull request as ready for review March 15, 2025 09:04
Copy link
Collaborator

This pull request has been marked as ready for review.

Copy link
Contributor Author

staabm commented Mar 15, 2025

//cc @herndlm

}
$newScope = $scope->filterBySpecifiedTypes($result);
$callType = $newScope->getType($expr->right);
$newContext = $context->newWithReturnType($callType);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future we could use this new api also for == and === not just < I think.

Copy link
Member

Yeah, this works, but I'd like to "burn more bridges" in the first PR about this change. I don't mean "breaking backward compatibility", but what I mean is to use the new code path as much as possible.

  1. TypeSpecifierContext should be created with the return type. So the constructor should accept ?Type, not ?int $value.
  2. We probably don't need the constants with bitmasks at all. The createTrue etc. methods should simply create the object with a type that makes sense and continues to be correct.
  3. The negate method is tricky. Maybe we need to create the object with both the "if" path type and the "else" path type.

Copy link
Member

I use this approach often. It forces the code to be correct and generally uncovers any flaws about the changes 😊

@staabm staabm marked this pull request as draft July 17, 2025 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /