Repository: PHP-CS-Fixer/PHP-CS-Fixer (php)

Issue:
Support for object initialisation in `fully_qualified_strict_types`
## Rule request

`fully_qualified_strict_types` should shorten FQCN to imported symbols in `new \Foo\Bar()`-like cases.

### Code snippet that shows how the rule would fix PHP code

```diff
<?php

use Foo\Bar;
use Some\CustomException;

-$a = new \Foo\Bar();
+$a = new Bar();

-throw new \Some\CustomException('ohoh');
+throw new CustomException('ohoh');
```
Support for shortening FQCN for trait usage
```diff
<?php

use Bar\Baz;

class Foo {
-    use \Bar\Baz;
+    use Baz;
}
```

_Originally posted by @mvorisek in https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/7459#discussion_r1403177320_
            

Pull request description:
feat: Support new/instanceof/use trait in `fully_qualified_strict_types`
fix #5594
fix #7568

Patch:
diff --git a/doc/rules/import/fully_qualified_strict_types.rst b/doc/rules/import/fully_qualified_strict_types.rst
index 5726ee8d2..cfe625009 100644
--- a/doc/rules/import/fully_qualified_strict_types.rst
+++ b/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
 -------------
diff --git a/doc/rules/index.rst b/doc/rules/index.rst
index b4d46a193..ce6a4e995 100644
--- a/doc/rules/index.rst
+++ b/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.
diff --git a/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php b/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
index 9dd1b8eb8..b953ed8fe 100644
--- a/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
+++ b/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 @@ public function getPriority(): int
     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 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
                 } 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 @@ private function fixCatch(Tokens $tokens, int $index, array &$uses, string $name
     /**
      * @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 @@ private function fixClassStaticAccess(Tokens $tokens, int $index, array &$uses,
         }
     }
 
+    /**
+     * @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


Test patch:
diff --git a/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php b/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
index a58291f63..26fabdad1 100644
--- a/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
+++ b/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
@@ -312,6 +312,40 @@ class SomeClass extends \Foo\Bar\A implements \Foo\Bar\Izumi, A, \A\B, \Foo\Bar\
             ['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 @@ function A(\Z\B\C\DE\Foo $fix) {}
 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 @@ public function foo(): FunctionReturnType
         } catch (CaughtThrowable $e) {}
     }
 }
+
+new NewClass();
+
+if ($a instanceof InstanceOfClass) { return false; }
             ',
             '<?php
 
@@ -412,6 +452,10 @@ public function foo(): \Other\FunctionReturnType
         } catch (\Other\CaughtThrowable $e) {}
     }
 }
+
+new \Other\NewClass();
+
+if ($a instanceof \Other\InstanceOfClass) { return false; }
             ',
             ['import_symbols' => true],
         ];