Make WordPress Core

Changeset 62642


Ignore:
Timestamp:
07/06/2026 03:47:16 PM (2 months ago)
Author:
westonruter
Message:

Build/Test Tools: Honor statement-level @global tags in PHPStan.

The GlobalDocBlockVisitor PHPStan extension introduced in [62292] only read @global tags from the enclosing function's docblock and skipped global statements outside any function/method. This meant that file-scope global statements (e.g. in admin templates included into another scope) required a redundant inline @var tag in addition to the @global tag for PHPStan to resolve the variable's type.

Now @global tags in a docblock attached directly to a global statement are honored as well. Statement-level tags take precedence over the enclosing function's tags for the same variable, and handwritten @var annotations continue to take precedence over synthetic ones. At higher PHPStan rule levels this resolves several hundred pre-existing errors, primarily in admin screen templates.

Developed in https://github.com/WordPress/wordpress-develop/pull/12407.
Follow-up to r62292.

See #64898.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpstan/GlobalDocBlockVisitor.php

    r62292 r62642  
    2626 * visitor closes the gap so PHPStan can use the existing core annotations
    2727 * without each `global` statement needing its own redundant `@var`.
     28 *
     29 * `@global` tags in a docblock attached directly to a `global` statement are
     30 * honored as well, and take precedence over the enclosing function's tags.
     31 * This makes `@global` sufficient for file-scope `global` statements (e.g. in
     32 * templates included into another scope), which have no enclosing function
     33 * docblock at all.
    2834 *
    2935 * Functions that do not document a global, or that import a global the
     
    7480                }
    7581
    76                 if ( ! ( $node instanceof Node\Stmt\Global_ ) || $this->stack === array() ) {
     82                if ( ! ( $node instanceof Node\Stmt\Global_ ) ) {
    7783                        return null;
    7884                }
    7985
    80                 $map = $this->stack[ count( $this->stack ) - 1 ];
     86                $map = $this->stack !== array() ? $this->stack[ count( $this->stack ) - 1 ] : array();
     87
     88                $existing      = $node->getDocComment();
     89                $existing_text = $existing !== null ? $existing->getText() : '';
     90
     91                /*
     92                 * Also honor `@global` tags on the docblock attached directly to the
     93                 * `global` statement itself. This covers file-scope statements (which
     94                 * have no enclosing function docblock) and takes precedence over the
     95                 * enclosing function's tags for the same variable.
     96                 */
     97                if ( $existing_text !== '' ) {
     98                        $map = array_merge( $map, $this->parse_global_tags( $existing_text ) );
     99                }
     100
    81101                if ( $map === array() ) {
    82102                        return null;
     
    88108                 * the remaining variables in a multi-variable `global $a, $b;` statement.
    89109                 */
    90                 $existing       = $node->getDocComment();
    91                 $existing_text  = $existing !== null ? $existing->getText() : '';
    92                 $already_typed  = array();
     110                $already_typed = array();
    93111                if ( $existing_text !== '' && preg_match_all( '/@(?:phpstan-)?var\s+[^\n]*?\$(\w+)/', $existing_text, $existing_matches ) > 0 ) {
    94112                        $already_typed = array_flip( $existing_matches[1] );
Note: See TracChangeset for help on using the changeset viewer.