Skip to content

Commit

Permalink
Add BackwardCompatible pretty printer
Browse files Browse the repository at this point in the history
  • Loading branch information
sidz committed Jan 14, 2024
1 parent 297306a commit d8843ed
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 32 deletions.
4 changes: 2 additions & 2 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
use Infection\Mutator\MutatorResolver;
use Infection\PhpParser\FileParser;
use Infection\PhpParser\NodeTraverserFactory;
use Infection\PhpParser\PrettyPrinter\BackwardCompatibleStandard;
use Infection\Process\Factory\InitialTestsRunProcessFactory;
use Infection\Process\Factory\MutantProcessFactory;
use Infection\Process\Runner\DryProcessRunner;
Expand Down Expand Up @@ -140,7 +141,6 @@
use function php_ini_loaded_file;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
use PhpParser\PrettyPrinterAbstract;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
Expand Down Expand Up @@ -297,7 +297,7 @@ public static function create(): self
),
Parser::class => static fn (): Parser => (new ParserFactory())->createForHostVersion(),
FileParser::class => static fn (self $container): FileParser => new FileParser($container->getParser()),
PrettyPrinterAbstract::class => static fn (): Standard => new Standard(),
PrettyPrinterAbstract::class => static fn (): BackwardCompatibleStandard => new BackwardCompatibleStandard(),
MetricsCalculator::class => static fn (self $container): MetricsCalculator => new MetricsCalculator($container->getConfiguration()->getMsiPrecision()),
ResultsCollector::class => static fn (self $container): ResultsCollector => new ResultsCollector(),
Stopwatch::class => static fn (): Stopwatch => new Stopwatch(),
Expand Down
6 changes: 4 additions & 2 deletions src/Mutation/Mutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Mutation
private readonly string $mutatorName;
/** @var array<string|int|float> */
private readonly array $attributes;
private readonly bool $coveredByTests;
private ?float $nominalTimeToTest = null;

private ?string $hash = null;
Expand All @@ -80,9 +81,9 @@ public function __construct(
foreach (MutationAttributeKeys::ALL as $key) {
Assert::keyExists($attributes, $key);
}

$this->mutatorName = $mutatorName;
$this->attributes = array_intersect_key($attributes, array_flip(MutationAttributeKeys::ALL));
$this->coveredByTests = $tests !== [];
}

public function getOriginalFilePath(): string
Expand Down Expand Up @@ -141,9 +142,10 @@ public function getMutatedNode(): MutatedNode
return $this->mutatedNode;
}

// TODO: hasTest()?
public function isCoveredByTest(): bool
{
return $this->tests !== [];
return $this->coveredByTests;
}

/**
Expand Down
13 changes: 10 additions & 3 deletions src/Mutator/Operator/Finally_.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@

namespace Infection\Mutator\Operator;

use Composer\InstalledVersions;
use function count;
use Infection\Mutator\Definition;
use Infection\Mutator\GetMutatorName;
use Infection\Mutator\Mutator;
use Infection\Mutator\MutatorCategory;
use Infection\PhpParser\Visitor\ParentConnector;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use function version_compare;
use Webmozart\Assert\Assert;

/**
Expand Down Expand Up @@ -73,11 +76,15 @@ public static function getDefinition(): ?Definition
/**
* @psalm-mutation-free
*
* @return iterable<int>
* @return iterable<int|Node\Stmt\Nop>
*/
public function mutate(Node $node): iterable

Check failure on line 81 in src/Mutator/Operator/Finally_.php

View workflow job for this annotation

GitHub Actions / Autoreview on PHP 8.2

