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
19 changes: 9 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,20 @@
},
"require": {
"php": "^8.2",
"ekino/phpstan-banned-code": "^1.0",
"ekino/phpstan-banned-code": "^3.0",
"ergebnis/phpstan-rules": "^2.5",
"moxio/php-codesniffer-sniffs": "^2.6",
"phpcsstandards/phpcsextra": "^1.0",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.1",
"phpstan/phpstan-webmozart-assert": "^1.2",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpstan/phpstan-webmozart-assert": "^2.0",
"phpunit/phpunit": "^9.6.16",
"rector/rector": "^1.2",
"roave/no-floaters": "^1.5",
"shipmonk/phpstan-rules": "^3.2",
"rector/rector": "^2.2",
"roave/no-floaters": "^1.13",
"shipmonk/phpstan-rules": "^4.2",
"slevomat/coding-standard": "^8.6",
"squizlabs/php_codesniffer": "^3",
"thecodingmachine/phpstan-strict-rules": "^1.0"
"squizlabs/php_codesniffer": "^3"
},
"config": {
"allow-plugins": {
Expand Down
4 changes: 3 additions & 1 deletion phpstan.extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ includes:
- %currentWorkingDirectory%/vendor/phpstan/phpstan-phpunit/rules.neon
- %currentWorkingDirectory%/vendor/phpstan/phpstan-strict-rules/rules.neon
- %currentWorkingDirectory%/vendor/roave/no-floaters/rules.neon
- %currentWorkingDirectory%/vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon
#- %currentWorkingDirectory%/vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon

rules:
- AssoConnect\PHPStanRules\Rules\DateTimeMustNotBeUsedRule
Expand Down Expand Up @@ -51,6 +51,8 @@ parameters:
'>', '>=', '<', '<=', '<=>', # checked by AllowComparingOnlyComparableTypesRule
'===', '!==', '??' # valid with null involved
]
banned_code:
non_ignorable: false
ignoreErrors:
-
# Prevents using @return mixed[] for all test providers
Expand Down
3 changes: 0 additions & 3 deletions src/Rector/rules.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use Rector\CodingStyle\Rector\ArrowFunction\StaticArrowFunctionRector;
use Rector\CodingStyle\Rector\Closure\StaticClosureRector;
use Rector\Config\RectorConfig;
use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector;

return static function (RectorConfig $rectorConfig): void {
Expand All @@ -16,8 +15,6 @@
StaticClosureRector::class,
// Code Quality
LogicalToBooleanRector::class,
// Strict
BooleanInBooleanNotRuleFixerRector::class,
// Type Declaration
ReturnTypeFromStrictTypedCallRector::class,
]);
Expand Down
12 changes: 10 additions & 2 deletions src/Rules/DateTimeMustNotBeUsedRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

Expand All @@ -24,7 +25,10 @@ public function getNodeType(): string
return New_::class;
}

