Hi,

It is my first post with an issue so sorry if missing something.
I have a valid license so I think it is a priority support, correct?

I found a bug about Generics and extends attribute:

<?php
    namespace DevSense;

    use StdClass;
    use ArrayObject;

    /**
     * @template T
     * @template U
     */
    class MyClass1
    {
        /**
         * @return null|T|U
         */
        public function foo()
        {
            return null;
        }
    }

    /**
     * @extends MyClass1<ArrayObject, bool>
     */
    class MyClass2 extends MyClass1
    {
    }

    /**
     * @param MyClass1<StdClass, string> $x
     */
    function test($x) {
        $t = $x->foo();
    }

    /**
     * @param MyClass2 $x
     */
    function test2($x) {
        $t = $x->foo();
    }

The intelliSense works worrectly with MyClass1 inside the test function ($t->...) but not inside the test2 function.

Thank you.

Another thing about this issue, if I fill <..., ...> it works in the function test2 but not inside MyClass2:

    /**
     * @param MyClass2<bool, bool> $x
     */
    function test2($x) {
        $t = $x->foo();
    }


This is the test inside MyClass2:

    /**
     * @extends MyClass1<ArrayObject, bool>
     */
    class MyClass2 extends MyClass1
    {
        public function bar()
        {
            $t = $this->foo();
        }
    }

The goal is to have intelliSense (autocompletion) from MyClass2 with @extends of course. The tests in the functions allow to debug this issue but I don't want to use Generics like this.

    Thank you for reporting the issue, and for the detailed test case.

    You are right, it seems we don't substitute generics in every situation - we'll try to improve that within the next update.

      Same problem with this example:

      /**
       * @template TKey
       * @template TValue
       * @implements \iteratorAggregate<TKey, TValue>
       */
      abstract class MyCollectionAbstract implements \iteratorAggregate
      {
        ...
      }
      
      /**
       * @extends MyCollectionAbstract<string, \ArrayObject>
       */
      class MyCollection extends MyCollectionAbstract 
      {
        ...
      }

      The type of the MyCollection items in a foreach is unknown instead to be of type \ArrayObject.

      The PHPStan documentation: https://phpstan.org/writing-php-code/phpdoc-types#iterables

      5 days later

      We have improved the generics inferring, and preparing pre-release.

      Thank you for your feedback. There are still improvements to do. So far it correctly substitutes type arguments and treats iteratorAggregate<,> .

      Any other test case is appreciated!

        Thank you for this fix.

        <?php
            namespace devsense;
        
            use stdClass;
            use ArrayObject;
        
            final class myArrayObject extends ArrayObject
            {
            }
            
            /**
              * @template T of ArrayObject
              */
            interface myInterface
            {
                /**
                  * @return T
                  */
                public function myParentMethod(): ArrayObject;
            }
            
            /**
              * @template T of ArrayObject
              * @implements myInterface<T>
              */
            abstract class myParent implements myInterface
            {
                /**
                  * @return T
                  */
                public function myParentMethod(): ArrayObject
                {
                    /**
                      * Returns "ArrayObject" allowing to test
                      * which type the tool will calculate
                      */
                    return new ArrayObject();
                }
            }
            
            final class myFinalStdClass extends stdClass
            {
            }
        
            /**
              * @template C
              * @template O of stdClass
              * @extends myParent<C>
              */
            class myChild extends myParent
            {
                /**
                  * Test with mixed return type
                  * @return O
                  */
                public function myChildMethod(): mixed
                {
                    /**
                      * Returns "true" allowing to test
                      * which type the tool will calculate
                      */
                    return true;
                }
            }
        
            final class myFinalArrayObject extends ArrayObject
            {
            }
        
            /**
              * @extends myChild<myFinalArrayObject, myFinalStdClass>
              */
            class myFinal extends myChild
            {
                public function test()
                {
                    // $t must be of type "myFinalStdClass" (and not "bool")
                    $t = $this->myChildMethod();
                    
                    // $x must be of type "myFinalArrayObject" (and not "ArrayObject")
                    $x = $this->myParentMethod();
                }
            }

        I writed this code without testing it, sorry if I made some errors and sorry for my bad english.

        This code will test 2 levels of generics and test if the tool shows the return type related to the return line/statement or related to the documentation (generic/template).

        Thank you.

        @JakubMisek Hi, just tested the last pre-release version and I think it does not work:

        About IteratorAggregate, in a foreach, we have not the autocompletion.

          Just tested again and if we declare

              /**
               * @template TKey
               * @template TValue
               * @implements \IteratorAggregate<TKey, TValue>
               */

          This will work (key + value types) but if we fill the value type only like this:

              /**
               * @template TValue
               * @implements \IteratorAggregate<TValue>
               */

          Without the key type, it does not work.

          Thank you for the fix.

          Write a Reply...