Return type (iterable<int|PhpParser\Node\Stmt\Nop>) of method Infection\Mutator\Operator\Finally_::mutate() should be covariant with return type (iterable<array<PhpParser\Node>|PhpParser\Node>) of method Infection\Mutator\Mutator<PhpParser\Node\Stmt\Finally_>::mutate()
{
yield NodeVisitor::REPLACE_WITH_NULL;
if (version_compare((string) InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) {

Check warning on line 83 in src/Mutator/Operator/Finally_.php

View workflow job for this annotation

GitHub Actions / Mutation Testing Code Review Annotations 8.1

Escaped Mutant for Mutator "CastString": --- Original +++ New @@ @@ */ public function mutate(Node $node): iterable { - if (version_compare((string) InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) { + if (version_compare(InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) { yield new Node\Stmt\Nop(); } else { yield NodeVisitor::REPLACE_WITH_NULL;
yield new Node\Stmt\Nop();
} else {
yield NodeVisitor::REPLACE_WITH_NULL;
}
}

public function canMutate(Node $node): bool
Expand All @@ -95,6 +102,6 @@ private function hasAtLeastOneCatchBlock(Node $node): bool
$parentNode = ParentConnector::getParent($node);
Assert::isInstanceOf($parentNode, Node\Stmt\TryCatch::class);

return $parentNode->catches !== [];
return count($parentNode->catches) > 0;
}
}
14 changes: 10 additions & 4 deletions src/Mutator/Operator/Throw_.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@

namespace Infection\Mutator\Operator;

use Composer\InstalledVersions;
use Infection\Mutator\Definition;
use Infection\Mutator\GetMutatorName;
use Infection\Mutator\Mutator;
use Infection\Mutator\MutatorCategory;
use PhpParser\Node;
use function version_compare;

/**
* @internal
*
* @implements Mutator<Node\Expr\Throw_>
* @implements Mutator<Node\Stmt\Throw_|Node\Expr\Throw_>
*/
final class Throw_ implements Mutator
{
Expand Down Expand Up @@ -82,15 +84,19 @@ public static function getDefinition(): ?Definition
*
* Replaces "throw new Exception();" with "new Exception();"
*
* @return iterable<Node\Expr>
* @return iterable<Node\Stmt\Expression|Node\Expr>
*/
public function mutate(Node $node): iterable
{
yield $node->expr;
if (version_compare((string) InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) {

Check warning on line 91 in src/Mutator/Operator/Throw_.php

View workflow job for this annotation

GitHub Actions / Mutation Testing Code Review Annotations 8.1

Escaped Mutant for Mutator "CastString": --- Original +++ New @@ @@ */ public function mutate(Node $node): iterable { - if (version_compare((string) InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) { + if (version_compare(InstalledVersions::getPrettyVersion('nikic/php-parser'), 'v5.0', '<')) { yield new Node\Stmt\Expression($node->expr); } else { yield $node->expr;
yield new Node\Stmt\Expression($node->expr);
} else {
yield $node->expr;
}
}

public function canMutate(Node $node): bool
{
return $node instanceof Node\Expr\Throw_;
return $node instanceof Node\Expr\Throw_ || $node instanceof Node\Stmt\Throw_;

Check warning on line 100 in src/Mutator/Operator/Throw_.php

View workflow job for this annotation

GitHub Actions / Mutation Testing Code Review Annotations 8.1

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ } public function canMutate(Node $node): bool { - return $node instanceof Node\Expr\Throw_ || $node instanceof Node\Stmt\Throw_; + return $node instanceof Node\Expr\Throw_ || false; } }
}
}
89 changes: 89 additions & 0 deletions src/PhpParser/PrettyPrinter/BackwardCompatibleStandard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* This code is licensed under the BSD 3-Clause License.
*
* Copyright (c) 2017, Maks Rafalko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

declare(strict_types=1);

namespace Infection\PhpParser\PrettyPrinter;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinter\Standard;
use function preg_replace;

/**
* Keep compatibility between PHP-Parser 4 and 5 as the 5th version follow PSR-12.
* Could be removed when PHP-Parser 4 support is dropped.
*
* @see https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md#changes-to-the-pretty-printer
*/
final class BackwardCompatibleStandard extends Standard
{
private const REPLACE_COLON_WITH_SPACE_REGEX = '#(^.*function .*\\(.*\\)) : #';

public function __construct(array $options = [])
{
$options['shortArraySyntax'] = true;

parent::__construct($options);
}

protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string
{
$content = parent::pStmt_ClassMethod($node);

if (!$node->returnType instanceof Node) {
return $content;
}

return preg_replace(self::REPLACE_COLON_WITH_SPACE_REGEX, '$1: ', $content);

Check failure on line 69 in src/PhpParser/PrettyPrinter/BackwardCompatibleStandard.php

View workflow job for this annotation

GitHub Actions / Autoreview on PHP 8.2

Function preg_replace is unsafe to use. It can return FALSE instead of throwing an exception. Please add 'use function Safe\preg_replace;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library.

Check failure on line 69 in src/PhpParser/PrettyPrinter/BackwardCompatibleStandard.php

View workflow job for this annotation

GitHub Actions / Autoreview on PHP 8.2

Method Infection\PhpParser\PrettyPrinter\BackwardCompatibleStandard::pStmt_ClassMethod() should return string but returns string|null.
}

protected function pStmt_Function(Stmt\Function_ $node): string
{
$content = parent::pStmt_Function($node);

if (!$node->returnType instanceof Node) {
return $content;
}

return preg_replace(self::REPLACE_COLON_WITH_SPACE_REGEX, '$1: ', $content);

Check failure on line 80 in src/PhpParser/PrettyPrinter/BackwardCompatibleStandard.php

View workflow job for this annotation

GitHub Actions / Autoreview on PHP 8.2

Function preg_replace is unsafe to use. It can return FALSE instead of throwing an exception. Please add 'use function Safe\preg_replace;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library.

Check failure on line 80 in src/PhpParser/PrettyPrinter/BackwardCompatibleStandard.php

View workflow job for this annotation

GitHub Actions / Autoreview on PHP 8.2

Method Infection\PhpParser\PrettyPrinter\BackwardCompatibleStandard::pStmt_Function() should return string but returns string|null.
}

protected function pExpr_ClosureUse(Expr\ClosureUse $node): string
{
$content = parent::pExpr_ClosureUse($node);

return preg_replace(self::REPLACE_COLON_WITH_SPACE_REGEX, '$1: ', $content);
}
}
30 changes: 9 additions & 21 deletions tests/phpunit/SingletonContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
namespace Infection\Tests;

use Infection\Container;
use Infection\PhpParser\PrettyPrinter\BackwardCompatibleStandard;
use Infection\Tests\AutoReview\PhpDoc\PHPDocParser;
use PhpParser\NodeDumper;
use PhpParser\PrettyPrinter\Standard;
use PhpParser\PrettyPrinterAbstract;

/**
Expand All @@ -48,43 +48,31 @@
*/
final class SingletonContainer
{
/**
* @var Container|null
*/
private static $container;
private static ?Container $container;

/**
* @var NodeDumper|null
*/
private static $dumper;
private static ?NodeDumper $dumper;

/**
* @var PrettyPrinterAbstract|null
*/
private static $printer;
private static ?PrettyPrinterAbstract $printer;

/**
* @var PHPDocParser|null
*/
private static $phpDocParser;
private static ?PHPDocParser $phpDocParser;

public static function getContainer(): Container
{
return self::$container ?? self::$container = Container::create();
return self::$container ??= Container::create();
}

public static function getNodeDumper(): NodeDumper
{
return self::$dumper ?? self::$dumper = new NodeDumper();
return self::$dumper ??= new NodeDumper();
}

public static function getPrinter(): PrettyPrinterAbstract
{
return self::$printer ?? self::$printer = new Standard();
return self::$printer ??= new BackwardCompatibleStandard();
}

public static function getPHPDocParser(): PHPDocParser
{
return self::$phpDocParser ?? self::$phpDocParser = new PHPDocParser();
return self::$phpDocParser ??= new PHPDocParser();
}
}

0 comments on commit d8843ed

Please sign in to comment.