diff --git a/README.md b/README.md index 6681e4e..7c5b142 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ parameters: shipmonkRules: allowComparingOnlyComparableTypes: enabled: true - allowNamedArgumentOnlyInAttributes: - enabled: true backedEnumGenerics: enabled: true classSuffixNaming: @@ -46,9 +44,6 @@ parameters: forbidArithmeticOperationOnNonNumber: enabled: true allowNumericString: false - forbidAssignmentNotMatchingVarDoc: - enabled: true - allowNarrowing: false forbidCast: enabled: true blacklist: ['(array)', '(object)', '(unset)'] @@ -113,7 +108,7 @@ parameters: enabled: true forbidReturnValueInYieldingMethod: enabled: true - reportRegardlessOfReturnType: false + reportRegardlessOfReturnType: true forbidVariableTypeOverwriting: enabled: true forbidUnsetClassField: @@ -162,27 +157,6 @@ new DateTime() > '2040-01-02'; // comparing different types is denied 200 > '1e2'; // comparing different types is denied ``` -### allowNamedArgumentOnlyInAttributes -- Allows usage of named arguments only in native attributes -- Before native attributes, we used [DisallowNamedArguments](https://github.com/slevomat/coding-standard/blob/master/doc/functions.md#slevomatcodingstandardfunctionsdisallownamedarguments) sniff. But we used Doctrine annotations, which almost "require" named arguments when converted to native attributes. -```php -class User { - #[Column(type: Types::STRING, nullable: false)] // allowed - private string $email; - - public function __construct(string $email) { - $this->setEmail(email: $email); // forbidden - } -} -``` -- This one is highly opinionated and will probably be disabled/dropped next major version as it does not provide any extra strictness, you can disable it by: -```neon -parameters: - shipmonkRules: - allowNamedArgumentOnlyInAttributes: - enabled: false -``` - ### backedEnumGenerics * - Ensures that every BackedEnum child defines generic type - This rule makes sense only when BackedEnum was hacked to be generic by stub as described in [this article](https://rnd.shipmonk.com/hacking-generics-into-backedenum-in-php-8-1/) @@ -352,58 +326,6 @@ function add(string $a, string $b) { } ``` -### forbidAssignmentNotMatchingVarDoc -- Verifies if defined type in `@var` phpdoc accepts the assigned type during assignment -- No other places except assignment are checked - -```php -/** @var string $foo */ -$foo = $this->methodReturningInt(); // invalid var phpdoc -``` - -- For reasons of imperfect implementation of [type infering in phpstan-doctrine](https://github.com/phpstan/phpstan-doctrine#query-type-inference), there is an option to check only array-shapes and forget all other types by using `check-shape-only` -- This is helpful for cases where field nullability is eliminated by WHERE field IS NOT NULL which is not propagated to the inferred types -```php -/** @var array $result check-shape-only */ -$result = $queryBuilder->select('t.id') - ->from(Table::class, 't') - ->andWhere('t.id IS NOT NULL') - ->getResult(); -``` - -- It is possible to explicitly allow narrowing of types by `@var` phpdoc by using `allow-narrowing` -```php -/** @var SomeClass $result allow-narrowing */ -$result = $service->getSomeClassOrNull(); -``` -- Or you can enable it widely by using: -```neon -parameters: - shipmonkRules: - forbidAssignmentNotMatchingVarDoc: - allowNarrowing: true -``` - -#### Differences with native check: - -- Since `phpstan/phpstan:1.10.0` with bleedingEdge, there is a [very similar check within PHPStan itself](https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type). -- The main difference is that it allows only subtype (narrowing), not supertype (widening) in `@var` phpdoc. -- This rule allows only widening, narrowing is allowed only when marked by `allow-narrowing` or configured by `allowNarrowing: true`. -- Basically, **there are 3 ways for you to check inline `@var` phpdoc**: - - allow only narrowing - - this rule disabled, native check enabled - - allow narrowing and widening - - this rule enabled with `allowNarrowing: true`, native check disabled - - allow only widening - - this rule enabled, native check disabled - -- You can disable native check while keeping bleedingEdge by: -```neon -parameters: - featureToggles: - varTagType: false -``` - ### forbidCast - Deny casting you configure - Possible values to use: diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4db5e68..8b7b02f 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -37,8 +37,8 @@ parameters: PHPStan\Rules\Rule: Rule PhpParser\NodeVisitor: Visitor ShipMonk\PHPStan\RuleTestCase: RuleTest - forbidAssignmentNotMatchingVarDoc: - enabled: false # native check is better now; this rule will be dropped / reworked in 3.0 + enforceClosureParamNativeTypehint: + enabled: false # we support even PHP 7.4, some typehints cannot be used ignoreErrors: - diff --git a/rules.neon b/rules.neon index 6534840..62375db 100644 --- a/rules.neon +++ b/rules.neon @@ -2,8 +2,6 @@ parameters: shipmonkRules: allowComparingOnlyComparableTypes: enabled: true - allowNamedArgumentOnlyInAttributes: - enabled: true backedEnumGenerics: enabled: true classSuffixNaming: @@ -25,9 +23,6 @@ parameters: forbidArithmeticOperationOnNonNumber: enabled: true allowNumericString: false - forbidAssignmentNotMatchingVarDoc: - enabled: true - allowNarrowing: false forbidCast: enabled: true blacklist: ['(array)', '(object)', '(unset)'] @@ -92,7 +87,7 @@ parameters: enabled: true forbidReturnValueInYieldingMethod: enabled: true - reportRegardlessOfReturnType: false + reportRegardlessOfReturnType: true forbidVariableTypeOverwriting: enabled: true forbidUnsetClassField: @@ -116,9 +111,6 @@ parametersSchema: allowComparingOnlyComparableTypes: structure([ enabled: bool() ]) - allowNamedArgumentOnlyInAttributes: structure([ - enabled: bool() - ]) backedEnumGenerics: structure([ enabled: bool() ]) @@ -149,10 +141,6 @@ parametersSchema: enabled: bool() allowNumericString: bool() ]) - forbidAssignmentNotMatchingVarDoc: structure([ - enabled: bool() - allowNarrowing: bool() - ]) forbidCast: structure([ enabled: bool() blacklist: arrayOf(string()) @@ -243,8 +231,6 @@ parametersSchema: conditionalTags: ShipMonk\PHPStan\Rule\AllowComparingOnlyComparableTypesRule: phpstan.rules.rule: %shipmonkRules.allowComparingOnlyComparableTypes.enabled% - ShipMonk\PHPStan\Rule\AllowNamedArgumentOnlyInAttributesRule: - phpstan.rules.rule: %shipmonkRules.allowNamedArgumentOnlyInAttributes.enabled% ShipMonk\PHPStan\Rule\BackedEnumGenericsRule: phpstan.rules.rule: %shipmonkRules.backedEnumGenerics.enabled% ShipMonk\PHPStan\Rule\ClassSuffixNamingRule: @@ -263,8 +249,6 @@ conditionalTags: phpstan.rules.rule: %shipmonkRules.enforceReadonlyPublicProperty.enabled% ShipMonk\PHPStan\Rule\ForbidArithmeticOperationOnNonNumberRule: phpstan.rules.rule: %shipmonkRules.forbidArithmeticOperationOnNonNumber.enabled% - ShipMonk\PHPStan\Rule\ForbidAssignmentNotMatchingVarDocRule: - phpstan.rules.rule: %shipmonkRules.forbidAssignmentNotMatchingVarDoc.enabled% ShipMonk\PHPStan\Rule\ForbidCastRule: phpstan.rules.rule: %shipmonkRules.forbidCast.enabled% ShipMonk\PHPStan\Rule\ForbidCheckedExceptionInCallableRule: @@ -320,8 +304,6 @@ conditionalTags: ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor: phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidCheckedExceptionInCallable.enabled% - ShipMonk\PHPStan\Visitor\NamedArgumentSourceVisitor: - phpstan.parser.richParserNodeVisitor: %shipmonkRules.allowNamedArgumentOnlyInAttributes.enabled% ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor: phpstan.parser.richParserNodeVisitor: %shipmonkRules.forbidUnusedException.enabled% ShipMonk\PHPStan\Visitor\UnusedMatchVisitor: @@ -339,8 +321,6 @@ conditionalTags: services: - class: ShipMonk\PHPStan\Rule\AllowComparingOnlyComparableTypesRule - - - class: ShipMonk\PHPStan\Rule\AllowNamedArgumentOnlyInAttributesRule - class: ShipMonk\PHPStan\Rule\BackedEnumGenericsRule - @@ -367,10 +347,6 @@ services: class: ShipMonk\PHPStan\Rule\ForbidArithmeticOperationOnNonNumberRule arguments: allowNumericString: %shipmonkRules.forbidArithmeticOperationOnNonNumber.allowNumericString% - - - class: ShipMonk\PHPStan\Rule\ForbidAssignmentNotMatchingVarDocRule - arguments: - allowNarrowing: %shipmonkRules.forbidAssignmentNotMatchingVarDoc.allowNarrowing% - class: ShipMonk\PHPStan\Rule\ForbidCastRule - @@ -449,10 +425,8 @@ services: - class: ShipMonk\PHPStan\Visitor\ImmediatelyCalledCallableVisitor arguments: - immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables% - allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables% - - - class: ShipMonk\PHPStan\Visitor\NamedArgumentSourceVisitor + immediatelyCalledCallables: %shipmonkRules.forbidCheckedExceptionInCallable.immediatelyCalledCallables% + allowedCheckedExceptionCallables: %shipmonkRules.forbidCheckedExceptionInCallable.allowedCheckedExceptionCallables% - class: ShipMonk\PHPStan\Visitor\UnusedExceptionVisitor - diff --git a/src/Rule/AllowNamedArgumentOnlyInAttributesRule.php b/src/Rule/AllowNamedArgumentOnlyInAttributesRule.php deleted file mode 100644 index ac2a23e..0000000 --- a/src/Rule/AllowNamedArgumentOnlyInAttributesRule.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -class AllowNamedArgumentOnlyInAttributesRule implements Rule -{ - - public function getNodeType(): string - { - return Arg::class; - } - - /** - * @param Arg $node - * @return list - */ - public function processNode(Node $node, Scope $scope): array - { - if ($node->name === null) { - return []; - } - - if ($node->getAttribute(NamedArgumentSourceVisitor::IS_ATTRIBUTE_NAMED_ARGUMENT) === true) { - return []; - } - - $error = RuleErrorBuilder::message('Named arguments are allowed only within native attributes') - ->identifier('shipmonk.namedArgumentOutsideAttribute') - ->build(); - return [$error]; - } - -} diff --git a/src/Rule/ClassSuffixNamingRule.php b/src/Rule/ClassSuffixNamingRule.php index d8a5424..82d09bc 100644 --- a/src/Rule/ClassSuffixNamingRule.php +++ b/src/Rule/ClassSuffixNamingRule.php @@ -2,9 +2,11 @@ namespace ShipMonk\PHPStan\Rule; +use LogicException; use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Node\InClassNode; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; @@ -25,8 +27,14 @@ class ClassSuffixNamingRule implements Rule /** * @param array $superclassToSuffixMapping */ - public function __construct(array $superclassToSuffixMapping = []) + public function __construct(ReflectionProvider $reflectionProvider, array $superclassToSuffixMapping = []) { + foreach ($superclassToSuffixMapping as $className => $suffix) { + if (!$reflectionProvider->hasClass($className)) { + throw new LogicException("Class $className used in 'superclassToSuffixMapping' does not exist"); + } + } + $this->superclassToSuffixMapping = $superclassToSuffixMapping; } diff --git a/src/Rule/ForbidAssignmentNotMatchingVarDocRule.php b/src/Rule/ForbidAssignmentNotMatchingVarDocRule.php deleted file mode 100644 index 5531fb6..0000000 --- a/src/Rule/ForbidAssignmentNotMatchingVarDocRule.php +++ /dev/null @@ -1,137 +0,0 @@ - - */ -class ForbidAssignmentNotMatchingVarDocRule implements Rule -{ - - private FileTypeMapper $fileTypeMapper; - - private bool $allowNarrowing; - - public function __construct( - FileTypeMapper $fileTypeMapper, - bool $allowNarrowing - ) - { - $this->fileTypeMapper = $fileTypeMapper; - $this->allowNarrowing = $allowNarrowing; - } - - public function getNodeType(): string - { - return Assign::class; - } - - /** - * @param Assign $node - * @return list - */ - public function processNode(Node $node, Scope $scope): array - { - $checkShapeOnly = false; - $allowNarrowing = $this->allowNarrowing; - $phpDoc = $node->getDocComment(); - - if ($phpDoc === null) { - return []; - } - - if (mb_strpos($phpDoc->getText(), 'check-shape-only') !== false) { - $checkShapeOnly = true; // this is needed for example when phpstan-doctrine deduces nullable field, but you added WHERE IS NOT NULL - } - - if (mb_strpos($phpDoc->getText(), 'allow-narrowing') !== false) { - $allowNarrowing = true; - } - - $phpDocBlock = $this->fileTypeMapper->getResolvedPhpDoc( - $scope->getFile(), - $scope->getClassReflection() !== null ? $scope->getClassReflection()->getName() : null, - $scope->getTraitReflection() !== null ? $scope->getTraitReflection()->getName() : null, - $scope->getFunctionName(), - $phpDoc->getText(), - ); - /** @var VarTag[] $varTags */ - $varTags = $phpDocBlock->getVarTags(); - - $variable = $node->var; - - if (!$variable instanceof Variable) { - return []; - } - - $variableName = $variable->name; - - if (!is_string($variableName)) { - return []; - } - - if (!isset($varTags[$variableName])) { - return []; - } - - $variableType = $varTags[$variableName]->getType(); - $valueType = $scope->getType($node->expr); - - if ($checkShapeOnly) { - $valueType = $this->weakenTypeToKeepShapeOnly($valueType); - } - - if ($variableType->accepts($valueType, $scope->isDeclareStrictTypes())->yes()) { - return []; - } - - $valueTypeString = $valueType->describe(VerbosityLevel::precise()); - $varPhpDocTypeString = $variableType->describe(VerbosityLevel::precise()); - - if ($valueType->accepts($variableType, $scope->isDeclareStrictTypes())->yes() && $variableType->isArray()->no()) { - if ($allowNarrowing) { - return []; - } - - $error = RuleErrorBuilder::message("Invalid var phpdoc of \${$variableName}. Cannot narrow {$valueTypeString} to {$varPhpDocTypeString}") - ->identifier('shipmonk.invalidVarDocAssignment') - ->build(); - return [$error]; - } - - $error = RuleErrorBuilder::message("Invalid var phpdoc of \${$variableName}. Cannot assign {$valueTypeString} to {$varPhpDocTypeString}") - ->identifier('shipmonk.invalidVarDocAssignment') - ->build(); - return [$error]; - } - - private function weakenTypeToKeepShapeOnly(Type $type): Type - { - return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { - if ($type instanceof ArrayType || $type instanceof IterableType) { - return $traverse($type); // keep array shapes, but forget all inner types - } - - return new MixedType(); - }); - } - -} diff --git a/src/Rule/ForbidCheckedExceptionInCallableRule.php b/src/Rule/ForbidCheckedExceptionInCallableRule.php index 3d1911b..2e0e398 100644 --- a/src/Rule/ForbidCheckedExceptionInCallableRule.php +++ b/src/Rule/ForbidCheckedExceptionInCallableRule.php @@ -69,6 +69,9 @@ public function __construct( array $allowedCheckedExceptionCallables ) { + $this->checkClassExistence($reflectionProvider, $immediatelyCalledCallables, 'immediatelyCalledCallables'); + $this->checkClassExistence($reflectionProvider, $allowedCheckedExceptionCallables, 'allowedCheckedExceptionCallables'); + /** @var array> $callablesWithAllowedCheckedExceptions */ $callablesWithAllowedCheckedExceptions = array_merge_recursive($immediatelyCalledCallables, $allowedCheckedExceptionCallables); @@ -339,4 +342,26 @@ private function normalizeArgumentIndexes($argumentIndexes): array return is_int($argumentIndexes) ? [$argumentIndexes] : $argumentIndexes; } + /** + * @param array> $callables + */ + private function checkClassExistence( + ReflectionProvider $reflectionProvider, + array $callables, + string $configName + ): void + { + foreach ($callables as $call => $args) { + if (strpos($call, '::') === false) { + continue; + } + + [$className] = explode('::', $call); + + if (!$reflectionProvider->hasClass($className)) { + throw new LogicException("Class $className used '$configName' in does not exist."); + } + } + } + } diff --git a/src/Rule/ForbidCustomFunctionsRule.php b/src/Rule/ForbidCustomFunctionsRule.php index d506a94..88e4cd3 100644 --- a/src/Rule/ForbidCustomFunctionsRule.php +++ b/src/Rule/ForbidCustomFunctionsRule.php @@ -70,6 +70,10 @@ public function __construct(array $forbiddenFunctions, ReflectionProvider $refle throw new LogicException("Unexpected format of forbidden function {$forbiddenFunction}, expected Namespace\Class::methodName"); } + if ($className !== self::FUNCTION && !$reflectionProvider->hasClass($className)) { + throw new LogicException("Class {$className} used in 'forbiddenFunctions' does not exist"); + } + $this->forbiddenFunctions[$className][$methodName] = $description; } } diff --git a/src/Rule/ForbidIdenticalClassComparisonRule.php b/src/Rule/ForbidIdenticalClassComparisonRule.php index 395f5b0..556d2ba 100644 --- a/src/Rule/ForbidIdenticalClassComparisonRule.php +++ b/src/Rule/ForbidIdenticalClassComparisonRule.php @@ -42,7 +42,7 @@ public function __construct( { foreach ($blacklist as $className) { if (!$reflectionProvider->hasClass($className)) { - throw new LogicException("Class {$className} does not exist."); + throw new LogicException("Class {$className} used in 'forbidIdenticalClassComparison' does not exist."); } } diff --git a/src/Visitor/NamedArgumentSourceVisitor.php b/src/Visitor/NamedArgumentSourceVisitor.php deleted file mode 100644 index f0b4d09..0000000 --- a/src/Visitor/NamedArgumentSourceVisitor.php +++ /dev/null @@ -1,64 +0,0 @@ -stack = []; - return null; - } - - public function enterNode(Node $node): ?Node - { - if ($this->stack !== []) { - $parent = $this->stack[count($this->stack) - 1]; - - if ( - $parent instanceof Attribute - && $node instanceof Arg - && $node->name !== null - ) { - $node->setAttribute(self::IS_ATTRIBUTE_NAMED_ARGUMENT, true); - } - } - - if ($this->shouldBuildStack($node)) { // start adding to stack once Attribute is reached - $this->stack[] = $node; - } - - return null; - } - - private function shouldBuildStack(Node $node): bool - { - return $this->stack !== [] || $node instanceof Attribute; - } - - public function leaveNode(Node $node): ?Node - { - array_pop($this->stack); - return null; - } - -} diff --git a/tests/Rule/AllowNamedArgumentOnlyInAttributesRuleTest.php b/tests/Rule/AllowNamedArgumentOnlyInAttributesRuleTest.php deleted file mode 100644 index 84a14cd..0000000 --- a/tests/Rule/AllowNamedArgumentOnlyInAttributesRuleTest.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -class AllowNamedArgumentOnlyInAttributesRuleTest extends RuleTestCase -{ - - protected function getRule(): Rule - { - return new AllowNamedArgumentOnlyInAttributesRule(); - } - - /** - * @return string[] - */ - public static function getAdditionalConfigFiles(): array - { - return array_merge( - parent::getAdditionalConfigFiles(), - [__DIR__ . '/data/AllowNamedArgumentOnlyInAttributesRule/named-argument-visitor.neon'], - ); - } - - public function testClass(): void - { - if (PHP_VERSION_ID < 80_000) { - self::markTestSkipped('Requires PHP 8.0'); - } - - $this->analyseFile(__DIR__ . '/data/AllowNamedArgumentOnlyInAttributesRule/code.php'); - } - -} diff --git a/tests/Rule/ClassSuffixNamingRuleTest.php b/tests/Rule/ClassSuffixNamingRuleTest.php index be6a7c7..5faaa79 100644 --- a/tests/Rule/ClassSuffixNamingRuleTest.php +++ b/tests/Rule/ClassSuffixNamingRuleTest.php @@ -2,6 +2,7 @@ namespace ShipMonk\PHPStan\Rule; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use ShipMonk\PHPStan\RuleTestCase; @@ -13,11 +14,13 @@ class ClassSuffixNamingRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ClassSuffixNamingRule([ // @phpstan-ignore-line ignore non existing class not being class-string - 'ClassSuffixNamingRule\CheckedParent' => 'Suffix', - 'ClassSuffixNamingRule\CheckedInterface' => 'Suffix2', - 'NotExistingClass' => 'Foo', - ]); + return new ClassSuffixNamingRule( + self::getContainer()->getByType(ReflectionProvider::class), + [ + 'ClassSuffixNamingRule\CheckedParent' => 'Suffix', + 'ClassSuffixNamingRule\CheckedInterface' => 'Suffix2', + ], + ); } public function testClass(): void diff --git a/tests/Rule/ForbidAssignmentNotMatchingVarDocRuleTest.php b/tests/Rule/ForbidAssignmentNotMatchingVarDocRuleTest.php deleted file mode 100644 index ef0d851..0000000 --- a/tests/Rule/ForbidAssignmentNotMatchingVarDocRuleTest.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -class ForbidAssignmentNotMatchingVarDocRuleTest extends RuleTestCase -{ - - private ?bool $allowNarrowing = null; - - protected function getRule(): Rule - { - self::assertNotNull($this->allowNarrowing); - - return new ForbidAssignmentNotMatchingVarDocRule( - self::getContainer()->getByType(FileTypeMapper::class), - $this->allowNarrowing, - ); - } - - public function testDefault(): void - { - $this->allowNarrowing = false; - $this->analyseFile(__DIR__ . '/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-disabled.php'); - } - - public function testNarrowing(): void - { - $this->allowNarrowing = true; - $this->analyseFile(__DIR__ . '/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-enabled.php'); - } - -} diff --git a/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/code.php b/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/code.php deleted file mode 100644 index 3bdbc13..0000000 --- a/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/code.php +++ /dev/null @@ -1,40 +0,0 @@ -myMethod(arg: 1); // error: Named arguments are allowed only within native attributes - var_dump(value: 1); // error: Named arguments are allowed only within native attributes - self::myStaticMethod(arg: 1); // error: Named arguments are allowed only within native attributes - } - - public static function myStaticMethod(int $arg): void - { - - } - -} diff --git a/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/named-argument-visitor.neon b/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/named-argument-visitor.neon deleted file mode 100644 index d0eb433..0000000 --- a/tests/Rule/data/AllowNamedArgumentOnlyInAttributesRule/named-argument-visitor.neon +++ /dev/null @@ -1,5 +0,0 @@ -services: - - - class: ShipMonk\PHPStan\Visitor\NamedArgumentSourceVisitor - tags: - - phpstan.parser.richParserNodeVisitor diff --git a/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-disabled.php b/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-disabled.php deleted file mode 100644 index 2c9e766..0000000 --- a/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-disabled.php +++ /dev/null @@ -1,178 +0,0 @@ -returnArrayShape(); - - /** @var mixed[] $var */ - $var = $this->returnArrayShape(); - - /** @var mixed $var */ - $var = $this->returnArrayShape(); - - /** @var mixed[][] $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array - - /** @var array{id: int, value: string} $var */ - $var = $this->returnArrayShape(); - - /** @var array{id: int, value: string, notPresent: bool} $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array{id: int, value: string, notPresent: bool} - - /** @var array{id: int, value: string, notPresent: bool} $var check-shape-only */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: mixed, value: mixed} to array{id: int, value: string, notPresent: bool} - - /** @var array{id: string, value: string} $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array{id: string, value: string} - - /** @var array{id: string, value: string} $var check-shape-only */ - $var = $this->returnArrayShape(); - - /** @var iterable $var check-shape-only */ - $var = $this->returnIterableWithArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign iterable to iterable - - - /** @var self $var */ - $var = $this->returnSelf(); - - /** @var ExampleClass $var */ - $var = $this->returnSelf(); - - /** @var ExampleInterface $var */ - $var = $this->returnSelf(); - - /** @var ExampleClassParent $var */ - $var = $this->returnSelf(); - - /** @var AnotherClass $var */ - $var = $this->returnSelf(); // error: Invalid var phpdoc of $var. Cannot assign ForbidAssignmentNotMatchingVarDocRule\NoNarrow\ExampleClass to ForbidAssignmentNotMatchingVarDocRule\NoNarrow\AnotherClass - - /** @var ExampleInterface $var */ - $var = $this->returnInterface(); - - /** @var ExampleClass $var */ - $var = $this->returnInterface(); // error: Invalid var phpdoc of $var. Cannot narrow ForbidAssignmentNotMatchingVarDocRule\NoNarrow\ExampleInterface to ForbidAssignmentNotMatchingVarDocRule\NoNarrow\ExampleClass - - /** @var ExampleClass $var allow-narrowing */ - $var = $this->returnInterface(); - - - /** @var int $var */ - $var = $this->returnInt(); - - /** @var int|string $var */ - $var = $this->returnInt(); - - /** @var mixed $var */ - $var = $this->returnInt(); - - - /** @var string $var */ - $var = $this->returnString(); - - /** @var class-string $var */ - $var = $this->returnString(); // error: Invalid var phpdoc of $var. Cannot narrow string to class-string - - /** @var class-string $var allow-narrowing */ - $var = $this->returnString(); - - /** @var string $var */ - $var = $this->returnNullableString(); // error: Invalid var phpdoc of $var. Cannot narrow string|null to string - - /** @var string $var allow-narrowing */ - $var = $this->returnNullableString(); - - /** @var string|null|int $var */ - $var = $this->returnNullableString(); - - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); // error: Invalid var phpdoc of $var. Cannot assign array to array - } - - /** - * @return array - */ - public function returnArrayOfSelf(): array - { - return []; - } - - /** - * @return array{ id: int, value: string } - */ - public function returnArrayShape(): array - { - return ['id' => 1, 'value' => 'foo']; - } - - /** - * @return iterable - */ - public function returnIterableWithArrayShape(): iterable - { - return [['id' => 1, 'value' => 'foo']]; - } - - public function returnInt(): int - { - return 0; - } - - public function returnString(): string - { - return ''; - } - - public function returnNullableString(): ?string - { - return ''; - } - - public function returnSelf(): self - { - return $this; - } - - public function returnInterface(): ExampleInterface - { - return $this; - } - -} diff --git a/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-enabled.php b/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-enabled.php deleted file mode 100644 index ea8f883..0000000 --- a/tests/Rule/data/ForbidAssignmentNotMatchingVarDocRule/narrowing-enabled.php +++ /dev/null @@ -1,178 +0,0 @@ -returnArrayShape(); - - /** @var mixed[] $var */ - $var = $this->returnArrayShape(); - - /** @var mixed $var */ - $var = $this->returnArrayShape(); - - /** @var mixed[][] $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array - - /** @var array{id: int, value: string} $var */ - $var = $this->returnArrayShape(); - - /** @var array{id: int, value: string, notPresent: bool} $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array{id: int, value: string, notPresent: bool} - - /** @var array{id: int, value: string, notPresent: bool} $var check-shape-only */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: mixed, value: mixed} to array{id: int, value: string, notPresent: bool} - - /** @var array{id: string, value: string} $var */ - $var = $this->returnArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign array{id: int, value: string} to array{id: string, value: string} - - /** @var array{id: string, value: string} $var check-shape-only */ - $var = $this->returnArrayShape(); - - /** @var iterable $var check-shape-only */ - $var = $this->returnIterableWithArrayShape(); // error: Invalid var phpdoc of $var. Cannot assign iterable to iterable - - - /** @var self $var */ - $var = $this->returnSelf(); - - /** @var ExampleClass $var */ - $var = $this->returnSelf(); - - /** @var ExampleInterface $var */ - $var = $this->returnSelf(); - - /** @var ExampleClassParent $var */ - $var = $this->returnSelf(); - - /** @var AnotherClass $var */ - $var = $this->returnSelf(); // error: Invalid var phpdoc of $var. Cannot assign ForbidAssignmentNotMatchingVarDocRule\Narrow\ExampleClass to ForbidAssignmentNotMatchingVarDocRule\Narrow\AnotherClass - - /** @var ExampleInterface $var */ - $var = $this->returnInterface(); - - /** @var ExampleClass $var */ - $var = $this->returnInterface(); - - /** @var ExampleClass $var allow-narrowing */ - $var = $this->returnInterface(); - - - /** @var int $var */ - $var = $this->returnInt(); - - /** @var int|string $var */ - $var = $this->returnInt(); - - /** @var mixed $var */ - $var = $this->returnInt(); - - - /** @var string $var */ - $var = $this->returnString(); - - /** @var class-string $var */ - $var = $this->returnString(); - - /** @var class-string $var allow-narrowing */ - $var = $this->returnString(); - - /** @var string $var */ - $var = $this->returnNullableString(); - - /** @var string $var allow-narrowing */ - $var = $this->returnNullableString(); - - /** @var string|null|int $var */ - $var = $this->returnNullableString(); - - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); - - /** @var array $var */ - $var = $this->returnArrayOfSelf(); // error: Invalid var phpdoc of $var. Cannot assign array to array - } - - /** - * @return array - */ - public function returnArrayOfSelf(): array - { - return []; - } - - /** - * @return array{ id: int, value: string } - */ - public function returnArrayShape(): array - { - return ['id' => 1, 'value' => 'foo']; - } - - /** - * @return iterable - */ - public function returnIterableWithArrayShape(): iterable - { - return [['id' => 1, 'value' => 'foo']]; - } - - public function returnInt(): int - { - return 0; - } - - public function returnString(): string - { - return ''; - } - - public function returnNullableString(): ?string - { - return ''; - } - - public function returnSelf(): self - { - return $this; - } - - public function returnInterface(): ExampleInterface - { - return $this; - } - -}