<?php

/**
 * Tests the backfilling of the T_FN token to PHP < 7.4.
 *
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 */
namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP;

use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase;
final class BackfillFnTokenTest extends AbstractTokenizerTestCase
{
    /**
     * Test simple arrow functions.
     *
     * @param string $testMarker The comment prefacing the target token.
     *
     * @dataProvider dataSimple
     * @covers       PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testSimple($testMarker)
    {
        $token = $this->getTargetToken($testMarker, \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 12);
    }
    //end testSimple()
    /**
     * Data provider.
     *
     * @see testSimple()
     *
     * @return array<string, array<string, string>>
     */
    public static function dataSimple()
    {
        return ['standard' => ['testMarker' => '/* testStandard */'], 'mixed case' => ['testMarker' => '/* testMixedCase */']];
    }
    //end dataSimple()
    /**
     * Test whitespace inside arrow function definitions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testWhitespace()
    {
        $token = $this->getTargetToken('/* testWhitespace */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 6, 13);
    }
    //end testWhitespace()
    /**
     * Test comments inside arrow function definitions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testComment()
    {
        $token = $this->getTargetToken('/* testComment */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 8, 15);
    }
    //end testComment()
    /**
     * Test heredocs inside arrow function definitions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testHeredoc()
    {
        $token = $this->getTargetToken('/* testHeredoc */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 4, 9);
    }
    //end testHeredoc()
    /**
     * Test nested arrow functions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNestedOuter()
    {
        $token = $this->getTargetToken('/* testNestedOuter */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 25);
    }
    //end testNestedOuter()
    /**
     * Test nested arrow functions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNestedInner()
    {
        $tokens = $this->phpcsFile->getTokens();
        $token = $this->getTargetToken('/* testNestedInner */', \T_FN);
        $this->backfillHelper($token, \true);
        $expectedScopeOpener = $token + 5;
        $expectedScopeCloser = $token + 16;
        $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer is not the semicolon token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer is not the semicolon token');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertSame($token - 4, $tokens[$closer]['scope_opener'], 'Closer scope opener is not the arrow token of the "outer" arrow function (shared scope closer)');
        $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer is not the semicolon token');
    }
    //end testNestedInner()
    /**
     * Test nested arrow functions with a shared closer.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNestedSharedCloser()
    {
        $tokens = $this->phpcsFile->getTokens();
        $token = $this->getTargetToken('/* testNestedSharedCloserOuter */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 4, 20);
        $token = $this->getTargetToken('/* testNestedSharedCloserInner */', \T_FN);
        $this->backfillHelper($token, \true);
        $expectedScopeOpener = $token + 4;
        $expectedScopeCloser = $token + 12;
        $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for "inner" arrow function is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for "inner" arrow function is not the TRUE token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for "inner" arrow function is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for "inner" arrow function is not the semicolon token');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertSame($token - 4, $tokens[$closer]['scope_opener'], 'Closer scope opener for "inner" arrow function is not the arrow token of the "outer" arrow function (shared scope closer)');
        $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for "inner" arrow function is not the TRUE token');
    }
    //end testNestedSharedCloser()
    /**
     * Test arrow functions that call functions.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testFunctionCall()
    {
        $token = $this->getTargetToken('/* testFunctionCall */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 17);
    }
    //end testFunctionCall()
    /**
     * Test arrow functions that are included in chained calls.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testChainedFunctionCall()
    {
        $token = $this->getTargetToken('/* testChainedFunctionCall */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 12, 'bracket');
    }
    //end testChainedFunctionCall()
    /**
     * Test arrow functions that are used as function arguments.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testFunctionArgument()
    {
        $token = $this->getTargetToken('/* testFunctionArgument */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 8, 15, 'comma');
    }
    //end testFunctionArgument()
    /**
     * Test arrow functions that use closures.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testClosure()
    {
        $token = $this->getTargetToken('/* testClosure */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 60, 'comma');
    }
    //end testClosure()
    /**
     * Test arrow functions using an array index.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testArrayIndex()
    {
        $token = $this->getTargetToken('/* testArrayIndex */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 8, 17, 'comma');
    }
    //end testArrayIndex()
    /**
     * Test arrow functions with a return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testReturnType()
    {
        $token = $this->getTargetToken('/* testReturnType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 11, 18, 'comma');
    }
    //end testReturnType()
    /**
     * Test arrow functions that return a reference.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testReference()
    {
        $token = $this->getTargetToken('/* testReference */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 6, 9);
    }
    //end testReference()
    /**
     * Test arrow functions that are grouped by parenthesis.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testGrouped()
    {
        $token = $this->getTargetToken('/* testGrouped */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 8);
    }
    //end testGrouped()
    /**
     * Test arrow functions that are used as array values.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testArrayValue()
    {
        $token = $this->getTargetToken('/* testArrayValue */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 4, 9, 'comma');
    }
    //end testArrayValue()
    /**
     * Test arrow functions that are used as array values with no trailing comma.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testArrayValueNoTrailingComma()
    {
        $token = $this->getTargetToken('/* testArrayValueNoTrailingComma */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 4, 8, 'closing parenthesis');
    }
    //end testArrayValueNoTrailingComma()
    /**
     * Test arrow functions that use the yield keyword.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testYield()
    {
        $token = $this->getTargetToken('/* testYield */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 14);
    }
    //end testYield()
    /**
     * Test arrow functions that use nullable type with unqualified class name.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNullableUnqualifiedClassName()
    {
        $token = $this->getTargetToken('/* testNullableUnqualifiedClassName */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 15, 18);
    }
    //end testNullableUnqualifiedClassName()
    /**
     * Test arrow functions that use namespace relative class name in the return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNamespaceRelativeClassNameInTypes()
    {
        $token = $this->getTargetToken('/* testNamespaceRelativeClassNameInTypes */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 16, 19);
    }
    //end testNamespaceRelativeClassNameInTypes()
    /**
     * Test arrow functions that use keyword return types.
     *
     * @param string $testMarker The comment prefacing the target token.
     *
     * @dataProvider dataKeywordReturnTypes
     * @covers       PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testKeywordReturnTypes($testMarker)
    {
        $token = $this->getTargetToken($testMarker, \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 11, 14);
    }
    //end testKeywordReturnTypes()
    /**
     * Data provider.
     *
     * @see testKeywordReturnTypes()
     *
     * @return array<string, array<string, string>>
     */
    public static function dataKeywordReturnTypes()
    {
        return ['self' => ['testMarker' => '/* testSelfReturnType */'], 'parent' => ['testMarker' => '/* testParentReturnType */'], 'callable' => ['testMarker' => '/* testCallableReturnType */'], 'array' => ['testMarker' => '/* testArrayReturnType */'], 'static' => ['testMarker' => '/* testStaticReturnType */'], 'false' => ['testMarker' => '/* testFalseReturnType */'], 'true' => ['testMarker' => '/* testTrueReturnType */'], 'null' => ['testMarker' => '/* testNullReturnType */']];
    }
    //end dataKeywordReturnTypes()
    /**
     * Test arrow function with a union parameter type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testUnionParamType()
    {
        $token = $this->getTargetToken('/* testUnionParamType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 13, 21);
    }
    //end testUnionParamType()
    /**
     * Test arrow function with a union return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testUnionReturnType()
    {
        $token = $this->getTargetToken('/* testUnionReturnType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 11, 18);
    }
    //end testUnionReturnType()
    /**
     * Test arrow function with a union return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testUnionReturnTypeWithTrue()
    {
        $token = $this->getTargetToken('/* testUnionReturnTypeWithTrue */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 11, 18);
    }
    //end testUnionReturnTypeWithTrue()
    /**
     * Test arrow function with a union return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testUnionReturnTypeWithFalse()
    {
        $token = $this->getTargetToken('/* testUnionReturnTypeWithFalse */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 11, 18);
    }
    //end testUnionReturnTypeWithFalse()
    /**
     * Test arrow function with an intersection parameter type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testIntersectionParamType()
    {
        $token = $this->getTargetToken('/* testIntersectionParamType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 13, 27);
    }
    //end testIntersectionParamType()
    /**
     * Test arrow function with an intersection return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testIntersectionReturnType()
    {
        $token = $this->getTargetToken('/* testIntersectionReturnType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 12, 20);
    }
    //end testIntersectionReturnType()
    /**
     * Test arrow function with a DNF parameter type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testDNFParamType()
    {
        $token = $this->getTargetToken('/* testDNFParamType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 17, 29);
    }
    //end testDNFParamType()
    /**
     * Test arrow function with a DNF return type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testDNFReturnType()
    {
        $token = $this->getTargetToken('/* testDNFReturnType */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 16, 29);
    }
    //end testDNFReturnType()
    /**
     * Test arrow function which returns by reference with a DNF parameter type.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testDNFParamTypeWithReturnByRef()
    {
        $token = $this->getTargetToken('/* testDNFParamTypeWithReturnByRef */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 15, 22);
    }
    //end testDNFParamTypeWithReturnByRef()
    /**
     * Test arrow functions used in ternary operators.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testTernary()
    {
        $tokens = $this->phpcsFile->getTokens();
        $token = $this->getTargetToken('/* testTernary */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 40);
        $token = $this->getTargetToken('/* testTernaryThen */', \T_FN);
        $this->backfillHelper($token);
        $expectedScopeOpener = $token + 8;
        $expectedScopeCloser = $token + 12;
        $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for THEN is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for THEN is not the semicolon token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for THEN is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for THEN is not the semicolon token');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], 'Closer scope opener for THEN is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for THEN is not the semicolon token');
        $token = $this->getTargetToken('/* testTernaryElse */', \T_FN);
        $this->backfillHelper($token, \true);
        $expectedScopeOpener = $token + 8;
        $expectedScopeCloser = $token + 11;
        $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for ELSE is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for ELSE is not the semicolon token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for ELSE is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for ELSE is not the semicolon token');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertSame($token - 24, $tokens[$closer]['scope_opener'], 'Closer scope opener for ELSE is not the arrow token of the "outer" arrow function (shared scope closer)');
        $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for ELSE is not the semicolon token');
    }
    //end testTernary()
    /**
     * Test typed arrow functions used in ternary operators.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testTernaryWithTypes()
    {
        $token = $this->getTargetToken('/* testTernaryWithTypes */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 15, 27);
    }
    //end testTernaryWithTypes()
    /**
     * Test arrow function returning a match control structure.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testWithMatchValue()
    {
        $token = $this->getTargetToken('/* testWithMatchValue */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 44);
    }
    //end testWithMatchValue()
    /**
     * Test arrow function returning a match control structure with something behind it.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testWithMatchValueAndMore()
    {
        $token = $this->getTargetToken('/* testWithMatchValueAndMore */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 48);
    }
    //end testWithMatchValueAndMore()
    /**
     * Test match control structure returning arrow functions.
     *
     * @param string $testMarker                 The comment prefacing the target token.
     * @param int    $openerOffset               The expected offset of the scope opener in relation to the testMarker.
     * @param int    $closerOffset               The expected offset of the scope closer in relation to the testMarker.
     * @param string $expectedCloserType         The type of token expected for the scope closer.
     * @param string $expectedCloserFriendlyName A friendly name for the type of token expected for the scope closer
     *                                           to be used in the error message for failing tests.
     *
     * @dataProvider dataInMatchValue
     * @covers       PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testInMatchValue($testMarker, $openerOffset, $closerOffset, $expectedCloserType, $expectedCloserFriendlyName)
    {
        $tokens = $this->phpcsFile->getTokens();
        $token = $this->getTargetToken($testMarker, \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, $openerOffset, $closerOffset, $expectedCloserFriendlyName);
        $this->assertSame($expectedCloserType, $tokens[$token + $closerOffset]['type'], 'Mismatched scope closer type');
    }
    //end testInMatchValue()
    /**
     * Data provider.
     *
     * @see testInMatchValue()
     *
     * @return array<string, array<string, string|int>>
     */
    public static function dataInMatchValue()
    {
        return ['not_last_value' => ['testMarker' => '/* testInMatchNotLastValue */', 'openerOffset' => 5, 'closerOffset' => 11, 'expectedCloserType' => 'T_COMMA', 'expectedCloserFriendlyName' => 'comma'], 'last_value_with_trailing_comma' => ['testMarker' => '/* testInMatchLastValueWithTrailingComma */', 'openerOffset' => 5, 'closerOffset' => 11, 'expectedCloserType' => 'T_COMMA', 'expectedCloserFriendlyName' => 'comma'], 'last_value_without_trailing_comma_1' => ['testMarker' => '/* testInMatchLastValueNoTrailingComma1 */', 'openerOffset' => 5, 'closerOffset' => 10, 'expectedCloserType' => 'T_CLOSE_PARENTHESIS', 'expectedCloserFriendlyName' => 'close parenthesis'], 'last_value_without_trailing_comma_2' => ['testMarker' => '/* testInMatchLastValueNoTrailingComma2 */', 'openerOffset' => 5, 'closerOffset' => 11, 'expectedCloserType' => 'T_VARIABLE', 'expectedCloserFriendlyName' => '$y variable']];
    }
    //end dataInMatchValue()
    /**
     * Test arrow function nested within a method declaration.
     *
     * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNestedInMethod()
    {
        $token = $this->getTargetToken('/* testNestedInMethod */', \T_FN);
        $this->backfillHelper($token);
        $this->scopePositionTestHelper($token, 5, 17);
    }
    //end testNestedInMethod()
    /**
     * Verify that "fn" keywords which are not arrow functions get tokenized as T_STRING and don't
     * have the extra token array indexes.
     *
     * @param string $testMarker  The comment prefacing the target token.
     * @param string $testContent The token content to look for.
     *
     * @dataProvider dataNotAnArrowFunction
     * @covers       PHP_CodeSniffer\Tokenizers\PHP::processAdditional
     *
     * @return void
     */
    public function testNotAnArrowFunction($testMarker, $testContent = 'fn')
    {
        $tokens = $this->phpcsFile->getTokens();
        $token = $this->getTargetToken($testMarker, [\T_STRING, \T_FN], $testContent);
        $tokenArray = $tokens[$token];
        $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as ' . $tokenArray['type'] . ', not T_STRING');
        $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set');
        $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set');
        $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set');
        $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set');
        $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set');
        $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set');
    }
    //end testNotAnArrowFunction()
    /**
     * Data provider.
     *
     * @see testNotAnArrowFunction()
     *
     * @return array<string, array<string, string>>
     */
    public static function dataNotAnArrowFunction()
    {
        return ['name of a function, context: declaration' => ['testMarker' => '/* testFunctionName */'], 'name of a constant, context: declaration using "const" keyword - uppercase' => ['testMarker' => '/* testConstantDeclaration */', 'testContent' => 'FN'], 'name of a constant, context: declaration using "const" keyword - lowercase' => ['testMarker' => '/* testConstantDeclarationLower */', 'testContent' => 'fn'], 'name of a (static) method, context: declaration' => ['testMarker' => '/* testStaticMethodName */'], 'name of a property, context: property access' => ['testMarker' => '/* testPropertyAssignment */'], 'name of a method, context: declaration in an anon class - mixed case' => ['testMarker' => '/* testAnonClassMethodName */', 'testContent' => 'fN'], 'name of a method, context: static method call' => ['testMarker' => '/* testNonArrowStaticMethodCall */'], 'name of a constant, context: constant access - uppercase' => ['testMarker' => '/* testNonArrowConstantAccess */', 'testContent' => 'FN'], 'name of a constant, context: constant access - mixed case' => ['testMarker' => '/* testNonArrowConstantAccessMixed */', 'testContent' => 'Fn'], 'name of a method, context: method call on object - lowercase' => ['testMarker' => '/* testNonArrowObjectMethodCall */'], 'name of a method, context: method call on object - uppercase' => ['testMarker' => '/* testNonArrowObjectMethodCallUpper */', 'testContent' => 'FN'], 'name of a (namespaced) function, context: partially qualified function call' => ['testMarker' => '/* testNonArrowNamespacedFunctionCall */', 'testContent' => 'Fn'], 'name of a (namespaced) function, context: namespace relative function call' => ['testMarker' => '/* testNonArrowNamespaceOperatorFunctionCall */'], 'name of a function, context: declaration with union types for param and return' => ['testMarker' => '/* testNonArrowFunctionNameWithUnionTypes */'], 'unknown - live coding/parse error' => ['testMarker' => '/* testLiveCoding */']];
    }
    //end dataNotAnArrowFunction()
    /**
     * Helper function to check that all token keys are correctly set for T_FN tokens.
     *
     * @param int  $token                The T_FN token to check.
     * @param bool $skipScopeCloserCheck Whether to skip the scope closer check.
     *                                   This should be set to "true" when testing nested arrow functions,
     *                                   where the "inner" arrow function shares a scope closer with the
     *                                   "outer" arrow function, as the 'scope_condition' for the scope closer
     *                                   of the "inner" arrow function will point to the "outer" arrow function.
     *
     * @return void
     */
    private function backfillHelper($token, $skipScopeCloserCheck = \false)
    {
        $tokens = $this->phpcsFile->getTokens();
        $this->assertArrayHasKey('scope_condition', $tokens[$token], 'Scope condition is not set');
        $this->assertArrayHasKey('scope_opener', $tokens[$token], 'Scope opener is not set');
        $this->assertArrayHasKey('scope_closer', $tokens[$token], 'Scope closer is not set');
        $this->assertSame($tokens[$token]['scope_condition'], $token, 'Scope condition is not the T_FN token');
        $this->assertArrayHasKey('parenthesis_owner', $tokens[$token], 'Parenthesis owner is not set');
        $this->assertArrayHasKey('parenthesis_opener', $tokens[$token], 'Parenthesis opener is not set');
        $this->assertArrayHasKey('parenthesis_closer', $tokens[$token], 'Parenthesis closer is not set');
        $this->assertSame($tokens[$token]['parenthesis_owner'], $token, 'Parenthesis owner is not the T_FN token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertArrayHasKey('scope_condition', $tokens[$opener], 'Opener scope condition is not set');
        $this->assertArrayHasKey('scope_opener', $tokens[$opener], 'Opener scope opener is not set');
        $this->assertArrayHasKey('scope_closer', $tokens[$opener], 'Opener scope closer is not set');
        $this->assertSame($tokens[$opener]['scope_condition'], $token, 'Opener scope condition is not the T_FN token');
        $this->assertSame(\T_FN_ARROW, $tokens[$opener]['code'], 'Arrow scope opener not tokenized as T_FN_ARROW (code)');
        $this->assertSame('T_FN_ARROW', $tokens[$opener]['type'], 'Arrow scope opener not tokenized as T_FN_ARROW (type)');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertArrayHasKey('scope_condition', $tokens[$closer], 'Closer scope condition is not set');
        $this->assertArrayHasKey('scope_opener', $tokens[$closer], 'Closer scope opener is not set');
        $this->assertArrayHasKey('scope_closer', $tokens[$closer], 'Closer scope closer is not set');
        if ($skipScopeCloserCheck === \false) {
            $this->assertSame($tokens[$closer]['scope_condition'], $token, 'Closer scope condition is not the T_FN token');
        }
        $opener = $tokens[$token]['parenthesis_opener'];
        $this->assertArrayHasKey('parenthesis_owner', $tokens[$opener], 'Opening parenthesis owner is not set');
        $this->assertSame($tokens[$opener]['parenthesis_owner'], $token, 'Opening parenthesis owner is not the T_FN token');
        $closer = $tokens[$token]['parenthesis_closer'];
        $this->assertArrayHasKey('parenthesis_owner', $tokens[$closer], 'Closing parenthesis owner is not set');
        $this->assertSame($tokens[$closer]['parenthesis_owner'], $token, 'Closing parenthesis owner is not the T_FN token');
    }
    //end backfillHelper()
    /**
     * Helper function to check that the scope opener/closer positions are correctly set for T_FN tokens.
     *
     * @param int    $token              The T_FN token to check.
     * @param int    $openerOffset       The expected offset of the scope opener in relation to
     *                                   the fn keyword.
     * @param int    $closerOffset       The expected offset of the scope closer in relation to
     *                                   the fn keyword.
     * @param string $expectedCloserType Optional. The type of token expected for the scope closer.
     *
     * @return void
     */
    private function scopePositionTestHelper($token, $openerOffset, $closerOffset, $expectedCloserType = 'semicolon')
    {
        $tokens = $this->phpcsFile->getTokens();
        $expectedScopeOpener = $token + $openerOffset;
        $expectedScopeCloser = $token + $closerOffset;
        $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer is not the ' . $expectedCloserType . ' token');
        $opener = $tokens[$token]['scope_opener'];
        $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer is not the ' . $expectedCloserType . ' token');
        $closer = $tokens[$token]['scope_closer'];
        $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], 'Closer scope opener is not the arrow token');
        $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer is not the ' . $expectedCloserType . ' token');
    }
    //end scopePositionTestHelper()
}
//end class
