WIP: Support ambiguous typehints

- Try to infer type of "object" typehint by using the return annotation
- Support late static binding with "static" and "this" return type
WIP-incremental-parsing
Hugo Thunnissen 2 years ago
parent f8bf0b611a
commit f013b3c709

@ -99,12 +99,21 @@
(method phpinspect--function))
(phpinspect--log "Adding method by name %s to class"
(phpinspect--function-name method))
(puthash (phpinspect--function-name-symbol method)
method
(phpinspect--class-methods class)))
(setq method (phpinspect--copy-function method))
(when (phpinspect--type-does-late-static-binding
(phpinspect--function-return-type method))
(setf (phpinspect--function-return-type method)
(alist-get 'class-name (phpinspect--class-index class))))
(puthash (phpinspect--function-name-symbol method)
method
(phpinspect--class-methods class)))
(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
(method phpinspect--function))
(setq method (phpinspect--copy-function method))
(puthash (phpinspect--function-name-symbol method)
method
(phpinspect--class-static-methods class)))
@ -135,12 +144,12 @@
(phpinspect--class-static-methods class))))
(if existing
(progn
(unless (eq (phpinspect--function-return-type method)
phpinspect--null-type)
(setf (phpinspect--function-return-type existing)
(phpinspect--function-return-type method))
(unless (phpinspect--type= (phpinspect--function-return-type method)
phpinspect--null-type)
(setf (phpinspect--function-return-type existing)
(phpinspect--function-return-type method)))
(setf (phpinspect--function-arguments existing)
(phpinspect--function-arguments method))))
(phpinspect--function-arguments method)))
(phpinspect--class-set-static-method class method))))
(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
@ -149,19 +158,31 @@
(phpinspect--class-methods class))))
(if existing
(progn
(unless (eq (phpinspect--function-return-type method)
phpinspect--null-type)
(setf (phpinspect--function-return-type existing)
(phpinspect--function-return-type method))
(unless (phpinspect--type= (phpinspect--function-return-type method)
phpinspect--null-type)
(phpinspect--log "method return type %s" (phpinspect--function-return-type method))
(setf (phpinspect--function-return-type existing)
;; The "static" return type returns the class that the method
;; is called on
(if (phpinspect--type-does-late-static-binding
(phpinspect--function-return-type method))
(alist-get 'class-name (phpinspect--class-index class))
(phpinspect--function-return-type method))))
(setf (phpinspect--function-arguments existing)
(phpinspect--function-arguments method))))
(phpinspect--function-arguments method)))
(phpinspect--class-set-method class method))))
(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
(other-class phpinspect--class))
(maphash (lambda (k method)
(phpinspect--class-update-method class method))
(phpinspect--class-methods other-class)))
(let ((class-index (phpinspect--class-index other-class)))
(dolist (method (alist-get 'methods class-index))
(phpinspect--class-update-method class method))
(dolist (method (alist-get 'static-methods class-index))
(phpinspect--class-update-static-method class method))))
(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
(subscription-class phpinspect--class))

@ -56,26 +56,34 @@
arg-index))))
(nreverse arg-index)))
(defsubst phpinspect--should-prefer-return-annotation (type)
"When the return annotation should be preferred over typehint of TYPE, if available."
(or (not type)
(phpinspect--type= type phpinspect--object-type)))
(defun phpinspect--index-function-from-scope (type-resolver scope comment-before)
(let* ((php-func (cadr scope))
(declaration (cadr php-func))
(type (if (phpinspect-word-p (car (last declaration)))
(phpinspect--make-type :name (cadar (last declaration))))))
(funcall type-resolver
(phpinspect--make-type :name (cadar (last declaration)))))))
;; @return annotation. Has precedence over typehint when dealing with a collection.
;; @return annotation. When dealing with a collection, we want to store the
;; type of its members.
(let* ((is-collection
(when type
(member (phpinspect--type-name
(funcall type-resolver type)) phpinspect-collection-types)))
(member (phpinspect--type-name type) phpinspect-collection-types)))
(return-annotation-type
(when (or (not type) is-collection)
(when (or (phpinspect--should-prefer-return-annotation type) is-collection)
(cadadr
(seq-find #'phpinspect-return-annotation-p
comment-before)))))
(phpinspect--log "found return annotation %s" return-annotation-type)
(phpinspect--log "found return annotation %s when type is %s"
return-annotation-type
type)
(when return-annotation-type
(cond ((not type)
(cond ((phpinspect--should-prefer-return-annotation type)
(setq type (funcall type-resolver
(phpinspect--make-type :name return-annotation-type))))
(is-collection

@ -49,11 +49,37 @@
`(phpinspect--make-type-generated
,@(phpinspect--wrap-plist-name-in-symbol property-list)))
(defconst phpinspect--null-type (phpinspect--make-type :name "\\null"))
(defun phpinspect--make-types (&rest type-names)
(mapcar (lambda (name) (phpinspect--make-type :name name))
type-names))
(defconst phpinspect-native-types
;; self, parent and resource are not valid type name.
;; see https://www.php.net/manual/ja/language.types.declarations.php
;;;
;; However, this list does not need to be valid, it just needs to contain the
;; list of type names that we should not attempt to resolve relatively.
'("array" "bool" "callable" "float" "int" "iterable" "mixed" "object" "string" "void" "self" "static" "this"))
(defvar phpinspect-collection-types
(phpinspect--make-types '("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed"))
"FQNs of types that should be treated as collecitons when inferring types.")
(defconst phpinspect--object-type (phpinspect--make-type :name "\\object" :fully-qualified t))
(defconst phpinspect--static-type (phpinspect--make-type :name "\\static" :fully-qualified t))
(defconst phpinspect--self-type (phpinspect--make-type :name "\\self" :fully-qualified t))
(defconst phpinspect--this-type (phpinspect--make-type :name "\\this" :fully-qualified t))
(defconst phpinspect--null-type (phpinspect--make-type :name "\\null" :fully-qualified t))
(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
(setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
(cl-defmethod phpinspect--type-does-late-static-binding ((type phpinspect--type))
"Whether or not TYPE is used for late static binding.
See https://wiki.php.net/rfc/static_return_type ."
(or (phpinspect--type= type phpinspect--static-type)
(phpinspect--type= type phpinspect--this-type)))
(cl-defmethod phpinspect--type-name ((type phpinspect--type))
(symbol-name (phpinspect--type-name-symbol type)))
@ -107,15 +133,12 @@ NAMESPACE may be nil, or a string with a namespace FQN."
(if token-tree (or (phpinspect--find-innermost-incomplete-class token-tree)
(phpinspect--find-class-token token-tree))))
(inside-class-name (if inside-class (phpinspect--get-class-name-from-token
inside-class)))
(self-type (phpinspect--make-type :name "self"))
(static-type (phpinspect--make-type :name "static")))
inside-class))))
(lambda (type)
(phpinspect--type-resolve
types
namespace
(if (and inside-class-name (or (phpinspect--type= type self-type)
(phpinspect--type= type static-type)))
(if (and inside-class-name (phpinspect--type= type phpinspect--self-type))
(progn
(phpinspect--log "Returning inside class name for %s : %s"
type inside-class-name)
@ -129,7 +152,8 @@ NAMESPACE may be nil, or a string with a namespace FQN."
(cl-defmethod phpinspect--format-type-name ((type phpinspect--type))
(phpinspect--format-type-name (phpinspect--type-name type)))
(cl-defstruct (phpinspect--function (:constructor phpinspect--make-function-generated))
(cl-defstruct (phpinspect--function (:constructor phpinspect--make-function-generated)
(:copier phpinspect--copy-function))
"A PHP function."
(name-symbol nil
:type symbol

@ -74,14 +74,6 @@ phpinspect")
Should normally be set to \"phpinspect-index.bash\" in the source
file directory.")
(defconst phpinspect-native-types
;; self, parent and resource are not valid type name.
;; see https://www.php.net/manual/ja/language.types.declarations.php
'("array" "bool" "callable" "float" "int" "iterable" "mixed" "object" "string" "void"))
(defvar phpinspect-collection-types
'("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed")
"FQNs of types that should be treated as collecitons when inferring types.")
(cl-defstruct (phpinspect--completion
(:constructor phpinspect--construct-completion))
@ -229,7 +221,7 @@ accompanied by all of its enclosing tokens."
(defsubst phpinspect-get-cached-project-class-method-type
(project-root class-fqn method-name)
(when project-root
(let* ((class (phpinspect-get-cached-project-class project-root class-fqn))
(let* ((class (phpinspect-get-or-create-cached-project-class project-root class-fqn))
(method))
(when class
(setq method
@ -266,7 +258,7 @@ accompanied by all of its enclosing tokens."
(defsubst phpinspect-get-cached-project-class-static-method-type
(project-root class-fqn method-name)
(when project-root
(let* ((class (phpinspect-get-cached-project-class project-root class-fqn))
(let* ((class (phpinspect-get-or-create-cached-project-class project-root class-fqn))
(method))
(when class
(setq method

Loading…
Cancel
Save