Skip to content

Commit

Permalink
Merge branch 'master' into 7291
Browse files Browse the repository at this point in the history
  • Loading branch information
keradus committed Sep 26, 2023
2 parents 4c8d48a + de07904 commit f7f4110
Show file tree
Hide file tree
Showing 9 changed files with 769 additions and 4 deletions.
13 changes: 13 additions & 0 deletions doc/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ List of Available Rules
Part of rule sets `@PHP74Migration <./ruleSets/PHP74Migration.rst>`_ `@PHP80Migration <./ruleSets/PHP80Migration.rst>`_ `@PHP81Migration <./ruleSets/PHP81Migration.rst>`_ `@PHP82Migration <./ruleSets/PHP82Migration.rst>`_

`Source PhpCsFixer\\Fixer\\Operator\\AssignNullCoalescingToCoalesceEqualFixer <./../src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.php>`_
- `attribute_empty_parentheses <./rules/attribute_notation/attribute_empty_parentheses.rst>`_

PHP attributes declared without arguments must (not) be followed by empty parentheses.

Configuration options:

- | ``use_parentheses``
| Whether attributes should be followed by parentheses or not.
| Allowed types: ``bool``
| Default value: ``false``

`Source PhpCsFixer\\Fixer\\AttributeNotation\\AttributeEmptyParenthesesFixer <./../src/Fixer/AttributeNotation/AttributeEmptyParenthesesFixer.php>`_
- `backtick_to_shell_exec <./rules/alias/backtick_to_shell_exec.rst>`_

Converts backtick operators to ``shell_exec`` calls.
Expand Down
59 changes: 59 additions & 0 deletions doc/rules/attribute_notation/attribute_empty_parentheses.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
====================================
Rule ``attribute_empty_parentheses``
====================================

PHP attributes declared without arguments must (not) be followed by empty
parentheses.

Configuration
-------------

``use_parentheses``
~~~~~~~~~~~~~~~~~~~

Whether attributes should be followed by parentheses or not.

Allowed types: ``bool``

Default value: ``false``

Examples
--------

Example #1
~~~~~~~~~~

*Default* configuration.

.. code-block:: diff
--- Original
+++ New
<?php
-#[Foo()]
+#[Foo]
class Sample1 {}
-#[Bar(), Baz()]
+#[Bar, Baz]
class Sample2 {}
Example #2
~~~~~~~~~~

With configuration: ``['use_parentheses' => true]``.

.. code-block:: diff
--- Original
+++ New
<?php
-#[Foo]
+#[Foo()]
class Sample1 {}
-#[Bar, Baz]
+#[Bar(), Baz()]
class Sample2 {}
7 changes: 7 additions & 0 deletions doc/rules/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ Array Notation

Yield from array must be unpacked to series of yields.

Attribute Notation
------------------

- `attribute_empty_parentheses <./attribute_notation/attribute_empty_parentheses.rst>`_

PHP attributes declared without arguments must (not) be followed by empty parentheses.

Basic
-----

Expand Down
4 changes: 2 additions & 2 deletions src/Fixer/ArrayNotation/ReturnToYieldFromFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ private function shouldBeFixed(Tokens $tokens, int $returnIndex): bool
$functionEndIndex = $arrayEndIndex;
do {
$functionEndIndex = $tokens->getNextMeaningfulToken($functionEndIndex);
} while ($tokens[$functionEndIndex]->equals(';'));
if (!$tokens[$functionEndIndex]->equals('}')) {
} while (null !== $functionEndIndex && $tokens[$functionEndIndex]->equals(';'));
if (null === $functionEndIndex || !$tokens[$functionEndIndex]->equals('}')) {
return false;
}

