Fix some bugs introduced by the incremental parsing feature
ci/woodpecker/push/woodpecker Pipeline was successful Details

Among other things:

- use-keyword parser handler result being registered for two positions due to
wrong use of "root" parser parameter.
- phpinspect-fix-imports was broken
master
Hugo Thunnissen 12 months ago
parent ad5ede01ad
commit 7f76ba4c11

@ -93,6 +93,9 @@
(defsubst phpinspect-meta-end (meta) (defsubst phpinspect-meta-end (meta)
(cadddr meta)) (cadddr meta))
(defsubst phpinspect-meta-whitespace-before (meta)
(car (cddddr meta)))
(defsubst phpinspect-meta-width (meta) (defsubst phpinspect-meta-width (meta)
(- (phpinspect-meta-end meta) (phpinspect-meta-start meta))) (- (phpinspect-meta-end meta) (phpinspect-meta-start meta)))

@ -90,7 +90,17 @@ linked with."
(cl-defmethod phpinspect-buffer-token-meta ((buffer phpinspect-buffer) token) (cl-defmethod phpinspect-buffer-token-meta ((buffer phpinspect-buffer) token)
(phpinspect-bmap-token-meta (phpinspect-buffer-map buffer) token)) (phpinspect-bmap-token-meta (phpinspect-buffer-map buffer) token))
(cl-defmethod phpinspect-buffer-location-resover ((buffer phpinspect-buffer)) (cl-defmethod phpinspect-buffer-location-resolver ((buffer phpinspect-buffer))
"Derive location resolver from BUFFER's buffer map. Guarantees to
retrieve the lastest available map of BUFFER upon first
invocation, but subsequent invocations will not update the used
map afterwards, so don't keep the resolver around for long term
use."
(let ((bmap-resolver))
(lambda (token)
(funcall (with-memoization bmap-resolver
(phpinspect-bmap-make-location-resolver (phpinspect-buffer-map buffer))) (phpinspect-bmap-make-location-resolver (phpinspect-buffer-map buffer)))
token))))
(provide 'phpinspect-buffer) (provide 'phpinspect-buffer)

