FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Sanity check `#[RequiresPhp]` value and range by staabm · Pull Request #269 · phpstan/phpstan-phpunit · GitHub

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .json  (1) .neon  (2) .php  (5) All 3 file types selected
Only manifest files
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
3 changes: 2 additions & 1 deletion composer.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"keywords": ["static analysis"],
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.48"
"phar-io/version": "^3.2",
"phpstan/phpstan": "^2.2.3"
},
"conflict": {
"phpunit/phpunit": "<7.0"
Expand Down
96 changes: 93 additions & 3 deletions src/Rules/PHPUnit/AttributeRequiresPhpVersionRule.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,39 @@

namespace PHPStan\Rules\PHPUnit;

use PharIo\Version\UnsupportedVersionConstraintException;
use PharIo\Version\Version;
use PharIo\Version\VersionConstraintParser;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Php\PhpMinorVersionIterator;
use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\IntegerRangeType;
use PHPUnit\Framework\TestCase;
use function count;
use function is_numeric;
use function preg_match;
use function sprintf;
use function version_compare;

/**
* @implements Rule<InClassMethodNode>
*/
class AttributeRequiresPhpVersionRule implements Rule
{

private const VERSION_COMPARISON = "/(?P<operator>!=|<|<=|<>|=|==|>|>=)?\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m";

private PHPUnitVersion $PHPUnitVersion;

private TestMethodsHelper $testMethodsHelper;

private PhpVersion $fallbackPhpVersion;

/**
* When phpstan-deprecation-rules is installed, it reports deprecated usages.
*/
Expand All @@ -30,12 +43,14 @@ class AttributeRequiresPhpVersionRule implements Rule
public function __construct(
PHPUnitVersion $PHPUnitVersion,
TestMethodsHelper $testMethodsHelper,
bool $deprecationRulesInstalled
bool $deprecationRulesInstalled,
PhpVersion $phpVersion
)
{
$this->PHPUnitVersion = $PHPUnitVersion;
$this->testMethodsHelper = $testMethodsHelper;
$this->deprecationRulesInstalled = $deprecationRulesInstalled;
$this->fallbackPhpVersion = $phpVersion;
}

public function getNodeType(): string
Expand All @@ -55,16 +70,64 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

$phpstanPharIoVersions = $this->getAnalyzedPhpVersions($scope);
if ($phpstanPharIoVersions === []) {
return [];
}

$errors = [];
$parser = new VersionConstraintParser();
foreach ($reflectionMethod->getAttributesByName('PHPUnit\Framework\Attributes\RequiresPhp') as $attr) {
$args = $attr->getArguments();
if (count($args) !== 1) {
continue;
}

// the following block is mimicing PHPUnit version parsing
// see https://github.com/sebastianbergmann/phpunit/blob/43c2cd7b96ee1e800b35e4df23b419a88b53111d/src/Metadata/Version/Requirement.php

$versionRequirement = $args[0];
if (
!is_numeric($args[0])
!is_numeric($versionRequirement)
) {
try {
// check composer like version constraints, e.g. ^1 or ~2
$testPhpVersionConstraint = $parser->parse($versionRequirement);

foreach ($phpstanPharIoVersions as $pharIoVersion) {
if ($testPhpVersionConstraint->complies($pharIoVersion)) {
// one of the versions within range matched, check next attribute
continue 2;
}
}
} catch (UnsupportedVersionConstraintException $e) {
// test php-src builtin operators as in version_compare()
if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches) <= 0) {
$errors[] = RuleErrorBuilder::message(
sprintf($e->getMessage()),
)
->identifier('phpunit.attributeRequiresPhpVersion')
->build();

continue;
}

$operator = $matches['operator'] !== '' ? $matches['operator'] : '>=';

foreach ($phpstanPharIoVersions as $pharIoVersion) {
if (version_compare($pharIoVersion->getVersionString(), $matches['version'], $operator)) {
// one of the versions within range matched, check next attribute
continue 2;
}
}
}

$errors[] = RuleErrorBuilder::message(
sprintf('Version requirement will always evaluate to false.'),
)
->identifier('phpunit.attributeRequiresPhpVersion')
->build();

continue;
}

Expand All @@ -84,10 +147,37 @@ public function processNode(Node $node, Scope $scope): array
->identifier('phpunit.attributeRequiresPhpVersion')
->build();
}

}

return $errors;
}

