Skip to content

Commit e38aadd

Browse files
ondrejmirtesclaude
andcommitted
Remove the superseded ScopeOps invalidation helpers after the rebase
The branch answers invalidation checks from the per-holder index of contained node keys in MutatingScope, so the extracted invalidateExpressionEntries/shouldInvalidateExpression/ containsExpressionToInvalidate/buildTypeSpecifications helpers have no callers left. Also drop the baseline entries for the unused-use closure errors that upstream's include/require handling (#6056) resolved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VktvX3FRdnSsL66xGtiUh8
1 parent 9d3d921 commit e38aadd

3 files changed

Lines changed: 6 additions & 267 deletions

File tree

phpstan-baseline.neon

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,6 @@ parameters:
126126
count: 1
127127
path: src/Collectors/Registry.php
128128

129-
-
130-
rawMessage: Anonymous function has an unused use $container.
131-
identifier: closure.unusedUse
132-
count: 1
133-
path: src/Command/CommandHelper.php
134-
135129
-
136130
rawMessage: 'Call to static method expand() of internal class Nette\DI\Helpers from outside its root namespace Nette.'
137131
identifier: staticMethod.internalClass
@@ -747,12 +741,6 @@ parameters:
747741
count: 1
748742
path: src/Testing/LevelsTestCase.php
749743

750-
-
751-
rawMessage: Anonymous function has an unused use $container.
752-
identifier: closure.unusedUse
753-
count: 1
754-
path: src/Testing/PHPStanTestCase.php
755-
756744
-
757745
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
758746
identifier: phpstanApi.instanceofType

src/Analyser/MutatingScope.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3574,7 +3574,7 @@ private function shouldInvalidateExpression(string $exprStringToInvalidate, Expr
35743574
return true;
35753575
}
35763576

