Skip to content

Commit

Permalink
MethodCall: mark virtual nullsafe call with attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored Sep 1, 2023
1 parent 3e03e9d commit 0d3dc89
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2213,7 +2213,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
} elseif ($expr instanceof Expr\NullsafeMethodCall) {
$nonNullabilityResult = $this->ensureShallowNonNullability($scope, $scope, $expr->var);
$exprResult = $this->processExprNode(new MethodCall($expr->var, $expr->name, $expr->args, $expr->getAttributes()), $nonNullabilityResult->getScope(), $nodeCallback, $context);
$exprResult = $this->processExprNode(new MethodCall($expr->var, $expr->name, $expr->args, array_merge($expr->getAttributes(), ['virtualNullsafeMethodCall' => true])), $nonNullabilityResult->getScope(), $nodeCallback, $context);
$scope = $this->revertNonNullability($exprResult->getScope(), $nonNullabilityResult->getSpecifiedExpressions());

return new ExpressionResult(
Expand Down Expand Up @@ -2368,7 +2368,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
}
} elseif ($expr instanceof Expr\NullsafePropertyFetch) {
$nonNullabilityResult = $this->ensureShallowNonNullability($scope, $scope, $expr->var);
$exprResult = $this->processExprNode(new PropertyFetch($expr->var, $expr->name, $expr->getAttributes()), $nonNullabilityResult->getScope(), $nodeCallback, $context);
$exprResult = $this->processExprNode(new PropertyFetch($expr->var, $expr->name, array_merge($expr->getAttributes(), ['virtualNullsafePropertyFetch' => true])), $nonNullabilityResult->getScope(), $nodeCallback, $context);
$scope = $this->revertNonNullability($exprResult->getScope(), $nonNullabilityResult->getSpecifiedExpressions());

return new ExpressionResult(
Expand Down
56 changes: 56 additions & 0 deletions tests/PHPStan/Rules/Methods/VirtualNullsafeMethodCallTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<Rule>
*/
class VirtualNullsafeMethodCallTest extends RuleTestCase
{

/**
* @return Rule<MethodCall>
*/
protected function getRule(): Rule
{
return new /** @implements Rule<MethodCall> */ class implements Rule {

public function getNodeType(): string
{
return MethodCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if ($node->getAttribute('virtualNullsafeMethodCall') === true) {
return [RuleErrorBuilder::message('Nullable method call detected')->identifier('')->build()];
}

return [RuleErrorBuilder::message('Regular method call detected')->identifier('')->build()];
}

};
}

public function testAttribute(): void
{
$this->analyse([ __DIR__ . '/data/virtual-nullsafe-method-call.php'], [
[
'Regular method call detected',
3,
],
[
'Nullable method call detected',
4,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php // lint >= 8.0

$foo->regularCall();
$foo?->nullsafeCall();
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Properties;

use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<Rule>
*/
class VirtualNullsafePropertyFetchTest extends RuleTestCase
{

/**
* @return Rule<PropertyFetch>
*/
protected function getRule(): Rule
{
return new /** @implements Rule<PropertyFetch> */ class implements Rule {

public function getNodeType(): string
{
return PropertyFetch::class;
}

public function processNode(Node $node, Scope $scope): array
{
if ($node->getAttribute('virtualNullsafePropertyFetch') === true) {
return [RuleErrorBuilder::message('Nullable property fetch detected')->identifier('')->build()];
}

return [RuleErrorBuilder::message('Regular property fetch detected')->identifier('')->build()];
}

};
}

public function testAttribute(): void
{
$this->analyse([ __DIR__ . '/data/virtual-nullsafe-property-fetch.php'], [
[
'Regular property fetch detected',
3,
],
[
'Nullable property fetch detected',
4,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php // lint >= 8.0

$foo->regularFetch;
$foo?->nullsafeFetch;

0 comments on commit 0d3dc89

Please sign in to comment.