Skip to content

NewWithoutParenthesesSniff removes constructor parentheses and produces invalid syntax when method chaining #1836

@martinbohmcz

Description

@martinbohmcz

Bug description

SlevomatCodingStandard.ControlStructures.NewWithoutParentheses incorrectly removes
empty constructor parentheses from new ClassName() when the expression is immediately
chained with ->method(). The resulting code is a parse error in PHP 8.4 and earlier.

Versions

  • slevomat/coding-standard: 8.27.1
  • PHP: 8.4.4

Steps to reproduce

<?php
$date = new DateTime()->setTime(0, 0);

Run phpcbf with SlevomatCodingStandard.ControlStructures.NewWithoutParentheses enabled.

Actual result

<?php
$date = new DateTime->setTime(0, 0);

Expected result

No change – the sniff should leave the code untouched.

Why this is a bug

PHP 8.4 added support for new Foo()->method() (chaining directly without outer
parentheses). However, new Foo->method() – without constructor () – is still
a parse error in PHP 8.4:

Parse error: syntax error, unexpected token "->", expecting ")" in ...

The sniff correctly detects that () contains no arguments, but does not check
what follows the closing ). When an object operator -> (or ::) follows, the
parentheses are not useless – they are required for the expression to be valid.

Root cause

In NewWithoutParenthesesSniff::process(), after confirming the parentheses are empty
(lines 81–84), there is no check for what token follows the closing ). A fix would be
to add before line 86:

$afterCloser = TokenHelper::findNextEffective(
    $phpcsFile,
    $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] + 1,
);
if (in_array($tokens[$afterCloser]['code'], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) {
    return;
}

Workaround

<rule ref="SlevomatCodingStandard.ControlStructures.NewWithoutParentheses">
    <exclude name="SlevomatCodingStandard.ControlStructures.NewWithoutParentheses.UselessParentheses"/>
</rule>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions