Skip to content
Merged
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: 3 additions & 0 deletions build/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ parameters:
- stubs/ReactStreams.stub
- stubs/NetteDIContainer.stub
- stubs/PhpParserName.stub
- stubs/PhpParserNode.stub
- stubs/PhpParserExpr.stub
- stubs/PhpParserStmt.stub
- stubs/Identifier.stub

rules:
Expand Down
108 changes: 108 additions & 0 deletions build/stubs/PhpParserExpr.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\NodeAbstract;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;
use PhpParser\Node\FunctionLike;

abstract class CallLike extends Expr {
/**
* Return raw arguments, which may be actual Args, or VariadicPlaceholders for first-class
* callables.
*
* @return list<Arg|VariadicPlaceholder>
*/
abstract public function getRawArgs(): array;
/**
* Assert that this is not a first-class callable and return only ordinary Args.
*
* @return list<Arg>
*/
public function getArgs(): array {
assert(!$this->isFirstClassCallable());
return $this->getRawArgs();
}
}

class FuncCall extends CallLike {
/** @var list<Node\Arg|Node\VariadicPlaceholder> Arguments */
public array $args;

/**
* Constructs a function call node.
*
* @param Node\Name|Expr $name Function name
* @param list<Node\Arg|Node\VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $name, array $args = [], array $attributes = []) {}
}

class MethodCall extends CallLike {
/** @var list<Node\Arg|Node\VariadicPlaceholder> Arguments */
public array $args;

/**
* Constructs a function call node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Method name
* @param list<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {}
}

class New_ extends CallLike {
/** @var list<Arg|VariadicPlaceholder> Arguments */
public array $args;

/**
* Constructs a function call node.
*
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
* @param list<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, array $args = [], array $attributes = []) {}
}

class NullsafeMethodCall extends CallLike {
/** @var list<Arg|VariadicPlaceholder> Arguments */
public array $args;

/**
* Constructs a nullsafe method call node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Method name
* @param list<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {}
}

class StaticCall extends CallLike {
/** @var list<Arg|VariadicPlaceholder> Arguments */
public array $args;

/**
* Constructs a static method call node.
*
* @param Node\Name|Expr $class Class name
* @param string|Identifier|Expr $name Method name
* @param list<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, $name, array $args = [], array $attributes = []) {}
}

class Closure extends Expr implements FunctionLike {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}
10 changes: 10 additions & 0 deletions build/stubs/PhpParserNode.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class PropertyHook extends NodeAbstract implements FunctionLike {
/** @var null|Expr|list<Stmt> Hook body */
public $body;
}
83 changes: 83 additions & 0 deletions build/stubs/PhpParserStmt.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;

