emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/phpinspect dfdef3e382 2/2: Implement support for PHP8.1


From: ELPA Syncer
Subject: [elpa] externals/phpinspect dfdef3e382 2/2: Implement support for PHP8.1 property typehints
Date: Sat, 3 Aug 2024 09:59:31 -0400 (EDT)

branch: externals/phpinspect
commit dfdef3e3823a07ce43229c258c652fd9afe673b4
Author: Hugo Thunnissen <devel@hugot.nl>
Commit: Hugo Thunnissen <devel@hugot.nl>

    Implement support for PHP8.1 property typehints
---
 Makefile                       |  2 +-
 phpinspect-buffer.el           | 25 ++++++++++++++++--
 phpinspect-index.el            | 42 +++++++++++++++++++++---------
 phpinspect-meta.el             | 16 ++++++++++++
 phpinspect-parser.el           |  6 ++---
 phpinspect-resolve.el          | 12 ++++-----
 phpinspect-token-predicates.el |  5 ++++
 test/fixtures/IndexClass1.eld  |  2 +-
 test/fixtures/IndexClass2.eld  |  2 +-
 test/test-buffer.el            | 58 ++++++++++++++++++++++++++++++++++++++++++
 test/test-index.el             | 54 +++++++++++++++++++++++++++++++++++++++
 11 files changed, 197 insertions(+), 27 deletions(-)

diff --git a/Makefile b/Makefile
index 4dce40a255..1dd88afd19 100644
--- a/Makefile
+++ b/Makefile
@@ -49,4 +49,4 @@ compile-native: ./.deps
 
 .PHONY: test
 test: deps
