From e020bb4e05d5e1ee45122755d51131261b83cf14 Mon Sep 17 00:00:00 2001 From: Hugo Thunnissen Date: Wed, 21 Aug 2024 21:36:38 +0200 Subject: [PATCH] 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 --- phpinspect-eldoc.el | 4 +- phpinspect-resolve.el | 239 ++++++++++++++++----------------- phpinspect-serialize.el | 12 +- phpinspect-token-predicates.el | 3 + phpinspect-type.el | 8 +- test/fixtures/IndexClass1.eld | 2 +- test/fixtures/IndexClass2.eld | 2 +- test/phpinspect-test.el | 62 --------- test/test-index.el | 22 +-- test/test-resolve.el | 71 +++++++++- 10 files changed, 224 insertions(+), 201 deletions(-) diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el index 1991654..a4ef57c 100644 --- a/phpinspect-eldoc.el +++ b/phpinspect-eldoc.el @@ -81,7 +81,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'") (let ((attrib (car (last (phpinspect--resolvecontext-subject rctx)))) type-before) (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 (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))) (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)))) (class (phpinspect-rctx-get-or-create-cached-project-class rctx type-of-previous-statement 'no-enqueue)) diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el index 148ed73..8b38222 100644 --- a/phpinspect-resolve.el +++ b/phpinspect-resolve.el @@ -215,6 +215,10 @@ $variable = $variable->method();" (or assignments (phpinspect--find-assignment-ctxs-in-token php-block)) 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 (resolvecontext statement php-block assignments type-resolver &optional function-arg-list) "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 value/type of ->bar must be derived from the type of $foo. So ->bar derives from the base token $foo." - - ;; A derived statement can be an assignment itself. - (when (seq-find #'phpinspect-assignment-p statement) - (phpinspect--log "Derived statement is an assignment: %s" statement) - (setq statement (cdr (seq-drop-while #'phpinspect-not-assignment-p statement)))) - (phpinspect--log "Get derived statement type in block: (truncated, real length: %d) %s" - (length statement) - (take 10 statement)) - (let* ((first-token (pop statement)) - (current-token) - (previous-attribute-type)) - ;; No first token means we were passed an empty list. - (when (and first-token - (setq previous-attribute-type - (or - ;; Statements starting with a bare word can indicate a static - ;; method call. These could be statements with "return" or - ;; another bare-word at the start though, so we drop preceding - ;; barewords when they are present. - (when (phpinspect-word-p first-token) - (when (phpinspect-word-p (car statement)) - (setq statement (phpinspect-drop-preceding-barewords - statement)) - (setq first-token (pop statement))) - (funcall type-resolver (phpinspect--make-type - :name (cadr first-token)))) - - ;; First token is a list, for example "(new DateTime())" - (when (phpinspect-list-p first-token) - (phpinspect--interpret-expression-type-in-context - resolvecontext php-block type-resolver (cdr first-token) - function-arg-list assignments)) - - ;; No bare word, assume we're dealing with a variable. - (when (phpinspect-variable-p first-token) - (phpinspect--get-variable-type-in-block - resolvecontext (cadr first-token) php-block assignments - type-resolver function-arg-list))))) - - (phpinspect--log "Statement: %s" statement) - (phpinspect--log "Starting attribute type: %s" previous-attribute-type) - (while (setq current-token (pop statement)) - (phpinspect--log "Current derived statement token: %s" current-token) - (cond ((phpinspect-object-attrib-p current-token) - (let ((attribute-word (cadr current-token))) - (when (phpinspect-word-p attribute-word) - (if (phpinspect-list-p (car statement)) - (progn - (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 + (phpinspect--log "Get derived statement type in block: (truncated, real length: %d) %s" + (length statement) + (take 10 statement)) + + (when-let ((start-expression (seq-take-while #'phpinspect-no-derivation-p statement)) + (statement (nthcdr (length start-expression) statement)) + (type-before + (if (phpinspect--match-sequence start-expression :f #'phpinspect-word-p) + (progn + (funcall type-resolver (phpinspect--make-type :name (cadar start-expression)))) + (phpinspect--interpret-expression-type-in-context + resolvecontext php-block type-resolver start-expression + function-arg-list assignments)))) + + (phpinspect--log "Rest of statement: %s" statement) + (phpinspect--log "Starting attribute type: %s" type-before) + (while-let ((current-token (pop statement))) + (phpinspect--log "Current derived statement token: %s" current-token) + (cond ((phpinspect-object-attrib-p current-token) + (let ((attribute-word (cadr current-token))) + (when (phpinspect-word-p attribute-word) + (if (phpinspect-list-p (car statement)) + (progn + (pop statement) + (setq type-before + (or + (phpinspect-get-cached-project-class-method-type + resolvecontext + (funcall type-resolver type-before) + (cadr attribute-word)) + type-before))) + (setq type-before + (or + (phpinspect-get-cached-project-class-variable-type + resolvecontext + (funcall type-resolver type-before) + (cadr attribute-word)) + type-before)))))) + ((phpinspect-static-attrib-p current-token) + (let ((attribute-word (cadr current-token))) + (phpinspect--log "Found attribute word: %s" attribute-word) + (phpinspect--log "checking if next token is a list. Token: %s" + (car statement)) + (when (phpinspect-word-p attribute-word) + (if (phpinspect-list-p (car statement)) + (progn + (pop statement) + (setq type-before (or - (phpinspect-get-cached-project-class-variable-type - resolvecontext - (funcall type-resolver previous-attribute-type) + (phpinspect-get-cached-project-class-static-method-type + resolvecontext + (funcall type-resolver type-before) (cadr attribute-word)) - previous-attribute-type)))))) - ((phpinspect-static-attrib-p current-token) - (let ((attribute-word (cadr current-token))) - (phpinspect--log "Found attribute word: %s" attribute-word) - (phpinspect--log "checking if next token is a list. Token: %s" - (car statement)) - (when (phpinspect-word-p attribute-word) - (if (phpinspect-list-p (car statement)) - (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)))) + type-before))))))) + ((and type-before (phpinspect-array-p current-token)) + (setq type-before + (or (phpinspect--type-contains type-before) + type-before))))) + (phpinspect--log "Found derived type: %s" type-before) + ;; Make sure to always return a FQN + (funcall type-resolver type-before))) (defun phpinspect-get-variable-type-in-block (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) (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 resolvecontext (phpinspect--make-pattern :m `(:variable ,variable-name)) 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)))) 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 (resolvecontext php-block type-resolver expression &optional function-arg-list assignments) "Infer EXPRESSION's type from provided context. @@ -475,6 +466,9 @@ value/type." (length expression) (take 10 expression)) + (unless (phpinspect-new-p (car expression)) + (setq expression (phpinspect-drop-preceding-barewords expression))) + (cond ((phpinspect-array-p (car expression)) (let ((collection-contains) (collection-items (phpinspect--split-statements (cdr (car expression)))) @@ -496,7 +490,7 @@ value/type." :collection t :contains collection-contains))) ((phpinspect--match-sequence expression - :m '(:word "new") + :f #'phpinspect-new-p :f #'phpinspect-word-p :f #'phpinspect-list-p) (funcall @@ -507,6 +501,14 @@ value/type." :f #'phpinspect-list-p) (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)) (= 1 (length (cdar expression))) (phpinspect-word-p (cadar expression))) @@ -527,13 +529,6 @@ value/type." (phpinspect--interpret-expression-type-in-context resolvecontext php-block type-resolver (cdar expression) 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 ;; 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))))) -(defun phpinspect-resolve-type-from-context (resolvecontext &optional type-resolver) - "Resolve the type that RESOLVECONTEXT's subject evaluates to." +(defun phpinspect-resolve-type-from-context (resolvecontext &optional type-resolver assume-derived) + "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. (when (phpinspect-probably-token-p (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) (setq enclosing-token (pop enclosing-tokens)) - (setq type - (cond ((phpinspect-namespace-p enclosing-token) - (phpinspect-get-derived-statement-type-in-block - resolvecontext - (phpinspect--resolvecontext-subject - resolvecontext) - (or (phpinspect-namespace-block enclosing-token) - enclosing-token) - type-resolver)) - ((or (phpinspect-block-p enclosing-token) - (phpinspect-root-p enclosing-token)) - (phpinspect-get-derived-statement-type-in-block - resolvecontext - (phpinspect--resolvecontext-subject - resolvecontext) - enclosing-token - type-resolver)) - ((phpinspect-function-p enclosing-token) - (phpinspect-get-derived-statement-type-in-block - resolvecontext - (phpinspect--resolvecontext-subject - resolvecontext) - (phpinspect-function-block enclosing-token) - type-resolver - (phpinspect-function-argument-list enclosing-token)))))) + (let ((subject (phpinspect--resolvecontext-subject resolvecontext))) + (setq type + (cond ((and assume-derived + (phpinspect--match-sequence subject :f #'phpinspect-word-p)) + (funcall type-resolver (phpinspect--make-type :name (cadar subject)))) + + ((phpinspect-namespace-p enclosing-token) + (phpinspect-interpret-expression-type-in-context + resolvecontext + (or (phpinspect-namespace-block enclosing-token) enclosing-token) + type-resolver subject)) + + ((or (phpinspect-block-p enclosing-token) + (phpinspect-root-p enclosing-token)) + (phpinspect-interpret-expression-type-in-context + resolvecontext enclosing-token type-resolver subject)) + + ((phpinspect-function-p enclosing-token) + (phpinspect-interpret-expression-type-in-context + resolvecontext + (phpinspect-function-block enclosing-token) + type-resolver subject + (phpinspect-function-argument-list enclosing-token))))))) type)) (defun phpinspect--function-get-pattern-type (fn rctx pattern type-resolver) diff --git a/phpinspect-serialize.el b/phpinspect-serialize.el index 545705d..efc2d8d 100644 --- a/phpinspect-serialize.el +++ b/phpinspect-serialize.el @@ -65,6 +65,7 @@ (complete . ,,(alist-get 'complete class)) (class-name . ,,(phpinspect--serialize-type (alist-get 'class-name class))) (declaration . ,(quote ,(alist-get 'declaration class))) + (location . ,(quote ,(alist-get 'location class))) (imports . ,,(append '(list) (mapcar #'phpinspect--serialize-import (alist-get 'imports class)))) @@ -88,7 +89,9 @@ (alist-get 'extends class)))) (implements . ,,(append '(list) (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))) ``(phpinspect--root-index @@ -99,7 +102,9 @@ ,@(mapcar (lambda (cons-class) `(cons ,(phpinspect--serialize-type (car 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) (mapcar #'phpinspect--serialize-function (alist-get 'functions index)))))) @@ -110,5 +115,8 @@ (phpinspect-intern-name ,(phpinspect-name-string (car import))) ,(phpinspect--serialize-type (cdr import)))) +(defun phpinspect--serialize-name (name) + `(phpinspect-intern-name ,(phpinspect-name-string name))) + (provide 'phpinspect-serialize) ;;; phpinspect-serialize.el ends here diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el index dbb5ecf..1e47cea 100644 --- a/phpinspect-token-predicates.el +++ b/phpinspect-token-predicates.el @@ -149,6 +149,9 @@ Type can be any of the token types returned by "Get the argument list of a function" (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) (phpinspect-token-type-p token :annotation)) diff --git a/phpinspect-type.el b/phpinspect-type.el index e829880..edc74fc 100644 --- a/phpinspect-type.el +++ b/phpinspect-type.el @@ -204,7 +204,13 @@ NAMESPACE may be nil, or a string with a namespace FQN." (phpinspect--type-resolve types 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 (phpinspect--log "Returning inside class name for %s : %s" type inside-class-name) diff --git a/test/fixtures/IndexClass1.eld b/test/fixtures/IndexClass1.eld index 88f6f3f..1c74afe 100644 --- a/test/fixtures/IndexClass1.eld +++ b/test/fixtures/IndexClass1.eld @@ -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 ";")))))))) diff --git a/test/fixtures/IndexClass2.eld b/test/fixtures/IndexClass2.eld index ce6fcde..1cc6250 100644 --- a/test/fixtures/IndexClass2.eld +++ b/test/fixtures/IndexClass2.eld @@ -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 ";")))))))) diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el index 4e8c42c..51e0a71 100644 --- a/test/phpinspect-test.el +++ b/test/phpinspect-test.el @@ -237,68 +237,6 @@ class FlufferUpper (phpinspect--make-type-resolver-for-resolvecontext 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 () (let* ((php-code-function " diff --git a/test/test-index.el b/test/test-index.el index f2d2a32..718ffeb 100644 --- a/test/test-index.el +++ b/test/test-index.el @@ -46,11 +46,11 @@ (:word "array") (:variable "things"))) (:block))))))) - (index (phpinspect--index-tokens class-tokens)) + (index (eval (phpinspect--serialize-root-index + (phpinspect--index-tokens class-tokens)))) (expected-index `(phpinspect--root-index (imports) - (namespaces) (classes (,(phpinspect--make-type :name "\\Potato" :fully-qualified t) phpinspect--indexed-class @@ -227,11 +227,14 @@ try { (phpinspect--make-type :name "\\void" :fully-qualified t) (phpinspect--function-return-type method)))))))) +(require 'phpinspect-serialize) (ert-deftest phpinspect-index-tokens-class () (let* ((index1 - (phpinspect--index-tokens - (phpinspect-test-read-fixture-data "IndexClass1"))) + (eval + (phpinspect--serialize-root-index + (phpinspect--index-tokens + (phpinspect-test-read-fixture-data "IndexClass1"))))) (index2 (phpinspect-test-read-fixture-serialization "IndexClass1-indexed")) (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)) (setf (phpinspect-pctx-bmap pctx) (phpinspect-make-bmap)) (phpinspect-with-parse-context pctx (setq tree (phpinspect-parse-current-buffer)))) - (let* ((index1 (phpinspect--index-tokens tree - nil - (phpinspect-bmap-make-location-resolver - (phpinspect-pctx-bmap pctx)))) + (let* ((index1 + (eval + (phpinspect--serialize-root-index + (phpinspect--index-tokens tree + nil + (phpinspect-bmap-make-location-resolver + (phpinspect-pctx-bmap pctx)))))) (index2 (phpinspect-test-read-fixture-serialization "IndexClass1-indexed")) (index1-class (car (alist-get 'classes index1))) diff --git a/test/test-resolve.el b/test/test-resolve.el index 7e985ec..29962da 100644 --- a/test/test-resolve.el +++ b/test/test-resolve.el @@ -169,10 +169,10 @@ result)))))) (ert-deftest phpinspect-get-variable-type-in-block-function-return () - (let ((base-code "$bar = foo();") - (paths (list (cons "$bar" "Foo") - (cons "$bar->baz" "string"))) - + (let ((base-code "$bar = foo()") + (paths (list (cons ";$bar" "Foo") + (cons ";$bar->baz" "string") + (cons "->baz" "string"))) (project (phpinspect--make-dummy-project))) (phpinspect-project-add-index @@ -189,3 +189,66 @@ (should result) (should (phpinspect--type= (phpinspect--make-type :name (concat "\\" (cdr path))) 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)))))))