Change !isset to ??= assignment operator: phpstan errors "always exists and is not nullable" case.

Created on 27 February 2024, 9 months ago
Updated 26 March 2024, 8 months ago

Problem/Motivation

After some code changes in πŸ“Œ Use null coalescing assignment operator: multi-lines case. Needs work using ??= operator the MR faced obstructions in #3423310-#11 πŸ“Œ Use null coalescing assignment operator: multi-lines case. Needs work from phpstan: 4 = 2x2 errors: 2 files,

--------------------------------------------------------------------------------- 
  Line   core/modules/views/src/Plugin/views/display/PathPluginBase.php
 --------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$access_plugin in isset\(\) always
         exists and is not nullable\.$# in path                                   
         /builds/issue/drupal-3423310/core/modules/views/src/Plugin/views/display/PathPluginBase.php
         was not matched in reported errors.                                      
  199    Variable $access_plugin on left side of ??= always exists and is not     
         nullable.                                                                   
 ----------------------------------------------------------------------------------
 ----------------------------------------------------------------------------------
  Line   core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
 ---------------------------------------------------------------------------------- 
         Ignored error pattern #^Variable \$message in isset\(\) always exists       
         and is not nullable\.$# in path                                             
         /builds/issue/drupal-3423310/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
         was not matched in reported errors.                                         
  309    Variable $message on left side of ??= always exists and is not              
         nullable.                                                                 
 ----------------------------------------------------------------------------------
 [ERROR] Found 4 errors

The two code changes are,

--- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php
@@ -195,10 +195,8 @@ protected function getRoute($view_id, $display_id) {
 
     // Add access check parameters to the route.
     $access_plugin = $this->getPlugin('access');
-    if (!isset($access_plugin)) {
-      // @todo Do we want to support a default plugin in getPlugin itself?
-      $access_plugin = Views::pluginManager('access')->createInstance('none');
-    }
+    // @todo Do we want to support a default plugin in getPlugin itself?
+    $access_plugin ??= Views::pluginManager('access')->createInstance('none');
     $access_plugin->alterRouteDefinition($route);
--- a/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
+++ b/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
@@ -306,9 +306,7 @@ protected function assertPreviewAJAX(int $row_count): void {
    * @internal
    */
   protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
-    if (!isset($message)) {
-      $message = "Class .$class found.";
-    }
+    $message ??= "Class .$class found.";
     $this->assertStringContainsString($class, $element->getAttribute('class'), $message);
   }

Steps to reproduce

Proposed resolution

The changed codes seem regular. Though what happens and the message are clear: the problem is not the change, but the concerned variables, $access_plugin and $message. With the changes the two files come in the scope of phpstan during the MR process. And phpstand thinks that the two variables are not nullable, since some thing is wrong in their origin. They should be explicitly declared as nullable.

- No use of ??= in this issue, only phpstan problems (see #5 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix and #6 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix ).

- For $message, it's an argument of the function. In the same file core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php in

   * @param string $message
   *   (optional) A verbose message to output.
   *
   * @internal
   */
  protected function assertClass(NodeElement $element, string $class, string $message = ''): void {

change,
* @param string $message
to
* @param string|null $message
Or,
\Drupal\Tests\views_ui\FunctionalJavascript\PreviewTest::assertClass
should match
\Drupal\Tests\system\Functional\Pager\PagerTest::assertClass
to do that change
protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
to
protected function assertClass(NodeElement $element, string $class, string $message = NULL): void {
in core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php. (see #6 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix and #14 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix )
We use the second one.

- For $access_plugin, it isn't an argument. Since it comes from $this->getPlugin('access') and for the class,
abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInterface, DependentPluginInterface {
the right file is core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php. Here, in,

  /**
   * Get the instance of a plugin, for example style or row.
   *
   * @param string $type
   *   The type of the plugin.
   *
   * @return \Drupal\views\Plugin\views\ViewsPluginInterface
   */
  public function getPlugin($type);

change,
* @return \Drupal\views\Plugin\views\ViewsPluginInterface
to
* @return \Drupal\views\Plugin\views\ViewsPluginInterface|null
(see #5 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix and #15 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix )

- After #20 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix the core/phpstan-baseline.neon should be update: remove the 5 items.

- Following the messages,

in empty\(\) always exists and is not falsy
in isset\(\) always exists and is not nullable

since always exists, make some changes empty($A) to !$A and isset($A) to $A !== NULL.
(Not always, to keep the ??= transformations still possible.)

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Postponed

Version

11.0 πŸ”₯

Component
OtherΒ  β†’

Last updated about 3 hours ago

Created by

πŸ‡«πŸ‡·France Chris64 France

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @Chris64
  • πŸ‡«πŸ‡·France Chris64 France
  • πŸ‡«πŸ‡·France Chris64 France
  • Status changed to Closed: won't fix 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia dpi Perth, Australia

    Variable $access_plugin on left side of ??= always exists and is not nullable.

    Ignored error pattern #^Variable \$access_plugin in isset\(\) always exists and is not nullable\.$# in path

    If either error is true, then the line should not be converted to ??=. Nor should it be left as isset().

    If the change is correct, then the code leading to it should be changed. With better typehints, for example.

    I'd suggest changing the code in the other issue. Just make sure you are justifying the change clearly in documenting a issue/MR comment.

  • πŸ‡¦πŸ‡ΊAustralia dpi Perth, Australia

    For PathPluginBase, \Drupal\views\Plugin\views\display\DisplayPluginInterface::getPlugin the docs say it can never return null.

    However I can see there a return void;, so lets create an issue just for this, which changes the documentation, and explicitly return NULL;

    This issue could be used as a template: πŸ› \Drupal\Core\Config\StorageInterface::read is typehinted as possibly returning bool, but never returns true Fixed

    In this case I wouldnt recommend changing the relevant lines in the ??= issue.

  • πŸ‡¦πŸ‡ΊAustralia dpi Perth, Australia

    For PreviewTest.php, lets keep the change in the ??= issue.

    However, lets change \Drupal\Tests\views_ui\FunctionalJavascript\PreviewTest::assertClass so the method signature (and docs) match \Drupal\Tests\system\Functional\Pager\PagerTest::assertClass.

  • πŸ‡«πŸ‡·France Chris64 France
  • πŸ‡«πŸ‡·France Chris64 France

    @dpi, thank you for your answer, We can discuss.

    For PreviewTest.php, lets keep the change in the ??= issue.

    Well, this case block the MR, because of the phpstan error. I face this before.

    For PathPluginBase, \Drupal\views\Plugin\views\display\DisplayPluginInterface::getPlugin the docs say it can never return null.

    So, is not nullable, so,

        if (!isset($access_plugin)) {
          // @todo Do we want to support a default plugin in getPlugin itself?
          $access_plugin = Views::pluginManager('access')->createInstance('none');
        }

    is inconsistent: never go in the block. Or is nullable, and this code is okay (...and could be changed to ??=).

  • πŸ‡«πŸ‡·France Chris64 France

    @dpi, I think the problem is not ??=. The problem, as phpstan tells us, is not nullable + isset.

  • πŸ‡«πŸ‡·France Chris64 France

    I push the work, since done. It can be refused.

  • Pipeline finished with Failed
    9 months ago
    Total: 177s
    #105178
  • πŸ‡«πŸ‡·France Chris64 France

    May be refused, but nice information: with these few changes now these two cases have MR mergeable. A step forward.

  • πŸ‡«πŸ‡·France Chris64 France

    Okay @dpi, after having a look your requests about null are clear for me.

  • πŸ‡«πŸ‡·France Chris64 France

    About #6 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix , on one side, in core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php,

      /**
       * Asserts that an element has a given class.
       *
       * @param \Behat\Mink\Element\NodeElement $element
       *   The element to test.
       * @param string $class
       *   The class to assert.
       * @param string $message
       *   (optional) A verbose message to output.
       *
       * @internal
       */
      protected function assertClass(NodeElement $element, string $class, string $message = ''): void {
        if (!isset($message)) {
          $message = "Class .$class found.";
        }
        $this->assertStringContainsString($class, $element->getAttribute('class'), $message);

    On the other side, in core/modules/system/tests/src/Functional/Pager/PagerTest.php,

      /**
       * Asserts that an element has a given class.
       *
       * @param \Behat\Mink\Element\NodeElement $element
       *   The element to test.
       * @param string $class
       *   The class to assert.
       * @param string $message
       *   (optional) A verbose message to output.
       *
       * @internal
       */
      protected function assertClass(NodeElement $element, string $class, string $message = NULL): void {
        if (!isset($message)) {
          $message = "Class .$class found.";
        }
        $this->assertTrue($element->hasClass($class), $message);
      }

    To make the two match change,
    , string $message = ''): void {
    to
    , string $message = NULL): void {

  • πŸ‡«πŸ‡·France Chris64 France

    @dpi, for #6 πŸ“Œ Use null coalescing assignment operator ??=: phpstan obstruction case. Closed: won't fix and $access_plugin,

    However I can see there a return void;, so lets create an issue just for this, which changes the documentation, and explicitly return NULL;

    I believe you have in head,

        // Return now if no options have been loaded.
        if (empty($options) || !isset($options['type'])) {
          return;
        }

    in getPlugin in core/modules/views/src/Plugin/views/display/DisplayPluginBase.php. And if I follow πŸ› \Drupal\Core\Config\StorageInterface::read is typehinted as possibly returning bool, but never returns true Fixed I think the change needed should be,
    * @return \Drupal\views\Plugin\views\ViewsPluginInterface
    to
    * @return \Drupal\views\Plugin\views\ViewsPluginInterface|null
    in,

      /**
       * Get the instance of a plugin, for example style or row.
       *
       * @param string $type
       *   The type of the plugin.
       *
       * @return \Drupal\views\Plugin\views\ViewsPluginInterface
       */
      public function getPlugin($type);

    in core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php since,
    abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInterface, DependentPluginInterface {

  • πŸ‡«πŸ‡·France Chris64 France

    Finally the issue perimeter could be restricted to the nullable problems,

    so lets create an issue just for this

    using this issue for these problems.

  • Pipeline finished with Failed
    9 months ago
    Total: 695s
    #107121
  • πŸ‡«πŸ‡·France Chris64 France
  • πŸ‡«πŸ‡·France Chris64 France
  • πŸ‡«πŸ‡·France Chris64 France
  • πŸ‡«πŸ‡·France Chris64 France

    Still some phpstan errors,

     ------ ------------------------------------------------------------------------------------------------ 
      Line   core/modules/views/src/Plugin/views/display/DisplayPluginBase.php                               
     ------ ------------------------------------------------------------------------------------------------ 
             Ignored error pattern #^Variable \$pager in isset\(\) always exists                             
             and is not nullable\.$# in path                                                                 
             /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php  
             was not matched in reported errors.                                                             
             Ignored error pattern #^Variable \$style in empty\(\) always exists                             
             and is not falsy\.$# in path                                                                    
             /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php  
             was not matched in reported errors.                                                             
     ------ ------------------------------------------------------------------------------------------------ 
     ------ --------------------------------------------------------------------------------------------- 
      Line   core/modules/views/src/Plugin/views/display/PathPluginBase.php                               
     ------ --------------------------------------------------------------------------------------------- 
             Ignored error pattern #^Variable \$access_plugin in isset\(\) always                         
             exists and is not nullable\.$# in path                                                       
             /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/display/PathPluginBase.php  
             was not matched in reported errors.                                                          
     ------ --------------------------------------------------------------------------------------------- 
     ------ -------------------------------------------------------------------------------------------- 
      Line   core/modules/views/src/Plugin/views/style/StylePluginBase.php                               
     ------ -------------------------------------------------------------------------------------------- 
             Ignored error pattern #^Variable \$plugin in empty\(\) always exists                        
             and is not falsy\.$# in path                                                                
             /builds/issue/drupal-3424107/core/modules/views/src/Plugin/views/style/StylePluginBase.php  
             was not matched in reported errors.                                                         
     ------ -------------------------------------------------------------------------------------------- 
     ------ --------------------------------------------------------------------------------------------------- 
      Line   core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php                               
     ------ --------------------------------------------------------------------------------------------------- 
             Ignored error pattern #^Variable \$message in isset\(\) always exists                              
             and is not nullable\.$# in path                                                                    
             /builds/issue/drupal-3424107/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php  
             was not matched in reported errors.                                                                
     ------ --------------------------------------------------------------------------------------------------- 
     [ERROR] Found 5 errors
  • Status changed to Needs work 9 months ago
  • πŸ‡«πŸ‡·France Chris64 France
  • Pipeline finished with Success
    9 months ago
    Total: 531s
    #110770
  • πŸ‡«πŸ‡·France Chris64 France

    - The phpstan errors have no line number. The corresponding items should be removed from core/phpstan-baseline.neon file.
    - Following the messages,

    in empty\(\) always exists and is not falsy
    in isset\(\) always exists and is not nullable

    since always exists, make some changes empty($A) to !$A and isset($A) to $A !== NULL. (Not always, to keep the ??= transformations still possible.)

  • Status changed to Needs review 9 months ago
  • πŸ‡«πŸ‡·France Chris64 France

    MR mergeable. NR.

  • πŸ‡³πŸ‡ΏNew Zealand quietone

    The title is also the commit message, so this should be easier to understand when searching history in the future.

  • πŸ‡«πŸ‡·France Chris64 France

    Title changed. Less detailed but more clear.

  • Status changed to Needs work 8 months ago
  • The Needs Review Queue Bot β†’ tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • Status changed to Postponed 8 months ago
Production build 0.71.5 2024