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

Issue:
Regression in FQCN shortening in 3.17
## Bug report
### Description
FQCNs in parameters are not being shortened in 3.17. Possibly caused by #6923

### Runtime version
```
$ php-cs-fixer -V
PHP CS Fixer 3.17.0 (3f0ed86) Brazilian Kangaroo by Fabien Potencier and Dariusz Ruminski.
PHP runtime: 8.2.6
```

### Used command
```
php-cs-fixer fix test.php
```

### Configuration file
<details>
<summary>Expand</summary>

```php
<?php

$finder = PhpCsFixer\Finder::create()
	->in(__DIR__ . '/src');

return (new PhpCsFixer\Config)
	->setRiskyAllowed(true)
	->setRules([
		'align_multiline_comment' => [
			'comment_type' => 'phpdocs_only'
		],
		'array_indentation' => true,
		'array_syntax' => [
			'syntax' => 'short'
		],
		'binary_operator_spaces' => [
			'default' => 'single_space'
		],
		'blank_line_after_namespace' => true,
		'blank_line_after_opening_tag' => true,
		'blank_line_before_statement' => [
			'statements' => [
				'declare'
			]
		],
		'cast_spaces' => [
			'space' => 'single'
		],
		'concat_space' => [
			'spacing' => 'one'
		],
		'declare_strict_types' => true,
		'elseif' => true,
		'fully_qualified_strict_types' => true,
		'global_namespace_import' => [
			'import_constants' => true,
			'import_functions' => true,
			'import_classes' => null,
		],
		'header_comment' => [
			'header' => <<<COMMENT
This file is part of BedrockProtocol.
Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>

BedrockProtocol is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
COMMENT,
			'location' => 'after_open',
		],
		'indentation_type' => true,
		'native_function_invocation' => [
			'scope' => 'namespaced',
			'include' => ['@all'],
		],
		'no_closing_tag' => true,
		'no_empty_phpdoc' => true,
		'no_extra_blank_lines' => true,
		'no_superfluous_phpdoc_tags' => [
			'allow_mixed' => true,
		],
		'no_trailing_whitespace' => true,
		'no_trailing_whitespace_in_comment' => true,
		'no_whitespace_in_blank_line' => true,
		'no_unused_imports' => true,
		'ordered_imports' => [
			'imports_order' => [
				'class',
				'function',
				'const',
			],
			'sort_algorithm' => 'alpha'
		],
		'phpdoc_line_span' => [
			'property' => 'single',
			'method' => null,
			'const' => null
		],
		'phpdoc_trim' => true,
		'phpdoc_trim_consecutive_blank_line_separation' => true,
		'return_type_declaration' => [
			'space_before' => 'one'
		],
		'single_blank_line_at_eof' => true,
		'single_import_per_statement' => true,
		'strict_param' => true,
		'unary_operator_spaces' => true,
	])
	->setFinder($finder)
	->setIndent("\t")
	->setLineEnding("\n");
```

</details>

### Code snippet that reproduces the problem
Input:
```php
<?php

/*
 * This file is part of BedrockProtocol.
 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
 *
 * BedrockProtocol is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

declare(strict_types=1);

namespace pocketmine\network\mcpe\protocol;

use pocketmine\math\Vector3;

class UpdateClientInputLocksPacket{
	private int $flags;
	private Vector3 $position;

	/**
	 * @generate-create-func
	 */
	public static function create(int $flags, \pocketmine\math\Vector3 $position) : self{
		$result = new self;
		$result->flags = $flags;
		$result->position = $position;
		return $result;
	}
}
```

Output of 3.16 (expected):
```php
<?php

/*
 * This file is part of BedrockProtocol.
 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
 *
 * BedrockProtocol is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

declare(strict_types=1);

namespace pocketmine\network\mcpe\protocol;

use pocketmine\math\Vector3;

class UpdateClientInputLocksPacket{
	private int $flags;
	private Vector3 $position;

	/**
	 * @generate-create-func
	 */
	public static function create(int $flags, Vector3 $position) : self{
		$result = new self;
		$result->flags = $flags;
		$result->position = $position;
		return $result;
	}
}
```

