Skip to content
Merged
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
19 changes: 0 additions & 19 deletions src/Analyser/ExprHandler/Helper/FuncCallScopeEffectsHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
use PHPStan\Analyser\MutatingScope;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Node\Expr\NativeTypeExpr;
use PHPStan\Node\Expr\PossiblyImpureCallExpr;
use PHPStan\Node\InvalidateExprNode;
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
use PHPStan\Reflection\FunctionReflection;
Expand All @@ -45,7 +43,6 @@
use function array_slice;
use function count;
use function in_array;
use function sprintf;
use function str_starts_with;

/**
Expand All @@ -65,8 +62,6 @@ final class FuncCallScopeEffectsHelper

public function __construct(
private OutputBufferHelper $outputBufferHelper,
#[AutowiredParameter]
private bool $rememberPossiblyImpureFunctionValues,
private AssignHandler $assignHandler,
)
{
Expand Down Expand Up @@ -129,20 +124,6 @@ public function applyCallScopeEffects(NodeScopeResolver $nodeScopeResolver, Stmt
$scope = $scope->invalidateExpression(new Variable('this'), true, null, $keepPropertyFetches);
}

if (
$functionReflection !== null
&& $parametersAcceptor !== null
&& $this->rememberPossiblyImpureFunctionValues
&& $functionReflection->hasSideEffects()->maybe()
&& !$functionReflection->isBuiltin()
) {
$scope = $scope->assignExpression(
new PossiblyImpureCallExpr($normalizedExpr, $normalizedExpr, sprintf('%s()', $functionReflection->getName())),
$parametersAcceptor->getReturnType(),
new MixedType(),
);
}

if (
$functionReflection !== null
&& in_array($functionReflection->getName(), ['json_encode', 'json_decode'], true)
Expand Down
22 changes: 0 additions & 22 deletions src/Analyser/ExprHandler/MethodCallHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use PHPStan\Analyser\ExprHandler\Helper\MethodCallReturnTypeHelper;
use PHPStan\Analyser\ExprHandler\Helper\MethodThrowPointHelper;
use PHPStan\Analyser\Generics\ClosureSignatureInference;
use PHPStan\Analyser\Generics\TemplateArgumentFrame;
use PHPStan\Analyser\ImpurePoint;
use PHPStan\Analyser\InternalThrowPoint;
use PHPStan\Analyser\MutatingScope;
Expand All @@ -37,7 +36,6 @@
use PHPStan\Dependency\DependencyTypes;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Node\Expr\PossiblyImpureCallExpr;
use PHPStan\Node\InvalidateExprNode;
use PHPStan\Reflection\Callables\SimpleImpurePoint;
use PHPStan\Reflection\ExtendedMethodReflection;
Expand All @@ -60,7 +58,6 @@
use function array_map;
use function array_merge;
use function count;
use function sprintf;
use function strtolower;

/**
Expand Down Expand Up @@ -327,25 +324,6 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
if ($methodReflection->getName() === '__construct' || $methodReflection->hasSideEffects()->yes()) {
$nodeScopeResolver->callNodeCallback($nodeCallback, new InvalidateExprNode($normalizedExpr->var), $scope, $storage);
$scope = $scope->invalidateExpression($normalizedExpr->var, true, $methodReflection->getDeclaringClass(), $methodReflection->isStatic());
} elseif ($this->rememberPossiblyImpureFunctionValues && $methodReflection->hasSideEffects()->maybe() && !$methodReflection->getDeclaringClass()->isBuiltin()) {
// the remembered call value and the @phpstan-self-out type are
// generic-sensitive: resolve them from the type-driven acceptor
// processArgs() selected (generics resolved against the actual arg
// types), falling back to the structural acceptor for dynamic callees.
$acceptorForGenerics = $resolvedParametersAcceptor ?? $parametersAcceptor;
$rememberedType = TemplateArgumentFrame::returnTypeOfCall($acceptorForGenerics, $scope, $expr);
if ($varResult->containsNullsafe() && TypeCombinator::containsNull($calledOnType)) {
// a call on a nullsafe chain whose receiver is nullable
// short-circuits to null - the tracked entry is keyed by the
// whole chain, so it must remember the propagated type, or a
// later ?? over the same chain reads it as never-null
$rememberedType = TypeCombinator::addNull($rememberedType);
}
$scope = $scope->assignExpression(
new PossiblyImpureCallExpr($normalizedExpr, $normalizedExpr->var, sprintf('%s::%s()', $methodReflection->getDeclaringClass()->getDisplayName(), $methodReflection->getName())),
$rememberedType,
new MixedType(),
);
}
if (!$methodReflection->isStatic()) {
$selfOutType = $methodReflection->getSelfOutType();
Expand Down
21 changes: 0 additions & 21 deletions src/Analyser/ExprHandler/StaticCallHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use PHPStan\Analyser\ExprHandler\Helper\EarlyTerminatingCallHelper;
use PHPStan\Analyser\ExprHandler\Helper\MethodCallReturnTypeHelper;
use PHPStan\Analyser\ExprHandler\Helper\MethodThrowPointHelper;
use PHPStan\Analyser\Generics\TemplateArgumentFrame;
use PHPStan\Analyser\ImpurePoint;
use PHPStan\Analyser\InternalThrowPoint;
use PHPStan\Analyser\MutatingScope;
Expand All @@ -36,7 +35,6 @@
use PHPStan\Dependency\DependencyTypes;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Node\Expr\PossiblyImpureCallExpr;
use PHPStan\Reflection\Callables\SimpleImpurePoint;
use PHPStan\Reflection\ExtendedMethodReflection;
use PHPStan\Reflection\ExtendedParametersAcceptor;
Expand All @@ -62,7 +60,6 @@
use function array_merge;
use function count;
use function in_array;
use function sprintf;
use function strtolower;

/**
Expand Down Expand Up @@ -391,24 +388,6 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
) {
// a static method never receives $this, so property fetches on it survive
$scope = $scope->invalidateExpression(new Variable('this'), true, $methodReflection->getDeclaringClass(), $methodReflection->isStatic());
} elseif (
$expr->class instanceof Name
&& $methodReflection !== null
&& $this->rememberPossiblyImpureFunctionValues
&& $scope->isInClass()
&& $scope->getClassReflection()->is($methodReflection->getDeclaringClass()->getName())
&& $methodReflection->hasSideEffects()->maybe()
&& !$methodReflection->getDeclaringClass()->isBuiltin()
) {
// the remembered call value is generic-sensitive: resolve it from the
// type-driven acceptor processArgs() selected (generics resolved against
// the actual arg types), falling back to the structural acceptor.
$acceptorForGenerics = $resolvedParametersAcceptor ?? $parametersAcceptor;
$scope = $scope->assignExpression(
new PossiblyImpureCallExpr($normalizedExpr, new Variable('this'), sprintf('%s::%s()', $methodReflection->getDeclaringClass()->getDisplayName(), $methodReflection->getName())),
TemplateArgumentFrame::returnTypeOfCall($acceptorForGenerics, $scope, $expr),
new MixedType(),
);
}

if (
Expand Down
60 changes: 0 additions & 60 deletions src/Analyser/ExprHandler/Virtual/PossiblyImpureCallExprHandler.php

This file was deleted.

123 changes: 2 additions & 121 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
use PHPStan\Node\Expr\OriginalForeachKeyExpr;
use PHPStan\Node\Expr\OriginalForeachValueExpr;
use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
use PHPStan\Node\Expr\PossiblyImpureCallExpr;
use PHPStan\Node\Expr\PropertyInitializationExpr;
use PHPStan\Node\Expr\SetExistingOffsetValueTypeExpr;
use PHPStan\Node\IssetExpr;
Expand Down Expand Up @@ -114,9 +113,7 @@
use function array_pop;
use function array_shift;
use function array_slice;
use function array_unique;
use function array_values;
use function assert;
use function count;
use function ctype_alnum;
use function explode;
Expand Down Expand Up @@ -847,114 +844,6 @@ public function getMaybeDefinedVariables(): array
return $variables;
}

/**
* @return list<string>
*/
public function findPossiblyImpureCallDescriptions(Expr $expr): array
{
$nodeFinder = new NodeFinder();
$callExprDescriptions = [];
$foundCallExprMatch = false;
$matchedCallExprKeys = [];
foreach ($this->expressionTypes as $holder) {
$holderExpr = $holder->getExpr();
if (!$holderExpr instanceof PossiblyImpureCallExpr) {
continue;
}

$callExprKey = $this->getNodeKey($holderExpr->callExpr);

$found = $nodeFinder->findFirst([$expr], function (Node $node) use ($callExprKey): bool {
if (!$node instanceof Expr) {
return false;
}

return $this->getNodeKey($node) === $callExprKey;
});

if ($found === null) {
continue;
}

$foundCallExprMatch = true;
$matchedCallExprKeys[$callExprKey] = true;

// Only show the tip when the scope's type for the call expression
// differs from the declared return type, meaning control flow
// narrowing affected the type (the cached value was narrowed).
assert($found instanceof Expr);
$scopeType = $this->getType($found);
$declaredReturnType = $holder->getType();
if ($declaredReturnType->isSuperTypeOf($scopeType)->yes() && $scopeType->isSuperTypeOf($declaredReturnType)->yes()) {
continue;
}

$callExprDescriptions[] = $holderExpr->getCallDescription();
}

// If the first pass found a callExpr in the error expression but
// filtered it out (return type wasn't narrowed), the error is
// explained by the return type alone - skip the fallback.
if ($foundCallExprMatch && count($callExprDescriptions) === 0) {
return [];
}

// Second pass: match by impactedExpr for cases where a maybe-impure method
// on an object didn't invalidate it, but a different method's return
// value was narrowed on that object.
// Skip when the expression itself is a direct method/static call -
// those are passed by ImpossibleCheckType rules where the error is
// about the call's arguments, not about object state.
if (!($expr instanceof Expr\MethodCall || $expr instanceof Expr\StaticCall)) {
$impactedExprDescriptions = [];
foreach ($this->expressionTypes as $holder) {
$holderExpr = $holder->getExpr();
if (!$holderExpr instanceof PossiblyImpureCallExpr) {
continue;
}

$impactedExprKey = $this->getNodeKey($holderExpr->impactedExpr);

// Skip if impactedExpr is the same as callExpr (function calls)
if ($impactedExprKey === $this->getNodeKey($holderExpr->callExpr)) {
continue;
}

// Skip if this entry's callExpr was already matched in the first pass
$callExprKey = $this->getNodeKey($holderExpr->callExpr);
if (isset($matchedCallExprKeys[$callExprKey])) {
continue;
}

$found = $nodeFinder->findFirst([$expr], function (Node $node) use ($impactedExprKey): bool {
if (!$node instanceof Expr) {
return false;
}

return $this->getNodeKey($node) === $impactedExprKey;
});

if ($found === null) {
continue;
}

$impactedExprDescriptions[] = $holderExpr->getCallDescription();
}

// Prefer impactedExpr matches (intermediate calls that could have
// invalidated the object) over callExpr matches
if (count($impactedExprDescriptions) > 0) {
return array_values(array_unique($impactedExprDescriptions));
}
}

if (count($callExprDescriptions) > 0) {
return array_values(array_unique($callExprDescriptions));
}

return [];
}

