From 8296641ba965f0d418a999e7cd7f7d41f9712a7a Mon Sep 17 00:00:00 2001 From: Hugo Thunnissen Date: Wed, 14 Aug 2024 09:09:12 +0200 Subject: [PATCH] Implement removal of unused imports in `phpinspect-fix-imports' --- phpinspect-imports.el | 62 ++++++++++++++++++++++++++++++++++++++----- phpinspect-meta.el | 6 +++++ phpinspect-type.el | 15 ++++++----- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/phpinspect-imports.el b/phpinspect-imports.el index fbb0cf4..ec5fd76 100644 --- a/phpinspect-imports.el +++ b/phpinspect-imports.el @@ -111,14 +111,56 @@ buffer position to insert the use statement at." (defalias 'phpinspect-fix-uses-interactive #'phpinspect-fix-imports "Alias for backwards compatibility") +(defsubst phpinspect-namespace-meta-body (namespace-meta) + "Return the token metadata of NAMESPACE-META's body. +More specifically, returns the token itself if it is a namespace +without block. If the namespace is defined with a block ('{}'), +NAMESPACE-META itself is returned without alterations." + (if (phpinspect-block-p (caddr (phpinspect-meta-token namespace-meta))) + (phpinspect-meta-find-first-child-matching-token namespace-meta #'phpinspect-block-p) + namespace-meta)) + +(defun phpinspect-find-use-statement-for-import (parent-token import-type) + (phpinspect-meta-find-first-child-matching-token + (if (phpinspect-namespace-p (phpinspect-meta-token parent-token)) + (phpinspect-namespace-meta-body parent-token) + parent-token) + (lambda (token) + (and (phpinspect-use-p token) + (phpinspect-word-p (cadr token)) + (phpinspect--type= import-type (phpinspect-use-name-to-type (cadadr token))))))) + +(defun phpinspect-remove-unneeded-use-statements (types buffer imports parent-token) + (let ((namespace (phpinspect-meta-find-parent-matching-token parent-token #'phpinspect-namespace-p))) + (dolist (import imports) + (unless (member (car import) types) + (when-let ((use-meta (phpinspect-find-use-statement-for-import namespace (cdr import)))) + (let ((start-point (phpinspect-meta-start use-meta)) + (use-before (phpinspect-meta-find-left-sibling use-meta))) + (if (phpinspect-use-p (phpinspect-meta-token use-before)) + ;; left-sibling is another use statement, remove all preceding whitespace + (setq start-point (- start-point (length (phpinspect-meta-whitespace-before use-meta)))) + ;; left-sibling isn't a use statement, just remove a newline if + ;; any whitespace is present + (when (length> (phpinspect-meta-whitespace-before use-meta) 0) + (setq start-point (- start-point 1)))) + + (delete-region start-point (phpinspect-meta-end use-meta)) + (phpinspect-buffer-parse buffer 'no-interrupt))))))) + (defun phpinspect-add-use-statements-for-missing-types (types buffer imports project parent-token) - (let (namespace namespace-name) - (dolist (type types) - (setq namespace (phpinspect-meta-find-parent-matching-token - parent-token #'phpinspect-namespace-p) - namespace-name (if namespace + "Add use statements to BUFFER for TYPES if not already included in IMPORTS. + +Uses PROJECT's autoloader to determine available types for import. + +PARENT-TOKEN must be a `token-meta' object and is used to +determine the scope of the imports (global or local namespace)." + (let* ((namespace (phpinspect-meta-find-parent-matching-token + parent-token #'phpinspect-namespace-p)) + (namespace-name (if namespace (phpinspect-namespace-name (phpinspect-meta-token namespace)) - "")) + ""))) + (dolist (type types) ;; Add use statements for types that aren't imported or already referenced ;; with a fully qualified name. (unless (or (or (alist-get type imports)) @@ -149,6 +191,9 @@ that there are import (\"use\") statements for them." (phpinspect-add-use-statements-for-missing-types used-types buffer imports project (phpinspect-buffer-root-meta buffer)) + (phpinspect-remove-unneeded-use-statements + used-types buffer imports (phpinspect-buffer-root-meta buffer)) + (dolist (class classes) (let* ((class-imports (alist-get 'imports class)) (used-types (alist-get 'used-types class)) @@ -164,6 +209,9 @@ that there are import (\"use\") statements for them." (error "Unable to find token for class %s" class-name)) (phpinspect-add-use-statements-for-missing-types - used-types buffer (append imports class-imports) project token-meta)))))) + used-types buffer (append imports class-imports) project token-meta) + + (phpinspect-remove-unneeded-use-statements + used-types buffer class-imports token-meta)))))) (provide 'phpinspect-imports) diff --git a/phpinspect-meta.el b/phpinspect-meta.el index 906fe7c..53ba954 100644 --- a/phpinspect-meta.el +++ b/phpinspect-meta.el @@ -270,6 +270,12 @@ (when (funcall predicate child) (throw 'return child))))) +(cl-defmethod phpinspect-meta-find-first-child-matching-token ((meta (head meta)) predicate) + (catch 'return + (phpinspect-splayt-traverse-lr (child (phpinspect-meta-children meta)) + (when (funcall predicate (phpinspect-meta-token child)) + (throw 'return child))))) + (cl-defmethod phpinspect-meta-last-child ((meta (head meta))) (phpinspect-meta-find-child-before meta (phpinspect-meta-end meta))) diff --git a/phpinspect-type.el b/phpinspect-type.el index 5ffa0fa..bee9a40 100644 --- a/phpinspect-type.el +++ b/phpinspect-type.el @@ -299,12 +299,15 @@ mutability of the variable") (not (or (phpinspect--variable-static-p variable) (phpinspect--variable-const-p variable)))) -(defun phpinspect--use-to-type (use) +(defun phpinspect-use-name-to-type (fqn-string) + (phpinspect--make-type :name (if (string-match "^\\\\" fqn-string) + fqn-string + (concat "\\" fqn-string)) + :fully-qualified t)) + +(defun phpinspect--use-to-type-cons (use) (let* ((fqn (cadr (cadr use))) - (type (phpinspect--make-type :name (if (string-match "^\\\\" fqn) - fqn - (concat "\\" fqn)) - :fully-qualified t)) + (type (phpinspect-use-name-to-type fqn)) (type-name (if (and (phpinspect-word-p (caddr use)) (string= "as" (cadr (caddr use)))) (cadr (cadddr use)) @@ -313,7 +316,7 @@ mutability of the variable") (cons (phpinspect-intern-name type-name) type))) (defun phpinspect--uses-to-types (uses) - (mapcar #'phpinspect--use-to-type uses)) + (mapcar #'phpinspect--use-to-type-cons uses)) (defun phpinspect--get-class-name-from-token (class-token) (let ((subtoken (seq-find (lambda (word)