Skip to content

Commit

Permalink
forbidXxxOnMixed: more generic detection of unknown caller (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored Sep 5, 2023
1 parent 4481bba commit e2388a7
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 10 deletions.
6 changes: 3 additions & 3 deletions src/Rule/ForbidFetchOnMixedRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use PhpParser\PrettyPrinter\Standard;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\MixedType;
use PHPStan\Type\TypeUtils;
use function get_class;
use function sprintf;

Expand Down Expand Up @@ -66,9 +66,9 @@ private function processFetch(Node $node, Scope $scope): array
return [];
}

$callerType = $scope->getType($caller);
$callerType = TypeUtils::toBenevolentUnion($scope->getType($caller));

if ($callerType instanceof MixedType) {
if ($callerType->getObjectTypeOrClassStringObjectType()->getObjectClassNames() === []) {
$name = $node->name;
$property = $name instanceof Identifier
? $this->printer->prettyPrint([$name])
Expand Down
6 changes: 3 additions & 3 deletions src/Rule/ForbidMethodCallOnMixedRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use PhpParser\PrettyPrinter\Standard;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\MixedType;
use PHPStan\Type\TypeUtils;
use function get_class;
use function sprintf;

Expand Down Expand Up @@ -67,9 +67,9 @@ private function checkCall(CallLike $node, Scope $scope): array
return [];
}

$callerType = $scope->getType($caller);
$callerType = TypeUtils::toBenevolentUnion($scope->getType($caller));

if ($callerType instanceof MixedType) {
if ($callerType->getObjectTypeOrClassStringObjectType()->getObjectClassNames() === []) {
$name = $node->name;
$method = $name instanceof Identifier ? $this->printer->prettyPrint([$name]) : $this->printer->prettyPrintExpr($name);

Expand Down
20 changes: 18 additions & 2 deletions tests/Rule/data/ForbidFetchOnMixedRuleTest/code.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,30 @@ class Foo {
public static ?int $staticProperty = null;
}

$fn = function (mixed $mixed, $unknown, array $array, ReflectionClass $reflection) {
$fn = function (mixed $mixed, $unknown, string $string, array $array, ReflectionClass $reflection, ?Foo $fooOrNull) {
(new Foo)->property;
Foo::$staticProperty;

/** @var class-string $classString */
$classString = '';
$classString::$staticProperty; // error: Property fetch ::$staticProperty is prohibited on unknown type ($classString)

/** @var class-string<Foo> $classString2 */
$classString2 = '';
$classString2::$staticProperty;

$string::$staticProperty; // error: Property fetch ::$staticProperty is prohibited on unknown type ($string)

$mixed->fetch1; // error: Property fetch ->fetch1 is prohibited on unknown type ($mixed)
$mixed::$fetch1; // error: Property fetch ::$fetch1 is prohibited on unknown type ($mixed)
$unknown->fetch2; // error: Property fetch ->fetch2 is prohibited on unknown type ($unknown)
$unknown::$fetch2; // error: Property fetch ::$fetch2 is prohibited on unknown type ($unknown)
$array[0]->fetch3; // error: Property fetch ->fetch3 is prohibited on unknown type ($array[0])
$reflection->newInstance()->fetch4;

$fooOrNull->property;
$fooOrNull?->property;

$reflection->newInstance()->property; // error: Property fetch ->property is prohibited on unknown type ($reflection->newInstance())
/** @var ReflectionClass<Foo> $reflection */
$reflection->newInstance()->property;
};
23 changes: 21 additions & 2 deletions tests/Rule/data/ForbidMethodCallOnMixedRule/code.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,39 @@ public function method() {}
public static function staticMethod() {}
}

$fn = function (mixed $mixed, $unknown, array $array, ReflectionClass $reflection) {
$fn = function (mixed $mixed, $unknown, array $array, string $string, ?Foo $fooOrNull, ReflectionClass $reflection) {

$foo = new Foo();
$foo->method();
$foo?->method();
Foo::staticMethod();
$foo::staticMethod();

$fooOrNull->method();
$fooOrNull?->method();
$fooOrNull->staticMethod();
$fooOrNull?->staticMethod();

/** @var class-string $classString */
$classString = '';
$classString::staticMethod(); // error: Method call ::staticMethod() is prohibited on unknown type ($classString)

/** @var class-string<Foo> $classString2 */
$classString2 = '';
$classString2::staticMethod();

$string::staticMethod(); // error: Method call ::staticMethod() is prohibited on unknown type ($string)

$mixed->call1(); // error: Method call ->call1() is prohibited on unknown type ($mixed)
$mixed?->call1(); // error: Method call ->call1() is prohibited on unknown type ($mixed)
$mixed::call1(); // error: Method call ::call1() is prohibited on unknown type ($mixed)
$unknown->call2(); // error: Method call ->call2() is prohibited on unknown type ($unknown)
$unknown?->call2(); // error: Method call ->call2() is prohibited on unknown type ($unknown)
$unknown::call2(); // error: Method call ::call2() is prohibited on unknown type ($unknown)
$array[0]->call3(); // error: Method call ->call3() is prohibited on unknown type ($array[0])
$reflection->newInstance()->call4();


$reflection->newInstance()->method(); // error: Method call ->method() is prohibited on unknown type ($reflection->newInstance())
/** @var ReflectionClass<Foo> $reflection */
$reflection->newInstance()->method();
};

0 comments on commit e2388a7

Please sign in to comment.