From e111df4ca9902328b2fb87a906faf198c51fc7e2 Mon Sep 17 00:00:00 2001 From: Hugo Thunnissen Date: Tue, 20 Aug 2024 21:22:53 +0200 Subject: [PATCH] Completion: filter out attributes that are not accessible from the current scope --- phpinspect-class.el | 54 +++++++++++++++++++++---------- phpinspect-suggest.el | 58 ++++++++++++++++++++++++++-------- phpinspect-token-predicates.el | 6 ++++ test/test-class.el | 4 +-- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/phpinspect-class.el b/phpinspect-class.el index 478f175..90059fc 100644 --- a/phpinspect-class.el +++ b/phpinspect-class.el @@ -155,25 +155,35 @@ Conditionally executes BODY depending on (puthash (phpinspect--function-name-symbol method) method - map)) + map) + method) (cl-defmethod phpinspect--class-set-method ((class phpinspect--class) - (method phpinspect--function)) + (method phpinspect--function) + &optional extended) (phpinspect--class-edit class (phpinspect--log "Adding method by name %s to class" (phpinspect--function-name method)) - (phpinspect--add-method-copy-to-map - (phpinspect--class-methods class) - (phpinspect--class-name class) - method))) + (setq method (phpinspect--add-method-copy-to-map + (phpinspect--class-methods class) + (phpinspect--class-name class) + method)) + (when extended + (setf (phpinspect--function--inherited method) extended)))) (cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class) - (method phpinspect--function)) + (method phpinspect--function) + &optional extended) (phpinspect--class-edit class - (phpinspect--add-method-copy-to-map - (phpinspect--class-static-methods class) - (phpinspect--class-name class) - method))) + (setq method (phpinspect--add-method-copy-to-map + (phpinspect--class-static-methods class) + (phpinspect--class-name class) + method)) + + (when extended + (setf (phpinspect--function--inherited method) extended)))) + + (cl-defmethod phpinspect--class-delete-method ((class phpinspect--class) (method phpinspect--function)) (phpinspect--class-edit class @@ -226,8 +236,7 @@ Conditionally executes BODY depending on (phpinspect--class-static-methods class)))) (if existing (phpinspect--merge-method (phpinspect--class-name class) existing method extended) - (setf (phpinspect--function--inherited method) extended) - (phpinspect--class-set-static-method class method))))) + (phpinspect--class-set-static-method class method extended))))) (cl-defmethod phpinspect--class-update-method ((class phpinspect--class) (method phpinspect--function) @@ -238,19 +247,30 @@ Conditionally executes BODY depending on (if existing (phpinspect--merge-method (phpinspect--class-name class) existing method extended) - (setf (phpinspect--function--inherited method) extended) - (phpinspect--class-set-method class method))))) + (phpinspect--class-set-method class method extended))))) + +(define-inline phpinspect--scope-inherits-p (scope) + "Returns non-nil when FN has a public or protected scope." + (inline-letevals (fn) + (inline-quote (or (phpinspect-public-p ,scope) + (phpinspect-protected-p ,scope))))) + +(define-inline phpinspect--function-inherits-p (fn) + (inline-quote (phpinspect--scope-inherits-p (phpinspect--function-scope ,fn)))) ;; FIXME: Remove inherited methods when they no longer exist in parent classes ;; (and/or the current class in the case of abstract methods). +;; TODO: Check if above comment is still accurate ^^ (cl-defmethod phpinspect--class-incorporate ((class phpinspect--class) (other-class phpinspect--class)) (phpinspect--class-edit class (dolist (method (phpinspect--class-get-method-list other-class)) - (phpinspect--class-update-method class method 'extended)) + (when (phpinspect--function-inherits-p method) + (phpinspect--class-update-method class method 'extended))) (dolist (method (phpinspect--class-get-static-method-list other-class)) - (phpinspect--class-update-static-method class method 'extended)) + (when (phpinspect--function-inherits-p method) + (phpinspect--class-update-static-method class method 'extended))) (phpinspect--class-subscribe class other-class))) diff --git a/phpinspect-suggest.el b/phpinspect-suggest.el index 5748d5c..8319bd4 100644 --- a/phpinspect-suggest.el +++ b/phpinspect-suggest.el @@ -105,6 +105,19 @@ (lambda (fqn) (phpinspect--get-methods-for-class resolvecontext fqn static))) +(cl-defmethod phpinspect--candidate-scope ((candidate phpinspect--function)) + (phpinspect--function-scope candidate)) + +(cl-defmethod phpinspect--candidate-scope ((candidate phpinspect--variable)) + (phpinspect--variable-scope candidate)) + +(cl-defmethod phpinspect--candiate-inherited ((candidate phpinspect--function)) + (phpinspect--function--inherited candidate)) + +;; FIXME: when inheriting of variables is implemented +(cl-defmethod phpinspect--candidate-inherited ((candidate phpinspect--variable)) + nil) + (defun phpinspect-suggest-attributes-at-point (resolvecontext &optional static) "Suggest object or class attributes at point. @@ -129,22 +142,41 @@ static variables and static methods." (setf (phpinspect--resolvecontext-subject resolvecontext) (butlast (phpinspect--resolvecontext-subject resolvecontext)))) - (let* ((type-resolver (phpinspect--make-type-resolver-for-resolvecontext + (let* ((enclosing-class (seq-find #'phpinspect-class-p + (phpinspect--resolvecontext-enclosing-tokens resolvecontext))) + (type-resolver (phpinspect--make-type-resolver-for-resolvecontext resolvecontext)) (method-lister (phpinspect--make-method-lister resolvecontext - static))) - (let ((statement-type (phpinspect-resolve-type-from-context + static)) + (statement-type (phpinspect-resolve-type-from-context resolvecontext - type-resolver))) - (phpinspect--log "Statement type: %s" statement-type) - (when statement-type - (let ((type (funcall type-resolver statement-type))) - (when-let ((result - (append (phpinspect--get-variables-for-class - resolvecontext type static) - (funcall method-lister type)))) - (phpinspect--log "Returning attributes %s" result) - result)))))) + type-resolver)) + enclosing-class-name) + + (phpinspect--log "Statement type: %s" statement-type) + (when statement-type + (when-let* ((type (funcall type-resolver statement-type)) + (result + (append (phpinspect--get-variables-for-class + resolvecontext type static) + (funcall method-lister type)))) + + ;; Filter out candidates according to scoping rules + (when (and enclosing-class + (setq enclosing-class-name + (phpinspect--get-class-name-from-token enclosing-class))) + (let* ((enclosing-type (funcall type-resolver (phpinspect--make-type :name enclosing-class-name))) + (allow-protected (phpinspect--type= type enclosing-type)) + filtered-result) + (dolist (candidate result) + (if allow-protected + (push candidate filtered-result) + (when (phpinspect-public-p (phpinspect--candidate-scope candidate)) + (push candidate filtered-result)))) + (setq result filtered-result)) + + (phpinspect--log "Returning attributes %s" result) + result))))) (provide 'phpinspect-suggest) diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el index b27e5d8..ffdb8dc 100644 --- a/phpinspect-token-predicates.el +++ b/phpinspect-token-predicates.el @@ -81,6 +81,12 @@ Type can be any of the token types returned by (or (phpinspect-token-type-p token :const) (phpinspect-incomplete-const-p token))) +(define-inline phpinspect-public-p (token) + (inline-quote (phpinspect-token-type-p ,token :public))) + +(define-inline phpinspect-protected-p (token) + (inline-quote (phpinspect-token-type-p ,token :protected))) + (define-inline phpinspect-scope-p (token) (inline-letevals (token) (inline-quote diff --git a/test/test-class.el b/test/test-class.el index 670fedd..154336c 100644 --- a/test/test-class.el +++ b/test/test-class.el @@ -52,10 +52,10 @@ (phpinspect--class-set-index class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "Class")))) (phpinspect--class-set-index other-class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "OtherClass")))) (phpinspect--class-update-method - class (phpinspect--make-function :name "test" :return-type phpinspect--null-type)) + class (phpinspect--make-function :scope '(:private) :name "test" :return-type phpinspect--null-type)) (phpinspect--class-update-method - other-class (phpinspect--make-function :name "other-test" :return-type phpinspect--null-type)) + other-class (phpinspect--make-function :scope '(:protected) :name "other-test" :return-type phpinspect--null-type)) (phpinspect--class-incorporate class other-class)