/**
* @return Version[]
*/
private function getAnalyzedPhpVersions(Scope $scope): array
{
$scopePhpVersion = $scope->getPhpVersion()->getType();
if ($scopePhpVersion instanceof ConstantIntegerType) {
$v = new PhpVersion($scopePhpVersion->getValue());
return [new Version($v->getVersionString())];
} elseif ($scopePhpVersion instanceof IntegerRangeType) {
if ($scopePhpVersion->getMin() === null || $scopePhpVersion->getMax() === null) {
return [];
}

$versions = [];
$minorVersionIterator = new PhpMinorVersionIterator(
new PhpVersion($scopePhpVersion->getMin()),
new PhpVersion($scopePhpVersion->getMax()),
);
foreach ($minorVersionIterator as $phpstanVersion) {
$versions[] = new Version($phpstanVersion->getVersionString());
}
return $versions;
}

return [new Version($this->fallbackPhpVersion->getVersionString())];
}

}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
phpVersion:
min: 80200
max: 80400
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\PHPUnit;

use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\FileTypeMapper;

/**
* @extends RuleTestCase<AttributeRequiresPhpVersionRule>
*/
final class AttributeRequiresPhpVersionRangeRuleTest extends RuleTestCase
{

private int $phpVersion = 80500;

public function testPhpVersionMismatch(): void
{
$this->analyse([__DIR__ . '/data/requires-php-version-mismatch.php'], [
[
'Version requirement will always evaluate to false.',
20,
],
[
'Version requirement will always evaluate to false.',
28,
],
[
'Version requirement will always evaluate to false.',
36,
],
[
'Version requirement will always evaluate to false.',
44,
],
[
'Version requirement will always evaluate to false.',
76,
],
]);
}

protected function getRule(): Rule
{
$phpunitVersion = new PHPUnitVersion(null, null);

return new AttributeRequiresPhpVersionRule(
$phpunitVersion,
new TestMethodsHelper(
self::getContainer()->getByType(FileTypeMapper::class),
$phpunitVersion,
),
false,
new PhpVersion($this->phpVersion),
);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/AttributeRequiresPhpVersionRangeRule.neon',
];
}

}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
parameters:
phpVersion: 80500
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Rules\PHPUnit;

use PHPStan\Php\PhpVersion;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\FileTypeMapper;
Expand All @@ -12,6 +13,8 @@
final class AttributeRequiresPhpVersionRuleTest extends RuleTestCase
{

private int $phpVersion = 80500;

private ?int $phpunitMajorVersion;

private ?int $phpunitMinorVersion;
Expand Down Expand Up @@ -78,6 +81,64 @@ public function testRuleOnPHPUnit13(): void
]);
}

public function testPhpVersionMismatch(): void
{
$this->phpunitMajorVersion = 12;
$this->phpunitMinorVersion = 4;
$this->deprecationRulesInstalled = false;

$this->analyse([__DIR__ . '/data/requires-php-version-mismatch.php'], [
[
// errors because https://github.com/sebastianbergmann/phpunit/issues/6451
// the test assumes PHP_VERSION_ID 80500 and the constraint only has 2 digits
'Version requirement will always evaluate to false.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

#[RequiresPhp('<= 8.5')] will always evaluate to false ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

because the test assumes PHP_VERSION_ID 80500 and the constraint only has 2 digits.
it would not error for '<= 8.5.0'.

this is what #303 is about

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

That's tricky.

Should we have a test with 8.6 to ensure this one is not reported ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

done

12,
],
[
'Version requirement will always evaluate to false.',
20,
],
[
'Version requirement will always evaluate to false.',
28,
],
[
'Version requirement will always evaluate to false.',
36,
],
[
'Version requirement will always evaluate to false.',
44,
],
[
'Version requirement will always evaluate to false.',
52,
],
[
'Version requirement will always evaluate to false.',
60,
],
[
'Version requirement will always evaluate to false.',
68,
],
]);
}

public function testInvalidPhpVersion(): void
{
$this->phpunitMajorVersion = 12;
$this->phpunitMinorVersion = 4;
$this->deprecationRulesInstalled = false;

$this->analyse([__DIR__ . '/data/requires-php-version-invalid.php'], [
[
'Version constraint abc is not supported.',
12,
],
]);
}

protected function getRule(): Rule
{
$phpunitVersion = new PHPUnitVersion($this->phpunitMajorVersion, $this->phpunitMinorVersion);
Expand All @@ -89,7 +150,15 @@ protected function getRule(): Rule
$phpunitVersion,
),
$this->deprecationRulesInstalled,
new PhpVersion($this->phpVersion),
);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/AttributeRequiresPhpVersionRule.neon',
];
}

}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace RequiresPhpVersionMismatch;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\RequiresPhp;

class InvalidConstraint extends TestCase
{
#[RequiresPhp('abc')]
public function testFoo(): void {

}
}

Loading
Loading

Back | FazBrowse Home | New Git URL