branch: externals/phpinspect commit 59c75176bc01851a3655486d14ab3a6d819235e4 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Add tests, fix bugs in reactive class property type indexation --- phpinspect-buffer.el | 41 ++++++++++++++++++++++++-- phpinspect-meta.el | 22 ++++++++++++++ phpinspect-property-cell.el | 2 +- phpinspect-typedef.el | 11 ++++--- test/test-buffer-indexation.el | 65 ++++++++++++++++++++++++++++++++++++++++++ test/test-buffer-parsing.el | 51 +++++++++++++++++++++++++++++++++ 6 files changed, 185 insertions(+), 7 deletions(-) diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el index 5896957ffa..452f65280b 100644 --- a/phpinspect-buffer.el +++ b/phpinspect-buffer.el @@ -146,7 +146,7 @@ linked with." (define-inline phpinspect--can-delete-buffer-index-for-token (token) (inline-quote (phpinspect-token-type-p - ,token :class-declaration :function-declaration :const :class-variable :function))) + ,token :class-declaration :function-declaration :const :class-variable :function :word))) (cl-defmethod phpinspect-buffer-delete-index-for-token ((buffer phpinspect-buffer) token) "Delete index entities that were derived from TOKEN. @@ -193,12 +193,24 @@ tokens that have been deleted from a buffer." (if-let ((token-meta (phpinspect-buffer-token-meta buffer token))) (phpi-typedef-delete-property-token-definition class (phpi-var-name (cdr var)) token-meta) (phpi-typedef-delete-property class (cdr var)))))) + ((phpinspect-word-p token) + (phpinspect-buffer--delete-word-index-reference buffer token)) ((or (phpinspect-this-p token) (phpinspect-attrib-p token)) (phpinspect-buffer--delete-dynamic-prop-index-reference buffer token)) ((phpinspect-function-p token) (phpinspect-buffer--delete-function-index-reference buffer token)) (t (error "Cannot delete index for token %s" token)))) +(defun phpinspect-buffer--delete-word-index-reference (buffer token) + (when-let ((index-ref (gethash token (phpinspect-buffer-token-index buffer)))) + (cond ((and (phpinspect--type-p (car index-ref)) + (phpinspect-property-p (cdr index-ref))) + (when-let ((typedef (phpinspect-project-get-typedef + (phpinspect-buffer-project buffer) + (car index-ref)))) + ;; Unset property type + (phpi-typedef-set-property-type typedef (phpi-prop-name (cdr index-ref)) nil)))))) + (defun phpinspect-buffer--delete-function-index-reference (buffer token) (when-let ((func (gethash token (phpinspect-buffer-token-index buffer)))) (let ((arg-list (phpinspect-function-argument-list token))) @@ -581,7 +593,18 @@ DECLARATION must be an object of type `phpinspect-meta'." (phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token var) - (cons (phpi-typedef-name typedef) indexed)))))) + (cons (phpi-typedef-name typedef) indexed)) + + ;; Add index reference for typehint if present + (when-let ((type-word (phpinspect-meta-find-left-sibling-matching-token + var #'phpinspect-word-p))) + (phpinspect-buffer-set-index-reference-for-token + buffer (phpinspect-meta-token type-word) + (cons (phpi-typedef-name typedef) indexed))))))) + +(defun phpinspect-buffer--index-prop-type-word (buffer word) + (let ((variable (phpinspect-meta-find-right-sibling-matching-token word #'phpinspect-class-variable-p))) + (phpinspect-buffer--index-class-variable buffer variable))) (cl-defmethod phpinspect-buffer-update-project-index ((buffer phpinspect-buffer)) "Update project index using the last parsed token map of this @@ -952,6 +975,17 @@ SHADOW should be an instance of `phpinspect-shadow'." ((pred phpinspect-class-variable-p) (phpinspect-buffer--update-class-variable-fqn-type buffer token type))))))) +(defun phpi-word-is-prop-type-and-should-be-indexed-p (word-meta additions) + (and-let* ( + ;; Word is inside a scope token + ((phpinspect-scope-p (phpinspect-meta-token (phpinspect-meta-parent word-meta)))) + + ;; Word is followed by a class variable + (var (phpinspect-meta-find-right-sibling-matching-token word-meta #'phpinspect-class-variable-p)) + + ;; Var is not already going to be indexed + ((not (gethash (phpinspect-meta-token var) additions)))))) + (defun phpi-shadow-process-additions (shadow ctx) "Process newly added tokens for SHADOW using CTX state. @@ -975,6 +1009,9 @@ SHADOW should be an instance of `phpinspect-shadow'." ((pred (phpinspect-use-p)) (phpinspect-buffer--index-use buffer addition) (setf (phpi-sidc-imports-changed ctx) t)) + ((pred phpinspect-word-p) + (when (phpi-word-is-prop-type-and-should-be-indexed-p addition additions) + (phpinspect-buffer--index-prop-type-word buffer addition))) ((or (pred phpinspect-class-variable-p) (pred phpinspect-const-p)) (phpinspect-buffer--index-class-variable buffer addition))))) diff --git a/phpinspect-meta.el b/phpinspect-meta.el index 79ad87e1d1..0aa281a3fe 100644 --- a/phpinspect-meta.el +++ b/phpinspect-meta.el @@ -245,6 +245,17 @@ positions based on the parent)." (phpinspect-splayt-find-largest-before (phpinspect-meta-children (phpinspect-meta-parent meta)) (phpinspect-meta-parent-offset meta)))) +(cl-defmethod phpinspect-meta-find-left-sibling-matching-token ((meta (head meta)) predicate) + (when-let ((parent (phpinspect-meta-parent meta))) + (catch 'phpinspect--return + (while (setq meta (phpinspect-splayt-find-largest-before (phpinspect-meta-children parent) + (phpinspect-meta-parent-offset meta))) + (when (funcall predicate (phpinspect-meta-token meta)) + (throw 'phpinspect--return meta)) + + (unless (phpinspect-comment-p (phpinspect-meta-token meta)) + (throw 'phpinspect--return nil)))))) + (define-inline phpinspect-meta-find-right-sibling (meta) (inline-letevals (meta) (inline-quote @@ -252,6 +263,17 @@ positions based on the parent)." (phpinspect-splayt-find-smallest-after (phpinspect-meta-children (phpinspect-meta-parent ,meta)) (phpinspect-meta-parent-offset ,meta)))))) +(cl-defmethod phpinspect-meta-find-right-sibling-matching-token ((meta (head meta)) predicate) + (when-let ((parent (phpinspect-meta-parent meta))) + (catch 'phpinspect--return + (while (setq meta (phpinspect-splayt-find-smallest-after (phpinspect-meta-children parent) + (phpinspect-meta-parent-offset meta))) + (when (funcall predicate (phpinspect-meta-token meta)) + (throw 'phpinspect--return meta)) + + (unless (phpinspect-comment-p (phpinspect-meta-token meta)) + (throw 'phpinspect--return nil)))))) + (define-inline phpinspect-meta-find-overlapping-child (meta point) (inline-letevals (meta point) (inline-quote diff --git a/phpinspect-property-cell.el b/phpinspect-property-cell.el index 767bbeb30d..d18dac946c 100644 --- a/phpinspect-property-cell.el +++ b/phpinspect-property-cell.el @@ -42,7 +42,7 @@ :type phpinspect-name) (definition-tokens nil) (definition nil - :type phpinspect--variable)) + :type phpinspect--variable)) (defun phpinspect-make-property (origin-type definition) ;; A property cannot have a nil name. diff --git a/phpinspect-typedef.el b/phpinspect-typedef.el index 5ef1b3c5fb..ca3a43052d 100644 --- a/phpinspect-typedef.el +++ b/phpinspect-typedef.el @@ -456,6 +456,11 @@ them, which are then incorporated into DEF's properties." ((def phpinspect-typedef) (method-name (head phpinspect-name)) &optional tryhard) (phpi-mcol-get-return-type (phpi-typedef-static-methods def) method-name tryhard)) +(cl-defmethod phpi-typedef-set-property-type ((def phpinspect-typedef) prop-name type) + (phpi--typedef-edit def + (when-let ((prop (phpi-typedef-get-property def prop-name))) + (setf (phpi-prop-type prop) type)))) + (cl-defmethod phpi-typedef-set-property ((def phpinspect-typedef) (prop phpinspect-property)) (phpi--typedef-edit def (phpi-pcol-add (phpi-typedef-properties def) prop) @@ -468,8 +473,8 @@ them, which are then incorporated into DEF's properties." (phpi-typedef-set-property def prop))) (cl-defmethod phpi-typedef-delete-property ((def phpinspect-typedef) - (name (head phpinspect-name)) - &optional origin-type) + (name (head phpinspect-name)) + &optional origin-type) (phpi--typedef-edit def (phpi-pcol-delete-for-type (phpi-typedef-properties def) (phpi-typedef-name def) name) @@ -486,7 +491,6 @@ them, which are then incorporated into DEF's properties." (phpi-typedef-delete-property def (phpinspect-intern-name (phpi-var-name prop)))) - (cl-defmethod phpi-typedef-delete-property-token-definition ((def phpinspect-typedef) (prop-name string) token-meta) (phpi-typedef-delete-property-token-definition @@ -500,7 +504,6 @@ them, which are then incorporated into DEF's properties." (unless (phpi-prop-definition-tokens prop) (phpi-typedef-delete-property def prop)))) - (cl-defmethod phpi-typedef-get-properties ((def phpinspect-typedef)) (seq-filter #'phpi-prop-vanilla-p (phpi-pcol-list-active (phpi-typedef-properties def)))) diff --git a/test/test-buffer-indexation.el b/test/test-buffer-indexation.el index ef06db4fee..fab1a72d12 100644 --- a/test/test-buffer-indexation.el +++ b/test/test-buffer-indexation.el @@ -133,6 +133,71 @@ class A (should (phpinspect--type= (phpinspect--make-type :name "\\App\\Harry") (phpi-prop-type property))))))) +(ert-deftest phpinspect-sync-property-type () + (with-temp-buffer + (let ((buffer (phpinspect-claim-buffer + (current-buffer) (phpinspect--make-dummy-composer-project-with-code)))) + + (insert "<?php + +namespace Example; + +use DateTime; + +class Sample +{ + private DateTime $date; + + public function setDate(DateTime $date) + { + $this->date = $date; + } + + public function getDate(): DateTime + { + return $this->date; + } +}") + (phpinspect-buffer-update-project-index buffer) + + (let ((typedef (phpinspect-project-get-typedef + (phpinspect-buffer-project buffer) + (phpinspect--make-type :name "\\Example\\Sample"))) + property) + + (should typedef) + (should (setq property (phpi-typedef-get-property typedef "date"))) + + (should (phpinspect--type= (phpinspect--make-type :name "\\DateTime") + (phpi-prop-type property))) + + ;; Change the type of the property to DateTimeImmutable + (goto-char 70) ;; Position right after "private " + (delete-region 70 78) ;; Delete "DateTime" + (insert "\\DateTimeImmutable") + (phpinspect-buffer-update-project-index buffer) + + (should (setq property (phpi-typedef-get-property typedef "date"))) + (should (phpi-prop-type property)) + (should (phpinspect--type= (phpinspect--make-type :name "\\DateTimeImmutable") + (phpi-prop-type property))) + + ;; Change the type of the property to string + (goto-char 71) ;; Position right after "private " + (delete-region 71 89) ;; Delete "DateTimeImmutable" + (insert "string") + (phpinspect-buffer-update-project-index buffer) + + (should (setq property (phpi-typedef-get-property typedef "date"))) + (should (phpinspect--type= (phpinspect--make-type :name "\\string") + (phpi-prop-type property))) + + ;; Remove the property + (goto-char 63) ;; Position right before "private string $date;" + (delete-region 63 82) ;; Delete "private string $date;" + (phpinspect-buffer-update-project-index buffer) + + (should-not (phpi-typedef-get-property typedef "date")))))) (provide 'test-buffer-indexation) ;;; test-buffer-indexation.el ends here diff --git a/test/test-buffer-parsing.el b/test/test-buffer-parsing.el index ef20b62c34..483f0e17c6 100644 --- a/test/test-buffer-parsing.el +++ b/test/test-buffer-parsing.el @@ -62,3 +62,54 @@ (insert "$d") (insert ")") (should (equal (phpinspect-parse-string (buffer-string)) (phpinspect-buffer-parse buffer)))))) + +(ert-deftest phpinspect-parse-nested-anonymous-functions-incrementally () + (with-temp-buffer + (let ((buffer (phpinspect-claim-buffer (current-buffer) (phpinspect--make-dummy-project)))) + (insert "<?php +class ComplexParsing { + private $callback; + + public function __construct() { + $this->callback = function($x) { + return function($y) use ($x) { + return $x + $y; + }; + }; + } + + public function executeCallback($value) { + $inner = $this->callback($value); + return $inner(10); + } +}") + (should (equal (phpinspect-parse-string (buffer-string)) (phpinspect-buffer-parse buffer))) + + ;; Insert a new method with nested anonymous functions + (goto-char 175) + (insert "\n\n public function nestedAnonymous() {\n") + (insert " $nested = function($a) {\n") + (insert " return function($b) use ($a) {\n") + (insert " return function($c) use ($a, $b) {\n") + (insert " return $a + $b + $c;\n") + (insert " };\n") + (insert " };\n") + (insert " };\n") + (insert " return $nested(1)(2)(3);\n") + (insert " }\n") + + (should (equal (phpinspect-parse-string (buffer-string)) (phpinspect-buffer-parse buffer))) + + ;; Modify the existing method to add another level of nesting + (goto-char 130) + (insert " return function($z) use ($x, $y) {\n") + (insert " return $x + $y + $z;\n") + (insert " };\n") + + (should (equal (phpinspect-parse-string (buffer-string)) (phpinspect-buffer-parse buffer))) + + ;; Add a comment inside the nested anonymous function + (goto-char 200) + (insert " // This is a comment inside a nested anonymous function\n") + + (should (equal (phpinspect-parse-string (buffer-string)) (phpinspect-buffer-parse buffer))))))