<commit_before><?php
/**
 * (c) Spryker Systems GmbH copyright protected.
 */

namespace Spryker\Sniffs\Commenting;

use PHP_CodeSniffer_File;
use PHP_CodeSniffer_Tokens;
use Spryker\Sniffs\AbstractSniffs\AbstractSprykerSniff;

/**
 * Methods that may not return anything need to be declared as `@return void`.
 * Constructor and destructor may not have this addition, as they cannot return by definition.
 */
class DocBlockReturnVoidSniff extends AbstractSprykerSniff
{

    /**
     * @inheritdoc
     */
    public function register()
    {
        return [T_FUNCTION];
    }

    /**
     * @inheritdoc
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();

        $nextIndex = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true);
        if ($tokens[$nextIndex]['content'] === '__construct' || $tokens[$nextIndex]['content'] === '__destruct') {
            $this->checkConstructorAndDestructor($phpcsFile, $nextIndex);
            return;
        }

        // Don't mess with closures
        $prevIndex = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true);
        if (!$this->isGivenKind(PHP_CodeSniffer_Tokens::$methodPrefixes, $tokens[$prevIndex])) {
            return;
        }

        $docBlockEndIndex = $this->findRelatedDocBlock($phpcsFile, $stackPtr);
        if (!$docBlockEndIndex) {
            $phpcsFile->addError('Method does not have a doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
            //$this->addNewDocBlock($tokens, $docBlockIndex, $returnType);
            return;
        }

        $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener'];

        $docBlockReturnIndex = $this->findDocBlockReturn($phpcsFile, $docBlockStartIndex, $docBlockEndIndex);

        // If interface we will at least report it
        if (empty($tokens[$stackPtr]['scope_opener']) || empty($tokens[$stackPtr]['scope_closer'])) {
            if (!$docBlockReturnIndex) {
                $phpcsFile->addError('Method does not have a return statement in doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
            }
            return;
        }

        // We only look for void methods right now
        $returnType = $this->detectReturnTypeVoid($phpcsFile, $stackPtr);
        if ($returnType === null) {
            return;
        }

        if (!$docBlockReturnIndex) {
            if ($this->findDocBlockInheritDoc($phpcsFile, $docBlockStartIndex, $docBlockEndIndex)) {
                return;
            }

            // For now
            $phpcsFile->addError('Method does not have a return statement in doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
            //$this->addReturnAnnotation($docBlock, $returnType);
        }
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $index
     * @return void
     */
    protected function checkConstructorAndDestructor(PHP_CodeSniffer_File $phpcsFile, $index)
    {
        $docBlockEndIndex = $this->findRelatedDocBlock($phpcsFile, $index);
        if (!$docBlockEndIndex) {
            return;
        }

        $tokens = $phpcsFile->getTokens();

        $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener'];

        $docBlockReturnIndex = $this->findDocBlockReturn($phpcsFile, $docBlockStartIndex, $docBlockEndIndex);
        if (!$docBlockReturnIndex) {
            return;
        }

        $fix = $phpcsFile->addFixableError($tokens[$index]['content'] . ' has invalid return statement.', $docBlockReturnIndex);
        if ($fix) {
            $phpcsFile->fixer->replaceToken($docBlockReturnIndex, '');

            $possibleStringToken = $tokens[$docBlockReturnIndex + 2];
            if ($this->isGivenKind(T_DOC_COMMENT_STRING, $possibleStringToken)) {
                $phpcsFile->fixer->replaceToken($docBlockReturnIndex + 1, '');
                $phpcsFile->fixer->replaceToken($docBlockReturnIndex + 2, '');
            }
        }
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $docBlockStartIndex
     * @param int $docBlockEndIndex
     *
     * @return int|null
     */
    protected function findDocBlockReturn(PHP_CodeSniffer_File $phpcsFile, $docBlockStartIndex, $docBlockEndIndex)
    {
        $tokens = $phpcsFile->getTokens();

        for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) {
            if (!$this->isGivenKind(T_DOC_COMMENT_TAG, $tokens[$i])) {
                continue;
            }
            if ($tokens[$i]['content'] !== '@return') {
                continue;
            }

            return $i;
        }

        return null;
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $docBlockStartIndex
     * @param int $docBlockEndIndex
     *
     * @return int|null
     */
    protected function findDocBlockInheritDoc(PHP_CodeSniffer_File $phpcsFile, $docBlockStartIndex, $docBlockEndIndex)
    {
        $tokens = $phpcsFile->getTokens();

        for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) {
            if (!$this->isGivenKind(T_DOC_COMMENT_TAG, $tokens[$i])) {
                continue;
            }
            if (strtolower($tokens[$i]['content']) !== '@inheritdoc') {
                continue;
            }

            return $i;
        }

        return null;
    }

