branch: externals/phpinspect commit 96dd700be53a400d547e9a99e994e03e608045eb Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Replace phpinspect--class with new phpinspect-typedef structure Typedef is more flexible and allows for more finegrained management of methods and their origins. --- phpinspect-buffer.el | 38 ++-- phpinspect-cache.el | 4 +- phpinspect-class-struct.el | 78 -------- phpinspect-class.el | 291 ---------------------------- phpinspect-completion.el | 26 ++- phpinspect-eldoc.el | 25 +-- phpinspect-imports.el | 5 - phpinspect-index.el | 27 +-- phpinspect-method-cell.el | 158 ++++++++++++++- phpinspect-project-struct.el | 14 +- phpinspect-project.el | 171 ++++++++-------- phpinspect-resolve.el | 63 +++--- phpinspect-resolvecontext.el | 4 +- phpinspect-serialize.el | 4 +- phpinspect-suggest.el | 21 +- phpinspect-type.el | 24 ++- phpinspect-typedef.el | 355 ++++++++++++++++++++++++++++++++++ phpinspect-worker.el | 2 +- phpinspect.el | 2 +- test/fixtures/IndexClass1-indexed.eld | 2 +- test/fixtures/IndexClass2-indexed.eld | 2 +- test/phpinspect-test.el | 2 +- test/test-autoload.el | 2 +- test/test-buffer.el | 62 +++--- test/test-class.el | 176 ++++++++--------- test/test-eldoc.el | 2 +- test/test-index.el | 88 +++++---- test/test-typedef.el | 75 +++++++ 28 files changed, 976 insertions(+), 747 deletions(-) diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el index a056807776..2dafc97e59 100644 --- a/phpinspect-buffer.el +++ b/phpinspect-buffer.el @@ -23,7 +23,6 @@ ;;; Code: -(require 'phpinspect-class) (require 'phpinspect-parser) (require 'phpinspect-bmap) (require 'phpinspect-edtrack) @@ -32,6 +31,7 @@ (require 'phpinspect-resolvecontext) (require 'phpinspect-resolve) (require 'phpinspect-util) +(require 'phpinspect-typedef) (phpinspect--declare-log-group 'buffer) @@ -158,24 +158,24 @@ linked with." (cond ((phpinspect-class-p token) (when-let ((class (gethash token (phpinspect-buffer-token-index buffer)))) (remhash token (phpinspect-buffer-token-index buffer)) - (phpinspect-project-delete-class (phpinspect-buffer-project buffer) class))) + (phpinspect-project-delete-typedef (phpinspect-buffer-project buffer) class))) ((or (phpinspect-const-p token) (phpinspect-variable-p token)) (when-let ((var (gethash token (phpinspect-buffer-token-index buffer)))) (remhash token (phpinspect-buffer-token-index buffer)) - (when-let ((class (phpinspect-project-get-class + (when-let ((class (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (car var)))) - (phpinspect--class-delete-variable class (cdr var))))) + (phpi-typedef-delete-variable class (cdr var))))) ((phpinspect-function-p token) (when-let ((func (gethash token (phpinspect-buffer-token-index buffer)))) (remhash token (phpinspect-buffer-token-index buffer)) (cond ((phpinspect-project-p (car func)) (phpinspect-project-delete-function (phpinspect-buffer-project buffer) (phpinspect--function-name-symbol (cdr func)))) ((phpinspect--type-p (car func)) - (when-let ((class (phpinspect-project-get-class + (when-let ((class (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (car func)))) - (phpinspect--class-delete-method class (cdr func)))) + (phpi-typedef-delete-method class (cdr func)))) (t (error "Invalid index location"))))) (t (error "Cannot delete index for token %s" token)))) @@ -241,11 +241,11 @@ linked with." (dolist (deleted deleted-classes) (if (and (setq class (phpinspect-buffer-get-index-for-token buffer (phpinspect-meta-token deleted))) - (setq replaced (assoc (phpinspect--class-declaration class) new-declarations #'equal))) + (setq replaced (assoc (phpi-typedef-declaration class) new-declarations #'equal))) (pcase-let ((`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports (cdr replaced)))) (phpinspect-buffer-update-index-reference-for-token buffer (phpinspect-meta-token deleted) (phpinspect-meta-token (cdr replaced))) - (phpinspect--class-update-declaration class (car replaced) imports namespace-name) + (phpi-typedef-update-declaration class (car replaced) imports namespace-name nil) (push (cdr replaced) indexed)) (phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deleted)))) @@ -260,9 +260,9 @@ linked with." (phpinspect-class-block (phpinspect-meta-token (cdr class))) namespace-name))) (when class-name - (setq class-obj (phpinspect-project-get-class-create project class-name 'no-enqueue)) + (setq class-obj (phpinspect-project-get-typedef-create project class-name 'no-enqueue)) (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token (cdr class)) class-obj) - (phpinspect--class-update-declaration class-obj (car class) imports namespace-name)))))) + (phpi-typedef-update-declaration class-obj (car class) imports namespace-name nil)))))) ;; Else: Index all classes (setf (phpinspect-buffer-classes buffer) (phpinspect-make-toc classes)) (phpinspect-splayt-traverse (class classes) @@ -276,9 +276,9 @@ linked with." namespace-name))) (class-obj)) (when class-name - (setq class-obj (phpinspect-project-get-class-create project class-name 'no-enqueue)) + (setq class-obj (phpinspect-project-get-typedef-create project class-name 'no-enqueue)) (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token class) class-obj) - (phpinspect--class-update-declaration class-obj (phpinspect-meta-token declaration) imports namespace-name))))))) + (phpi-typedef-update-declaration class-obj (phpinspect-meta-token declaration) imports namespace-name nil))))))) (cl-defmethod phpinspect-buffer-index-functions ((buffer phpinspect-buffer) (functions (head phpinspect-splayt))) (let ((classes (phpinspect-buffer-classes buffer)) @@ -331,14 +331,14 @@ linked with." type-resolver scope (and (phpinspect-comment-p comment-before) comment-before))) - (unless (phpinspect--function-anonyous-p indexed) + (unless (phpinspect--function-anonymous-p indexed) (if static - (phpinspect--class-set-static-method class-obj indexed) - (phpinspect--class-set-method class-obj indexed))) + (phpi-typedef-set-static-method class-obj indexed) + (phpi-typedef-set-method class-obj indexed))) (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token func) - (cons (phpinspect--class-name class-obj) indexed)))) + (cons (phpi-typedef-name class-obj) indexed)))) ;; Else: index function (pcase-let ((`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces imports func)) (comment-before (phpinspect-meta-find-left-sibling func))) @@ -432,15 +432,15 @@ linked with." (when (and (phpinspect-variable-p (phpinspect-meta-token var)) (not (phpinspect--variable-type indexed))) (setf (phpinspect--variable-type indexed) - (phpinspect--class-resolve-property-type + (phpi-typedef-resolve-property-type class-obj (phpinspect-buffer-project buffer) (cadr (phpinspect-meta-token var)) type-resolver class))) - (phpinspect--class-set-variable class-obj indexed) + (phpi-typedef-set-variable class-obj indexed) (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token var) - (cons (phpinspect--class-name class-obj) indexed))))))) + (cons (phpi-typedef-name class-obj) indexed))))))) (cl-defmethod phpinspect-buffer-reset ((buffer phpinspect-buffer)) (setf (phpinspect-buffer-tree buffer) nil) diff --git a/phpinspect-cache.el b/phpinspect-cache.el index d8f2d1265f..643872300e 100644 --- a/phpinspect-cache.el +++ b/phpinspect-cache.el @@ -102,7 +102,7 @@ as keys and project caches as values.")) (phpinspect--log "Getting stub class for %s" fqn) (catch 'return (maphash (lambda (_name project) - (when-let ((class (phpinspect-project-get-class project fqn))) + (when-let ((class (phpinspect-project-get-typedef project fqn))) (throw 'return class))) (phpinspect--cache-projects stub-cache))))) @@ -176,7 +176,7 @@ then returned." (phpinspect--make-project :fs (phpinspect-make-fs) :root project-root - :extra-class-retriever (phpinspect--cache-extra-class-retriever cache) + :extra-typedef-retriever (phpinspect--cache-extra-class-retriever cache) :extra-function-retriever (phpinspect--cache-extra-function-retriever cache) :worker (phpinspect-make-dynamic-worker)) (phpinspect--cache-projects cache))) diff --git a/phpinspect-class-struct.el b/phpinspect-class-struct.el deleted file mode 100644 index 9b7131c1ad..0000000000 --- a/phpinspect-class-struct.el +++ /dev/null @@ -1,78 +0,0 @@ -;;; phpinspect-class-struct.el --- PHP parsing and completion package -*- 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: - -;;; Code: - - -(cl-defstruct (phpinspect--class (:constructor phpinspect--make-class-generated)) - (class-retriever nil - :type lambda - :documentaton - "A function that returns classes for types -(should accept `phpinspect--type' as argument)") - - (read-only-p nil - :type boolean - :documentation - "Whether this class instance is read-only, meaning that its data -should never be changed. Methods and functions that are meant to -manipulate class data should become no-ops when this slot has a -non-nil value.") - (index nil - :type phpinspect--indexed-class - :documentation - "The index that this class is derived from") - (methods (make-hash-table :test 'eq :size 20 :rehash-size 20) - :type hash-table - :documentation - "All methods, including those from extended classes.") - (static-methods (make-hash-table :test 'eq :size 20 :rehash-size 20) - :type hash-table - :documentation - "All static methods this class provides, - including those from extended classes.") - (name nil - :type phpinspect--type) - (variables nil - :type list - :documentation - "Variables that belong to this class.") - (extended-classes nil - :type list - :documentation - "All extended/implemented classes.") - (subscriptions (make-hash-table :test #'eq :size 10 :rehash-size 1.5) - :type hash-table - :documentation - "A list of subscription functions that should be - called whenever anything about this class is - updated") - (declaration nil) - (initial-index nil - :type bool - :documentation - "A boolean indicating whether or not this class - has been indexed yet.")) - - -(provide 'phpinspect-class-struct) diff --git a/phpinspect-class.el b/phpinspect-class.el deleted file mode 100644 index 012dc7cbbf..0000000000 --- a/phpinspect-class.el +++ /dev/null @@ -1,291 +0,0 @@ -;;; phpinspect-class.el --- A model for php type 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: - -;;; Code: - -(require 'phpinspect-type) -(require 'phpinspect-class-struct) - -(defmacro phpinspect--class-edit (class &rest body) - "Declare intent to edit CLASS in BODY. - -Conditionally executes BODY depending on -`phpinspect--class-read-only-p' value." - (declare (indent 1)) - `(unless (phpinspect--class-read-only-p ,class) - ,@body)) - -(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class)) - (dolist (sub (hash-table-values (phpinspect--class-subscriptions class))) - (funcall sub class))) - -(cl-defmethod phpinspect--class-update-extensions ((class phpinspect--class) extensions) - (phpinspect--class-edit class - (setf (phpinspect--class-extended-classes class) - (seq-filter - #'phpinspect--class-p - (mapcar - (lambda (class-name) - (funcall (phpinspect--class-class-retriever class) class-name)) - extensions))) - - (dolist (extended (phpinspect--class-extended-classes class)) - (phpinspect--class-incorporate class extended)))) - - -(cl-defmethod phpinspect--class-set-index ((class phpinspect--class) - (index (head phpinspect--indexed-class))) - (phpinspect--class-edit class - (setf (phpinspect--class-declaration class) (alist-get 'declaration index)) - (setf (phpinspect--class-name class) (alist-get 'class-name index)) - - ;; Override methods when class seems syntactically correct (has balanced braces) - (when (alist-get 'complete index) - (let ((methods (phpinspect--class-methods class)) - (static-methods (phpinspect--class-static-methods class))) - - (dolist (method (hash-table-values methods)) - (unless (phpinspect--function--inherited method) - (remhash (phpinspect--function-name-symbol method) methods))) - (dolist (method (hash-table-values static-methods)) - (unless (phpinspect--function--inherited method) - (remhash (phpinspect--function-name-symbol method) static-methods))))) - - (setf (phpinspect--class-initial-index class) t) - (setf (phpinspect--class-index class) index) - - (dolist (method (alist-get 'methods index)) - (phpinspect--class-update-method class method)) - - (dolist (method (alist-get 'static-methods index)) - (phpinspect--class-update-static-method class method)) - - (setf (phpinspect--class-variables class) - (append (alist-get 'variables index) - (alist-get 'constants index) - (alist-get 'static-variables index))) - - (phpinspect--class-update-extensions - class `(,@(alist-get 'implements index) ,@(alist-get 'extends index))) - - (phpinspect--class-trigger-update class))) - -(cl-defmethod phpinspect--class-update-declaration - ((class phpinspect--class) declaration imports namespace-name) - (phpinspect--class-edit class - (pcase-let ((`(,class-name ,extends ,implements ,_used-types) - (phpinspect--index-class-declaration - declaration (phpinspect--make-type-resolver - (phpinspect--uses-to-types imports) nil namespace-name)))) - (setf (phpinspect--class-name class) class-name) - (setf (phpinspect--class-declaration class) declaration) - (phpinspect--class-update-extensions class `(,@extends ,@implements))))) - -(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) (method-name (head phpinspect-name))) - (gethash method-name (phpinspect--class-methods class))) - -(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) (method-name string)) - (phpinspect--class-get-method class (phpinspect-intern-name method-name))) - -(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) (method-name (head phpinspect-name))) - (gethash method-name (phpinspect--class-static-methods class))) - -(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) (method-name string)) - (phpinspect--class-get-static-method class (phpinspect-intern-name method-name))) - - -(cl-defmethod phpinspect--class-get-variable - ((class phpinspect--class) (variable-name string)) - (catch 'found - (dolist (variable (phpinspect--class-variables class)) - (when (string= variable-name (phpinspect--variable-name variable)) - (throw 'found variable))))) - -(cl-defmethod phpinspect--class-set-variable ((class phpinspect--class) - (var phpinspect--variable)) - (phpinspect--class-edit class - (push var (phpinspect--class-variables class)))) - -(cl-defmethod phpinspect--class-delete-variable ((class phpinspect--class) - (var phpinspect--variable)) - (phpinspect--class-edit class - (setf (phpinspect--class-variables class) - (seq-filter (lambda (clvar) (not (eq var clvar))) - (phpinspect--class-variables class))))) - -(cl-defmethod phpinspect--class-get-variables ((class phpinspect--class)) - (seq-filter #'phpinspect--variable-vanilla-p (phpinspect--class-variables class))) - -(cl-defmethod phpinspect--class-get-static-variables ((class phpinspect--class)) - (seq-filter #'phpinspect--variable-static-p (phpinspect--class-variables class))) - -(cl-defmethod phpinspect--class-get-constants ((class phpinspect--class)) - (seq-filter #'phpinspect--variable-const-p (phpinspect--class-variables class))) - -(cl-defmethod phpinspect--add-method-copy-to-map - ((map hash-table) - (class-name phpinspect--type) - (method phpinspect--function)) - (setq method (phpinspect--copy-function method)) - - (setf (phpinspect--function-return-type method) - (phpinspect--resolve-late-static-binding - (phpinspect--function-return-type method) - class-name)) - - (puthash (phpinspect--function-name-symbol method) - method - map) - method) - -(cl-defmethod phpinspect--class-set-method ((class phpinspect--class) - (method phpinspect--function) - &optional extended) - (phpinspect--class-edit class - (phpinspect--log "Adding method by name %s to class" - (phpinspect--function-name method)) - (setq method (phpinspect--add-method-copy-to-map - (phpinspect--class-methods class) - (phpinspect--class-name class) - method)) - (when extended - (setf (phpinspect--function--inherited method) extended)))) - -(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class) - (method phpinspect--function) - &optional extended) - (phpinspect--class-edit class - (setq method (phpinspect--add-method-copy-to-map - (phpinspect--class-static-methods class) - (phpinspect--class-name class) - method)) - - (when extended - (setf (phpinspect--function--inherited method) extended)))) - - - -(cl-defmethod phpinspect--class-delete-method ((class phpinspect--class) (method phpinspect--function)) - (phpinspect--class-edit class - (remhash (phpinspect--function-name-symbol method) (phpinspect--class-static-methods class)) - (remhash (phpinspect--function-name-symbol method) (phpinspect--class-methods class)))) - -(cl-defmethod phpinspect--class-get-method-return-type - ((class phpinspect--class) (method-name (head phpinspect-name))) - (let ((method (phpinspect--class-get-method class method-name))) - (when method - (phpinspect--function-return-type method)))) - -(cl-defmethod phpinspect--class-get-static-method-return-type - ((class phpinspect--class) (method-name (head phpinspect-name))) - (let ((method (phpinspect--class-get-static-method class method-name))) - (when method - (phpinspect--function-return-type method)))) - -(cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class)) - (hash-table-values (phpinspect--class-methods class))) - -(cl-defmethod phpinspect--class-get-static-method-list ((class phpinspect--class)) - (hash-table-values (phpinspect--class-static-methods class))) - - -(cl-defmethod phpinspect--merge-method ((class-name phpinspect--type) - (existing phpinspect--function) - (method phpinspect--function) - &optional extended) - (let ((new-return-type (phpinspect--resolve-late-static-binding - (phpinspect--function-return-type method) - class-name))) - (unless (phpinspect--type= new-return-type phpinspect--null-type) - (phpinspect--log "method return type %s" (phpinspect--function-return-type method)) - (setf (phpinspect--function-return-type existing) - new-return-type)) - - (setf (phpinspect--function--inherited existing) - extended) - - (setf (phpinspect--function-arguments existing) - (phpinspect--function-arguments method))) - existing) - -(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class) - (method phpinspect--function) - &optional extended) - (phpinspect--class-edit class - (let ((existing (gethash (phpinspect--function-name-symbol method) - (phpinspect--class-static-methods class)))) - (if existing - (phpinspect--merge-method (phpinspect--class-name class) existing method extended) - (phpinspect--class-set-static-method class method extended))))) - -(cl-defmethod phpinspect--class-update-method ((class phpinspect--class) - (method phpinspect--function) - &optional extended) - (phpinspect--class-edit class - (let* ((existing (gethash (phpinspect--function-name-symbol method) - (phpinspect--class-methods class)))) - - (if existing - (phpinspect--merge-method (phpinspect--class-name class) existing method extended) - (phpinspect--class-set-method class method extended))))) - -(define-inline phpinspect--scope-inherits-p (scope) - "Returns non-nil when FN has a public or protected scope." - (inline-letevals (scope) - (inline-quote (or (phpinspect-public-p ,scope) - (phpinspect-protected-p ,scope))))) - -(define-inline phpinspect--function-inherits-p (fn) - (inline-quote (phpinspect--scope-inherits-p (phpinspect--function-scope ,fn)))) - -;; FIXME: Remove inherited methods when they no longer exist in parent classes -;; (and/or the current class in the case of abstract methods). -;; TODO: Check if above comment is still accurate ^^ -(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class) - (other-class phpinspect--class)) - (phpinspect--class-edit class - (dolist (method (phpinspect--class-get-method-list other-class)) - (when (phpinspect--function-inherits-p method) - (phpinspect--class-update-method class method 'extended))) - - (dolist (method (phpinspect--class-get-static-method-list other-class)) - (when (phpinspect--function-inherits-p method) - (phpinspect--class-update-static-method class method 'extended))) - - (phpinspect--class-subscribe class other-class))) - -(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class) - (subscription-class phpinspect--class)) - (phpinspect--class-edit class - (unless (gethash subscription-class (phpinspect--class-subscriptions class)) - (let ((update-function - (lambda (new-class) - (phpinspect--class-edit class - (phpinspect--class-incorporate class new-class) - (phpinspect--class-trigger-update class))))) - (puthash subscription-class update-function - (phpinspect--class-subscriptions subscription-class)))))) - - -(provide 'phpinspect-class) -;;; phpinspect-class.el ends here diff --git a/phpinspect-completion.el b/phpinspect-completion.el index d0c4675888..15a585ec64 100644 --- a/phpinspect-completion.el +++ b/phpinspect-completion.el @@ -244,25 +244,35 @@ Returns list of `phpinspect--completion'." (phpinspect--log "Returning completion list %s" completion-list) (setq phpinspect--last-completion-list completion-list))) -(cl-defmethod phpinspect--make-completion - ((completion-candidate phpinspect--function)) - "Create a `phpinspect--completion` for COMPLETION-CANDIDATE." + +(defun phpinspect-make-fn-completion (completion-candidate) (phpinspect--construct-completion - :value (phpinspect--function-name completion-candidate) + :value (phpi-fn-name completion-candidate) :meta (concat "(" (mapconcat (lambda (arg) (concat "$" (if (> (length (car arg)) 8) (truncate-string-to-width (car arg) 8 nil) (car arg)))) - (phpinspect--function-arguments completion-candidate) + (phpi-fn-arguments completion-candidate) ", ") ") " - (phpinspect--display-format-type-name (phpinspect--function-return-type completion-candidate))) + (phpinspect--display-format-type-name (phpi-fn-return-type completion-candidate))) :annotation (concat " " (phpinspect--type-bare-name - (phpinspect--function-return-type completion-candidate))) + (phpi-fn-return-type completion-candidate))) :target completion-candidate :kind 'function)) + +(cl-defmethod phpinspect--make-completion + ((completion-candidate phpinspect--function)) + "Create a `phpinspect--completion` for COMPLETION-CANDIDATE." + (phpinspect-make-fn-completion completion-candidate)) + +(cl-defmethod phpinspect--make-completion + ((completion-candidate phpinspect-method)) + "Create a `phpinspect--completion` for COMPLETION-CANDIDATE." + (phpinspect-make-fn-completion completion-candidate)) + (cl-defmethod phpinspect--make-completion ((completion-candidate phpinspect--variable)) (phpinspect--construct-completion @@ -312,7 +322,7 @@ Returns list of `phpinspect--completion'." (when (and (eq 'finished state) (eq 'function (phpinspect--completion-kind comp))) (insert "(") - (when (= 0 (length (phpinspect--function-arguments + (when (= 0 (length (phpi-fn-arguments (phpinspect--completion-target comp)))) (insert ")"))))) :company-kind (lambda (comp-name) diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el index 538a9d2732..7974f3496d 100644 --- a/phpinspect-eldoc.el +++ b/phpinspect-eldoc.el @@ -84,30 +84,30 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") (setq type-before (phpinspect-resolve-type-from-context rctx nil t)) (when type-before - (let ((class (phpinspect-project-get-class-extra-or-create + (let ((class (phpinspect-project-get-typedef-extra-or-create (phpinspect--resolvecontext-project rctx) type-before 'no-enqueue)) (attribute-name (cadadr attrib)) variable method result) (when attribute-name (cond ((phpinspect-static-attrib-p attrib) - (setq variable (phpinspect--class-get-variable class attribute-name)) + (setq variable (phpi-typedef-get-variable class attribute-name)) (if (and variable (or (phpinspect--variable-static-p variable) (phpinspect--variable-const-p variable))) (setq result variable) - (setq method (phpinspect--class-get-static-method + (setq method (phpi-typedef-get-static-method class (phpinspect-intern-name attribute-name))) (when method (setq result (phpinspect-make-function-doc :fn method))))) ((phpinspect-object-attrib-p attrib) - (setq variable (phpinspect--class-get-variable class attribute-name)) + (setq variable (phpi-typedef-get-variable class attribute-name)) (if (and variable (phpinspect--variable-vanilla-p variable)) (setq result variable) - (setq method (phpinspect--class-get-method + (setq method (phpi-typedef-get-method class (phpinspect-intern-name attribute-name))) (when method (setq result (phpinspect-make-function-doc :fn method)))))) @@ -170,6 +170,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") ((setq match-result (phpinspect--match-sequence (last statement 2) :f (phpinspect-meta-wrap-token-pred #'phpinspect-attrib-p) :f (phpinspect-meta-wrap-token-pred #'phpinspect-list-p))) + (phpinspect--log "Eldoc context is a method call") (setq arg-list (car (last match-result)) static (phpinspect-static-attrib-p (phpinspect-meta-token (car match-result))) @@ -189,11 +190,12 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") (when-let* ((type-of-previous-statement (phpinspect-resolve-type-from-context rctx nil t)) (method-name (cadadr (phpinspect-meta-token (car match-result)))) - (class (phpinspect-rctx-get-or-create-cached-project-class + + (class (phpinspect-rctx-get-typedef rctx type-of-previous-statement 'no-enqueue)) (method (if static - (phpinspect--class-get-static-method class method-name) - (phpinspect--class-get-method class method-name)))) + (phpi-typedef-get-static-method class method-name) + (phpi-typedef-get-method class method-name)))) (when method (phpinspect-make-function-doc :fn method :arg-pos arg-pos)))) @@ -231,12 +233,13 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") :type phpinspect--function) (arg-pos nil)) + (cl-defmethod phpinspect-eldoc-string ((doc phpinspect-function-doc)) (let ((fn (phpinspect-function-doc-fn doc)) (arg-pos (phpinspect-function-doc-arg-pos doc)) (arg-count 0)) (concat (truncate-string-to-width - (phpinspect--function-name fn) phpinspect-eldoc-word-width) ": (" + (phpi-fn-name fn) phpinspect-eldoc-word-width) ": (" (mapconcat (lambda (arg) (let ((doc-string @@ -250,10 +253,10 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") doc-string 'face 'eldoc-highlight-function-argument))) (setq arg-count (+ arg-count 1)) doc-string)) - (phpinspect--function-arguments fn) + (phpi-fn-arguments fn) ", ") "): " - (phpinspect--display-format-type-name (phpinspect--function-return-type fn))))) + (phpinspect--display-format-type-name (phpi-fn-return-type fn))))) (defvar phpinspect-eldoc-strategies (list (phpinspect-make-eld-attribute) (phpinspect-make-eld-function-args) diff --git a/phpinspect-imports.el b/phpinspect-imports.el index 41743f15b6..f481c8ca00 100644 --- a/phpinspect-imports.el +++ b/phpinspect-imports.el @@ -378,11 +378,6 @@ that there are import (\"use\") statements for them." (phpinspect-add-use-statements-for-missing-types used-types buffer (append imports namespace-imports) project token-meta) - (with-current-buffer (get-buffer-create "attempt-before-namespace-removal-code") - (erase-buffer) - (insert (with-current-buffer (phpinspect-buffer-buffer buffer) (buffer-string)))) - - (phpinspect-remove-unneeded-use-statements used-types buffer (append imports namespace-imports) token-meta) diff --git a/phpinspect-index.el b/phpinspect-index.el index 12b226241b..59f55aa374 100644 --- a/phpinspect-index.el +++ b/phpinspect-index.el @@ -181,7 +181,7 @@ function (think \"new\" statements, return types etc.)." :token php-func :name (concat (if namespace (concat namespace "\\") "") name) :throws throws - :return-type (or type phpinspect--null-type) + :return-type type :arguments arguments))) (define-inline phpinspect--safe-cadr (list) @@ -329,6 +329,7 @@ SCOPE should be a scope token (`phpinspect-scope-p')." "Create an alist with relevant attributes of a parsed class." (phpinspect--log "INDEXING CLASS") (let ((methods) + (trait-config) (static-methods) (static-variables) (variables) @@ -414,9 +415,10 @@ SCOPE should be a scope token (`phpinspect-scope-p')." ;; Prevent comments from sticking around too long ((and (phpinspect-use-p token) (phpinspect-word-p (cadr token))) - ;; FIXME: Actually implement indexation of trait usage. This just - ;; marks the type as used for now. - (push (cadadr token) used-types)) + ;; Trait use statement + (setq trait-config + (nconc trait-config + (phpinspect--index-trait-use token type-resolver add-used-types)))) (t (phpinspect--log "Unsetting comment-before") (setq comment-before nil)))) @@ -458,6 +460,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) (class-name . ,class-name) (declaration . ,(seq-find #'phpinspect-declaration-p class)) (location . ,(funcall location-resolver class)) @@ -560,13 +563,13 @@ NAMESPACE will be assumed the root namespace if not provided" (setq function (phpinspect--index-function-from-scope type-resolver `(:public ,token) comment-before add-used-types namespace)) - (unless (phpinspect--function-anonyous-p function) + (unless (phpinspect--function-anonymous-p function) (push function functions))) ((phpinspect-block-or-list-p token) (dolist (fn (phpinspect--index-functions-in-tokens (cdr token) type-resolver-factory imports namespace add-used-types type-resolver)) - (unless (phpinspect--function-anonyous-p fn) + (unless (phpinspect--function-anonymous-p fn) (push fn functions)))))) functions)) @@ -710,10 +713,10 @@ Returns a list of type name strings." (defun phpinspect--index-trait-use (token type-resolver add-used-types) (cl-assert (phpinspect-use-p token)) - (setq token (cddr (seq-filter #'phpinspect-not-comment-p token))) + (setq token (cdr (seq-filter #'phpinspect-not-comment-p token))) (let ((block? (car (last token))) - used-types config match) + used-types config) (when (phpinspect-end-of-statement-p block?) (setq token (butlast token))) @@ -721,7 +724,7 @@ Returns a list of type name strings." (dolist (word token) (when (phpinspect-word-p word) - (push (cadr token) used-types) + (push (cadr word) used-types) (push `(,(funcall type-resolver (phpinspect--make-type :name (cadr word)))) config))) @@ -762,9 +765,11 @@ 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?)))))) - config))) + (when add-used-types + (funcall add-used-types used-types)) + config)) (provide 'phpinspect-index) ;;; phpinspect-index.el ends here diff --git a/phpinspect-method-cell.el b/phpinspect-method-cell.el index 1b7b81823f..0fe412fe24 100644 --- a/phpinspect-method-cell.el +++ b/phpinspect-method-cell.el @@ -24,17 +24,35 @@ ;;; Code: (require 'cl-macs) +(require 'phpinspect-util) +(require 'phpinspect-type) (cl-defstruct (phpinspect-method - (:constructor phpinspect-make-method) - (:conc-name phpi-method-)) + (:constructor phpinspect-make-method-generated) + (:conc-name phpi-method-) + (:copier phpi-copy-method)) (name nil :type phpinspect-name) + (aliased-from nil + :type phpinspect-name) (origin-type nil :type phpinspect--type) (definition nil :type phpinspect--function)) +(defun phpinspect-make-method (origin-type definition) + "Create a method for ORIGIN-TYPE, defined in DEFINITION. + +ORIGIN-TYPE must be a structure of type `phpinspect--type'. +DEFINITION must be a structure of type `phpinspect--function'." + (phpinspect-make-method-generated + :name (phpinspect--function-name-symbol definition) + :origin-type origin-type + :definition definition)) + +(defun phpi-method-name-string (method) + (phpinspect-name-string (phpi-method-name method))) + (cl-defstruct (phpinspect-method-cell (:constructor phpinspect-make-method-cell) (:conc-name phpi-mc-)) @@ -58,6 +76,14 @@ :type alist :documentation "<phpinspect-name, phpinspect-method-cell>")) +(defun phpi-mcol-set-home-type (mcol type) + "Set home type of MCOL to TYPE. + +Also updates all members of MCOL with the same origin-type." + (setf (phpi-mcol-home-type mcol) type) + (dolist (method (phpi-mcol-list-own mcol)) + (setf (phpi-method-origin-type method) type))) + (defun phpi-mcol-find-cell (mcol method-name &optional remove) (cl-assert (phpinspect-name-p method-name)) @@ -72,24 +98,61 @@ cell)) -(defun phpi-mcol-add (mcol method) +(defun phpi-mcol-add (mcol method &optional overwrite) (let ((cell (phpi-mcol-find-cell-create mcol (phpi-method-name method)))) - (phpi-mc-set cell (phpi-mcol-home-type mcol) (phpi-method-origin-type method) method) - cell)) + ;; insert when not present or overwriting + (when (or (not (phpi-mc-get-for-type-category + cell (phpi-mcol-home-type mcol) (phpi-method-origin-type method)))) + overwrite) + (phpi-mc-set cell (phpi-mcol-home-type mcol) (phpi-method-origin-type method) method) + cell)) + +(defun phpi-mcol-list-own (mcol) + (let (list) + (dolist (cons (phpi-mcol-cells mcol)) + (when-let ((method (phpi-mc-own (cdr cons)))) + (push method list))) + list)) + +(defun phpi-mcol-list-active (mcol) + "List all active methods in MCOL." + (let (list) + (dolist (cons (phpi-mcol-cells mcol)) + (when-let ((method (phpi-mc-get-active (cdr cons)))) + (push method list))) + list)) +(defun phpi-method-set-return-type (method type) + (setf (phpinspect--function-return-type (phpi-method-definition method)) + type)) (defun phpi-mc-set (cell home-type origin-type method) - (if (phpinspect--type= home-type - origin-type) + (if (phpinspect--type= home-type origin-type) ;; Method belongs to home type - (setf (phpi-mc-own cell) method) + (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 (phpi-method-origin-type method)) + (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) + (phpi-mc-own cell) + (pcase (phpinspect--type-category type) + ('trait (phpi-mc-trait cell)) + ('interface (phpi-mc-interface cell)) + (_ (phpi-mc-inherited cell))))) + (defun phpi-method-origin-type= (method type) (phpinspect--type= (phpi-method-origin-type method) type)) @@ -119,13 +182,31 @@ https://www.php.net/manual/en/language.oop5.traits.php" (phpi-mc-inherited cell) (phpi-mc-interface cell))) +(defun phpi-mc-get-return-type (cell) + (when-let ((method (phpi-mc-get-active cell))) + (phpi-method-return-type method))) + +(define-inline phpi-try-method-return-type (method?) + (inline-letevals (method?) + (inline-quote + (and ,method? (phpi-method-return-type ,method?))))) + +(defun phpi-mc-get-return-type-tryhard (cell) + (or (phpi-try-method-return-type (phpi-mc-own cell)) + (phpi-try-method-return-type (phpi-mc-trait cell)) + (phpi-try-method-return-type (phpi-mc-inherited cell)) + (phpi-try-method-return-type (phpi-mc-interface cell)))) + +(defun 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. When NAME is provided, only method with NAME is deleted." (if name ;; Name is provided, only delete method with NAME. - (let ((cell (phpi-mcol-find-cell mcol name))) + (when-let ((cell (phpi-mcol-find-cell mcol name))) (when (phpi-mc-get-for-type cell type) (phpi-mc-set cell (phpi-mcol-home-type mcol) type nil)) @@ -146,5 +227,62 @@ When NAME is provided, only method with NAME is deleted." ;; Delete all empty cells (setf (phpi-mcol-cells mcol) (delete (cons nil nil) cells))))) +(defun phpi-mcol-get-active-method (mcol name) + (when-let ((cell (phpi-mcol-find-cell mcol name))) + (phpi-mc-get-active cell))) + +(defun phpi-mcol-get-return-type (mcol name &optional tryhard) + "Get the returntype of method with NAME from MCOL. + +If TRYHARD is provided and non-nil, also check for possible +return types in extended classes, traits and interfaces." + (when-let ((cell (phpi-mcol-find-cell mcol name))) + (if tryhard + (phpi-mc-get-return-type-tryhard cell) + (phpi-mc-get-return-type cell)))) + +;; Helpers for generic property access +(cl-defmethod phpi-fn-name ((fn phpinspect--function)) + (phpinspect--function-name fn)) + +(cl-defmethod phpi-fn-name ((method phpinspect-method)) + (phpi-method-name-string method)) + +(cl-defmethod phpi-fn-name-symbol ((fn phpinspect--function)) + (phpinspect--function-name-symbol fn)) + +(cl-defmethod phpi-fn-name-symbol ((fn phpinspect-method)) + (phpi-method-name-string fn)) + +(cl-defmethod phpi-fn-arguments ((method phpinspect-method)) + (phpi-fn-arguments (phpi-method-definition method))) + +(cl-defmethod phpi-fn-arguments ((fn phpinspect--function)) + (phpinspect--function-arguments fn)) + +(cl-defmethod phpi-fn-return-type ((fn phpinspect--function)) + (phpinspect--function-return-type fn)) + +(cl-defmethod phpi-fn-return-type ((method phpinspect-method)) + (phpi-method-return-type method)) + +(cl-defmethod phpi-fn-argument-type ((fn phpinspect--function) argument-name) + (phpinspect--function-argument-type fn argument-name)) + +(cl-defmethod phpi-fn-argument-type ((method phpinspect-method) argument-name) + (phpi-fn-argument-type (phpi-method-definition method) argument-name)) + +(cl-defmethod phpi-fn-anonymous-p ((fn phpinspect--function)) + (phpinspect--function-anonymous-p fn)) + +(cl-defmethod phpi-fn-anonymous-p ((_method phpinspect-method)) + nil) + +(cl-defmethod phpi-fn-token ((fn phpinspect--function)) + (phpinspect--function-token fn)) + +(cl-defmethod phpi-fn-token ((method phpinspect-method)) + (phpi-fn-token (phpi-method-definition method))) + (provide 'phpinspect-method-cell) ;;; phpinspect-method-cell.el ends here diff --git a/phpinspect-project-struct.el b/phpinspect-project-struct.el index 2720f00dce..07f4d269b5 100644 --- a/phpinspect-project-struct.el +++ b/phpinspect-project-struct.el @@ -35,15 +35,15 @@ should never be changed. When this slot has a non-nil value: -- Methods and functions that are meant to manipulate class data +- Methods and functions that are meant to manipulate typedef data should become no-ops. -- All classes retrieved from it should be marked as read-only as well.") - (extra-class-retriever nil +- All typedefes retrieved from it should be marked as read-only as well.") + (extra-typedef-retriever nil :type lambda :documentation "A function that should accept a `phpinspect--type' and return -matching `phpinspect--class' instances or nil. Used to discover -classes that are defined outside of project code.") +matching `phpinspect--typedef' instances or nil. Used to discover +typedefes that are defined outside of project code.") (extra-function-retriever nil :type lambda :documentation @@ -51,11 +51,11 @@ classes that are defined outside of project code.") `phpinspect-intern-name') and return matching `phpinspect--function' instances or nil. Used to discover functions that are defined outside of project code.") - (class-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5) + (typedef-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5) :type hash-table :documentation "A `hash-table` that contains all of the currently -indexed classes in the project") +indexed typedefs in the project") (function-index (make-hash-table :test 'eq :size 100 :rehash-size 2.0) :type hash-table :documentation diff --git a/phpinspect-project.el b/phpinspect-project.el index 3f46942f5e..ef38af5245 100644 --- a/phpinspect-project.el +++ b/phpinspect-project.el @@ -27,9 +27,11 @@ (require 'phpinspect-autoload) (require 'phpinspect-worker) (require 'phpinspect-index) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (require 'phpinspect-type) (require 'phpinspect-fs) +(require 'phpinspect-typedef) +(require 'phpinspect-method-cell) (require 'filenotify) (defvar phpinspect-auto-reindex nil @@ -74,10 +76,10 @@ serious performance hits. Enable at your own risk (:") ((project phpinspect-project) methods) (phpinspect-project-edit project (dolist (method methods) - (when (phpinspect--function-return-type method) + (when (phpi-method-return-type method) (phpinspect-project-enqueue-if-not-present project - (phpinspect--function-return-type method)))))) + (phpi-method-return-type method)))))) (cl-defmethod phpinspect-project-add-variable-types-to-index-queue ((project phpinspect-project) variables) @@ -90,27 +92,27 @@ serious performance hits. Enable at your own risk (:") ((project phpinspect-project) (type phpinspect--type)) (phpinspect-project-edit project (unless (phpinspect--type-is-native type) - (let ((class (phpinspect-project-get-class project type))) - (when (or (not class) - (not (or (phpinspect--class-initial-index class)))) + (let ((typedef (phpinspect-project-get-typedef project type))) + (when (or (not typedef) + (not (or (phpi-typedef-initial-index typedef)))) (unless (or (phpinspect--type= phpinspect--null-type type) (phpinspect--type-is-native type)) - (phpinspect--log "Adding unpresent class %s to index queue" type) + (phpinspect--log "Adding unpresent typedef %s to index queue" type) (phpinspect-worker-enqueue (phpinspect-project-worker project) (phpinspect-make-index-task project type)))))))) -(cl-defmethod phpinspect-project-add-class-attribute-types-to-index-queue - ((project phpinspect-project) (class phpinspect--class)) +(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 - (phpinspect--class-get-method-list class)) + (phpi-typedef-get-methods typedef)) (phpinspect-project-add-return-types-to-index-queueue project - (phpinspect--class-get-static-method-list class)) + (phpi-typedef-get-static-methods typedef)) (phpinspect-project-add-variable-types-to-index-queue project - (phpinspect--class-variables class)))) + (phpi-typedef-variables typedef)))) (cl-defmethod phpinspect-project-add-index ((project phpinspect-project) (index (head phpinspect--root-index)) &optional index-imports) @@ -118,8 +120,8 @@ serious performance hits. Enable at your own risk (:") (when index-imports (phpinspect-project-enqueue-imports project (alist-get 'imports (cdr index)))) - (dolist (indexed-class (alist-get 'classes (cdr index))) - (phpinspect-project-add-class project (cdr indexed-class) index-imports)) + (dolist (indexed-typedef (alist-get 'classes (cdr index))) + (phpinspect-project-add-typedef project (cdr indexed-typedef) index-imports)) (dolist (func (alist-get 'functions (cdr index))) (phpinspect-project-set-function project func)))) @@ -183,107 +185,106 @@ serious performance hits. Enable at your own risk (:") (phpinspect--log "Adding import to index queue: %s" import) (phpinspect-project-enqueue-if-not-present project (cdr import)))))) -(cl-defmethod phpinspect-project-delete-class ((project phpinspect-project) (class phpinspect--class)) - (phpinspect-project-delete-class project (phpinspect--class-name class))) +(cl-defmethod phpinspect-project-delete-typedef ((project phpinspect-project) (typedef phpinspect-typedef)) + (phpinspect-project-delete-typedef project (phpi-typedef-name typedef))) -(cl-defmethod phpinspect-project-delete-class ((project phpinspect-project) (class-name phpinspect--type)) +(cl-defmethod phpinspect-project-delete-typedef ((project phpinspect-project) (typedef-name phpinspect--type)) (phpinspect-project-edit project - (remhash (phpinspect--type-name-symbol class-name) (phpinspect-project-class-index project)))) + (remhash (phpinspect--type-name-symbol typedef-name) (phpinspect-project-typedef-index project)))) -(cl-defmethod phpinspect-project-add-class - ((project phpinspect-project) (indexed-class (head phpinspect--indexed-class)) &optional index-imports) +(cl-defmethod phpinspect-project-add-typedef + ((project phpinspect-project) (indexed-typedef (head phpinspect--indexed-class)) &optional index-imports) (phpinspect-project-edit project - (if (not (alist-get 'class-name (cdr indexed-class))) - (phpinspect--log "Error: Class with declaration %s does not have a name" (alist-get 'declaration indexed-class)) + (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)) ;; Else - (let* ((class-name (phpinspect--type-name-symbol - (alist-get 'class-name (cdr indexed-class)))) - (class (gethash class-name - (phpinspect-project-class-index project)))) - (unless class - (setq class (phpinspect--make-class-generated - :class-retriever (phpinspect-project-make-class-retriever project)))) + (let* ((typedef-type-name (alist-get 'class-name (cdr indexed-typedef))) + (typedef-name (phpinspect--type-name-symbol typedef-type-name)) + (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-class)))) + project (alist-get 'imports (cdr indexed-typedef)))) - (phpinspect--class-set-index class indexed-class) - (puthash class-name class (phpinspect-project-class-index project)) - (phpinspect-project-add-class-attribute-types-to-index-queue project class))))) + (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))))) -(cl-defmethod phpinspect-project-set-class - ((project phpinspect-project) (class-fqn phpinspect--type) (class phpinspect--class)) +(cl-defmethod phpinspect-project-set-typedef + ((project phpinspect-project) (typedef-fqn phpinspect--type) (typedef phpinspect-typedef)) (phpinspect-project-edit project - (puthash (phpinspect--type-name-symbol class-fqn) - class - (phpinspect-project-class-index project)))) + (puthash (phpinspect--type-name-symbol typedef-fqn) + typedef + (phpinspect-project-typedef-index project)))) -(cl-defmethod phpinspect-project-create-class - ((project phpinspect-project) (class-fqn phpinspect--type)) +(cl-defmethod phpinspect-project-create-typedef + ((project phpinspect-project) (typedef-fqn phpinspect--type)) (phpinspect-project-edit project - (let ((class (phpinspect--make-class-generated - :class-retriever (phpinspect-project-make-class-retriever project)))) - (phpinspect-project-set-class project class-fqn class) - class))) + (let ((typedef (phpinspect-make-typedef typedef-fqn (phpinspect-project-make-typedef-retriever project)))) + (phpinspect-project-set-typedef project typedef-fqn typedef) + typedef))) -(cl-defmethod phpinspect-project-get-class-create - ((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue) - "Get class object belonging to CLASS-FQN from PROJECT. +(cl-defmethod phpinspect-project-get-typedef-create + ((project phpinspect-project) (typedef-fqn phpinspect--type) &optional no-enqueue) + "Get typedef object belonging to TYPEDEF-FQN from PROJECT. -If the class does exist on the filesystem but has not yet been -indexed, it will be queued for indexation and an empty class +If the typedef does exist on the filesystem but has not yet been +indexed, it will be queued for indexation and an empty typedef object (awaiting indedaxation) is returned. -If NO-ENQUEUE is non-nil, the class will not be queued for +If NO-ENQUEUE is non-nil, the typedef will not be queued for indexation, but indexed synchronously before returning." - (let ((class (phpinspect-project-get-class project class-fqn))) - (unless class + (let ((typedef (phpinspect-project-get-typedef project typedef-fqn))) + (unless typedef (phpinspect-project-edit project - (setq class (phpinspect-project-create-class project class-fqn)) + (setq typedef (phpinspect-project-create-typedef project typedef-fqn)) (unless no-enqueue - (phpinspect-project-enqueue-if-not-present project class-fqn)))) + (phpinspect-project-enqueue-if-not-present project typedef-fqn)))) - (phpinspect--log "Got project class, no-enqueue is set to: %s, initial-index is: %s" - no-enqueue (phpinspect--class-initial-index class)) + (phpinspect--log "Got project typedef, no-enqueue is set to: %s, initial-index is: %s" + no-enqueue (phpi-typedef-initial-index typedef)) (phpinspect-project-edit project - (when (and no-enqueue (phpinspect--class-initial-index class)) - (phpinspect--log "Indexing type file for %s" class-fqn) + (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 class-fqn)))) - class)) + (phpinspect-project-index-type-file project typedef-fqn)))) + typedef)) -(cl-defmethod phpinspect-project-get-class-extra-or-create - ((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue) - (or (phpinspect-project-get-class-or-extra project class-fqn no-enqueue) - (phpinspect-project-get-class-create project class-fqn no-enqueue))) +(cl-defmethod phpinspect-project-get-typedef-extra-or-create + ((project phpinspect-project) (typedef-fqn phpinspect--type) &optional no-enqueue) + (or (phpinspect-project-get-typedef-or-extra project typedef-fqn no-enqueue) + (phpinspect-project-get-typedef-create project typedef-fqn no-enqueue))) -(cl-defmethod phpinspect-project-get-class - ((project phpinspect-project) (class-fqn phpinspect--type) &optional index) - "Get indexed class by name of CLASS-FQN stored in PROJECT." - (let ((class (gethash (phpinspect--type-name-symbol class-fqn) - (phpinspect-project-class-index project)))) - (when class +(cl-defmethod phpinspect-project-get-typedef + ((project phpinspect-project) (typedef-fqn phpinspect--type) &optional index) + "Get indexed typedef by name of TYPEDEF-FQN stored in PROJECT." + (let ((typedef (gethash (phpinspect--type-name-symbol typedef-fqn) + (phpinspect-project-typedef-index project)))) + (when typedef (when (and (phpinspect-project-read-only-p project) - (not (phpinspect--class-read-only-p class))) - (setf (phpinspect--class-read-only-p class) t)) + (not (phpi-typedef-read-only-p typedef))) + (setf (phpi-typedef-read-only-p typedef) t)) - (when (and index (not (phpinspect--class-initial-index class))) + (when (and index (not (phpi-typedef-initial-index typedef))) (phpinspect-project-add-index project - (phpinspect-project-index-type-file project class-fqn)))) + (phpinspect-project-index-type-file project typedef-fqn)))) - class)) + typedef)) -(cl-defmethod phpinspect-project-get-class-or-extra - ((project phpinspect-project) (class-fqn phpinspect--type) &optional index) - (or (phpinspect-project-get-class project class-fqn index) - (and (phpinspect-project-extra-class-retriever project) - (funcall (phpinspect-project-extra-class-retriever project) - class-fqn)))) +(cl-defmethod phpinspect-project-get-typedef-or-extra + ((project phpinspect-project) (typedef-fqn phpinspect--type) &optional index) + (or (phpinspect-project-get-typedef project typedef-fqn index) + (and (phpinspect-project-extra-typedef-retriever project) + (funcall (phpinspect-project-extra-typedef-retriever project) + typedef-fqn)))) (cl-defmethod phpinspect-project-get-type-filepath ((project phpinspect-project) (type phpinspect--type) &optional index-new) @@ -348,10 +349,10 @@ before the search is executed." (defun phpinspect-project-make-root-resolver (project) (lambda () (phpinspect-project-root project))) -(defun phpinspect-project-make-class-retriever (project) +(defun phpinspect-project-make-typedef-retriever (project) (lambda (type) - (or (phpinspect-project-get-class-or-extra project type) - (phpinspect-project-get-class-create project type)))) + (or (phpinspect-project-get-typedef-or-extra project type) + (phpinspect-project-get-typedef-create project type)))) ;;; INDEX TASK (cl-defstruct (phpinspect-index-task @@ -387,7 +388,7 @@ before the search is executed." (let ((project (phpinspect-index-task-project task)) (is-native-type (phpinspect--type-is-native (phpinspect-index-task-type task)))) - (phpinspect--log "Indexing class %s for project in %s as task." + (phpinspect--log "Indexing typedef %s for project in %s as task." (phpinspect-index-task-type task) (phpinspect-project-root project)) diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el index e66217f83d..01c05ea26d 100644 --- a/phpinspect-resolve.el +++ b/phpinspect-resolve.el @@ -25,7 +25,7 @@ (require 'phpinspect-resolvecontext) (require 'phpinspect-cache) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (require 'phpinspect-type) (require 'phpinspect-token-predicates) @@ -151,50 +151,50 @@ Destructively removes tokens from the end of ASSIGNMENT-TOKENS." statement) (defsubst phpinspect-get-cached-project-class (rctx class-fqn) - (phpinspect-project-get-class-or-extra (phpinspect--resolvecontext-project rctx) class-fqn)) + (phpinspect-project-get-typedef-or-extra (phpinspect--resolvecontext-project rctx) class-fqn)) -(defun phpinspect-get-cached-project-class-methods (rctx class-fqn &optional static) +(defun phpinspect-get-cached-project-typedef-methods (rctx class-fqn &optional static) (phpinspect--log "Getting cached project class methods for %s" class-fqn) - (let ((class (phpinspect-rctx-get-or-create-cached-project-class rctx class-fqn))) + (let ((class (phpinspect-rctx-get-typedef rctx class-fqn))) (when class (phpinspect--log "Retrieved class index, starting method collection for %s" class-fqn) (if static - (phpinspect--class-get-static-method-list class) - (phpinspect--class-get-method-list class))))) + (phpi-typedef-get-static-methods class) + (phpi-typedef-get-methods class))))) -(defsubst phpinspect-get-cached-project-class-method-type (rctx class-fqn method-name) - (let* ((class (phpinspect-rctx-get-or-create-cached-project-class rctx class-fqn)) +(defsubst phpinspect-get-cached-project-typedef-method-type (rctx class-fqn method-name) + (let* ((class (phpinspect-rctx-get-typedef rctx class-fqn)) (method)) (when class (setq method - (phpinspect--class-get-method class (phpinspect-intern-name method-name))) + (phpi-typedef-get-method class (phpinspect-intern-name method-name))) (when method - (phpinspect--function-return-type method))))) + (phpi-fn-return-type method))))) -(defsubst phpinspect-get-cached-project-class-variable-type +(defsubst phpinspect-get-cached-project-typedef-variable-type (rctx class-fqn variable-name) (phpinspect--log "Getting cached project class variable type %s::%s" class-fqn variable-name) (let ((found-variable - (phpinspect--class-get-variable - (phpinspect-rctx-get-or-create-cached-project-class rctx class-fqn) + (phpi-typedef-get-variable + (phpinspect-rctx-get-typedef rctx class-fqn) variable-name))) (when found-variable (phpinspect--variable-type found-variable)))) -(defsubst phpinspect-get-cached-project-class-static-method-type +(defsubst phpinspect-get-cached-project-typedef-static-method-type (rctx class-fqn method-name) - (let* ((class (phpinspect-rctx-get-or-create-cached-project-class rctx class-fqn)) + (let* ((class (phpinspect-rctx-get-typedef rctx class-fqn)) (method)) (when class (setq method - (phpinspect--class-get-static-method + (phpi-typedef-get-static-method class (phpinspect-intern-name method-name))) (when method - (phpinspect--function-return-type method))))) + (phpi-fn-return-type method))))) (defun phpinspect-get-derived-statement-type-in-block (resolvecontext statement php-block type-resolver &optional function-arg-list assignments) @@ -258,14 +258,14 @@ value/type of ->bar must be derived from the type of $foo. So (pop statement) (setq type-before (or - (phpinspect-get-cached-project-class-method-type + (phpinspect-get-cached-project-typedef-method-type resolvecontext (funcall type-resolver type-before) (cadr attribute-word)) type-before))) (setq type-before (or - (phpinspect-get-cached-project-class-variable-type + (phpinspect-get-cached-project-typedef-variable-type resolvecontext (funcall type-resolver type-before) (cadr attribute-word)) @@ -281,7 +281,7 @@ value/type of ->bar must be derived from the type of $foo. So (pop statement) (setq type-before (or - (phpinspect-get-cached-project-class-static-method-type + (phpinspect-get-cached-project-typedef-static-method-type resolvecontext (funcall type-resolver type-before) (cadr attribute-word)) @@ -603,13 +603,13 @@ type. (`phpinspect--type-p')" (defun phpinspect--function-get-pattern-type (fn rctx pattern type-resolver) (phpinspect-get-pattern-type-in-block rctx pattern - (phpinspect-function-block (phpinspect--function-token fn)) + (phpinspect-function-block (phpi-fn-token fn)) type-resolver - (phpinspect-function-argument-list (phpinspect--function-token fn)))) + (phpinspect-function-argument-list (phpi-fn-token fn)))) -(cl-defmethod phpinspect--class-resolve-property-type - ((class phpinspect--class) (project phpinspect-project) (property-name string) type-resolver class-token-meta) +(cl-defmethod phpi-typedef-resolve-property-type + ((typedef phpinspect-typedef) (project phpinspect-project) (property-name string) type-resolver class-token-meta) "Resolve type of POPERTY-NAME in the context of CLASS using CLASS-TOKEN-META as parse result." (let ((pattern (phpinspect--make-pattern @@ -621,17 +621,18 @@ CLASS-TOKEN-META as parse result." (constructor-name (phpinspect-intern-name "__construct"))) (or - (when-let ((constructor (phpinspect--class-get-method class constructor-name))) - (phpinspect--function-get-pattern-type constructor rctx pattern type-resolver)) + (when-let ((constructor (phpi-typedef-get-method typedef constructor-name))) + (phpinspect--function-get-pattern-type (phpi-method-definition constructor) rctx pattern type-resolver)) (catch 'found - (dolist (method (phpinspect--class-get-method-list class)) - (unless (eq constructor-name (phpinspect--function-name-symbol method)) - (when-let ((result (phpinspect--function-get-pattern-type method rctx pattern type-resolver))) + (dolist (method (phpi-typedef-get-methods typedef)) + (unless (eq constructor-name (phpi-method-name method)) + (when-let ((result (phpinspect--function-get-pattern-type + (phpi-method-definition method) rctx pattern type-resolver))) (throw 'found result)))) nil)))) -(cl-defmethod phpinspect--class-resolve-property-type - ((_class phpinspect--class) (_project phpinspect-project) property-name &rest _ignored) +(cl-defmethod phpi-typedef-resolve-property-type + ((_typedef phpinspect-typedef) (_project phpinspect-project) property-name &rest _ignored) ;; Catch-all for cases where one attempts to resolve a nil property ;; name. Saves an if-statement for the caller. ;; Can't resolve property type when property name is nil, so we do nothing. diff --git a/phpinspect-resolvecontext.el b/phpinspect-resolvecontext.el index a36e8b31f4..e5d1fa1b1f 100644 --- a/phpinspect-resolvecontext.el +++ b/phpinspect-resolvecontext.el @@ -217,10 +217,10 @@ accompanied by all of its enclosing tokens." resolvecontext))) -(defun phpinspect-rctx-get-or-create-cached-project-class (rctx class-fqn &optional no-enqueue) +(defun phpinspect-rctx-get-typedef (rctx class-fqn &optional no-enqueue) (cl-assert (phpinspect--resolvecontext-p rctx)) (let ((project (phpinspect--resolvecontext-project rctx))) - (phpinspect-project-get-class-extra-or-create project class-fqn no-enqueue))) + (phpinspect-project-get-typedef-extra-or-create project class-fqn no-enqueue))) (defun phpinspect-rctx-get-function-return-type (rctx function-name) (cl-assert (phpinspect--resolvecontext-p rctx)) diff --git a/phpinspect-serialize.el b/phpinspect-serialize.el index ca46d703fb..d40ecafd74 100644 --- a/phpinspect-serialize.el +++ b/phpinspect-serialize.el @@ -24,7 +24,7 @@ ;;; Code: (require 'phpinspect-type) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (cl-defgeneric phpinspect--serialize-type (_type) nil) @@ -63,6 +63,8 @@ (cl-defmethod phpinspect--serialize-indexed-class ((class (head phpinspect--indexed-class))) ``(phpinspect--indexed-class (complete . ,,(alist-get 'complete class)) + (trait-config . ,,(mapcar (lambda (conf) (list (phpinspect--serialize-type (car conf)) (cdr conf))) + (alist-get 'trait-config class))) (class-name . ,,(phpinspect--serialize-type (alist-get 'class-name class))) (declaration . ,(quote ,(alist-get 'declaration class))) (location . ,(quote ,(alist-get 'location class))) diff --git a/phpinspect-suggest.el b/phpinspect-suggest.el index 2a492998cb..1e2d93202e 100644 --- a/phpinspect-suggest.el +++ b/phpinspect-suggest.el @@ -28,7 +28,7 @@ (require 'phpinspect-token-predicates) (require 'phpinspect-type) (require 'phpinspect-project) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (phpinspect--declare-log-group 'suggest) @@ -75,31 +75,31 @@ variable-list))) -(defun phpinspect-get-cached-project-class-methods (rctx class-fqn &optional static) +(defun phpinspect-get-cached-project-typedef-methods (rctx class-fqn &optional static) (phpinspect--log "Getting cached project class methods for %s" class-fqn) - (let ((class (phpinspect-rctx-get-or-create-cached-project-class rctx class-fqn 'no-enqueue))) + (let ((class (phpinspect-rctx-get-typedef rctx class-fqn 'no-enqueue))) (phpinspect--log (if class "Retrieved class index, starting method collection %s" "No class index found for %s") class-fqn) (when class (if static - (phpinspect--class-get-static-method-list class) - (phpinspect--class-get-method-list class))))) + (phpi-typedef-get-static-methods class) + (phpi-typedef-get-methods class))))) (defun phpinspect--get-methods-for-class (resolvecontext class &optional static) "Find all known cached methods for CLASS." - (or (phpinspect-get-cached-project-class-methods resolvecontext class static) + (or (phpinspect-get-cached-project-typedef-methods resolvecontext class static) (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-or-create-cached-project-class + (let ((class (phpinspect-rctx-get-typedef rctx class-name 'no-enqueue))) (when class (if static - (append (phpinspect--class-get-static-variables class) (phpinspect--class-get-constants class)) - (phpinspect--class-get-variables class))))) + (append (phpi-typedef-get-static-variables class) (phpi-typedef-get-constants class)) + (phpi-typedef-get-variables class))))) (defun phpinspect--make-method-lister (resolvecontext &optional static) (lambda (fqn) @@ -108,6 +108,9 @@ (cl-defmethod phpinspect--candidate-scope ((candidate phpinspect--function)) (phpinspect--function-scope candidate)) +(cl-defmethod phpinspect--candidate-scope ((candidate phpinspect-method)) + (phpinspect--function-scope (phpi-method-definition candidate))) + (cl-defmethod phpinspect--candidate-scope ((candidate phpinspect--variable)) (phpinspect--variable-scope candidate)) diff --git a/phpinspect-type.el b/phpinspect-type.el index e4dea22951..e3cb085004 100644 --- a/phpinspect-type.el +++ b/phpinspect-type.el @@ -107,11 +107,10 @@ See https://wiki.php.net/rfc/static_return_type ." (or (phpinspect--type= type phpinspect--static-type) (phpinspect--type= type phpinspect--this-type))) -(cl-defmethod phpinspect--resolve-late-static-binding - ((type phpinspect--type) - (class-type phpinspect--type)) +(defun phpinspect--resolve-late-static-binding (type origin-type) + "Resolve TYPE late static binding using ORIGIN-TYPE." (if (phpinspect--type-does-late-static-binding type) - class-type + origin-type type)) (defsubst phpinspect--type-is-native (type) @@ -284,7 +283,7 @@ return type of the function.")) (defun phpinspect--function-argument-type (fn argument-name) (alist-get argument-name (phpinspect--function-arguments fn) nil nil #'string=)) -(defun phpinspect--function-anonyous-p (fn) +(defun phpinspect--function-anonymous-p (fn) (eq (phpinspect-intern-name "anonymous") (phpinspect--function-name-symbol fn))) (defmacro phpinspect--make-function (&rest property-list) @@ -368,7 +367,7 @@ mutability of the variable") (defun phpinspect--index-class-declaration (decl type-resolver) ;; Find out what the class extends or implements - (let (encountered-extends encountered-implements encountered-class + (let (keyword encountered-extends encountered-implements encountered-class class-name extends implements used-types) (dolist (word decl) (if (phpinspect-word-p word) @@ -383,7 +382,8 @@ mutability of the variable") (eval-when-compile (concat "^" (phpinspect--class-keyword-handler-regexp) "?$")) (cadr word)) - (setq encountered-class t)) + (setq keyword word + encountered-class t)) (t (phpinspect--log "Calling Resolver from index-class on %s" (cadr word)) (cond (encountered-extends @@ -397,7 +397,15 @@ mutability of the variable") implements) (push (cadr word) used-types)) (encountered-class - (setq class-name (funcall type-resolver (phpinspect--make-type :name (cadr word))) + (setq class-name + (funcall type-resolver (phpinspect--make-type + :category (pcase (cadr keyword) + ("class" 'class) + ("trait" 'trait) + ("interface" 'interface) + ("enum" 'enum) + (_ 'class)) + :name (cadr word))) encountered-class nil))))))) (list class-name extends implements used-types))) diff --git a/phpinspect-typedef.el b/phpinspect-typedef.el new file mode 100644 index 0000000000..1015159841 --- /dev/null +++ b/phpinspect-typedef.el @@ -0,0 +1,355 @@ + + +(require 'phpinspect-method-cell) + +(cl-defstruct (phpinspect-typedef + (:constructor phpinspect-make-typedef-generated) + (:conc-name phpi-typedef-)) + (read-only-p nil + :type boolean + :documentation + "Whether this typedef is read-only, meaning that its data +should never be changed. Methods and functions that are meant to +manipulate typedef data should become no-ops when this slot has a +non-nil value.") + (retriever nil + :type lambda + :documentaton + "A function that returns typedefs for types +(should accept object of type `phpinspect--type' as argument)") + (methods nil + :type phpinspect-method-collection) + (static-methods nil + :type phpinspect-method-collection) + (variables nil + :type list) + (initial-index nil + :type bool) + (trait-config nil + :documentation + "The configuration of traits that are used in this type") + (method-adder nil + :type phpinspect-method-adder) + (subscribed-types nil + :type list + :documentation "list of phpinspect--type structures.") + (subscribed-to-types nil + :type list + :documentation "list of phpinspect--type structures") + (name nil + :type phpinspect--type) + (declaration nil + :type phpinspect-declaration-p)) + +(defun phpinspect-make-typedef (type &optional retriever) + (cl-assert (phpinspect--type-p type)) + + (let ((def (phpinspect-make-typedef-generated + :name type + :retriever retriever + :methods (phpinspect-make-method-collection :home-type type) + :static-methods (phpinspect-make-method-collection :home-type type) + :method-adder (phpinspect-make-method-adder)))) + def)) + +(define-inline phpi--typedef-get-foreign-type (def type) + (inline-letevals (def type) + (inline-quote + (when-let ((retriever (phpi-typedef-retriever ,def))) + (funcall retriever ,type))))) + +(defmacro phpi--typedef-edit (typedef &rest body) + "Declare intent to edit TYPEDEF in BODY. + +Conditionally executes BODY depending on +`phpi-typedef-read-only-p' value." + (declare (indent 1)) + `(unless (phpi-typedef-read-only-p ,typedef) + ,@body)) + +(cl-defstruct (phpinspect-method-adder + (:constructor phpinspect-make-method-adder) + (:conc-name phpi-ma-)) + (aliases nil + :type alist) + (overrides nil + :type alist)) + +(defun phpi-ma-get-overrides (ma type) + (alist-get type (phpi-ma-overrides ma) nil nil #'phpinspect--type=)) + +(defun phpi-ma-get-aliases (ma type) + (alist-get type (phpi-ma-aliases ma) nil nil #'phpinspect--type=)) + +(defun phpi-ma-set-config (ma config) + "Set type method import config for MA to CONFIG. + +This function reconfigures MA to add methods according to the +rules specified in CONFIG. CONFIG is expected to be a list of the +structure returned by `phpinspect--index-trait-use'." + (setf (phpi-ma-aliases ma) nil) + (setf (phpi-ma-overrides ma) nil) + + (dolist (typeconf config) + (let ((type (car typeconf)) + overrides aliases) + (dolist (setting (cdr typeconf)) + (pcase (car setting) + ('alias (push (cons (phpinspect-intern-name (cadr setting)) + (phpinspect-intern-name (caddr setting))) + aliases)) + ('override (push (cons (phpinspect-intern-name (cadr setting)) + (caddr setting)) + overrides)) + (_ (error (format "Invalid type configuration instruction %s" (car setting)))))) + + (push (cons type aliases) (phpi-ma-aliases ma)) + (push (cons type overrides) (phpi-ma-overrides ma))))) + +(defun phpi-ma-add (ma mcol method &optional overwrite) + (if-let ((overrides (phpi-ma-get-overrides ma (phpi-method-origin-type method))) + (aliases (phpi-ma-get-aliases ma (phpi-method-origin-type method)))) + (progn + (if-let ((override (alist-get (phpi-method-name method) overrides))) + ;; Override basically just means insert and overwrite without checking + (phpi-mcol-add mcol method 'overwrite) + ;; Can't alias if overriding, can't override if aliasing + (when-let ((alias (alist-get (phpi-method-name method) aliases)) + (copy (phpi-copy-method method))) + ;; Rename method to alias, record original name in aliased-from slot + (setf (phpi-method-aliased-from method) (phpi-method-name method) + (phpi-method-name method) alias)))) + + ;; Not dealing with aliases or overrides, just add the methods (unless they + ;; already exist) + (phpi-mcol-add mcol method overwrite))) + +(defun phpi-typedef-get-methods (def) + (phpi-mcol-list-active (phpi-typedef-methods def))) + +(defun phpi-typedef-get-static-methods (def) + (phpi-mcol-list-active (phpi-typedef-static-methods def))) + +(cl-defmethod phpi-typedef-get-method ((def phpinspect-typedef) (method-name (head phpinspect-name))) + (phpi-mcol-get-active-method (phpi-typedef-methods def) method-name)) + +(cl-defmethod phpi-typedef-get-method ((def phpinspect-typedef) (method-name string)) + (phpi-typedef-get-method def (phpinspect-intern-name method-name))) + +(cl-defmethod phpi-typedef-get-static-method ((def phpinspect-typedef) (method-name (head phpinspect-name))) + (phpi-mcol-get-active-method (phpi-typedef-static-methods def) method-name)) + +(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-static-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-static-methods def) + method 'overwrite))) + + +(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-mcol-delete-for-type + (phpi-typedef-static-methods def) (phpi-typedef-name def) name)) + +(cl-defmethod phpi-typedef-delete-method ((def phpinspect-typedef) (fn phpinspect--function)) + (phpi-typedef-delete-method def (phpinspect--function-name-symbol fn))) + +(defun phpi-typedef-set-trait-config (def config) + (phpi--typedef-edit def + (let ((existing-config (phpi-typedef-trait-config def)) + types) + (if existing-config + ;; This can probably be made more sophisticated by only applying the + ;; difference, but just deleting all incorporated methods will do for + ;; an initial implementation. + (dolist (use existing-config) + (phpi-mcol-delete-for-type (phpi-typedef-methods def) (car use))) + ;; Apply new config + (progn + (phpi-ma-set-config (phpi-typedef-method-adder def) config) + (dolist (cons config) + (push (car config) types)))) + ;; return all types that were in config + types))) + +(defun phpi-typedef-add-foreign-members (def foreign-def) + "Add methods and properties to DEF from FOREIGN-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)) + + (dolist (method (phpi-typedef-get-static-methods foreign-def)) + (phpi-ma-add (phpi-typedef-method-adder def) (phpi-typedef-methods def) method)) + + (dolist (var (phpi-typedef-variables foreign-def)) + (phpi-typedef-set-variable def var)))) + +(defun phpi-typedef-subscribe-to-foreign-typedef (def foreign-def) + (push (phpi-typedef-name def) (phpi-typedef-subscribed-types foreign-def)) + (push (phpi-typedef-name foreign-def) (phpi-typedef-subscribed-to-types def))) + +(defun phpi-typedef-unsubscribe-from-foreign-typedef (def foreign-def) + "Unsubscribe DEF from changes in FOREIGN-DEF. + +This undoes any links and data sharing between this type and any +extended classes, used traits or implemented interfaces." + (setf (phpi-typedef-subscribed-types foreign-def) + (cl-remove (phpi-typedef-name def) (phpi-typedef-subscribed-types foreign-def) + :test #'phpinspect--type=)) + + (setf (phpi-typedef-subscribed-to-types def) + (cl-remove (phpi-typedef-name foreign-def) (phpi-typedef-subscribed-types def) + :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))) + +(defun phpi-typedef-trigger-subscriber-update (def) + "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)))) + +(defun phpi-typedef-set-name (def type) + "Set the TYPE name of DEF. + +TYPE must be a structure of type `phpinspect--type'." + (phpi--typedef-edit def + (cl-assert (phpinspect--type-p type)) + + (let ((existing (phpi-typedef-name def))) + ;; 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))))) + +(defun phpi-typedef-update-declaration (def declaration imports namespace-name trait-config) + (phpi--typedef-edit def + (pcase-let ((`(,type ,extends ,implements ,_used-types) + (phpinspect--index-class-declaration + declaration (phpinspect--make-type-resolver + (phpinspect--uses-to-types imports) nil namespace-name)))) + (phpi-typedef-set-name def type) + (setf (phpi-typedef-declaration def) declaration) + (phpi-typedef-update-extensions + def `(,@extends ,@implements ,@(phpi-typedef-set-trait-config def trait-config)))))) + +(defun phpi-typedef-update-extensions (def extensions) + (phpi--typedef-edit def + (when-let ((subscriptions (phpi-typedef-subscribed-to-types def))) + (dolist (sub subscriptions) + (unless (cl-member sub extensions :test #'phpinspect--type=) + ;; No longer an extended type, unsubscribe + (when-let ((fd (phpi--typedef-get-foreign-type def sub))) + (phpi-typedef-unsubscribe-from-foreign-typedef def fd)))) + + (dolist (ext extensions) + (when (cl-member ext subscriptions :test #'phpinspect--type=) + ;; Already subscribed, no refresh needed + (delq ext extensions)))) + + (when-let ((foreign-defs + (seq-filter + #'phpinspect-typedef-p + (mapcar + (lambda (class-name) + (funcall (phpi-typedef-retriever def) class-name)) + extensions)))) + + (dolist (fd foreign-defs) + (phpi-typedef-add-foreign-members def fd) + (phpi-typedef-subscribe-to-foreign-typedef def fd))))) + +(cl-defmethod phpi-typedef-set-index ((def phpinspect-typedef) + (index (head phpinspect--indexed-class))) + (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)) + (home-type (phpi-typedef-name def))) + + ;; Override methods when class seems syntactically correct (has balanced braces) + (when (alist-get 'complete index) + ;; Delete all known own methods + (phpi-mcol-delete-for-type mcol home-type)) + + (dolist (method (alist-get 'methods index)) + (phpi-ma-add ma mcol (phpinspect-make-method home-type method) 'overwrite)) + + (dolist (method (alist-get 'static-methods index)) + (phpi-ma-add ma stmcol (phpinspect-make-method home-type method) 'overwrite)) + + (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))) + + (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-trigger-subscriber-update def)))) + +(cl-defmethod phpi-typedef-get-method-return-type + ((def phpinspect-typedef) (method-name (head phpinspect-name)) &optional tryhard) + (phpi-mcol-get-return-type (phpi-typedef-methods def) method-name tryhard)) + +(cl-defmethod phpi-typedef-get-static-method-return-type + ((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) + (var phpinspect--variable)) + (phpi--typedef-edit def + (push var (phpi-typedef-variables def)))) + +(cl-defmethod phpi-typedef-delete-variable ((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))))) + +(provide 'phpinspect-typedef) +;;; phpinspect-typedef.el ends here diff --git a/phpinspect-worker.el b/phpinspect-worker.el index b7fde19fef..b2566ad250 100644 --- a/phpinspect-worker.el +++ b/phpinspect-worker.el @@ -27,7 +27,7 @@ (require 'phpinspect-util) (require 'phpinspect-project-struct) (require 'phpinspect-index) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (require 'phpinspect-queue) (require 'phpinspect-pipeline) diff --git a/phpinspect.el b/phpinspect.el index e38cedc517..5721588792 100644 --- a/phpinspect.el +++ b/phpinspect.el @@ -54,7 +54,7 @@ (require 'phpinspect-util) (require 'phpinspect-type) (require 'phpinspect-index) -(require 'phpinspect-class) +(require 'phpinspect-typedef) (require 'phpinspect-worker) (require 'phpinspect-autoload) (require 'phpinspect-imports) diff --git a/test/fixtures/IndexClass1-indexed.eld b/test/fixtures/IndexClass1-indexed.eld index 848dcca241..6a9c0d6de8 100644 --- a/test/fixtures/IndexClass1-indexed.eld +++ b/test/fixtures/IndexClass1-indexed.eld @@ -1 +1 @@ -`(phpinspect--root-index (imports \, (list)) (classes \, (list (cons (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t) `(phpinspect--indexed-class (complete \, t) (class-name \, (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t)) (declaration \, '(:declaration (:word "class") (:word "AuthToken"))) (location \, '(0 0)) (imports \, (list (cons (phpinspect-intern-name "ORM") (phpin [...] +`(phpinspect--root-index (imports \, (list)) (classes \, (list (cons (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t) `(phpinspect--indexed-class (complete \, t) (trait-config \, nil) (class-name \, (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t)) (declaration \, '(:declaration (:word "class") (:word "AuthToken"))) (location \, '(0 0)) (imports \, (list (cons (phpinspect-int [...] diff --git a/test/fixtures/IndexClass2-indexed.eld b/test/fixtures/IndexClass2-indexed.eld index a34d222ff0..ba8abb27af 100644 --- a/test/fixtures/IndexClass2-indexed.eld +++ b/test/fixtures/IndexClass2-indexed.eld @@ -1 +1 @@ -`(phpinspect--root-index (imports \, (list)) (classes \, (list (cons (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t) `(phpinspect--indexed-class (complete \, t) (class-name \, (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t)) (declaration \, '(:declaration (:word "class") (:word "AuthToken"))) (location \, '(0 0)) (imports \, (list (cons (phpinspect-intern-name "ORM") (phpin [...] +`(phpinspect--root-index (imports \, (list)) (classes \, (list (cons (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t) `(phpinspect--indexed-class (complete \, t) (trait-config \, nil) (class-name \, (phpinspect--make-type :name "\\App\\Entity\\AuthToken" :collection nil :contains nil :fully-qualified t)) (declaration \, '(:declaration (:word "class") (:word "AuthToken"))) (location \, '(0 0)) (imports \, (list (cons (phpinspect-int [...] diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el index 51e0a7173e..7ea20a616c 100644 --- a/test/phpinspect-test.el +++ b/test/phpinspect-test.el @@ -323,7 +323,7 @@ class FlufferUpper (load-file (concat phpinspect-test-directory "/test-project.el")) (load-file (concat phpinspect-test-directory "/test-buffer.el")) (load-file (concat phpinspect-test-directory "/test-index.el")) -(load-file (concat phpinspect-test-directory "/test-class.el")) +(load-file (concat phpinspect-test-directory "/test-typedef.el")) (load-file (concat phpinspect-test-directory "/test-type.el")) (load-file (concat phpinspect-test-directory "/test-util.el")) (load-file (concat phpinspect-test-directory "/test-bmap.el")) diff --git a/test/test-autoload.el b/test/test-autoload.el index 89b7442ff7..216e70b34f 100644 --- a/test/test-autoload.el +++ b/test/test-autoload.el @@ -175,7 +175,7 @@ (should-not (hash-table-empty-p (phpinspect-autoloader-own-types autoloader))) (should-not (hash-table-empty-p (phpinspect-autoloader-types autoloader))) - (should (phpinspect-project-get-class project (phpinspect--make-type :name "\\FilesList"))) + (should (phpinspect-project-get-typedef project (phpinspect--make-type :name "\\FilesList"))) (should (string= "/project/root/vendor/runescape/client/src/Runescape/Banana/App.php" (phpinspect-autoloader-resolve diff --git a/test/test-buffer.el b/test/test-buffer.el index 3a06e06cc5..329703c58e 100644 --- a/test/test-buffer.el +++ b/test/test-buffer.el @@ -424,11 +424,11 @@ use CCC; (phpinspect-buffer-index-namespaces buffer namespaces) (phpinspect-buffer-index-classes buffer classes) - (should (phpinspect-project-get-class (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass"))) + (should (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass"))) - (should (= 2 (hash-table-count (phpinspect-project-class-index (phpinspect-buffer-project buffer))))) - (should (= 1 (length (phpinspect--class-extended-classes - (phpinspect-project-get-class + (should (= 2 (hash-table-count (phpinspect-project-typedef-index (phpinspect-buffer-project buffer))))) + (should (= 1 (length (phpi-typedef-subscribed-to-types + (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass")))))) @@ -451,12 +451,12 @@ use CCC; (phpinspect-buffer-index-declarations buffer new-declarations) (phpinspect-buffer-index-classes buffer new-classes) - (should (phpinspect-project-get-class + (should (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass"))) - (should (= 0 (length (phpinspect--class-extended-classes - (phpinspect-project-get-class + (should (= 0 (length (phpi-typedef-subscribed-to-types + (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass"))))))) @@ -465,11 +465,11 @@ use CCC; (setf (phpinspect-bmap--root-meta (phpinspect-buffer-map buffer)) new-root) (phpinspect-buffer-index-classes buffer new-classes) - (should-not (phpinspect-project-get-class + (should-not (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass"))) - (should (= 1 (hash-table-count (phpinspect-project-class-index (phpinspect-buffer-project buffer)))))))) + (should (= 1 (hash-table-count (phpinspect-project-typedef-index (phpinspect-buffer-project buffer)))))))) (ert-deftest phpinspect-buffer-index-functions () (with-temp-buffer @@ -486,23 +486,23 @@ class TestClass (phpinspect-buffer-update-project-index buffer) - (should (phpinspect-project-get-class + (should (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\NS\\TestClass"))) - (should (= 1 (hash-table-count (phpinspect--class-methods - (phpinspect-project-get-class - (phpinspect-buffer-project buffer) - (phpinspect--make-type :name "\\NS\\TestClass")))))) + (should (= 1 (length (phpi-typedef-get-methods + (phpinspect-project-get-typedef + (phpinspect-buffer-project buffer) + (phpinspect--make-type :name "\\NS\\TestClass")))))) (setf (phpinspect-buffer-map buffer) (phpinspect-make-bmap :-root-meta (phpinspect-make-meta nil 1 400 "" 'root))) (phpinspect-buffer-index-functions buffer (phpinspect-make-splayt)) - (should (= 0 (hash-table-count (phpinspect--class-methods - (phpinspect-project-get-class - (phpinspect-buffer-project buffer) - (phpinspect--make-type :name "\\NS\\TestClass"))))))))) + (should (= 0 (length (phpi-typedef-get-methods + (phpinspect-project-get-typedef + (phpinspect-buffer-project buffer) + (phpinspect--make-type :name "\\NS\\TestClass"))))))))) (ert-deftest phpinspect-buffer-index-class-variables () (let ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader)))) @@ -542,25 +542,25 @@ class TestClass (phpinspect-buffer-index-class-variables buffer variables) - (should (phpinspect-project-get-class + (should (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass"))) - (should (= 2 (length (phpinspect--class-variables - (phpinspect-project-get-class + (should (= 2 (length (phpi-typedef-variables + (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass")))))) - (should (= 1 (length (phpinspect--class-get-constants - (phpinspect-project-get-class + (should (= 1 (length (phpi-typedef-get-constants + (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass")))))) (should (phpinspect--type= (phpinspect--make-type :name "\\array") (phpinspect--variable-type - (phpinspect--class-get-variable - (phpinspect-project-get-class + (phpi-typedef-get-variable + (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestClass")) "banana")))))) @@ -616,19 +616,19 @@ class AccountStatisticsController { (phpinspect-buffer-update-project-index buffer) - (let ((class (phpinspect-project-get-class + (let ((class (phpinspect-project-get-typedef (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\App\\Controller\\Api\\V1\\AccountStatisticsController" :fully-qualified t)))) (should class) - (let ((model (phpinspect--class-get-variable class "model")) - (priv-model (phpinspect--class-get-variable class "privModel")) + (let ((model (phpi-typedef-get-variable class "model")) + (priv-model (phpi-typedef-get-variable class "privModel")) ;; Static variables are stored with "$" prefix - (relation (phpinspect--class-get-variable class "$relation")) - (static-relation (phpinspect--class-get-variable class "$staticRelation")) - (relations (phpinspect--class-get-variable class "relations"))) + (relation (phpi-typedef-get-variable class "$relation")) + (static-relation (phpi-typedef-get-variable class "$staticRelation")) + (relations (phpi-typedef-get-variable class "relations"))) (should model) (should priv-model) (should relation) diff --git a/test/test-class.el b/test/test-class.el index 154336c302..dbf03762dd 100644 --- a/test/test-class.el +++ b/test/test-class.el @@ -23,91 +23,91 @@ ;;; Code: -(require 'ert) -(require 'phpinspect-class) -(require 'phpinspect-project) -(require 'subr-x) -(require 'phpinspect-worker) - -(phpinspect-ensure-worker) - -(ert-deftest phpinspect--merge-method-return-type () - (let* ((class-name (phpinspect--make-type :name "\\Something")) - (method1 (phpinspect--make-function - :name "fun" - :return-type (phpinspect--make-type :name "\\array"))) - (method2 (phpinspect--make-function - :name "fun" - :return-type (phpinspect--make-type :name "\\bool"))) - (result (phpinspect--merge-method class-name method1 method2))) - - (should (phpinspect--type= (phpinspect--make-type :name "\\bool") - (phpinspect--function-return-type result))) - (should (phpinspect--type= (phpinspect--make-type :name "\\bool") - (phpinspect--function-return-type method1))))) - -(ert-deftest phpinspect-class-incorporate () - (let ((class (phpinspect--make-class-generated)) - (other-class (phpinspect--make-class-generated))) - (phpinspect--class-set-index class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "Class")))) - (phpinspect--class-set-index other-class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "OtherClass")))) - (phpinspect--class-update-method - class (phpinspect--make-function :scope '(:private) :name "test" :return-type phpinspect--null-type)) - - (phpinspect--class-update-method - other-class (phpinspect--make-function :scope '(:protected) :name "other-test" :return-type phpinspect--null-type)) - - (phpinspect--class-incorporate class other-class) - - (should (= 2 (length (hash-table-values (phpinspect--class-methods class))))) - (should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class))))) - - (phpinspect--class-set-index - class - `(phpinspect--indexed-class - (complete . t) - (class-name . ,(phpinspect--make-type :name "Class")) - (methods . ,(list (phpinspect--make-function :name "test" :return-type phpinspect--null-type) - (phpinspect--make-function :name "foobar" :return-type phpinspect--null-type))))) - - (should (= 3 (length (hash-table-values (phpinspect--class-methods class))))) - - (phpinspect--class-incorporate class other-class) - (should (= 3 (length (hash-table-values (phpinspect--class-methods class))))) - - (phpinspect--class-set-index - class - `(phpinspect--indexed-class - (complete . t) - (class-name . ,(phpinspect--make-type :name "Class")) - (methods . ,(list (phpinspect--make-function :name "foobar" :return-type phpinspect--null-type))))) - - (should (= 2 (length (hash-table-values (phpinspect--class-methods class))))) - (should (phpinspect--class-get-method class (phpinspect-intern-name "other-test"))) - (should (phpinspect--class-get-method class (phpinspect-intern-name "foobar"))) - - (phpinspect--class-set-index - class - `(phpinspect--indexed-class - (complete . t) - (class-name . ,(phpinspect--make-type :name "Class")) - (methods))) - - (should (= 1 (length (hash-table-values (phpinspect--class-methods class))))) - (should (phpinspect--class-get-method class (phpinspect-intern-name "other-test"))) - - (phpinspect--class-incorporate class other-class) - (should (= 1 (length (hash-table-values (phpinspect--class-methods class))))) - (should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class))))))) - -(ert-deftest phpinspect--class-update-declaration () - (let ((class (phpinspect--make-class-generated - :class-retriever (phpinspect-project-make-class-retriever - (phpinspect--make-project))))) - (phpinspect--class-update-declaration class '(:declaration (:word "class") (:word "TestClass") - (:word "extends") (:word "OtherClass") - (:word "implements") (:word "ImplClass")) - nil "NS") - (should (= 2 (length (phpinspect--class-extended-classes class)))) - (should (phpinspect--type= (phpinspect--make-type :name "\\NS\\TestClass" :fully-qualified t) - (phpinspect--class-name class))))) +;; (require 'ert) +;; (require 'phpinspect-typedef) +;; (require 'phpinspect-project) +;; (require 'subr-x) +;; (require 'phpinspect-worker) + +;; (phpinspect-ensure-worker) + +;; (ert-deftest phpinspect--merge-method-return-type () +;; (let* ((class-name (phpinspect--make-type :name "\\Something")) +;; (method1 (phpinspect--make-function +;; :name "fun" +;; :return-type (phpinspect--make-type :name "\\array"))) +;; (method2 (phpinspect--make-function +;; :name "fun" +;; :return-type (phpinspect--make-type :name "\\bool"))) +;; (result (phpinspect--merge-method class-name method1 method2))) + +;; (should (phpinspect--type= (phpinspect--make-type :name "\\bool") +;; (phpinspect--function-return-type result))) +;; (should (phpinspect--type= (phpinspect--make-type :name "\\bool") +;; (phpinspect--function-return-type method1))))) + +;; (ert-deftest phpinspect-class-incorporate () +;; (let ((class (phpinspect--make-class-generated)) +;; (other-class (phpinspect--make-class-generated))) +;; (phpinspect--class-set-index class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "Class")))) +;; (phpinspect--class-set-index other-class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "OtherClass")))) +;; (phpinspect--class-update-method +;; class (phpinspect--make-function :scope '(:private) :name "test" :return-type phpinspect--null-type)) + +;; (phpinspect--class-update-method +;; other-class (phpinspect--make-function :scope '(:protected) :name "other-test" :return-type phpinspect--null-type)) + +;; (phpinspect--class-incorporate class other-class) + +;; (should (= 2 (length (hash-table-values (phpinspect--class-methods class))))) +;; (should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class))))) + +;; (phpinspect--class-set-index +;; class +;; `(phpinspect--indexed-class +;; (complete . t) +;; (class-name . ,(phpinspect--make-type :name "Class")) +;; (methods . ,(list (phpinspect--make-function :name "test" :return-type phpinspect--null-type) +;; (phpinspect--make-function :name "foobar" :return-type phpinspect--null-type))))) + +;; (should (= 3 (length (hash-table-values (phpinspect--class-methods class))))) + +;; (phpinspect--class-incorporate class other-class) +;; (should (= 3 (length (hash-table-values (phpinspect--class-methods class))))) + +;; (phpinspect--class-set-index +;; class +;; `(phpinspect--indexed-class +;; (complete . t) +;; (class-name . ,(phpinspect--make-type :name "Class")) +;; (methods . ,(list (phpinspect--make-function :name "foobar" :return-type phpinspect--null-type))))) + +;; (should (= 2 (length (hash-table-values (phpinspect--class-methods class))))) +;; (should (phpinspect--class-get-method class (phpinspect-intern-name "other-test"))) +;; (should (phpinspect--class-get-method class (phpinspect-intern-name "foobar"))) + +;; (phpinspect--class-set-index +;; class +;; `(phpinspect--indexed-class +;; (complete . t) +;; (class-name . ,(phpinspect--make-type :name "Class")) +;; (methods))) + +;; (should (= 1 (length (hash-table-values (phpinspect--class-methods class))))) +;; (should (phpinspect--class-get-method class (phpinspect-intern-name "other-test"))) + +;; (phpinspect--class-incorporate class other-class) +;; (should (= 1 (length (hash-table-values (phpinspect--class-methods class))))) +;; (should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class))))))) + +;; (ert-deftest phpinspect--class-update-declaration () +;; (let ((class (phpinspect--make-class-generated +;; :class-retriever (phpinspect-project-make-class-retriever +;; (phpinspect--make-project))))) +;; (phpinspect--class-update-declaration class '(:declaration (:word "class") (:word "TestClass") +;; (:word "extends") (:word "OtherClass") +;; (:word "implements") (:word "ImplClass")) +;; nil "NS") +;; (should (= 2 (length (phpinspect--class-extended-classes class)))) +;; (should (phpinspect--type= (phpinspect--make-type :name "\\NS\\TestClass" :fully-qualified t) +;; (phpinspect--class-name class))))) diff --git a/test/test-eldoc.el b/test/test-eldoc.el index a697aa1047..ad1b03cd9e 100644 --- a/test/test-eldoc.el +++ b/test/test-eldoc.el @@ -77,7 +77,7 @@ class Thing (should (phpinspect-function-doc-p result)) (should (= (cdr expected) (phpinspect-function-doc-arg-pos result))) - (should (string= "getThis" (phpinspect--function-name (phpinspect-function-doc-fn result))))))))) + (should (string= "getThis" (phpi-fn-name (phpinspect-function-doc-fn result))))))))) (ert-deftest phpinspect-eldoc-function-for-object-method () diff --git a/test/test-index.el b/test/test-index.el index 9244fef669..9e511ccc7a 100644 --- a/test/test-index.el +++ b/test/test-index.el @@ -55,6 +55,7 @@ (,(phpinspect--make-type :name "\\Potato" :fully-qualified t) phpinspect--indexed-class (complete . t) + (trait-config) (class-name . ,(phpinspect--make-type :name "\\Potato" :fully-qualified t)) (declaration . (:declaration (:word "class") (:word "Potato"))) (location . (0 0)) @@ -74,7 +75,7 @@ ("things" . ,(phpinspect--make-type :name "\\array" :collection t :fully-qualified t))) - :return-type phpinspect--null-type))) + :return-type nil))) (static-variables) (variables) (constants) @@ -116,6 +117,7 @@ try { }))))); }"))) (used-types (alist-get 'used-types (car (alist-get 'classes result))))) + (should (equal (mapcar #'phpinspect-intern-name (sort @@ -159,33 +161,33 @@ try { (methods (alist-get 'methods class))) (should (= 3 (length methods))) (dolist (method methods) - (should (member (phpinspect--function-name method) + (should (member (phpi-fn-name method) '("duplicate" "hold" "peel"))) - (cond ((string= (phpinspect--function-name method) + (cond ((string= (phpi-fn-name method) "duplicate") (should (phpinspect--type= (phpinspect--make-type :name "\\Banana" :fully-qualified t) - (phpinspect--function-return-type method)))) - ((string= (phpinspect--function-name method) + (phpi-fn-return-type method)))) + ((string= (phpi-fn-name method) "peel") (should (phpinspect--type= (phpinspect--make-type :name "\\int" :fully-qualified t) - (phpinspect--function-return-type method))) + (phpi-fn-return-type method))) - (should (= 2 (length (phpinspect--function-arguments method)))) + (should (= 2 (length (phpi-fn-arguments method)))) (should (phpinspect--type= (phpinspect--make-type :name "\\array" :fully-qualified t) - (phpinspect--function-argument-type method "loose"))) + (phpi-fn-argument-type method "loose"))) (should (phpinspect--type= (phpinspect--make-type :name "\\bool" :fully-qualified t) - (phpinspect--function-argument-type method "fast")))) - ((string= (phpinspect--function-name method) + (phpi-fn-argument-type method "fast")))) + ((string= (phpi-fn-name method) "hold") (should (phpinspect--type= (phpinspect--make-type :name "\\void" :fully-qualified t) - (phpinspect--function-return-type method)))))))) + (phpi-fn-return-type method)))))))) (ert-deftest phpinspect-index-static-method-annotations () (let* ((result (phpinspect--index-tokens @@ -200,32 +202,32 @@ try { (methods (alist-get 'static-methods class))) (should (= 3 (length methods))) (dolist (method methods) - (should (member (phpinspect--function-name method) + (should (member (phpi-fn-name method) '("create" "hold" "peel"))) - (cond ((string= (phpinspect--function-name method) + (cond ((string= (phpi-fn-name method) "duplicate") (should (phpinspect--type= (phpinspect--make-type :name "\\Banana" :fully-qualified t) - (phpinspect--function-return-type method)))) - ((string= (phpinspect--function-name method) + (phpi-fn-return-type method)))) + ((string= (phpi-fn-name method) "peel") (should (phpinspect--type= (phpinspect--make-type :name "\\int" :fully-qualified t) - (phpinspect--function-return-type method))) + (phpi-fn-return-type method))) - (should (= 2 (length (phpinspect--function-arguments method)))) + (should (= 2 (length (phpi-fn-arguments method)))) (should (phpinspect--type= (phpinspect--make-type :name "\\array" :fully-qualified t) - (phpinspect--function-argument-type method "loose"))) + (phpi-fn-argument-type method "loose"))) (should (phpinspect--type= (phpinspect--make-type :name "\\bool" :fully-qualified t) - (phpinspect--function-argument-type method "fast")))) - ((string= (phpinspect--function-name method) + (phpi-fn-argument-type method "fast")))) + ((string= (phpi-fn-name method) "hold") (should (phpinspect--type= (phpinspect--make-type :name "\\void" :fully-qualified t) - (phpinspect--function-return-type method)))))))) + (phpi-fn-return-type method)))))))) (require 'phpinspect-serialize) @@ -284,19 +286,19 @@ function example($bing): Thing {}") (should (setq functions (alist-get 'functions index))) (should (= 2 (length functions))) - (should (string= "test_func" (phpinspect--function-name (cadr functions)))) - (should (string= "example" (phpinspect--function-name (car functions)))) + (should (string= "test_func" (phpi-fn-name (cadr functions)))) + (should (string= "example" (phpi-fn-name (car functions)))) (let ((example (car functions))) - (should (= 1 (length (phpinspect--function-arguments example)))) + (should (= 1 (length (phpi-fn-arguments example)))) (should (phpinspect--type= (phpinspect--make-type :name "\\array") - (phpinspect--function-argument-type example "bing")))) + (phpi-fn-argument-type example "bing")))) (should (phpinspect--type= (phpinspect--make-type :name "\\array") - (phpinspect--function-return-type (cadr functions)))) + (phpi-fn-return-type (cadr functions)))) (should (phpinspect--type= (phpinspect--make-type :name "\\Example\\Thing") - (phpinspect--function-return-type (car functions)))))) + (phpi-fn-return-type (car functions)))))) (ert-deftest phpinspect-index-functions-in-namespace () (let* ((code "<?php @@ -316,13 +318,13 @@ function example(Firewall $wall): Thing {}") (should (setq functions (alist-get 'functions index))) (should (= 2 (length functions))) - (should (string= "Local\\test_func" (phpinspect--function-name (cadr functions)))) - (should (string= "Local\\example" (phpinspect--function-name (car functions)))) + (should (string= "Local\\test_func" (phpi-fn-name (cadr functions)))) + (should (string= "Local\\example" (phpi-fn-name (car functions)))) (should (phpinspect--type= (phpinspect--make-type :name "\\array") - (phpinspect--function-return-type (cadr functions)))) + (phpi-fn-return-type (cadr functions)))) (should (phpinspect--type= (phpinspect--make-type :name "\\Example\\Thing") - (phpinspect--function-return-type (car functions)))) + (phpi-fn-return-type (car functions)))) (should (alist-get 'used-types namespace)) (should (= 3 (length (alist-get 'used-types namespace)))) (should (member (phpinspect-intern-name "Firewall") (alist-get 'used-types namespace))) @@ -350,18 +352,18 @@ class AccountStatisticsController { }") (phpinspect-project-add-index project (phpinspect-index-current-buffer)) - (let ((class (phpinspect-project-get-class + (let ((class (phpinspect-project-get-typedef project (phpinspect--make-type :name "\\App\\Controller\\Api\\V1\\AccountStatisticsController" :fully-qualified t)))) (should class) - (let ((model (phpinspect--class-get-variable class "model")) - (priv-model (phpinspect--class-get-variable class "privModel")) + (let ((model (phpi-typedef-get-variable class "model")) + (priv-model (phpi-typedef-get-variable class "privModel")) ;; Static variables are stored with "$" prefix - (relation (phpinspect--class-get-variable class "$relation")) - (static-relation (phpinspect--class-get-variable class "$staticRelation"))) + (relation (phpi-typedef-get-variable class "$relation")) + (static-relation (phpi-typedef-get-variable class "$staticRelation"))) (should model) (should priv-model) (should relation) @@ -405,14 +407,14 @@ public function doStuff() (let* ((type (phpinspect--make-type :name "\\App\\Controller\\Api\\V1\\AccountStatisticsController" :fully-qualified t)) - (class (phpinspect-project-get-class project type))) + (class (phpinspect-project-get-typedef project type))) (should class) - (let ((method (phpinspect--class-get-method class "doStuff"))) + (let ((method (phpi-typedef-get-method class "doStuff"))) (should method) - (should (phpinspect--function-return-type method)) - (should (phpinspect--type= type (phpinspect--function-return-type method)))))))) + (should (phpi-method-return-type method)) + (should (phpinspect--type= type (phpi-method-return-type method)))))))) (ert-deftest phpinspect-index-nested-functions () (with-temp-buffer @@ -437,12 +439,12 @@ if (something()) { (let ((nestedConditional (car functions)) (conditional (cadr functions))) - (should (string= "conditional" (phpinspect--function-name conditional))) - (should (string= "nestedConditional" (phpinspect--function-name nestedConditional))))))) + (should (string= "conditional" (phpi-fn-name conditional))) + (should (string= "nestedConditional" (phpi-fn-name nestedConditional))))))) (ert-deftest phpinspect-index-trait-use () (let* ((tree (with-temp-buffer - (insert "use B, C { C::foo insteadof B, B::bar as banana }") + (insert "B, C { C::foo insteadof B, B::bar as banana }") (goto-char (point-min)) (phpinspect--parse-use (current-buffer) (point-max)))) (expected `((,(phpinspect--make-type :name "\\C" :fully-qualified t)) diff --git a/test/test-typedef.el b/test/test-typedef.el new file mode 100644 index 0000000000..76adbd33db --- /dev/null +++ b/test/test-typedef.el @@ -0,0 +1,75 @@ + + +(require 'ert) +(require 'phpinspect-typedef) +(require 'phpinspect-index) +(require 'phpinspect-parser) +(require 'phpinspect-test-env + (expand-file-name "phpinspect-test-env.el" + (file-name-directory (macroexp-file-name)))) + +(ert-deftest phpinspect-typedef-set-index-simple () + (let* ((code "class A { function B(): C {} }") + (index (phpinspect--index-tokens (phpinspect-parse-string code))) + (class (cdar (alist-get 'classes index))) + (typedef (phpinspect-make-typedef (alist-get 'class-name class)))) + (phpi-typedef-set-index typedef class) + + (should (phpi-typedef-get-methods typedef)) + (should (= 1 (length (phpi-typedef-get-methods typedef)))) + + (should (eq (phpinspect-intern-name "B") + (phpi-method-name (car (phpi-typedef-get-methods typedef))))))) + +(ert-deftest phpinspect-typedef-subscribe () + (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-method def2 (phpinspect--make-function :name "test")) + + (phpi-typedef-update-extensions def1 (list (phpi-typedef-name def2))) + + (let ((method (phpi-typedef-get-method def1 (phpinspect-intern-name "test")))) + (should method) + (should-not (phpi-method-return-type method)) + + (phpi-typedef-set-method def1 (phpinspect--make-function + :name "test" + :return-type (phpinspect--make-type :name "\\aaa"))) + + (setq method (phpi-typedef-get-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-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-method def1 (phpinspect-intern-name "test"))) + (should-not method)))) + +(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")) + + (let ((test1 (phpi-typedef-get-variable def "test")) + (test2 (phpi-typedef-get-variable def "test2"))) + + (should test1) + (should (string= "test" (phpinspect--variable-name test1))) + + (should (phpi-typedef-get-variable def "test2")) + (should (string= "test2" (phpinspect--variable-name test2))))))