Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3833,7 +3833,15 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
}

$conditions[$conditionalExprString][] = $conditionalExpression;
$specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder();
$newTypeHolder = $conditionalExpression->getTypeHolder();
if (array_key_exists($conditionalExprString, $specifiedExpressions) && $specifiedExpressions[$conditionalExprString]->getCertainty()->yes() && $newTypeHolder->getCertainty()->yes()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unclear to me why we're only concerned about Yes & Yes.

Looking at this code, the last newTypeHolder override all the previous one if there is some Maybe certainty in the game.

Is this condition clear to you @staabm ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Updating the code to

if (array_key_exists($conditionalExprString, $specifiedExpressions)) {

does not creates any failure. I feel like we're lacking of coverage. But I never worked with ExpressionTypeHolder...

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we're lacking of coverage.

might make sense to add tests before the code changes with this PR would be helpful

$specifiedExpressions[$conditionalExprString] = ExpressionTypeHolder::createYes(
Copy link
Contributor

Choose a reason for hiding this comment

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

ExpressionTypeHolder has a method and

public function and(self $other): self
	{
		if ($this->type === $other->type || $this->type->equals($other->type)) {
			if ($this->certainty->and($other->certainty)->yes()) {
				return $this;
			}

			if ($this->certainty->maybe()) {
				return $this;
			}

			return $other;
		}

		return new self(
			$this->expr,
			TypeCombinator::union($this->type, $other->type),
			$this->certainty->and($other->certainty),
		);
	}

Should we introduce a new method for ExpressionTypeHolder which does the intersect ?

Copy link
Contributor

Choose a reason for hiding this comment

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

looking at your quoted function it feels like it implements or, not and (wrongly named)?

$newTypeHolder->getExpr(),
TypeCombinator::intersect($specifiedExpressions[$conditionalExprString]->getType(), $newTypeHolder->getType()),
Copy link
Contributor

Choose a reason for hiding this comment

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

This might add a lot of possibly useless intersect calls.

For instance if the next newTypeHolder does not have a certainty of Yes. we will override the value.

Might be better to check it first by computing all the newTypeHolder and

  • if they all have a yes certainty we intersect them
  • if not, we take the last one

WDYT @staabm ?

Copy link
Contributor

Choose a reason for hiding this comment

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

in case we can save intersect calls its a way we should at least try.

);
} else {
$specifiedExpressions[$conditionalExprString] = $newTypeHolder;
}
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-14211.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types=1);

namespace Bug14211;

use function PHPStan\Testing\assertType;

/** @param array<mixed> $data */
function doSomething(array $data): bool {

if (!isset($data['x']))
return false;

$m = isset($data['y']);

if ($m) {
assertType('true', $m);
}
assertType('bool', $m);

if ($m) {
assertType('true', $m);
}

return true;
}
Loading