Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ It also contains this strict framework-specific rules (can be enabled separately
* Check that you are not using `assertSame()` with `count($variable)` as second parameter. `assertCount($variable)` should be used instead.
* Check that you are not using `assertEquals()` with same types (`assertSame()` should be used)
* Check that you are not using `assertNotEquals()` with same types (`assertNotSame()` should be used)
* When PHPStan Strict Rules' `disallowedEmpty` rule is enabled, disallow PHPUnit's `assertEmpty()` and `assertNotEmpty()` assertions as well.

## How to document mock objects in phpDocs?

Expand Down
1 change: 1 addition & 0 deletions e2e/composer-version/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"php": "^8.1",
"phpstan/phpstan": "@dev",
"phpstan/phpstan-phpunit": "@dev",
"phpstan/phpstan-strict-rules": "@dev",
"phpunit/phpunit": "^12.5",
"phpstan/extension-installer": "^1.4"
},
Expand Down
5 changes: 5 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ rules:
conditionalTags:
PHPStan\Rules\PHPUnit\AssertEqualsIsDiscouragedRule:
phpstan.rules.rule: [%strictRulesInstalled%, %featureToggles.bleedingEdge%]

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.

@staabm You implemented AssertEqualIsDiscouraged with 10880da

Looking at how AssertEmptyIsDiscouragedRule is configured ; I wonder if it wouldn't be good to be behind disallowedLooseComparison here ?

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.

I am not sure what you are referring to? AssertEqualIsDiscouraged is configured with [%strictRulesInstalled%, %featureToggles.bleedingEdge%]?

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.

I mean it should be with

[%strictRulesInstalled%, %strictRules.disallowedLooseComparison%, %featureToggles.bleedingEdge%]

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.

I think we cannot use %strictRules.disallowedLooseComparison% because it would result into a fatal error when strict rules are not installed.

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.

%strictRulesInstalled% is a flag which is available because it is known in phpstan-src.

https://github.com/phpstan/phpstan-src/blob/b3547b6e094d604560bd0f97420f2ac8fcfa0961/conf/config.neon#L92

we cannot depend on other 1st party config flags which are only available when a extension is installed

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.

PHPStan\Rules\PHPUnit\AssertEmptyIsDiscouragedRule:
phpstan.rules.rule: [%strictRulesInstalled%, %strictRules.disallowedEmpty%]

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.

Should it be also behind %featureToggles.bleedingEdge% ?

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.

good catch, I think you are right.


PHPStan\Rules\PHPUnit\DataProviderDataRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
Expand Down Expand Up @@ -39,5 +41,8 @@ services:
-
class: PHPStan\Rules\PHPUnit\AssertEqualsIsDiscouragedRule

-
class: PHPStan\Rules\PHPUnit\AssertEmptyIsDiscouragedRule

-
class: PHPStan\Rules\PHPUnit\DataProviderDataRule
62 changes: 62 additions & 0 deletions src/Rules/PHPUnit/AssertEmptyIsDiscouragedRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\PHPUnit;

use PhpParser\Node;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function count;
use function in_array;
use function sprintf;
use function strtolower;

/**
* @implements Rule<CallLike>
*/
class AssertEmptyIsDiscouragedRule implements Rule
{

public function getNodeType(): string
{
return CallLike::class;
}

public function processNode(Node $node, Scope $scope): array
{
if ($node->isFirstClassCallable() || count($node->getArgs()) < 1) {
return [];
}

if ($node instanceof MethodCall || $node instanceof StaticCall) {
if (!$node->name instanceof Identifier || !in_array($node->name->toLowerString(), ['assertempty', 'assertnotempty'], true)) {
return [];
}
if (!AssertRuleHelper::isMethodOrStaticCallOnAssert($node, $scope)) {
return [];
}
} elseif ($node instanceof FuncCall) {
if (!$node->name instanceof Name || !in_array(strtolower($scope->resolveName($node->name)), ['phpunit\\framework\\assertempty', 'phpunit\\framework\\assertnotempty'], true)) {
return [];
}
} else {
return [];
}
Comment on lines +44 to +50

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.

function based assertions seem to be not supported by other Assert* rules, so I think we shouldn't start here.

it would need a separate PR doing it in all rules (not sure its worth adding,... noone asked for these yet AFAIR)


return [
RuleErrorBuilder::message(sprintf(
'%s() is not allowed. Use more strict assertion.',
$node instanceof FuncCall ? $node->name->getLast() : $node->name->toString(),
))
->identifier('empty.notAllowed')
->build(),
];
}

}
31 changes: 31 additions & 0 deletions tests/Rules/PHPUnit/AssertEmptyIsDiscouragedRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\PHPUnit;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<AssertEmptyIsDiscouragedRule>
*/
final class AssertEmptyIsDiscouragedRuleTest extends RuleTestCase
{

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/assert-empty-is-discouraged.php'], [
['assertEmpty() is not allowed. Use more strict assertion.', 15],
['assertNotEmpty() is not allowed. Use more strict assertion.', 16],
['assertEmpty() is not allowed. Use more strict assertion.', 17],
['assertNotEmpty() is not allowed. Use more strict assertion.', 18],
['assertEmpty() is not allowed. Use more strict assertion.', 19],
['assertNotEmpty() is not allowed. Use more strict assertion.', 20],
]);
}

protected function getRule(): Rule
{
return new AssertEmptyIsDiscouragedRule();
}

}
23 changes: 23 additions & 0 deletions tests/Rules/PHPUnit/data/assert-empty-is-discouraged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types = 1);

namespace AssertEmptyIsDiscouragedTest;

use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use function PHPUnit\Framework\assertEmpty;
use function PHPUnit\Framework\assertNotEmpty;

final class AssertEmptyTest extends TestCase
{

public function test(): void
{
$this->assertEmpty([]);
$this->assertNotEmpty([1]);
Assert::assertEmpty([]);
static::assertNotEmpty([1]);
assertEmpty([]);
assertNotEmpty([1]);
}

}
Loading