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

    Test type-resolver + make type resolving work for "blocked" namespaces
    
    Test type-resolver created from resolvecontext with:
     - File containing single namespace and no block
     - File containing one namespace with a block ('{}')
     - File containing multiple namespaces with blocks
    
    Patch `phpinspect--make-type-resolver-for-resolvecontext' to support 
namespaces with
    blocks.
---
 phpinspect.el                                      |  19 ++-
 test/fixtures/IncompleteClassBlockedNamespace.eld  |   1 +
 test/fixtures/IncompleteClassBlockedNamespace.php  |  81 ++++++++++
 .../fixtures/IncompleteClassMultipleNamespaces.eld |   1 +
 .../fixtures/IncompleteClassMultipleNamespaces.php | 164 +++++++++++++++++++++
 test/phpinspect-test.el                            |  55 ++++++-
 test/util/generate-test-data.el                    |   4 +-
 7 files changed, 318 insertions(+), 7 deletions(-)

diff --git a/phpinspect.el b/phpinspect.el
index 7743212963..23b7be484b 100644
--- a/phpinspect.el
+++ b/phpinspect.el
@@ -1513,7 +1513,8 @@ said FQN's by class name"
          (concat "\\" namespace "\\" type))
 
         ;; Clas|interface|trait name
-        (t (concat "\\" (or (assoc-default type types #'string=) (concat 
namespace "\\" type))))))
+        (t (concat "\\" (or (assoc-default type types #'string=)
+                            (concat namespace "\\" type))))))
 
 (defun phpinspect-var-annotation-p (token)
   (phpinspect-type-p token :var-annotation))
@@ -1702,6 +1703,12 @@ said FQN's by class name"
                      (extends . ,extends)
                      (implements . ,implements))))))
 
+(defsubst phpinspect-namespace-body (namespace)
+  "Return the nested tokens in NAMESPACE tokens' body.
+Accounts for namespaces that are defined with '{}' blocks."
+  (if (phpinspect-block-p (caddr namespace))
+      (cdaddr namespace)
+    (cdr namespace)))
 
 (defun phpinspect--index-classes (types classes &optional namespace indexed)
   "Index the class tokens in `classes`, using the types in `types`
@@ -2087,15 +2094,19 @@ static variables and static methods."
   (let ((namespace-or-root
          (seq-find #'phpinspect-namespace-or-root-p
                    (phpinspect--resolvecontext-enclosing-tokens
-                    resolvecontext))))
+                    resolvecontext)))
+        (namespace-name))
+    (when (phpinspect-namespace-p namespace-or-root)
+      (setq namespace-name (cadadr namespace-or-root))
+      (setq namespace-or-root (phpinspect-namespace-body namespace-or-root)))
+
       (phpinspect--make-type-resolver
        (phpinspect--uses-to-types
         (seq-filter #'phpinspect-use-p namespace-or-root))
        (seq-find #'phpinspect-class-p
                    (phpinspect--resolvecontext-enclosing-tokens
                     resolvecontext))
-       (when (phpinspect-namespace-p namespace-or-root)
-         (cadadr namespace-or-root)))))
+       namespace-name)))
 
 (defun phpinspect--get-last-statement-in-token (token)
   (setq token (cond ((phpinspect-function-p token)
diff --git a/test/fixtures/IncompleteClassBlockedNamespace.eld 
b/test/fixtures/IncompleteClassBlockedNamespace.eld
new file mode 100644
index 0000000000..bb7e112c81
--- /dev/null
+++ b/test/fixtures/IncompleteClassBlockedNamespace.eld
@@ -0,0 +1 @@
+(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) 
(:terminator ";") (:namespace (:word "App\\Controller") (:incomplete-block 
(:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) 
(:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word 
"Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) 
(:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use 
(:word "App\\Repository\\UserRepository") ( [...]
\ No newline at end of file
diff --git a/test/fixtures/IncompleteClassBlockedNamespace.php 
b/test/fixtures/IncompleteClassBlockedNamespace.php
new file mode 100644
index 0000000000..43c133c468
--- /dev/null
+++ b/test/fixtures/IncompleteClassBlockedNamespace.php
@@ -0,0 +1,81 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Controller {
+
+    use Symfony\Component\HttpFoundation\Response;
+    use App\Entity\Address;
+    use Symfony\Component\HttpFoundation\RedirectResponse;
+    use App\Repository\AddressRepository;
+    use App\Repository\UserRepository;
+    use Doctrine\ORM\EntityManagerInterface;
+    use Twig\Environment;
+    use Symfony\Component\HttpFoundation\Request;
+    use Symfony\Component\Routing\Annotation\Route;
+
+    class AddressController
+    {
+        const A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE = 'a value';
+        public const ARRAY_CONSTANT = [
+            'key' => 'value',
+            'key' => 0
+        ];
+
+        private $repo;
+        private $user_repo;
+        private $twig;
+        private $em;
+
+        public function __construct(
+            AddressRepository $repo,
+            UserRepository $user_repo,
+            Environment $twig,
+            EntityManagerInterface $em
+        ) {
+            $this->repo = $repo;
+            $this->user_repo = $user_repo;
+            $this->twig = $twig;
+            $this->em = $em;
+        }
+
+        /**
+         * @Route("/address/add", methods={"GET"})
+         */
+        public function addAddressPage(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->get('user'));
+
+            return new Response(
+                $this->twig->render('address/create.html.twig', [
+                    'user' => $user,
+                ])
+            );
+        }
+
+        /**
+         * @Route("/address/add", methods={"POST"})
+         */
+        public function addAddressAction(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->request->get('user'));
+            $address_string = $req->request->get('address');
+
+            $address = new Address($user, $address_string);
+
+            $this->em->persist($address);
+            $this->em->flush();
+
+
+            return new RedirectResponse('/user/' . $user->getLoginName() . 
'/manage');
+        }
+
+        /**
+         * @Route("/address/delete", methods={"POST"})
+         */
+        public function deleteAddressAction(Request $req): Response
+        {
+            $address = $this->repo->find($req->request->get('address'));
+
+            // This is what a while looks like to phpinspect when it parses up
+            // until "point" to complete.
+            $this->em->remove($this->em->
diff --git a/test/fixtures/IncompleteClassMultipleNamespaces.eld 
b/test/fixtures/IncompleteClassMultipleNamespaces.eld
new file mode 100644
index 0000000000..7d333155b3
--- /dev/null
+++ b/test/fixtures/IncompleteClassMultipleNamespaces.eld
@@ -0,0 +1 @@
+(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) 
(:terminator ";") (:namespace (:word "Circus\\Artist") (:block (:use (:word 
"Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word 
"App\\Entity\\Address") (:terminator ";")) (:use (:word 
"Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) 
(:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use 
(:word "App\\Repository\\UserRepository") (:terminator  [...]
\ No newline at end of file
diff --git a/test/fixtures/IncompleteClassMultipleNamespaces.php 
b/test/fixtures/IncompleteClassMultipleNamespaces.php
new file mode 100644
index 0000000000..15a705741d
--- /dev/null
+++ b/test/fixtures/IncompleteClassMultipleNamespaces.php
@@ -0,0 +1,164 @@
+<?php
+declare(strict_types=1);
+
+namespace Circus\Artist {
+
+    use Symfony\Component\HttpFoundation\Response;
+    use App\Entity\Address;
+    use Symfony\Component\HttpFoundation\RedirectResponse;
+    use App\Repository\AddressRepository;
+    use App\Repository\UserRepository;
+    use Doctrine\ORM\EntityManagerInterface;
+    use Twig\Environment;
+    use Symfony\Component\HttpFoundation\Request;
+    use Symfony\Component\Routing\Annotation\Route;
+
+    class AddressController
+    {
+        const A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE = 'a value';
+        public const ARRAY_CONSTANT = [
+            'key' => 'value',
+            'key' => 0
+        ];
+
+        private $repo;
+        private $user_repo;
+        private $twig;
+        private $em;
+
+        public function __construct(
+            AddressRepository $repo,
+            UserRepository $user_repo,
+            Environment $twig,
+            EntityManagerInterface $em
+        ) {
+            $this->repo = $repo;
+            $this->user_repo = $user_repo;
+            $this->twig = $twig;
+            $this->em = $em;
+        }
+
+        /**
+         * @Route("/address/add", methods={"GET"})
+         */
+        public function addAddressPage(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->get('user'));
+
+            return new Response(
+                $this->twig->render('address/create.html.twig', [
+                    'user' => $user,
+                ])
+            );
+        }
+
+        /**
+         * @Route("/address/add", methods={"POST"})
+         */
+        public function addAddressAction(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->request->get('user'));
+            $address_string = $req->request->get('address');
+
+            $address = new Address($user, $address_string);
+
+            $this->em->persist($address);
+            $this->em->flush();
+
+
+            return new RedirectResponse('/user/' . $user->getLoginName() . 
'/manage');
+        }
+
+        /**
+         * @Route("/address/delete", methods={"POST"})
+         */
+        public function deleteAddressAction(Request $req): Response
+        {
+            $address = $this->repo->find($req->request->get('address'));
+
+            $this->em->remove($address);
+            $this->em->flush();
+
+            return new RedirectResponse('/user/' . 
$address->getUser()->getLoginName() . '/manage');
+        }
+    }
+}
+
+namespace App\Controller {
+
+    use Symfony\Component\HttpFoundation\Response;
+    use App\Entity\Address;
+    use Symfony\Component\HttpFoundation\RedirectResponse;
+    use App\Repository\AddressRepository;
+    use App\Repository\UserRepository;
+    use Doctrine\ORM\EntityManagerInterface;
+    use Twig\Environment;
+    use Symfony\Component\HttpFoundation\Request;
+    use Symfony\Component\Routing\Annotation\Route;
+
+    class AddressController
+    {
+        const A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE = 'a value';
+        public const ARRAY_CONSTANT = [
+            'key' => 'value',
+            'key' => 0
+        ];
+
+        private $repo;
+        private $user_repo;
+        private $twig;
+        private $em;
+
+        public function __construct(
+            AddressRepository $repo,
+            UserRepository $user_repo,
+            Environment $twig,
+            EntityManagerInterface $em
+        ) {
+            $this->repo = $repo;
+            $this->user_repo = $user_repo;
+            $this->twig = $twig;
+            $this->em = $em;
+        }
+
+        /**
+         * @Route("/address/add", methods={"GET"})
+         */
+        public function addAddressPage(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->get('user'));
+
+            return new Response(
+                $this->twig->render('address/create.html.twig', [
+                    'user' => $user,
+                ])
+            );
+        }
+
+        /**
+         * @Route("/address/add", methods={"POST"})
+         */
+        public function addAddressAction(Request $req): Response
+        {
+            $user = $this->user_repo->findOne($req->request->get('user'));
+            $address_string = $req->request->get('address');
+
+            $address = new Address($user, $address_string);
+
+            $this->em->persist($address);
+            $this->em->flush();
+
+
+            return new RedirectResponse('/user/' . $user->getLoginName() . 
'/manage');
+        }
+
+        /**
+         * @Route("/address/delete", methods={"POST"})
+         */
+        public function deleteAddressAction(Request $req): Response
+        {
+            $address = $this->repo->find($req->request->get('address'));
+
+            // This is what a while looks like to phpinspect when it parses up
+            // until "point" to complete.
+            $this->em->remove($this->em->
diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el
index 8cdb154c9b..44c93668b5 100644
--- a/test/phpinspect-test.el
+++ b/test/phpinspect-test.el
@@ -127,7 +127,7 @@
            (phpinspect-test-read-fixture-data
             "class-index-1-2-undestructive-merge"))))
 
-(ert-deftest phpinspect-find-innermost-incomplete-nested-token ()
+(ert-deftest phpinspect--get-resolvecontext ()
   (let ((resolvecontext (phpinspect--get-resolvecontext
                          (phpinspect-test-read-fixture-data 
"IncompleteClass"))))
 
@@ -150,7 +150,58 @@
 
     (should (phpinspect-incomplete-class-p
              (cadddr (phpinspect--resolvecontext-enclosing-tokens
-                     resolvecontext))))))
+                      resolvecontext))))))
+
+(ert-deftest phpinspect-type-resolver-for-resolvecontext ()
+  (let* ((resolvecontext (phpinspect--get-resolvecontext
+                         (phpinspect-test-read-fixture-data 
"IncompleteClass")))
+         (type-resolver (phpinspect--make-type-resolver-for-resolvecontext
+                         resolvecontext)))
+
+    (should (string= "\\array" (funcall type-resolver "array")))
+    (should (string= "\\array" (funcall type-resolver "\\array")))
+    (should (string= "\\Symfony\\Component\\HttpFoundation\\Response"
+                     (funcall type-resolver "Response")))
+    (should (string= "\\Response" (funcall type-resolver "\\Response")))
+    (should (string= "\\App\\Controller\\GastonLagaffe"
+                     (funcall type-resolver "GastonLagaffe")))
+    (should (string= "\\App\\Controller\\Dupuis\\GastonLagaffe"
+                     (funcall type-resolver "Dupuis\\GastonLagaffe")))))
+
+(ert-deftest phpinspect-type-resolver-for-resolvecontext-namespace-block ()
+  (let* ((resolvecontext (phpinspect--get-resolvecontext
+                          (phpinspect-test-read-fixture-data
+                           "IncompleteClassBlockedNamespace")))
+         (type-resolver (phpinspect--make-type-resolver-for-resolvecontext
+                         resolvecontext)))
+
+    (should (string= "\\array" (funcall type-resolver "array")))
+    (should (string= "\\array" (funcall type-resolver "\\array")))
+    (should (string= "\\Symfony\\Component\\HttpFoundation\\Response"
+                     (funcall type-resolver "Response")))
+    (should (string= "\\Response" (funcall type-resolver "\\Response")))
+    (should (string= "\\App\\Controller\\GastonLagaffe"
+                     (funcall type-resolver "GastonLagaffe")))
+    (should (string= "\\App\\Controller\\Dupuis\\GastonLagaffe"
+                     (funcall type-resolver "Dupuis\\GastonLagaffe")))))
+
+(ert-deftest 
phpinspect-type-resolver-for-resolvecontext-multiple-namespace-blocks ()
+  (let* ((resolvecontext (phpinspect--get-resolvecontext
+                          (phpinspect-test-read-fixture-data
+                           "IncompleteClassMultipleNamespaces")))
+         (type-resolver (phpinspect--make-type-resolver-for-resolvecontext
+                         resolvecontext)))
+
+    (should (string= "\\array" (funcall type-resolver "array")))
+    (should (string= "\\array" (funcall type-resolver "\\array")))
+    (should (string= "\\Symfony\\Component\\HttpFoundation\\Response"
+                     (funcall type-resolver "Response")))
+    (should (string= "\\Response" (funcall type-resolver "\\Response")))
+    (should (string= "\\App\\Controller\\GastonLagaffe"
+                     (funcall type-resolver "GastonLagaffe")))
+    (should (string= "\\App\\Controller\\Dupuis\\GastonLagaffe"
+                     (funcall type-resolver "Dupuis\\GastonLagaffe")))))
+
 
 (provide 'phpinspect-test)
 ;;; phpinspect-test.el ends here
diff --git a/test/util/generate-test-data.el b/test/util/generate-test-data.el
index defed76e24..4da44047a0 100644
--- a/test/util/generate-test-data.el
+++ b/test/util/generate-test-data.el
@@ -3,7 +3,9 @@
 
 (let ((here (file-name-directory
              (or load-file-name
-                 buffer-file-name))))
+                 buffer-file-name)))
+      (print-length 1000)
+      (print-level 1000))
   (dolist (file (directory-files (concat here "/../fixtures" ) t "\\.php$"))
     (with-temp-buffer
       (insert-file-contents-literally file)

Reply via email to