Add testcase for derived statement starting with function + fix bug

- Derived statements starting with functions were not resolved to a type. This
was caused by bugs in `phpinspect--get-derived-statement-type-in-block' and
`phpinspect--interpret-expression-type-in-block'. The division in each
function's functionalities was blurry and both duplicated some of each other's
logic. The situation has now been cleaned up a bit.
- An extra testcase was added for derived statements starting with a function
call
- a bunch of code that broke because of the changes was fixed
master
Hugo Thunnissen 4 weeks ago
parent 81194568e3
commit e020bb4e05

@ -81,7 +81,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'")
(let ((attrib (car (last (phpinspect--resolvecontext-subject rctx)))) (let ((attrib (car (last (phpinspect--resolvecontext-subject rctx))))
type-before) type-before)
(setf (phpinspect--resolvecontext-subject rctx) (butlast (phpinspect--resolvecontext-subject rctx))) (setf (phpinspect--resolvecontext-subject rctx) (butlast (phpinspect--resolvecontext-subject rctx)))
(setq type-before (phpinspect-resolve-type-from-context rctx)) (setq type-before (phpinspect-resolve-type-from-context rctx nil t))
(when type-before (when type-before
(let ((class (phpinspect-project-get-class-extra-or-create (let ((class (phpinspect-project-get-class-extra-or-create
@ -187,7 +187,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'")
(mapcar #'phpinspect-meta-token (butlast statement 2))) (mapcar #'phpinspect-meta-token (butlast statement 2)))
(when-let* ((type-of-previous-statement (when-let* ((type-of-previous-statement
(phpinspect-resolve-type-from-context rctx)) (phpinspect-resolve-type-from-context rctx nil t))
(method-name (cadadr (phpinspect-meta-token (car match-result)))) (method-name (cadadr (phpinspect-meta-token (car match-result))))
(class (phpinspect-rctx-get-or-create-cached-project-class (class (phpinspect-rctx-get-or-create-cached-project-class
rctx type-of-previous-statement 'no-enqueue)) rctx type-of-previous-statement 'no-enqueue))

@ -215,6 +215,10 @@ $variable = $variable->method();"
(or assignments (phpinspect--find-assignment-ctxs-in-token php-block)) (or assignments (phpinspect--find-assignment-ctxs-in-token php-block))
type-resolver function-arg-list)) type-resolver function-arg-list))
(defun phpinspect-no-derivation-p (token)
(not (or (phpinspect-attrib-p token)
(phpinspect-array-p token))))
(defun phpinspect--get-derived-statement-type-in-block (defun phpinspect--get-derived-statement-type-in-block
(resolvecontext statement php-block assignments type-resolver &optional function-arg-list) (resolvecontext statement php-block assignments type-resolver &optional function-arg-list)
"Determine the type that STATEMENT evaluates to in RESOLVECONTEXT. "Determine the type that STATEMENT evaluates to in RESOLVECONTEXT.
@ -228,92 +232,67 @@ a derived statement. In the statement $foo->bar which is parsed
into ((:variable foo) (:object-attrib (:word bar))), the into ((:variable foo) (:object-attrib (:word bar))), the
value/type of ->bar must be derived from the type of $foo. So value/type of ->bar must be derived from the type of $foo. So
->bar derives from the base token $foo." ->bar derives from the base token $foo."
(phpinspect--log "Get derived statement type in block: (truncated, real length: %d) %s"
;; A derived statement can be an assignment itself. (length statement)
(when (seq-find #'phpinspect-assignment-p statement) (take 10 statement))
(phpinspect--log "Derived statement is an assignment: %s" statement)
(setq statement (cdr (seq-drop-while #'phpinspect-not-assignment-p statement)))) (when-let ((start-expression (seq-take-while #'phpinspect-no-derivation-p statement))
(phpinspect--log "Get derived statement type in block: (truncated, real length: %d) %s" (statement (nthcdr (length start-expression) statement))
(length statement) (type-before
(take 10 statement)) (if (phpinspect--match-sequence start-expression :f #'phpinspect-word-p)
(let* ((first-token (pop statement)) (progn
(current-token) (funcall type-resolver (phpinspect--make-type :name (cadar start-expression))))
(previous-attribute-type)) (phpinspect--interpret-expression-type-in-context
;; No first token means we were passed an empty list. resolvecontext php-block type-resolver start-expression
(when (and first-token function-arg-list assignments))))
(setq previous-attribute-type
(or (phpinspect--log "Rest of statement: %s" statement)
;; Statements starting with a bare word can indicate a static (phpinspect--log "Starting attribute type: %s" type-before)
;; method call. These could be statements with "return" or (while-let ((current-token (pop statement)))
;; another bare-word at the start though, so we drop preceding (phpinspect--log "Current derived statement token: %s" current-token)
;; barewords when they are present. (cond ((phpinspect-object-attrib-p current-token)
(when (phpinspect-word-p first-token) (let ((attribute-word (cadr current-token)))
(when (phpinspect-word-p (car statement)) (when (phpinspect-word-p attribute-word)
(setq statement (phpinspect-drop-preceding-barewords (if (phpinspect-list-p (car statement))
statement)) (progn
(setq first-token (pop statement))) (pop statement)
(funcall type-resolver (phpinspect--make-type (setq type-before
:name (cadr first-token)))) (or
(phpinspect-get-cached-project-class-method-type
;; First token is a list, for example "(new DateTime())" resolvecontext
(when (phpinspect-list-p first-token) (funcall type-resolver type-before)
(phpinspect--interpret-expression-type-in-context (cadr attribute-word))
resolvecontext php-block type-resolver (cdr first-token) type-before)))
function-arg-list assignments)) (setq type-before
(or
;; No bare word, assume we're dealing with a variable. (phpinspect-get-cached-project-class-variable-type
(when (phpinspect-variable-p first-token) resolvecontext
(phpinspect--get-variable-type-in-block (funcall type-resolver type-before)
resolvecontext (cadr first-token) php-block assignments (cadr attribute-word))
type-resolver function-arg-list))))) type-before))))))
((phpinspect-static-attrib-p current-token)
(phpinspect--log "Statement: %s" statement) (let ((attribute-word (cadr current-token)))
(phpinspect--log "Starting attribute type: %s" previous-attribute-type) (phpinspect--log "Found attribute word: %s" attribute-word)
(while (setq current-token (pop statement)) (phpinspect--log "checking if next token is a list. Token: %s"
(phpinspect--log "Current derived statement token: %s" current-token) (car statement))
(cond ((phpinspect-object-attrib-p current-token) (when (phpinspect-word-p attribute-word)
(let ((attribute-word (cadr current-token))) (if (phpinspect-list-p (car statement))
(when (phpinspect-word-p attribute-word) (progn
(if (phpinspect-list-p (car statement)) (pop statement)
(progn (setq type-before
(pop statement)
(setq previous-attribute-type
(or
(phpinspect-get-cached-project-class-method-type
resolvecontext
(funcall type-resolver previous-attribute-type)
(cadr attribute-word))
previous-attribute-type)))
(setq previous-attribute-type
(or (or
(phpinspect-get-cached-project-class-variable-type (phpinspect-get-cached-project-class-static-method-type
resolvecontext resolvecontext
(funcall type-resolver previous-attribute-type) (funcall type-resolver type-before)
(cadr attribute-word)) (cadr attribute-word))
previous-attribute-type)))))) type-before)))))))
((phpinspect-static-attrib-p current-token) ((and type-before (phpinspect-array-p current-token))
(let ((attribute-word (cadr current-token))) (setq type-before
(phpinspect--log "Found attribute word: %s" attribute-word) (or (phpinspect--type-contains type-before)
(phpinspect--log "checking if next token is a list. Token: %s" type-before)))))
(car statement)) (phpinspect--log "Found derived type: %s" type-before)
(when (phpinspect-word-p attribute-word) ;; Make sure to always return a FQN
(if (phpinspect-list-p (car statement)) (funcall type-resolver type-before)))
(progn
(pop statement)
(setq previous-attribute-type
(or
(phpinspect-get-cached-project-class-static-method-type
resolvecontext
(funcall type-resolver previous-attribute-type)
(cadr attribute-word))
previous-attribute-type)))))))
((and previous-attribute-type (phpinspect-array-p current-token))
(setq previous-attribute-type
(or (phpinspect--type-contains previous-attribute-type)
previous-attribute-type)))))
(phpinspect--log "Found derived type: %s" previous-attribute-type)
;; Make sure to always return a FQN
(funcall type-resolver previous-attribute-type))))
(defun phpinspect-get-variable-type-in-block (defun phpinspect-get-variable-type-in-block
(resolvecontext variable-name php-block type-resolver &optional function-arg-list) (resolvecontext variable-name php-block type-resolver &optional function-arg-list)
@ -335,7 +314,7 @@ resolve types of function argument variables."
(phpinspect--log "Looking for assignments of variable %s in php block" variable-name) (phpinspect--log "Looking for assignments of variable %s in php block" variable-name)
(if (string= variable-name "this") (if (string= variable-name "this")
(funcall type-resolver (phpinspect--make-type :name "self")) (funcall type-resolver (phpinspect--make-type :name "\\self" :fully-qualified t))
(phpinspect--get-pattern-type-in-block (phpinspect--get-pattern-type-in-block
resolvecontext (phpinspect--make-pattern :m `(:variable ,variable-name)) resolvecontext (phpinspect--make-pattern :m `(:variable ,variable-name))
php-block assignments type-resolver function-arg-list))) php-block assignments type-resolver function-arg-list)))
@ -461,6 +440,18 @@ ARG-LIST. ARG-LIST should be a list token as returned by
(phpinspect--make-type :name (car (last arg)))) (phpinspect--make-type :name (car (last arg))))
nil))))) nil)))))
(define-inline phpinspect-new-p (token)
(inline-letevals (token)
(inline-quote
(and (phpinspect-word-p ,token)
(string= "new" (cadr ,token))))))
(defun phpinspect-interpret-expression-type-in-context
(resolvecontext php-block type-resolver expression &optional function-arg-list assignments)
(phpinspect--interpret-expression-type-in-context
resolvecontext php-block type-resolver expression function-arg-list
(or assignments (phpinspect--find-assignment-ctxs-in-token php-block))))
(defun phpinspect--interpret-expression-type-in-context (defun phpinspect--interpret-expression-type-in-context
(resolvecontext php-block type-resolver expression &optional function-arg-list assignments) (resolvecontext php-block type-resolver expression &optional function-arg-list assignments)
"Infer EXPRESSION's type from provided context. "Infer EXPRESSION's type from provided context.
@ -475,6 +466,9 @@ value/type."
(length expression) (length expression)
(take 10 expression)) (take 10 expression))
(unless (phpinspect-new-p (car expression))
(setq expression (phpinspect-drop-preceding-barewords expression)))
(cond ((phpinspect-array-p (car expression)) (cond ((phpinspect-array-p (car expression))
(let ((collection-contains) (let ((collection-contains)
(collection-items (phpinspect--split-statements (cdr (car expression)))) (collection-items (phpinspect--split-statements (cdr (car expression))))
@ -496,7 +490,7 @@ value/type."
:collection t :collection t
:contains collection-contains))) :contains collection-contains)))
((phpinspect--match-sequence expression ((phpinspect--match-sequence expression
:m '(:word "new") :f #'phpinspect-new-p
:f #'phpinspect-word-p :f #'phpinspect-word-p
:f #'phpinspect-list-p) :f #'phpinspect-list-p)
(funcall (funcall
@ -507,6 +501,14 @@ value/type."
:f #'phpinspect-list-p) :f #'phpinspect-list-p)
(phpinspect-rctx-get-function-return-type resolvecontext (cadar expression))) (phpinspect-rctx-get-function-return-type resolvecontext (cadar expression)))
;; Expression is a (chain of) assignments. The right-most subexpression
;; is the type it evaluates to.
((seq-find #'phpinspect-assignment-p expression)
(phpinspect--interpret-expression-type-in-context
resolvecontext php-block type-resolver
(car (last (phpinspect--split-statements expression #'phpinspect-maybe-assignment-p)))
function-arg-list assignments))
((and (phpinspect-list-p (car expression)) ((and (phpinspect-list-p (car expression))
(= 1 (length (cdar expression))) (= 1 (length (cdar expression)))
(phpinspect-word-p (cadar expression))) (phpinspect-word-p (cadar expression)))
@ -527,13 +529,6 @@ value/type."
(phpinspect--interpret-expression-type-in-context (phpinspect--interpret-expression-type-in-context
resolvecontext php-block type-resolver (cdar expression) resolvecontext php-block type-resolver (cdar expression)
function-arg-list assignments)) function-arg-list assignments))
;; Expression is a (chain of) assignments. The right-most subexpression
;; is the type it evaluates to.
((seq-find #'phpinspect-assignment-p expression)
(phpinspect--interpret-expression-type-in-context
resolvecontext php-block type-resolver
(car (last (phpinspect--split-statements expression #'phpinspect-maybe-assignment-p)))
function-arg-list assignments))
;; If the right of an assignment is just $variable;, we can check if it is a ;; If the right of an assignment is just $variable;, we can check if it is a
;; function argument and otherwise recurse to find the type of that variable. ;; function argument and otherwise recurse to find the type of that variable.
@ -548,8 +543,14 @@ value/type."
resolvecontext (cadar expression) php-block assignments type-resolver function-arg-list))))) resolvecontext (cadar expression) php-block assignments type-resolver function-arg-list)))))
(defun phpinspect-resolve-type-from-context (resolvecontext &optional type-resolver) (defun phpinspect-resolve-type-from-context (resolvecontext &optional type-resolver assume-derived)
"Resolve the type that RESOLVECONTEXT's subject evaluates to." "Resolve the type that RESOLVECONTEXT's subject evaluates to.
When ASSUME-DERIVED is non-nil, it will be assumed that
RESOLVECONTEXT's subject precedes a token that passes
`phpinspect-attrib-p'. In this case, when the subject is a single
bare word, it is assumed to be a type name at the start of a
static method call and resolved to a fully qualified type. (`phpinspect--type-p')"
;; Subject should be a statement, not a single token. ;; Subject should be a statement, not a single token.
(when (phpinspect-probably-token-p (phpinspect--resolvecontext-subject resolvecontext)) (when (phpinspect-probably-token-p (phpinspect--resolvecontext-subject resolvecontext))
(setf (phpinspect--resolvecontext-subject resolvecontext) (setf (phpinspect--resolvecontext-subject resolvecontext)
@ -573,31 +574,29 @@ value/type."
;;(phpinspect--log "Trying to find type in %s" enclosing-token) ;;(phpinspect--log "Trying to find type in %s" enclosing-token)
(setq enclosing-token (pop enclosing-tokens)) (setq enclosing-token (pop enclosing-tokens))
(setq type (let ((subject (phpinspect--resolvecontext-subject resolvecontext)))
(cond ((phpinspect-namespace-p enclosing-token) (setq type
(phpinspect-get-derived-statement-type-in-block (cond ((and assume-derived
resolvecontext (phpinspect--match-sequence subject :f #'phpinspect-word-p))
(phpinspect--resolvecontext-subject (funcall type-resolver (phpinspect--make-type :name (cadar subject))))
resolvecontext)
(or (phpinspect-namespace-block enclosing-token) ((phpinspect-namespace-p enclosing-token)
enclosing-token) (phpinspect-interpret-expression-type-in-context
type-resolver)) resolvecontext
((or (phpinspect-block-p enclosing-token) (or (phpinspect-namespace-block enclosing-token) enclosing-token)
(phpinspect-root-p enclosing-token)) type-resolver subject))
(phpinspect-get-derived-statement-type-in-block
resolvecontext ((or (phpinspect-block-p enclosing-token)
(phpinspect--resolvecontext-subject (phpinspect-root-p enclosing-token))
resolvecontext) (phpinspect-interpret-expression-type-in-context
enclosing-token resolvecontext enclosing-token type-resolver subject))
type-resolver))
((phpinspect-function-p enclosing-token) ((phpinspect-function-p enclosing-token)
(phpinspect-get-derived-statement-type-in-block (phpinspect-interpret-expression-type-in-context
resolvecontext resolvecontext
(phpinspect--resolvecontext-subject (phpinspect-function-block enclosing-token)
resolvecontext) type-resolver subject
(phpinspect-function-block enclosing-token) (phpinspect-function-argument-list enclosing-token)))))))
type-resolver
(phpinspect-function-argument-list enclosing-token))))))
type)) type))
(defun phpinspect--function-get-pattern-type (fn rctx pattern type-resolver) (defun phpinspect--function-get-pattern-type (fn rctx pattern type-resolver)

@ -65,6 +65,7 @@
(complete . ,,(alist-get 'complete class)) (complete . ,,(alist-get 'complete class))
(class-name . ,,(phpinspect--serialize-type (alist-get 'class-name class))) (class-name . ,,(phpinspect--serialize-type (alist-get 'class-name class)))
(declaration . ,(quote ,(alist-get 'declaration class))) (declaration . ,(quote ,(alist-get 'declaration class)))
(location . ,(quote ,(alist-get 'location class)))
(imports . ,,(append '(list) (imports . ,,(append '(list)
(mapcar #'phpinspect--serialize-import (mapcar #'phpinspect--serialize-import
(alist-get 'imports class)))) (alist-get 'imports class))))
@ -88,7 +89,9 @@
(alist-get 'extends class)))) (alist-get 'extends class))))
(implements . ,,(append '(list) (implements . ,,(append '(list)
(mapcar #'phpinspect--serialize-type (mapcar #'phpinspect--serialize-type
(alist-get 'implements class)))))) (alist-get 'implements class))))
(used-types . ,(list ,@(mapcar #'phpinspect--serialize-name
(alist-get 'used-types class))))))
(cl-defmethod phpinspect--serialize-root-index ((index (head phpinspect--root-index))) (cl-defmethod phpinspect--serialize-root-index ((index (head phpinspect--root-index)))
``(phpinspect--root-index ``(phpinspect--root-index
@ -99,7 +102,9 @@
,@(mapcar (lambda (cons-class) ,@(mapcar (lambda (cons-class)
`(cons ,(phpinspect--serialize-type (car cons-class)) `(cons ,(phpinspect--serialize-type (car cons-class))
,(phpinspect--serialize-indexed-class (cdr cons-class)))) ,(phpinspect--serialize-indexed-class (cdr cons-class))))
(alist-get 'classes index)))) (alist-get 'classes index))))
(used-types . ,(list ,@(mapcar #'phpinspect--serialize-name
(alist-get 'used-types index))))
(functions . ,,(append '(list) (functions . ,,(append '(list)
(mapcar #'phpinspect--serialize-function (mapcar #'phpinspect--serialize-function
(alist-get 'functions index)))))) (alist-get 'functions index))))))
@ -110,5 +115,8 @@
(phpinspect-intern-name ,(phpinspect-name-string (car import))) (phpinspect-intern-name ,(phpinspect-name-string (car import)))
,(phpinspect--serialize-type (cdr import)))) ,(phpinspect--serialize-type (cdr import))))
(defun phpinspect--serialize-name (name)
`(phpinspect-intern-name ,(phpinspect-name-string name)))
(provide 'phpinspect-serialize) (provide 'phpinspect-serialize)
;;; phpinspect-serialize.el ends here ;;; phpinspect-serialize.el ends here

@ -149,6 +149,9 @@ Type can be any of the token types returned by
"Get the argument list of a function" "Get the argument list of a function"
(seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func nil) nil)) (seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func nil) nil))
(defun phpinspect-equals-p (token)
(phpinspect-token-type-p token :equals))
(defun phpinspect-annotation-p (token) (defun phpinspect-annotation-p (token)
(phpinspect-token-type-p token :annotation)) (phpinspect-token-type-p token :annotation))

@ -204,7 +204,13 @@ NAMESPACE may be nil, or a string with a namespace FQN."
(phpinspect--type-resolve (phpinspect--type-resolve
types types
namespace namespace
(if (and inside-class-name (phpinspect--type= type phpinspect--self-type)) (if (and inside-class-name
(or (phpinspect--type= type phpinspect--self-type)
;; Type has not yet been resolved, so we can compare bare
;; names to detect a "self" type.
(and (not (phpinspect--type-fully-qualified type))
(eq (phpinspect--type-bare-name-sym type)
(phpinspect--type-bare-name-sym phpinspect--self-type)))))
(progn (progn
(phpinspect--log "Returning inside class name for %s : %s" (phpinspect--log "Returning inside class name for %s : %s"
type inside-class-name) type inside-class-name)

@ -1 +1 @@
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:word "false") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "string")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";")))) (:doc-block (:return-annotation (:word "DateTime[]"))) (:public (:function (:declaration (:word "function") (:word "arrayReturn") (:list) (:word "array")) (:block (:word "return") (:array (:word "new") (:word "\\DateTime") (:list)) (:terminator ";")))))))) (:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:assignment "=") (:word "false") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "string")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";")))) (:doc-block (:return-annotation (:word "DateTime[]"))) (:public (:function (:declaration (:word "function") (:word "arrayReturn") (:list) (:word "array")) (:block (:word "return") (:array (:word "new") (:word "\\DateTime") (:list)) (:terminator ";"))))))))

@ -1 +1 @@
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:private (:class-variable "extra") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:word "false") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "anAddedFunction") (:list)) (:block (:word "return") (:variable "this") (:object-attrib (:word "extra")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";")))))))) (:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:private (:class-variable "extra") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:assignment "=") (:word "false") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "anAddedFunction") (:list)) (:block (:word "return") (:variable "this") (:object-attrib (:word "extra")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";"))))))))

@ -237,68 +237,6 @@ class FlufferUpper
(phpinspect--make-type-resolver-for-resolvecontext (phpinspect--make-type-resolver-for-resolvecontext
context)))))) context))))))
(ert-deftest phpinspect-resolve-type-from-context-static-method ()
(with-temp-buffer
(insert "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
self::doThing()->")
(let* ((bmap (phpinspect-make-bmap))
(tokens (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
:bmap bmap)
(phpinspect-parse-current-buffer)))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100)
(project (phpinspect--make-dummy-project))
(context (phpinspect-get-resolvecontext project bmap (point))))
(phpinspect-purge-cache)
(phpinspect-project-add-index project index)
(should (phpinspect--type= (phpinspect--make-type :name "\\Thing")
(phpinspect-resolve-type-from-context
context
(phpinspect--make-type-resolver-for-resolvecontext
context)))))))
(ert-deftest phpinspect-resolve-type-from-context-static-method-with-preceding-words ()
(with-temp-buffer
(insert "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
if (true) {
return self::doThing()->")
(let* ((bmap (phpinspect-make-bmap))
(tokens (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
:bmap bmap)
(phpinspect-parse-current-buffer)))
(index (phpinspect--index-tokens tokens))
(phpinspect-eldoc-word-width 100)
(project (phpinspect--make-dummy-project))
(context (phpinspect-get-resolvecontext project bmap (point))))
(phpinspect-purge-cache)
(phpinspect-project-add-index project index)
(should (phpinspect--type= (phpinspect--make-type :name "\\Thing")
(phpinspect-resolve-type-from-context
context
(phpinspect--make-type-resolver-for-resolvecontext
context)))))))
(ert-deftest phpinspect-get-last-statement-in-token-with-static-attribute-context () (ert-deftest phpinspect-get-last-statement-in-token-with-static-attribute-context ()
(let* ((php-code-function " (let* ((php-code-function "

@ -46,11 +46,11 @@
(:word "array") (:word "array")
(:variable "things"))) (:variable "things")))
(:block))))))) (:block)))))))
(index (phpinspect--index-tokens class-tokens)) (index (eval (phpinspect--serialize-root-index
(phpinspect--index-tokens class-tokens))))
(expected-index (expected-index
`(phpinspect--root-index `(phpinspect--root-index
(imports) (imports)
(namespaces)
(classes (classes
(,(phpinspect--make-type :name "\\Potato" :fully-qualified t) (,(phpinspect--make-type :name "\\Potato" :fully-qualified t)
phpinspect--indexed-class phpinspect--indexed-class
@ -227,11 +227,14 @@ try {
(phpinspect--make-type :name "\\void" :fully-qualified t) (phpinspect--make-type :name "\\void" :fully-qualified t)
(phpinspect--function-return-type method)))))))) (phpinspect--function-return-type method))))))))
(require 'phpinspect-serialize)
(ert-deftest phpinspect-index-tokens-class () (ert-deftest phpinspect-index-tokens-class ()
(let* ((index1 (let* ((index1
(phpinspect--index-tokens (eval
(phpinspect-test-read-fixture-data "IndexClass1"))) (phpinspect--serialize-root-index
(phpinspect--index-tokens
(phpinspect-test-read-fixture-data "IndexClass1")))))
(index2 (index2
(phpinspect-test-read-fixture-serialization "IndexClass1-indexed")) (phpinspect-test-read-fixture-serialization "IndexClass1-indexed"))
(index1-class (car (alist-get 'classes index1))) (index1-class (car (alist-get 'classes index1)))
@ -248,10 +251,13 @@ try {
(insert-file-contents (expand-file-name "IndexClass1.php" phpinspect-test-php-file-directory)) (insert-file-contents (expand-file-name "IndexClass1.php" phpinspect-test-php-file-directory))
(setf (phpinspect-pctx-bmap pctx) (phpinspect-make-bmap)) (setf (phpinspect-pctx-bmap pctx) (phpinspect-make-bmap))
(phpinspect-with-parse-context pctx (setq tree (phpinspect-parse-current-buffer)))) (phpinspect-with-parse-context pctx (setq tree (phpinspect-parse-current-buffer))))
(let* ((index1 (phpinspect--index-tokens tree (let* ((index1
nil (eval
(phpinspect-bmap-make-location-resolver (phpinspect--serialize-root-index
(phpinspect-pctx-bmap pctx)))) (phpinspect--index-tokens tree
nil
(phpinspect-bmap-make-location-resolver
(phpinspect-pctx-bmap pctx))))))
(index2 (index2
(phpinspect-test-read-fixture-serialization "IndexClass1-indexed")) (phpinspect-test-read-fixture-serialization "IndexClass1-indexed"))
(index1-class (car (alist-get 'classes index1))) (index1-class (car (alist-get 'classes index1)))

@ -169,10 +169,10 @@
result)))))) result))))))
(ert-deftest phpinspect-get-variable-type-in-block-function-return () (ert-deftest phpinspect-get-variable-type-in-block-function-return ()
(let ((base-code "$bar = foo();") (let ((base-code "$bar = foo()")
(paths (list (cons "$bar" "Foo") (paths (list (cons ";$bar" "Foo")
(cons "$bar->baz" "string"))) (cons ";$bar->baz" "string")
(cons "->baz" "string")))
(project (phpinspect--make-dummy-project))) (project (phpinspect--make-dummy-project)))
(phpinspect-project-add-index (phpinspect-project-add-index
@ -189,3 +189,66 @@
(should result) (should result)
(should (phpinspect--type= (phpinspect--make-type :name (concat "\\" (cdr path))) (should (phpinspect--type= (phpinspect--make-type :name (concat "\\" (cdr path)))
result)))))) result))))))
(ert-deftest phpinspect-resolve-type-from-context-static-method ()
(with-temp-buffer
(insert "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
self::doThing()->")
(let* ((bmap (phpinspect-make-bmap))
(tokens (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
:bmap bmap)
(phpinspect-parse-current-buffer)))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100)
(project (phpinspect--make-dummy-project))
(context (phpinspect-get-resolvecontext project bmap (point))))
(phpinspect-purge-cache)
(phpinspect-project-add-index project index)
(should (phpinspect--type= (phpinspect--make-type :name "\\Thing")
(phpinspect-resolve-type-from-context
context
(phpinspect--make-type-resolver-for-resolvecontext
context)))))))
(ert-deftest phpinspect-resolve-type-from-context-static-method-with-preceding-words ()
(with-temp-buffer
(insert "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
if (true) {
return self::doThing()->")
(let* ((bmap (phpinspect-make-bmap))
(tokens (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
:bmap bmap)
(phpinspect-parse-current-buffer)))
(index (phpinspect--index-tokens tokens))
(phpinspect-eldoc-word-width 100)
(project (phpinspect--make-dummy-project))
(context (phpinspect-get-resolvecontext project bmap (point))))
(phpinspect-purge-cache)
(phpinspect-project-add-index project index)
(should (phpinspect--type= (phpinspect--make-type :name "\\Thing")
(phpinspect-resolve-type-from-context
context
(phpinspect--make-type-resolver-for-resolvecontext
context)))))))

Loading…
Cancel
Save