3577-
public function isPrivatePropertyOfDifferentClass(Expr $expr, ClassReflection $invalidatingClass): bool
3577+
private function isPrivatePropertyOfDifferentClass(Expr $expr, ClassReflection $invalidatingClass): bool
35783578
{
35793579
if ($expr instanceof Expr\StaticPropertyFetch || $expr instanceof PropertyFetch) {
35803580
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);

src/Analyser/ScopeOps.php

Lines changed: 5 additions & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,13 @@
44

55
use PhpParser\Node;
66
use PhpParser\Node\Expr;
7-
use PhpParser\Node\Expr\Array_;
87
use PhpParser\Node\Expr\FuncCall;
98
use PhpParser\Node\Expr\MethodCall;
10-
use PhpParser\Node\Expr\PropertyFetch;
119
use PhpParser\Node\Expr\Variable;
12-
use PhpParser\Node\Name;
1310
use PhpParser\NodeFinder;
14-
use PHPStan\Node\Expr\IntertwinedVariableByReferenceWithExpr;
1511
use PHPStan\Node\Printer\ExprPrinter;
1612
use PHPStan\Node\VirtualNode;
1713
use PHPStan\Parser\ArrayMapArgVisitor;
18-
use PHPStan\Reflection\ClassReflection;
1914
use PHPStan\Reflection\FunctionReflection;
2015
use PHPStan\Reflection\MethodReflection;
2116
use PHPStan\Reflection\ParameterReflection;
@@ -24,15 +19,10 @@
2419
use PHPStan\Type\Type;
2520
use function array_filter;
2621
use function array_key_exists;
27-
use function array_key_first;
22+
use function array_keys;
2823
use function count;
29-
use function get_class;
3024
use function in_array;
31-
use function is_array;
3225
use function is_string;
33-
use function str_contains;
34-
use function strlen;
35-
use function usort;
3626

3727
/**
3828
* Hot scope-table operations extracted from MutatingScope.
@@ -374,7 +364,7 @@ public static function createConditionalExpressions(
374364
// branch — but it remains a valid conditional *target*, so only exclude
375365
// it from guard selection instead of dropping it entirely.
376366
$guardsToExclude = [];
377-
foreach ($differingKeys as $exprString => $unusedDiffMarker) {
367+
foreach (array_keys($differingKeys) as $exprString) {
378368
if (!array_key_exists($exprString, $theirExpressionTypes)) {
379369
continue;
380370
}
@@ -399,7 +389,7 @@ public static function createConditionalExpressions(
399389
}
400390

401391
$typeGuards = [];
402-
foreach ($differingKeys as $exprString => $unusedDiffMarker) {
392+
foreach (array_keys($differingKeys) as $exprString) {
403393
if (!array_key_exists($exprString, $newVariableTypes)) {
404394
continue;
405395
}
@@ -443,7 +433,7 @@ public static function createConditionalExpressions(
443433
$guardIsSuperTypeOfTheirExprCache = [];
444434
$theirExprIsSuperTypeOfGuardCache = [];
445435

446-
foreach ($differingKeys as $exprString => $unusedDiffMarker) {
436+
foreach (array_keys($differingKeys) as $exprString) {
447437
if (!array_key_exists($exprString, $newVariableTypes)) {
448438
continue;
449439
}
@@ -508,7 +498,7 @@ public static function createConditionalExpressions(
508498
}
509499
}
510500

511-
foreach ($differingKeys as $exprString => $unusedDiffMarker) {
501+
foreach (array_keys($differingKeys) as $exprString) {
512502
if (!array_key_exists($exprString, $mergedExpressionTypes)) {
513503
continue;
514504
}
@@ -526,130 +516,6 @@ public static function createConditionalExpressions(
526516
return $conditionalExpressions;
527517
}
528518

529-
/**
530-
* Depth-first pre-order search for the invalidated expression, replacing a
531-
* NodeFinder::findFirst() call - this runs for every (stored expression,
532-
* invalidated expression) pair whose keys pass the substring pre-filter,
533-
* so the traverser/visitor machinery overhead was significant.
534-
*
535-
* @param class-string<Expr> $expressionToInvalidateClass
536-
*/
537-
private static function containsExpressionToInvalidate(Scope $scope, ExprPrinter $exprPrinter, Node $node, string $expressionToInvalidateClass, string $exprStringToInvalidate): bool
538-
{
539-
if (
540-
$exprStringToInvalidate === '$this'
541-
&& $node instanceof Name
542-
&& (
543-
in_array($node->toLowerString(), ['self', 'static', 'parent'], true)
544-
|| ($scope->getClassReflection() !== null && $scope->getClassReflection()->is($scope->resolveName($node)))
545-
)
546-
) {
547-
return true;
548-
}
549-
550-
if (
551-
$node instanceof $expressionToInvalidateClass
552-
&& self::nodeKey($node, $exprPrinter) === $exprStringToInvalidate
553-
) {
554-
return true;
555-
}
556-
557-
foreach ($node->getSubNodeNames() as $subNodeName) {
558-
$subNode = $node->$subNodeName;
559-
if ($subNode instanceof Node) {
560-
if (self::containsExpressionToInvalidate($scope, $exprPrinter, $subNode, $expressionToInvalidateClass, $exprStringToInvalidate)) {
561-
return true;
562-
}
563-
} elseif (is_array($subNode)) {
564-
foreach ($subNode as $subNodeItem) {
565-
if (
566-
$subNodeItem instanceof Node
567-
&& self::containsExpressionToInvalidate($scope, $exprPrinter, $subNodeItem, $expressionToInvalidateClass, $exprStringToInvalidate)
568-
) {
569-
return true;
570-
}
571-
}
572-
}
573-
}
574-
575-
return false;
576-
}
577-
578-
/**
579-
* The scan of MutatingScope::invalidateExpression(): computes the tables
580-
* with the invalidated entries removed, or null when nothing changed.
581-
*
582-
* @param array<string, ExpressionTypeHolder> $expressionTypes
583-
* @param array<string, ExpressionTypeHolder> $nativeExpressionTypes
584-
* @param array<string, ConditionalExpressionHolder[]> $conditionalExpressions
585-
* @return array{array<string, ExpressionTypeHolder>, array<string, ExpressionTypeHolder>, array<string, ConditionalExpressionHolder[]>}|null
586-
*/
587-
public static function invalidateExpressionEntries(
588-
MutatingScope $scope,
589-
ExprPrinter $exprPrinter,
590-
string $exprStringToInvalidate,
591-
Expr $expressionToInvalidate,
592-
bool $requireMoreCharacters,
593-
?ClassReflection $invalidatingClass,
594-
array $expressionTypes,
595-
array $nativeExpressionTypes,
596-
array $conditionalExpressions,
597-
): ?array
598-
{
599-
$invalidated = false;
600-
601-
foreach ($expressionTypes as $exprString => $exprTypeHolder) {
602-
$exprExpr = $exprTypeHolder->getExpr();
603-
if (!self::shouldInvalidateExpression($scope, $exprPrinter, $exprStringToInvalidate, $expressionToInvalidate, $exprExpr, (string) $exprString, $requireMoreCharacters, $invalidatingClass)) {
604-
continue;
605-
}
606-
607-
unset($expressionTypes[$exprString]);
608-
unset($nativeExpressionTypes[$exprString]);
609-
$invalidated = true;
610-
}
611-
612-
$newConditionalExpressions = [];
613-
foreach ($conditionalExpressions as $conditionalExprString => $holders) {
614-
if (count($holders) === 0) {
615-
continue;
616-
}
617-
$firstExpr = $holders[array_key_first($holders)]->getTypeHolder()->getExpr();
618-
if (self::shouldInvalidateExpression($scope, $exprPrinter, $exprStringToInvalidate, $expressionToInvalidate, $firstExpr, self::nodeKey($firstExpr, $exprPrinter), $requireMoreCharacters, $invalidatingClass)) {
619-
$invalidated = true;
620-
continue;
621-
}
622-
$filteredHolders = [];
623-
foreach ($holders as $key => $holder) {
624-
$shouldKeep = true;
625-
$conditionalTypeHolders = $holder->getConditionExpressionTypeHolders();
626-
foreach ($conditionalTypeHolders as $conditionalTypeHolderExprString => $conditionalTypeHolder) {
627-
if (self::shouldInvalidateExpression($scope, $exprPrinter, $exprStringToInvalidate, $expressionToInvalidate, $conditionalTypeHolder->getExpr(), (string) $conditionalTypeHolderExprString, invalidatingClass: $invalidatingClass)) {
628-
$invalidated = true;
629-
$shouldKeep = false;
630-
break;
631-
}
632-
}
633-
if (!$shouldKeep) {
634-
continue;
635-
}
636-
637-
$filteredHolders[$key] = $holder;
638-
}
639-
if (count($filteredHolders) <= 0) {
640-
continue;
641-
}
642-
643-
$newConditionalExpressions[$conditionalExprString] = $filteredHolders;
644-
}
645-
646-
if (!$invalidated) {
647-
return null;
648-
}
649-
650-
return [$expressionTypes, $nativeExpressionTypes, $newConditionalExpressions];
651-
}
652-
653519
/**
654520
* The scan of MutatingScope::invalidateMethodsOnExpression(): drops tracked
655521
* MethodCall expressions whose var matches the invalidated key, or returns
@@ -689,76 +555,6 @@ public static function invalidateMethodsOnExpression(
689555
return [$expressionTypes, $nativeExpressionTypes];
690556
}
691557

692-
/**
693-
* Mirrors the former MutatingScope::shouldInvalidateExpression().
694-
*/
695-
public static function shouldInvalidateExpression(MutatingScope $scope, ExprPrinter $exprPrinter, string $exprStringToInvalidate, Expr $exprToInvalidate, Expr $expr, string $exprString, bool $requireMoreCharacters = false, ?ClassReflection $invalidatingClass = null): bool
696-
{
697-
if (
698-
$expr instanceof IntertwinedVariableByReferenceWithExpr
699-
&& $exprToInvalidate instanceof Variable
700-
&& is_string($exprToInvalidate->name)
701-
&& (
702-
$expr->getVariableName() === $exprToInvalidate->name
703-
|| self::getIntertwinedRefRootVariableName($expr->getExpr()) === $exprToInvalidate->name
704-
|| self::getIntertwinedRefRootVariableName($expr->getAssignedExpr()) === $exprToInvalidate->name
705-
)
706-
) {
707-
return false;
708-
}
709-
710-
if ($requireMoreCharacters && $exprStringToInvalidate === $exprString) {
711-
return false;
712-
}
713-
714-
// Variables will not contain traversable expressions. skip the NodeFinder overhead
715-
if ($expr instanceof Variable && is_string($expr->name) && !$requireMoreCharacters) {
716-
return $exprStringToInvalidate === $exprString;
717-
}
718-
719-
// getNodeKey() is the pretty-printed expression, and the standard printer is
720-
// compositional: the key of any sub-expression appears verbatim as a substring of
721-
// the key of the expression containing it. So if the invalidated expression's key
722-
// does not appear anywhere in this expression's key, this expression cannot contain
723-
// it and we can skip the expensive AST traversal below.
724-
// Carve-outs where that invariant does not hold:
725-
// - '$this' is special-cased in the visitor to also match self/static/parent,
726-
// - PHPStan's virtual nodes (printed as '__phpstan…') use non-compositional printers
727-
// (e.g. a wrapped variable is printed by name, not as '$name'),
728-
// - keys carrying a getNodeKey() suffix ('/*…*/') are not plain substrings.
729-
if (
730-
$exprStringToInvalidate !== '$this'
731-
&& !str_contains($exprStringToInvalidate, '__phpstan')
732-
&& !str_contains($exprStringToInvalidate, '/*')
733-
&& !str_contains($exprString, '__phpstan')
734-
&& !str_contains($exprString, $exprStringToInvalidate)
735-
) {
736-
return false;
737-
}
738-
739-
if (!self::containsExpressionToInvalidate($scope, $exprPrinter, $expr, get_class($exprToInvalidate), $exprStringToInvalidate)) {
740-
return false;
741-
}
742-
743-
if (
744-
$expr instanceof PropertyFetch
745-
&& $requireMoreCharacters
746-
&& $scope->isReadonlyPropertyFetch($expr, false)
747-
) {
748-
return false;
749-
}
750-
751-
if (
752-
$invalidatingClass !== null
753-
&& $requireMoreCharacters
754-
&& $scope->isPrivatePropertyOfDifferentClass($expr, $invalidatingClass)
755-
) {
756-
return false;
757-
}
758-
759-
return true;
760-
}
761-
762558
public static function getIntertwinedRefRootVariableName(Expr $expr): ?string
763559
{
764560
if ($expr instanceof Variable && is_string($expr->name)) {
@@ -770,51 +566,6 @@ public static function getIntertwinedRefRootVariableName(Expr $expr): ?string
770566
return null;
771567
}
772568

773-
/**
774-
* The sorted type-specification list of MutatingScope::filterBySpecifiedTypes().
775-
*
776-
* @param array<string|int, array{Expr, Type}> $sureTypes
777-
* @param array<string|int, array{Expr, Type}> $sureNotTypes
778-
* @return list<array{sure: bool, exprString: string, expr: Expr, type: Type}>
779-
*/
780-
public static function buildTypeSpecifications(array $sureTypes, array $sureNotTypes): array
781-
{
782-
$typeSpecifications = [];
783-
foreach ($sureTypes as $exprString => [$expr, $type]) {
784-
if ($expr instanceof Node\Scalar || $expr instanceof Array_ || $expr instanceof Expr\UnaryMinus && $expr->expr instanceof Node\Scalar) {
785-
continue;
786-
}
787-
$typeSpecifications[] = [
788-
'sure' => true,
789-
'exprString' => (string) $exprString,
790-
'expr' => $expr,
791-
'type' => $type,
792-
];
793-
}
794-
foreach ($sureNotTypes as $exprString => [$expr, $type]) {
795-
if ($expr instanceof Node\Scalar || $expr instanceof Array_ || $expr instanceof Expr\UnaryMinus && $expr->expr instanceof Node\Scalar) {
796-
continue;
797-
}
798-
$typeSpecifications[] = [
799-
'sure' => false,
800-
'exprString' => (string) $exprString,
801-
'expr' => $expr,
802-
'type' => $type,
803-
];
804-
}
805-
806-
usort($typeSpecifications, static function (array $a, array $b): int {
807-
$length = strlen($a['exprString']) - strlen($b['exprString']);
808-
if ($length !== 0) {
809-
return $length;
810-
}
811-
812-
return $b['sure'] - $a['sure']; // @phpstan-ignore minus.leftNonNumeric, minus.rightNonNumeric
813-
});
814-
815-
return $typeSpecifications;
816-
}
817-
818569
/**
819570
* The conditional-expressions fixed-point matching of
820571
* MutatingScope::filterBySpecifiedTypes().

0 commit comments

Comments
 (0)