From 529e46deddc397651ba088d272d98bb010ecf40a Mon Sep 17 00:00:00 2001 From: Dimitrios Pantazis Date: Sun, 16 Aug 2026 14:22:36 -0700 Subject: [PATCH 1/2] fix: remove greater.invalid baseline and fix its issues --- phpstan.neon.dist | 1 - src/wp-includes/category.php | 2 + .../class-wp-customize-manager.php | 8 + .../class-wp-customize-widgets.php | 2 + .../class-wp-image-editor-imagick.php | 2 + src/wp-includes/class-wp-widget.php | 3 +- ...lass-wp-customize-header-image-control.php | 2 + src/wp-includes/general-template.php | 52 ++-- src/wp-includes/link-template.php | 32 ++- src/wp-includes/user.php | 3 + tests/phpstan/baselines/return.missing.neon | 225 ------------------ 11 files changed, 78 insertions(+), 254 deletions(-) delete mode 100644 tests/phpstan/baselines/return.missing.neon diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e2e72fb367482..a68171fecb7fd 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -80,7 +80,6 @@ includes: - tests/phpstan/baselines/property.private.neon - tests/phpstan/baselines/property.protected.neon - tests/phpstan/baselines/return.empty.neon - - tests/phpstan/baselines/return.missing.neon - tests/phpstan/baselines/return.type.neon - tests/phpstan/baselines/return.unusedType.neon - tests/phpstan/baselines/smallerOrEqual.alwaysTrue.neon diff --git a/src/wp-includes/category.php b/src/wp-includes/category.php index 8fb6b1c5479ba..dbb48d630b076 100644 --- a/src/wp-includes/category.php +++ b/src/wp-includes/category.php @@ -176,6 +176,8 @@ function get_category_by_path( $category_path, $full_match = true, $output = OBJ return $category; } + + return null; } /** diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index e298b04efcf90..6ee4292496aa6 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -3875,6 +3875,8 @@ public function get_setting( $id ) { if ( isset( $this->settings[ $id ] ) ) { return $this->settings[ $id ]; } + + return null; } /** @@ -3927,6 +3929,8 @@ public function get_panel( $id ) { if ( isset( $this->panels[ $id ] ) ) { return $this->panels[ $id ]; } + + return null; } /** @@ -4023,6 +4027,8 @@ public function get_section( $id ) { if ( isset( $this->sections[ $id ] ) ) { return $this->sections[ $id ]; } + + return null; } /** @@ -4102,6 +4108,8 @@ public function get_control( $id ) { if ( isset( $this->controls[ $id ] ) ) { return $this->controls[ $id ]; } + + return null; } /** diff --git a/src/wp-includes/class-wp-customize-widgets.php b/src/wp-includes/class-wp-customize-widgets.php index 7484d2deee7da..d7cd0d5de15dd 100644 --- a/src/wp-includes/class-wp-customize-widgets.php +++ b/src/wp-includes/class-wp-customize-widgets.php @@ -189,6 +189,8 @@ protected function get_setting_type( $setting_id ) { return $type; } } + + return null; } /** diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 57ed7c79e56ae..dfcc28e5a1b12 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -531,6 +531,8 @@ public static function set_imagick_time_limit() { return $limit; } + + return null; } /** diff --git a/src/wp-includes/class-wp-widget.php b/src/wp-includes/class-wp-widget.php index dc84f54774162..ab4178afa32cc 100644 --- a/src/wp-includes/class-wp-widget.php +++ b/src/wp-includes/class-wp-widget.php @@ -138,7 +138,8 @@ public function update( $new_instance, $old_instance ) { * @since 2.8.0 * * @param array $instance The settings for the particular instance of the widget. - * @return string|null Default return is 'noform'. A subclass may opt to return null. + * + * @return string|null|void Default return is 'noform'. A subclass may opt to return nothing. */ public function form( $instance ) { echo '

' . __( 'There are no options for this widget.' ) . '

'; diff --git a/src/wp-includes/customize/class-wp-customize-header-image-control.php b/src/wp-includes/customize/class-wp-customize-header-image-control.php index f6da1275aec94..23018366425e9 100644 --- a/src/wp-includes/customize/class-wp-customize-header-image-control.php +++ b/src/wp-includes/customize/class-wp-customize-header-image-control.php @@ -201,6 +201,8 @@ public function get_current_image_src() { $src = call_user_func( $this->get_url, $src ); return $src; } + + return null; } /** diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 22a1d3e307d3e..b626b5ca605ce 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -1673,11 +1673,13 @@ function wp_title( $sep = '»', $display = true, $seplocation = '' ) { $title = apply_filters( 'wp_title', $title, $sep, $seplocation ); // Send it out. - if ( $display ) { - echo $title; - } else { + if ( ! $display ) { return $title; } + + echo $title; + + return null; } /** @@ -1712,11 +1714,14 @@ function single_post_title( $prefix = '', $display = true ) { * @param WP_Post $_post The current post. */ $title = apply_filters( 'single_post_title', $_post->post_title, $_post ); - if ( $display ) { - echo $prefix . $title; - } else { + + if ( ! $display ) { return $prefix . $title; } + + echo $prefix . $title; + + return null; } /** @@ -1753,11 +1758,13 @@ function post_type_archive_title( $prefix = '', $display = true ) { */ $title = apply_filters( 'post_type_archive_title', $post_type_obj->labels->name, $post_type ); - if ( $display ) { - echo $prefix . $title; - } else { + if ( ! $display ) { return $prefix . $title; } + + echo $prefix . $title; + + return null; } /** @@ -1849,11 +1856,13 @@ function single_term_title( $prefix = '', $display = true ) { return null; } - if ( $display ) { - echo $prefix . $term_name; - } else { + if ( ! $display ) { return $prefix . $term_name; } + + echo $prefix . $term_name; + + return null; } /** @@ -1896,7 +1905,10 @@ function single_month_title( $prefix = '', $display = true ) { if ( ! $display ) { return $result; } + echo $result; + + return null; } /** @@ -2910,11 +2922,13 @@ function the_date( $format = '', $before = '', $after = '', $display = true ) { */ $the_date = apply_filters( 'the_date', $the_date, $format, $before, $after ); - if ( $display ) { - echo $the_date; - } else { + if ( ! $display ) { return $the_date; } + + echo $the_date; + + return null; } /** @@ -2978,11 +2992,13 @@ function the_modified_date( $format = '', $before = '', $after = '', $display = */ $the_modified_date = apply_filters( 'the_modified_date', $the_modified_date, $format, $before, $after ); - if ( $display ) { - echo $the_modified_date; - } else { + if ( ! $display ) { return $the_modified_date; } + + echo $the_modified_date; + + return null; } /** diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php index 484a8c8f8591c..4c6b1fa929586 100644 --- a/src/wp-includes/link-template.php +++ b/src/wp-includes/link-template.php @@ -1164,11 +1164,13 @@ function edit_term_link( $link = '', $before = '', $after = '', $term = null, $d */ $link = $before . apply_filters( 'edit_term_link', $link, $term->term_id ) . $after; - if ( $display ) { - echo $link; - } else { + if ( ! $display ) { return $link; } + + echo $link; + + return null; } /** @@ -2530,6 +2532,8 @@ function get_next_posts_page_link( $max_page = 0 ) { return get_pagenum_link( $next_page ); } } + + return null; } /** @@ -2545,11 +2549,13 @@ function next_posts( $max_page = 0, $display = true ) { $link = get_next_posts_page_link( $max_page ); $output = $link ? esc_url( $link ) : ''; - if ( $display ) { - echo $output; - } else { + if ( ! $display ) { return $output; } + + echo $output; + + return null; } /** @@ -2598,6 +2604,8 @@ function get_next_posts_link( $label = null, $max_page = 0 ) { preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&$1', $label ) ); } + + return null; } /** @@ -2637,6 +2645,8 @@ function get_previous_posts_page_link() { return get_pagenum_link( $previous_page ); } + + return null; } /** @@ -2651,11 +2661,13 @@ function previous_posts( $display = true ) { $link = get_previous_posts_page_link(); $output = $link ? esc_url( $link ) : ''; - if ( $display ) { - echo $output; - } else { + if ( ! $display ) { return $output; } + + echo $output; + + return null; } /** @@ -2692,6 +2704,8 @@ function get_previous_posts_link( $label = null ) { preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&$1', $label ) ); } + + return null; } /** diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 539240a42e9d2..31b158aff92f7 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -1014,7 +1014,10 @@ function wp_list_users( $args = array() ) { if ( ! $parsed_args['echo'] ) { return $return; } + echo $return; + + return null; } /** diff --git a/tests/phpstan/baselines/return.missing.neon b/tests/phpstan/baselines/return.missing.neon deleted file mode 100644 index 11bb654bafb2c..0000000000000 --- a/tests/phpstan/baselines/return.missing.neon +++ /dev/null @@ -1,225 +0,0 @@ -# PHPStan baseline for the `return.missing` errors in WordPress core. -# -# https://phpstan.org/error-identifiers/return.missing -# -# Each entry is scoped to a single file and carries an exact occurrence count, -# so that a new instance is reported as a new error rather than being absorbed -# silently. Fixing an occurrence therefore means decrementing or removing its -# entry here as part of the same change. -# -# The goal is to empty this file and delete it, along with the `includes` entry -# for it in phpstan.neon.dist. -# -# Generated by `composer phpstan:baselines`. Do not edit by hand; regenerate with -# -# composer phpstan:baselines -- --identifier=return.missing -# -# which reruns the analysis with this file suppressed so the errors surface again. - -parameters: - ignoreErrors: - - - message: '#^Method Twenty_Eleven_Ephemera_Widget\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-content/themes/twentyeleven/inc/widgets.php - - - message: '#^Method Twenty_Fourteen_Ephemera_Widget\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-content/themes/twentyfourteen/inc/widgets.php - - - message: '#^Function get_category_by_path\(\) should return array\|WP_Error\|WP_Term\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/category.php - - - message: '#^Method WP_Customize_Manager\:\:get_control\(\) should return WP_Customize_Control\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-customize-manager.php - - - message: '#^Method WP_Customize_Manager\:\:get_panel\(\) should return WP_Customize_Panel\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-customize-manager.php - - - message: '#^Method WP_Customize_Manager\:\:get_section\(\) should return WP_Customize_Section\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-customize-manager.php - - - message: '#^Method WP_Customize_Manager\:\:get_setting\(\) should return WP_Customize_Setting\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-customize-manager.php - - - message: '#^Method WP_Customize_Widgets\:\:get_setting_type\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-customize-widgets.php - - - message: '#^Method WP_Image_Editor_Imagick\:\:set_imagick_time_limit\(\) should return int\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/class-wp-image-editor-imagick.php - - - message: '#^Method WP_Customize_Header_Image_Control\:\:get_current_image_src\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/customize/class-wp-customize-header-image-control.php - - - message: '#^Function post_type_archive_title\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function single_month_title\(\) should return string\|false\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function single_post_title\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function single_term_title\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function the_date\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function the_modified_date\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function wp_title\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/general-template.php - - - message: '#^Function edit_term_link\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function get_next_posts_link\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function get_next_posts_page_link\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 2 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function get_previous_posts_link\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function get_previous_posts_page_link\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function next_posts\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function previous_posts\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/link-template.php - - - message: '#^Function wp_list_users\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/user.php - - - message: '#^Method WP_Nav_Menu_Widget\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-nav-menu-widget.php - - - message: '#^Method WP_Widget_Archives\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-archives.php - - - message: '#^Method WP_Widget_Block\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-block.php - - - message: '#^Method WP_Widget_Calendar\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-calendar.php - - - message: '#^Method WP_Widget_Categories\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-categories.php - - - message: '#^Method WP_Widget_Custom_HTML\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-custom-html.php - - - message: '#^Method WP_Widget_Links\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-links.php - - - message: '#^Method WP_Widget_Media\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-media.php - - - message: '#^Method WP_Widget_Meta\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-meta.php - - - message: '#^Method WP_Widget_Pages\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-pages.php - - - message: '#^Method WP_Widget_Recent_Comments\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-recent-comments.php - - - message: '#^Method WP_Widget_Recent_Posts\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-recent-posts.php - - - message: '#^Method WP_Widget_RSS\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-rss.php - - - message: '#^Method WP_Widget_Search\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: ../../../src/wp-includes/widgets/class-wp-widget-search.php - - - message: '#^Method WP_Widget_Tag_Cloud\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 2 - path: ../../../src/wp-includes/widgets/class-wp-widget-tag-cloud.php - - - message: '#^Method WP_Widget_Text\:\:form\(\) should return string\|null but return statement is missing\.$#' - identifier: return.missing - count: 2 - path: ../../../src/wp-includes/widgets/class-wp-widget-text.php From 25876fcddbd70227edd2995fc214f619498cbeb5 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 27 Aug 2026 14:00:03 -0700 Subject: [PATCH 2/2] Use `string|void` for the `WP_Widget::form()` return type The annotation had been widened to `string|null|void`, but the `null` there is inert. PHPStan resolves `string|null|void` and `string|void` to the same call-site type, `string|null`, and suppresses `return.missing` on overrides in both cases; only `string|null` on its own makes the return obligatory. The union therefore behaved exactly as `string|void` while reading as though the stricter `null` spelling had been chosen. That distinction matters for this method. The base implementation echoes a notice and returns 'noform', while the 18 subclass overrides echo their own markup and fall off the end, four of them in bundled themes. `void` is the accurate description of that contract, and the one that keeps the overrides compliant. `string|null|void` was also the only union in `src/` to combine `null` and `void`, and it ordered `void` last where 45 of the 47 surviving `void` unions in core put it first. Matches the wording already used for this method on the `fix/phpstan-7.1-regressions` branch, and drops the blank line that had been inserted between `@param` and `@return`, which was the only such gap in the file. Co-Authored-By: Claude Opus 5 (1M context) --- src/wp-includes/class-wp-widget.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-widget.php b/src/wp-includes/class-wp-widget.php index ab4178afa32cc..b131c50db3226 100644 --- a/src/wp-includes/class-wp-widget.php +++ b/src/wp-includes/class-wp-widget.php @@ -138,8 +138,8 @@ public function update( $new_instance, $old_instance ) { * @since 2.8.0 * * @param array $instance The settings for the particular instance of the widget. - * - * @return string|null|void Default return is 'noform'. A subclass may opt to return nothing. + * @return string|void Default return is 'noform'. A subclass which echoes its own + * form returns nothing. */ public function form( $instance ) { echo '

' . __( 'There are no options for this widget.' ) . '

';