diff --git a/blink/internal/fixer/rules/yoda_style.go b/blink/internal/fixer/rules/yoda_style.go index ad7f48db76..c2df83e710 100644 --- a/blink/internal/fixer/rules/yoda_style.go +++ b/blink/internal/fixer/rules/yoda_style.go @@ -164,16 +164,20 @@ func (f YodaStyle) Fix(s *tokens.Stream) bool { if rs < 0 { continue } - // with always_move_variable=false an operand wrapped in "(" is not - // treated as a variable, so a grouped/assigned right side stays put (a - // leading cast like "(int) $x" is not a grouping paren) - if s.At(rs).Kind == token.Punct && s.At(rs).Value == "(" && !isCastParen(s, rs) { - continue - } re := rightComparisonOperandEnd(s, rs) if re < 0 { continue } + // with always_move_variable=false a right side that is purely a + // parenthesized group "(...)" is not treated as a variable and stays put + // (a leading cast like "(int) $x" is not a grouping paren). A group that + // is only the base of a larger primary, e.g. "(new X())->y()", is a + // variable expression and is still swapped. + if s.At(rs).Kind == token.Punct && s.At(rs).Value == "(" && !isCastParen(s, rs) { + if c := s.MatchForward(rs); c < 0 || c == re { + continue + } + } if spanIsConstant(s, rs, re) { continue // both sides constant } diff --git a/blink/internal/fixer/rules/yoda_style_test.go b/blink/internal/fixer/rules/yoda_style_test.go index 4b408e26d9..6adab9e300 100644 --- a/blink/internal/fixer/rules/yoda_style_test.go +++ b/blink/internal/fixer/rules/yoda_style_test.go @@ -16,6 +16,9 @@ func TestYodaStyle(t *testing.T) { assertFix(t, f, "pending) {}", "pending !== null) {}", true) assertFix(t, f, "resolved($a)) {}", "resolved($a) === true) {}", true) + // right side is a parenthesized group that is the base of a larger primary, + // e.g. "(new X())->y()": still a variable expression, so it is de-yoda'd + assertFix(t, f, "user()) {}", "user() !== null) {}", true) // constant name, ::class, signed number, empty array on the left assertFix(t, f, "