-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rules to accomodate first-class callables
- Loading branch information
1 parent
23ea5ed
commit e12d55f
Showing
11 changed files
with
322 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
src/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\StrictCalls; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Node\MethodCallableNode; | ||
use PHPStan\Rules\RuleLevelHelper; | ||
use PHPStan\Type\ErrorType; | ||
use PHPStan\Type\Type; | ||
|
||
/** | ||
* @implements \PHPStan\Rules\Rule<MethodCallableNode> | ||
*/ | ||
class DynamicCallOnStaticMethodsCallableRule implements \PHPStan\Rules\Rule | ||
{ | ||
|
||
/** @var \PHPStan\Rules\RuleLevelHelper */ | ||
private $ruleLevelHelper; | ||
|
||
public function __construct(RuleLevelHelper $ruleLevelHelper) | ||
{ | ||
$this->ruleLevelHelper = $ruleLevelHelper; | ||
} | ||
|
||
public function getNodeType(): string | ||
{ | ||
return MethodCallableNode::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if (!$node->getName() instanceof Node\Identifier) { | ||
return []; | ||
} | ||
|
||
$name = $node->getName()->name; | ||
$type = $this->ruleLevelHelper->findTypeToCheck( | ||
$scope, | ||
$node->getVar(), | ||
'', | ||
function (Type $type) use ($name): bool { | ||
return $type->canCallMethods()->yes() && $type->hasMethod($name)->yes(); | ||
} | ||
)->getType(); | ||
|
||
if ($type instanceof ErrorType || !$type->canCallMethods()->yes() || !$type->hasMethod($name)->yes()) { | ||
return []; | ||
} | ||
|
||
$methodReflection = $type->getMethod($name, $scope); | ||
if ($methodReflection->isStatic()) { | ||
return [sprintf( | ||
'Dynamic call to static method %s::%s().', | ||
$methodReflection->getDeclaringClass()->getDisplayName(), | ||
$methodReflection->getName() | ||
)]; | ||
} | ||
|
||
return []; | ||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
src/Rules/VariableVariables/VariableMethodCallableRule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\VariableVariables; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Node\MethodCallableNode; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Type\VerbosityLevel; | ||
|
||
/** | ||
* @implements Rule<MethodCallableNode> | ||
*/ | ||
class VariableMethodCallableRule implements Rule | ||
{ | ||
|
||
public function getNodeType(): string | ||
{ | ||
return MethodCallableNode::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if ($node->getName() instanceof Node\Identifier) { | ||
return []; | ||
} | ||
|
||
return [ | ||
sprintf( | ||
'Variable method call on %s.', | ||
$scope->getType($node->getVar())->describe(VerbosityLevel::typeOnly()) | ||
), | ||
]; | ||
} | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
src/Rules/VariableVariables/VariableStaticMethodCallableRule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\VariableVariables; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Node\StaticMethodCallableNode; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Type\VerbosityLevel; | ||
|
||
/** | ||
* @implements Rule<StaticMethodCallableNode> | ||
*/ | ||
class VariableStaticMethodCallableRule implements Rule | ||
{ | ||
|
||
public function getNodeType(): string | ||
{ | ||
return StaticMethodCallableNode::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if ($node->getName() instanceof Node\Identifier) { | ||
return []; | ||
} | ||
|
||
if ($node->getClass() instanceof Node\Name) { | ||
$methodCalledOn = $scope->resolveName($node->getClass()); | ||
} else { | ||
$methodCalledOn = $scope->getType($node->getClass())->describe(VerbosityLevel::typeOnly()); | ||
} | ||
|
||
return [ | ||
sprintf( | ||
'Variable static method call on %s.', | ||
$methodCalledOn | ||
), | ||
]; | ||
} | ||
|
||
} |
45 changes: 45 additions & 0 deletions
45
tests/Rules/StrictCalls/DynamicCallOnStaticMethodsCallableRuleTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\StrictCalls; | ||
|
||
use PHPStan\Rules\Rule; | ||
use PHPStan\Rules\RuleLevelHelper; | ||
|
||
/** | ||
* @extends \PHPStan\Testing\RuleTestCase<DynamicCallOnStaticMethodsCallableRule> | ||
*/ | ||
class DynamicCallOnStaticMethodsCallableRuleTest extends \PHPStan\Testing\RuleTestCase | ||
{ | ||
|
||
protected function getRule(): Rule | ||
{ | ||
return new DynamicCallOnStaticMethodsCallableRule(self::getContainer()->getByType(RuleLevelHelper::class)); | ||
} | ||
|
||
public function testRule(): void | ||
{ | ||
if (PHP_VERSION_ID < 80100) { | ||
self::markTestSkipped('Test requires PHP 8.1.'); | ||
} | ||
|
||
$this->analyse([__DIR__ . '/data/dynamic-calls-on-static-methods-callables.php'], [ | ||
[ | ||
'Dynamic call to static method StrictCallsCallables\ClassWithStaticMethod::foo().', | ||
14, | ||
], | ||
[ | ||
'Dynamic call to static method StrictCallsCallables\ClassWithStaticMethod::foo().', | ||
21, | ||
], | ||
[ | ||
'Dynamic call to static method StrictCallsCallables\ClassUsingTrait::foo().', | ||
34, | ||
], | ||
[ | ||
'Dynamic call to static method StrictCallsCallables\ClassUsingTrait::foo().', | ||
46, | ||
], | ||
]); | ||
} | ||
|
||
} |
48 changes: 48 additions & 0 deletions
48
tests/Rules/StrictCalls/data/dynamic-calls-on-static-methods-callables.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php // lint >= 8.1 | ||
|
||
namespace StrictCallsCallables; | ||
|
||
class ClassWithStaticMethod | ||
{ | ||
public static function foo() | ||
{ | ||
|
||
} | ||
|
||
public function bar() | ||
{ | ||
$this->foo(...); | ||
$this->bar(...); | ||
} | ||
} | ||
|
||
function () { | ||
$classWithStaticMethod = new ClassWithStaticMethod(); | ||
$classWithStaticMethod->foo(...); | ||
$classWithStaticMethod->bar(...); | ||
}; | ||
|
||
trait TraitWithStaticMethod | ||
{ | ||
public static function foo() | ||
{ | ||
|
||
} | ||
|
||
public function bar() | ||
{ | ||
$this->foo(...); | ||
$this->bar(...); | ||
} | ||
} | ||
|
||
class ClassUsingTrait | ||
{ | ||
use TraitWithStaticMethod; | ||
} | ||
|
||
function () { | ||
$classUsingTrait = new ClassUsingTrait(); | ||
$classUsingTrait->foo(...); | ||
$classUsingTrait->bar(...); | ||
}; |
33 changes: 33 additions & 0 deletions
33
tests/Rules/VariableVariables/VariableMethodCallableRuleTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\VariableVariables; | ||
|
||
use PHPStan\Rules\Rule; | ||
use PHPStan\Testing\RuleTestCase; | ||
|
||
/** | ||
* @extends RuleTestCase<VariableMethodCallableRule> | ||
*/ | ||
class VariableMethodCallableRuleTest extends RuleTestCase | ||
{ | ||
|
||
protected function getRule(): Rule | ||
{ | ||
return new VariableMethodCallableRule(); | ||
} | ||
|
||
public function testRule(): void | ||
{ | ||
if (PHP_VERSION_ID < 80100) { | ||
self::markTestSkipped('Test requires PHP 8.1.'); | ||
} | ||
|
||
$this->analyse([__DIR__ . '/data/methods-callables.php'], [ | ||
[ | ||
'Variable method call on stdClass.', | ||
7, | ||
], | ||
]); | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
tests/Rules/VariableVariables/VariableStaticMethodCallableRuleTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\VariableVariables; | ||
|
||
use PHPStan\Rules\Rule; | ||
use PHPStan\Testing\RuleTestCase; | ||
|
||
/** | ||
* @extends RuleTestCase<VariableStaticMethodCallableRule> | ||
*/ | ||
class VariableStaticMethodCallableRuleTest extends RuleTestCase | ||
{ | ||
|
||
protected function getRule(): Rule | ||
{ | ||
return new VariableStaticMethodCallableRule(); | ||
} | ||
|
||
public function testRule(): void | ||
{ | ||
$this->analyse([__DIR__ . '/data/staticMethods-callables.php'], [ | ||
[ | ||
'Variable static method call on Foo.', | ||
7, | ||
], | ||
[ | ||
'Variable static method call on stdClass.', | ||
9, | ||
], | ||
]); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php // lint >= 8.1 | ||
|
||
function (stdClass $std) { | ||
$std->foo(...); | ||
|
||
$foo = 'bar'; | ||
$std->$foo(...); | ||
}; |
10 changes: 10 additions & 0 deletions
10
tests/Rules/VariableVariables/data/staticMethods-callables.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php // lint >= 8.1 | ||
|
||
function (stdClass $std) { | ||
Foo::doFoo(...); | ||
|
||
$foo = 'doBar'; | ||
Foo::$foo(...); | ||
|
||
$std::$foo(...); | ||
}; |