diff --git a/src/needs-parens.mjs b/src/needs-parens.mjs index 2bd051fc0..1fa215282 100644 --- a/src/needs-parens.mjs +++ b/src/needs-parens.mjs @@ -128,9 +128,14 @@ function needsParens(path, options) { } case "clone": case "new": { + // Parentheses around `new` with member access are only optional on + // PHP >= 8.4. When the PHP version is just a guess (default "auto" + // without a composer.json), stay conservative and keep them so the + // output stays valid on older PHP versions (see #2440). const requiresParens = node.kind === "clone" || - (node.kind === "new" && options.phpVersion < 8.4); + (node.kind === "new" && + (options.phpVersion < 8.4 || options.phpVersionIsGuessed)); switch (parent.kind) { case "propertylookup": case "nullsafepropertylookup": diff --git a/src/options.mjs b/src/options.mjs index e650f85ad..34a423ab9 100644 --- a/src/options.mjs +++ b/src/options.mjs @@ -87,8 +87,13 @@ export function resolvePhpVersion(options) { return; } if (options.phpVersion === "auto") { - options.phpVersion = - getComposerPhpVersion() ?? LATEST_SUPPORTED_PHP_VERSION; + const composerVersion = getComposerPhpVersion(); + options.phpVersion = composerVersion ?? LATEST_SUPPORTED_PHP_VERSION; + // Without a composer.json pinning a PHP version, "auto" falls back to + // the latest supported version, which is only a guess. Remember that + // so the printer stays conservative and never emits syntax that is + // invalid on older PHP versions (see #2440). + options.phpVersionIsGuessed = composerVersion === null; } else if (options.phpVersion === "composer") { const v = getComposerPhpVersion(); if (v === null) { diff --git a/tests/assign/__snapshots__/jsfmt.spec.mjs.snap b/tests/assign/__snapshots__/jsfmt.spec.mjs.snap index 78d9bd542..b9d4aef69 100644 --- a/tests/assign/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/assign/__snapshots__/jsfmt.spec.mjs.snap @@ -233,7 +233,7 @@ $veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongVariable = $veryVeryVeryVeryVer $obj->property = $obj->oneProperty = $obj->twoPeroperty = 0; $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = 0; $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = $obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = $veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongValue; -$obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = new MyClass()->call( +$obj->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongProperty = (new MyClass())->call( $arg, ); $obj->loooooooooooong->lookup = $this->getRequest()->getParam( @@ -251,7 +251,7 @@ $component = Foo::$insertReallyReallyReallyReallyReallyReallyReallyReallyLongName; $component = $a::$insertReallyReallyReallyReallyReallyReallyReallyReallyLongName; -$component = new Foo() +$component = (new Foo()) ->insertReallyReallyReallyReallyReallyReallyReallyReallyLongName; $component = (clone $a) ->insertReallyReallyReallyReallyReallyReallyReallyReallyLongName; @@ -266,7 +266,7 @@ $component = Foo::$insertReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName; $component = $a::$insertReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName; -$component = new Foo() +$component = (new Foo()) ->insertReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName; $component = (clone $a) ->insertReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName; diff --git a/tests/comments/__snapshots__/jsfmt.spec.mjs.snap b/tests/comments/__snapshots__/jsfmt.spec.mjs.snap index 5adfe5f8c..fed543709 100644 --- a/tests/comments/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/comments/__snapshots__/jsfmt.spec.mjs.snap @@ -6590,16 +6590,16 @@ $inspect = $condition $extractTextPluginOptions = $shouldUseRelativeAssetPaths ? // Making sure that the publicPath goes back to to build folder. - ["publicPath" => new Foo($cssFilename->split("/")->length)->join("../")] + ["publicPath" => (new Foo($cssFilename->split("/")->length))->join("../")] : []; $extractTextPluginOptions = $shouldUseRelativeAssetPaths ? // Making sure that the publicPath goes back to to build folder. - ["publicPath" => new Foo($cssFilename->split("/")->length)->join("../")] + ["publicPath" => (new Foo($cssFilename->split("/")->length))->join("../")] : []; $extractTextPluginOptions = $shouldUseRelativeAssetPaths // Making sure that the publicPath goes back to to build folder. - ? ["publicPath" => new Foo($cssFilename->split("/")->length)->join("../")] + ? ["publicPath" => (new Foo($cssFilename->split("/")->length))->join("../")] : []; $var = diff --git a/tests/member_chain/__snapshots__/jsfmt.spec.mjs.snap b/tests/member_chain/__snapshots__/jsfmt.spec.mjs.snap index 4d8631650..548a8e419 100644 --- a/tests/member_chain/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/member_chain/__snapshots__/jsfmt.spec.mjs.snap @@ -87,10 +87,10 @@ $superSupersuperSupersuperSupersuperSupersuperSuperLong->{$exampleOfOrderOfGette $superSupersuperSupersuperSupersuperSupersuperSuperLong::$exampleOfOrderOfGetterAndSetterReordered; $superSupersuperSupersuperSupersuperSupersuperSuperLong ::$exampleOfOrderOfGetterAndSetterReordered[0]; -$superSupersuperSupersuperSupersuperSupersuperSuperLong = new SuperSuperSuperSuperSuperSuperLongClassName() +$superSupersuperSupersuperSupersuperSupersuperSuperLong = (new SuperSuperSuperSuperSuperSuperLongClassName()) ->some_very_long_member_expression; new $superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong(); -new $superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong() +(new $superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong->superSupersuperSupersuperSupersuperSupersuperSuperLong()) ->superSupersuperSupersuperSupersuperSupersuperSuperLong; $test = $superSupersuperSupersuperSupersuperSupersuperSuperLong->exampleOfOrderOfGetterAndSetterReordered; @@ -241,7 +241,7 @@ $object[ : $helper->responseBody($defaultUser) ]->map(); -new TestClassWithReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName() +(new TestClassWithReallyReallyReallyReallyReallyReallyReallyReallyReallyLongName()) ->map() ->filter(); @@ -565,14 +565,14 @@ $brian->hotel->orders()->ordered()->with("smith")->get(); $brian::$hotel->orders()->ordered()->with("smith")->get(); $brian["hotel"]->orders()->ordered()->with("smith")->get(); Foo::$hotel->orders()->ordered()->with("smith")->get(); -new Foo()->hotel->orders()->ordered()->with("smith")->get(); +(new Foo())->hotel->orders()->ordered()->with("smith")->get(); (clone $a)->hotel->orders()->ordered()->with("smith")->get(); $var = $brian->hotel->orders()->ordered()->with("smith")->get(); $var = $brian::$hotel->orders()->ordered()->with("smith")->get(); $var = $brian["hotel"]->orders()->ordered()->with("smith")->get(); $var = Foo::$hotel->orders()->ordered()->with("smith")->get(); -$var = new Foo()->hotel->orders()->ordered()->with("smith")->get(); +$var = (new Foo())->hotel->orders()->ordered()->with("smith")->get(); $var = (clone $a)->hotel->orders()->ordered()->with("smith")->get(); $var = Foo::keys($items) @@ -583,10 +583,10 @@ $var = Foo::keys($items) return $x * 2; }); -new static(func_get_args())->push($this)->each(function ($item) { +(new static(func_get_args()))->push($this)->each(function ($item) { VarDumper::dump($item); }); -new static(func_get_args()) +(new static(func_get_args())) ->offset(10) ->push($this) ->each(function ($item) { @@ -812,8 +812,8 @@ $window ->first() ->second(); -new Foo()->call($foo->bar->baz)->first()->second(); -new Foo() +(new Foo())->call($foo->bar->baz)->first()->second(); +(new Foo()) ->call($foo->bar->baz->foo()) ->first() ->second(); diff --git a/tests/new/__snapshots__/jsfmt.spec.mjs.snap b/tests/new/__snapshots__/jsfmt.spec.mjs.snap index 177c147da..51d18a506 100644 --- a/tests/new/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/new/__snapshots__/jsfmt.spec.mjs.snap @@ -17,6 +17,9 @@ $bar = 'MyClassName'; $foo = new $bar; $foo = new $bar(); +// https://github.com/prettier/plugin-php/issues/2440 +$timestamp = (new DateTimeImmutable())->format(DATE_ATOM); + abstract class A { public static function create() @@ -211,6 +214,9 @@ $bar = "MyClassName"; $foo = new $bar(); $foo = new $bar(); +// https://github.com/prettier/plugin-php/issues/2440 +$timestamp = (new DateTimeImmutable())->format(DATE_ATOM); + abstract class A { public static function create() @@ -453,6 +459,9 @@ $bar = 'MyClassName'; $foo = new $bar; $foo = new $bar(); +// https://github.com/prettier/plugin-php/issues/2440 +$timestamp = (new DateTimeImmutable())->format(DATE_ATOM); + abstract class A { public static function create() @@ -647,6 +656,9 @@ $bar = "MyClassName"; $foo = new $bar(); $foo = new $bar(); +// https://github.com/prettier/plugin-php/issues/2440 +$timestamp = (new DateTimeImmutable())->format(DATE_ATOM); + abstract class A { public static function create() @@ -656,12 +668,12 @@ abstract class A } } -$class = new Foo()->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongMethod(); -$class = new Foo([ +$class = (new Foo())->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongMethod(); +$class = (new Foo([ "VeryVeryVeryVeryVeryVeryVeryVeryVeryLongKey" => "VeryVeryVeryVeryVeryVeryVeryVeryVeryLongValue", -])->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongMethod(); -$class = new PendingDispatch(new $this->class(...func_get_args()))->chain( +]))->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongMethod(); +$class = (new PendingDispatch(new $this->class(...func_get_args())))->chain( $this->chain, ); $dumper = in_array(PHP_SAPI, ["cli", "phpdbg"]) @@ -678,7 +690,7 @@ $class = new static( $response = new \\Illuminate\\Http\\JsonResponse( new JsonResponseTestJsonSerializeObject(), ); -$result = new Pipeline(new \\Illuminate\\Container\\Container()) +$result = (new Pipeline(new \\Illuminate\\Container\\Container())) ->send("foo") ->through([new PipelineTestPipeOne()]) ->then(function ($piped) { diff --git a/tests/new/new.php b/tests/new/new.php index a4a16e1f1..efb58c000 100644 --- a/tests/new/new.php +++ b/tests/new/new.php @@ -8,6 +8,9 @@ $foo = new $bar; $foo = new $bar(); +// https://github.com/prettier/plugin-php/issues/2440 +$timestamp = (new DateTimeImmutable())->format(DATE_ATOM); + abstract class A { public static function create() diff --git a/tests/parens/__snapshots__/jsfmt.spec.mjs.snap b/tests/parens/__snapshots__/jsfmt.spec.mjs.snap index 7a00c40a9..de7727794 100644 --- a/tests/parens/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/parens/__snapshots__/jsfmt.spec.mjs.snap @@ -4865,11 +4865,11 @@ $var = $var[0][1]::foo(); $var = $var[0]->foo()->baz; $var = $var[0]->foo()->baz; -$var = new Foo()->bar; -$var = new Foo()::bar; -$var = new Foo()->bar(); -$var = new Foo()::bar(); -$var = new Foo()[1]; +$var = (new Foo())->bar; +$var = (new Foo())::bar; +$var = (new Foo())->bar(); +$var = (new Foo())::bar(); +$var = (new Foo())[1]; $var = $var->bar()(); $var = $var->bar()(); @@ -5181,46 +5181,46 @@ new Translator( c(); -new Foo()->prop; -new Foo()->method(); -new Foo()->$var; +$var = (new Foo())->c(); +(new Foo())->prop; +(new Foo())->method(); +(new Foo())->$var; $var = new class { public function log($msg) { echo $msg; } }; -$var = new foo()->bar(); -$var = new foo()->bar()->foo(); -$var = new foo()->bar()->foo(); -$var = new foo()->bar()->foo(); -$var = new foo()->bar()->foo()[0]; -$var = new foo()->bar()->foo()[0][1]; -$var = new foo()->bar()->foo()->baz(); -$var = new $foo()->bar; -$var = new $bar->y()->x; -new SortOfLongClassName() +$var = (new foo())->bar(); +$var = (new foo())->bar()->foo(); +$var = (new foo())->bar()->foo(); +$var = (new foo())->bar()->foo(); +$var = (new foo())->bar()->foo()[0]; +$var = (new foo())->bar()->foo()[0][1]; +$var = (new foo())->bar()->foo()->baz(); +$var = (new $foo())->bar; +$var = (new $bar->y())->x; +(new SortOfLongClassName()) ->withALongMethodName() ->andAnother() ->toPushItPast80Chars(); -$asdf = new SortOfLongClassName() +$asdf = (new SortOfLongClassName()) ->withALongMethodName() ->andAnother() ->toPushItPast80Chars(); -$var = new foo()[0]; -$var = new foo()[0]["string"]; -$var = new Foo()::foo; -$var = new Foo()::$foo; +$var = (new foo())[0]; +$var = (new foo())[0]["string"]; +$var = (new Foo())::foo; +$var = (new Foo())::$foo; $var = new $a->b(); $var = new $a->b(); -$var = new $a()->b(); -$var = new $a()->b(); -new class {}->foo; -new class {}->foo(); -new class {}(); -new class {}["foo"]; -$var = new class {}->foo; +$var = (new $a())->b(); +$var = (new $a())->b(); +(new class {})->foo; +(new class {})->foo(); +(new class {})(); +(new class {})["foo"]; +$var = (new class {})->foo; ================================================================================ diff --git a/tests/staticlookup/__snapshots__/jsfmt.spec.mjs.snap b/tests/staticlookup/__snapshots__/jsfmt.spec.mjs.snap index 121cb5059..e2ff9cef4 100644 --- a/tests/staticlookup/__snapshots__/jsfmt.spec.mjs.snap +++ b/tests/staticlookup/__snapshots__/jsfmt.spec.mjs.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`single.php 1`] = ` ====================================options===================================== @@ -344,7 +344,7 @@ $var = $var[0]::foo; $var = $var[0][1]::foo; $var = $var[0][1]::foo; $var = $var[0][1]::foo; -$var = new Foo()::bar; +$var = (new Foo())::bar; $var = Foo::$bar["baz"](); $var = Foo::$bar["baz"](); $var = Foo::{$bar["baz"]}();