Output of 3.17 (unexpected, unchanged):
```php
<?php

/*
 * This file is part of BedrockProtocol.
 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
 *
 * BedrockProtocol is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

declare(strict_types=1);

namespace pocketmine\network\mcpe\protocol;

use pocketmine\math\Vector3;

class UpdateClientInputLocksPacket{
	private int $flags;
	private Vector3 $position;

	/**
	 * @generate-create-func
	 */
	public static function create(int $flags, \pocketmine\math\Vector3 $position) : self{
		$result = new self;
		$result->flags = $flags;
		$result->position = $position;
		return $result;
	}
}

```



Discussion:
Can't reproduce with provided snippet and config, Fixer does not apply any change to the example code. @dktapps can you run Fixer with `--verbose --diff --dry-run` and provide the output, especially part with list of fixers that are going to be applied?
> Fixer does not apply any change to the example code.

That's the problem. It's supposed to change it but doesn't as of 3.17.

```
$ php-cs-fixer fix test.php --dry-run --verbose --diff
PHP CS Fixer 3.17.0 (3f0ed86) Brazilian Kangaroo by Fabien Potencier and Dariusz Ruminski.
PHP runtime: 8.2.6
Loaded config default from "C:\Users\dylan-work\Documents\projects\pocketmine-mp\deps\BedrockProtocol\.php-cs-fixer.php".
Using cache file ".php-cs-fixer.cache".
Paths from configuration file have been overridden by paths provided as command arguments.
.                                                                                                                                                                                                                                                                1 / 1 (100%)
Legend: .-no changes, F-fixed, S-skipped (cached or empty file), I-invalid file syntax (file ignored), E-error

Found 0 of 1 files that can be fixed in 0.011 seconds, 10.000 MB memory used
```
Result from 3.16.0 (this is what I expected):
```
$ php ./php-cs-fixer-3.16.phar fix test.php --dry-run --verbose --diff
PHP CS Fixer 3.16.0 (d40f943) BoY42 by Fabien Potencier and Dariusz Ruminski.
PHP runtime: 8.2.6
Loaded config default from "C:\Users\dylan-work\Documents\projects\pocketmine-mp\deps\BedrockProtocol\.php-cs-fixer.php".
Using cache file ".php-cs-fixer.cache".
Paths from configuration file have been overridden by paths provided as command arguments.
F                                                                                                                                                                                                                                                                1 / 1 (100%)
Legend: .-no changes, F-fixed, S-skipped (cached or empty file), I-invalid file syntax (file ignored), E-error
   1) test.php (fully_qualified_strict_types)
      ---------- begin diff ----------
--- C:\Users\dylan-work\Documents\projects\pocketmine-mp\deps\BedrockProtocol\test.php
+++ C:\Users\dylan-work\Documents\projects\pocketmine-mp\deps\BedrockProtocol\test.php
@@ -23,7 +23,7 @@
        /**
         * @generate-create-func
         */
-       public static function create(int $flags, \pocketmine\math\Vector3 $position) : self{
+       public static function create(int $flags, Vector3 $position) : self{
                $result = new self;
                $result->flags = $flags;
                $result->position = $position;

      ----------- end diff -----------


Found 1 of 1 files that can be fixed in 0.012 seconds, 10.000 MB memory used
```
Thanks, I somehow understood it the other way. It was actually introduced in #6898 [here](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/6898/files#diff-fb97bed889611f9efcaeed9822ab2e134d3854a1ffcc4d0f3664c55b1dff63c8R158) (CC: @edsrzf).
> Thanks, I somehow understood it the other way. It was actually introduced in #6898 [here](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/6898/files#diff-fb97bed889611f9efcaeed9822ab2e134d3854a1ffcc4d0f3664c55b1dff63c8R158) (CC: @edsrzf).

