Skip to content

Commit 511f5d0

Browse files
phpstan-botclaude
andcommitted
Rename specifyOnly to equality on SpecifiedTypes
Renames setSpecifyOnly()/shouldSpecifyOnly() to setEquality()/isEquality() and the backing property to $equality, tying the flag to the documented "equality assertions" concept it implements (https://phpstan.org/writing-php-code/narrowing-types#equality-assertions). The flag is set by AssertTag::isEquality() assertions and by the type-specifying extensions that narrow argument types as a consequence of a check; in all those cases the narrowed types must not determine the check outcome in ImpossibleCheckTypeHelper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 755457a commit 511f5d0

8 files changed

Lines changed: 30 additions & 26 deletions

src/Analyser/MutatingScope.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3265,7 +3265,7 @@ public function filterByTruthyValue(Expr $expr): self
32653265
}
32663266

32673267
$specifiedTypes = $this->typeSpecifier->specifyTypesInCondition($this, $expr, TypeSpecifierContext::createTruthy());
3268-
if ($specifiedTypes->shouldSpecifyOnly()) {
3268+
if ($specifiedTypes->isEquality()) {
32693269
$specifiedTypes = $specifiedTypes->unionWith(
32703270
$this->typeSpecifier->create($expr, new ConstantBooleanType(true), TypeSpecifierContext::createTrue(), $this),
32713271
);
@@ -3287,7 +3287,7 @@ public function filterByFalseyValue(Expr $expr): self
32873287
}
32883288

32893289
$specifiedTypes = $this->typeSpecifier->specifyTypesInCondition($this, $expr, TypeSpecifierContext::createFalsey());
3290-
if ($specifiedTypes->shouldSpecifyOnly()) {
3290+
if ($specifiedTypes->isEquality()) {
32913291
$specifiedTypes = $specifiedTypes->unionWith(
32923292
$this->typeSpecifier->create($expr, new ConstantBooleanType(false), TypeSpecifierContext::createTrue(), $this),
32933293
);

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,8 +1150,8 @@ public function processStmtNode(
11501150
TypeSpecifierContext::createNull(),
11511151
);
11521152
$scope = $scope->filterBySpecifiedTypes($specifiedTypes);
1153-
if ($specifiedTypes->shouldSpecifyOnly()) {
1154-
// Statement counterpart of the specifyOnly handling in filterByTruthyValue():
1153+
if ($specifiedTypes->isEquality()) {
1154+
// Statement counterpart of the equality handling in filterByTruthyValue():
11551155
// store the call's true result so a duplicate void assertion statement is
11561156
// reported as always-true. We assign directly because void calls have no
11571157
// return value to protect, and intersecting true with void would produce never.

src/Analyser/SpecifiedTypes.php

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ final class SpecifiedTypes
1313

1414
private bool $overwrite = false;
1515

16-
private bool $specifyOnly = false;
16+
private bool $equality = false;
1717

1818
/** @var array<string, ConditionalExpressionHolder[]> */
1919
private array $newConditionalExpressionHolders = [];
@@ -53,34 +53,38 @@ public function setAlwaysOverwriteTypes(): self
5353
{
5454
$self = new self($this->sureTypes, $this->sureNotTypes);
5555
$self->overwrite = true;
56-
$self->specifyOnly = $this->specifyOnly;
56+
$self->equality = $this->equality;
5757
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
5858
$self->rootExpr = $this->rootExpr;
5959

6060
return $self;
6161
}
6262

6363
/**
64-
* Marks these SpecifiedTypes as only narrowing types, not determining
65-
* the check outcome. ImpossibleCheckTypeHelper will not use sureTypes
66-
* to report always-true/false for the check expression.
64+
* Marks these types as coming from an equality check, the same concept as
65+
* the "=Type" equality assertions documented at
66+
* https://phpstan.org/writing-php-code/narrowing-types#equality-assertions
67+
*
68+
* The narrowed types are only applied; they do not determine the check
69+
* outcome, so ImpossibleCheckTypeHelper will not use them to report
70+
* always-true/false for the check expression.
6771
*
6872
* @api
6973
*/
70-
public function setSpecifyOnly(): self
74+
public function setEquality(): self
7175
{
7276
$self = new self($this->sureTypes, $this->sureNotTypes);
7377
$self->overwrite = $this->overwrite;
74-
$self->specifyOnly = true;
78+
$self->equality = true;
7579
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
7680
$self->rootExpr = $this->rootExpr;
7781

7882
return $self;
7983
}
8084

81-
public function shouldSpecifyOnly(): bool
85+
public function isEquality(): bool
8286
{
83-
return $this->specifyOnly;
87+
return $this->equality;
8488
}
8589

8690
/**
@@ -90,7 +94,7 @@ public function setRootExpr(?Expr $rootExpr): self
9094
{
9195
$self = new self($this->sureTypes, $this->sureNotTypes);
9296
$self->overwrite = $this->overwrite;
93-
$self->specifyOnly = $this->specifyOnly;
97+
$self->equality = $this->equality;
9498
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
9599
$self->rootExpr = $rootExpr;
96100

@@ -104,7 +108,7 @@ public function setNewConditionalExpressionHolders(array $newConditionalExpressi
104108
{
105109
$self = new self($this->sureTypes, $this->sureNotTypes);
106110
$self->overwrite = $this->overwrite;
107-
$self->specifyOnly = $this->specifyOnly;
111+
$self->equality = $this->equality;
108112
$self->newConditionalExpressionHolders = $newConditionalExpressionHolders;
109113
$self->rootExpr = $this->rootExpr;
110114

@@ -156,7 +160,7 @@ public function removeExpr(string $exprString): self
156160

157161
$self = new self($sureTypes, $sureNotTypes);
158162
$self->overwrite = $this->overwrite;
159-
$self->specifyOnly = $this->specifyOnly;
163+
$self->equality = $this->equality;
160164
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
161165
$self->rootExpr = $this->rootExpr;
162166

@@ -196,8 +200,8 @@ public function intersectWith(SpecifiedTypes $other): self
196200
if ($this->overwrite && $other->overwrite) {
197201
$result = $result->setAlwaysOverwriteTypes();
198202
}
199-
if ($this->specifyOnly || $other->specifyOnly) {
200-
$result->specifyOnly = true;
203+
if ($this->equality || $other->equality) {
204+
$result->equality = true;
201205
}
202206

203207
return $result->setRootExpr($rootExpr);
@@ -236,8 +240,8 @@ public function unionWith(SpecifiedTypes $other): self
236240
if ($this->overwrite || $other->overwrite) {
237241
$result = $result->setAlwaysOverwriteTypes();
238242
}
239-
if ($this->specifyOnly || $other->specifyOnly) {
240-
$result->specifyOnly = true;
243+
if ($this->equality || $other->equality) {
244+
$result->equality = true;
241245
}
242246

243247
$conditionalExpressionHolders = $this->newConditionalExpressionHolders;
@@ -270,7 +274,7 @@ public function normalize(Scope $scope): self
270274
if ($this->overwrite) {
271275
$result = $result->setAlwaysOverwriteTypes();
272276
}
273-
$result->specifyOnly = $this->specifyOnly;
277+
$result->equality = $this->equality;
274278

275279
return $result->setRootExpr($this->rootExpr);
276280
}

src/Analyser/TypeSpecifier.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1858,7 +1858,7 @@ static function (Type $type, callable $traverse) use ($templateTypeMap, &$contai
18581858
$scope,
18591859
);
18601860
if ($containsUnresolvedTemplate || $assert->isEquality()) {
1861-
$newTypes = $newTypes->setSpecifyOnly();
1861+
$newTypes = $newTypes->setEquality();
18621862
}
18631863
$types = $types !== null ? $types->unionWith($newTypes) : $newTypes;
18641864

src/Rules/Comparison/ImpossibleCheckTypeHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ public function findSpecifiedType(
273273
return null;
274274
}
275275

276-
if ($specifiedTypes->shouldSpecifyOnly()) {
276+
if ($specifiedTypes->isEquality()) {
277277
if ($scope->hasExpressionType($node)->yes()) {
278278
$nodeType = $this->treatPhpDocTypesAsCertain ? $scope->getType($node) : $scope->getNativeType($node);
279279
if ($nodeType->isTrue()->yes()) {

src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function specifyTypes(
112112
$arrayType->getIterableValueType(),
113113
$context,
114114
$scope,
115-
))->setSpecifyOnly();
115+
))->setEquality();
116116
}
117117

118118
return new SpecifiedTypes();

src/Type/Php/PregMatchTypeSpecifyingExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
8383
$matchedType,
8484
$context,
8585
$scope,
86-
)->setSpecifyOnly();
86+
)->setEquality();
8787
if ($overwrite) {
8888
$types = $types->setAlwaysOverwriteTypes();
8989
}

src/Type/Php/StrContainingTypeSpecifyingExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
8484
new IntersectionType($accessories),
8585
$context,
8686
$scope,
87-
)->setSpecifyOnly();
87+
)->setEquality();
8888
}
8989
}
9090

0 commit comments

Comments
 (0)