branch: externals/phpinspect
commit a66e35f57ca48289a22b8c15b1d2a62dfff10070
Author: Hugo Thunnissen <de...@hugot.nl>
Commit: Hugo Thunnissen <de...@hugot.nl>

    Test and improve typedef index reactivity and lazy loading + fix a bunch of 
bugs
---
 phpinspect-completion.el  |   3 +-
 phpinspect-index.el       |  12 ++--
 phpinspect-method-cell.el |  64 ++++++++++++++--------
 phpinspect-pipeline.el    |   4 +-
 phpinspect-project.el     |  69 +++++++++++------------
 phpinspect-resolve.el     |   4 +-
 phpinspect-suggest.el     |   2 +-
 phpinspect-type.el        |   7 +++
 phpinspect-typedef.el     |  81 ++++++++++++++++++++++-----
 test/test-project.el      | 101 ++++++++++++++++++++++++++++++++++
 test/test-typedef.el      | 136 ++++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 400 insertions(+), 83 deletions(-)

diff --git a/phpinspect-completion.el b/phpinspect-completion.el
index 15a585ec64..a5c0821e57 100644
--- a/phpinspect-completion.el
+++ b/phpinspect-completion.el
@@ -180,7 +180,8 @@ belonging to a token that conforms with 
`phpinspect-attrib-p'"
                        (phpinspect-completion-query-buffer q)
                        (phpinspect-completion-query-point q)
                        #'phpinspect-static-attrib-p)))
-    (phpinspect--log "Returning region for attribute access subject %s" 
(phpinspect-meta-string subject))
+    (phpinspect--log "[comp-static-attribute] Returning region for attribute 
access subject %s"
+                     (phpinspect-meta-string subject))
     (list (phpinspect-attrib-start subject) (phpinspect-meta-end subject))))
 
 (cl-defmethod phpinspect-comp-strategy-execute
diff --git a/phpinspect-index.el b/phpinspect-index.el
index 59f55aa374..60a5e45411 100644
--- a/phpinspect-index.el
+++ b/phpinspect-index.el
@@ -160,7 +160,8 @@ function (think \"new\" statements, return types etc.)."
                  (cadadr (seq-find #'phpinspect-return-annotation-p 
comment-before))))
       (if type
           (phpinspect--apply-annotation-type return-annotation-type type 
type-resolver)
-        (setq type (funcall type-resolver (phpinspect--make-type :name 
return-annotation-type)))))
+        (when (stringp return-annotation-type)
+          (setq type (funcall type-resolver (phpinspect--make-type :name 
return-annotation-type))))))
 
     (when-let ((throw-annotations (seq-filter #'phpinspect-throws-annotation-p 
comment-before)))
       (dolist (tr throw-annotations)
@@ -410,7 +411,6 @@ SCOPE should be a scope token (`phpinspect-scope-p')."
                                                           add-used-types)
                    methods))
             ((phpinspect-doc-block-p token)
-             (phpinspect--log "setting comment-before %s" token)
              (setq comment-before token))
 
             ;; Prevent comments from sticking around too long
