-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathphpinspect-method-cell.el
312 lines (246 loc) · 11.1 KB
/
phpinspect-method-cell.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
;;; phpinspect-method-cell.el --- Models for PHP method definitions -*- lexical-binding: t; -*-
;; Copyright (C) 2021-2023 Free Software Foundation, Inc
;; Author: Hugo Thunnissen <[email protected]>
;; Keywords: php, languages, tools, convenience
;; Version: 2.1.0
;; 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:
;; This file contains struct definitions and logic for the management of
;; (collections of) methods associated with a specific type.
;;; Code:
(require 'cl-macs)
(require 'phpinspect-util)
(require 'phpinspect-type)
(require 'phpinspect-name)
(cl-defstruct (phpinspect-method
(:constructor phpinspect-make-method-generated)
(:conc-name phpi-method-)
(:copier phpi-copy-method))
(name nil
:type phpinspect-name)
(aliased-from nil
:type phpinspect-name)
(origin-type nil
:type phpinspect--type)
(-return-type nil
:type phpinspect--type)
(definition nil
:type phpinspect--function))
(defun phpinspect-make-method (origin-type definition)
"Create a method for ORIGIN-TYPE, defined in DEFINITION.
ORIGIN-TYPE must be a structure of type `phpinspect--type'.
DEFINITION must be a structure of type `phpinspect--function'."
(phpinspect-make-method-generated
:name (phpinspect--function-name definition)
:origin-type origin-type
:definition definition))
(defun phpi-method-name-string (method)
(phpinspect-name-string (phpi-method-name method)))
(cl-defstruct (phpinspect-method-cell
(:constructor phpinspect-make-method-cell)
(:conc-name phpi-mc-))
(name nil
:type phpinspect-name)
(own nil
:type phpinspect-method)
(trait nil
:type phpinspect-method)
(inherited nil
:type phpinspect-method)
(interface nil
:type phpinspect-method))
(cl-defstruct (phpinspect-method-collection
(:constructor phpinspect-make-method-collection)
(:conc-name phpi-mcol-))
(home-type nil
:type phpinspect--type)
(cells nil
:type alist
:documentation "<phpinspect-name, phpinspect-method-cell>"))
(defun phpi-mcol-set-home-type (mcol type)
"Set home type of MCOL to TYPE.
Also updates all members of MCOL with the same origin-type."
(setf (phpi-mcol-home-type mcol) type)
(dolist (method (phpi-mcol-list-own mcol))
(setf (phpi-method-origin-type method) type)))
(defun phpi-mcol-find-cell (mcol method-name &optional remove)
(cl-assert (phpinspect-name-p method-name))
(alist-get method-name (phpi-mcol-cells mcol) nil remove #'eq))
(defun phpi-mcol-find-cell-create (mcol method-name)
(let ((cell (phpi-mcol-find-cell mcol method-name)))
(unless cell
(setq cell (phpinspect-make-method-cell
:name method-name))
(push (cons method-name cell) (phpi-mcol-cells mcol)))
cell))
(defun phpi-mcol-add (mcol method &optional overwrite)
(let ((cell (phpi-mcol-find-cell-create mcol (phpi-method-name method))))
;; insert when not present or overwriting
(when (or (not (phpi-mc-get-for-type-category
cell (phpi-mcol-home-type mcol) (phpi-method-origin-type method))))
overwrite)
(phpi-mc-set cell (phpi-mcol-home-type mcol) (phpi-method-origin-type method) method)
cell))
(defun phpi-mcol-list-own (mcol)
(let (list)
(dolist (cons (phpi-mcol-cells mcol))
(when-let ((method (phpi-mc-own (cdr cons))))
(push method list)))
list))
(defun phpi-mcol-list-active (mcol)
"List all active methods in MCOL."
(let (list)
(dolist (cons (phpi-mcol-cells mcol))
(when-let ((method (phpi-mc-get-active (cdr cons))))
(push method list)))
list))
(defun phpi-method-set-return-type (method type)
(setf (phpi-method--return-type method) type))
(defun phpi-method-original-return-type (method)
(phpinspect--function-return-type (phpi-method-definition method)))
(defun phpi-method-does-late-static-binding (method)
(when-let ((og-type (phpi-method-original-return-type method)))
(phpinspect--type-does-late-static-binding og-type)))
(defun phpi-method-resolve-late-static-binding (method home-type)
"Destructively modify METHOD to return HOME-TYPE if late static binding."
(when-let ((og-type (phpi-method-original-return-type method)))
(phpi-method-set-return-type
method (phpinspect--resolve-late-static-binding og-type home-type))))
(defun phpi-mc-set (cell home-type origin-type method)
(let ((late-static-binding (and method (phpi-method-does-late-static-binding method))))
(if (phpinspect--type= home-type origin-type)
;; Method belongs to home type
(progn
(when late-static-binding
(phpi-method-resolve-late-static-binding method home-type))
(setf (phpi-mc-own cell) method))
;; resolve late static binding
(when late-static-binding
;; Copy on write (we don't want to change the return type for the
;; original typedef as well).
(setq method (phpi-copy-method method))
(phpi-method-resolve-late-static-binding method home-type))
;; Method is from a trait, interface or inherited
(pcase (phpinspect--type-category origin-type)
('trait (setf (phpi-mc-trait cell) method))
('interface (setf (phpi-mc-interface cell) method))
;; class or abstract class
(_ (setf (phpi-mc-inherited cell) method) home-type)))))
(defun phpi-mc-get-for-type-category (cell home-type type)
(if (phpinspect--type= home-type type)
(phpi-mc-own cell)
(pcase (phpinspect--type-category type)
('trait (phpi-mc-trait cell))
('interface (phpi-mc-interface cell))
(_ (phpi-mc-inherited cell)))))
(defun phpi-method-origin-type= (method type)
(phpinspect--type= (phpi-method-origin-type method) type))
(defun phpi-mc-get-for-type (cell type)
(catch 'phpinspect--break
(dolist (method (list (phpi-mc-own cell) (phpi-mc-trait cell)
(phpi-mc-inherited cell) (phpi-mc-interface cell)))
(when (and method (phpi-method-origin-type= method type))
(throw 'phpinspect--break method)))
nil))
(defun phpi-mcol-delete (mcol method-name)
(phpi-mcol-find-cell mcol method-name 'remove))
(defun phpi-mc-empty-p (cell)
"CELL is empty when it contains no methods."
(not (phpi-mc-get-active cell)))
(defun phpi-mc-get-active (cell)
"Get active method from CELL, according to PHP precendence order.
PHP's precendence order is described in the documentation for
traits, located at:
https://www.php.net/manual/en/language.oop5.traits.php"
(or (phpi-mc-own cell)
(phpi-mc-trait cell)
(phpi-mc-inherited cell)
(phpi-mc-interface cell)))
(defun phpi-mc-get-return-type (cell)
(when-let ((method (phpi-mc-get-active cell)))
(phpi-method-return-type method)))
(define-inline phpi-try-method-return-type (method?)
(inline-letevals (method?)
(inline-quote
(and ,method? (phpi-method-return-type ,method?)))))
(defun phpi-mc-get-return-type-tryhard (cell)
(or (phpi-try-method-return-type (phpi-mc-own cell))
(phpi-try-method-return-type (phpi-mc-trait cell))
(phpi-try-method-return-type (phpi-mc-inherited cell))
(phpi-try-method-return-type (phpi-mc-interface cell))))
(defun phpi-method-return-type (method)
(or (phpi-method--return-type method)
(phpinspect--function-return-type (phpi-method-definition method))))
(defun phpi-mcol-delete-for-type (mcol type &optional name)
"Delete from MCOL all methods that originate from TYPE.
When NAME is provided, only method with NAME is deleted."
(if name
;; Name is provided, only delete method with NAME.
(when-let ((cell (phpi-mcol-find-cell mcol name)))
(when (phpi-mc-get-for-type cell type)
(phpi-mc-set cell (phpi-mcol-home-type mcol) type nil))
(when (phpi-mc-empty-p cell)
(phpi-mcol-delete mcol name)))
(let ((cells (phpi-mcol-cells mcol)))
(dolist (cons cells)
(let ((cell (cdr cons)))
(when (phpi-mc-get-for-type cell type)
(phpi-mc-set cell (phpi-mcol-home-type mcol) type nil))
;; mark cell for deletion by setting car and cdr of alist member to
;; nil
(when (phpi-mc-empty-p cell)
(setcar cons nil)
(setcdr cons nil))))
;; Delete all empty cells
(setf (phpi-mcol-cells mcol) (delete (cons nil nil) cells)))))
(defun phpi-mcol-get-active-method (mcol name)
(when-let ((cell (phpi-mcol-find-cell mcol name)))
(phpi-mc-get-active cell)))
(defun phpi-mcol-get-return-type (mcol name &optional tryhard)
"Get the returntype of method with NAME from MCOL.
If TRYHARD is provided and non-nil, also check for possible
return types in extended classes, traits and interfaces."
(when-let ((cell (phpi-mcol-find-cell mcol name)))
(if tryhard
(phpi-mc-get-return-type-tryhard cell)
(phpi-mc-get-return-type cell))))
;; Helpers for generic property access
(cl-defmethod phpi-fn-name ((fn phpinspect--function))
(phpinspect--function-name-string fn))
(cl-defmethod phpi-fn-name ((method phpinspect-method))
(phpi-method-name-string method))
(cl-defmethod phpi-fn-name-symbol ((fn phpinspect--function))
(phpinspect--function-name fn))
(cl-defmethod phpi-fn-name-symbol ((fn phpinspect-method))
(phpi-method-name-string fn))
(cl-defmethod phpi-fn-arguments ((method phpinspect-method))
(phpi-fn-arguments (phpi-method-definition method)))
(cl-defmethod phpi-fn-arguments ((fn phpinspect--function))
(phpinspect--function-arguments fn))
(cl-defmethod phpi-fn-return-type ((fn phpinspect--function))
(or (phpinspect--function-return-type fn) phpinspect--unknown-type))
(cl-defmethod phpi-fn-return-type ((method phpinspect-method))
(or (phpi-method-return-type method) phpinspect--unknown-type))
(cl-defmethod phpi-fn-argument-type ((fn phpinspect--function) argument-name)
(phpinspect--function-argument-type fn argument-name))
(cl-defmethod phpi-fn-argument-type ((method phpinspect-method) argument-name)
(phpi-fn-argument-type (phpi-method-definition method) argument-name))
(cl-defmethod phpi-fn-anonymous-p ((fn phpinspect--function))
(phpinspect--function-anonymous-p fn))
(cl-defmethod phpi-fn-anonymous-p ((_method phpinspect-method))
nil)
(cl-defmethod phpi-fn-token ((fn phpinspect--function))
(phpinspect--function-token fn))
(cl-defmethod phpi-fn-token ((method phpinspect-method))
(phpi-fn-token (phpi-method-definition method)))
(provide 'phpinspect-method-cell)
;;; phpinspect-method-cell.el ends here