* Dariusz Rumiński * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Tests\Fixer\Semicolon; use PhpCsFixer\Fixer\Semicolon\MultilineWhitespaceBeforeSemicolonsFixer; use PhpCsFixer\Tests\Test\AbstractFixerTestCase; use PhpCsFixer\WhitespacesFixerConfig; /** * @author John Kelly * @author Graham Campbell * @author Dariusz Rumiński * @author Egidijus Girčys * * @internal * * @covers \PhpCsFixer\Fixer\Semicolon\MultilineWhitespaceBeforeSemicolonsFixer * * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Semicolon\MultilineWhitespaceBeforeSemicolonsFixer> * * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\Semicolon\MultilineWhitespaceBeforeSemicolonsFixer */ final class MultilineWhitespaceBeforeSemicolonsFixerTest extends AbstractFixerTestCase { /** * @dataProvider provideFixMultiLineWhitespaceCases */ public function testFixMultiLineWhitespace(string $expected, ?string $input = null): void { $this->fixer->configure(['strategy' => MultilineWhitespaceBeforeSemicolonsFixer::STRATEGY_NO_MULTI_LINE]); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFixMultiLineWhitespaceCases(): iterable { yield [ 'bar(); // test', 'bar() // test ;', ]; yield [ 'bar(); # test', 'bar() # test ;', ]; yield [ 'bar();// test', 'bar()// test ;', ]; yield [ "', ]; yield [ 'setName(\'readme1\') ->setDescription(\'Generates the README\'); ', 'setName(\'readme1\') ->setDescription(\'Generates the README\') ; ', ]; yield [ 'setName(\'readme2\') ->setDescription(\'Generates the README\'); ', 'setName(\'readme2\') ->setDescription(\'Generates the README\') ; ', ]; yield [ 'foo(\'with param containing ;\') ;" ;', ]; yield [ 'foo();', ]; yield [ 'foo() ;', ]; yield [ 'foo(\'with param containing ;\') ;', ]; yield [ 'foo(\'with param containing ) ; \') ;', ]; yield [ 'foo("with param containing ) ; ") ; ?>', ]; yield [ 'foo("with semicolon in string) ; "); ?>', ]; yield [ 'example();', 'example() ;', ]; yield [ 'setDescription(\'Generates the README\'); ', 'setDescription(\'Generates the README\') ; ', ]; yield [ 'setDescription(\'Generates the README\'); ', 'setDescription(\'Generates the README\') ; ', ]; yield [ '', ]; yield [ '', ]; yield [ 'fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n")); $this->fixer->configure(['strategy' => MultilineWhitespaceBeforeSemicolonsFixer::STRATEGY_NO_MULTI_LINE]); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideMessyWhitespacesMultiLineWhitespaceCases(): iterable { yield [ 'fixer->configure(['strategy' => MultilineWhitespaceBeforeSemicolonsFixer::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS]); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideSemicolonForChainedCallsFixCases(): iterable { yield [ 'method1() ->method2() ; ?>', 'method1() ->method2(); ?>', ]; yield [ 'method1() ->method2() // comment ; ', 'method1() ->method2(); // comment ', ]; yield [ 'method1() ->method2() ; $service->method3(); $this ->method1() ->method2() ;', 'method1() ->method2() ; $service->method3(); $this ->method1() ->method2();', ]; yield [ 'method2() ; ?>', 'method2(); ?>', ]; yield [ 'method1() ->method2() ->method3() ->method4() ; ?>', 'method1() ->method2() ->method3() ->method4(); ?>', ]; yield [ 'service->method1() ->method2([1, 2]) ->method3( "2", 2, [1, 2] ) ->method4() ; ?>', 'service->method1() ->method2([1, 2]) ->method3( "2", 2, [1, 2] ) ->method4(); ?>', ]; yield [ 'method1() ->method2() ->method3() ->method4() ; ?>', 'method1() ->method2() ->method3() ->method4(); ?>', ]; yield [ 'method1("a", true) ->method2(true, false) ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f) ; ?>', 'method1("a", true) ->method2(true, false) ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f); ?>', ]; yield [ 'method1("a", true) // this is a comment /* ->method2(true, false) */ ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f) /* this is a comment */ ; ?>', 'method1("a", true) // this is a comment /* ->method2(true, false) */ ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f); /* this is a comment */ ?>', ]; yield [ 'method1(); $service->method2()->method3(); ?>', ]; yield [ 'method1() ; $service->method2()->method3() ; ?>', ]; yield [ 'method2(function ($a) { $a->otherCall() ->a() ->b() ; }) ; ?>', 'method2(function ($a) { $a->otherCall() ->a() ->b() ; }); ?>', ]; yield [ 'method2(function ($a) { $a->otherCall() ->a() ->b(array_merge([ 1 => 1, 2 => 2, ], $this->getOtherArray() )) ; }) ; ?>', 'method2(function ($a) { $a->otherCall() ->a() ->b(array_merge([ 1 => 1, 2 => 2, ], $this->getOtherArray() )); }); ?>', ]; yield [ 'method1(null, null, [ null => null, 1 => $data->getId() > 0, ]) ->method2(4, Type::class) ; ', 'method1(null, null, [ null => null, 1 => $data->getId() > 0, ]) ->method2(4, Type::class); ', ]; yield [ 'method1() ->method2() ; ?>', 'method1() ->method2(); ?>', ]; yield [ 'method2() ; ?>', 'method2(); ?>', ]; yield [ 'method2() // comment ; ', 'method2(); // comment ', ]; yield [ 'method2() ; Service::method3(); $this ->method1() ->method2() ;', 'method2() ; Service::method3(); $this ->method1() ->method2();', ]; yield [ '', '', ]; yield [ 'method2() ->method3() ->method4() ; ?>', 'method2() ->method3() ->method4(); ?>', ]; yield [ 'method2([1, 2]) ->method3( "2", 2, [1, 2] ) ->method4() ; ?>', 'method2([1, 2]) ->method3( "2", 2, [1, 2] ) ->method4(); ?>', ]; yield [ 'method2() ->method3() ->method4() ; ?>', 'method2() ->method3() ->method4(); ?>', ]; yield [ 'method2(true, false) ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f) ; ?>', 'method2(true, false) ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f); ?>', ]; yield [ 'method2(true, false) */ ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f) /* this is a comment */ ; ?>', 'method2(true, false) */ ->method3([1, 2, 3], ["a" => "b", "c" => 1, "d" => true]) ->method4(1, "a", $f); /* this is a comment */ ?>', ]; yield [ 'method3(); ?>', ]; yield [ 'method3() ; ?>', ]; yield [ 'otherCall() ->a() ->b() ; }) ; ?>', 'otherCall() ->a() ->b() ; }); ?>', ]; yield [ 'a() ->b(array_merge([ 1 => 1, 2 => 2, ], $this->getOtherArray() )) ; }) ; ?>', 'a() ->b(array_merge([ 1 => 1, 2 => 2, ], $this->getOtherArray() )); }); ?>', ]; yield [ ' null, 1 => $data->getId() > 0, ]) ->method2(4, Type::class) ; ', ' null, 1 => $data->getId() > 0, ]) ->method2(4, Type::class); ', ]; yield [ 'method2() ; ?>', 'method2(); ?>', ]; yield [ 'bar() ; } return (new Foo($bar)) ->baz() ; } ?>', 'bar(); } return (new Foo($bar)) ->baz(); } ?>', ]; yield [ 'baz() ; function foo($bar) { $foo = (new Foo($bar)) ->baz() ; } ?>', 'baz(); function foo($bar) { $foo = (new Foo($bar)) ->baz(); } ?>', ]; yield [ 'methodA() ->methodB() ; ', 'methodA() ->methodB(); ', ]; yield [ 'methodA() ->methodB() ; ', 'methodA() ->methodB(); ', ]; yield [ "one()\n ->two(2, )\n;", "one()\n ->two(2, );", ]; yield [ "one(1, )\n ->two()\n;", "one(1, )\n ->two();", ]; yield [ 'bar(); Service::method1() ->method2() ->method3()->method4() ; ?>', 'bar() ; Service::method1() ->method2() ->method3()->method4(); ?>', ]; yield [ 'bar(); \Service::method1() ->method2() ->method3()->method4() ; ?>', 'bar() ; \Service::method1() ->method2() ->method3()->method4(); ?>', ]; yield [ 'bar(); Ns\Service::method1() ->method2() ->method3()->method4() ; ?>', 'bar() ; Ns\Service::method1() ->method2() ->method3()->method4(); ?>', ]; yield [ 'bar(); \Ns\Service::method1() ->method2() ->method3()->method4() ; ?>', 'bar() ; \Ns\Service::method1() ->method2() ->method3()->method4(); ?>', ]; yield [ 'setName(\'readme2\') ->setDescription(\'Generates the README\') ; ', 'setName(\'readme2\') ->setDescription(\'Generates the README\') ; ', ]; yield [ 'foo() ->{$bar ? \'bar\' : \'baz\'}() ; ', ]; yield [ 'method1() ->method2() ; ', 'method1() ->method2(); ', ]; yield [ 'method1() ->method2() ; ', 'method1() ->method2(); ', ]; yield [ ' 2, 3 => $baz->method(), ]; ', ]; yield [ 'baz() ; } ', 'baz() ; } ', ]; yield [ <<<'PHP' foo() ->bar() ;$y = 42; PHP, <<<'PHP' foo() ->bar();$y = 42; PHP, ]; } /** * @dataProvider provideMessyWhitespacesSemicolonForChainedCallsCases */ public function testMessyWhitespacesSemicolonForChainedCalls(string $expected, ?string $input = null): void { $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n")); $this->fixer->configure(['strategy' => MultilineWhitespaceBeforeSemicolonsFixer::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS]); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideMessyWhitespacesSemicolonForChainedCallsCases(): iterable { yield [ "method1()\r\n\t\t->method2()\r\n ;", "method1()\r\n\t\t->method2();", ]; yield [ "method1()\r\n\t\t->method2()\r\n\t\t->method(3)\r\n\t;", "method1()\r\n\t\t->method2()\r\n\t\t->method(3);", ]; yield [ "method2(function (\$a) {\r\n\t\t\t\$a->otherCall()\r\n\t\t\t\t->a()\r\n\t\t\t\t->b(array_merge([\r\n\t\t\t\t\t\t1 => 1,\r\n\t\t\t\t\t\t2 => 2,\r\n\t\t\t\t\t], \$this->getOtherArray()\r\n\t\t\t\t))\r\n\t\t\t;\r\n\t\t})\r\n\t;\r\n?>", "method2(function (\$a) {\r\n\t\t\t\$a->otherCall()\r\n\t\t\t\t->a()\r\n\t\t\t\t->b(array_merge([\r\n\t\t\t\t\t\t1 => 1,\r\n\t\t\t\t\t\t2 => 2,\r\n\t\t\t\t\t], \$this->getOtherArray()\r\n\t\t\t\t));\r\n\t\t});\r\n?>", ]; } /** * @requires PHP 8.0 */ public function testFix80(): void { $this->fixer->configure(['strategy' => MultilineWhitespaceBeforeSemicolonsFixer::STRATEGY_NEW_LINE_FOR_CHAINED_CALLS]); $this->doTest( 'method1() ?->method2() ?->method3() ; ', 'method1() ?->method2() ?->method3(); ' ); } }