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

minor: add custom tokens for Disjunctive Normal Form types parentheses #6823

Merged
merged 4 commits into from
Mar 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Tokenizer/CT.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ final class CT
public const T_NAMED_ARGUMENT_COLON = 10033;
public const T_FIRST_CLASS_CALLABLE = 10034;
public const T_TYPE_INTERSECTION = 10035;
public const T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN = 10036;
public const T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE = 10037;

private function __construct()
{
Expand Down
5 changes: 5 additions & 0 deletions src/Tokenizer/Tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Tokens extends \SplFixedArray
public const BLOCK_TYPE_DESTRUCTURING_SQUARE_BRACE = 9;
public const BLOCK_TYPE_BRACE_CLASS_INSTANTIATION = 10;
public const BLOCK_TYPE_ATTRIBUTE = 11;
public const BLOCK_TYPE_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS = 12;

/**
* Static class cache.
Expand Down Expand Up @@ -239,6 +240,10 @@ public static function getBlockEdgeDefinitions(): array
'start' => [CT::T_BRACE_CLASS_INSTANTIATION_OPEN, '('],
'end' => [CT::T_BRACE_CLASS_INSTANTIATION_CLOSE, ')'],
],
self::BLOCK_TYPE_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS => [
'start' => [CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN, '('],
'end' => [CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE, ')'],
],
];

// @TODO: drop condition when PHP 8.0+ is required
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?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\Tokenizer\Transformer;

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

/**
* Transform DNF parentheses into CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN and CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE.
*
* @see https://wiki.php.net/rfc/dnf_types
*
* @internal
*/
final class DisjunctiveNormalFormTypeParenthesisTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority(): int
{
// needs to run after TypeAlternationTransformer
return -16;
}

/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId(): int
{
return 8_02_00;
}

/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, int $index): void
{
if ($token->equals('(') && $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(CT::T_TYPE_ALTERNATION)) {
$openIndex = $index;
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
} elseif ($token->equals(')') && $tokens[$tokens->getNextMeaningfulToken($index)]->isGivenKind(CT::T_TYPE_ALTERNATION)) {
$openIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$closeIndex = $index;
} else {
return;
}

$tokens[$openIndex] = new Token([CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN, '(']);
$tokens[$closeIndex] = new Token([CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE, ')']);
}

/**
* {@inheritdoc}
*/
public function getCustomTokens(): array
{
return [
CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_OPEN,
CT::T_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS_CLOSE,
];
}
}
1 change: 1 addition & 0 deletions tests/AutoReview/TransformerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public function provideTransformerPriorityCases(): array
[$transformers['type_colon'], $transformers['type_alternation']],
[$transformers['array_typehint'], $transformers['type_intersection']],
[$transformers['type_colon'], $transformers['type_intersection']],
[$transformers['type_alternation'], $transformers['disjunctive_normal_form_type_parenthesis']],
[$transformers['use'], $transformers['type_colon']],
];
}
Expand Down
37 changes: 37 additions & 0 deletions tests/Tokenizer/TokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,43 @@ public static function provideFindBlockEnd80Cases(): array
];
}

/**
* @requires PHP 8.2
*
* @dataProvider provideFindBlockEnd82Cases
*
* @param Tokens::BLOCK_TYPE_* $type
*/
public function testFindBlockEnd82(int $expectedIndex, string $source, int $type, int $searchIndex): void
{
static::assertFindBlockEnd($expectedIndex, $source, $type, $searchIndex);
}

public static function provideFindBlockEnd82Cases(): iterable
kubawerlos marked this conversation as resolved.
Show resolved Hide resolved
{
yield [
11,
'<?php function foo(A|(B&C) $x) {}',
Tokens::BLOCK_TYPE_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS,
7,
];

yield [
11,
'<?php function foo((A&B&C)|D $x) {}',
Tokens::BLOCK_TYPE_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS,
5,
];
foreach ([7 => 11, 19 => 23, 27 => 35] as $openIndex => $closeIndex) {
yield [
$closeIndex,
'<?php function foo(A|(B&C)|D $x): (A&B)|bool|(C&D&E&F) {}',
Tokens::BLOCK_TYPE_DISJUNCTIVE_NORMAL_FORM_TYPE_PARENTHESIS,
$openIndex,
];
}
}

public function testFindBlockEndInvalidType(): void
{
Tokens::clearCache();
Expand Down