@@ -420,7 +420,6 @@ SCOPE should be a scope token (`phpinspect-scope-p')."
                    (nconc trait-config
                           (phpinspect--index-trait-use token type-resolver 
add-used-types))))
             (t
-             (phpinspect--log "Unsetting comment-before")
              (setq comment-before nil))))
 
     ;; Dirty hack that assumes the constructor argument names to be the same 
as the object
@@ -437,7 +436,7 @@ SCOPE should be a scope token (`phpinspect-scope-p')."
                                         constructor-sym))
                                   methods)))
       (when constructor
-        (phpinspect--log "Constructor was found")
+        (phpinspect--log "Constructor was found for %s" class-name)
         (dolist (variable variables)
           (when (not (phpinspect--variable-type variable))
             (phpinspect--log "Looking for variable type in constructor 
arguments (%s)"
@@ -460,7 +459,7 @@ SCOPE should be a scope token (`phpinspect-scope-p')."
     `(,class-name .
                   (phpinspect--indexed-class
                    (complete . ,(not (phpinspect-incomplete-class-p class)))
-                   (trait-confg . ,trait-config)
+                   (trait-config . ,trait-config)
                    (class-name . ,class-name)
                    (declaration . ,(seq-find #'phpinspect-declaration-p class))
                    (location . ,(funcall location-resolver class))
@@ -765,7 +764,8 @@ Returns a list of type name strings."
                    (push `(alias ,(cadadr (cadr block?)) ,(cadr (nth 3 
block?)))
                          (cdr t-config))))
 
-               (setq block? (nthcdr 4 block?))))))
+               (setq block? (nthcdr 4 block?)))
+              (t (pop block?)))))
 
     (when add-used-types
       (funcall add-used-types used-types))
diff --git a/phpinspect-method-cell.el b/phpinspect-method-cell.el
index 0fe412fe24..59c06aa799 100644
--- a/phpinspect-method-cell.el
+++ b/phpinspect-method-cell.el
@@ -37,6 +37,8 @@
                 :type phpinspect-name)
   (origin-type nil
                :type phpinspect--type)
+  (-return-type nil
+                :type phpinspect--type)
   (definition  nil
                :type phpinspect--function))
 
@@ -123,27 +125,44 @@ Also updates all members of MCOL with the same 
origin-type."
     list))
 
 (defun phpi-method-set-return-type (method type)
-  (setf (phpinspect--function-return-type (phpi-method-definition method))
-        type))
+  (setf (phpi-method--return-type method) type))
+
+(defun phpi-method-original-return-type (method)
+  (phpinspect--function-return-type (phpi-method-definition method)))
+
+(defun phpi-method-does-late-static-binding (method)
+  (when-let ((og-type (phpi-method-original-return-type method)))
+    (phpinspect--type-does-late-static-binding og-type)))
+
+(defun phpi-method-resolve-late-static-binding (method home-type)
+  "Destructively modify METHOD to return HOME-TYPE if late static binding."
+  (when-let ((og-type (phpi-method-original-return-type method)))
+    (phpi-method-set-return-type
+     method (phpinspect--resolve-late-static-binding og-type home-type))))
 
 (defun phpi-mc-set (cell home-type origin-type method)
-  (if (phpinspect--type= home-type origin-type)
-      ;; Method belongs to home type
-      (progn
-        (when (and method
-                   (phpi-method-return-type method)
-                   (phpinspect--type-does-late-static-binding 
(phpi-method-return-type method)))
-          (phpi-method-set-return-type
-           method (phpinspect--resolve-late-static-binding
-                   (phpi-method-return-type method) home-type)))
-
-        (setf (phpi-mc-own cell) method))
-    ;; Method is from a trait, interface or inherited
-    (pcase (phpinspect--type-category origin-type)
-      ('trait (setf (phpi-mc-trait cell) method))
-      ('interface (setf (phpi-mc-interface cell) method))
-      ;; class or abstract class
-      (_ (setf (phpi-mc-inherited cell) method)))))
+  (let ((late-static-binding (and method (phpi-method-does-late-static-binding 
method))))
+    (if (phpinspect--type= home-type origin-type)
+        ;; Method belongs to home type
+        (progn
+          (when late-static-binding
+            (phpi-method-resolve-late-static-binding method home-type))
+
+          (setf (phpi-mc-own cell) method))
+
+      ;; resolve late static binding
+      (when late-static-binding
+        ;; Copy on write (we don't want to change the return type for the
+        ;; original typedef as well).
+        (setq method (phpi-copy-method method))
+        (phpi-method-resolve-late-static-binding method home-type))
+
+      ;; Method is from a trait, interface or inherited
+      (pcase (phpinspect--type-category origin-type)
+        ('trait (setf (phpi-mc-trait cell) method))
+        ('interface (setf (phpi-mc-interface cell) method))
+        ;; class or abstract class
+        (_ (setf (phpi-mc-inherited cell) method))))))
 
 (defun phpi-mc-get-for-type-category (cell home-type type)
   (if (phpinspect--type= home-type type)
@@ -198,7 +217,8 @@ https://www.php.net/manual/en/language.oop5.traits.php";
       (phpi-try-method-return-type (phpi-mc-interface cell))))
 
 (defun phpi-method-return-type (method)
-  (phpinspect--function-return-type (phpi-method-definition method)))
+  (or (phpi-method--return-type method)
+      (phpinspect--function-return-type (phpi-method-definition method))))
 
 (defun phpi-mcol-delete-for-type (mcol type &optional name)
   "Delete from MCOL all methods that originate from TYPE.
@@ -261,10 +281,10 @@ return types in extended classes, traits and interfaces."
   (phpinspect--function-arguments fn))
 
 (cl-defmethod phpi-fn-return-type ((fn phpinspect--function))
-  (phpinspect--function-return-type fn))
+  (or (phpinspect--function-return-type fn) phpinspect--unknown-type))
 
 (cl-defmethod phpi-fn-return-type ((method phpinspect-method))
-  (phpi-method-return-type method))
+  (or (phpi-method-return-type method) phpinspect--unknown-type))
 
 (cl-defmethod phpi-fn-argument-type ((fn phpinspect--function) argument-name)
   (phpinspect--function-argument-type fn argument-name))
diff --git a/phpinspect-pipeline.el b/phpinspect-pipeline.el
index b09029ab1c..c71bf82713 100644
--- a/phpinspect-pipeline.el
+++ b/phpinspect-pipeline.el
@@ -176,7 +176,7 @@ directories."
 
               (phpinspect-pipeline--register-wakeup-function ,inc-queue)
               (while ,continue-running
-                (condition-case-unless-debug err
+                (condition-case err
                     (progn
                       (phpinspect-pipeline-pause)
                       ;; Prevent quitting during step execution, as this could
@@ -208,7 +208,7 @@ directories."
                   (quit (ignore-error phpinspect-pipeline-incoming
                           (phpinspect-pipeline-pause)))
                   (phpinspect-pipeline-incoming)
-                  (t (phpinspect--log "Pipeline thread errored: %s" err)
+                  (t (phpinspect-message "Pipeline thread errored: %s" err)
                      (setq ,end (phpinspect-make-pipeline-end :thread 
(current-thread) :error err))
                      (setq ,continue-running nil)
                      (phpinspect-pipeline-ctx-register-end ,pctx-sym ,end)
diff --git a/phpinspect-project.el b/phpinspect-project.el
index ef38af5245..0aaf24c592 100644
--- a/phpinspect-project.el
+++ b/phpinspect-project.el
@@ -101,27 +101,18 @@ serious performance hits. Enable at your own risk (:")
             (phpinspect-worker-enqueue (phpinspect-project-worker project)
                                        (phpinspect-make-index-task project 
type))))))))
 
-(cl-defmethod phpinspect-project-add-typedef-attribute-types-to-index-queue
-  ((project phpinspect-project) (typedef phpinspect-typedef))
-  (phpinspect-project-edit project
-    (phpinspect-project-add-return-types-to-index-queueue
-     project
-     (phpi-typedef-get-methods typedef))
-    (phpinspect-project-add-return-types-to-index-queueue
-     project
-     (phpi-typedef-get-static-methods typedef))
-    (phpinspect-project-add-variable-types-to-index-queue
-     project
-     (phpi-typedef-variables typedef))))
+(defun phpinspect-project-enqueue-types (project types)
+  (dolist (type types)
+    (phpinspect-project-enqueue-if-not-present project type)))
 
 (cl-defmethod phpinspect-project-add-index
-  ((project phpinspect-project) (index (head phpinspect--root-index)) 
&optional index-imports)
+  ((project phpinspect-project) (index (head phpinspect--root-index)) 
&optional index-dependencies)
   (phpinspect-project-edit project
-    (when index-imports
+    (when index-dependencies
       (phpinspect-project-enqueue-imports project (alist-get 'imports (cdr 
index))))
 
     (dolist (indexed-typedef (alist-get 'classes (cdr index)))
-      (phpinspect-project-add-typedef project (cdr indexed-typedef) 
index-imports))
+      (phpinspect-project-add-typedef project (cdr indexed-typedef) 
index-dependencies))
 
     (dolist (func (alist-get 'functions (cdr index)))
       (phpinspect-project-set-function project func))))
@@ -193,7 +184,7 @@ serious performance hits. Enable at your own risk (:")
     (remhash (phpinspect--type-name-symbol typedef-name) 
(phpinspect-project-typedef-index project))))
 
 (cl-defmethod phpinspect-project-add-typedef
-  ((project phpinspect-project) (indexed-typedef (head 
phpinspect--indexed-class)) &optional index-imports)
+  ((project phpinspect-project) (indexed-typedef (head 
phpinspect--indexed-class)) &optional index-dependencies)
   (phpinspect-project-edit project
     (if (not (alist-get 'class-name (cdr indexed-typedef)))
         (phpinspect--log "Error: Typedef with declaration %s does not have a 
name" (alist-get 'declaration indexed-typedef))
@@ -203,16 +194,15 @@ serious performance hits. Enable at your own risk (:")
              (typedef (gethash typedef-name
                                (phpinspect-project-typedef-index project))))
         (unless typedef
-          (setq typedef (phpinspect-make-typedef typedef-type-name 
(phpinspect-project-make-typedef-retriever project))))
-        ;; :typedef-retriever (phpinspect-project-make-typedef-retriever 
project))))
-
-        (when index-imports
-          (phpinspect-project-enqueue-imports
-           project (alist-get 'imports (cdr indexed-typedef))))
+          (setq typedef (phpinspect-make-typedef
+                         typedef-type-name 
(phpinspect-project-make-typedef-retriever project))))
 
         (phpi-typedef-set-index typedef indexed-typedef)
-        (puthash typedef-name typedef (phpinspect-project-typedef-index 
project))
-        (phpinspect-project-add-typedef-attribute-types-to-index-queue project 
typedef)))))
+
+        (when index-dependencies
+          (phpinspect-project-enqueue-types project 
(phpi-typedef-get-dependencies typedef)))
+
+        (puthash typedef-name typedef (phpinspect-project-typedef-index 
project))))))
 
 (cl-defmethod phpinspect-project-set-typedef
   ((project phpinspect-project) (typedef-fqn phpinspect--type) (typedef 
phpinspect-typedef))
@@ -249,11 +239,8 @@ indexation, but indexed synchronously before returning."
                      no-enqueue (phpi-typedef-initial-index typedef))
 
     (phpinspect-project-edit project
-      (when  (and no-enqueue (phpi-typedef-initial-index typedef))
-        (phpinspect--log "Indexing type file for %s" typedef-fqn)
-        (phpinspect-project-add-index
-         project
-         (phpinspect-project-index-type-file project typedef-fqn))))
+      (when no-enqueue
+        (phpinspect-project-ensure-index-typedef-and-dependencies project 
typedef)))
     typedef))
 
 (cl-defmethod phpinspect-project-get-typedef-extra-or-create
@@ -261,6 +248,22 @@ indexation, but indexed synchronously before returning."
   (or (phpinspect-project-get-typedef-or-extra project typedef-fqn no-enqueue)
       (phpinspect-project-get-typedef-create project typedef-fqn no-enqueue)))
 
+(defun phpinspect-project-ensure-index-typedef-and-dependencies (project 
typedef)
+  (unless (phpi-typedef-initial-index typedef)
+    (phpinspect-project-add-index
+     project
+     (phpinspect-project-index-type-file project (phpi-typedef-name typedef))))
+
+  (dolist (dep (phpi-typedef-get-dependencies typedef))
+    (unless (phpi-typedef-initial-index
+             (phpinspect-project-get-typedef-create project dep))
+      (phpinspect-project-add-index
+       project
+       (phpinspect-project-index-type-file project dep))))
+
+  (dolist (extended (phpi-typedef-subscribed-to-types typedef))
+    (phpinspect-project-ensure-index-typedef-and-dependencies
+     project (phpinspect-project-get-typedef-create project extended))))
 
 (cl-defmethod phpinspect-project-get-typedef
   ((project phpinspect-project) (typedef-fqn phpinspect--type) &optional index)
@@ -272,11 +275,9 @@ indexation, but indexed synchronously before returning."
                  (not (phpi-typedef-read-only-p typedef)))
         (setf (phpi-typedef-read-only-p typedef) t))
 
-      (when (and index (not (phpi-typedef-initial-index typedef)))
-        (phpinspect-project-add-index
-         project
-         (phpinspect-project-index-type-file project typedef-fqn))))
 
+      (when index
+        (phpinspect-project-ensure-index-typedef-and-dependencies project 
typedef)))
     typedef))
 
 (cl-defmethod phpinspect-project-get-typedef-or-extra
@@ -404,7 +405,7 @@ before the search is executed."
            (let* ((type (phpinspect-index-task-type task))
                   (root-index (phpinspect-project-index-type-file project 
type)))
              (when root-index
-               (phpinspect-project-add-index project root-index)))))))
+               (phpinspect-project-add-index project root-index 
'index-dependencies)))))))
 
 ;;; INDEX FILE TASK
 (cl-defstruct (phpinspect-index-dir-task (:constructor 
phpinspect-make-index-dir-task))
diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el
index 01c05ea26d..f27cc4bcb6 100644
--- a/phpinspect-resolve.el
+++ b/phpinspect-resolve.el
@@ -561,8 +561,8 @@ type. (`phpinspect--type-p')"
     (setq type-resolver
           (phpinspect--make-type-resolver-for-resolvecontext resolvecontext)))
 
-  (phpinspect--log "Looking for type of statement: %s in nested token"
-                   (phpinspect--resolvecontext-subject resolvecontext))
+  (phpinspect--log "Looking for type of statement: %s in nested token, 
assume-derived is: %s"
+                   (phpinspect--resolvecontext-subject resolvecontext) 
assume-derived)
   ;; Find all enclosing tokens that aren't classes. Classes do not contain 
variable
   ;; assignments which have effect in the current scope, which is what we're 
trying
   ;; to find here to infer the statement type.
diff --git a/phpinspect-suggest.el b/phpinspect-suggest.el
index 1e2d93202e..d6f8c56240 100644
--- a/phpinspect-suggest.el
+++ b/phpinspect-suggest.el
@@ -147,7 +147,7 @@ static variables and static methods."
                          static))
          (statement-type (phpinspect-resolve-type-from-context
                            resolvecontext
-                           type-resolver)))
+                           type-resolver static)))
 
     (phpinspect--log "Statement type: %s" statement-type)
     (when statement-type
diff --git a/phpinspect-type.el b/phpinspect-type.el
index e3cb085004..304b8ee626 100644
--- a/phpinspect-type.el
+++ b/phpinspect-type.el
@@ -85,6 +85,7 @@ that the collection is expected to contain")
 (defvar phpinspect--self-type (phpinspect--make-type :name "\\self" 
:fully-qualified t))
 (defvar phpinspect--this-type (phpinspect--make-type :name "\\this" 
:fully-qualified t))
 (defvar phpinspect--null-type (phpinspect--make-type :name "\\null" 
:fully-qualified t))
+(defvar phpinspect--unknown-type (phpinspect--make-type :name "unknown-type"))
 
 (defun phpinspect-define-standard-types ()
   (setq phpinspect-native-types
@@ -243,6 +244,12 @@ NAMESPACE may be nil, or a string with a namespace FQN."
        self)
      'face 'font-lock-type-face)))
 
+(cl-defmethod phpinspect--display-format-type-name (type)
+  (cl-assert (not type))
+
+  (phpinspect--display-format-type-name "unknown-type"))
+
+
 (cl-defstruct (phpinspect--function (:constructor 
phpinspect--make-function-generated)
                                     (:copier phpinspect--copy-function))
   "A PHP function."
diff --git a/phpinspect-typedef.el b/phpinspect-typedef.el
index 1015159841..a3f827c539 100644
--- a/phpinspect-typedef.el
+++ b/phpinspect-typedef.el
@@ -142,13 +142,24 @@ structure returned by `phpinspect--index-trait-use'."
 (cl-defmethod phpi-typedef-get-static-method ((def phpinspect-typedef) 
(method-name string))
   (phpi-typedef-get-static-method def (phpinspect-intern-name method-name)))
 
-(cl-defmethod phpi-typedef-set-method ((def phpinspect-typedef) (fn 
phpinspect--function))
-  (let ((method (phpinspect-make-method (phpi-typedef-name def) fn)))
-    (phpi-ma-add (phpi-typedef-method-adder def)
-                 (phpi-typedef-methods def)
-                 method 'overwrite)))
+(cl-defmethod phpi-typedef-set-method ((def phpinspect-typedef) (m 
phpinspect-method) &optional no-propagate)
+  (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-methods def) m 
'overwrite)
+  (unless no-propagate
+    (phpi-typedef-trigger-subscriber-method-update def m)))
+
+(cl-defmethod phpi-typedef-set-method ((def phpinspect-typedef) (fn 
phpinspect--function) &optional no-propagate)
+  (phpi-typedef-set-method def (phpinspect-make-method (phpi-typedef-name def) 
fn)))
+
+(cl-defmethod phpi-typedef-set-static-method ((def phpinspect-typedef) (m 
phpinspect-method) &optional no-propagate)
+  (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-static-methods 
def) m 'overwrite)
+  (unless no-propagate
+    (phpi-typedef-trigger-subscriber-method-update def m 'static)))
+
+(cl-defmethod phpi-typedef-set-static-method ((def phpinspect-typedef) (fn 
phpinspect--function) &optional no-propagate)
+  (phpi-typedef-set-static-method def (phpinspect-make-method 
(phpi-typedef-name def) fn)))
 
-(cl-defmethod phpi-typedef-set-static-method ((def phpinspect-typedef) (fn 
phpinspect--function))
+
+(cl-defmethod phpi-typedef-set-static-method ((def phpinspect-typedef) (fn 
phpinspect--function) &optional no-propagate)
   (let ((method (phpinspect-make-method (phpi-typedef-name def) fn)))
     (phpi-ma-add (phpi-typedef-method-adder def)
                  (phpi-typedef-static-methods def)
@@ -158,9 +169,14 @@ structure returned by `phpinspect--index-trait-use'."
 (cl-defmethod phpi-typedef-delete-method ((def phpinspect-typedef) (name (head 
phpinspect-name)))
   (phpi-mcol-delete-for-type
    (phpi-typedef-methods def) (phpi-typedef-name def) name)
+  (phpi-typedef-trigger-subscriber-method-delete def (phpi-typedef-name def) 
name)
 
   (phpi-mcol-delete-for-type
-   (phpi-typedef-static-methods def) (phpi-typedef-name def) name))
+   (phpi-typedef-static-methods def) (phpi-typedef-name def) name)
+  (phpi-typedef-trigger-subscriber-method-delete def (phpi-typedef-name def) 
name 'static))
+
+(cl-defmethod phpi-typedef-delete-method ((def phpinspect-typedef) (name 
string))
+  (phpi-typedef-delete-method def (phpinspect-intern-name name)))
 
 (cl-defmethod phpi-typedef-delete-method ((def phpinspect-typedef) (fn 
phpinspect--function))
   (phpi-typedef-delete-method def (phpinspect--function-name-symbol fn)))
@@ -179,7 +195,7 @@ structure returned by `phpinspect--index-trait-use'."
         (progn
           (phpi-ma-set-config (phpi-typedef-method-adder def) config)
           (dolist (cons config)
-            (push (car config) types))))
+            (push (car cons) types))))
       ;; return all types that were in config
       types)))
 
@@ -190,7 +206,7 @@ structure returned by `phpinspect--index-trait-use'."
       (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-methods def) 
method))
 
     (dolist (method (phpi-typedef-get-static-methods foreign-def))
-      (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-methods def) 
method))
+      (phpi-ma-add (phpi-typedef-method-adder def) 
(phpi-typedef-static-methods def) method))
 
     (dolist (var (phpi-typedef-variables foreign-def))
       (phpi-typedef-set-variable def var))))
