Skip to content

Commit a7c6e1e

Browse files
mreidenclaude
andauthored
fix: print PHP 8.5 clone with (clone($obj, [...])) (#2531)
php-parser 3.7 parses the PHP 8.5 clone-with form into `clone.properties`, but the printer only printed `what`, so $copy = clone($obj, ['name' => $name]); was silently rewritten as `$copy = clone $obj;`, dropping the property overrides (exit code 0). Print it laid out like a two-argument call, mirroring printArgumentsList: on one line while it fits, otherwise hugging a trailing array (`clone($obj, [` ... `])`), otherwise one argument per line. The broken-out form has no trailing comma because php-parser does not accept one after the properties expression yet. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent a449dfe commit a7c6e1e

4 files changed

Lines changed: 117 additions & 3 deletions

File tree

‎src/printer.mjs‎

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2161,11 +2161,64 @@ function printNode(path, options, print) {
21612161

21622162
return parts;
21632163
}
2164-
case "clone":
2164+
case "clone": {
2165+
if (!node.properties) {
2166+
return [
2167+
"clone ",
2168+
node.what.comments ? indent(print("what")) : print("what"),
2169+
];
2170+
}
2171+
2172+
// PHP 8.5 clone with: `clone($obj, ['prop' => $value])`, laid out like
2173+
// a call with two arguments (see printArgumentsList). No trailing comma:
2174+
// php-parser does not accept one after the properties expression.
2175+
const printedWhat = print("what");
2176+
const printedProperties = print("properties");
2177+
const printedArguments = [printedWhat, ",", line, printedProperties];
2178+
const allArgsBrokenOut = () =>
2179+
group(["clone(", indent([line, ...printedArguments]), line, ")"], {
2180+
shouldBreak: true,
2181+
});
2182+
2183+
if (!shouldGroupLastArg([node.what, node.properties])) {
2184+
return group(
2185+
["clone(", indent([softline, ...printedArguments]), softline, ")"],
2186+
{
2187+
shouldBreak: willBreak(printedWhat) || willBreak(printedProperties),
2188+
}
2189+
);
2190+
}
2191+
2192+
const somePrintedArgumentsWillBreak =
2193+
willBreak(printedWhat) || willBreak(printedProperties);
2194+
const simpleConcat = [
2195+
"clone(",
2196+
printedWhat,
2197+
", ",
2198+
printedProperties,
2199+
")",
2200+
];
2201+
21652202
return [
2166-
"clone ",
2167-
node.what.comments ? indent(print("what")) : print("what"),
2203+
somePrintedArgumentsWillBreak ? breakParent : "",
2204+
conditionalGroup(
2205+
[
2206+
somePrintedArgumentsWillBreak
2207+
? ifBreak(allArgsBrokenOut(), simpleConcat)
2208+
: simpleConcat,
2209+
[
2210+
"clone(",
2211+
printedWhat,
2212+
", ",
2213+
group(printedProperties, { shouldBreak: true }),
2214+
")",
2215+
],
2216+
allArgsBrokenOut(),
2217+
],
2218+
{ shouldBreak: willBreak(printedWhat) }
2219+
),
21682220
];
2221+
}
21692222
case "propertylookup":
21702223
case "nullsafepropertylookup":
21712224
case "staticlookup":
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`clone-with.php 1`] = `
4+
====================================options=====================================
5+
parsers: ["php"]
6+
phpVersion: "8.5"
7+
printWidth: 80
8+
| printWidth
9+
=====================================input======================================
10+
<?php
11+
12+
$copy = clone($obj);
13+
$copy = clone($obj, ['name' => $name]);
14+
$copy = clone($this->template, ['id' => null, 'createdAt' => new DateTimeImmutable()]);
15+
$copy = clone($obj, $overrides);
16+
$copy = clone($obj, array_merge($defaults, ['name' => $name, 'email' => $email, 'createdAt' => $now]));
17+
$copy = clone($this->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongPropertyName, ['id' => null, 'createdAt' => $now, 'updatedAt' => $now]);
18+
$copy = clone($obj, [
19+
'name' => $name,
20+
]);
21+
22+
=====================================output=====================================
23+
<?php
24+
25+
$copy = clone $obj;
26+
$copy = clone($obj, ["name" => $name]);
27+
$copy = clone($this->template, [
28+
"id" => null,
29+
"createdAt" => new DateTimeImmutable(),
30+
]);
31+
$copy = clone($obj, $overrides);
32+
$copy = clone(
33+
$obj,
34+
array_merge($defaults, [
35+
"name" => $name,
36+
"email" => $email,
37+
"createdAt" => $now,
38+
])
39+
);
40+
$copy = clone(
41+
$this->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongPropertyName,
42+
["id" => null, "createdAt" => $now, "updatedAt" => $now]
43+
);
44+
$copy = clone($obj, [
45+
"name" => $name,
46+
]);
47+
48+
================================================================================
49+
`;

‎tests/clone-with/clone-with.php‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
$copy = clone($obj);
4+
$copy = clone($obj, ['name' => $name]);
5+
$copy = clone($this->template, ['id' => null, 'createdAt' => new DateTimeImmutable()]);
6+
$copy = clone($obj, $overrides);
7+
$copy = clone($obj, array_merge($defaults, ['name' => $name, 'email' => $email, 'createdAt' => $now]));
8+
$copy = clone($this->veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongPropertyName, ['id' => null, 'createdAt' => $now, 'updatedAt' => $now]);
9+
$copy = clone($obj, [
10+
'name' => $name,
11+
]);

‎tests/clone-with/jsfmt.spec.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
run_spec(import.meta, ["php"], { phpVersion: "8.5" });

0 commit comments

Comments
 (0)