Code Quality: Annotate WP_HTML_Tag_Processor::parse_next_tag() as impure - #13057
Code Quality: Annotate WP_HTML_Tag_Processor::parse_next_tag() as impure#13057tstokes8040 wants to merge 1 commit into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
| * | ||
| * @return bool Whether a tag was found before the end of the document. | ||
| * | ||
| * @phpstan-impure |
There was a problem hiding this comment.
This is consistent with the same which was added in 68c0f19 on the next_tag() method:
wordpress-develop/src/wp-includes/html-api/class-wp-html-tag-processor.php
Lines 895 to 899 in b84edad
…ure.
PHPStan infers `parse_next_tag()` as pure, so it retains the `STATE_READY` narrowing assigned to `$parser_state` before the call, even though the method reassigns that property throughout. Every subsequent state comparison in `base_class_next_token()` is then decided statically, which additionally left the remainder of that method analyzed as unreachable and two of its callees reported as unused.
This empties the `booleanAnd.alwaysTrue` and `property.unusedType` PHPStan baselines, so both files are removed along with their `includes` entries in `phpstan.neon.dist`. The baselines were regenerated with:
{{{
composer phpstan:baselines -- --identifier=booleanAnd.alwaysTrue,notIdentical.alwaysTrue,identical.alwaysFalse,deadCode.unreachable,method.unused,property.unusedType
}}}
Props tstokes8040.
See #65817.
b84edad to
0cd03e6
Compare
…PHPStan. PHPStan infers the `WP_HTML_Tag_Processor::parse_next_tag()` method as pure, so the `STATE_READY` value narrowed onto `$parser_state` before the call survives across it, even though the method reassigns that property throughout its body. Every state comparison that follows in the `base_class_next_token()` method is then decided at analysis time rather than at runtime: both `&&` operands resolve to `true`, the early `return true` is treated as unconditional, and the remainder of the method is analyzed as unreachable. This in turn leaves `skip_rawtext()` and `skip_script_data()` reported as uncalled and `$skip_newline_at` as never assigned an `int`. Adding `@phpstan-impure`, the annotation PHPStan's own tip recommends, resolves ten baselined errors across six identifiers; `next_tag()` in the same class already carries it. This empties the `booleanAnd.alwaysTrue` and `property.unusedType` baselines, so both files are removed along with their `includes` entries in `phpstan.neon.dist`. Developed in #13057. Follow-up to r61934, r63023. Props tstokes8040. See #65817. git-svn-id: https://develop.svn.wordpress.org/trunk@63301 602fd350-edb4-49c9-b593-d223f7449a82
…PHPStan. PHPStan infers the `WP_HTML_Tag_Processor::parse_next_tag()` method as pure, so the `STATE_READY` value narrowed onto `$parser_state` before the call survives across it, even though the method reassigns that property throughout its body. Every state comparison that follows in the `base_class_next_token()` method is then decided at analysis time rather than at runtime: both `&&` operands resolve to `true`, the early `return true` is treated as unconditional, and the remainder of the method is analyzed as unreachable. This in turn leaves `skip_rawtext()` and `skip_script_data()` reported as uncalled and `$skip_newline_at` as never assigned an `int`. Adding `@phpstan-impure`, the annotation PHPStan's own tip recommends, resolves ten baselined errors across six identifiers; `next_tag()` in the same class already carries it. This empties the `booleanAnd.alwaysTrue` and `property.unusedType` baselines, so both files are removed along with their `includes` entries in `phpstan.neon.dist`. Developed in WordPress/wordpress-develop#13057. Follow-up to r61934, r63023. Props tstokes8040. See #65817. Built from https://develop.svn.wordpress.org/trunk@63301 git-svn-id: http://core.svn.wordpress.org/trunk@62494 1a063a9b-81f0-0310-95a4-ce76da25c4cd
✅ Committed in r63301 (3ecb392).
PHPStan infers
WP_HTML_Tag_Processor::parse_next_tag()as pure, so it retains theSTATE_READYnarrowing assigned to$parser_stateon line 981 across the call on line 989 — even thoughparse_next_tag()reassigns that property throughout its body. Every state comparison after the call is then decided at analysis time rather than at runtime.The effects compound. The three
!==comparisons on lines 1004–1006 each resolve totrue, so both&&nodes in that condition resolve totrue, so the earlyreturn trueon line 1008 is treated as unconditional. Everything from line 1012 to the end ofbase_class_next_token()is consequently analyzed as unreachable, which in turn makesskip_rawtext()andskip_script_data()appear uncalled and$skip_newline_atappear never assigned anint.@phpstan-impureis the annotation PHPStan's own tip recommends here, and there is precedent for it in this very file:next_tag()already carries one.Ten baselined errors across six identifiers are resolved:
booleanAnd.alwaysTruenotIdentical.alwaysTrueidentical.alwaysFalsedeadCode.unreachablemethod.unusedproperty.unusedTypebooleanAnd.alwaysTrueandproperty.unusedTypereach zero, so as the baseline headers direct, both files are deleted along with theirincludesentries inphpstan.neon.dist. The baselines were regenerated withcomposer phpstan:baselines -- --identifier=..., not edited by hand.Testing instructions
npm run typecheck:phpontrunkreports[OK] No errors, because the occurrences are baselined.tests/phpstan/baselines/booleanAnd.alwaysTrue.neonand itsincludesentry, then re-run: PHPStan reportsResult of && is always true.twice atsrc/wp-includes/html-api/class-wp-html-tag-processor.php:1004.npm run typecheck:phpreports[OK] No errorsacross 1289 files with both baselines gone.composer lintis also clean for the modified file.npm run test:php— 30865 tests, 4558966 assertions, 86 warnings, 44 skipped, and one failure:Tests_Script_Modules_WpScriptModules::test_default_script_module_files_exist, looking forsrc/wp-includes/js/dist/script-modules/a11y/index.js. That path is gitignored build output that is unbuilt in my checkout, and the test fails identically on unmodifiedtrunk, so it is unrelated to this change.npm run test:php -- --group html-apipasses 1714 tests, 6107 assertions, 0 failures.Documentation-only change; no runtime behaviour is affected.
Trac ticket: https://core.trac.wordpress.org/ticket/65817
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: locating the occurrence via the PHPStan
booleanAnd.alwaysTruebaseline, diagnosing the purity-inference cause and its knock-on effects, regenerating the baselines, and drafting this description. The change itself and the verification against the full PHPStan, PHPCS and PHPUnit runs were reviewed and confirmed by me in a local development environment.