Skip to content
Merged
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
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
"php": "~8.1.0 || ~8.2.0",
"ext-mbstring": "*",
"ext-tokenizer": "*",
"friendsofphp/php-cs-fixer": "^3.16"
"friendsofphp/php-cs-fixer": "^3.17"
},
"require-dev": {
"phpstan/phpstan": "^1.10.11",
"phpstan/phpstan-phpunit": "^1.3.11",
"phpunit/phpunit": "^10.0.19",
"phpstan/phpstan": "^1.10.15",
"phpstan/phpstan-phpunit": "^1.3.12",
"phpunit/phpunit": "^10.1.3",
"slam/php-debug-r": "^1.8.0",
"slam/phpstan-extensions": "^6.0.0"
},
Expand Down
5 changes: 2 additions & 3 deletions lib/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ final class Config extends PhpCsFixerConfig
{
public const RULES = [
'@DoctrineAnnotation' => true,
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => true,
'@PHP80Migration:risky' => true,
'@PHP81Migration' => true,
'@PHPUnit84Migration:risky' => true,
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => true,
'Slam/final_abstract_public' => true,
'Slam/final_internal_class' => true,
'Slam/function_reference_space' => true,
Expand Down Expand Up @@ -65,7 +65,6 @@ final class Config extends PhpCsFixerConfig
// 'psr0' => true,
'random_api_migration' => true,
'regular_callable_call' => true,
'self_static_accessor' => true,
'simple_to_complex_string_variable' => false,
'simplified_if_return' => true,
'simplified_null_return' => false,
Expand Down
5 changes: 5 additions & 0 deletions lib/FinalInternalClassFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ protected function applyFix(SplFileInfo $file, Tokens $tokens): void
$classes = \array_keys($tokens->findGivenKind(\T_CLASS));

while ($classIndex = \array_pop($classes)) {
$prevTokenIndex = $tokens->getPrevMeaningfulToken($classIndex);
if (\defined('T_READONLY') && $tokens[$prevTokenIndex]->isGivenKind([\T_READONLY])) {
$classIndex = $prevTokenIndex;
}

// ignore class if it is abstract or already final
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($classIndex)];
if ($prevToken->isGivenKind([\T_ABSTRACT, \T_FINAL, \T_NEW])) {
Expand Down
8 changes: 5 additions & 3 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
cacheDirectory=".phpunit.cache"
>
<coverage>
<include>
<directory suffix=".php">./lib</directory>
</include>
<report>
<text outputFile="php://stdout" showOnlySummary="true"/>
</report>
Expand All @@ -19,4 +16,9 @@
<testsuite name="SlamCsFixer">
<directory>./tests</directory>
</testsuite>
<source>
<include>
<directory>./lib</directory>
</include>
</source>
</phpunit>
30 changes: 14 additions & 16 deletions tests/AbstractFixerTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,31 +64,29 @@ final protected function doTest(string $expected, ?string $input = null, ?SplFil
$fileIsSupported = $this->fixer->supports($file);

if (null !== $input) {
static::assertNull($this->lintSource($input));
self::assertNull($this->lintSource($input));

Tokens::clearCache();
$tokens = Tokens::fromCode($input);

if ($fileIsSupported) {
static::assertTrue($this->fixer->isCandidate($tokens), 'Fixer must be a candidate for input code.');
static::assertFalse($tokens->isChanged(), 'Fixer must not touch Tokens on candidate check.');
self::assertTrue($this->fixer->isCandidate($tokens), 'Fixer must be a candidate for input code.');
self::assertFalse($tokens->isChanged(), 'Fixer must not touch Tokens on candidate check.');
$this->fixer->fix($file, $tokens);
}

static::assertSame(
self::assertSame(
$expected,
$tokens->generateCode(),
'Code build on input code must match expected code.'
);
static::assertTrue($tokens->isChanged(), 'Tokens collection built on input code must be marked as changed after fixing.');
self::assertTrue($tokens->isChanged(), 'Tokens collection built on input code must be marked as changed after fixing.');

$tokens->clearEmptyTokens();

static::assertSame(
self::assertSame(
\count($tokens),
\count(\array_unique(\array_map(static function (Token $token) {
return \spl_object_hash($token);
}, $tokens->toArray()))),
\count(\array_unique(\array_map(static fn (Token $token) => \spl_object_hash($token), $tokens->toArray()))),
'Token items inside Tokens collection must be unique.'
);

Expand All @@ -97,7 +95,7 @@ final protected function doTest(string $expected, ?string $input = null, ?SplFil
self::assertTokens($expectedTokens, $tokens);
}

static::assertNull($this->lintSource($expected));
self::assertNull($this->lintSource($expected));

Tokens::clearCache();
$tokens = Tokens::fromCode($expected);
Expand All @@ -106,12 +104,12 @@ final protected function doTest(string $expected, ?string $input = null, ?SplFil
$this->fixer->fix($file, $tokens);
}

static::assertSame(
self::assertSame(
$expected,
$tokens->generateCode(),
'Code build on expected code must not change.'
);
static::assertFalse($tokens->isChanged(), 'Tokens collection built on expected code must not be marked as changed after fixing.');
self::assertFalse($tokens->isChanged(), 'Tokens collection built on expected code must not be marked as changed after fixing.');
}

private function lintSource(string $source): ?string
Expand All @@ -129,18 +127,18 @@ private static function assertTokens(Tokens $expectedTokens, Tokens $inputTokens
{
foreach ($expectedTokens as $index => $expectedToken) {
if (! isset($inputTokens[$index])) {
static::fail(\sprintf("The token at index %d must be:\n%s, but is not set in the input collection.", $index, $expectedToken->toJson()));
self::fail(\sprintf("The token at index %d must be:\n%s, but is not set in the input collection.", $index, $expectedToken->toJson()));
}

$inputToken = $inputTokens[$index];

static::assertTrue(
self::assertTrue(
$expectedToken->equals($inputToken),
\sprintf("The token at index %d must be:\n%s,\ngot:\n%s.", $index, $expectedToken->toJson(), $inputToken->toJson())
);

$expectedTokenKind = $expectedToken->isArray() ? $expectedToken->getId() : $expectedToken->getContent();
static::assertTrue(
self::assertTrue(
$inputTokens->isTokenKindFound($expectedTokenKind),
\sprintf(
'The token kind %s (%s) must be found in tokens collection.',
Expand All @@ -150,6 +148,6 @@ private static function assertTokens(Tokens $expectedTokens, Tokens $inputTokens
);
}

static::assertSame($expectedTokens->count(), $inputTokens->count(), 'Both collections must have the same length.');
self::assertSame($expectedTokens->count(), $inputTokens->count(), 'Both collections must have the same length.');
}
}
16 changes: 7 additions & 9 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,14 @@ public function testAllRulesAreSpecifiedAndDifferentFromRuleSets(): void
$fixerFactory->registerCustomFixers($config->getCustomFixers());
$fixers = $fixerFactory->getFixers();

$availableRules = \array_filter($fixers, static function (FixerInterface $fixer): bool {
return ! $fixer instanceof DeprecatedFixerInterface;
});
$availableRules = \array_map(function (FixerInterface $fixer): string {
return $fixer->getName();
}, $availableRules);
$availableRules = \array_filter($fixers, static fn (FixerInterface $fixer): bool => ! $fixer instanceof DeprecatedFixerInterface);
$availableRules = \array_map(fn (FixerInterface $fixer): string => $fixer->getName(), $availableRules);
\sort($availableRules);

/*
$diff = \array_diff($availableRules, $currentRules);
self::assertEmpty($diff, \sprintf("The following fixers are missing:\n- %s", \implode(\PHP_EOL . '- ', $diff)));
*/

$diff = \array_diff($currentRules, $availableRules);
self::assertEmpty($diff, \sprintf("The following fixers do not exist:\n- %s", \implode(\PHP_EOL . '- ', $diff)));
Expand All @@ -77,16 +75,16 @@ public function testAllRulesAreSpecifiedAndDifferentFromRuleSets(): void
}
self::assertSame([], $alreadyDefinedRules, 'These rules are already defined in the respective set');

/*
$currentSets = \array_values(\array_filter(\array_keys($configRules), static function (string $fixerName): bool {
return isset($fixerName[0]) && '@' === $fixerName[0];
}));
$defaultSets = RuleSets::getSetDefinitionNames();
$intersectSets = \array_values(\array_intersect($defaultSets, $currentSets));
self::assertEquals($intersectSets, $currentSets, \sprintf('Rule sets must be ordered as the appear in %s', RuleSet::class));
*/

$currentRules = \array_values(\array_filter(\array_keys($configRules), static function (string $fixerName): bool {
return isset($fixerName[0]) && '@' !== $fixerName[0];
}));
$currentRules = \array_values(\array_filter(\array_keys($configRules), static fn (string $fixerName): bool => isset($fixerName[0]) && '@' !== $fixerName[0]));

$orderedCurrentRules = $currentRules;
\sort($orderedCurrentRules);
Expand Down
33 changes: 33 additions & 0 deletions tests/FinalInternalClassFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\RequiresPhp;
use SlamCsFixer\FinalInternalClassFixer;

#[CoversClass(FinalInternalClassFixer::class)]
Expand All @@ -15,6 +16,7 @@ final class FinalInternalClassFixerTest extends AbstractFixerTestCase
public function testFix(string $expected, ?string $input = null): void
{
$this->doTest($expected, $input);
$this->doTest($expected);
}

/** @return string[][] */
Expand Down Expand Up @@ -66,4 +68,35 @@ public static function provideCases(): array
],
];
}

#[RequiresPhp('8.2')]
#[DataProvider('provide82Cases')]
public function test82Fix(string $expected, ?string $input = null): void
{
$this->doTest($expected, $input);
$this->doTest($expected);
}

/** @return string[][] */
public static function provide82Cases(): array
{
return [
[
'<?php final readonly class MyClass {}',
'<?php readonly class MyClass {}',
],
[
'<?php final readonly class MyClass extends MyAbstract {}',
'<?php readonly class MyClass extends MyAbstract {}',
],
[
'<?php final readonly class MyClass implements MyInterface {}',
'<?php readonly class MyClass implements MyInterface {}',
],
[
"<?php\n/**\n * @codeCoverageIgnore\n */\nfinal readonly class MyEntity {}",
"<?php\n/**\n * @codeCoverageIgnore\n */\nreadonly class MyEntity {}",
],
];
}
}