Skip to content

phpcs hangs when using arrow functions that return heredoc #2926

@bcremer

Description

@bcremer

The following combination of arrow function and heredoc will lead to an endless loop:

fn () => <<<HTML foo HTML;

Steps to reproduce:

curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar cat <<EOT >> repro.php <?php fn () => <<<HTML foo HTML; EOT php -l repro.php No syntax errors detected in repro.php $ php phpcs.phar -v repro.php Registering sniffs in the PEAR standard... DONE (28 sniffs registered) Creating file list... DONE (1 files in queue) Changing into directory /tmp/repro Processing repro.php 

This will run infinitely.

Full log

This also happens with the current master branch commit 7c406b75a4e53a50aa1eba2d3dda28e3d55f9cfe.

Following the very verbose output of 7c406b75a4e53a50aa1eba2d3dda28e3d55f9cfe:

$ ./PHP_CodeSniffer/bin/phpcs -vvv repro.php Processing ruleset /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/ruleset.xml	Adding sniff files from /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs directory	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeIndentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeClosingBraceSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/WhiteSpace/ObjectOperatorIndentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/NamingConventions/ValidVariableNameSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/NamingConventions/ValidFunctionNameSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/NamingConventions/ValidClassNameSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Functions/ValidDefaultValueSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Formatting/MultiLineAssignmentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Files/IncludingFileSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/ControlStructures/MultiLineConditionSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/ControlStructures/ControlSignatureSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Commenting/InlineCommentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Commenting/ClassCommentSniff.php	=> /tmp/repro/PHP_CodeSniffer/src/Standards/PEAR/Sniffs/Classes/ClassDeclarationSniff.php	Processing rule "Generic.Functions.FunctionCallArgumentSpacing"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php	Processing rule "Generic.NamingConventions.UpperCaseConstantName"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php	Processing rule "Generic.PHP.LowerCaseConstant"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/PHP/LowerCaseConstantSniff.php	Processing rule "Generic.PHP.DisallowShortOpenTag"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/PHP/DisallowShortOpenTagSniff.php	Processing rule "Generic.WhiteSpace.DisallowTabIndent"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/WhiteSpace/DisallowTabIndentSniff.php	Processing rule "Generic.Commenting.DocComment"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/Commenting/DocCommentSniff.php	Processing rule "Squiz.Commenting.DocCommentAlignment"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Squiz/Sniffs/Commenting/DocCommentAlignmentSniff.php	Processing rule "Generic.Files.LineLength"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/Files/LineLengthSniff.php	=> property "lineLimit" set to "85"	=> property "absoluteLineLimit" set to "0"	Processing rule "Generic.Files.LineEndings"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/Files/LineEndingsSniff.php	=> property "eolChar" set to "\n"	Processing rule "Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php	=> severity set to 0	Processing rule "Generic.ControlStructures.InlineControlStructure"	=> /tmp/repro/PHP_CodeSniffer/src/Standards/Generic/Sniffs/ControlStructures/InlineControlStructureSniff.php	=> property "error" set to "false" => Ruleset processing complete; included 28 sniffs and excluded 0 Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\WhiteSpace\ScopeIndentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\WhiteSpace\ScopeClosingBraceSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\WhiteSpace\ObjectOperatorIndentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidVariableNameSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidFunctionNameSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidClassNameSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\ValidDefaultValueSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionDeclarationSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionCallSignatureSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Formatting\MultiLineAssignmentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Files\IncludingFileSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\ControlStructures\MultiLineConditionSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\ControlStructures\ControlSignatureSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\InlineCommentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FunctionCommentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FileCommentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\ClassCommentSniff Registered PHP_CodeSniffer\Standards\PEAR\Sniffs\Classes\ClassDeclarationSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\FunctionCallArgumentSpacingSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\UpperCaseConstantNameSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\LowerCaseConstantSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\DisallowShortOpenTagSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\DisallowTabIndentSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting\DocCommentSniff Registered PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\DocCommentAlignmentSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineEndingsSniff Registered PHP_CodeSniffer\Standards\Generic\Sniffs\ControlStructures\InlineControlStructureSniff *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_DO => do	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_CURLY_BRACKET => {	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token [2]: T_WHILE => while	Process token [3]: T_WHITESPACE => ·	Process token 4 : T_OPEN_PARENTHESIS => (	Process token [5]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token 1 : T_SEMICOLON => ;	Process token [2]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHILE => while	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_PARENTHESIS => (	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_FOR => for	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_PARENTHESIS => (	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_IF => if	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_PARENTHESIS => (	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_FOREACH => foreach	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_PARENTHESIS => (	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token 1 : T_CLOSE_CURLY_BRACKET => }	Process token [2]: T_WHITESPACE => ·	Process token [3]: T_ELSE => else	Process token [4]: T_WHITESPACE => ·	Process token [5]: T_IF => if	Process token [6]: T_WHITESPACE => ·	Process token 7 : T_OPEN_PARENTHESIS => (	Process token [8]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token 1 : T_CLOSE_CURLY_BRACKET => }	Process token [2]: T_WHITESPACE => ·	Process token [3]: T_ELSEIF => elseif	Process token [4]: T_WHITESPACE => ·	Process token 5 : T_OPEN_PARENTHESIS => (	Process token [6]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_WHITESPACE => ·	Process token 2 : T_OPEN_CURLY_BRACKET => {	Process token [3]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token 1 : T_CLOSE_CURLY_BRACKET => }	Process token [2]: T_WHITESPACE => ·	Process token [3]: T_ELSE => else	Process token [4]: T_WHITESPACE => ·	Process token 5 : T_OPEN_CURLY_BRACKET => {	Process token [6]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php·	Process token [1]: T_DO => do	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_CURLY_BRACKET => {	Process token [4]: T_CLOSE_TAG => ?> *** END PHP TOKENIZING *** Creating file list... DONE (1 files in queue) Changing into directory /tmp/repro Processing repro.php *** START PHP TOKENIZING ***	Process token [0]: T_OPEN_TAG => <?php\n	Process token [1]: T_FN => fn	Process token [2]: T_WHITESPACE => ·	Process token 3 : T_OPEN_PARENTHESIS => (	Process token 4 : T_CLOSE_PARENTHESIS => )	Process token [5]: T_WHITESPACE => ·	Process token [6]: T_DOUBLE_ARROW => =>	Process token [7]: T_WHITESPACE => ·	Process token [8]: T_START_HEREDOC => <<<HTML\n	Process token 11 : T_SEMICOLON => ;	Process token [12]: T_WHITESPACE => \n *** END PHP TOKENIZING *** *** START TOKEN MAP ***	=> Found unowned parenthesis opener at 3	=> Found unowned parenthesis closer at 4 for 3 *** END TOKEN MAP *** *** START SCOPE MAP ***	Start scope map at 8:T_START_HEREDOC => <<<HTML\n	=> Begin scope map recursion at token 8 with depth 1	Process token 9 on line 3 [opener:8;]: T_HEREDOC => foo\n	Process token 10 on line 4 [opener:8;]: T_END_HEREDOC => HTML	=> Found scope closer (10:T_END_HEREDOC) for 8:T_START_HEREDOC *** END SCOPE MAP *** *** START LEVEL MAP ***	Process token 0 on line 1 [col:1;len:5;lvl:0;]: T_OPEN_TAG => <?php\n	Process token 1 on line 2 [col:1;len:2;lvl:0;]: T_FN => fn	Process token 2 on line 2 [col:3;len:1;lvl:0;]: T_WHITESPACE => ·	Process token 3 on line 2 [col:4;len:1;lvl:0;]: T_OPEN_PARENTHESIS => (	Process token 4 on line 2 [col:5;len:1;lvl:0;]: T_CLOSE_PARENTHESIS => )	Process token 5 on line 2 [col:6;len:1;lvl:0;]: T_WHITESPACE => ·	Process token 6 on line 2 [col:7;len:2;lvl:0;]: T_DOUBLE_ARROW => =>	Process token 7 on line 2 [col:9;len:1;lvl:0;]: T_WHITESPACE => ·	Process token 8 on line 2 [col:10;len:7;lvl:0;]: T_START_HEREDOC => <<<HTML\n	=> Found scope opener for 8:T_START_HEREDOC * level increased * * token 8:T_START_HEREDOC added to conditions array *	Process token 9 on line 3 [col:1;len:3;lvl:1;conds;T_START_HEREDOC;]: T_HEREDOC => foo\n	Process token 10 on line 4 [col:1;len:4;lvl:1;conds;T_START_HEREDOC;]: T_END_HEREDOC => HTML	=> Found scope closer for 8:T_START_HEREDOC * token T_START_HEREDOC removed from conditions array * * level decreased *	Process token 11 on line 4 [col:5;len:1;lvl:0;]: T_SEMICOLON => ;	Process token 12 on line 4 [col:6;len:0;lvl:0;]: T_WHITESPACE => \n *** END LEVEL MAP *** *** START ADDITIONAL PHP PROCESSING ***

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions