Skip to content

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

Fixes a false positive argument.type error where passing a generic object with a nullable type argument (e.g., Foo<string|null>) to a parameter expecting the exact same type would report "expects Foo<string|null>, Foo<string|null> given" at rule levels 5-7 (but not at level 8+).

Changes

  • Modified GenericObjectType::isSuperTypeOfInternal() in src/Type/Generic/GenericObjectType.php to handle the accepts context for invariant template type parameters: when the invariance check fails, verify whether the mismatch is only due to asymmetric null stripping by RuleLevelHelper::transformAcceptedType
  • Added regression test in tests/PHPStan/Rules/Methods/data/bug-10698.php and test method in tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Root cause

RuleLevelHelper::transformAcceptedType() uses TypeTraverser::map to strip null from union types when checkNullables is false (rule levels < 8). This traversal recurses into GenericObjectType's inner type arguments, transforming Foo<string|null> (accepted) into Foo<string>. However, the accepting type Foo<string|null> retains its null. The invariance check in TemplateTypeVariance::isValidVariance() then compares string|null (from accepting) vs string (from accepted) using equals(), which fails, producing the false positive.

The fix adds a fallback check in GenericObjectType::isSuperTypeOfInternal(): when in accepts context and the invariance check fails, it verifies whether the accepting type without null equals the accepted type (i.e., the mismatch was solely due to null stripping). If so, the result is treated as a match.

Test

Added a regression test that reproduces the exact scenario from the issue: a generic class Foo<T> passed to a method expecting Foo<T>, where the type argument is string|null. The test runs with checkNullables=false and checkUnionTypes=true (simulating level 7) and verifies no error is reported.

Fixes phpstan/phpstan#10698

… types

- Fixed false positive where passing Foo<string|null> to a parameter expecting
  Foo<string|null> reported an error at rule levels 5-7
- Root cause: RuleLevelHelper::transformAcceptedType strips null from inside
  generic type arguments of the accepted type, but the accepting type retains
  its nulls, causing the invariance check to compare string|null vs string
- Fix in GenericObjectType::isSuperTypeOfInternal: when in accepts context,
  if the invariance check fails, verify whether the difference is only due to
  null stripping by checking if the types are equivalent modulo null
- New regression test in tests/PHPStan/Rules/Methods/data/bug-10698.php

Fixes phpstan/phpstan#10698
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive argument.type (… expects Foo<string|null>, Foo<string|null> given.)

2 participants