@ -59,11 +59,10 @@
(or (caar edit) 0)) (or (caar edit) 0))
(defsubst phpinspect-edit-end (edit) (defsubst phpinspect-edit-end (edit)
(if edit
(let ((end (or (caar edit) 0)) (let ((end (or (caar edit) 0))
(delta 0) (delta 0)
(previous-edit (cdr edit))) (previous-edit (cdr edit)))
(+ end (phpinspect-edit-delta previous-edit))))) (+ end (phpinspect-edit-delta previous-edit))))
(defsubst phpinspect-edit-delta (edit) (defsubst phpinspect-edit-delta (edit)
(let ((delta (or (cdar edit) 0)) (let ((delta (or (cdar edit) 0))
@ -73,28 +72,47 @@
delta)) delta))
(defsubst phpinspect-edtrack-original-position-at-point (track point) (defsubst phpinspect-edtrack-original-position-at-point (track point)
(let ((edit (phpinspect-edtrack-edits track))) (let ((edit (phpinspect-edtrack-edits track))
(encroached)
(pos))
(while (and edit (< point (phpinspect-edit-end edit))) (while (and edit (< point (phpinspect-edit-end edit)))
(setq edit (cdr edit))) (setq edit (cdr edit)))
(- point (phpinspect-edit-delta edit)))) (setq pos (- point (phpinspect-edit-delta edit)))
;; When point is within the edit delta's range, correct the delta by the
;; amount of encroachment.
(if (< 0 (setq encroached (- (+ (phpinspect-edit-end edit) (or (cdar edit) 0)) point)))
(+ pos encroached)
pos)))
(defsubst phpinspect-edtrack-current-position-at-point (track point) (defsubst phpinspect-edtrack-current-position-at-point (track point)
(let ((edit (phpinspect-edtrack-edits track))) (let ((edit (phpinspect-edtrack-edits track))
(encroached)
(pos))
(while (and edit (< point (phpinspect-edit-original-end edit))) (while (and edit (< point (phpinspect-edit-original-end edit)))
(setq edit (cdr edit))) (setq edit (cdr edit)))
(+ point (phpinspect-edit-delta edit)))) (setq pos (+ point (phpinspect-edit-delta edit)))
(if (< 0 (setq encroached (- (+ (phpinspect-edit-original-end edit) (or (cdar edit) 0)) point)))
(- pos encroached)
pos)))
(defsubst phpinspect-edtrack-register-edit (track start end pre-change-length) (defsubst phpinspect-edtrack-register-edit (track start end pre-change-length)
(let ((edit-before (phpinspect-edtrack-edits track))) (phpinspect--log
(while (and edit-before (< end (phpinspect-edit-end edit-before))) "Edtrack registered change: [start: %d, end: %d, pre-change-length: %d]"
(setq edit-before (cdr edit-before))) start end pre-change-length)
(phpinspect-edtrack-register-taint (phpinspect-edtrack-register-taint
track track
(phpinspect-edtrack-original-position-at-point track start) (phpinspect-edtrack-original-position-at-point track start)
(phpinspect-edtrack-original-position-at-point track end)) (phpinspect-edtrack-original-position-at-point track (+ start pre-change-length)))
(let ((edit-before (phpinspect-edtrack-edits track)))
(while (and edit-before (< end (phpinspect-edit-end edit-before)))
(setq edit-before (cdr edit-before)))
(let* ((new-edit (cons (let* ((new-edit (cons
;; The end location of the edited region, before being ;; The end location of the edited region, before being

@ -131,14 +131,12 @@ that there are import (\"use\") statements for them."
(phpinspect-index-get-class (phpinspect-index-get-class
index class-name))) index class-name)))
(let ((namespace (let ((namespace
(seq-find #'phpinspect-namespace-p (seq-find (lambda (meta) (phpinspect-namespace-p (phpinspect-meta-token meta)))
(phpinspect-buffer-tokens-enclosing-point (phpinspect-buffer-tokens-enclosing-point
phpinspect-current-buffer (phpinspect-meta-start meta))))) phpinspect-current-buffer (phpinspect-region-start region)))))
;; Add use statements for types that aren't imported.
;; Add use statements for types that aren't imported.
(unless (or (or (alist-get type class-imports) (unless (or (or (alist-get type class-imports)
(alist-get type imports)) (alist-get type imports))
(gethash (phpinspect-intern-name (gethash (phpinspect-intern-name
@ -149,7 +147,7 @@ that there are import (\"use\") statements for them."
(phpinspect-autoloader-types (phpinspect-autoloader-types
(phpinspect-project-autoload project)))) (phpinspect-project-autoload project))))
(phpinspect-add-use-interactive (phpinspect-add-use-interactive
type phpinspect-current-buffer project namespace) type phpinspect-current-buffer project (phpinspect-meta-token namespace))
;; Buffer has been modified by adding type, update tree + ;; Buffer has been modified by adding type, update tree +
;; location map. This is not optimal but will have to do until ;; location map. This is not optimal but will have to do until
;; partial parsing is implemented. ;; partial parsing is implemented.

@ -463,7 +463,7 @@ Return value is a list of the types that are \"newed\"."
(cl-defmethod phpinspect-index-get-class (cl-defmethod phpinspect-index-get-class
((index (head phpinspect--root-index) (class-name phpinspect--type))) ((index (head phpinspect--root-index)) (class-name phpinspect--type))
(alist-get class-name (alist-get 'classes index) (alist-get class-name (alist-get 'classes index)
nil nil #'phpinspect--type=)) nil nil #'phpinspect--type=))

@ -385,7 +385,10 @@ during runtime. Parsers are implemented with macros, so changing
handler functions without calling this function will often not handler functions without calling this function will often not
have any effect." have any effect."
(interactive) (interactive)
(obarray-map #'fmakunbound phpinspect-parser-obarray)) (obarray-map (lambda (parser-symbol)
(fmakunbound parser-symbol)
(setf (phpinspect-parser-incremental-func (symbol-value parser-symbol)) nil))
phpinspect-parser-obarray))
(defmacro phpinspect-pctx-save-whitespace (pctx &rest body) (defmacro phpinspect-pctx-save-whitespace (pctx &rest body)
(declare (indent 1)) (declare (indent 1))
@ -492,83 +495,6 @@ parsing. Usually used in combination with
(setf (phpinspect-pctx-whitespace-before pctx) "") (setf (phpinspect-pctx-whitespace-before pctx) "")
whitespace)) whitespace))
(defun phpinspect-make-bmap-parser-function (tree-type handler-list &optional delimiter-predicate)
"Like `phpinspect-make-parser-function', but returned function is able to reuse an already parsed tree."
(let ((handlers (mapcar
(lambda (handler-name)
(let* ((handler-name (symbol-name handler-name))
(handler (intern-soft handler-name phpinspect-handler-obarray)))
(if handler
handler
(error "No handler found by name \"%s\"" handler-name))))
handler-list))
(delimiter-predicate (if (symbolp delimiter-predicate)
`(quote ,delimiter-predicate)
delimiter-predicate)))
`(lambda (context buffer max-point &optional continue-condition root)
(with-current-buffer buffer
(let* ((tokens)
(root-start (point))
(bmap (phpinspect-pctx-bmap context))
(previous-bmap (phpinspect-pctx-previous-bmap context))
(edtrack (phpinspect-pctx-edtrack context))
(taint-iterator (when edtrack (phpinspect-edtrack-make-taint-iterator edtrack)))
(delimiter-predicate (when (functionp ,delimiter-predicate) ,delimiter-predicate)))
(phpinspect-pctx-save-whitespace context
(while (and (< (point) max-point)
(if continue-condition (funcall continue-condition) t)
(not (if delimiter-predicate
(funcall delimiter-predicate (car (last tokens)))
nil)))
(cond ,@(mapcar
(lambda (handler)
`((looking-at ,(plist-get (symbol-value handler) 'regexp))
(let* ((match (match-string 0))
(start-position (point))
(original-position
(when (and previous-bmap edtrack)
(phpinspect-edtrack-original-position-at-point edtrack start-position)))
(existing-meta)
(current-end-position)
(token))
(when (and previous-bmap edtrack)
(setq existing-meta (phpinspect-bmap-token-starting-at previous-bmap original-position))
(when existing-meta
(setq current-end-position (phpinspect-edtrack-current-position-at-point
edtrack (phpinspect-meta-end existing-meta)))))
(if (and existing-meta
(not (or (phpinspect-root-p (phpinspect-meta-token existing-meta))
(phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta))))
(progn
(setq token (phpinspect-meta-token existing-meta))
;; Re-register existing token
(let ((delta (- start-position original-position)))
(phpinspect-bmap-overlay
bmap previous-bmap existing-meta delta
(phpinspect-pctx-consume-whitespace context)))
(goto-char current-end-position))
(progn
(setq token (funcall ,(symbol-function handler) match max-point))
(when token
(phpinspect-pctx-register-token context token start-position (point)))))
(when token
(if (null tokens)
(setq tokens (list token))
(progn
(nconc tokens (list token))))))))
handlers)
(t (forward-char)))))
(push ,tree-type tokens)
(when root
(phpinspect-pctx-register-token context tokens root-start (point)))
;; Return
tokens)))))
(defun phpinspect-make-incremental-parser-function (tree-type handler-list &optional delimiter-predicate) (defun phpinspect-make-incremental-parser-function (tree-type handler-list &optional delimiter-predicate)
"Like `phpinspect-make-parser-function', but returned function is able to reuse an already parsed tree." "Like `phpinspect-make-parser-function', but returned function is able to reuse an already parsed tree."
(let ((handlers (mapcar (let ((handlers (mapcar
@ -596,6 +522,7 @@ parsing. Usually used in combination with
(original-position) (original-position)
(current-end-position) (current-end-position)
(existing-meta) (existing-meta)
(delta)
(token) (token)
(delimiter-predicate (when (functionp ,delimiter-predicate) ,delimiter-predicate))) (delimiter-predicate (when (functionp ,delimiter-predicate) ,delimiter-predicate)))
(phpinspect-pctx-save-whitespace context (phpinspect-pctx-save-whitespace context
@ -605,7 +532,6 @@ parsing. Usually used in combination with
(funcall delimiter-predicate (car (last tokens))) (funcall delimiter-predicate (car (last tokens)))
nil))) nil)))
(setq start-position (point)) (setq start-position (point))
(cond ((and previous-bmap edtrack (cond ((and previous-bmap edtrack
(setq existing-meta (setq existing-meta
(phpinspect-bmap-token-starting-at (phpinspect-bmap-token-starting-at
@ -614,32 +540,29 @@ parsing. Usually used in combination with
(phpinspect-edtrack-original-position-at-point edtrack start-position)))) (phpinspect-edtrack-original-position-at-point edtrack start-position))))
(not (or (phpinspect-root-p (phpinspect-meta-token existing-meta)) (not (or (phpinspect-root-p (phpinspect-meta-token existing-meta))
(phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta)))) (phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta))))
(setq current-end-position (phpinspect-edtrack-current-position-at-point (setq delta (- start-position original-position)
edtrack (phpinspect-meta-end existing-meta))) current-end-position (+ (phpinspect-meta-end existing-meta) delta)
token (phpinspect-meta-token existing-meta))
(setq token (phpinspect-meta-token existing-meta))
;;(message "reusing token %s" token)
;; Re-register existing token ;; Re-register existing token
(phpinspect-bmap-overlay (phpinspect-bmap-overlay
bmap previous-bmap existing-meta (- start-position original-position) bmap previous-bmap existing-meta delta
(phpinspect-pctx-consume-whitespace context)) (phpinspect-pctx-consume-whitespace context))
;; Check if we can fast-forward to more siblings ;; Check if we can fast-forward to more siblings
(when (phpinspect-meta-right-siblings existing-meta) ;; (when (phpinspect-meta-right-siblings existing-meta)
(dolist (sibling (phpinspect-meta-right-siblings existing-meta)) ;; (dolist (sibling (phpinspect-meta-right-siblings existing-meta))
(setq existing-meta (phpinspect-bmap-token-meta previous-bmap sibling)) ;; (setq existing-meta (phpinspect-bmap-token-meta previous-bmap sibling))
(unless (phpinspect-taint-iterator-region-is-tainted-p ;; (unless (phpinspect-taint-iterator-region-is-tainted-p
taint-iterator current-end-position (phpinspect-meta-end existing-meta)) ;; taint-iterator current-end-position (phpinspect-meta-end existing-meta))
(nconc tokens (list token)) ;; (nconc tokens (list token))
(setq token (phpinspect-meta-token existing-meta)) ;; (setq token (phpinspect-meta-token existing-meta))
(phpinspect-bmap-overlay ;; (phpinspect-bmap-overlay
bmap previous-bmap existing-meta (- start-position original-position) ;; bmap previous-bmap existing-meta (- start-position original-position)
(phpinspect-pctx-consume-whitespace context)) ;; (phpinspect-meta-whitespace-before existing-meta))
(setq current-end-position (phpinspect-edtrack-current-position-at-point ;; (setq current-end-position (phpinspect-edtrack-current-position-at-point
edtrack (phpinspect-meta-end existing-meta)))))) ;; edtrack (phpinspect-meta-end existing-meta))))))
;;(message "Current pos: %d, end pos: %d" (point) current-end-position) ;;(message "Current pos: %d, end pos: %d" (point) current-end-position)
(goto-char current-end-position) (goto-char current-end-position)
@ -825,13 +748,13 @@ executing.")
(doc-block (save-restriction (doc-block (save-restriction
(goto-char region-start) (goto-char region-start)
(narrow-to-region region-start region-end) (narrow-to-region region-start region-end)
(funcall parser (current-buffer) (point-max) nil 'root)))) (funcall parser (current-buffer) (point-max) nil))))
(forward-char 2) (forward-char 2)
doc-block)) doc-block))
(t (t
(let ((parser (phpinspect-get-parser-func 'comment)) (let ((parser (phpinspect-get-parser-func 'comment))
(end-position (line-end-position))) (end-position (line-end-position)))
(funcall parser (current-buffer) end-position nil 'root))))) (funcall parser (current-buffer) end-position nil)))))
(phpinspect-defhandler variable (start-token &rest _ignored) (phpinspect-defhandler variable (start-token &rest _ignored)
"Handler for tokens indicating reference to a variable" "Handler for tokens indicating reference to a variable"
@ -875,7 +798,7 @@ executing.")
(forward-char (length start-token)) (forward-char (length start-token))
(let ((parser (phpinspect-get-parser-func 'use))) (let ((parser (phpinspect-get-parser-func 'use)))
(funcall parser (current-buffer) max-point nil 'root))) (funcall parser (current-buffer) max-point nil)))
(phpinspect-defhandler attribute-reference (start-token &rest _ignored) (phpinspect-defhandler attribute-reference (start-token &rest _ignored)
"Handler for references to object attributes, or static class attributes." "Handler for references to object attributes, or static class attributes."
@ -921,7 +844,7 @@ executing.")
(setq start-token (phpinspect--strip-word-end-space start-token)) (setq start-token (phpinspect--strip-word-end-space start-token))
(forward-char (length start-token)) (forward-char (length start-token))
(let* ((parser (phpinspect-get-parser-func 'const)) (let* ((parser (phpinspect-get-parser-func 'const))
(token (funcall parser (current-buffer) max-point nil 'root))) (token (funcall parser (current-buffer) max-point nil)))
(when (phpinspect-incomplete-token-p (car (last token))) (when (phpinspect-incomplete-token-p (car (last token)))
(setcar token :incomplete-const)) (setcar token :incomplete-const))
token)) token))
@ -969,7 +892,7 @@ static keywords with the same meaning as in a class block."
(continue-condition (lambda () (continue-condition (lambda ()
(not (and (char-equal (char-after) ?}) (not (and (char-equal (char-after) ?})
(setq complete-block t))))) (setq complete-block t)))))
(parsed (funcall parser (current-buffer) max-point continue-condition 'root))) (parsed (funcall parser (current-buffer) max-point continue-condition)))
(if complete-block (if complete-block
(forward-char) (forward-char)
(setcar parsed :incomplete-block)) (setcar parsed :incomplete-block))

@ -29,6 +29,14 @@
(should (eq 'token (phpinspect-meta-token (should (eq 'token (phpinspect-meta-token
(phpinspect-bmap-token-starting-at bmap2 7)))) (phpinspect-bmap-token-starting-at bmap2 7))))
;; Nesting for token-starting-at
(should (eq 'token3 (phpinspect-meta-token
(phpinspect-bmap-token-starting-at bmap 50))))
(should (eq 'token3 (phpinspect-meta-token
(phpinspect-bmap-token-starting-at bmap2 55))))
(should (phpinspect-bmap-token-meta bmap 'token)) (should (phpinspect-bmap-token-meta bmap 'token))
(should (phpinspect-bmap-token-meta bmap2 'token2)) (should (phpinspect-bmap-token-meta bmap2 'token2))
(should (phpinspect-bmap-token-meta bmap2 'token)) (should (phpinspect-bmap-token-meta bmap2 'token))

@ -124,7 +124,8 @@
(let ((function (phpinspect-meta-token (phpinspect-meta-parent (phpinspect-meta-parent bello1))))) (let ((function (phpinspect-meta-token (phpinspect-meta-parent (phpinspect-meta-parent bello1)))))
(should (= 2 (length function))) (should (= 2 (length function)))
(should (phpinspect-declaration-p (cadr function))) (should (phpinspect-declaration-p (cadr function)))
(should (member '(:word "Bello") (cadr function)))) (should (member '(:word "Bello") (cadr function)))
(should (member '(:word "echo") (cadr function))))
(phpinspect-document-apply-edit document 24 25 1 "{") (phpinspect-document-apply-edit document 24 25 1 "{")
(should (string= "<?php function Bello() { echo 'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }" (should (string= "<?php function Bello() { echo 'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }"
@ -134,3 +135,74 @@
(should parsed) (should parsed)
(setq bello2 (car (phpinspect-buffer-tokens-enclosing-point buffer 18))) (setq bello2 (car (phpinspect-buffer-tokens-enclosing-point buffer 18)))
(should (eq (phpinspect-meta-token bello) (phpinspect-meta-token bello2)))))) (should (eq (phpinspect-meta-token bello) (phpinspect-meta-token bello2))))))
(ert-deftest phpinspect-buffer-parse-incrementally-position-change ()
(with-temp-buffer
(let ((buffer (phpinspect-make-buffer :buffer (current-buffer))))
(insert "<?php
declare(strict_types=1);
namespace App\\Controller\\Api\\V1;
class AccountStatisticsController {
function __construct(){}
}")
(setq-local phpinspect-test-buffer t)
(add-to-list 'after-change-functions
(lambda (start end pre-change-length)
(when (boundp 'phpinspect-test-buffer)
(phpinspect-buffer-register-edit buffer start end pre-change-length))))
(let* ((bmap (phpinspect-buffer-parse-map buffer))
(class-location 67)
(class (phpinspect-bmap-token-starting-at bmap class-location))
(should class)
(should (phpinspect-class-p (phpinspect-meta-token class)))
(should (= class-location (phpinspect-meta-start class))))
(goto-char 65)
(let ((edit-string "use Symfony\\Component\\HttpFoundation\\JsonResponse;\n")
bmap class tokens-enclosing use-statement)
(insert edit-string)
(setq bmap (phpinspect-buffer-parse-map buffer)
class (phpinspect-bmap-token-starting-at bmap (+ 67 (length edit-string))))
(setq class-location (+ class-location (length edit-string)))
(should class)
(should (phpinspect-class-p (phpinspect-meta-token class)))
(should (= class-location (phpinspect-meta-start class)))
(setq tokens-enclosing (phpinspect-bmap-tokens-overlapping bmap class-location))
(setq class (seq-find (lambda (meta) (phpinspect-class-p (phpinspect-meta-token meta)))
tokens-enclosing))
(should class)
(should (= class-location (phpinspect-meta-start class)))
(should (phpinspect-class-p (phpinspect-meta-token class)))
(setq use-statement (phpinspect-bmap-token-starting-at bmap 65))
(should use-statement)
(should (phpinspect-use-p (phpinspect-meta-token use-statement)))
(should (seq-find #'phpinspect-use-p (seq-find #'phpinspect-namespace-p (phpinspect-buffer-tree buffer))))
(let ((second-use))
(goto-char 65)
(setq edit-string "use Another\\Use\\Statement;\n")
(insert edit-string)
(setq class-location (+ class-location (length edit-string)))
(setq bmap (phpinspect-buffer-parse-map buffer)
class (phpinspect-bmap-token-starting-at bmap class-location))
(should class)
(setq second-use (phpinspect-bmap-token-starting-at bmap 65))
(should second-use)
(setq class (phpinspect-bmap-token-starting-at bmap class-location))
(should class)
(should (= class-location (phpinspect-meta-start class)))
(should (phpinspect-class-p (phpinspect-meta-token class)))))))))

@ -12,13 +12,15 @@
(edit2 (phpinspect-edtrack-register-edit edtrack 15 22 7))) (edit2 (phpinspect-edtrack-register-edit edtrack 15 22 7)))
(should (equal `((255 . -50) (27 . 0) (15 . -5)) (phpinspect-edtrack-edits edtrack))))) (should (equal `((255 . -50) (27 . 0) (15 . -5)) (phpinspect-edtrack-edits edtrack)))))
;; (pp (phpinspect-edtrack-edits edtrack))
;; (should (= 10 (phpinspect-edit-end edit1)))
;; (should (= 22 (phpinspect-edit-end edit2)))
;; (should (= 30 (phpinspect-edtrack-original-position-at-point edtrack 25))) (ert-deftest phpinspect-edtrack-orginal-position-at-point ()
;; (should (= 4 (phpinspect-edtrack-original-position-at-point edtrack 4))) (let ((track (phpinspect-make-edtrack)))
;; (should (= 260 (phpinspect-edtrack-original-position-at-point edtrack 205))))) (phpinspect-edtrack-register-edit track 10 20 0)
(should (= 10 (phpinspect-edtrack-original-position-at-point track 20)))
(should (= 10 (phpinspect-edtrack-original-position-at-point track 15)))
(phpinspect-edtrack-register-edit track 30 40 5)
(should (= 35 (phpinspect-edtrack-original-position-at-point track 50)))
(should (= 25 (phpinspect-edtrack-original-position-at-point track 39)))))
(ert-deftest phpinsepct-edtrack-register-multi-edits () (ert-deftest phpinsepct-edtrack-register-multi-edits ()
(let ((track (phpinspect-make-edtrack))) (let ((track (phpinspect-make-edtrack)))

Loading…
Cancel
Save