<?php
/**
 * PHPCompatibility, an external standard for PHP_CodeSniffer.
 *
 * @package   PHPCompatibility
 * @copyright 2012-2019 PHPCompatibility Contributors
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
 * @link      https://github.com/PHPCompatibility/PHPCompatibility
 */

namespace PHPCompatibility\Sniffs\UseDeclarations;

use PHPCompatibility\Sniff;
use PHP_CodeSniffer_File as File;
use PHP_CodeSniffer_Tokens as Tokens;

/**
 * Detect group use declarations as introduced in PHP 7.0.
 *
 * Checks for:
 * - Group use statements as introduced in PHP 7.0.
 * - Trailing comma's in group use statements as allowed since PHP 7.2.
 *
 * PHP version 7.0
 * PHP version 7.2
 *
 * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.group-use-declarations
 * @link https://www.php.net/manual/en/migration72.new-features.php#migration72.new-features.trailing-comma-in-grouped-namespaces
 * @link https://wiki.php.net/rfc/group_use_declarations
 * @link https://wiki.php.net/rfc/list-syntax-trailing-commas
 * @link https://www.php.net/manual/en/language.namespaces.importing.php#language.namespaces.importing.group
 *
 * @since 7.0.0
 * @since 8.0.1 Now also checks for trailing comma's in group `use` declarations.
 */
class NewGroupUseDeclarationsSniff extends Sniff {

	/**
	 * Returns an array of tokens this test wants to listen for.
	 *
	 * @since 7.0.0
	 *
	 * @return array
	 */
	public function register() {
		if ( \defined( 'T_OPEN_USE_GROUP' ) ) {
			return array( \T_OPEN_USE_GROUP );
		} else {
			return array( \T_USE );
		}
	}


	/**
	 * Processes this test, when one of its tokens is encountered.
	 *
	 * @since 7.0.0
	 *
	 * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
	 * @param int                   $stackPtr  The position of the current token in
	 *                                         the stack passed in $tokens.
	 *
	 * @return void
	 */
	public function process( File $phpcsFile, $stackPtr ) {
		if ( $this->supportsBelow( '7.1' ) === false ) {
			return;
		}

		$tokens = $phpcsFile->getTokens();
		$token  = $tokens[ $stackPtr ];

		// Deal with PHPCS pre-2.6.0.
		if ( $token['code'] === \T_USE ) {
			$hasCurlyBrace = $phpcsFile->findNext( \T_OPEN_CURLY_BRACKET, ( $stackPtr + 1 ), null, false, null, true );
			if ( $hasCurlyBrace === false ) {
				return;
			}

			$prevToken = $phpcsFile->findPrevious( Tokens::$emptyTokens, ( $hasCurlyBrace - 1 ), null, true );
			if ( $prevToken === false || $tokens[ $prevToken ]['code'] !== \T_NS_SEPARATOR ) {
				return;
			}

			$stackPtr = $hasCurlyBrace;
		}

		// Still here ? In that case, it is a group use statement.
		if ( $this->supportsBelow( '5.6' ) === true ) {
			$phpcsFile->addError(
				'Group use declarations are not allowed in PHP 5.6 or earlier',
				$stackPtr,
				'Found'
			);
		}

		$closers = array( \T_CLOSE_CURLY_BRACKET );
		if ( \defined( 'T_CLOSE_USE_GROUP' ) ) {
			$closers[] = \T_CLOSE_USE_GROUP;
		}

		$closeCurly = $phpcsFile->findNext( $closers, ( $stackPtr + 1 ), null, false, null, true );
		if ( $closeCurly === false ) {
			return;
		}

		$prevToken = $phpcsFile->findPrevious( Tokens::$emptyTokens, ( $closeCurly - 1 ), null, true );
		if ( $tokens[ $prevToken ]['code'] === \T_COMMA ) {
			$phpcsFile->addError(
				'Trailing comma\'s are not allowed in group use statements in PHP 7.1 or earlier',
				$prevToken,
				'TrailingCommaFound'
			);
		}
	}
}
