Compare commits

...

8 Commits

@ -23,7 +23,7 @@ $(CURDIR): ./data/builtin-stubs-index.eld.gz
$(RUN_EMACS) -l phpinspect-cache -f phpinspect-dump-stub-index
%.elc: %.el
$(RUN_EMACS) --eval '(setq byte-compile-error-on-warn t)' -f batch-byte-compile $<
$(RUN_EMACS) --eval '(setq byte-compile-error-on-warn nil)' -f batch-byte-compile $<
.PHONY: deps
deps: ./.deps

@ -93,6 +93,9 @@
qualified names congruent with a bareword type name. Keyed by
bareword typenames."))
(cl-defmethod phpinspect-autoloader-get-fqn-bag ((autoloader phpinspect-autoloader) name)
(gethash name (phpinspect-autoloader-type-name-fqn-bags autoloader)))
(cl-defmethod phpinspect--read-json-file (fs file)
(with-temp-buffer
(phpinspect-fs-insert-file-contents fs file)

@ -221,7 +221,7 @@ linked with."
(unless (memq (cdr class) indexed)
(let (imports namespace-name class-name class-obj)
(pcase-setq `(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports (cdr class))
`(,class-name) (phpinspect--index-class-declaration
`(,_ign ,class-name) (phpinspect--index-class-declaration
(car class)
(phpinspect--make-type-resolver
(phpinspect--uses-to-types imports)
@ -236,7 +236,7 @@ linked with."
(phpinspect-splayt-traverse (class classes)
(pcase-let* ((declaration (phpinspect-toc-token-at-or-after-point declarations (phpinspect-meta-start class)))
(`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports class))
(`(,class-name) (phpinspect--index-class-declaration
(`(,_type ,class-name) (phpinspect--index-class-declaration
(phpinspect-meta-token declaration)
(phpinspect--make-type-resolver
(phpinspect--uses-to-types imports)

@ -21,11 +21,14 @@
;;; Commentary:
;; FIXME: Storage mechanism for abstract methods is missing (@abstract-method)
;;; Code:
(require 'phpinspect-project)
(require 'phpinspect-autoload)
(require 'phpinspect-worker)
(require 'inline)
(defcustom phpinspect-load-stubs t
"If and when phpinspect should load code stubs."
@ -172,7 +175,7 @@ currently opened projects."
(cl-defmethod phpinspect--cache-get-project-create
((cache phpinspect--cache) (project-root string))
"Get a project that is located in PROJECT-ROOT from CACHE.
"Get a project that is located in PROJECT-ROOT from CACHE.
If no such project exists in the cache yet, it is created and
then returned."
(let ((project (phpinspect--cache-get-project cache project-root)))
@ -251,7 +254,9 @@ then returned."
(phpinspect-project-add-index builtin-project (phpinspect-build-stub-index))))
(defun phpinspect-build-stub-index ()
(phpinspect--index-tokens (phpinspect-parse-file (expand-file-name "builtins.php" phpinspect-stub-directory))))
(phpinspect--index-tokens
(phpinspect-parse-file
(expand-file-name "builtins.php" phpinspect-stub-directory))))
(defun phpinspect-dump-stub-index ()
(interactive)
@ -286,5 +291,951 @@ then returned."
(puthash "builtins" project (phpinspect--cache-projects phpinspect-stub-cache))
(setf (phpinspect--cache-read-only-p phpinspect-stub-cache) t)))
(cl-defstruct (phpinspect-bidi-graph (:constructor phpinspect-make-bidi-graph))
"A bidirectional graph."
(rel (make-hash-table :test #'eq :size 2000 :rehash-size 2.0))
(rel-back (make-hash-table :test #'eq :size 2000 :rehash-size 2.0)))
(defun phpinspect-bidi-graph-link (graph obj1 obj2)
(let ((existing-rel (gethash obj1 (phpinspect-bidi-graph-rel graph)))
(existing-back-rel (gethash obj2 (phpinspect-bidi-graph-rel-back graph))))
(if existing-rel
(setcdr existing-rel (cons obj2 (cdr existing-rel)))
(puthash obj1 (list obj2) (phpinspect-bidi-graph-rel graph)))
(if existing-back-rel
(setcdr existing-back-rel (cons obj1 (cdr existing-back-rel)))
(puthash obj2 (list obj1) (phpinspect-bidi-graph-rel-back graph)))))
(defun phpinspect-bidi-graph-unlink-between (graph obj1 obj2)
(when-let ((rel (gethash obj1 (phpinspect-bidi-graph-rel graph)))
(back-rel (gethash obj2 (phpinspect-bidi-graph-rel-back graph))))
(puthash obj1 (delq obj2 rel) (phpinspect-bidi-graph-rel graph))
(puthash obj2 (delq obj1 back-rel) (phpinspect-bidi-graph-rel-back graph))))
(defun phpinspect-bidi-graph-unlink (graph obj)
(when-let ((obj-link (gethash obj (phpinspect-bidi-graph-rel graph))))
(dolist (back-rel obj-link)
(when-let ((back-link (gethash back-rel (phpinspect-bidi-graph-rel-back graph))))
(puthash back-rel (delq obj back-link) (phpinspect-bidi-graph-rel-back graph))))
(remhash obj (phpinspect-bidi-graph-rel graph))))
(defun phpinspect-bidi-graph-get-linking-from (graph obj)
(gethash obj (phpinspect-bidi-graph-rel graph)))
(defun phpinspect-bidi-graph-get-linking-to (graph obj)
(gethash obj (phpinspect-bidi-graph-rel-back graph)))
(cl-defstruct (phpinspect-cache (:constructor phpinspect-make-cache))
(groups (make-hash-table :test #'equal :size 2000 :rehash-size 1.2)))
(cl-defstruct (phpinspect-cache-type (:constructor phpinspect-make-cache-type))
(group nil)
(category nil)
(name nil)
(methods nil)
(variables nil))
(cl-defstruct (phpinspect-cache-namespace (:constructor phpinspect-make-cache-namespace))
(name nil
:type phpinspect-name)
(group nil
:type phpinspect-cache-group)
(types nil)
(functions nil))
(cl-defstruct (phpinspect-cache-group (:constructor phpinspect-make-cache-group))
(autoloader nil
:type phpinspect-autoloader)
(spec nil)
(namespaces (make-hash-table :test #'eq :size 2000 :rehash-size 2.0))
(extends (phpinspect-make-bidi-graph))
(implements (phpinspect-make-bidi-graph)))
(eval-and-compile
(defconst phpinspect-cache-types '(@interface @trait @class @method @type
@abstract-method @function @variable @namespace)
"Types of entities that the cache can store.")
(defconst phpinspect-cache-containing-types '(@class @trait @interface)
"Types of entities that the cache can store members for.")
(defconst phpinspect-cache-member-types '(@method @abstract-method @function @variable)
"Types of entities that the cache can store as members."))
(defun phpinspect-cache-type-get-extends (type)
(phpinspect-cache-group-get-extends
(phpinspect-cache-type-group type)
(phpinspect-cache-type-name type)))
(defun phpinspect-cache-type-get-implements (type)
(phpinspect-cache-group-get-implements
(phpinspect-cache-type-group type)
(phpinspect-cache-type-name type)))
(defun phpinspect-cache-type-add-method (type method)
(push (cons (phpinspect--function-short-name-symbol method)
method)
(phpinspect-cache-type-methods type)))
(defun phpinspect-cache-group-get-extends (group type-name)
(phpinspect-bidi-graph-get-linking-from
(phpinspect-cache-group-extends group)
type-name))
(defun phpinspect-cache-group-get-implements (group type-name)
(phpinspect-bidi-graph-get-linking-from
(phpinspect-cache-group-implements group)
type-name))
(defun phpinspect-cache-group-get-extending (group type-name)
(phpinspect-bidi-graph-get-linking-to
(phpinspect-cache-group-extends group)
type-name))
(defun phpinspect-cache-group-get-implementing (group type-name)
(phpinspect-bidi-graph-get-linking-to
(phpinspect-cache-group-implements group)
type-name))
(defun phpinspect-cache-group-get-namespace (group namespace)
(gethash namespace (phpinspect-cache-group-namespaces group)))
(defun phpinspect-cache-group-get-namespace-create (group name)
"Retrieve namespace by NAME from GROUP.
Name must be of type phpinspect-name (see `phpinspect-intern-name`)."
(or (phpinspect-cache-group-get-namespace group name)
(puthash name (phpinspect-make-cache-namespace :name name :group group)
(phpinspect-cache-group-namespaces group))))
(defun phpinspect-cache-namespace-get-type-create (namespace type category group)
(or (phpinspect-cache-namespace-get-type namespace type category)
(cdar
(push (cons (phpinspect--type-short-name type)
(phpinspect-make-cache-type
:name (phpinspect--type-name-symbol type)
:category category
:group group))
(phpinspect-cache-namespace-types namespace)))))
(defun phpinspect-cache-namespace-get-type (namespace type category)
"Retrieve cached type metadata for TYPE attributed to CATEGORY from NAMESPACE.
If an autoloader is available for the cache group that NAMESPACE
belongs to and no in-memory match is found, the autoloader is
queried and any resulting locations are indexed, after which the
resulting metadata is returned."
(let* ((short-name (phpinspect--type-short-name type))
(namespace-name (phpinspect-cache-namespace-name namespace))
(autoloader (phpinspect-cache-group-autoloader
(phpinspect-cache-namespace-group namespace)))
(entity (cdr (assq short-name
(phpinspect-cache-namespace-types namespace)))))
;; No entity was found in-memory, attempt to query autoloader when
;; available.
(when (and (not entity) autoloader)
(let ((names (phpinspect-autoloader-get-fqn-bag autoloader namespace-name)))
(when (memq short-name names)
;; ... parse/index type
(setq entity nil))))
;; Only return types of the requested category
(and entity
(or (eq '@type category)
(eq category (phpinspect-cache-type-category entity)))
entity)))
;; (when-let ((entity
;; (cdr (assq (phpinspect--type-short-name type)
;; (phpinspect-cache-namespace-types namespace)))))
;; (and (or (eq '@type category)
;; (eq category (phpinspect-cache-type-category entity)))
;; entity)))
(defun phpinspect-cache-namespace-delete-type (namespace type category)
"Delete TYPE attributed to CATEGORY from the in-memory cache of NAMESPACE."
(let ((types (phpinspect-cache-namespace-types namespace))
(name (phpinspect--type-short-name type))
cell-before)
(catch 'break
(while types
(let ((type-cell (car types)))
(when (eq (car type-cell) name)
(when (or (eq '@type category)
(eq category
(phpinspect-cache-type-category (cdr type-cell))))
(if cell-before
(setcdr cell-before (cdr types))
(setf (phpinspect-cache-namespace-types namespace) (cdr types)))
(throw 'break (cdr type-cell)))))
(setq cell-before types
types (cdr types))))))
(defun phpinspect-cache-namespace-delete-function (namespace function-name)
(let ((functions (phpinspect-cache-namespace-functions namespace))
(name (phpinspect-intern-name
(phpinspect-type-name-short
(phpinspect-name-string function-name))))
cell-before)
(catch 'break
(while functions
(let ((type-cell (car functions)))
(when (eq (car type-cell) name)
(if cell-before
(setcdr cell-before (cdr functions))
(setf (phpinspect-cache-namespace-functions namespace)
(cdr functions)))
(throw 'break (cdr type-cell))))
(setq cell-before functions
functions (cdr functions))))))
(defun phpinspect-cache-group-unlink-type-dependencies (group name)
(phpinspect-bidi-graph-unlink
(phpinspect-cache-group-extends group) name)
(phpinspect-bidi-graph-unlink
(phpinspect-cache-group-implements group) name))
(defmacro phpinspect--inline-wildcard-param-p (param)
`(let ((param ,param))
(and (inline-const-p param) (eq '* (inline-const-val param)))))
(define-inline phpinspect-cache-query--do-delete-type
(group param type _member namespace _implements _extends)
(setq type (inline-const-val type))
(inline-letevals (group param namespace)
(if (phpinspect--inline-wildcard-param-p param)
(if namespace
(inline-quote
(when-let ((namespace (phpinspect-cache-group-get-namespace
,group ,namespace)))
(let (resultset)
(setf (phpinspect-cache-namespace-types namespace)
(seq-filter
(lambda (type-cell)
(if ,(if (eq type '@type)
t
`(eq (phpinspect-cache-type-category (cdr type-cell))
(quote ,type)))
(progn
(push (cdr type-cell) resultset)
(phpinspect-cache-group-unlink-type-dependencies
,group (car type-cell))
nil)
t))
(phpinspect-cache-namespace-types namespace)))
(cons 'phpinspect-cache-multiresult resultset))))
(inline-quote
(let (resultset)
(dolist (namespace (hash-table-values
(phpinspect-cache-group-namespaces ,group)))
(let (new-types)
(dolist (type-cell (phpinspect-cache-namespace-types namespace))
(if ,(if (eq type '@type)
t
`(eq (phpinspect-cache-type-category
(cdr type-cell))
(quote ,type)))
(progn
(push (cdr type-cell) resultset)
(phpinspect-cache-group-unlink-type-dependencies
,group (car type-cell))
nil)
(push type-cell new-types)))
(setf (phpinspect-cache-namespace-types namespace) new-types)))
(cons 'phpinspect-cache-multiresult resultset))))
(inline-quote
(when-let* ((namespace (phpinspect-cache-group-get-namespace-create
,group
(or ,namespace
(phpinspect--type-namespace ,param))))
(result (phpinspect-cache-namespace-delete-type
namespace ,param (quote ,type))))
(phpinspect-cache-group-unlink-type-dependencies
,group (phpinspect-cache-type-name result))
result)))))
(define-inline phpinspect-cache-query--do-delete-function
(group param _type _member namespace _implements _extends)
(inline-letevals (group param namespace)
(if (phpinspect--inline-wildcard-param-p param)
(if namespace
(inline-quote
(when-let ((namespace (phpinspect-cache-group-get-namespace
,group ,namespace)))
(let ((resultset
(cons 'phpinspect-cache-multiresult
(mapcar #'cdr
(phpinspect-cache-namespace-functions
namespace)))))
(setf (phpinspect-cache-namespace-functions namespace) nil)
resultset)))
(inline-quote
(let (resultset)
(dolist (namespace (hash-table-values
(phpinspect-cache-group-namespaces ,group)))
(setq resultset
(nconc
(mapcar #'cdr
(phpinspect-cache-namespace-functions
namespace))
resultset)))
(cons 'phpinspect-cache-multiresult resultset))))
(inline-quote
(when-let ((namespace
(phpinspect-cache-group-get-namespace
,group (or ,namespace
(phpinspect-intern-name
(phpinspect-type-name-namespace
(phpinspect-name-string ,param)))))))
(phpinspect-cache-namespace-delete-function
namespace ,param))))))
(defun phpinspect--assq-delete (key alist)
(let* ((result (cons nil nil))
(result-rear result)
deleted)
(catch 'break
(while alist
(if (eq key (caar alist))
(progn
(setcdr result-rear (cdr alist))
(setq deleted (car alist))
(throw 'break nil))
(setq result-rear (setcdr result-rear (cons (car alist) nil)))
(setq alist (cdr alist)))))
(list deleted (cdr result))))
;; OPTIMIZE: this is probably not optimal for large numbers of keys
(defun phpinspect--assq-delete-multi (keys alist)
(let (all-deleted)
(dolist (key keys)
(pcase-let ((`(,deleted ,filtered) (phpinspect--assq-delete key alist)))
(push deleted all-deleted)
(setq alist filtered)))
(list all-deleted alist)))
(defun phpinspect--alist-values (alist)
(let* ((vals (cons nil nil))
(vals-rear vals))
(dolist (cell alist)
(setq vals-rear (setcdr vals-rear (cons (cdr cell) nil))))
(cdr vals)))
(defun phpinspect--alist-keys (alist)
(let* ((keys (cons nil nil))
(keys-rear keys))
(dolist (cell alist)
(setq keys-rear (setcdr keys-rear (cons (car cell) nil))))
(cdr keys)))
(defun phpinspect-cache-query--do-delete-method (cache group param type member)
(when-let ((member-type
(car
(phpinspect-cache-transact cache (list (phpinspect-cache-group-spec group))
:get member :as @type))))
(cond ((eq '* param)
(let ((deleted (phpinspect--alist-values
(phpinspect-cache-type-methods member-type))))
(setf (phpinspect-cache-type-methods member-type) nil)
(cons 'phpinspect-cache-multiresult deleted)))
((and (listp param) (not (phpinspect-name-p param)))
(pcase-let ((`(,deleted ,filtered)
(phpinspect--assq-delete-multi
param (phpinspect-cache-type-methods member-type))))
(setf (phpinspect-cache-type-methods member-type) filtered)
(cons 'phpinspect-cache-multiresult deleted)))
(t
(pcase-let ((`(,deleted ,filtered)
(phpinspect--assq-delete
param (phpinspect-cache-type-methods member-type))))
(setf (phpinspect-cache-type-methods member-type) filtered)
deleted)))))
(defun phpinspect-cache-query--do-delete-variable (cache group param type member)
(when-let ((member-type
(car
(phpinspect-cache-transact cache (list (phpinspect-cache-group-spec group))
:get member :as @type))))
(cond ((eq '* param)
(let ((deleted (phpinspect--alist-values
(phpinspect-cache-type-variables member-type))))
(setf (phpinspect-cache-type-variables member-type) nil)
(cons 'phpinspect-cache-multiresult deleted)))
((and (listp param) (not (phpinspect-name-p param)))
(pcase-let ((`(,deleted ,filtered)
(phpinspect--assq-delete-multi
param (phpinspect-cache-type-variables member-type))))
(setf (phpinspect-cache-type-variables member-type) filtered)
(cons 'phpinspect-cache-multiresult deleted)))
(t
(pcase-let ((`(,deleted ,filtered)
(phpinspect--assq-delete
param (phpinspect-cache-type-variables member-type))))
(setf (phpinspect-cache-type-variables member-type) filtered)
deleted)))))
(define-inline phpinspect-cache-query--do-delete-member
(cache group param type member namespace implements extends)
(inline-letevals (cache group param member)
(setq type (inline-const-val type))
(pcase type
((or '@function '@method)
(inline-quote
(phpinspect-cache-query--do-delete-method ,cache ,group ,param (quote ,type) ,member)))
('@variable
(inline-quote
(phpinspect-cache-query--do-delete-variable ,cache ,group ,param (quote ,type) ,member)))
(_ (error "Delete not supported for member type %s" type)))))
(defmacro phpinspect-cache-query--do-delete
(cache group param type member namespace implements extends)
(cl-assert (symbolp type))
(cond
((memq type (cons '@type phpinspect-cache-containing-types))
`(phpinspect-cache-query--do-delete-type
,group ,param (quote ,type) ,member ,namespace ,implements ,extends))
((and (eq '@function type) (not member))
`(phpinspect-cache-query--do-delete-function
,group ,param (quote ,type) ,member ,namespace ,implements ,extends))
((and (memq type phpinspect-cache-member-types) member)
`(phpinspect-cache-query--do-delete-member
,cache ,group ,param (quote ,type) ,member ,namespace ,implements ,extends))
(t (error "Delete not supported for entity type %s" type))))
(defun phpinspect-cache-namespace-get-function (namespace func-name)
(cdr (assq func-name (phpinspect-cache-namespace-functions namespace))))
(defun phpinspect-cache-group-register-extends (group type extends)
(if (listp extends)
(dolist (ext extends)
(phpinspect-cache-group-register-extends type ext))
(phpinspect-bidi-graph-link
(phpinspect-cache-group-extends group)
(phpinspect--type-name-symbol type)
(phpinspect--type-name-symbol extends))))
(defun phpinspect-cache-group-register-implements (group type implements)
(if (listp implements)
(dolist (ext implements)
(phpinspect-cache-group-register-implements type ext))
(phpinspect-bidi-graph-link
(phpinspect-cache-group-implements group)
(phpinspect--type-name-symbol type)
(phpinspect--type-name-symbol implements))))
(defun phpinspect-cache-type-add-variable (type variable)
(push (cons (phpinspect--variable-name-symbol variable)
variable)
(phpinspect-cache-type-variables type)))
(define-inline phpinspect-cache-query--do-insert-method
(cache group param type member namespace)
(setq type (inline-const-val type))
(inline-letevals (cache group param member namespace)
(inline-quote
(let ((member-type
(car
(phpinspect-cache-transact ,cache (list (phpinspect-cache-group-spec ,group))
:get ,member
:in ,namespace
:as @type)))
resultset)
,(pcase type
((or '@function '@method)
`(if (listp ,param)
(progn
(dolist (method ,param)
(push method resultset)
(phpinspect-cache-type-add-method member-type method))
(cons 'phpinspect-cache-multiresult resultset))
(cdar (phpinspect-cache-type-add-method member-type ,param))))
('@variable
`(if (listp ,param)
(progn
(dolist (variable ,param)
(push variable resultset)
(phpinspect-cache-type-add-variable member-type variable))
(cons 'phpinspect-cache-multiresult resultset))
(cdar (phpinspect-cache-type-add-variable member-type ,param))))
(_ (error "Insert not supported for member type %s" type)))))))
(defun phpinspect-cache-query--do-insert-function (cache group param namespace)
(let ((namespace
(phpinspect-cache-group-get-namespace-create
group (or namespace
(phpinspect--function-namespace param)))))
(or (when-let ((existing (assq (phpinspect--function-name-symbol param)
(phpinspect-cache-namespace-functions namespace))))
(cdar (setcdr existing (cons param (cdr existing)))))
(cdar (push (cons (phpinspect--function-short-name-symbol param) param)
(phpinspect-cache-namespace-functions namespace))))))
(define-inline phpinspect-cache-query--do-insert-member
(cache group param type member namespace implements extends)
(if member
(inline-quote
(phpinspect-cache-query--do-insert-method
,cache ,group ,param ,type ,member ,namespace))
;; Else: insert function in namespace
(inline-quote
(phpinspect-cache-query--do-insert-function ,cache ,group ,param ,namespace))))
(defmacro phpinspect-cache-query--do-insert
(cache group param type member namespace implements extends)
(cl-assert (symbolp type))
(cond
((memq type (cons '@type phpinspect-cache-containing-types))
`(let* ((namespace (phpinspect-cache-group-get-namespace-create
,group
(or ,namespace (phpinspect--type-namespace ,param)))))
,(when extends
`(phpinspect-cache-group-register-extends ,group ,param ,extends))
,(when implements
`(phpinspect-cache-group-register-implements ,group ,param ,implements))
(phpinspect-cache-namespace-get-type-create
namespace ,param (quote ,type) ,group)))
((memq type phpinspect-cache-member-types)
`(phpinspect-cache-query--do-insert-member
,cache ,group ,param (quote ,type) ,member ,namespace ,implements ,extends))
(t (error "Insert not supported for entity type %s" type))))
(defun phpinspect-cache-query--do-get-type-wildcard (group param type namespace)
"Retrieve all type metadata matching TYPE and NAMESPACE available
in the in-memory cache.
This function does not query an autoloader even if it is
available for the cache group."
(let* ((all
(if namespace
(when-let ((namespace (phpinspect-cache-group-get-namespace group namespace)))
(phpinspect--alist-values (phpinspect-cache-namespace-types namespace)))
(mapcan
(lambda (namespace)
(mapcar #'cdr (phpinspect-cache-namespace-types namespace)))
(hash-table-values (phpinspect-cache-group-namespaces group)))))
filtered)
(if (eq '@type type)
(setq filtered all)
(dolist (row all)
(when (eq (phpinspect-cache-type-category row) type)
(push row filtered))))
(cons 'phpinspect-cache-multiresult filtered)))
(defun phpinspect-cache-insert-index (cache group-spec index)
(cl-assert (eq 'phpinspect--root-index (car index)))
(phpinspect-cache-transact cache group-spec
:insert (alist-get 'functions index)
:as @function)
(dolist (class (alist-get 'classes index))
(let ((class-name (alist-get 'class-name class)))
(pcase (alist-get 'type class)
('@trait
(phpinspect-cache-transact cache group-spec
:insert class-name :as @trait))
('@interface
(phpinspect-cache-transact cache group-spec
:insert class-name :as @interface))
('@class
(phpinspect-cache-transact cache group-spec
:insert class-name
:implementing (alist-get 'implements class)
:extending (alist-get 'extending class)
:as @class))
(_ (error "Unexpected class type: %s" (alist-get 'type class))))
(phpinspect-cache-transact cache group-spec
:insert (append (alist-get 'methods class)
(alist-get 'static-methods class))
:as @method
:member-of class-name)
(phpinspect-cache-transact cache group-spec
:insert (append (alist-get 'variables class)
(alist-get 'constants class)
(alist-get 'static-variabes class))
:as @variable
:member-of class-name))))
(defun phpinspect-cache-query--do-get-type
(group param type member namespace implements extends)
(let ((result
(if (eq '* param)
(phpinspect-cache-query--do-get-type-wildcard group param type namespace)
(when-let* ((namespace (phpinspect-cache-group-get-namespace
group
(or namespace
(phpinspect--type-namespace param)))))
(phpinspect-cache-namespace-get-type namespace param type)))))
;; OPTIMIZE: Performance of these filters won't be very good when using
;; wildcards without namespace parameter. Probably worth optimizing when
;; this becomes a frequent use case.
(when implements
(setq result
(let ((implementing (phpinspect-cache-group-get-implementing group implements))
filtered)
(when result
(if (and (consp result)
(eq 'phpinspect-cache-multiresult (car result)))
(progn
(dolist (res (cdr result))
(when (memq (phpinspect-cache-type-name res) implementing)
(push res filtered)))
(cons 'phpinspect-cache-multiresult filtered))
(when (memq (phpinspect-cache-type-name result) implementing)
result))))))
(when extends
(setq result
(let ((extending (phpinspect-cache-group-get-extending group extends))
filtered)
(when result
(if (and (consp result)
(eq 'phpinspect-cache-multiresult (car result)))
(progn
(dolist (res (cdr result))
(when (memq (phpinspect-cache-type-name res) extending)
(push res filtered)))
(cons 'phpinspect-cache-multiresult filtered))
(when (memq (phpinspect-cache-type-name result) extending)
result))))))
result))
(define-inline phpinspect-cache-query--do-get-function
(group param type member namespace implements extends)
(inline-letevals (group param member namespace implements extends)
(if (phpinspect--inline-wildcard-param-p param)
(if namespace
(inline-quote
(when-let ((namespace (phpinspect-cache-group-get-namespace
,group ,namespace)))
(cons 'phpinspect-cache-multiresult
(mapcar #'cdr
(phpinspect-cache-namespace-functions namespace)))))
(inline-quote
(let (resultset)
(dolist (namespace (hash-table-values
(phpinspect-cache-group-namespaces ,group)))
(setq resultset
(nconc
(mapcar #'cdr
(phpinspect-cache-namespace-functions namespace))
resultset)))
(cons 'phpinspect-cache-multiresult resultset))))
(if namespace
(inline-quote
(when-let ((namespace (phpinspect-cache-group-get-namespace
,group ,namespace)))
(phpinspect-cache-namespace-get-function namespace ,param)))
(inline-quote
(let* ((name-string (phpinspect-name-string ,param))
(namespace-name (phpinspect-intern-name
(phpinspect-type-name-namespace name-string)))
(short-name (phpinspect-intern-name
(phpinspect-type-name-short name-string))))
(if (string-match-p "^\\\\" (phpinspect-name-string ,param))
(when-let ((namespace (phpinspect-cache-group-get-namespace
,group namespace-name)))
(phpinspect-cache-namespace-get-function namespace short-name))
(let (resultset)
(dolist (namespace (hash-table-values
(phpinspect-cache-group-namespaces ,group)))
(when-let ((func (phpinspect-cache-namespace-get-function
namespace short-name)))
(push func resultset)))
(cons 'phpinspect-cache-multiresult resultset)))))))))
(defun phpinspect-cache-type-get-method (type method-name)
(cdr (assq method-name (phpinspect-cache-type-methods type))))
(defun phpinspect-cache-type-get-variable (type method-name)
(cdr (assq method-name (phpinspect-cache-type-variables type))))
(define-inline phpinspect-cache-query--do-get-member (cache group param type member namespace)
(inline-letevals (cache group param member namespace)
(setq type (inline-const-val type))
(inline-quote
(when-let ((member-type
(car
(phpinspect-cache-transact ,cache (list (phpinspect-cache-group-spec ,group))
:get ,member
:as @type
:in ,namespace))))
,(pcase type
((or '@function '@method)
(if (phpinspect--inline-wildcard-param-p param)
`(cons 'phpinspect-cache-multiresult
(phpinspect--alist-values (phpinspect-cache-type-methods member-type)))
`(phpinspect-cache-type-get-method member-type ,param)))
('@variable
(if (phpinspect--inline-wildcard-param-p param)
`(cons 'phpinspect-cache-multiresult
(phpinspect--alist-values (phpinspect-cache-type-variables member-type)))
`(phpinspect-cache-type-get-variable member-type ,param)))
(_ (error "Get not supported for member entity type %s" type)))))))
(defmacro phpinspect-cache-query--do-get
(cache group param type member namespace implements extends)
(cl-assert (symbolp type))
(cond
((memq type (cons '@type phpinspect-cache-containing-types))
`(phpinspect-cache-query--do-get-type
,group ,param (quote ,type) ,member ,namespace ,implements ,extends))
((and (eq '@function type) (not member))
`(phpinspect-cache-query--do-get-function
,group ,param ,type ,member ,namespace ,implements ,extends))
((and (memq type phpinspect-cache-member-types) member)
`(phpinspect-cache-query--do-get-member
,cache ,group ,param (quote ,type) ,member ,namespace))
(t (error "Get not supported for entity type %s" type))))
(defmacro phpinspect-cache-query--wrap-action
(action cache group param type member namespace implements extends)
(let* ((param-sym (gensym "param")))
`(let ((,param-sym ,param))
(if (and (sequencep ,param-sym) (not (phpinspect-name-p ,param-sym)))
(let* ((result (cons 'phpinspect-cache-multiresult nil))
(result-rear result))
(seq-doseq (p ,param-sym)
(when-let ((action-result
(,action ,cache ,group p ,type ,member
,namespace ,implements ,extends)))
(setq result-rear
(setcdr result-rear
(cons action-result nil)))))
result)
(,action ,cache ,group ,param ,type ,member ,namespace ,implements ,extends)))))
(defun phpinspect-cache-query--compile-groups (cache group-specs intent)
(cl-assert (phpinspect-cache-p cache))
(let (groups)
(if (phpinspect-cache-group-p cache)
(list cache)
(if group-specs
(dolist (spec group-specs)
(cl-assert (listp spec))
(cl-assert (memq (car spec) '(project label))
t "Spec car must be the symbol `project' or `label'")
(let ((group (gethash spec (phpinspect-cache-groups cache))))
(when (and (eq :insert intent) (not group))
(setq group (puthash spec (phpinspect-make-cache-group :spec spec)
(phpinspect-cache-groups cache))))
(push group groups)))
(if (eq :insert intent)
(error "Cannot insert without defining cache group")
(setq groups (hash-table-values (phpinspect-cache-groups cache)))))
groups)))
(defmacro phpinspect-cache-transact (cache group-specs &rest query)
"Execute QUERY on CACHE, filtering by GROUP-SPECS.
CACHE must be an instance of `phpinspect-cache' or
`phpinspect-cache-group'. If CACHE is a cache group, the query is
only executed on this group and GROUP-SPECS is
ignored. Otherwise, the groups in CACHE are filtered by
GROUP-SPECS and each group in the resulting list of groups is
subject of QUERY."
(declare (indent 2))
(cl-assert (listp query))
(let (type intent intent-param member namespace implements extends key value)
(while (setq key (pop query))
(cl-assert (keywordp key) t "Query keys must be keywords, %s provided" key)
(setq value (pop query))
;; Namespace is allowed to be dynamic/nil
(unless (eq :in key)
(cl-assert value t "Key %s has no value" key))
(pcase key
((or :insert :get :delete)
(when intent
(error "Defined duplicate intent: %s, %s" intent key))
(setq intent key
intent-param value))
(:as (unless (and (symbolp value)
(memq value phpinspect-cache-types))
(error ":type must be one of %s, %s provided"
phpinspect-cache-types value))
(setq type value))
(:member-of (setq member value))
(:in (setq namespace value))
(:implementing (setq implements value))
(:extending (setq extends value))
(_ (error "Unexpected query keyword %s" key))))
;; Query validation
(unless type
(error "Providing entity type with keyword :as is required."))
(when (and member (not (memq type phpinspect-cache-member-types)))
(error "Keyword :member-of can only be used for types %s"
phpinspect-cache-member-types))
(when (and extends (not (memq type '(@class @trait @interface @type))))
(error "Keyword :extending cannot be used for types other than %s"
'(class trait interface)))
(when (eq :insert intent)
(when (and (memq type '(@variable @method @abstract-method)) (not member))
(error "Variable and methods must be member of %s."
phpinspect-cache-containing-types))
(when (eq '@type type)
(error ":as @type cannot be used for insertions.")))
(when (and intent (not intent-param))
(error "Intent %s must have a parameter %s" intent))
(let ((group-specs-sym (make-symbol "group-specs"))
(intent-param-sym (make-symbol "intent-param"))
(member-sym (make-symbol "member"))
(namespace-sym (make-symbol "namespace"))
(implements-sym (make-symbol "implements"))
(extends-sym (make-symbol "extends"))
(resultset-sym (make-symbol "resultset"))
(groups-sym (make-symbol "groups")))
(let* ((action-args
`(,(if (eq '* intent-param) `(quote ,intent-param) intent-param-sym)
,type
,(if member member-sym nil)
,(if namespace namespace-sym nil)
,(if implements implements-sym nil)
,(if extends extends-sym nil)))
(validate-args
`(,(if (eq '* intent-param) `(quote ,intent-param) intent-param-sym)
(quote ,type)
,(if member member-sym nil)
,(if namespace namespace-sym nil)
,(if implements implements-sym nil)
,(if extends extends-sym nil)))
(action (pcase intent
(:insert 'phpinspect-cache-query--do-insert)
(:delete 'phpinspect-cache-query--do-delete)
(:get 'phpinspect-cache-query--do-get)
(_ (error "Invalid intent %s" intent)))))
`(let* ,(seq-filter
(lambda (val) val)
`((,group-specs-sym ,group-specs)
,(unless (eq '* intent-param) `(,intent-param-sym ,intent-param))
,(when member `(,member-sym ,member))
,(when namespace `(,namespace-sym ,namespace))
,(when implements `(,implements-sym ,implements))
,(when extends `(,extends-sym ,extends))
(,groups-sym (phpinspect-cache-query--compile-groups ,cache ,group-specs ,intent))
,resultset-sym))
,(cons 'phpinspect-cache-query--validate (cons intent validate-args))
(dolist (group ,groups-sym)
(when-let ((result ,(cons 'phpinspect-cache-query--wrap-action
(cons action (cons cache (cons 'group action-args))))))
(if (and (listp result) (eq 'phpinspect-cache-multiresult (car result)))
(setq ,resultset-sym (nconc ,resultset-sym (cdr result)))
(push result ,resultset-sym))))
,resultset-sym)))))
(defun phpinspect-cache-query--validate
(intent intent-param type member namespace implements extends)
(and
;; Validate intent-param
(cond
((phpinspect--type-p intent-param)
(cond
((not (memq type '(@class @trait @interface @type)))
(error "Cannot use intent-param of type phpinspect--type when querying for %s" type))
((and (phpinspect--type-fully-qualified intent-param)
namespace)
(error "Use of fully qualified type %s while specifying namespace %s in query"
intent-param namespace)))
t)
((phpinspect-name-p intent-param) t)
((listp intent-param)
(if (memq type '(@class @trait @interface @type))
(unless (seq-every-p #'phpinspect--type-p intent-param)
(error "Each intent param must be of type `phpinspect--type'. Got: %s" intent-param))
(pcase intent
(:insert
(unless (or (seq-every-p #'phpinspect--variable-p intent-param)
(seq-every-p #'phpinspect--function-p intent-param))
(error "Each intent param must be an instance of `phpinspect--function' or `phpinspect--variable'")))
(:get
(unless (seq-every-p #'phpinspect-name-p intent-param)
(error "Each intent param must be a name, got: %s" intent-param)))
(:delete
(unless (or (seq-every-p #'phpinspect-name-p intent-param))
(error "Each intent param must be an instance of `phpinspect--function', `phpinspect--variable' or `phpinspect-name'")))))
t)
((eq '* intent-param)
(unless (memq intent '(:get :delete))
(error "Wildcard '* cannot be used with intent %s" intent)))
((phpinspect--function-p intent-param)
(cond
((not (memq intent '(:insert :delete)))
(error "`phpinspect--function' can only be used as parameter for :insert and :delete intents."))
((not (memq type '(@function @method @abstract-method)))
(error "Inserting/deleting `phpinspect--function' as type %s is not supported" type)))
t)
(t (error "Unsupported intent-param %s" intent-param)))
;; Validate member
(cond
((phpinspect--type-p member) t)
((not member) t)
(t (error "unsupported member type (allowed `phpinspect--type'): %s" member)))
;; Validate namespace
(cond
((phpinspect-name-p namespace) t)
((not namespace) t)
(t (error "unsupported namespace type (allowed `phpinspect-name'): %s" namespace)))
;; Validate implements
(cond
((listp implements)
(unless (seq-every-p #'phpinspect--type-p implements)
(error "Each parameter of :implementing must be of type `phpinspect--type'. Got: %s" implements))
t)
((phpinspect--type-p implements) t)
((not implements) t)
(t (error "unsupported parameter for :implementing (allowed `phpinspect--type': %s" implements)))
;; Validate extends
(cond
((listp extends)
(unless (seq-every-p #'phpinspect--type-or-name-p extends)
(error "Each parameter of :extending must be of type `phpinspect--type'. Got: %s" extends))
t)
((phpinspect--type-p extends) t)
((not extends) t)
(t (error "unsupported parameter for :extending (allowed `phpinspect--type': %s" extends)))))
;;; phpinspect.el ends here
(provide 'phpinspect-cache)

@ -93,7 +93,7 @@ Conditionally executes BODY depending on
(cl-defmethod phpinspect--class-update-declaration
((class phpinspect--class) declaration imports namespace-name)
(phpinspect--class-edit class
(pcase-let ((`(,class-name ,extends ,implements ,_used-types)
(pcase-let ((`(,_ign ,class-name ,extends ,implements ,_used-types)
(phpinspect--index-class-declaration
declaration (phpinspect--make-type-resolver
(phpinspect--uses-to-types imports) nil namespace-name))))

@ -106,9 +106,6 @@ buffer position to insert the use statement at."
buffer namespace-token))
(t (phpinspect-message "No import found for type %s" typename))))))
(defun phpinspect-namespace-part-of-typename (typename)
(string-trim-right typename "\\\\?[^\\]+"))
(defalias 'phpinspect-fix-uses-interactive #'phpinspect-fix-imports
"Alias for backwards compatibility")

@ -227,14 +227,15 @@ function (think \"new\" statements, return types etc.)."
(comment-before)
;; The types that are used within the code of this class' methods.
(used-types)
(add-used-types))
(add-used-types)
(class-type))
(setq add-used-types
(lambda (additional-used-types)
(if used-types
(nconc used-types additional-used-types)
(setq used-types additional-used-types))))
(pcase-setq `(,class-name ,extends ,implements ,used-types)
(pcase-setq `(,class-type ,class-name ,extends ,implements ,used-types)
(phpinspect--index-class-declaration (cadr class) type-resolver))
@ -337,6 +338,7 @@ function (think \"new\" statements, return types etc.)."
`(,class-name .
(phpinspect--indexed-class
(type . ,class-type)
(complete . ,(not (phpinspect-incomplete-class-p class)))
(class-name . ,class-name)
(declaration . ,(seq-find #'phpinspect-declaration-p class))

@ -55,6 +55,20 @@ that the collection is expected to contain")
`(phpinspect--make-type-generated
,@(phpinspect--wrap-plist-name-in-symbol property-list)))
(defun phpinspect-type-name-namespace (typename)
(let ((ns (string-trim-right typename "\\\\?[^\\]+")))
(if (string= "" ns)
"\\"
ns)))
(defun phpinspect--type-namespace (type)
(phpinspect-intern-name
(phpinspect-type-name-namespace (phpinspect--type-name type))))
(defun phpinspect--type-short-name (type)
(phpinspect-intern-name
(phpinspect-type-name-short (phpinspect--type-name type))))
(defun phpinspect--make-types (type-names)
(mapcar (lambda (name) (phpinspect--make-type :name name))
type-names))
@ -125,12 +139,12 @@ See https://wiki.php.net/rfc/static_return_type ."
(cl-defmethod phpinspect--type-name ((type phpinspect--type))
(phpinspect-name-string (phpinspect--type-name-symbol type)))
(defun phpinspect--get-bare-class-name-from-fqn (fqn)
(defun phpinspect-type-name-short (fqn)
(car (last (split-string fqn "\\\\"))))
(cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
"Return just the name, without namespace part, of TYPE."
(phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
(phpinspect-type-name-short (phpinspect--type-name type)))
(cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2 phpinspect--type))
(eq (phpinspect--type-name-symbol type1) (phpinspect--type-name-symbol type2)))
@ -248,6 +262,12 @@ as first element and the type as second element.")
"A phpinspect--type object representing the the
return type of the function."))
(defun phpinspect--function-namespace (func)
(let ((namespace (phpinspect-type-name-namespace (phpinspect--function-name func))))
(when (string= "" namespace)
(setq namespace "\\"))
(phpinspect-intern-name namespace)))
(defmacro phpinspect--make-function (&rest property-list)
`(phpinspect--make-function-generated
,@(phpinspect--wrap-plist-name-in-symbol property-list)))
@ -258,6 +278,14 @@ return type of the function."))
(define-inline phpinspect--function-name (func)
(inline-quote (phpinspect-name-string (phpinspect--function-name-symbol ,func))))
(define-inline phpinspect--function-short-name (func)
(inline-quote (phpinspect-type-name-short
(phpinspect-name-string
(phpinspect--function-name-symbol ,func)))))
(defun phpinspect--function-short-name-symbol (func)
(phpinspect-intern-name (phpinspect--function-short-name func)))
(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
"A PHP Variable."
(name nil
@ -293,6 +321,9 @@ mutability of the variable")
(not (or (phpinspect--variable-static-p variable)
(phpinspect--variable-const-p variable))))
(defun phpinspect--variable-name-symbol (variable)
(phpinspect-intern-name (phpinspect--variable-name variable)))
(defun phpinspect--use-to-type (use)
(let* ((fqn (cadr (cadr use)))
(type (phpinspect--make-type :name (if (string-match "^\\\\" fqn)
@ -321,7 +352,7 @@ mutability of the variable")
(defun phpinspect--index-class-declaration (decl type-resolver)
;; Find out what the class extends or implements
(let (encountered-extends encountered-implements encountered-class
class-name extends implements used-types)
class-name extends implements used-types class-type)
(dolist (word decl)
(if (phpinspect-word-p word)
(cond ((string= (cadr word) "extends")
@ -331,10 +362,11 @@ mutability of the variable")
(setq encountered-extends nil)
(phpinspect--log "Class %s implements in interface" class-name)
(setq encountered-implements t))
((string-match-p
(eval-when-compile
(concat "^" (phpinspect--class-keyword-handler-regexp) "?$"))
(cadr word))
((and (not encountered-class)
(setq class-type (pcase (cadr word)
("class" '@class)
("interface" '@interface)
("trait" '@trait))))
(setq encountered-class t))
(t
(phpinspect--log "Calling Resolver from index-class on %s" (cadr word))
@ -352,7 +384,7 @@ mutability of the variable")
(setq class-name (funcall type-resolver (phpinspect--make-type :name (cadr word)))
encountered-class nil)))))))
(list class-name extends implements used-types)))
(list class-type class-name extends implements used-types)))
(defun phpinspect-namespace-name (namespace)
(or (and (phpinspect-namespace-p namespace)

@ -128,6 +128,12 @@ level of START-FILE in stead of `default-directory`."
(or (gethash name phpinspect-names)
(puthash name name phpinspect-names)))
(define-inline phpinspect-name-p (name)
(inline-letevals (name)
(inline-quote
(and (consp ,name)
(eq 'phpinspect-name (car ,name))))))
(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
(let ((new-plist)
(wrap-value))

@ -5,12 +5,12 @@ rm *.elc
for file in ./*.el; do
echo 'Compiling '"$file"' ...'
cask emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
done
for file in ./**/*.el; do
echo 'Compiling '"$file"' ...'
cask emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
done

@ -0,0 +1,610 @@
; test-cache.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
;; Copyright (C) 2021-2023 Free Software Foundation, Inc.
;; Author: Hugo Thunnissen <devel@hugot.nl>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'ert)
(require 'phpinspect-cache)
(ert-deftest phpinspect-cache-insert-type ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\TestClass") :as @class)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestClass") :as @class))
(should result)
(should (listp result))
(should (= 1 (length result)))
(should (phpinspect-cache-type-p (car result)))
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\TestInterface") :as @interface)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @interface))
(should result)
(should (listp result))
(should (= 1 (length result)))
(should (phpinspect-cache-type-p (car result)))
;; When a query defines an entity category other than the one the existing
;; entity was inserted as, nothing should be returned.
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @class))
(should-not result)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @type))
(should result)
(setq result
(phpinspect-cache-transact cache '((label test))
:get `(,(phpinspect--make-type :name "\\TestInterface")
,(phpinspect--make-type :name "\\TestClass"))
:as @type))
(should result)
(should (= 2 (length result)))
(should (seq-every-p #'phpinspect-cache-type-p result))
(setq result
(phpinspect-cache-transact cache '((label test))
:get `(,(phpinspect--make-type :name "\\TestInterface")
,(phpinspect--make-type :name "\\TestClass"))
:as @interface))
(should result)
(should (= 1 (length result)))
(should (seq-every-p #'phpinspect-cache-type-p result))
(setq result
(phpinspect-cache-transact cache '((label test))
:get * :as @type))
(should result)
(should (= 2 (length result)))
(should (seq-every-p #'phpinspect-cache-type-p result))
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestClass") :as @type))
(should result)
(setq result
(phpinspect-cache-transact cache '((label test))
:delete (phpinspect--make-type :name "\\TestClass") :as @type))
(should result)
(should (phpinspect-cache-type-p (car result)))
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestClass") :as @type))
(should-not result)
(setq result
(phpinspect-cache-transact cache '((label test))
:delete (phpinspect--make-type :name "\\TestClass") :as @type))
(should-not result)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @type))
(should result)
(setq result
(phpinspect-cache-transact cache '((label test))
:delete (phpinspect--make-type :name "\\TestInterface") :as @class))
(should-not result)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @type))
(should result)
(setq result
(phpinspect-cache-transact cache '((label test))
:delete (phpinspect--make-type :name "\\TestInterface") :as @interface))
(should result)
(setq result
(phpinspect-cache-transact cache '((label test))
:get (phpinspect--make-type :name "\\TestInterface") :as @type))
(should-not result)))
(ert-deftest phpinspect-cache-namespace-query ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-type :name "\\Namespace1\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass1"))
:as @class)
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @class :in (phpinspect-intern-name "\\Namespace1")))
(should result)
(should (= 1 (length result)))
(should (eq (phpinspect-intern-name "\\Namespace1\\TestClass")
(phpinspect-cache-type-name (car result))))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @class :in (phpinspect-intern-name "\\Namespace2")))
(should result)
(should (= 2 (length result)))))
(ert-deftest phpinspect-cache-delete-wildcard-types ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-type :name "\\Namespace1\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass1"))
:as @class)
(phpinspect-cache-transact cache '((label test))
:delete * :as @class)
(should-not (phpinspect-cache-transact cache '((label test))
:get * :as @class))))
(ert-deftest phpinspect-cache-delete-wildcard-namespace-types ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-type :name "\\Namespace1\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass")
(phpinspect--make-type :name "\\Namespace2\\TestClass1"))
:as @class)
(phpinspect-cache-transact cache '((label test))
:delete * :as @class :in (phpinspect-intern-name "\\Namespace2"))
(setq result (phpinspect-cache-transact cache '((label test)) :get * :as @class))
(should result)
(should (= 1 (length result)))
(should (eq (phpinspect-intern-name "\\Namespace1\\TestClass")
(phpinspect-cache-type-name (car result))))))
(ert-deftest phpinspect-cache-insert-function ()
(let ((cache (phpinspect-make-cache))
result)
(setq result (phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-function :name "test_func")
:as @function))
(should result)
(should (phpinspect--function-p (car result)))
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-function :name "test_func")
(phpinspect--make-function :name "other_func"))
:as @function
:in (phpinspect-intern-name "\\Namespace1")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get (phpinspect-intern-name "\\test_func")
:as @function))
(should result)
(should (phpinspect--function-p (car result)))
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete (phpinspect-intern-name "\\test_func")
:as @function))
(should result)
(should (phpinspect--function-p (car result)))
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @function
:in (phpinspect-intern-name "\\Namespace1")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete *
:as @function
:in (phpinspect-intern-name "\\Namespace1")))
(should result)
(should (= 2 (length result)))
(phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-function :name "\\Ns\\test_func")
(phpinspect--make-function :name "\\Ns\\other_func")
(phpinspect--make-function :name "\\root_func"))
:as @function)
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @function :in (phpinspect-intern-name "\\Ns")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @function :in (phpinspect-intern-name "\\")))
(should result)
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @function))
(should result)
(should (= 3 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete * :as @function))
(should result)
(should (= 3 (length result)))))
(ert-deftest phpinspect-insert-type-extending/implementing ()
(let ((cache (phpinspect-make-cache))
result)
(setq result
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\Namespace1\\TestClass")
:as @class
:extending (phpinspect--make-type :name "\\App\\TestClassAbstract")
:implementing (phpinspect--make-type :name "\\App\\TestInterface")))
(should result)
(should (= 1 (length result)))
(setq result (car result))
(should (phpinspect-cache-type-get-implements result))
(should (= 1 (length (phpinspect-cache-type-get-implements result))))
(should (eq (phpinspect-intern-name "\\App\\TestInterface")
(car (phpinspect-cache-type-get-implements result))))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:implementing (phpinspect-intern-name "\\App\\TestInterface")
:as @type))
(should result)
(should (= 1 (length result)))
(should (eq (phpinspect-intern-name "\\Namespace1\\TestClass")
(phpinspect-cache-type-name (car result))))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:extending (phpinspect-intern-name "\\App\\TestClassAbstract")
:as @type))
(should result)
(should (= 1 (length result)))
(should (eq (phpinspect-intern-name "\\Namespace1\\TestClass")
(phpinspect-cache-type-name (car result))))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:extending (phpinspect-intern-name "\\App\\TestClass")
:as @type))
(should-not result)
(setq result (phpinspect-cache-transact cache '((label test))
:delete (phpinspect--make-type :name "\\Namespace1\\TestClass")
:as @type))
(should result)
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:extending (phpinspect-intern-name "\\App\\TestClassAbstract")
:as @type))
(should-not result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\Namespace1\\TestClass")
:as @class)
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:extending (phpinspect-intern-name "\\App\\TestClassAbstract")
:as @type))
(should-not result)
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:implementing (phpinspect-intern-name "\\App\\TestInterface")
:as @type))
(should-not result)))
(ert-deftest phpinspect-cache-insert-method ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\TestClass")
:as @class)
(setq result (phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-function :name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(setq result (phpinspect-cache-transact cache '((label test))
:get (phpinspect-intern-name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(should (phpinspect--function-p (car result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get (phpinspect-intern-name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\Banana")))
(should-not result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-function :name "newTestMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass"))
(setq result (phpinspect-cache-transact cache '((label test))
:get (phpinspect-intern-name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(should (phpinspect--function-p (car result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete (phpinspect-intern-name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-function :name "testMethod")
:as @method
:member-of (phpinspect--make-type :name "\\TestClass"))
(setq result (phpinspect-cache-transact cache '((label test))
:delete *
:as @function
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 2 (length result)))
(should-not
(phpinspect-cache-transact cache '((label test))
:get *
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))))
(ert-deftest phpinspect-cache-delete-method-multi ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\TestClass") :as @class)
(phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-function :name "test1")
(phpinspect--make-function :name "test3")
(phpinspect--make-function :name "test2"))
:as @method
:member-of (phpinspect--make-type :name "\\TestClass"))
(setq result (phpinspect-cache-transact cache '((label test))
:delete (list (phpinspect-intern-name "test1")
(phpinspect-intern-name "test2"))
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @method
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(should (string= "test3" (phpinspect--function-name (car result))))))
(ert-deftest phpinspect-cache-insert-variable ()
(let ((cache (phpinspect-make-cache))
result)
(phpinspect-cache-transact cache '((label test))
:insert (phpinspect--make-type :name "\\TestClass") :as @class)
(setq result (phpinspect-cache-transact cache '((label test))
:insert (list (phpinspect--make-variable :name "test1")
(phpinspect--make-variable :name "test3")
(phpinspect--make-variable :name "test2"))
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 3 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 3 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get (phpinspect-intern-name "test2")
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete (phpinspect-intern-name "test2")
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:delete *
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))
(should result)
(should (= 2 (length result)))
(should-not (phpinspect-cache-transact cache '((label test))
:get *
:as @variable
:member-of (phpinspect--make-type :name "\\TestClass")))))
(ert-deftest phpinspect-cache-insert-index ()
(let ((cache (phpinspect-make-cache))
(index (phpinspect--index-tokens
(phpinspect-parse-string
"<?php
function foo() {};
namespace Bar;
class Baz {
private $boo;
const BEE;
function baa() {}
}
interface Banana {
function shouldImplement(): void;
}
trait Atrait {
function isOftenEvil() {}
}"))))
(phpinspect-cache-insert-index cache '((label test)) index)
(let ((result (phpinspect-cache-transact cache '((label test))
:get * :as @interface)))
(should result)
(should (= 1 (length result)))
(should (phpinspect--type= (phpinspect--make-type :name "\\Bar\\Banana")
(phpinspect--make-type
:name-symbol
(phpinspect-cache-type-name (car result)))))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @class))
(should result)
(should (= 1 (length result)))
(should (phpinspect--type= (phpinspect--make-type :name "\\Bar\\Baz")
(phpinspect--make-type
:name-symbol
(phpinspect-cache-type-name (car result)))))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @trait))
(should result)
(should (= 1 (length result)))
(should (phpinspect--type= (phpinspect--make-type :name "\\Bar\\Atrait")
(phpinspect--make-type
:name-symbol
(phpinspect-cache-type-name (car result)))))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @variable
:member-of (phpinspect--make-type :name "\\Bar\\Baz")))
(should result)
(should (= 2 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @method
:member-of (phpinspect--make-type :name "\\Bar\\Baz")))
(should result)
(should (= 1 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get *
:as @method
:member-of (phpinspect--make-type :name "\\Bar\\Banana")))
(should result)
(should (= 1 (length result)))
(should (string= "shouldImplement"
(phpinspect--function-name (car result))))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @type :in (phpinspect-intern-name "\\Bar")))
(should result)
(should (= 3 (length result)))
(setq result (phpinspect-cache-transact cache '((label test))
:get * :as @function))
(should result)
(should (= 1 (length result)))
(should (string= "foo"
(phpinspect--function-name (car result)))))))

@ -53,6 +53,7 @@
(classes
(,(phpinspect--make-type :name "\\Potato" :fully-qualified t)
phpinspect--indexed-class
(type . @class)
(complete . t)
(class-name . ,(phpinspect--make-type :name "\\Potato" :fully-qualified t))
(declaration . (:declaration (:word "class") (:word "Potato")))

Loading…
Cancel
Save