private function isGlobalVariable(string $variableName): bool
{
return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true);
Expand Down Expand Up @@ -1537,9 +1426,6 @@ public function getDifferingVariableRoots(self $other): ?array
];
foreach ($tables as [$ours, $theirs]) {
foreach ($ours as $key => $holder) {
if ($holder->getExpr() instanceof PossiblyImpureCallExpr) {
continue;
}
$theirHolder = $theirs[$key] ?? null;
if ($theirHolder !== null && ($theirHolder === $holder || $holder->equals($theirHolder))) {
continue;
Expand All @@ -1553,7 +1439,7 @@ public function getDifferingVariableRoots(self $other): ?array
}
}
foreach ($theirs as $key => $holder) {
if (isset($ours[$key]) || $holder->getExpr() instanceof PossiblyImpureCallExpr) {
if (isset($ours[$key])) {
continue;
}
$variables = self::getVariablesOfExpressionKey($key);
Expand All @@ -1576,12 +1462,7 @@ public function getDifferingVariableRoots(self $other): ?array
}
$variables = self::getVariablesOfExpressionKey($key);
if ($variables === null) {
foreach ($holders as $holder) {
if (!$holder->getTypeHolder()->getExpr() instanceof PossiblyImpureCallExpr) {
return null;
}
}
continue;
return null;
}
foreach ($variables as $variable) {
$roots[$variable] = true;
Expand Down
2 changes: 1 addition & 1 deletion src/Analyser/ScopeOps.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class ScopeOps
// parameter original-value markers (__phpstanOriginalForeachKey etc.) hide a
// synthesized Variable child on purpose - reassigning the variable must
// invalidate them through containment - so they can never be listed here.
private const COMPOSITIONAL_VIRTUAL_KEY_PREFIXES = ['__phpstanForeachValueByRef(', '__phpstanIntertwinedVariableByReference(', '__phpstanPossiblyImpure(', '__phpstanPropertyInitialization(', '__phpstanRemembered('];
private const COMPOSITIONAL_VIRTUAL_KEY_PREFIXES = ['__phpstanForeachValueByRef(', '__phpstanIntertwinedVariableByReference(', '__phpstanPropertyInitialization(', '__phpstanRemembered('];

/**
* Mirrors MutatingScope::getNodeKey().
Expand Down
Loading
Loading