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

Enhancement: Implement NoNamedArgumentRule #916

Draft
wants to merge 1 commit 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
1 change: 1 addition & 0 deletions .php-cs-fixer.fixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
->notPath([
'Closures/NoNullableReturnTypeDeclarationRule/script.php',
'Closures/NoParameterWithNullableTypeDeclarationRule/script.php',
'FunctionCalls/NoNamedArgumentRule/script.php',
'Functions/NoNullableReturnTypeDeclarationRule/script.php',
'Functions/NoParameterWithNullableTypeDeclarationRule/script.php',
'Methods/NoNullableReturnTypeDeclarationRule/MethodInClassWithNullableUnionReturnTypeDeclaration.php',
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
- [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule)
- [`Ergebnis\PHPStan\Rules\Expressions\NoIssetRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoissetrule)
- [`Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule`](https://github.com/ergebnis/phpstan-rules#filesdeclarestricttypesrule)
- [`Ergebnis\PHPStan\Rules\FunctionCalls\NoNamedArgumentRule`](https://github.com/ergebnis/phpstan-rules#functioncallsnonamedargumentrule)
- [`Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnonullablereturntypedeclarationrule)
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterpassedbyreferencerule)
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule)
Expand Down Expand Up @@ -328,6 +329,23 @@ parameters:
enabled: false
```

### Function Calls

#### `FunctionCalls\NoNamedArgumentRule`

This rule reports an error when a function call uses a [named argument](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments).

##### Disabling the rule

You can set the `enabled` parameter to `false` to disable this rule.

```neon
parameters:
ergebnis:
noNamedArgument:
enabled: false
```

### Functions

#### `Functions\NoNullableReturnTypeDeclarationRule`
Expand Down
10 changes: 10 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ conditionalTags:
phpstan.rules.rule: %ergebnis.noIsset.enabled%
Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule:
phpstan.rules.rule: %ergebnis.declareStrictTypes.enabled%
Ergebnis\PHPStan\Rules\FunctionCalls\NoNamedArgumentRule:
phpstan.rules.rule: %ergebnis.noNamedArgument.enabled%
Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule:
phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled%
Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule:
Expand Down Expand Up @@ -80,6 +82,8 @@ parameters:
enabled: %ergebnis.allRules%
noIsset:
enabled: %ergebnis.allRules%
noNamedArgument:
enabled: %ergebnis.allRules%
noNullableReturnTypeDeclaration:
enabled: %ergebnis.allRules%
noParameterPassedByReference:
Expand Down Expand Up @@ -138,6 +142,9 @@ parametersSchema:
noIsset: structure([
enabled: bool(),
])
noNamedArgument: structure([
enabled: bool(),
])
noNullableReturnTypeDeclaration: structure([
enabled: bool(),
])
Expand Down Expand Up @@ -214,6 +221,9 @@ services:
-
class: Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule

-
class: Ergebnis\PHPStan\Rules\FunctionCalls\NoNamedArgumentRule

-
class: Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule

Expand Down
5 changes: 5 additions & 0 deletions src/ErrorIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public static function noIsset(): self
return new self('noIsset');
}

public static function noNamedArgument(): self
{
return new self('noNamedArgument');
}

public static function noParameterPassedByReference(): self
{
return new self('noParameterPassedByReference');
Expand Down
69 changes: 69 additions & 0 deletions src/FunctionCalls/NoNamedArgumentRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018-2025 Andreas Möller
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/ergebnis/phpstan-rules
*/

namespace Ergebnis\PHPStan\Rules\FunctionCalls;

use Ergebnis\PHPStan\Rules\ErrorIdentifier;
use PhpParser\Node;
use PHPStan\Analyser;
use PHPStan\Rules;

/**
* @implements Rules\Rule<Node\Expr\FuncCall>
*/
final class NoNamedArgumentRule implements Rules\Rule
{
public function getNodeType(): string
{
return Node\Expr\FuncCall::class;
}

public function processNode(
Node $node,
Analyser\Scope $scope
): array {
if (0 === \count($node->args)) {
return [];
}

/** @var list<Node\Arg> $namedArguments */
$namedArguments = \array_values(\array_filter($node->args, static function ($argument): bool {
if (!$argument instanceof Node\Arg) {
return false;

Check warning on line 42 in src/FunctionCalls/NoNamedArgumentRule.php

View check run for this annotation

Codecov / codecov/patch

src/FunctionCalls/NoNamedArgumentRule.php#L42

Added line #L42 was not covered by tests
}

return $argument->name instanceof Node\Identifier;
}));

if (0 === \count($namedArguments)) {
return [];
}

$functionName = $node->name;

return \array_map(static function (Node\Arg $namedArgument) use ($functionName): Rules\RuleError {
/** @var Node\Identifier $argumentName */
$argumentName = $namedArgument->name;

$message = \sprintf(
'Function %s() is invoked with named argument for parameter $%s.',
$functionName,
$argumentName->toString(),
);

return Rules\RuleErrorBuilder::message($message)
->identifier(ErrorIdentifier::noNamedArgument()->toString())
->build();
}, $namedArguments);
}
}
31 changes: 31 additions & 0 deletions test/Fixture/FunctionCalls/NoNamedArgumentRule/script.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Ergebnis\PHPStan\Rules\Test\Fixture\FunctionCalls\NoNamedArgumentRule;

use Doctrine\DBAL\Schema\Identifier;

function foo($bar = null)
{
}

function bar(...$baz)
{
}
foo();

foo(2000);

foo(bar: 2000);

foo(...);

bar();

bar(2000, 3000);

bar(baz: 2000);

bar(...);

53 changes: 53 additions & 0 deletions test/Integration/FunctionCalls/NoNamedArgumentRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018-2025 Andreas Möller
*
* For the full copyright and license information, please view
* the LICENSE.md file that was distributed with this source code.
*
* @see https://github.com/ergebnis/phpstan-rules
*/

namespace Ergebnis\PHPStan\Rules\Test\Integration\FunctionCalls;

use Ergebnis\PHPStan\Rules\FunctionCalls;
use Ergebnis\PHPStan\Rules\Test;
use PHPStan\Rules;
use PHPStan\Testing;

/**
* @covers \Ergebnis\PHPStan\Rules\FunctionCalls\NoNamedArgumentRule
*
* @uses \Ergebnis\PHPStan\Rules\ErrorIdentifier
*
* @extends Testing\RuleTestCase<FunctionCalls\NoNamedArgumentRule>
*/
final class NoNamedArgumentRuleTest extends Testing\RuleTestCase
{
use Test\Util\Helper;

public function testNoNamedArgumentRule(): void
{
$this->analyse(
self::phpFilesIn(__DIR__ . '/../../Fixture/FunctionCalls/NoNamedArgumentRule'),
[
[
'Function foo() is invoked with named argument for parameter $bar.',
20,
],
[
'Function bar() is invoked with named argument for parameter $baz.',
28,
],
],
);
}

protected function getRule(): Rules\Rule
{
return new FunctionCalls\NoNamedArgumentRule();
}
}
7 changes: 7 additions & 0 deletions test/Unit/ErrorIdentifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ public function testNoIssetReturnsErrorIdentifier(): void
self::assertSame('ergebnis.noIsset', $errorIdentifier->toString());
}

public function testNoNamedArgumentReturnsErrorIdentifier(): void
{
$errorIdentifier = ErrorIdentifier::noNamedArgument();

self::assertSame('ergebnis.noNamedArgument', $errorIdentifier->toString());
}

public function testNoNullableReturnTypeDeclarationReturnsErrorIdentifier(): void
{
$errorIdentifier = ErrorIdentifier::noNullableReturnTypeDeclaration();
Expand Down
Loading