Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHPStan 2.0 support #271

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"symplify/phpstan-extensions": "^11.4",
"symplify/phpstan-rules": "^12.4",
"symplify/rule-doc-generator": "^12.1",
"tightenco/duster": "^2.7"
"tightenco/duster": "^2.7",
"phpstan/phpstan-deprecation-rules": "^1.2"
},
"autoload": {
"psr-4": {
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
includes:
- vendor/symplify/phpstan-rules/config/rector-rules.neon
- phar://phpstan.phar/conf/bleedingEdge.neon
parameters:
level: max

Expand Down Expand Up @@ -32,3 +33,6 @@ parameters:

# Laravel Container not being recognized properly in some of the tests
- '#Call to method needs\(\) on an unknown class Illuminate\\Contracts\\Container\\ContextualBindingBuilder#'

# No easy replacement for this check, it's also ignored in core Rector
- '#Doing instanceof PHPStan\\Type\\Generic\\GenericObjectType is error\-prone and deprecated#'
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ public function getNodeTypes(): array

/**
* @param Expression $node
* @return array<int, Node>|null
*/
public function refactor(Node $node): Node|array|int|null
public function refactor(Node $node): ?array
{
if (! $node->expr instanceof Assign) {
return null;
Expand Down
26 changes: 8 additions & 18 deletions src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
Expand Down Expand Up @@ -239,21 +237,17 @@ private function getRelatedModelClassFromMethodCall(MethodCall $methodCall): ?st
{
$argType = $this->getType($methodCall->getArgs()[0]->value);

if ($argType instanceof ConstantStringType && $argType->isClassStringType()->yes()) {
return $argType->getValue();
}

if (! $argType instanceof GenericClassStringType) {
if (! $argType->isClassStringType()->yes()) {
return null;
}

$modelType = $argType->getGenericType();
$objectClassNames = $argType->getClassStringObjectType()->getObjectClassNames();

if (! $modelType instanceof ObjectType) {
if ($objectClassNames === []) {
return null;
}

return $modelType->getClassName();
return $objectClassNames[0];
}

private function getRelationMethodCall(ClassMethod $classMethod): ?MethodCall
Expand Down Expand Up @@ -326,21 +320,17 @@ private function getClassForIntermediateGeneric(MethodCall $methodCall): ?string

$argType = $this->getType($args[1]->value);

if ($argType instanceof ConstantStringType && $argType->isClassStringType()->yes()) {
return $argType->getValue();
}

if (! $argType instanceof GenericClassStringType) {
if (! $argType->isClassStringType()->yes()) {
return null;
}

$modelType = $argType->getGenericType();
$objectClassNames = $argType->getClassStringObjectType()->getObjectClassNames();

if (! $modelType instanceof ObjectType) {
if ($objectClassNames === []) {
return null;
}

return $modelType->getClassName();
return $objectClassNames[0];
}

private function areNativeTypeAndPhpDocReturnTypeEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function getNodeTypes(): array
/**
* @param Class_ $node
*/
public function refactor(Node $node): Node|array|int|null
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
return null;
Expand Down
12 changes: 9 additions & 3 deletions src/Rector/Coalesce/ApplyDefaultInsteadOfNullCoalesceRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ public function refactor(Node $node): MethodCall|StaticCall|FuncCall|null

$call = $node->left;

if ($call instanceof MethodCall) {
$objectType = $call->var;
} elseif ($call instanceof StaticCall) {
$objectType = $call->class;
} else {
$objectType = null;
}

foreach ($this->applyDefaultWith as $applyDefaultWith) {
$valid = false;

$objectType = $call->var ?? $call->class ?? null;

if (
$applyDefaultWith->getObjectType() instanceof ObjectType &&
$applyDefaultWith->getObjectType() !== null &&
$objectType !== null &&
$this->isObjectType(
$objectType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,20 @@ private function processCall(FuncCall|MethodCall|StaticCall $call): FuncCall|Met
private function isDispatchablesCall(MethodCall $methodCall): bool
{
$type = $this->getType($methodCall->var);
if (! $type instanceof ObjectType) {

if (! $type->isObject()->yes()) {
return false;
}

$objectClassNames = $type->getObjectClassNames();

if (count($objectClassNames) !== 1) {
return false;
}

try {
// Will trigger ClassNotFoundException if the class definition is not found
$reflection = $this->reflectionProvider->getClass(
$type->getClassName()
);
$reflection = $this->reflectionProvider->getClass($objectClassNames[0]);

if ($reflection->hasTraitUse(self::DISPATCHABLE_TRAIT)) {
return true;
Expand Down
3 changes: 2 additions & 1 deletion src/Rector/FuncCall/RemoveDumpDataDeadCodeRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ public function getNodeTypes(): array

/**
* @param Expression $node
* @return NodeTraverser::REMOVE_NODE|null
*/
public function refactor(Node $node): int|Node|array|null
public function refactor(Node $node): ?int
{
if (! $node->expr instanceof FuncCall) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ public function getNodeTypes(): array

/**
* @param Expression $node
* @return array<int, Expression|Node>|null
*/
public function refactor(Node $node): Node|array|int|null
public function refactor(Node $node): ?array
{
if (! $node->expr instanceof MethodCall) {
return null;
Expand Down
11 changes: 8 additions & 3 deletions src/Rector/MethodCall/EloquentOrderByToLatestOrOldestRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private function isOrderByMethodCall(MethodCall $methodCall): bool

private function isAllowedPattern(MethodCall $methodCall): bool
{
$columnArg = $methodCall->args[0]->value ?? null;
$columnArg = $methodCall->args[0] instanceof Arg ? $methodCall->args[0]->value : null;

// If no patterns are specified, consider all column names as matching
if ($this->allowedPatterns === []) {
Expand Down Expand Up @@ -150,12 +150,17 @@ private function convertOrderByToLatest(MethodCall $methodCall): MethodCall
return $methodCall;
}

$columnVar = $methodCall->args[0]->value ?? null;
$columnVar = $methodCall->args[0] instanceof Arg ? $methodCall->args[0]->value : null;
if ($columnVar === null) {
return $methodCall;
}

$direction = $methodCall->args[1]->value->value ?? 'asc';
if (isset($methodCall->args[1]) && $methodCall->args[1] instanceof Arg && $methodCall->args[1]->value instanceof String_) {
$direction = $methodCall->args[1]->value->value;
} else {
$direction = 'asc';
}

if ($this->isName($methodCall->name, 'orderByDesc')) {
$newMethod = 'latest';
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/Rector/New_/AddGuardToLoginEventRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public function getNodeTypes(): array

/**
* @param Expression $node
* @return array<int, Expression|Node>|null
*/
public function refactor(Node $node): Node|array|int|null
public function refactor(Node $node): ?array
{
$newNode = $this->getNewNode($node);

Expand Down
3 changes: 3 additions & 0 deletions src/Rector/PropertyFetch/OptionalToNullsafeOperatorRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ public function refactor(Node $node): ?Node
return new NullsafeMethodCall($firstArg->value, $node->name, $node->args);
}

/**
* @return PhpVersion::PHP_80
*/
public function provideMinPhpVersion(): int
{
return PhpVersion::PHP_80;
Expand Down
13 changes: 8 additions & 5 deletions src/Rector/StaticCall/DispatchToHelperFunctionsRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\Exception\PoorDocumentationException;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
Expand Down Expand Up @@ -91,14 +90,18 @@ public function refactor(Node $node): ?Node
private function getClassReflection(StaticCall $staticCall): ?ClassReflection
{
$type = $this->getType($staticCall->class);
if (! $type instanceof ObjectType) {
if (! $type->isObject()->yes()) {
return null;
}

$objectClassNames = $type->getObjectClassNames();

if (count($objectClassNames) !== 1) {
return null;
}

try {
return $this->reflectionProvider->getClass(
$type->getClassName()
);
return $this->reflectionProvider->getClass($objectClassNames[0]);
} catch (ClassNotFoundException) {
}

Expand Down
17 changes: 13 additions & 4 deletions src/Rector/StaticCall/RouteActionCallableRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\String_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\Php\PhpMethodReflection;
use PHPStan\Reflection\MethodReflection;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\Node\Value\ValueResolver;
Expand Down Expand Up @@ -123,7 +123,7 @@ public function refactor(Node $node): ?Node

$phpMethodReflection = $this->reflectionResolver->resolveMethodReflection($segments[0], $segments[1], $scope);

if (! $phpMethodReflection instanceof PhpMethodReflection) {
if (! $phpMethodReflection instanceof MethodReflection) {
return null;
}

Expand All @@ -132,7 +132,7 @@ public function refactor(Node $node): ?Node
$segments[1],
]);

if (is_array($argValue) && isset($argValue['as'])) {
if (is_array($argValue) && isset($argValue['as']) && is_string($argValue['as'])) {
$node = new MethodCall($node, 'name', [new Arg(new String_($argValue['as']))]);
}

Expand All @@ -144,9 +144,17 @@ public function refactor(Node $node): ?Node
if (is_string($argValue['middleware'])) {
$argument = new String_($argValue['middleware']);
} else {
// if any of the elements in the middleware array is not a string, return node as is
if (array_filter($argValue['middleware'], static fn ($value) => ! is_string($value)) !== []) {
return $node;
}

/** @var list<string> $middleware */
$middleware = $argValue['middleware'];

$argument = new Array_(array_map(
static fn ($value) => new ArrayItem(new String_($value)),
$argValue['middleware']
$middleware
));
}
$node = new MethodCall($node, 'middleware', [new Arg($argument)]);
Expand All @@ -164,6 +172,7 @@ public function configure(array $configuration): void
Assert::isArray($routes);
Assert::allString(array_keys($routes));
Assert::allString($routes);
/** @var array<string, string> $routes */
$this->routes = $routes;

$namespace = $configuration[self::NAMESPACE] ?? self::DEFAULT_NAMESPACE;
Expand Down
4 changes: 1 addition & 3 deletions src/Set/LaravelLevelSetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace RectorLaravel\Set;

use Rector\Set\Contract\SetListInterface;

final class LaravelLevelSetList implements SetListInterface
final class LaravelLevelSetList
{
/**
* @var string
Expand Down
4 changes: 1 addition & 3 deletions src/Set/LaravelSetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace RectorLaravel\Set;

use Rector\Set\Contract\SetListInterface;

final class LaravelSetList implements SetListInterface
final class LaravelSetList
{
/**
* @var string
Expand Down
4 changes: 1 addition & 3 deletions src/Set/Packages/Cashier/CashierLevelSetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace RectorLaravel\Set\Packages\Cashier;

use Rector\Set\Contract\SetListInterface;

final class CashierLevelSetList implements SetListInterface
final class CashierLevelSetList
{
/**
* @var string
Expand Down
4 changes: 1 addition & 3 deletions src/Set/Packages/Cashier/CashierSetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace RectorLaravel\Set\Packages\Cashier;

use Rector\Set\Contract\SetListInterface;

final class CashierSetList implements SetListInterface
final class CashierSetList
{
/**
* @var string
Expand Down
4 changes: 1 addition & 3 deletions src/Set/Packages/Livewire/LivewireLevelSetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

namespace RectorLaravel\Set\Packages\Livewire;

use Rector\Set\Contract\SetListInterface;

final class LivewireLevelSetList implements SetListInterface
final class LivewireLevelSetList
{
/**
* @var string
Expand Down
Loading
Loading