    /**
     * @param DocBlock $doc
     * @param string $returnType
     *
     * @return void
     */
    protected function addReturnAnnotation(DocBlock $doc, $returnType = 'void')
    {
        $lines = $doc->getLines();
        $count = count($lines);

        $lastLine = $doc->getLine($count - 1);
        $lastLineContent = $lastLine['content'];
        $whiteSpaceLength = strlen($lastLineContent) - 2;

        $returnLine = str_repeat(' ', $whiteSpaceLength) . '* @return ' . $returnType;
        $lastLineContent = $returnLine . "\n" . $lastLineContent;

        $lastLine->setContent($lastLineContent);
    }

    /**
     * For right now we only try to detect void.
     *
     * @param int $index
     *
     * @return string|null
     */
    protected function detectReturnTypeVoid(PHP_CodeSniffer_File $phpcsFile, $index)
    {
        $tokens = $phpcsFile->getTokens();

        $type = 'void';

        $methodStartIndex = $tokens[$index]['scope_opener'];
        $methodEndIndex = $tokens[$index]['scope_closer'];

        for ($i = $methodStartIndex + 1; $i < $methodEndIndex; ++$i) {
            if ($this->isGivenKind([T_FUNCTION], $tokens[$i])) {
                $endIndex = $tokens[$i]['scope_closer'];
                $i = $endIndex;
                continue;
            }

            if (!$this->isGivenKind([T_RETURN], $tokens[$i])) {
                continue;
            }

            $nextIndex = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $i + 1, null, true);
            if (!$this->isGivenKind(T_SEMICOLON, $tokens[$nextIndex])) {
                return null;
            }
        }

        return $type;
    }

    /**
     * @param int $docBlockIndex
     * @param string $returnType
     *
     * @return void
     */
    protected function addNewDocBlock(PHP_CodeSniffer_File $phpcsFile, $docBlockIndex, $returnType)
    {
        $tokens = $phpcsFile->getTokens();

        $docBlockTemplate = <<<TXT
/**
     * @return $returnType
     */

TXT;
        $docBlockTemplate = $docBlockTemplate . '    ' . $tokens[$docBlockIndex]['content'];

        $tokens[$docBlockIndex]->setContent($docBlockTemplate);
    }

}
<commit_msg>Fix CS<commit_after><?php

namespace Spryker\Sniffs\Commenting;

use PHP_CodeSniffer_File;
use PHP_CodeSniffer_Tokens;
use Spryker\Sniffs\AbstractSniffs\AbstractSprykerSniff;
use Spryker\Tools\Traits\CommentingTrait;

/**
 * Methods that may not return anything need to be declared as `@return void`.
 * Constructor and destructor may not have this addition, as they cannot return by definition.
 */
class DocBlockReturnVoidSniff extends AbstractSprykerSniff
{

    use CommentingTrait;

    /**
     * @inheritDoc
     */
    public function register()
    {
        return [T_FUNCTION];
    }