class Block extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Case_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Catch_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Do_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class ElseIf_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Else_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Finally_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class For_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Foreach_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class If_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class Namespace_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class TryCatch extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class While_ extends Node\Stmt {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

class ClassMethod extends Node\Stmt implements FunctionLike {
/** @var list<Node\Stmt>|null Statements */
public ?array $stmts;
}

class Function_ extends Node\Stmt implements FunctionLike {
/** @var list<Node\Stmt> Statements */
public array $stmts;
}

10 changes: 8 additions & 2 deletions src/Analyser/ArgumentsNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantArrayType;
use function array_is_list;
use function array_key_exists;
use function array_keys;
use function array_values;
use function count;
use function ksort;
use function max;
Expand Down Expand Up @@ -182,8 +184,8 @@ public static function reorderNewArguments(
}

/**
* @param Arg[] $callArgs
* @return ?array<int, Arg>
* @param list<Arg> $callArgs
* @return ?list<Arg>
*/
public static function reorderArgs(ParametersAcceptor $parametersAcceptor, array $callArgs): ?array
{
Expand Down Expand Up @@ -322,6 +324,10 @@ public static function reorderArgs(ParametersAcceptor $parametersAcceptor, array
$reorderedArgs[] = $arg;
}

if (!array_is_list($reorderedArgs)) {
$reorderedArgs = array_values($reorderedArgs);
}

return $reorderedArgs;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4455,7 +4455,7 @@ private function getMethodThrowPoint(MethodReflection $methodReflection, Paramet
}

/**
* @param Node\Arg[] $args
* @param list<Node\Arg> $args
*/
private function getConstructorThrowPoint(MethodReflection $constructorReflection, ParametersAcceptor $parametersAcceptor, ClassReflection $classReflection, New_ $new, Name $className, array $args, MutatingScope $scope): ?ThrowPoint
{
Expand Down
4 changes: 2 additions & 2 deletions src/Parser/CleaningVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public function enterNode(Node $node): ?Node
}

/**
* @param Node\Stmt[] $stmts
* @return Node\Stmt[]
* @param list<Node\Stmt|Node\Expr> $stmts
* @return list<Node\Stmt>
*/
private function keepVariadicsAndYields(array $stmts, ?string $hookedPropertyName): array
{
Expand Down
6 changes: 6 additions & 0 deletions src/Reflection/ParametersAcceptorSelector.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use function array_is_list;
use function array_key_exists;
use function array_key_last;
use function array_map;
Expand Down Expand Up @@ -471,6 +472,11 @@ public static function selectFromArgs(
$parameters = null;
$singleParametersAcceptor = null;
if (count($parametersAcceptors) === 1) {
if (!array_is_list($args)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doing a runtime check, to not change the phpdoc on a @api class

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this is worse because suddenly you're throwing an exception for a valid input advertised by the signature.

If it's not a list, let's make it a list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea.

It might even be worth creating a new rule which errors in my wrong code example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to express the logic you'd want...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error on conditions which always lead to unchecked exceptions, when the conditions narrows a parameter type.

But you are right, maybe too specific

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd merge this if you call array_values here instead 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was not at the computer until now. fix incoming ;).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also outside the entire time 😂

// actually $args parameter should be typed to list but we can't atm,
// because its a BC break.
$args = array_values($args);
}
$reorderedArgs = ArgumentsNormalizer::reorderArgs($parametersAcceptors[0], $args);
$singleParametersAcceptor = $parametersAcceptors[0];
}
Expand Down
3 changes: 1 addition & 2 deletions src/Rules/Functions/RandomIntParametersRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\VerbosityLevel;
use function array_values;
use function count;
use function sprintf;

Expand Down Expand Up @@ -49,7 +48,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

$args = array_values($node->getArgs());
$args = $node->getArgs();
if (count($args) < 2) {
return [];
}
Expand Down
8 changes: 0 additions & 8 deletions tests/PHPStan/Analyser/ArgumentsNormalizerLegacyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ public function testArgumentReorderAllNamed(): void
$reorderedArgs = $funcCall->getArgs();
$this->assertCount(2, $reorderedArgs);

$this->assertArrayHasKey(0, $reorderedArgs);
$this->assertNull($reorderedArgs[0]->name, 'named-arg turned into regular numeric arg');
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at the right position');

$this->assertArrayHasKey(1, $reorderedArgs);
$this->assertNull($reorderedArgs[1]->name, 'named-arg turned into regular numeric arg');
$this->assertInstanceOf(LNumber::class, $reorderedArgs[1]->value, 'flags-arg at the right position');
$this->assertSame(0, $reorderedArgs[1]->value->value);
Expand Down Expand Up @@ -90,17 +88,14 @@ public function testArgumentReorderAllNamedWithSkipped(): void
$reorderedArgs = $funcCall->getArgs();
$this->assertCount(3, $reorderedArgs);

$this->assertArrayHasKey(0, $reorderedArgs);
$this->assertNull($reorderedArgs[0]->name, 'named-arg turned into regular numeric arg');
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at the right position');

$this->assertArrayHasKey(1, $reorderedArgs);
$this->assertNull($reorderedArgs[1]->name, 'named-arg turned into regular numeric arg');
$this->assertInstanceOf(TypeExpr::class, $reorderedArgs[1]->value, 'flags-arg at the right position');
$this->assertInstanceOf(ConstantIntegerType::class, $reorderedArgs[1]->value->getExprType());
$this->assertSame(0, $reorderedArgs[1]->value->getExprType()->getValue(), 'flags-arg with default value');

$this->assertArrayHasKey(2, $reorderedArgs);
$this->assertNull($reorderedArgs[2]->name, 'named-arg turned into regular numeric arg');
$this->assertInstanceOf(LNumber::class, $reorderedArgs[2]->value, 'depth-arg at the right position');
$this->assertSame(128, $reorderedArgs[2]->value->value);
Expand Down Expand Up @@ -153,10 +148,7 @@ public function testLeaveRegularCallAsIs(): void
$reorderedArgs = $funcCall->getArgs();
$this->assertCount(2, $reorderedArgs);

$this->assertArrayHasKey(0, $reorderedArgs);
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at unchanged position');

$this->assertArrayHasKey(1, $reorderedArgs);
$this->assertInstanceOf(LNumber::class, $reorderedArgs[1]->value, 'flags-arg at unchanged position');
$this->assertSame(0, $reorderedArgs[1]->value->value);
}
Expand Down
Loading