Browse Source

feat: Support new/instanceof/use trait in `fully_qualified_strict_types` (#7653)

Michael Voříšek 1 year ago
parent
commit
f1500e4604

+ 1 - 3
doc/rules/import/fully_qualified_strict_types.rst

@@ -3,9 +3,7 @@ Rule ``fully_qualified_strict_types``
 =====================================
 
 Removes the leading part of fully qualified symbol references if a given symbol
-is imported or belongs to the current namespace. Fixes function arguments,
-exceptions in ``catch`` block, ``extend`` and ``implements`` of classes and
-interfaces.
+is imported or belongs to the current namespace.
 
 Configuration
 -------------

+ 1 - 1
doc/rules/index.rst

@@ -431,7 +431,7 @@ Import
 
 - `fully_qualified_strict_types <./import/fully_qualified_strict_types.rst>`_
 
-  Removes the leading part of fully qualified symbol references if a given symbol is imported or belongs to the current namespace. Fixes function arguments, exceptions in ``catch`` block, ``extend`` and ``implements`` of classes and interfaces.
+  Removes the leading part of fully qualified symbol references if a given symbol is imported or belongs to the current namespace.
 - `global_namespace_import <./import/global_namespace_import.rst>`_
 
   Imports or fully qualifies global classes/functions/constants.

+ 33 - 7
src/Fixer/Import/FullyQualifiedStrictTypesFixer.php

@@ -52,7 +52,7 @@ final class FullyQualifiedStrictTypesFixer extends AbstractFixer implements Conf
     public function getDefinition(): FixerDefinitionInterface
     {
         return new FixerDefinition(
-            'Removes the leading part of fully qualified symbol references if a given symbol is imported or belongs to the current namespace. Fixes function arguments, exceptions in `catch` block, `extend` and `implements` of classes and interfaces.',
+            'Removes the leading part of fully qualified symbol references if a given symbol is imported or belongs to the current namespace.',
             [
                 new CodeSample(
                     '<?php
@@ -162,12 +162,15 @@ class Foo extends \Other\BaseClass implements \Other\Interface1, \Other\Interfac
     public function isCandidate(Tokens $tokens): bool
     {
         return $tokens->isAnyTokenKindsFound([
-            T_FUNCTION,
-            T_DOC_COMMENT,
-            T_IMPLEMENTS,
-            T_EXTENDS,
+            CT::T_USE_TRAIT,
             T_CATCH,
             T_DOUBLE_COLON,
+            T_DOC_COMMENT,
+            T_EXTENDS,
+            T_FUNCTION,
+            T_IMPLEMENTS,
+            T_INSTANCEOF,
+            T_NEW,
         ]);
     }
 
@@ -242,7 +245,9 @@ class Foo extends \Other\BaseClass implements \Other\Interface1, \Other\Interfac
                 } elseif ($tokens[$index]->isGivenKind(T_CATCH)) {
                     $this->fixCatch($tokens, $index, $uses, $namespaceName);
                 } elseif ($tokens[$index]->isGivenKind(T_DOUBLE_COLON)) {
-                    $this->fixClassStaticAccess($tokens, $index, $uses, $namespaceName);
+                    $this->fixPrevName($tokens, $index, $uses, $namespaceName);
+                } elseif ($tokens[$index]->isGivenKind([T_INSTANCEOF, T_NEW, CT::T_USE_TRAIT])) {
+                    $this->fixNextName($tokens, $index, $uses, $namespaceName);
                 }
 
                 if ($tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
@@ -390,7 +395,7 @@ class Foo extends \Other\BaseClass implements \Other\Interface1, \Other\Interfac
     /**
      * @param array<string, string> $uses
      */
-    private function fixClassStaticAccess(Tokens $tokens, int $index, array &$uses, string $namespaceName): void
+    private function fixPrevName(Tokens $tokens, int $index, array &$uses, string $namespaceName): void
     {
         $classConstantRef = ['content' => '', 'tokens' => []];
 
@@ -409,6 +414,27 @@ class Foo extends \Other\BaseClass implements \Other\Interface1, \Other\Interfac
         }
     }
 
+    /**
+     * @param array<string, string> $uses
+     */
+    private function fixNextName(Tokens $tokens, int $index, array &$uses, string $namespaceName): void
+    {
+        $classConstantRef = ['content' => '', 'tokens' => []];
+
+        while (true) {
+            $index = $tokens->getNextMeaningfulToken($index);
+
+            if ($tokens[$index]->equalsAny([[T_STRING], [T_NS_SEPARATOR]])) {
+                $classConstantRef['tokens'][] = $index;
+                $classConstantRef['content'] .= $tokens[$index]->getContent();
+            } else {
+                $this->shortenClassIfPossible($tokens, $classConstantRef, $uses, $namespaceName);
+
+                break;
+            }
+        }
+    }
+
     /**
      * @param array{content: string, tokens: array<int>} $class
      * @param array<string, string>                      $uses

+ 44 - 0
tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php

@@ -312,6 +312,40 @@ namespace B {
             ['leading_backslash_in_global_namespace' => true],
         ];
 
+        yield 'new class' => [
+            '<?php use A\B; new B();',
+            '<?php use A\B; new \A\B();',
+        ];
+
+        yield 'new class namespaced' => [
+            '<?php namespace B; new A();',
+            '<?php namespace B; new \B\A();',
+        ];
+
+        yield 'new class not imported' => [
+            '<?php new \A\B(); new A\B();',
+        ];
+
+        yield 'instanceof' => [
+            '<?php use A\B; $res = $v instanceof B;',
+            '<?php use A\B; $res = $v instanceof \A\B;',
+        ];
+
+        yield 'instanceof namespaced' => [
+            '<?php namespace B; $res = ($v->obj()) instanceof A;',
+            '<?php namespace B; $res = ($v->obj()) instanceof \B\A;',
+        ];
+
+        yield 'use trait simple' => [
+            '<?php use A\B; class Foo { use B; };',
+            '<?php use A\B; class Foo { use \A\B; };',
+        ];
+
+        yield 'use trait complex' => [
+            '<?php use A\B; class Foo { use \A\C; use \D; use B { B::bar as baz; } };',
+            '<?php use A\B; class Foo { use \A\C; use \D; use \A\B { \A\B::bar as baz; } };',
+        ];
+
         yield 'starts with but not full name extends' => [
             '<?php namespace a\abcd;
 class Foo extends \a\abcdTest { }',
@@ -378,8 +412,10 @@ use Other\BaseClass;
 use Other\CaughtThrowable;
 use Other\FunctionArgument;
 use Other\FunctionReturnType;
+use Other\InstanceOfClass;
 use Other\Interface1;
 use Other\Interface2;
+use Other\NewClass;
 use Other\PropertyPhpDoc;
 use Other\StaticFunctionCall;
 
@@ -395,6 +431,10 @@ class Foo extends BaseClass implements Interface1, Interface2
         } catch (CaughtThrowable $e) {}
     }
 }
+
+new NewClass();
+
+if ($a instanceof InstanceOfClass) { return false; }
             ',
             '<?php
 
@@ -412,6 +452,10 @@ class Foo extends \Other\BaseClass implements \Other\Interface1, \Other\Interfac
         } catch (\Other\CaughtThrowable $e) {}
     }
 }
+
+new \Other\NewClass();
+
+if ($a instanceof \Other\InstanceOfClass) { return false; }
             ',
             ['import_symbols' => true],
         ];