Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 0 additions & 174 deletions CakePHP/Sniffs/Commenting/FunctionCommentSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,183 +175,9 @@ public function process(File $phpcsFile, $stackPtr)
}
}

$this->processReturn($phpcsFile, $stackPtr, $commentStart);
$this->processThrows($phpcsFile, $stackPtr, $commentStart);
}

/**
* Checks if the doc comment is an inheritDoc comment.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $commentStart The position in the stack where the comment started.
* @return bool True if the comment is an inheritdoc
*/
protected function isInheritDoc(File $phpcsFile, $commentStart)
{
$tokens = $phpcsFile->getTokens();

$empty = [
T_DOC_COMMENT_WHITESPACE,
T_DOC_COMMENT_STAR,
];

$commentEnd = $tokens[$commentStart]['comment_closer'];
$inheritDoc = $phpcsFile->findNext($empty, $commentStart + 1, $commentEnd, true);
if ($inheritDoc === false) {
return false;
}

if (preg_match('/^@inheritDoc$/i', $tokens[$inheritDoc]['content']) === 1) {
return true;
}

if (preg_match('/^{@inheritDoc}$/i', $tokens[$inheritDoc]['content']) !== 1) {
return false;
}

$notAllowed = ['@param', '@return'];
foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
if (in_array($tokens[$tag]['content'], $notAllowed, true)) {
return false;
}
}

return true;
}

/**
* Process the return comment of this function comment.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
* @param int $commentStart The position in the stack where the comment started.
* @return void
*/
protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
{
if ($this->isInheritDoc($phpcsFile, $commentStart)) {
return;
}

$tokens = $phpcsFile->getTokens();

// Skip constructor and destructor.
$className = '';
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
if ($condition === T_CLASS || $condition === T_INTERFACE) {
$className = $phpcsFile->getDeclarationName($condPtr);
$className = strtolower(ltrim($className, '_'));
}
}

$methodName = $phpcsFile->getDeclarationName($stackPtr);
$isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
if ($methodName !== '_') {
$methodName = strtolower(ltrim($methodName, '_'));
}

$return = null;
foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
if ($tokens[$tag]['content'] === '@return') {
if ($return !== null) {
$error = 'Only 1 @return tag is allowed in a function comment';
$phpcsFile->addError($error, $tag, 'DuplicateReturn');

return;
}

$return = $tag;
}
}

if ($isSpecialMethod === true) {
return;
}

if ($return === null) {
$error = 'Missing @return tag in function comment';
$phpcsFile->addWarning($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn');

return;
}

$content = $tokens[$return + 2]['content'];
if (empty($content) === true || $tokens[$return + 2]['code'] !== T_DOC_COMMENT_STRING) {
$error = 'Return type missing for @return tag in function comment';
$phpcsFile->addError($error, $return, 'MissingReturnType');

return;
}

$endToken = $tokens[$stackPtr]['scope_closer'] ?? false;
if (!$endToken) {
return;
}

[$types, ] = explode(' ', $content);
$typeNames = explode('|', $types);

// If the return type is void, make sure there is
// no non-void return statements in the function.
if ($typeNames === ['void']) {
for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) {
if ($tokens[$returnToken]['code'] === T_CLOSURE) {
$returnToken = $tokens[$returnToken]['scope_closer'];
continue;
}

if (
$tokens[$returnToken]['code'] === T_RETURN
|| $tokens[$returnToken]['code'] === T_YIELD
|| $tokens[$returnToken]['code'] === T_YIELD_FROM
) {
break;
}
}

if ($returnToken !== $endToken) {
// If the function is not returning anything, just
// exiting, then there is no problem.
$semicolon = $phpcsFile->findNext(T_WHITESPACE, $returnToken + 1, null, true);
if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
$error = 'Function return type is void, but function contains return statement';
$phpcsFile->addWarning($error, $return, 'InvalidReturnVoid');
}
}

return;
}

// If return type is not void, there needs to be a return statement
// somewhere in the function that returns something.
if (!in_array('mixed', $typeNames, true) && !in_array('void', $typeNames, true)) {
$returnToken = $phpcsFile->findNext([T_RETURN, T_YIELD, T_YIELD_FROM], $stackPtr, $endToken);
if ($returnToken === false && !$this->hasException($phpcsFile, $stackPtr, $endToken)) {
$error = 'Function return type is not void, but function has no return statement';
$phpcsFile->addWarning($error, $return, 'InvalidNoReturn');
} else {
$semicolon = $phpcsFile->findNext(T_WHITESPACE, $returnToken + 1, null, true);
if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
$error = 'Function return type is not void, but function is returning void here';
$phpcsFile->addWarning($error, $returnToken, 'InvalidReturnNotVoid');
}
}
}
}

/**
* @param \PHP_CodeSniffer\Files\File $phpcsFile File
* @param int $startIndex Start index
* @param int $endIndex End index
* @return bool
*/
protected function hasException(File $phpcsFile, $startIndex, $endIndex)
{
$throwIndex = $phpcsFile->findNext([T_THROW], $startIndex, $endIndex);

return $throwIndex !== false;
}

/**
* Process any throw tags that this function comment has.
*
Expand Down
132 changes: 0 additions & 132 deletions CakePHP/Tests/Commenting/FunctionCommentUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,6 @@ use Other\Error as OtherError;

class Foo
{
/**
* Some sentence.
*
* @return void
*/
public function invalidReturnVoid()
{
return 'string';
}

/**
* [doThing description]
*
* @param string $foo Foo foo foo.
* @return void
*/
public function doThing($foo)
{
}

/**
* Test throws
*
Expand All @@ -52,45 +32,6 @@ class Foo
}
}

/**
* @return void
* @return void
*/
public function doubleReturn()
{
}

/**
* @return
*/
public function missingReturnType()
{
}

/**
* @return int
*/
public function returnIntPass()
{
return 1;
}

/**
* @return bool
*/
public function returnBooleanPass()
{
return false;
}

/**
* @return int
*/
public function invalidReturnNotVoid()
{
return;
}

/**
* @return void
* @throws
Expand All @@ -100,77 +41,4 @@ class Foo
throw new \RuntimeException();
return;
}

/**
*
*/
public function missingReturn()
{
return 'what';
}

/**
* @inheritDoc
*/
public function inherited()
{
}

/**
* @inheritdoc
*/
public function inheritedCaseInsentive()
{
}

/**
* Some sentence.
*
* @param \Foo&\Bar $param Some Param.
* @return void
*/
public function intersectionType($param)
{
}

/**
* {@inheritDoc}
*/
public function withInheritDocNoTags($param)
{
}

/**
* {@inheritDoc}
*
* @throws \Exception Comment.
* @see \Exception
* @link http://cakephp.org
* @psalm-suppress SomeError
* @phpstan-tag SomeError
*/
public function withInheritDocAllowedTags($param)
{
}

/**
* {@inheritDoc}
*
* @param int $param This is not allowed on its own.
*/
public function withInheritDocIncompleteTags($param)
{
}

/**
* Some sentence.
*
* @param integer $param Some Param.
* @param boolean $otherParam Some Other Param.
* @return string Something.
*/
#[ReturnTypeWillChange]
public function returnWillChange($param, $otherParam)
{
}
}
Loading