/** @param New_ $node */
/**
* @param New_ $node
* @return list<IdentifierRuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
if (!$node->class instanceof FullyQualified) {
Expand All @@ -34,7 +38,11 @@ public function processNode(Node $node, Scope $scope): array
$classString = $node->class->toCodeString();

if ('\DateTime' === $classString) {
return [RuleErrorBuilder::message(self::MESSAGE)->tip(self::TIP)->build()];
return [
RuleErrorBuilder::message(self::MESSAGE)->tip(self::TIP)
->identifier('assoconnect.dateTimeMustNotBeUsed')
->build(),
];
}
return [];
}
Expand Down
10 changes: 7 additions & 3 deletions src/Rules/EnforceHttpsLinksRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace AssoConnect\PHPStanRules\Rules;

use PhpParser\Node;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;

/**
Expand All @@ -17,11 +17,15 @@ abstract class EnforceHttpsLinksRule implements Rule
{
public const MESSAGE = 'The string contains an insecure link';

/** @return RuleError[] errors */
/** @return list<IdentifierRuleError> errors */
protected function checkStringValue(string $value): array
{
if (false !== strpos($value, 'http' . ':')) {
return [RuleErrorBuilder::message(self::MESSAGE)->build()];
return [
RuleErrorBuilder::message(self::MESSAGE)
->identifier('assoconnect.insecureLink')
->build(),
];
}

return[];
Expand Down
20 changes: 11 additions & 9 deletions src/Rules/ForbidIdenticalClassComparisonRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CallableType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
Expand Down Expand Up @@ -83,7 +81,7 @@ public function getNodeType(): string

/**
* @param BinaryOp $node
* @return list<RuleError>
* @return list<IdentifierRuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
Expand All @@ -92,7 +90,7 @@ public function processNode(Node $node, Scope $scope): array
}

$nodeType = $scope->getType($node);
if ($nodeType instanceof ConstantBooleanType) {
if ($nodeType->isTrue()->yes() || $nodeType->isFalse()->yes()) {
return []; // always-true or always-false, already reported by native PHPStan (like $a === $a)
}

Expand All @@ -109,7 +107,9 @@ public function processNode(Node $node, Scope $scope): array
$node->getOperatorSigil(),
$leftType->describe(VerbosityLevel::typeOnly()),
$rightType->describe(VerbosityLevel::typeOnly()),
))->build(),
))
->identifier('assoconnect.identicalClassComparison')
->build(),
];
}

Expand All @@ -130,12 +130,14 @@ private function isAccepted(Type $firstArm, Type $otherArm): bool
}


if ($firstArm instanceof ArrayType) {
$arrays = $firstArm->getArrays();
if ([] !== $arrays) {
$firstArray = $arrays[0];
// Empty array
if ($firstArm->isIterableAtLeastOnce()->no()) {
if ($firstArray->isIterableAtLeastOnce()->no()) {
return true;
}
return $this->isAccepted($firstArm->getIterableValueType(), $otherArm);
return $this->isAccepted($firstArray->getIterableValueType(), $otherArm);
}

if ($this->typeIsEnumOrNull($firstArm) && $this->typeIsEnumOrNull($otherArm)) {
Expand Down
2 changes: 1 addition & 1 deletion tests/Rules/EnforceHttpsLinksInPhpDocRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function testTruePositivesAreDetected(): void
{
$this->analyse([__DIR__ . '/EnforceHttpsLinksRule.file.php'], array_fill(
0,
5,
1,
[
EnforceHttpsLinksRule::MESSAGE,
16,
Expand Down
20 changes: 20 additions & 0 deletions tests/Rules/FooBar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace AssoConnect\PHPStanRules\Tests\Rules;

enum FooBar: string
{
case FOO = 'FOO';
case BAR = 'BAR';
}

function enum(): FooBar
{
return FooBar::BAR;
}
function enumNullable(): ?FooBar
{
return random_int(0, 2) < 1 ? FooBar::BAR : null;
}
18 changes: 4 additions & 14 deletions tests/Rules/ForbidIdenticalClassComparisonRule.file.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// phpcs:ignoreFile
declare(strict_types=1);

use AssoConnect\PHPStanRules\Tests\Rules\FooBar;
use function AssoConnect\PHPStanRules\Tests\Rules\enum;
use function AssoConnect\PHPStanRules\Tests\Rules\enumNullable;

(new \DateTime()) === (new \DateTime());
[(new \DateTime())] === [(new \DateTime())];

Expand Down Expand Up @@ -44,20 +48,6 @@
$arrayOfStrings2 = ['b'];
$arrayOfStrings1 === $arrayOfStrings2;

enum FooBar: string
{
case FOO = 'FOO';
case BAR = 'BAR';
}
function enum(): FooBar
{
return FooBar::BAR;
}
function enumNullable(): ?FooBar
{
return FooBar::BAR;
}


FooBar::FOO === enum();
FooBar::FOO === enumNullable();
Expand Down
10 changes: 5 additions & 5 deletions tests/Rules/ForbidIdenticalClassComparisonRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@ public function testTruePositivesAreDetected(): void
$this->analyse([__DIR__ . '/ForbidIdenticalClassComparisonRule.file.php'], [
[
'Using === with DateTime and DateTime is denied',
5,
9,
],
[
'Using === with array<int, DateTime> and array<int, DateTime> is denied',
6,
10,
],
[
'Using === with string|false and string|false is denied',
12,
16,
],
[
'Using === with DateTime|string and DateTime|string is denied',
18,
22,
],
[
'Using === with int|string and int|string is denied',
24,
28,
],
]);
}
Expand Down
Loading