diff --git a/src/Tokenizers/Tokenizer.php b/src/Tokenizers/Tokenizer.php index bfe8c7a143..2a4cb7cd89 100644 --- a/src/Tokenizers/Tokenizer.php +++ b/src/Tokenizers/Tokenizer.php @@ -1142,6 +1142,13 @@ private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0) continue; } + if ($tokenType === T_START_HEREDOC) { + // Heredocs are special because they can be used as a value, including + // inside a function call or as a default value for a parameter. + // So if we find them nested inside another opener, just skip them. + continue; + } + if ($tokenType === T_FUNCTION && $this->tokens[$stackPtr]['code'] !== T_FUNCTION ) { diff --git a/tests/Core/Tokenizer/ScopeOwnerTest.inc b/tests/Core/Tokenizer/ScopeOwnerTest.inc new file mode 100644 index 0000000000..a6a17af270 --- /dev/null +++ b/tests/Core/Tokenizer/ScopeOwnerTest.inc @@ -0,0 +1,20 @@ + + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +final class ScopeOwnerTest extends AbstractMethodUnitTest +{ + + + /** + * Test that a basic 'if' condition gets scope opener/closer set as expected. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int $conditionWidth How many tokens wide the 'if' condition should be. + * + * @dataProvider dataIfCondition + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testIfCondition($testMarker, $conditionWidth) + { + $tokens = self::$phpcsFile->getTokens(); + $targetIf = $this->getTargetToken($testMarker, T_IF); + + $this->assertSame($targetIf, $tokens[$targetIf]['parenthesis_owner'], 'parenthesis owner is self for if'); + $this->assertSame(($targetIf + 2), $tokens[$targetIf]['parenthesis_opener'], 'expected parenthesis opener'); + $this->assertSame(($targetIf + 2 + $conditionWidth), $tokens[$targetIf]['parenthesis_closer'], 'expected parenthesis closer'); + $this->assertSame($targetIf, $tokens[$targetIf]['scope_condition']); + + $targetOpenCurly = self::$phpcsFile->findNext(T_OPEN_CURLY_BRACKET, $targetIf); + $this->assertSame($targetIf, $tokens[$targetOpenCurly]['scope_condition'], 'scope_condition set on open curly'); + + $targetCloseCurly = $tokens[$targetOpenCurly]['bracket_closer']; + $this->assertSame($targetIf, $tokens[$targetCloseCurly]['scope_condition'], 'scope_condition set on close curly'); + + $targetReturn = self::$phpcsFile->findNext(T_RETURN, $targetIf); + $this->assertSame([$targetIf => T_IF], $tokens[$targetReturn]['conditions'], 'conditions set on if statement body'); + + }//end testIfCondition() + + + /** + * Data provider. + * + * @see testIfCondition() + * + * @return array> + */ + public static function dataIfCondition() + { + return [ + 'Basic if' => [ + 'testMarker' => '/* testNormalIfCondition */', + 'conditionWidth' => 2, + ], + 'Function call in if condition' => [ + 'testMarker' => '/* testFunctionCallInIfCondition */', + 'conditionWidth' => 5, + ], + 'Heredoc in if condition' => [ + 'testMarker' => '/* testHeredocInIfCondition */', + 'conditionWidth' => 8, + ], + ]; + + }//end dataIfCondition() + + +}//end class