branch: externals/phpinspect commit 692711e34dadda142bfc3649abdbec06c02f9e82 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Implement property cells for more reliable property inheritance modeling --- phpinspect-buffer.el | 4 +- phpinspect-eldoc.el | 22 +++-- phpinspect-method-cell.el | 3 + phpinspect-property-cell.el | 210 ++++++++++++++++++++++++++++++++++++++++++++ phpinspect-resolve.el | 4 +- phpinspect-suggest.el | 11 ++- phpinspect-typedef.el | 140 +++++++++++++++++------------ test/test-buffer.el | 36 ++++---- test/test-index.el | 24 ++--- test/test-typedef.el | 92 +++++++++++++++++-- 10 files changed, 437 insertions(+), 109 deletions(-) diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el index dddb76b708..925bb16331 100644 --- a/phpinspect-buffer.el +++ b/phpinspect-buffer.el @@ -167,7 +167,7 @@ linked with." (when-let ((class (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (car var)))) - (phpi-typedef-delete-variable class (cdr var))))) + (phpi-typedef-delete-property class (cdr var))))) ((phpinspect-function-p token) (when-let ((func (gethash token (phpinspect-buffer-token-index buffer)))) (remhash token (phpinspect-buffer-token-index buffer)) @@ -498,7 +498,7 @@ linked with." class-obj (phpinspect-buffer-project buffer) (cadr (phpinspect-meta-token var)) type-resolver class))) - (phpi-typedef-set-variable class-obj indexed) + (phpi-typedef-set-property class-obj indexed) (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token var) diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el index 7974f3496d..91b3215a84 100644 --- a/phpinspect-eldoc.el +++ b/phpinspect-eldoc.el @@ -91,11 +91,11 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") variable method result) (when attribute-name (cond ((phpinspect-static-attrib-p attrib) - (setq variable (phpi-typedef-get-variable class attribute-name)) + (setq variable (phpi-typedef-get-property class attribute-name)) (if (and variable - (or (phpinspect--variable-static-p variable) - (phpinspect--variable-const-p variable))) + (or (phpi-prop-static-p variable) + (phpi-prop-const-p variable))) (setq result variable) (setq method (phpi-typedef-get-static-method class (phpinspect-intern-name attribute-name))) @@ -105,7 +105,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") (setq variable (phpi-typedef-get-variable class attribute-name)) (if (and variable - (phpinspect--variable-vanilla-p variable)) + (phpi-prop-vanilla-p variable)) (setq result variable) (setq method (phpi-typedef-get-method class (phpinspect-intern-name attribute-name))) @@ -219,14 +219,20 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") (when func (phpinspect-make-function-doc :fn func :arg-pos arg-pos)))))))) -(cl-defmethod phpinspect-eldoc-string ((var phpinspect--variable)) +(defun phpinspect-var-eldoc-string (prop-or-var) (concat (truncate-string-to-width - (propertize (concat (if (phpinspect--variable-vanilla-p var) "$" "") - (phpinspect--variable-name var)) + (propertize (concat (if (phpi-var-vanilla-p var) "$" "") + (phpi-var-name var)) 'face 'font-lock-variable-name-face) phpinspect-eldoc-word-width) ": " - (phpinspect--display-format-type-name (phpinspect--variable-type var)))) + (phpinspect--display-format-type-name (phpi-var-type var)))) + +(cl-defmethod phpinspect-eldoc-string ((var phpinspect--variable)) + (phpinspect-var-eldoc-string var)) + +(cl-defmethod phpinspect-eldoc-string ((prop phpinspect-property)) + (phpinspect-var-eldoc-string prop)) (cl-defstruct (phpinspect-function-doc (:constructor phpinspect-make-function-doc)) (fn nil diff --git a/phpinspect-method-cell.el b/phpinspect-method-cell.el index d52ef1ef83..f460eb8582 100644 --- a/phpinspect-method-cell.el +++ b/phpinspect-method-cell.el @@ -21,6 +21,9 @@ ;;; Commentary: +;; This file contains struct definitions and logic for the management of +;; (collections of) methods associated with a specific type. + ;;; Code: (require 'cl-macs) diff --git a/phpinspect-property-cell.el b/phpinspect-property-cell.el new file mode 100644 index 0000000000..70c07abb38 --- /dev/null +++ b/phpinspect-property-cell.el @@ -0,0 +1,210 @@ +;;; phpinspect-property-cell.el --- Models for PHP property definitions -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2023 Free Software Foundation, Inc + +;; Author: Hugo Thunnissen <de...@hugot.nl> +;; Keywords: php, languages, tools, convenience +;; Version: 1.2.1 + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; There's a good amount of code from phpinspect-method-cell.el copied over to +;; this one with small modifications to cater to class properties instead of +;; methods. The alternative would have been to create some kind of reusable +;; generic implementation, but the subtle differences in the ways property +;; inheritance and method inheritance work makes this more complicated than it's +;; worth. + +;;; Code: + +(require 'cl-macs) +(require 'phpinspect-type) + +(cl-defstruct (phpinspect-property + (:constructor phpinspect-make-property-generated) + (:conc-name phpi-prop-)) + (origin-type nil + :type phpinspect--type) + (name nil + :type phpinspect-name) + (definition nil + :type phpinspect--variable)) + +(defun phpinspect-make-property (origin-type definition) + (phpinspect-make-property-generated + :name (phpinspect-intern-name (phpinspect--variable-name definition)) + :origin-type origin-type + :definition definition)) + +(cl-defstruct (phpinspect-property-cell + (:constructor phpinspect-make-property-cell) + (:conc-name phpi-pc-)) + (own nil + :type phpinspect-property) + (inherited nil + :type phpinspect-property)) + +(cl-defstruct(phpinspect-property-collection + (:constructor phpinspect-make-property-collection) + (:conc-name phpi-pcol-)) + (home-type nil + :type phpinspect--type) + (cells nil + :type list + :documentation "<string,phpinspect-property-cell>")) + +(defun phpi-pcol-find-cell (pcol prop-name &optional remove) + (cl-assert (phpinspect-name-p prop-name)) + + (alist-get prop-name (phpi-pcol-cells pcol) nil remove #'eq)) + +(defun phpi-pcol-find-cell-create (pcol prop-name) + (let ((cell (phpi-pcol-find-cell pcol prop-name))) + (unless cell + (setq cell (phpinspect-make-property-cell)) + (push (cons prop-name cell) (phpi-pcol-cells pcol))) + + cell)) + +(defun phpi-prop-inheritable-p (prop) + (and-let* ((var (phpi-prop-definition prop)) + (scope (phpinspect--variable-scope var)) + ((or (phpinspect-protected-p scope) + (phpinspect-public-p scope)))))) + +(defun phpi-pc-set (cell home-type origin-type property) + (if (phpinspect--type= home-type origin-type) + (setf (phpi-pc-own cell) property) + ;; Prop is not from home-type, only add if it can be inherited. + (when (or (not property) ;; Deletion, always allowed + (phpi-prop-inheritable-p property)) ;; Update, only when inheritable + (setf (phpi-pc-inherited cell) property)))) + +(defun phpi-pcol-add (pcol property) + (let ((cell (phpi-pcol-find-cell-create pcol (phpi-prop-name property)))) + (phpi-pc-set + cell (phpi-pcol-home-type pcol) (phpi-prop-origin-type property) property))) + +(defun phpi-pcol-list-active (pcol) + (let (active) + (dolist (cons (phpi-pcol-cells pcol)) + (when-let ((prop (phpi-pc-get-active (cdr cons)))) + (push prop active))) + + active)) + +(defun phpi-pc-empty-p (cell) + "CELL is empty when it contains no methods." + (not (phpi-pc-get-active cell))) + +(defun phpi-pc-get-active (cell) + "Get active property from CELL, according to PHP precendence order." + (or (phpi-pc-own cell) + (phpi-pc-inherited cell))) + +(defun phpi-pcol-get-active-property (mcol name) + (when-let ((cell (phpi-pcol-find-cell mcol name))) + (phpi-pc-get-active cell))) + +(defun phpi-pcol-list-own (pcol) + (let (list) + (dolist (cons (phpi-pcol-cells pcol)) + (when-let ((prop (phpi-pc-own (cdr cons)))) + (push prop list))) + list)) + +(defun phpi-pcol-set-home-type (pcol type) + "Set home type of PCOL to TYPE. + +Also updates all members of PCOL with the same origin-type." + (setf (phpi-pcol-home-type pcol) type) + (dolist (prop (phpi-pcol-list-own pcol)) + (setf (phpi-prop-origin-type prop) type))) + +(defun phpi-pc-get-for-type (cell type) + (cl-block try-get + (dolist (prop (list (phpi-pc-own cell) + (phpi-pc-inherited cell))) + (when (and prop (phpinspect--type= type (phpi-prop-origin-type prop))) + (cl-return-from try-get prop))))) + +(defun phpi-pcol-delete (pcol prop-name) + (phpi-pcol-find-cell pcol prop-name t)) + +(defun phpi-pcol-delete-for-type (pcol type &optional name) + + "Delete from PCOL all properties that originate from TYPE. + +When NAME is provided, only property with NAME is deleted." + (if name + ;; Name is provided, only delete method with NAME. + (when-let ((cell (phpi-pcol-find-cell pcol name))) + (when (phpi-pc-get-for-type cell type) + (phpi-pc-set cell (phpi-pcol-home-type pcol) type nil)) + + (when (phpi-pc-empty-p cell) + (phpi-pcol-delete pcol name))) + + (let ((cells (phpi-pcol-cells pcol))) + (dolist (cons cells) + (let ((cell (cdr cons))) + (when (phpi-pc-get-for-type cell type) + (phpi-pc-set cell (phpi-pcol-home-type pcol) type nil)) + + ;; mark cell for deletion by setting car and cdr of alist member to + ;; nil + (when (phpi-pc-empty-p cell) + (setcar cons nil) + (setcdr cons nil)))) + ;; Delete all empty cells + (setf (phpi-pcol-cells pcol) (delete (cons nil nil) cells))))) + +(defun phpi-prop-static-p (prop) + (phpinspect--variable-static-p (phpi-prop-definition prop))) + +(defun phpi-prop-const-p (prop) + (phpinspect--variable-const-p (phpi-prop-definition prop))) + +(defun phpi-prop-vanilla-p (prop) + (phpinspect--variable-vanilla-p (phpi-prop-definition prop))) + +(defun phpi-prop-type (prop) + (phpinspect--variable-type (phpi-prop-definition prop))) + +(defun phpi-prop-scope (prop) + (phpinspect--variable-scope (phpi-prop-definition prop))) + +;; helper functions +(cl-defmethod phpi-var-type ((var phpinspect--variable)) + (phpinspect--variable-type var)) + +(cl-defmethod phpi-var-type ((prop phpinspect-property)) + (phpi-prop-type prop)) + +(cl-defmethod phpi-var-name ((var phpinspect--variable)) + (phpinspect--variable-name var)) + +(cl-defmethod phpi-var-name ((prop phpinspect-property)) + (phpinspect-name-string (phpi-prop-name prop))) + +(cl-defmethod phpi-var-vanilla-p ((variable phpinspect--variable)) + (phpinspect--variable-vanilla-p variable)) + +(cl-defmethod phpi-var-vanilla-p ((prop phpinspect-property)) + (phpi-prop-vanilla-p prop)) + +(provide 'phpinspect-property-cell) +;;; phpinspect-property-cell.el ends here diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el index f27cc4bcb6..7bafaeb7b6 100644 --- a/phpinspect-resolve.el +++ b/phpinspect-resolve.el @@ -178,11 +178,11 @@ Destructively removes tokens from the end of ASSIGNMENT-TOKENS." (phpinspect--log "Getting cached project class variable type %s::%s" class-fqn variable-name) (let ((found-variable - (phpi-typedef-get-variable + (phpi-typedef-get-property (phpinspect-rctx-get-typedef rctx class-fqn) variable-name))) (when found-variable - (phpinspect--variable-type found-variable)))) + (phpi-var-type found-variable)))) (defsubst phpinspect-get-cached-project-typedef-static-method-type (rctx class-fqn method-name) diff --git a/phpinspect-suggest.el b/phpinspect-suggest.el index d6f8c56240..be5a59bc13 100644 --- a/phpinspect-suggest.el +++ b/phpinspect-suggest.el @@ -94,12 +94,11 @@ (progn (phpinspect--log "Failed to find methods for class %s :(" class) nil))) (defun phpinspect--get-variables-for-class (rctx class-name &optional static) - (let ((class (phpinspect-rctx-get-typedef - rctx class-name 'no-enqueue))) + (let ((class (phpinspect-rctx-get-typedef rctx class-name 'no-enqueue))) (when class (if static - (append (phpi-typedef-get-static-variables class) (phpi-typedef-get-constants class)) - (phpi-typedef-get-variables class))))) + (append (phpi-typedef-get-static-properties class) (phpi-typedef-get-constants class)) + (phpi-typedef-get-properties class))))) (defun phpinspect--make-method-lister (resolvecontext &optional static) (lambda (fqn) @@ -114,6 +113,10 @@ (cl-defmethod phpinspect--candidate-scope ((candidate phpinspect--variable)) (phpinspect--variable-scope candidate)) +(cl-defmethod phpinspect--candidate-scope ((candidate phpinspect-property)) + (phpi-prop-scope candidate)) + + (defun phpinspect-suggest-attributes-at-point (resolvecontext &optional static) "Suggest object or class attributes at point. diff --git a/phpinspect-typedef.el b/phpinspect-typedef.el index 5106b2a0a7..58bce5cfd2 100644 --- a/phpinspect-typedef.el +++ b/phpinspect-typedef.el @@ -23,6 +23,8 @@ ;;; Code: (require 'phpinspect-method-cell) +(require 'phpinspect-property-cell) +(require 'phpinspect-type) (cl-defstruct (phpinspect-typedef (:constructor phpinspect-make-typedef-generated) @@ -45,8 +47,8 @@ non-nil value.") :type phpinspect-method-collection) (static-methods nil :type phpinspect-method-collection) - (variables nil - :type list) + (properties nil + :type list) (initial-index nil :type bool) (trait-config nil @@ -76,6 +78,7 @@ non-nil value.") (let ((def (phpinspect-make-typedef-generated :name type :retriever retriever + :properties (phpinspect-make-property-collection :home-type type) :methods (phpinspect-make-method-collection :home-type type) :static-methods (phpinspect-make-method-collection :home-type type) :method-adder (phpinspect-make-method-adder)))) @@ -114,6 +117,8 @@ Conditionally executes BODY depending on (cl-defstruct (phpinspect-method-adder (:constructor phpinspect-make-method-adder) (:conc-name phpi-ma-)) + "The method adder adds methods to a method-collection, aliasing +and/or overriding methods when appropriate." (aliases nil :type alist) (overrides nil @@ -251,14 +256,19 @@ Note: this function returns all types found in CONFIG." "Add methods and properties to DEF from FOREIGN-DEF." (phpi--typedef-edit def (when foreign-def - (dolist (method (phpi-typedef-get-methods foreign-def)) - (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-methods def) method)) + (let ((ma (phpi-typedef-method-adder def)) + (methods (phpi-typedef-methods def)) + (st-methods (phpi-typedef-static-methods def)) + (props (phpi-typedef-properties def))) - (dolist (method (phpi-typedef-get-static-methods foreign-def)) - (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-static-methods def) method)) + (dolist (method (phpi-typedef-get-methods foreign-def)) + (phpi-ma-add ma methods method)) - (dolist (var (phpi-typedef-variables foreign-def)) - (phpi-typedef-set-variable def var))))) + (dolist (method (phpi-typedef-get-static-methods foreign-def)) + (phpi-ma-add ma st-methods method)) + + (dolist (prop (phpi-pcol-list-active (phpi-typedef-properties foreign-def))) + (phpi-pcol-add props prop)))))) (defun phpi-typedef-subscribe-to-foreign-typedef (def foreign-def) (phpi--typedef-edit def @@ -280,11 +290,10 @@ extended classes, used traits or implemented interfaces." :test #'phpinspect--type=)) ;; Remove all members of foreign typedef - (phpi-mcol-delete-for-type (phpi-typedef-methods def) (phpi-typedef-name foreign-def)) - (phpi-mcol-delete-for-type (phpi-typedef-static-methods def) (phpi-typedef-name foreign-def)) - (dolist (variable (phpi-typedef-variables foreign-def)) - (phpi-typedef-delete-variable def variable)))) - + (let ((ft (phpi-typedef-name foreign-def))) + (phpi-mcol-delete-for-type (phpi-typedef-methods def) ft) + (phpi-mcol-delete-for-type (phpi-typedef-static-methods def) ft) + (phpi-pcol-delete-for-type (phpi-typedef-properties def) ft)))) (defun phpi-typedef-trigger-subscriber-update (def) "Incorporate DEF into subscribed typedefs." @@ -304,6 +313,21 @@ If STATIC is non-nil, updates static method." (phpi-typedef-set-static-method foreign-def method) (phpi-typedef-set-method foreign-def method))))) +(defun phpi-typedef-trigger-subscriber-property-update (def property) + "Update downstream subscribers of DEF by setting PROPERTY." + (dolist (type (phpi-typedef-subscribed-types def)) + (when-let ((foreign-def (phpi--typedef-get-foreign-type def type))) + (phpi-typedef-set-property foreign-def property)))) + +(defun phpi-typedef-trigger-subscriber-property-delete (def origin-type property-name) + "Update subscribers of DEF by deleting PROPERTY-NAME of origin TYPE." + (dolist (subtype (phpi-typedef-subscribed-types def)) + (when-let ((foreign-def (phpi--typedef-get-foreign-type def subtype))) + (phpi--typedef-edit foreign-def + (phpi-pcol-delete-for-type + (phpi-typedef-properties foreign-def) origin-type property-name) + (phpi-typedef-trigger-subscriber-property-delete foreign-def origin-type property-name))))) + (defun phpi-typedef-trigger-subscriber-method-delete (def type method-name &optional static) "Update subscribers of DEF by deleting METHOD-NAME of origin TYPE." (dolist (subtype (phpi-typedef-subscribed-types def)) @@ -327,7 +351,8 @@ TYPE must be a structure of type `phpinspect--type'." (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-static-methods def) type))))) + (phpi-mcol-set-home-type (phpi-typedef-static-methods def) type) + (phpi-pcol-set-home-type (phpi-typedef-properties def) type))))) (defun phpi-typedef-update-declaration (def declaration imports namespace-name trait-config) "Update declaration of DEF. @@ -392,18 +417,15 @@ them, which are then incorporated into DEF's properties." (let ((ma (phpi-typedef-method-adder def)) (mcol (phpi-typedef-methods def)) (stmcol (phpi-typedef-static-methods def)) - (home-type (phpi-typedef-name def))) + (home-type (phpi-typedef-name def)) + (pcol (phpi-typedef-properties def))) ;; Override methods when class seems syntactically correct (has balanced braces) (when (alist-get 'complete index) - ;; Delete all known own methods + ;; Delete all known own methods and properties (phpi-mcol-delete-for-type mcol home-type) (phpi-mcol-delete-for-type stmcol home-type) - - ;; FIXME: delete variables. This will require some way to keep track of - ;; a variables origin as we would otherwise risk deleting variables that - ;; originate from extended classes. - ) + (phpi-pcol-delete-for-type pcol home-type)) (dolist (method (alist-get 'methods index)) (phpi-ma-add ma mcol (phpinspect-make-method home-type method) 'overwrite)) @@ -413,10 +435,9 @@ them, which are then incorporated into DEF's properties." (setf (phpi-typedef-initial-index def) t) - (setf (phpi-typedef-variables def) - (append (alist-get 'variables index) - (alist-get 'constants index) - (alist-get 'static-variables index))) + (let-alist index + (dolist (var (append .variables .constants .static-variables)) + (phpi-pcol-add pcol (phpinspect-make-property home-type var)))) (phpi-typedef-update-extensions def `(,@(alist-get 'implements index) @@ -433,37 +454,45 @@ them, which are then incorporated into DEF's properties." ((def phpinspect-typedef) (method-name (head phpinspect-name)) &optional tryhard) (phpi-mcol-get-return-type (phpi-typedef-static-methods def) method-name tryhard)) -(cl-defmethod phpi-typedef-set-variable ((def phpinspect-typedef) +(cl-defmethod phpi-typedef-set-property ((def phpinspect-typedef) (prop phpinspect-property)) + (phpi--typedef-edit def + (phpi-pcol-add (phpi-typedef-properties def) prop) + (phpi-typedef-trigger-subscriber-property-update def prop))) + +(cl-defmethod phpi-typedef-set-property ((def phpinspect-typedef) (var phpinspect--variable)) + (phpi-typedef-set-property + def (phpinspect-make-property (phpi-typedef-name def) var))) + +(cl-defmethod phpi-typedef-delete-property ((def phpinspect-typedef) + (name (head phpinspect-name)) + &optional origin-type) (phpi--typedef-edit def - (push var (phpi-typedef-variables def)))) + (phpi-pcol-delete-for-type + (phpi-typedef-properties def) (phpi-typedef-name def) name) + (phpi-typedef-trigger-subscriber-property-delete + def (or origin-type (phpi-typedef-name def)) name))) -(cl-defmethod phpi-typedef-delete-variable ((def phpinspect-typedef) +(cl-defmethod phpi-typedef-delete-property ((def phpinspect-typedef) (var phpinspect--variable)) - (phpi--typedef-edit def - (let ((first-half (seq-take-while (lambda (clvar) (not (eq var clvar))) - (phpi-typedef-variables def)))) - ;; Only remove the first occurrence. If a parent class or a trait has the - ;; same variable, it will stick around. - (setf (phpi-typedef-variables def) - (append first-half (nthcdr (+ 1 (length first-half)) - (phpi-typedef-variables def))))))) - -(cl-defmethod phpi-typedef-get-variables ((class phpinspect-typedef)) - (seq-filter #'phpinspect--variable-vanilla-p (phpi-typedef-variables class))) - -(cl-defmethod phpi-typedef-get-static-variables ((class phpinspect-typedef)) - (seq-filter #'phpinspect--variable-static-p (phpi-typedef-variables class))) - -(cl-defmethod phpi-typedef-get-constants ((class phpinspect-typedef)) - (seq-filter #'phpinspect--variable-const-p (phpi-typedef-variables class))) - -(cl-defmethod phpi-typedef-get-variable - ((def phpinspect-typedef) (variable-name string)) - (catch 'found - (dolist (variable (phpi-typedef-variables def)) - (when (string= variable-name (phpinspect--variable-name variable)) - (throw 'found variable))))) + (phpi-typedef-delete-property def (phpinspect-intern-name (phpi-variable-name)))) + +(cl-defmethod phpi-typedef-get-properties ((def phpinspect-typedef)) + (seq-filter #'phpi-prop-vanilla-p + (phpi-pcol-list-active (phpi-typedef-properties def)))) + +(cl-defmethod phpi-typedef-get-static-properties ((class phpinspect-typedef)) + (seq-filter #'phpi-prop-static-p + (phpi-pcol-list-active (phpi-typedef-properties def)))) + +(cl-defmethod phpi-typedef-get-constants ((def phpinspect-typedef)) + (seq-filter #'phpi-prop-const-p + (phpi-pcol-list-active (phpi-typedef-properties def)))) + +(cl-defmethod phpi-typedef-get-property + ((def phpinspect-typedef) (prop-name string)) + (phpi-pcol-get-active-property + (phpi-typedef-properties def) (phpinspect-intern-name prop-name))) (defun phpi-typedef-get-dependencies (def) "Gets types that DEF directly depends on. @@ -476,14 +505,13 @@ Return value is a list if structures of the type (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))) + (dolist (prop (phpi-pcol-list-active (phpi-typedef-properties def))) + (when (phpi-prop-type prop) + (push (phpi-prop-type prop) deps))) (phpinspect--types-uniq deps)))) diff --git a/test/test-buffer.el b/test/test-buffer.el index 3b99595c80..e5bfa16c10 100644 --- a/test/test-buffer.el +++ b/test/test-buffer.el @@ -549,7 +549,7 @@ class TestClass (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass"))) - (should (= 2 (length (phpi-typedef-variables + (should (= 1 (length (phpi-typedef-get-properties (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass")))))) @@ -561,8 +561,8 @@ class TestClass (phpinspect--make-type :name "\\TestClass")))))) (should (phpinspect--type= (phpinspect--make-type :name "\\array") - (phpinspect--variable-type - (phpi-typedef-get-variable + (phpi-var-type + (phpi-typedef-get-property (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass")) @@ -626,12 +626,12 @@ class AccountStatisticsController { :fully-qualified t)))) (should class) - (let ((model (phpi-typedef-get-variable class "model")) - (priv-model (phpi-typedef-get-variable class "privModel")) + (let ((model (phpi-typedef-get-property class "model")) + (priv-model (phpi-typedef-get-property class "privModel")) ;; Static variables are stored with "$" prefix - (relation (phpi-typedef-get-variable class "$relation")) - (static-relation (phpi-typedef-get-variable class "$staticRelation")) - (relations (phpi-typedef-get-variable class "relations"))) + (relation (phpi-typedef-get-property class "$relation")) + (static-relation (phpi-typedef-get-property class "$staticRelation")) + (relations (phpi-typedef-get-property class "relations"))) (should model) (should priv-model) (should relation) @@ -646,19 +646,19 @@ class AccountStatisticsController { :fully-qualified t)) (array-type (phpinspect--make-type :name "\\array" :fully-qualified t))) - (should (phpinspect--variable-type model)) - (should (phpinspect--type= model-type (phpinspect--variable-type model))) - (should (phpinspect--variable-type priv-model)) - (should (phpinspect--type= model-type (phpinspect--variable-type priv-model))) - (should (phpinspect--variable-type relation)) - (should (phpinspect--type= relation-type (phpinspect--variable-type relation))) - (should (phpinspect--variable-type static-relation)) - (should (phpinspect--type= relation-type (phpinspect--variable-type static-relation))) + (should (phpi-var-type model)) + (should (phpinspect--type= model-type (phpi-var-type model))) + (should (phpi-var-type priv-model)) + (should (phpinspect--type= model-type (phpi-var-type priv-model))) + (should (phpi-var-type relation)) + (should (phpinspect--type= relation-type (phpi-var-type relation))) + (should (phpi-var-type static-relation)) + (should (phpinspect--type= relation-type (phpi-var-type static-relation))) - (should (phpinspect--type= array-type (phpinspect--variable-type relations))) + (should (phpinspect--type= array-type (phpi-var-type relations))) (should (phpinspect--type= relation-type - (phpinspect--type-contains (phpinspect--variable-type relations)))))))))) + (phpinspect--type-contains (phpi-var-type relations)))))))))) (ert-deftest phpinspect-buffer-parse-incrementally-unfinished-variable-scope () diff --git a/test/test-index.el b/test/test-index.el index 9e511ccc7a..2a61580fcd 100644 --- a/test/test-index.el +++ b/test/test-index.el @@ -359,11 +359,11 @@ class AccountStatisticsController { :fully-qualified t)))) (should class) - (let ((model (phpi-typedef-get-variable class "model")) - (priv-model (phpi-typedef-get-variable class "privModel")) + (let ((model (phpi-typedef-get-property class "model")) + (priv-model (phpi-typedef-get-property class "privModel")) ;; Static variables are stored with "$" prefix - (relation (phpi-typedef-get-variable class "$relation")) - (static-relation (phpi-typedef-get-variable class "$staticRelation"))) + (relation (phpi-typedef-get-property class "$relation")) + (static-relation (phpi-typedef-get-property class "$staticRelation"))) (should model) (should priv-model) (should relation) @@ -376,14 +376,14 @@ class AccountStatisticsController { :name "\\Illuminate\\Database\\Eloquent\\Relations\\Relation" :fully-qualified t))) - (should (phpinspect--variable-type model)) - (should (phpinspect--type= model-type (phpinspect--variable-type model))) - (should (phpinspect--variable-type priv-model)) - (should (phpinspect--type= model-type (phpinspect--variable-type priv-model))) - (should (phpinspect--variable-type relation)) - (should (phpinspect--type= relation-type (phpinspect--variable-type relation))) - (should (phpinspect--variable-type static-relation)) - (should (phpinspect--type= relation-type (phpinspect--variable-type static-relation))))))))) + (should (phpi-var-type model)) + (should (phpinspect--type= model-type (phpi-var-type model))) + (should (phpi-var-type priv-model)) + (should (phpinspect--type= model-type (phpi-var-type priv-model))) + (should (phpi-var-type relation)) + (should (phpinspect--type= relation-type (phpi-var-type relation))) + (should (phpi-var-type static-relation)) + (should (phpinspect--type= relation-type (phpi-var-type static-relation))))))))) (ert-deftest phpinspect-index-return-type-annotation-for-method () diff --git a/test/test-typedef.el b/test/test-typedef.el index 86687a78df..f9805b54ca 100644 --- a/test/test-typedef.el +++ b/test/test-typedef.el @@ -161,17 +161,17 @@ (ert-deftest phpinspect-typedef-variables () (let ((def (phpinspect-make-typedef (phpinspect--make-type :name "\\test")))) - (phpi-typedef-set-variable def (phpinspect--make-variable :name "test")) - (phpi-typedef-set-variable def (phpinspect--make-variable :name "test2")) + (phpi-typedef-set-property def (phpinspect--make-variable :name "test")) + (phpi-typedef-set-property def (phpinspect--make-variable :name "test2")) - (let ((test1 (phpi-typedef-get-variable def "test")) - (test2 (phpi-typedef-get-variable def "test2"))) + (let ((test1 (phpi-typedef-get-property def "test")) + (test2 (phpi-typedef-get-property def "test2"))) (should test1) - (should (string= "test" (phpinspect--variable-name test1))) + (should (string= "test" (phpi-var-name test1))) - (should (phpi-typedef-get-variable def "test2")) - (should (string= "test2" (phpinspect--variable-name test2)))))) + (should (phpi-typedef-get-property def "test2")) + (should (string= "test2" (phpi-var-name test2)))))) (ert-deftest phpinspect-typedef-set-index-trait () @@ -247,3 +247,81 @@ (should (string= "boo" (phpi-fn-name method))) (should (phpinspect--type= (phpinspect--make-type :name "\\string") (phpi-fn-return-type method)))))) + +(ert-deftest phpinspect-typedef-inherited-properties () + (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-property def3 (phpinspect--make-variable :name "testPublic" :scope '(:public))) + (phpi-typedef-set-property def3 (phpinspect--make-variable :name "testProtected" :scope '(:protected))) + (phpi-typedef-set-property def3 (phpinspect--make-variable :name "testPrivate" :scope '(:private))) + (phpi-typedef-trigger-subscriber-update def3) + + (let ((prop (phpi-typedef-get-property def1 "testPublic"))) + (should prop) + (should-not (phpi-prop-type prop))) + + (let ((prop (phpi-typedef-get-property def1 "testProtected"))) + (should prop) + (should-not (phpi-prop-type prop))) + + (let ((prop (phpi-typedef-get-property def1 "testPrivate"))) + (should-not prop)))) + +(ert-deftest phpinspect-typedef-inherited-properties-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))) + + (phpi-typedef-set-property def3 (phpinspect--make-variable :name "testPublic" :scope '(:public))) + (phpi-typedef-set-property def3 (phpinspect--make-variable :name "testProtected" :scope '(:protected))) + (phpi-typedef-set-property def3 (phpinspect--make-variable :name "testPrivate" :scope '(:private))) + + ;; Don't manually trigger subscriber update + + (let ((prop (phpi-typedef-get-property def1 "testPublic"))) + (should prop) + (should-not (phpi-prop-type prop))) + + (let ((prop (phpi-typedef-get-property def1 "testProtected"))) + (should prop) + (should-not (phpi-prop-type prop))) + + (let ((prop (phpi-typedef-get-property def1 "testPrivate"))) + (should-not prop)) + + (phpi-typedef-delete-property def3 (phpinspect-intern-name "testPublic")) + + (let ((prop (phpi-typedef-get-property def1 "testPublic"))) + (should-not prop))))