Yeah, I realized the diff I sent in the OP might be confusing. I've updated the OP to make it clearer.
FYI: I have a fix for that, but I want to prepare some additional test cases, will finish it later in the evening.
No rush, thanks for your efforts!



Patch:
diff --git a/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php b/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
index 707d849900b..83024e216db 100644
--- a/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
+++ b/src/Fixer/Import/FullyQualifiedStrictTypesFixer.php
@@ -141,15 +141,13 @@ private function replaceByShortType(Tokens $tokens, TypeAnalysis $type, array $u
         $namespaceNameLength = \strlen($namespaceName);
         $types = $this->getTypes($tokens, $typeStartIndex, $type->getEndIndex());
 
-        $prefix = $namespaceName ? '\\'.$namespaceName.'\\' : '\\';
         foreach ($types as $typeName => [$startIndex, $endIndex]) {
-            $typeNameLower = strtolower($typeName);
-            if (!str_starts_with($typeNameLower, $prefix)) {
-                continue; // no shorter type possible
+            if (!str_starts_with($typeName, '\\')) {
+                continue; // Not a FQCN, no shorter type possible
             }
 
             $typeName = substr($typeName, 1);
-            $typeNameLower = substr($typeNameLower, 1);
+            $typeNameLower = strtolower($typeName);
 
             if (isset($uses[$typeNameLower])) {
                 // if the type without leading "\" equals any of the full "uses" long names, it can be replaced with the short one
@@ -163,7 +161,11 @@ private function replaceByShortType(Tokens $tokens, TypeAnalysis $type, array $u
                 }
 
                 $tokens->overrideRange($startIndex, $endIndex, $this->namespacedStringToTokens($typeName));
-            } elseif ($typeNameLower !== $namespaceName && str_starts_with($typeNameLower, $namespaceName)) {
+            } elseif (!str_contains($typeName, '\\')) {
+                // If we're NOT in the global namespace, there's no related import,
+                // AND used type is from global namespace, then it can't be shortened.
+                continue;
+            } elseif ($typeNameLower !== $namespaceName && str_starts_with($typeNameLower, $namespaceName.'\\')) {
                 // if the type starts with namespace and the type is not the same as the namespace it can be shortened
                 $typeNameShort = substr($typeName, $namespaceNameLength + 1);
 


Test patch:
diff --git a/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php b/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
index 16a4db326f6..b23f872bbfc 100644
--- a/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
+++ b/tests/Fixer/Import/FullyQualifiedStrictTypesFixerTest.php
@@ -28,7 +28,7 @@ final class FullyQualifiedStrictTypesFixerTest extends AbstractFixerTestCase
     /**
      * @dataProvider provideNewLogicCases
      */
-    public function testNewLogic(string $expected, ?string $input): void
+    public function testNewLogic(string $expected, ?string $input = null): void
     {
         $this->doTest($expected, $input);
     }
@@ -104,8 +104,31 @@ function A(\A\B\C\D\Foo $fix, \X\B\C\D\E\Bar $doNotFix) {}
             '<?php use \A\Exception; function foo(\A\Exception $e) {}',
         ];
 
-        yield 'common prefix' => [
+        yield 'common prefix 1' => [
             '<?php namespace Foo; function foo(\FooBar $v): \FooBar {}',
+        ];
+
+        yield 'common prefix 2' => [
+            '<?php namespace Foo; function foo(\FooBar\Baz $v): \FooBar {}',
+        ];
+
+        yield 'issue #7025 - non-empty namespace, import and FQCN in argument' => [
+            '<?php namespace foo\bar\baz;
+
+use foo\baz\buzz;
+
+class A {
+    public function b(buzz $buzz): void {
+    }
+}',
+            '<?php namespace foo\bar\baz;
+
+use foo\baz\buzz;
+
+class A {
+    public function b(\foo\baz\buzz $buzz): void {
+    }
+}',
             null,
         ];
     }