Skip to content

Commit

Permalink
NamedArgumentTransformer - Introduction
Browse files Browse the repository at this point in the history
  • Loading branch information
SpacePossum committed Dec 9, 2020
1 parent 1ff5457 commit cde0236
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Tokenizer/CT.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ final class CT
const T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED = 10029;
const T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE = 10030;
const T_ATTRIBUTE_CLOSE = 10031;
const T_NAMED_ARGUMENT_NAME = 10032;
const T_NAMED_ARGUMENT_COLON = 10033;

private function __construct()
{
Expand Down
85 changes: 85 additions & 0 deletions src/Tokenizer/Transformer/NamedArgumentTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <[email protected]>
* Dariusz Rumiński <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Tokenizer\Transformer;

use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
* Transform named argument tokens.
*
* @author SpacePossum
*
* @internal
*/
final class NamedArgumentTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// needs to run after TypeColonTransformer
return -15;
}

/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 80000;
}

/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$tokens[$index]->equals(':')) {
return;
}

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

if (!$tokens[$stringIndex]->isGivenKind(T_STRING)) {
return;
}

$preStringIndex = $tokens->getPrevMeaningfulToken($stringIndex);

// if equals any [';', '{', '}', [T_OPEN_TAG]] than it is a goto label
// if equals ')' than likely it is a type colon, but sure not a name argument
// if equals '?' than it is part of ternary statement

if (!$tokens[$preStringIndex]->equalsAny([',', '('])) {
return;
}

$tokens[$stringIndex] = new Token([CT::T_NAMED_ARGUMENT_NAME, $tokens[$stringIndex]->getContent()]);
$tokens[$index] = new Token([CT::T_NAMED_ARGUMENT_COLON, ':']);
}

/**
* {@inheritdoc}
*/
protected function getDeprecatedCustomTokens()
{
return [
CT::T_NAMED_ARGUMENT_COLON,
CT::T_NAMED_ARGUMENT_NAME,
];
}
}
1 change: 1 addition & 0 deletions tests/AutoReview/TransformerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public function provideTransformerPriorityCases()
[$transformers['name_qualified'], $transformers['namespace_operator']],
[$transformers['return_ref'], $transformers['type_colon']],
[$transformers['square_brace'], $transformers['brace_class_instantiation']],
[$transformers['type_colon'], $transformers['named_argument']],
[$transformers['type_colon'], $transformers['nullable_type']],
[$transformers['use'], $transformers['type_colon']],
];
Expand Down
170 changes: 170 additions & 0 deletions tests/Tokenizer/Transformer/NamedArgumentTransformerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <[email protected]>
* Dariusz Rumiński <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Tests\Tokenizer\Transformer;

use PhpCsFixer\Tests\Test\AbstractTransformerTestCase;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;

/**
* @internal
*
* @covers \PhpCsFixer\Tokenizer\Transformer\NamedArgumentTransformer
*/
final class NamedArgumentTransformerTest extends AbstractTransformerTestCase
{
/**
* @param string $source
*
* @dataProvider provideProcessCases
* @requires PHP 8.0
*/
public function testProcess($source, array $expectedTokens)
{
$this->doTest($source, $expectedTokens);
}

public function provideProcessCases()
{
yield 'function call' => [
'<?php foo(test: 1);',
[
3 => CT::T_NAMED_ARGUMENT_NAME,
4 => CT::T_NAMED_ARGUMENT_COLON,
],
];

yield 'dynamic function 2x' => [
'<?php $foo(foo: 1, bar: 2, 3,);',
[
3 => CT::T_NAMED_ARGUMENT_NAME,
4 => CT::T_NAMED_ARGUMENT_COLON,
9 => CT::T_NAMED_ARGUMENT_NAME,
10 => CT::T_NAMED_ARGUMENT_COLON,
],
];

yield 'method' => [
'<?php
class Bar {
public function a($foo){}
}
$foo = new Bar();
$foo->a(foo: 1);
',
[
36 => CT::T_NAMED_ARGUMENT_NAME,
37 => CT::T_NAMED_ARGUMENT_COLON,
],
];

yield 'nested' => [
'<?php
foo(test: static function() {
bar(test: 1);
},);
',
[
4 => CT::T_NAMED_ARGUMENT_NAME,
5 => CT::T_NAMED_ARGUMENT_COLON,
17 => CT::T_NAMED_ARGUMENT_NAME,
18 => CT::T_NAMED_ARGUMENT_COLON,
],
];
}

/**
* @param string $source
*
* @dataProvider provideDoNotChangeCases
*/
public function testDoNotChange($source)
{
static::assertNotChange($source);
}

public function provideDoNotChangeCases()
{
yield 'switch/case/constants' => [
'<?php
define(\'FOO\', 123);
define(\'BAR\', 123);
$a = $guard = 123;
switch($a) {
case FOO:
echo 456;
break;
case 3 + FOO:
echo 789;
break;
case ($guard ? BAR : 2):
echo 456;
break;
}
foo(1 , $a3 ? BAR : 2);
$a1 = [1, BAR ? 1 : 2];
$a2 = [1, (BAR) ? 1 : 2];
',
];

yield 'goto' => [
'<?php
define(\'FOO\', 123);
$guard = 1;
{
beginning:
echo $guard ? 1 + FOO : 2;
echo $guard ? 1 : 2;
}
',
];
}

/**
* @param string $source
*
* @dataProvider provideDoNotChange70Cases
* @requires PHP 7.0
*/
public function testDoNotChange70($source)
{
static::assertNotChange($source);
}

public function provideDoNotChange70Cases()
{
yield 'return type' => ['<?php function foo(): array { return []; }'];
}

/**
* @param string $source
*/
private static function assertNotChange($source)
{
Tokens::clearCache();

foreach (Tokens::fromCode($source) as $token) {
static::assertFalse($token->isGivenKind([
CT::T_NAMED_ARGUMENT_NAME,
CT::T_NAMED_ARGUMENT_COLON,
]));
}
}
}

0 comments on commit cde0236

Please sign in to comment.