Skip to content

Commit 0d3dc89

Browse files
authored
MethodCall: mark virtual nullsafe call with attribute
1 parent 3e03e9d commit 0d3dc89

5 files changed

Lines changed: 122 additions & 2 deletions

File tree

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,7 +2213,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
22132213
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
22142214
} elseif ($expr instanceof Expr\NullsafeMethodCall) {
22152215
$nonNullabilityResult = $this->ensureShallowNonNullability($scope, $scope, $expr->var);
2216-
$exprResult = $this->processExprNode(new MethodCall($expr->var, $expr->name, $expr->args, $expr->getAttributes()), $nonNullabilityResult->getScope(), $nodeCallback, $context);
2216+
$exprResult = $this->processExprNode(new MethodCall($expr->var, $expr->name, $expr->args, array_merge($expr->getAttributes(), ['virtualNullsafeMethodCall' => true])), $nonNullabilityResult->getScope(), $nodeCallback, $context);
22172217
$scope = $this->revertNonNullability($exprResult->getScope(), $nonNullabilityResult->getSpecifiedExpressions());
22182218

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

23742374
return new ExpressionResult(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Methods;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Testing\RuleTestCase;
11+
12+
/**
13+
* @extends RuleTestCase<Rule>
14+
*/
15+
class VirtualNullsafeMethodCallTest extends RuleTestCase
16+
{
17+
18+
/**
19+
* @return Rule<MethodCall>
20+
*/
21+
protected function getRule(): Rule
22+
{
23+
return new /** @implements Rule<MethodCall> */ class implements Rule {
24+
25+
public function getNodeType(): string
26+
{
27+
return MethodCall::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
if ($node->getAttribute('virtualNullsafeMethodCall') === true) {
33+
return [RuleErrorBuilder::message('Nullable method call detected')->identifier('')->build()];
34+
}
35+
36+
return [RuleErrorBuilder::message('Regular method call detected')->identifier('')->build()];
37+
}
38+
39+
};
40+
}
41+
42+
public function testAttribute(): void
43+
{
44+
$this->analyse([ __DIR__ . '/data/virtual-nullsafe-method-call.php'], [
45+
[
46+
'Regular method call detected',
47+
3,
48+
],
49+
[
50+
'Nullable method call detected',
51+
4,
52+
],
53+
]);
54+
}
55+
56+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php // lint >= 8.0
2+
3+
$foo->regularCall();
4+
$foo?->nullsafeCall();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Properties;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\PropertyFetch;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Testing\RuleTestCase;
11+
12+
/**
13+
* @extends RuleTestCase<Rule>
14+
*/
15+
class VirtualNullsafePropertyFetchTest extends RuleTestCase
16+
{
17+
18+
/**
19+
* @return Rule<PropertyFetch>
20+
*/
21+
protected function getRule(): Rule
22+
{
23+
return new /** @implements Rule<PropertyFetch> */ class implements Rule {
24+
25+
public function getNodeType(): string
26+
{
27+
return PropertyFetch::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
if ($node->getAttribute('virtualNullsafePropertyFetch') === true) {
33+
return [RuleErrorBuilder::message('Nullable property fetch detected')->identifier('')->build()];
34+
}
35+
36+
return [RuleErrorBuilder::message('Regular property fetch detected')->identifier('')->build()];
37+
}
38+
39+
};
40+
}
41+
42+
public function testAttribute(): void
43+
{
44+
$this->analyse([ __DIR__ . '/data/virtual-nullsafe-property-fetch.php'], [
45+
[
46+
'Regular property fetch detected',
47+
3,
48+
],
49+
[
50+
'Nullable property fetch detected',
51+
4,
52+
],
53+
]);
54+
}
55+
56+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php // lint >= 8.0
2+
3+
$foo->regularFetch;
4+
$foo?->nullsafeFetch;

0 commit comments

Comments
 (0)