@@ -222,7 +238,25 @@ extended classes, used traits or implemented interfaces."
   "Incorporate DEF into subscribed typedefs."
   (dolist (type (phpi-typedef-subscribed-types def))
     (when-let ((foreign-def (phpi--typedef-get-foreign-type def type)))
-      (phpi-typedef-add-foreign-members foreign-def def))))
+      (phpi-typedef-add-foreign-members foreign-def def)
+      ;; Notify subscribers of upstream changes
+      (phpi-typedef-trigger-subscriber-update foreign-def))))
+
+(defun phpi-typedef-trigger-subscriber-method-update (def method &optional 
static)
+  (dolist (type (phpi-typedef-subscribed-types def))
+    (when-let ((foreign-def (phpi--typedef-get-foreign-type def type)))
+      (if static
+          (phpi-typedef-set-static-method foreign-def method)
+        (phpi-typedef-set-method foreign-def method)))))
+
+(defun phpi-typedef-trigger-subscriber-method-delete (def type method-name 
&optional static)
+  (dolist (subtype (phpi-typedef-subscribed-types def))
+    (when-let ((foreign-def (phpi--typedef-get-foreign-type def subtype)))
+      (phpi-mcol-delete-for-type
+       (if static (phpi-typedef-static-methods foreign-def) 
(phpi-typedef-methods foreign-def))
+       type method-name)
+
+      (phpi-typedef-trigger-subscriber-method-delete foreign-def type 
method-name static))))
 
 (defun phpi-typedef-set-name (def type)
   "Set the TYPE name of DEF.
@@ -235,7 +269,8 @@ TYPE must be a structure of type `phpinspect--type'."
       ;; Only update if type name is actually different
       (unless (phpinspect--type= existing type)
         (setf (phpi-typedef-name def) type)
-        (phpi-mcol-set-home-type (phpi-typedef-methods def) type)))))
+        (phpi-mcol-set-home-type (phpi-typedef-methods def) type)
+        (phpi-mcol-set-home-type (phpi-typedef-static-methods def) type)))))
 
 (defun phpi-typedef-update-declaration (def declaration imports namespace-name 
trait-config)
   (phpi--typedef-edit def
@@ -267,7 +302,7 @@ TYPE must be a structure of type `phpinspect--type'."
                  #'phpinspect-typedef-p
                  (mapcar
                   (lambda (class-name)
-                    (funcall (phpi-typedef-retriever def) class-name))
+                    (phpi--typedef-get-foreign-type def class-name))
                   extensions))))
 
       (dolist (fd foreign-defs)
@@ -279,8 +314,6 @@ TYPE must be a structure of type `phpinspect--type'."
   (phpi--typedef-edit def
     (setf (phpi-typedef-declaration def) (alist-get 'declaration index))
 
-
-
     (let ((ma (phpi-typedef-method-adder def))
           (mcol (phpi-typedef-methods def))
           (stmcol (phpi-typedef-static-methods def))
@@ -307,7 +340,7 @@ TYPE must be a structure of type `phpinspect--type'."
       (phpi-typedef-update-extensions
        def `(,@(alist-get 'implements index)
              ,@(alist-get 'extends index)
-             ,@(phpi-typedef-set-trait-config def (alist-get 'trait-usage 
index))))
+             ,@(phpi-typedef-set-trait-config def (alist-get 'trait-config 
index))))
 
       (phpi-typedef-trigger-subscriber-update def))))
 
@@ -351,5 +384,23 @@ TYPE must be a structure of type `phpinspect--type'."
       (when (string= variable-name (phpinspect--variable-name variable))
         (throw 'found variable)))))
 
+(defun phpi-typedef-get-dependencies (def)
+  (let ((deps (phpi-typedef-subscribed-to-types def)))
+    (dolist (method (phpi-typedef-get-methods def))
+      (when (phpi-method-return-type method)
+        (push (phpi-method-return-type method) deps)))
+
+
+    (dolist (method (phpi-typedef-get-static-methods def))
+      (when (phpi-method-return-type method)
+        (push (phpi-method-return-type method) deps)))
+
+    (dolist (var (phpi-typedef-variables def))
+      (when (phpinspect--variable-type var)
+        (push (phpinspect--variable-type var) deps)))
+
+    (seq-uniq deps #'phpinspect--type=)))
+
+
 (provide 'phpinspect-typedef)
 ;;; phpinspect-typedef.el ends here
diff --git a/test/test-project.el b/test/test-project.el
index 719ad15774..432d153008 100644
--- a/test/test-project.el
+++ b/test/test-project.el
@@ -26,6 +26,11 @@
 (require 'ert)
 (require 'phpinspect-project)
 
+(require 'phpinspect-test-env
+         (expand-file-name "phpinspect-test-env.el"
+                           (file-name-directory (macroexp-file-name))))
+
+
 (ert-deftest phpinspect-project-purge ()
   (let ((project (phpinspect--make-project)))
     (phpinspect-project-purge project)
@@ -42,3 +47,99 @@
     (phpinspect-project-purge project)
 
     (should (= 0 (length (hash-table-values (phpinspect-project-file-watchers 
project)))))))
+
+(defun phpinspect--make-dummy-composer-project-with-code ()
+  (let ((fs (phpinspect-make-virtual-fs)))
+    (phpinspect-virtual-fs-set-file
+      fs
+      "/project/root/composer.json"
+      "{ \"autoload\": { \"psr-4\": {\"App\\\\\": [\"src/\", \"lib\"]}}}")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Foo.php"
+      "<?php namespace App; trait Foo { public function do(): static {} public 
static function dont(): Baz {} }")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Baz.php"
+      "<?php namespace App; class Baz { public function amBaz(): bool {} }")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Bar.php"
+      "<?php namespace App; class Bar { use Foo; public function foo(): Foo {} 
}")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Harry.php"
+      "<?php namespace App; class Harry { public function amBarry(): bool {} 
}")
+
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Barry.php"
+      "<?php namespace App; class Barry { public function getHarry(): Harry {} 
}")
+
+
+    (let* ((project (phpinspect--make-dummy-project fs "/project/root"))
+           (autoload (phpinspect-project-autoload project))
+           result error)
+
+      (phpinspect-autoloader-refresh autoload (lambda (res err)
+                                                (setq result res error err)))
+
+      (while (not (or result error))
+        (thread-yield))
+
+      project)))
+
+
+(ert-deftest phpinspect-project-no-enqueue ()
+  (let* ((project (phpinspect--make-dummy-composer-project-with-code))
+         (bar (phpinspect-project-get-typedef-extra-or-create
+               project (phpinspect--make-type :name "\\App\\Bar") 'no-enqueue))
+         ;; Fetch foo without passing no-enqueue
+         (foo (phpinspect-project-get-typedef-extra-or-create
+               project (phpinspect--make-type :name "\\App\\Foo")))
+         (baz  (phpinspect-project-get-typedef-extra-or-create
+               project (phpinspect--make-type :name "\\App\\Baz")))
+         (barry (phpinspect-project-get-typedef-extra-or-create
+                 project (phpinspect--make-type :name "\\App\\Barry")))
+         (harry (phpinspect-project-get-typedef-extra-or-create
+                 project (phpinspect--make-type :name "\\App\\Harry"))))
+    ;; Bar includes foo's method
+    (should bar)
+    (should (= 2 (length (phpi-typedef-get-methods bar))))
+
+    ;; Foo is loaded/indexed
+    (should foo)
+    (should (= 1 (length (phpi-typedef-get-methods foo))))
+
+    ;; Baz should be loaded as dependency of foo
+    (should baz)
+    (should (phpi-typedef-initial-index baz))
+
+    ;; barry and harry are not loaded (worker is null, so they are not indexed
+    ;; in the background either).
+    (should harry)
+    (should-not (phpi-typedef-initial-index harry))
+    (should barry)
+    (should-not (phpi-typedef-initial-index barry))
+
+    ;; Trigger index of barry
+    (phpinspect-project-get-typedef-extra-or-create
+     project (phpinspect--make-type :name "\\App\\Barry") 'no-enqueue)
+
+    ;; barry and harry should be loaded now (harry as dependency of barry)
+    (should (phpi-typedef-initial-index barry))
+    (should (phpi-typedef-initial-index harry))))
+
+(ert-deftest phpinspect-project-late-static-binding ()
+  (let* ((project (phpinspect--make-dummy-composer-project-with-code))
+         (bar (phpinspect-project-get-typedef-extra-or-create
+               project (phpinspect--make-type :name "\\App\\Bar") 
'no-enqueue)))
+
+    (should bar)
+    (should (= 2 (length (phpi-typedef-get-methods bar))))
+
+    (let ((method (phpi-typedef-get-method bar "do")))
+      (should (phpinspect--type= (phpinspect--make-type :name "\\App\\Bar")
+                                   (phpi-method-return-type method)))
+      (should (phpinspect--type= (phpinspect--make-type :name "\\App\\Bar")
+                                   (phpi-fn-return-type method))))))
diff --git a/test/test-typedef.el b/test/test-typedef.el
index 76adbd33db..63344ed89c 100644
--- a/test/test-typedef.el
+++ b/test/test-typedef.el
@@ -59,6 +59,108 @@
       (setq method (phpi-typedef-get-method def1 (phpinspect-intern-name 
"test")))
       (should-not method))))
 
+(ert-deftest phpinspect-typedef-subscribe-static ()
+  (let* ((def1 (phpinspect-make-typedef (phpinspect--make-type :name "\\A")))
+         (def2 (phpinspect-make-typedef (phpinspect--make-type :name "\\B")))
+         (retriever (lambda (type)
+                      (cond ((phpinspect--type= type (phpinspect--make-type 
:name "\\A"))
+                             def1)
+                            ((phpinspect--type= type (phpinspect--make-type 
:name "\\B"))
+                             def2)))))
+
+    (setf (phpi-typedef-retriever def1) retriever
+          (phpi-typedef-retriever def2) retriever)
+    (phpi-typedef-set-static-method def2 (phpinspect--make-function :name 
"test"))
+
+    (phpi-typedef-update-extensions def1 (list (phpi-typedef-name def2)))
+
+    (let ((method (phpi-typedef-get-static-method def1 (phpinspect-intern-name 
"test"))))
+      (should method)
+      (should-not (phpi-method-return-type method))
+
+      (phpi-typedef-set-static-method def1 (phpinspect--make-function
+                                     :name "test"
+                                     :return-type (phpinspect--make-type :name 
"\\aaa")))
+
+      (setq method (phpi-typedef-get-static-method def1 
(phpinspect-intern-name "test")))
+      (should method)
+      (should (phpi-method-return-type method))
+
+      (phpi-typedef-delete-method def1 (phpinspect-intern-name "test"))
+
+      (setq method (phpi-typedef-get-static-method def1 
(phpinspect-intern-name "test")))
+      (should method)
+      (should-not (phpi-method-return-type method))
+
+      (phpi-typedef-update-extensions def1 nil)
+
+      (setq method (phpi-typedef-get-static-method def1 
(phpinspect-intern-name "test")))
+      (should-not method))))
+
+(ert-deftest phpinspect-typedef-subscribe-multi-ancestor ()
+  (let* ((def1 (phpinspect-make-typedef (phpinspect--make-type :name "\\A")))
+         (def2 (phpinspect-make-typedef (phpinspect--make-type :name "\\B")))
+         (def3 (phpinspect-make-typedef (phpinspect--make-type :name "\\C")))
+         (retriever (lambda (type)
+                      (cond ((phpinspect--type= type (phpi-typedef-name def1))
+                             def1)
+                            ((phpinspect--type= type (phpi-typedef-name def2))
+                             def2)
+                            ((phpinspect--type= type (phpi-typedef-name def3))
+                             def3)))))
+
+    (setf (phpi-typedef-retriever def1) retriever
+          (phpi-typedef-retriever def2) retriever
+          (phpi-typedef-retriever def3) retriever)
+
+
+    (phpi-typedef-update-extensions def1 (list (phpi-typedef-name def2)))
+    (phpi-typedef-update-extensions def2 (list (phpi-typedef-name def3)))
+
+    (phpi-typedef-set-method def3 (phpinspect--make-function :name "test"))
+    (phpi-typedef-trigger-subscriber-update def3)
+
+
+    (let ((method (phpi-typedef-get-method def1 (phpinspect-intern-name 
"test"))))
+      (should method)
+      (should-not (phpi-method-return-type method)))))
+
+(ert-deftest phpinspect-typedef-subscribe-multi-ancestor-no-manual-trigger ()
+  (let* ((def1 (phpinspect-make-typedef (phpinspect--make-type :name "\\A")))
+         (def2 (phpinspect-make-typedef (phpinspect--make-type :name "\\B")))
+         (def3 (phpinspect-make-typedef (phpinspect--make-type :name "\\C")))
+         (retriever (lambda (type)
+                      (cond ((phpinspect--type= type (phpi-typedef-name def1))
+                             def1)
+                            ((phpinspect--type= type (phpi-typedef-name def2))
+                             def2)
+                            ((phpinspect--type= type (phpi-typedef-name def3))
+                             def3)))))
+
+    (setf (phpi-typedef-retriever def1) retriever
+          (phpi-typedef-retriever def2) retriever
+          (phpi-typedef-retriever def3) retriever)
+
+
+    (phpi-typedef-update-extensions def1 (list (phpi-typedef-name def2)))
+    (phpi-typedef-update-extensions def2 (list (phpi-typedef-name def3)))
+
+    ;; Set method, don't trigger update (expect automatic single method 
propagation)
+    (phpi-typedef-set-method def3 (phpinspect--make-function :name "test"))
+
+    (let ((method (phpi-typedef-get-method def1 (phpinspect-intern-name 
"test"))))
+      (should method)
+      (should-not (phpi-method-return-type method))
+
+      (phpi-typedef-delete-method def3 "test")
+
+      (setq method (phpi-typedef-get-method def1 (phpinspect-intern-name 
"test")))
+
+      (should-not method))))
+
+
+
+
 (ert-deftest phpinspect-typedef-variables ()
   (let ((def (phpinspect-make-typedef (phpinspect--make-type :name "\\test"))))
 
@@ -73,3 +175,37 @@
 
       (should (phpi-typedef-get-variable def "test2"))
       (should (string= "test2" (phpinspect--variable-name test2))))))
+
+
+(ert-deftest phpinspect-typedef-set-index-trait ()
+  (let* ((code "class A { use \\AAA; function B(): C {} }")
+         (index (phpinspect--index-tokens (phpinspect-parse-string code)))
+         (class (cdar (alist-get 'classes index)))
+         (def1 (phpinspect-make-typedef (alist-get 'class-name class)))
+         (def2 (phpinspect-make-typedef (phpinspect--make-type :name "\\AAA")))
+         (retriever (lambda (type)
+                      (cond ((phpinspect--type= type (phpi-typedef-name def1))
+                             def1)
+                            ((phpinspect--type= type (phpi-typedef-name def2))
+                             def2)))))
+
+    (setf (phpi-typedef-retriever def1) retriever
+          (phpi-typedef-retriever def2) retriever)
+
+    (phpi-typedef-set-static-method def2 (phpinspect--make-function
+                                          :name "test"
+                                          :return-type (phpinspect--make-type 
:name "\\aaa")))
+
+    (phpi-typedef-set-index def1 class)
+
+    (should (phpi-typedef-get-methods def1))
+    (should (= 1 (length (phpi-typedef-get-methods def1))))
+    (should (= 1 (length (phpi-typedef-get-static-methods def1))))
+
+    (should (eq (phpinspect-intern-name "B")
+                (phpi-method-name (car (phpi-typedef-get-methods def1)))))
+
+    (let ((static-method (phpi-typedef-get-static-method def1 
(phpinspect-intern-name "test"))))
+      (should static-method)
+      (should (eq (phpinspect-intern-name "test")
+                  (phpi-method-name static-method))))))


Reply via email to