Expand Down
123 changes: 123 additions & 0 deletions src/Fixer/AttributeNotation/AttributeEmptyParenthesesFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Fixer\AttributeNotation;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
* @author HypeMC <hypemc@gmail.com>
*/
final class AttributeEmptyParenthesesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'PHP attributes declared without arguments must (not) be followed by empty parentheses.',
[
new CodeSample("<?php\n\n#[Foo()]\nclass Sample1 {}\n\n#[Bar(), Baz()]\nclass Sample2 {}\n"),
new CodeSample(
"<?php\n\n#[Foo]\nclass Sample1 {}\n\n#[Bar, Baz]\nclass Sample2 {}\n",
['use_parentheses' => true]
),
]
);
}

public function isCandidate(Tokens $tokens): bool
{
return \defined('T_ATTRIBUTE') && $tokens->isTokenKindFound(T_ATTRIBUTE);
}

protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('use_parentheses', 'Whether attributes should be followed by parentheses or not.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}

protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$index = 0;

while (null !== $index = $tokens->getNextTokenOfKind($index, [[T_ATTRIBUTE]])) {
$nextIndex = $index;

do {
$parenthesesIndex = $tokens->getNextTokenOfKind($nextIndex, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]);

if (true === $this->configuration['use_parentheses']) {
$this->ensureParenthesesAt($tokens, $parenthesesIndex);
} else {
$this->ensureNoParenthesesAt($tokens, $parenthesesIndex);
}

$nextIndex = $tokens->getNextTokenOfKind($nextIndex, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]);

// Find closing parentheses, we need to do this in case there's a comma inside the parentheses
if ($tokens[$nextIndex]->equals('(')) {
$nextIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
$nextIndex = $tokens->getNextTokenOfKind($nextIndex, [',', [CT::T_ATTRIBUTE_CLOSE]]);
}

// In case there's a comma right before T_ATTRIBUTE_CLOSE
if (!$tokens[$nextIndex]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
}
} while (!$tokens[$nextIndex]->isGivenKind(CT::T_ATTRIBUTE_CLOSE));
}
}

private function ensureParenthesesAt(Tokens $tokens, int $index): void
{
if ($tokens[$index]->equals('(')) {
return;
}

$tokens->insertAt(
$tokens->getPrevMeaningfulToken($index) + 1,
[new Token('('), new Token(')')]
);
}

private function ensureNoParenthesesAt(Tokens $tokens, int $index): void
{
if (!$tokens[$index]->equals('(')) {
return;
}

$closingIndex = $tokens->getNextMeaningfulToken($index);

// attribute has arguments - parentheses can not be removed
if (!$tokens[$closingIndex]->equals(')')) {
return;
}

$tokens->clearTokenAndMergeSurroundingWhitespace($closingIndex);
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
use PhpCsFixer\Tokenizer\Tokens;

/**
* @author HypeMC
* @author HypeMC <hypemc@gmail.com>
*/
final class NullableTypeDeclarationForDefaultNullValueFixer extends AbstractFixer implements ConfigurableFixerInterface
{
Expand Down
16 changes: 16 additions & 0 deletions tests/Fixer/ArrayNotation/ReturnToYieldFromFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ public function testFix(string $expected, ?string $input = null): void
*/
public static function provideFixCases(): iterable
{
yield ['<?php return [function() { return [1, 2, 3]; }];'];

yield ['<?php return [fn() => [1, 2, 3]];'];

yield [
'<?php
function foo(): iterable { return $z; }
return [1,2] ?> X <?php { echo 2; }',
];

yield ['<?php function foo() { return [1, 2, 3]; }'];

yield ['<?php function foo(): MyAwesomeIterableType { return [1, 2, 3]; }'];
Expand All @@ -51,6 +62,11 @@ public function baz(): array { return []; }
}
'];

yield [
'<?php return [function(): iterable { yield from [1, 2, 3]; }];',
'<?php return [function(): iterable { return [1, 2, 3]; }];',
];

yield [
'<?php class Foo {
function bar(): iterable { yield from [1, 2, 3]; }
Expand Down

0 comments on commit f7f4110

Please sign in to comment.