Skip to content

Commit

Permalink
feature: NoUselessNullsafeOperatorFixer - Introduction (#6425)
Browse files Browse the repository at this point in the history
NoUselessNullsafeOperatorFixer - Introduction
  • Loading branch information
SpacePossum authored Jul 10, 2022
1 parent ccab1db commit 83071d9
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 0 deletions.
7 changes: 7 additions & 0 deletions doc/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,13 @@ List of Available Rules
Part of rule set `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_

`Source PhpCsFixer\\Fixer\\ControlStructure\\NoUselessElseFixer <./../src/Fixer/ControlStructure/NoUselessElseFixer.php>`_
- `no_useless_nullsafe_operator <./rules/operator/no_useless_nullsafe_operator.rst>`_

There should not be useless ``null-safe-operators`` ``?->`` used.

Part of rule sets `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_

`Source PhpCsFixer\\Fixer\\Operator\\NoUselessNullsafeOperatorFixer <./../src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php>`_
- `no_useless_return <./rules/return_notation/no_useless_return.rst>`_

There should not be an empty ``return`` statement at the end of a function.
Expand Down
1 change: 1 addition & 0 deletions doc/ruleSets/Symfony.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Rules
- `no_unneeded_import_alias <./../rules/import/no_unneeded_import_alias.rst>`_
- `no_unset_cast <./../rules/cast_notation/no_unset_cast.rst>`_
- `no_unused_imports <./../rules/import/no_unused_imports.rst>`_
- `no_useless_nullsafe_operator <./../rules/operator/no_useless_nullsafe_operator.rst>`_
- `no_whitespace_before_comma_in_array <./../rules/array_notation/no_whitespace_before_comma_in_array.rst>`_
- `normalize_index_brace <./../rules/array_notation/normalize_index_brace.rst>`_
- `object_operator_without_whitespace <./../rules/operator/object_operator_without_whitespace.rst>`_
Expand Down
3 changes: 3 additions & 0 deletions doc/rules/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ Operator
- `no_space_around_double_colon <./operator/no_space_around_double_colon.rst>`_

There must be no space around double colons (also called Scope Resolution Operator or Paamayim Nekudotayim).
- `no_useless_nullsafe_operator <./operator/no_useless_nullsafe_operator.rst>`_

There should not be useless ``null-safe-operators`` ``?->`` used.
- `not_operator_with_space <./operator/not_operator_with_space.rst>`_

Logical NOT operators (``!``) should have leading and trailing whitespaces.
Expand Down
35 changes: 35 additions & 0 deletions doc/rules/operator/no_useless_nullsafe_operator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
=====================================
Rule ``no_useless_nullsafe_operator``
=====================================

There should not be useless ``null-safe-operators`` ``?->`` used.

Examples
--------

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

.. code-block:: diff
--- Original
+++ New
<?php
class Foo extends Bar
{
public function test() {
- echo $this?->parentMethod();
+ echo $this->parentMethod();
}
}
Rule sets
---------

The rule is part of the following rule sets:

@PhpCsFixer
Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``no_useless_nullsafe_operator`` rule.

@Symfony
Using the `@Symfony <./../../ruleSets/Symfony.rst>`_ rule set will enable the ``no_useless_nullsafe_operator`` rule.
82 changes: 82 additions & 0 deletions src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?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\Operator;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

final class NoUselessNullsafeOperatorFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'There should not be useless `null-safe-operators` `?->` used.',
[
new VersionSpecificCodeSample(
'<?php
class Foo extends Bar
{
public function test() {
echo $this?->parentMethod();
}
}
',
new VersionSpecification(80000)
),
]
);
}

/**
* {@inheritdoc}
*/
public function isCandidate(Tokens $tokens): bool
{
return \PHP_VERSION_ID >= 80000 && $tokens->isAllTokenKindsFound([T_VARIABLE, T_NULLSAFE_OBJECT_OPERATOR]);
}

/**
* {@inheritdoc}
*/
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if (!$tokens[$index]->isGivenKind(T_NULLSAFE_OBJECT_OPERATOR)) {
continue;
}

$nullsafeObjectOperatorIndex = $index;
$index = $tokens->getPrevMeaningfulToken($index);

if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
continue;
}

if ('$this' !== strtolower($tokens[$index]->getContent())) {
continue;
}

$tokens[$nullsafeObjectOperatorIndex] = new Token([T_OBJECT_OPERATOR, '->']);
}
}
}
1 change: 1 addition & 0 deletions src/RuleSet/Sets/SymfonySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public function getRules(): array
'no_unneeded_import_alias' => true,
'no_unset_cast' => true,
'no_unused_imports' => true,
'no_useless_nullsafe_operator' => true,
'no_whitespace_before_comma_in_array' => true,
'normalize_index_brace' => true,
'object_operator_without_whitespace' => true,
Expand Down
65 changes: 65 additions & 0 deletions tests/Fixer/Operator/NoUselessNullsafeOperatorFixerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?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\Tests\Fixer\Operator;

use PhpCsFixer\Tests\Test\AbstractFixerTestCase;

/**
* @internal
* @requires PHP 8.0
*
* @covers \PhpCsFixer\Fixer\Operator\NoUselessNullsafeOperatorFixer
*/
final class NoUselessNullsafeOperatorFixerTest extends AbstractFixerTestCase
{
/**
* @dataProvider provideFixCases
*/
public function testFix(string $expected, ?string $input = null): void
{
$this->doTest($expected, $input);
}

public function provideFixCases(): iterable
{
yield 'simple case + comment' => [
'<?php $a = new class extends foo {
public function bar() {
$this->g();
// $this?->g();
}
};',
'<?php $a = new class extends foo {
public function bar() {
$this?->g();
// $this?->g();
}
};',
];

yield 'multiple casing cases + comment + no candidate' => [
'<?php $a = new class extends foo {
public function bar() {
return $THIS /*1*/ -> g().$THis->g().$this->do()?->o();
}
};',
'<?php $a = new class extends foo {
public function bar() {
return $THIS /*1*/ ?-> g().$THis?->g().$this->do()?->o();
}
};',
];
}
}

0 comments on commit 83071d9

Please sign in to comment.