-       $(RUN_EMACS) -L ./test -l ./test/phpinspect-test e -f 
ert-run-tests-batch
+       $(RUN_EMACS) -L ./test -l ./test/phpinspect-test -f ert-run-tests-batch
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index 3bdaa5c95d..354ec5e0b2 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -339,27 +339,38 @@ linked with."
       (setf (phpinspect-buffer-class-variables buffer) (phpinspect-make-toc 
class-variables)))
 
     (dolist (var to-be-indexed)
+      ;; We have left the class we were previously indexing properties
+      ;; for.
       (when (and class (> (phpinspect-meta-start var) (phpinspect-meta-end 
class)))
         (setq class nil))
 
+      ;; Retrieve the class that the property belongs to. In a lot of cases 
only
+      ;; one class will be in the current buffer. But PHP allows the definition
+      ;; of multiple classes in the same file so this should be supported.
       (unless class
         (setq class (phpinspect-toc-token-at-point classes 
(phpinspect-meta-start var))))
 
+      ;; Fetch the index for the current class
       (setq class-obj (phpinspect-buffer-get-index-for-token buffer 
(phpinspect-meta-token class)))
 
       (let (scope static indexed index-env comment-before)
         (if (phpinspect-static-p (phpinspect-meta-token 
(phpinspect-meta-parent var)))
+            ;; Variable is defined as [scope?] static [type?] $variable
             (progn
               (setq static (phpinspect-meta-parent var))
               (when (phpinspect-scope-p (phpinspect-meta-token 
(phpinspect-meta-parent static)))
+                ;; Variable is defined as scope static [type?] $variable
                 (setq scope `(,(car (phpinspect-meta-token 
(phpinspect-meta-parent static)))
-                              ,(phpinspect-meta-token var))
+                              ,@(phpinspect-meta-token-with-left-siblings var))
                       comment-before (phpinspect-meta-find-left-sibling 
(phpinspect-meta-parent static)))))
           (when (phpinspect-scope-p (phpinspect-meta-token 
(phpinspect-meta-parent var)))
+            ;; Variable is defined as scope [type?] $variable
             (setq scope (phpinspect-meta-token (phpinspect-meta-parent var))
                   comment-before (phpinspect-meta-find-left-sibling 
(phpinspect-meta-parent var)))))
 
-        (unless scope (setq scope `(:public ,(phpinspect-meta-token var))))
+        (unless scope
+          (setq scope `(:public ,@(mapcar #'phpinspect-meta-token 
(phpinspect-meta-left-siblings var))
+                                ,(phpinspect-meta-token var))))
 
         (unless (setq index-env (alist-get class class-environments nil nil 
#'eq))
           (setq index-env (phpinspect-get-token-index-context namespaces 
imports class))
@@ -419,6 +430,16 @@ linked with."
   (phpinspect-buffer-parse buffer 'no-interrupt))
 
 (cl-defmethod phpinspect-buffer-update-project-index ((buffer 
phpinspect-buffer))
+  "Update project index using the last parsed token map of this
+buffer. When `phpinspect-buffer-parse' has been executed before
+and a map is available from the previous parse, this is used. If
+none is available `phpinspect-buffer-parse' is called before
+continuing execution."
+
+  ;; Parse buffer if no map is available.
+  (unless (phpinspect-buffer-map buffer)
+    (phpinspect-buffer-parse buffer))
+
   ;; Use inhibit-quit to prevent index corruption though partial index
   ;; application.
   (let ((inhibit-quit t))
diff --git a/phpinspect-index.el b/phpinspect-index.el
index 6c999d0fed..a786861399 100644
--- a/phpinspect-index.el
+++ b/phpinspect-index.el
@@ -174,10 +174,29 @@ function (think \"new\" statements, return types etc.)."
     (when (stringp type) type)))
 
 (defun phpinspect--index-variable-from-scope (type-resolver scope 
comment-before &optional static)
-  "Index the variable inside `scope`."
-  (let* ((variable-name (cadr (cadr scope)))
-         (type
-          (phpinspect--variable-type-string-from-comment comment-before 
variable-name)))
+  "Index the variable inside SCOPE, use doc in COMMENT-BEFORE if missing 
typehint.
+
+Provide STATIC if the variable was defined as static.
+
+SCOPE should be a scope token (`phpinspect-scope-p')."
+  (setq scope (take 5 (seq-filter #'phpinspect-not-comment-p scope)))
+  (let (variable-name type)
+    (cond
+     ((phpinspect--match-sequence (take 3 scope)
+        ;; Sequence: scope-type, typehint, variable [ = value ]
+        :m * :f #'phpinspect-word-p :f #'phpinspect-variable-p)
+      (setq variable-name (cadr (nth 2 scope)))
+      (setq type (cadr (nth 1 scope))))
+     ((phpinspect--match-sequence (take 2 scope)
+        ;; Sequence: variable [ = value ]
+        :m * :f #'phpinspect-variable-p)
+      (setq variable-name (cadr (nth 1 scope))
+            ;; Since no typehint is available, attempt extracting one from a
+            ;; docstring.
+            type (phpinspect--variable-type-string-from-comment
+                  comment-before variable-name)))
+     (t (error (format "Failed to determine variable from scope %s" scope))))
+
     (phpinspect--log "calling resolver from index-variable-from-scope")
     (phpinspect--make-variable
      ;; Static class variables are always prefixed with dollar signs when
@@ -260,7 +279,7 @@ function (think \"new\" statements, return types etc.)."
              (cond ((phpinspect-const-p (cadr token))
                     (push (phpinspect--index-const-from-scope token) 
constants))
 
-                   ((phpinspect-variable-p (cadr token))
+                   ((seq-find #'phpinspect-variable-p token)
                     (push (phpinspect--index-variable-from-scope type-resolver
                                                                  token
                                                                  
comment-before)
@@ -275,10 +294,9 @@ function (think \"new\" statements, return types etc.)."
                                                                         
add-used-types)
                                  static-methods))
 
-                          ((phpinspect-variable-p (cadadr token))
+                          ((seq-find #'phpinspect-variable-p (cdadr token))
                            (push (phpinspect--index-variable-from-scope 
type-resolver
-                                                                        (list 
(car token)
-                                                                              
(cadadr token))
+                                                                        
`(,(car token) ,@(cdadr token))
                                                                         
comment-before
                                                                         
'static)
                                  static-variables))))
@@ -298,11 +316,11 @@ function (think \"new\" statements, return types etc.)."
                                                                  
add-used-types)
                           static-methods))
 
-                   ((phpinspect-variable-p (cadr token))
+                   ((seq-find #'phpinspect-variable-p (cdr token))
                     (push (phpinspect--index-variable-from-scope type-resolver
-                                                                 `(:public
-                                                                   ,(cadr 
token))
-                                                                 
comment-before)
+                                                                 `(:public 
,@(cdr token))
+                                                                 comment-before
+                                                                 'static)
                           static-variables))))
             ((phpinspect-const-p token)
              ;; Bare constants are always public
diff --git a/phpinspect-meta.el b/phpinspect-meta.el
index 098b9ed7a5..d6590af02a 100644
--- a/phpinspect-meta.el
+++ b/phpinspect-meta.el
@@ -149,6 +149,22 @@
     (phpinspect-meta-children (phpinspect-meta-parent meta)) 
(phpinspect-meta-parent-offset meta))
    #'phpinspect-meta-sort-start))
 
+(defun phpinspect-meta-left-sibling-tokens (meta)
+  (let* ((tokens (cons nil nil))
+         (rear tokens))
+    (dolist (sibling (phpinspect-meta-left-siblings meta))
+      (setq rear (setcdr rear (cons (phpinspect-meta-token sibling) nil))))
+    (cdr tokens)))
+
+(defun phpinspect-meta-token-with-left-siblings (meta)
+  (nconc (phpinspect-meta-left-sibling-tokens meta) (list 
(phpinspect-meta-token meta))))
+
+(defun phpinspect-meta-left-siblings (meta)
+  (sort
+   (phpinspect-splayt-find-all-before
+    (phpinspect-meta-children (phpinspect-meta-parent meta)) 
(phpinspect-meta-parent-offset meta))
+   #'phpinspect-meta-sort-start))
+
 (defun phpinspect-meta-wrap-token-pred (predicate)
   (lambda (meta) (funcall predicate (phpinspect-meta-token meta))))
 
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index 61f41f69fc..26cc97c7d7 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -760,19 +760,19 @@ Returns the consumed text string without face properties."
 (phpinspect-defparser scope-public
   :tree-keyword "public"
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
-                               string terminator tag comment)
+                               string terminator tag comment word)
   :delimiter-predicate #'phpinspect--scope-terminator-p)
 
 (phpinspect-defparser scope-private
   :tree-keyword "private"
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
-                               string terminator tag comment)
+                               string terminator tag comment word)
   :delimiter-predicate #'phpinspect--scope-terminator-p)
 
 (phpinspect-defparser scope-protected
   :tree-keyword "protected"
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
-                               string terminator tag comment)
+                               string terminator tag comment word)
   :delimiter-predicate #'phpinspect--scope-terminator-p)
 
 (phpinspect-defhandler scope-keyword (start-token max-point)
diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el
index cae5fa8185..9dfdb87291 100644
--- a/phpinspect-resolve.el
+++ b/phpinspect-resolve.el
@@ -46,6 +46,11 @@
   (or (phpinspect-assignment-p token)
       (equal '(:word "as") token)))
 
+(define-inline phpinspect-not-assignment-p (token)
+  "Inverse of applying `phpinspect-assignment-p to TOKEN."
+  (inline-quote
+   (not (phpinspect-maybe-assignment-p ,token))))
+
 (cl-defgeneric phpinspect--find-assignments-in-token (token)
   "Find any assignments that are in TOKEN, at top level or nested in blocks"
   (when (keywordp (car token))
@@ -71,13 +76,6 @@
     (phpinspect--log "Found statements in token: %s" statements)
     assignments))
 
-(defsubst phpinspect-not-assignment-p (token)
-  "Inverse of applying `phpinspect-assignment-p to TOKEN."
-  (not (phpinspect-maybe-assignment-p token)))
-
-(defsubst phpinspect-not-comment-p (token)
-  (not (phpinspect-comment-p token)))
-
 (defun phpinspect--find-assignments-by-predicate (token predicate)
   (let ((variable-assignments)
         (all-assignments (phpinspect--find-assignments-in-token token)))
diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el
index 27dce56988..4724c39e6f 100644
--- a/phpinspect-token-predicates.el
+++ b/phpinspect-token-predicates.el
@@ -251,4 +251,9 @@ Type can be any of the token types returned by
   (and (listp token)
        (keywordp (car token))))
 
+(define-inline phpinspect-not-comment-p (token)
+  (inline-quote
+   (not (phpinspect-comment-p ,token))))
+
+
 (provide 'phpinspect-token-predicates)
diff --git a/test/fixtures/IndexClass1.eld b/test/fixtures/IndexClass1.eld
index 8ccf155b4c..88f6f3f9d2 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") (: [...]
+(: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") (: [...]
diff --git a/test/fixtures/IndexClass2.eld b/test/fixtures/IndexClass2.eld
index 691272b1e9..ce6fcde686 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\\\\E [...]
+(: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\\\\E [...]
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 9df7b2a476..689e7cb2d4 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -491,3 +491,61 @@ class AccountStatisticsController {
                    (:use (:word "Illuminate\\Support\\Facades\\Auth") 
(:terminator ";")))
                  (mapcar #'phpinspect-meta-token
                          (phpinspect-splayt-to-list (phpinspect-bmap-imports 
bmap)))))))))
+
+
+(ert-deftest phpinspect-buffer-index-typehinted-class-variables ()
+  (with-temp-buffer
+    (let ((buffer (phpinspect-make-buffer
+                   :buffer (current-buffer)
+                   :-project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader)))))
+      (insert "<?php
+declare(strict_types=1);
+
+namespace App\\Controller\\Api\\V1;
+
+use Illuminate\\Database\\Eloquent\\Model;
+use Illuminate\\Database\\Eloquent\\Relations\\Relation;
+use Illuminate\\Support\\Facades\\Auth;
+
+class AccountStatisticsController {
+
+    public Model $model;
+    private Model $privModel;
+    static Relation $relation;
+    public static Relation $staticRelation;
+}")
+
+      (phpinspect-buffer-update-project-index buffer)
+
+      (let ((class (phpinspect-project-get-class
+                    (phpinspect-buffer-project buffer)
+                    (phpinspect--make-type
+                     :name 
"\\App\\Controller\\Api\\V1\\AccountStatisticsController"
+                     :fully-qualified t))))
+        (should class)
+
+        (let ((model (phpinspect--class-get-variable class "model"))
+              (priv-model (phpinspect--class-get-variable class "privModel"))
+              ;; Static variables are stored with "$" prefix
+              (relation (phpinspect--class-get-variable class "$relation"))
+              (static-relation (phpinspect--class-get-variable class 
"$staticRelation")))
+          (should model)
+          (should priv-model)
+          (should relation)
+          (should static-relation)
+
+          (let ((model-type (phpinspect--make-type
+                             :name "\\Illuminate\\Database\\Eloquent\\Model"
+                             :fully-qualified t))
+                (relation-type (phpinspect--make-type
+                             :name 
"\\Illuminate\\Database\\Eloquent\\Relations\\Relation"
+                             :fully-qualified t)))
+
+            (should (phpinspect--variable-type model))
+            (should (phpinspect--type= model-type (phpinspect--variable-type 
model)))
+            (should (phpinspect--variable-type priv-model))
+            (should (phpinspect--type= model-type (phpinspect--variable-type 
priv-model)))
+            (should (phpinspect--variable-type relation))
+            (should (phpinspect--type= relation-type 
(phpinspect--variable-type relation)))
+            (should (phpinspect--variable-type static-relation))
+            (should (phpinspect--type= relation-type 
(phpinspect--variable-type static-relation)))))))))
diff --git a/test/test-index.el b/test/test-index.el
index f984b6e76b..d1621bc8ae 100644
--- a/test/test-index.el
+++ b/test/test-index.el
@@ -291,3 +291,57 @@ function example(Firewall $wall): Thing {}")
     (should (member (phpinspect-intern-name "Thing") (alist-get 'used-types 
index)))
 
     (should (alist-get 'used-types index))))
+
+(ert-deftest phpinspect-index-typehinted-variables ()
+  (with-temp-buffer
+    (let ((project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader))))
+      (insert "<?php
+declare(strict_types=1);
+
+namespace App\\Controller\\Api\\V1;
+
+use Illuminate\\Database\\Eloquent\\Model;
+use Illuminate\\Database\\Eloquent\\Relations\\Relation;
+use Illuminate\\Support\\Facades\\Auth;
+
+class AccountStatisticsController {
+
+    public Model $model;
+    private Model $privModel;
+    static Relation $relation;
+    public static Relation $staticRelation;
+}")
+      (phpinspect-project-add-index project (phpinspect-index-current-buffer))
+
+      (let ((class (phpinspect-project-get-class
+                   project
+                    (phpinspect--make-type
+                     :name 
"\\App\\Controller\\Api\\V1\\AccountStatisticsController"
+                     :fully-qualified t))))
+        (should class)
+
+        (let ((model (phpinspect--class-get-variable class "model"))
+              (priv-model (phpinspect--class-get-variable class "privModel"))
+              ;; Static variables are stored with "$" prefix
+              (relation (phpinspect--class-get-variable class "$relation"))
+              (static-relation (phpinspect--class-get-variable class 
"$staticRelation")))
+          (should model)
+          (should priv-model)
+          (should relation)
+          (should static-relation)
+
+          (let ((model-type (phpinspect--make-type
+                             :name "\\Illuminate\\Database\\Eloquent\\Model"
+                             :fully-qualified t))
+                (relation-type (phpinspect--make-type
+                             :name 
"\\Illuminate\\Database\\Eloquent\\Relations\\Relation"
+                             :fully-qualified t)))
+
+            (should (phpinspect--variable-type model))
+            (should (phpinspect--type= model-type (phpinspect--variable-type 
model)))
+            (should (phpinspect--variable-type priv-model))
+            (should (phpinspect--type= model-type (phpinspect--variable-type 
priv-model)))
+            (should (phpinspect--variable-type relation))
+            (should (phpinspect--type= relation-type 
(phpinspect--variable-type relation)))
+            (should (phpinspect--variable-type static-relation))
+            (should (phpinspect--type= relation-type 
(phpinspect--variable-type static-relation)))))))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]