Skip to content

Conversation

@acoulton
Copy link
Contributor

@acoulton acoulton commented Dec 8, 2025

This is a first pass at adding backwards-compatible return types that we can safely merge into the 3.x series, as we work towards #1744.

The return types applied are based on:

  • Rector's type coverage rules (up to level 21) - these are based on the actual type returned where this can be conclusively determined from static analysis.
  • Manual modification to the phpdoc @return type of any interface the method implements, if this was less specific than the type that Rector detected. This is the type Rector would have used if the interface had a strict return type, but we cannot add those until 4.x.

I've committed this a level at a time, running phpstan at each commit and resolving any errors it produced (there were a few due to invalid / old-style phpdoc @return tags for lists / arrays etc that were previously ignored but now don't match the runtime return type.

Return types are added to

  • public, protected and private methods in final classes
  • private methods in non-final classes
  • final public methods in non-final classes

Rector mostly limited itself to these rules, but there were a few cases where by default it added types to non-final classes (I think because they are not extended anywhere by us, and the parent method has no strict type). I've excluded those changes.

Note that this depends on #1762 and will be rebased after that is merged. now rebased

@acoulton
Copy link
Contributor Author

acoulton commented Dec 8, 2025

@carlos-granados This is quite a big PR, but hopefully keeping it as separate commits will make it easier to review.

@acoulton acoulton force-pushed the 3.x-initial-strict-types branch from 7f4cfc1 to d1cb94c Compare December 8, 2025 11:40
@carlos-granados
Copy link
Contributor

@acoulton I'd like to take a good look at this PR, will try to do it this evening, please don't merge before I check it

The rector run in CI runs on PHP 8.1, which means it also installs
symfony 5.4. In symfony/console 5.4, `getOption` has no strict return
type so Rector ignores use of it.

Newer symfony explicitly types this as `:mixed`, so running Rector on a
later php version (e.g. locally) will by default add a (string) cast to
the `explode` statement. However, in practice this is unnecessary
because symfony/console guarantees that an option with
`InputOption::VALUE_REQUIRED` will give a value of `?string`.
Rules applied:
- AddArrowFunctionReturnTypeRector
- NullToStrictStringFuncCallArgRector
Rules applied:
- BoolReturnTypeFromBooleanConstReturnsRector
These can have a more specific type in the interface phpdoc than we had
before, and they can also have strict returns on their final
implementations.

Also improves the phpdoc type of internal class maps.
Where these are guaranteed to return explicit arrays. Based on the
Rector rule but manually applied to improve the phpdoc typing of the
results.
Rules applied:
- ReturnTypeFromStrictConstantReturnRector
Rules applied:
- StringReturnTypeFromStrictScalarReturnsRector
Rules applied:
- NumericReturnTypeFromStrictScalarReturnsRector
Rules applied:
- BoolReturnTypeFromBooleanStrictReturnsRector
Rules applied:
- StringReturnTypeFromStrictStringReturnsRector
Rules applied:
- NumericReturnTypeFromStrictReturnsRector
- StringReturnTypeFromStrictStringReturnsRector
Rules applied:
- ReturnTypeFromStrictTernaryRector
With manual fixes to some phpdoc array/list types

Rules applied:
- ReturnTypeFromReturnDirectArrayRector
Started off with Rector to apply level 17, then adjusted manually to
match interfaces (which Rector does not fully respect as they are only
phpdoc).

Rules applied:
- ReturnTypeFromReturnNewRector
Applied rules:
- AddVoidReturnTypeWhereNoReturnRector
Rules applied:
- ReturnTypeFromStrictTypedPropertyRector
- AddArrowFunctionReturnTypeRector
- ExplicitBoolCompareRector
Although these classes are not final, the individual methods are, and
therefore it is BC to add a strict return type to them.

Note that roave/backward-compatibility-check does not recognise that
this is safe.

Rules applied:
- ReturnTypeFromStrictTypedPropertyRector
Line numbering in RuntimeCallHandler has changed due to removal of
now-redundant `@return` tags.
@acoulton acoulton force-pushed the 3.x-initial-strict-types branch from d1cb94c to 4b95d58 Compare December 8, 2025 11:47
@acoulton
Copy link
Contributor Author

acoulton commented Dec 8, 2025

@acoulton I'd like to take a good look at this PR, will try to do it this evening, please don't merge before I check it

I'd be delighted if you did, very happy to wait for your review whenever you get a chance :)

Copy link
Contributor

@carlos-granados carlos-granados left a comment

Choose a reason for hiding this comment

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

Finished reviewing these changes, outstanding work, many thanks @acoulton!

@acoulton
Copy link
Contributor Author

This is now updated to revert the temporary Rector config changes.

I will squash and release it after we release 3.28.0.

@acoulton
Copy link
Contributor Author

@carlos-granados I have added the changelog for 3.29.0 directly to this branch for simplicity. There are no changes to code (other than reverting the rector config) since you reviewed.

Copy link
Contributor

@carlos-granados carlos-granados left a comment

Choose a reason for hiding this comment

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

Let's go ahead with this

@acoulton acoulton merged commit 51bdf81 into Behat:3.x Dec 11, 2025
22 checks passed
@acoulton acoulton deleted the 3.x-initial-strict-types branch December 11, 2025 09:51
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.

3 participants