    /**
     * @inheritDoc
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();

        $nextIndex = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true);
        if ($tokens[$nextIndex]['content'] === '__construct' || $tokens[$nextIndex]['content'] === '__destruct') {
            $this->checkConstructorAndDestructor($phpcsFile, $nextIndex);
            return;
        }

        // Don't mess with closures
        $prevIndex = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr - 1, null, true);
        if (!$this->isGivenKind(PHP_CodeSniffer_Tokens::$methodPrefixes, $tokens[$prevIndex])) {
            return;
        }

        $docBlockEndIndex = $this->findRelatedDocBlock($phpcsFile, $stackPtr);
        if (!$docBlockEndIndex) {
            return;
        }

        $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener'];

        $docBlockReturnIndex = $this->findDocBlockReturn($phpcsFile, $docBlockStartIndex, $docBlockEndIndex);

        $hasInheritDoc = $this->hasInheritDoc($phpcsFile, $docBlockStartIndex, $docBlockEndIndex);

        // If interface we will at least report it
        if (empty($tokens[$stackPtr]['scope_opener']) || empty($tokens[$stackPtr]['scope_closer'])) {
            if (!$docBlockReturnIndex && !$hasInheritDoc) {
                $phpcsFile->addError('Method does not have a return statement in doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
            }
            return;
        }

        // If inheritdoc is present assume the parent contains it
        if ($docBlockReturnIndex || !$docBlockReturnIndex && $hasInheritDoc) {
            return;
        }

        // We only look for void methods right now
        $returnType = $this->detectReturnTypeVoid($phpcsFile, $stackPtr);
        if ($returnType === null) {
            $phpcsFile->addError('Method does not have a return statement in doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
            return;
        }

        $fix = $phpcsFile->addFixableError('Method does not have a return void statement in doc block: ' . $tokens[$nextIndex]['content'], $nextIndex);
        if (!$fix) {
            return;
        }

        $this->addReturnAnnotation($phpcsFile, $docBlockStartIndex, $docBlockEndIndex, $returnType);
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $index
     * @return void
     */
    protected function checkConstructorAndDestructor(PHP_CodeSniffer_File $phpcsFile, $index)
    {
        $docBlockEndIndex = $this->findRelatedDocBlock($phpcsFile, $index);
        if (!$docBlockEndIndex) {
            return;
        }

        $tokens = $phpcsFile->getTokens();

        $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener'];

        $docBlockReturnIndex = $this->findDocBlockReturn($phpcsFile, $docBlockStartIndex, $docBlockEndIndex);
        if (!$docBlockReturnIndex) {
            return;
        }

        $fix = $phpcsFile->addFixableError($tokens[$index]['content'] . ' has invalid return statement.', $docBlockReturnIndex);
        if ($fix) {
            $phpcsFile->fixer->replaceToken($docBlockReturnIndex, '');

            $possibleStringToken = $tokens[$docBlockReturnIndex + 2];
            if ($this->isGivenKind(T_DOC_COMMENT_STRING, $possibleStringToken)) {
                $phpcsFile->fixer->replaceToken($docBlockReturnIndex + 1, '');
                $phpcsFile->fixer->replaceToken($docBlockReturnIndex + 2, '');
            }
        }
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $docBlockStartIndex
     * @param int $docBlockEndIndex
     *
     * @return int|null
     */
    protected function findDocBlockReturn(PHP_CodeSniffer_File $phpcsFile, $docBlockStartIndex, $docBlockEndIndex)
    {
        $tokens = $phpcsFile->getTokens();

        for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) {
            if (!$this->isGivenKind(T_DOC_COMMENT_TAG, $tokens[$i])) {
                continue;
            }
            if ($tokens[$i]['content'] !== '@return') {
                continue;
            }

            return $i;
        }

        return null;
    }

    /**
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $docBlockStartIndex
     * @param int $docBlockEndIndex
     * @param string $returnType
     *
     * @return void
     */
    protected function addReturnAnnotation(PHP_CodeSniffer_File $phpcsFile, $docBlockStartIndex, $docBlockEndIndex, $returnType = 'void')
    {
        $tokens = $phpcsFile->getTokens();

        $indentation = $this->getIndentationWhitespace($phpcsFile, $docBlockEndIndex);

        $lastLineEndIndex = $phpcsFile->findPrevious([T_DOC_COMMENT_WHITESPACE], $docBlockEndIndex - 1, null, true);

        $phpcsFile->fixer->beginChangeset();
        $phpcsFile->fixer->addNewline($lastLineEndIndex);
        $phpcsFile->fixer->addContent($lastLineEndIndex, $indentation . '* @return ' . $returnType);
        $phpcsFile->fixer->endChangeset();
    }

    /**
     * For right now we only try to detect void.
     *
     * @param \PHP_CodeSniffer_File $phpcsFile
     * @param int $index
     *
     * @return string|null
     */
    protected function detectReturnTypeVoid(PHP_CodeSniffer_File $phpcsFile, $index)
    {
        $tokens = $phpcsFile->getTokens();

        $type = 'void';

        $methodStartIndex = $tokens[$index]['scope_opener'];
        $methodEndIndex = $tokens[$index]['scope_closer'];

        for ($i = $methodStartIndex + 1; $i < $methodEndIndex; ++$i) {
            if ($this->isGivenKind([T_FUNCTION], $tokens[$i])) {
                $endIndex = $tokens[$i]['scope_closer'];
                $i = $endIndex;
                continue;
            }

            if (!$this->isGivenKind([T_RETURN], $tokens[$i])) {
                continue;
            }

            $nextIndex = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $i + 1, null, true);
            if (!$this->isGivenKind(T_SEMICOLON, $tokens[$nextIndex])) {
                return null;
            }
        }

        return $type;
    }

}

{'fraction_non_alphanumeric': 0.13472577633307975, 'fraction_numerical': 0.0013832215229268967, 'mean_word_length': 2.504604944255938, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 84, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4735362', 'n_tokens_mistral': 4351, 'n_tokens_neox': 4257, 'n_words': 806}