protected function FunctionCommentSniff::processReturn in Coder 8.2
Same name and namespace in other branches
- 8.3 coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php \Drupal\Sniffs\Commenting\FunctionCommentSniff::processReturn()
- 8.3.x coder_sniffer/Drupal/Sniffs/Commenting/FunctionCommentSniff.php \Drupal\Sniffs\Commenting\FunctionCommentSniff::processReturn()
Process the return comment of this function comment.
Parameters
\PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.:
int $stackPtr The position of the current token: in the stack passed in $tokens.
int $commentStart The position in the stack where the comment started.:
Return value
void
1 call to FunctionCommentSniff::processReturn()
- FunctionCommentSniff::process in coder_sniffer/
Drupal/ Sniffs/ Commenting/ FunctionCommentSniff.php - Processes this test, when one of its tokens is encountered.
File
- coder_sniffer/
Drupal/ Sniffs/ Commenting/ FunctionCommentSniff.php, line 178
Class
- FunctionCommentSniff
- Parses and verifies the doc comments for functions. Largely copied from PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\FunctionCommentSniff.
Namespace
Drupal\Sniffs\CommentingCode
protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) {
$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';
$methodName = strtolower(ltrim($methodName, '_'));
$return = null;
foreach ($tokens[$commentStart]['comment_tags'] as $pos => $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;
// Any strings until the next tag belong to this comment.
if (isset($tokens[$commentStart]['comment_tags'][$pos + 1]) === true) {
$end = $tokens[$commentStart]['comment_tags'][$pos + 1];
}
else {
$end = $tokens[$commentStart]['comment_closer'];
}
}
}
$type = null;
if ($isSpecialMethod === false && $methodName !== $className) {
if ($return !== null) {
$type = trim($tokens[$return + 2]['content']);
if (empty($type) === true || $tokens[$return + 2]['code'] !== T_DOC_COMMENT_STRING) {
$error = 'Return type missing for @return tag in function comment';
$phpcsFile
->addError($error, $return, 'MissingReturnType');
}
else {
if (strpos($type, ' ') === false) {
// Check return type (can be multiple, separated by '|').
$typeNames = explode('|', $type);
$suggestedNames = array();
$hasNull = false;
$hasMultiple = false;
if (count($typeNames) > 0) {
$hasMultiple = true;
}
foreach ($typeNames as $i => $typeName) {
if (strtolower($typeName) === 'null') {
$hasNull = true;
}
$suggestedName = static::suggestType($typeName);
if (in_array($suggestedName, $suggestedNames) === false) {
$suggestedNames[] = $suggestedName;
}
}
$suggestedType = implode('|', $suggestedNames);
if ($type !== $suggestedType) {
$error = 'Expected "%s" but found "%s" for function return type';
$data = array(
$suggestedType,
$type,
);
$fix = $phpcsFile
->addFixableError($error, $return, 'InvalidReturn', $data);
if ($fix === true) {
$content = $suggestedType;
$phpcsFile->fixer
->replaceToken($return + 2, $content);
}
}
//end if
if ($type === 'void') {
$error = 'If there is no return value for a function, there must not be a @return tag.';
$phpcsFile
->addError($error, $return, 'VoidReturn');
}
else {
if ($type !== 'mixed') {
// If return type is not void, there needs to be a return statement
// somewhere in the function that returns something.
if (isset($tokens[$stackPtr]['scope_closer']) === true) {
$endToken = $tokens[$stackPtr]['scope_closer'];
$foundReturnToken = false;
$searchStart = $stackPtr;
$foundNonVoidReturn = false;
do {
$returnToken = $phpcsFile
->findNext(T_RETURN, $searchStart, $endToken);
if ($returnToken === false && $foundReturnToken === false) {
$error = '@return doc comment specified, but function has no return statement';
$phpcsFile
->addError($error, $return, 'InvalidNoReturn');
}
else {
// Check for return token as the last loop after the last return
// in the function will enter this else condition
// but without the returnToken.
if ($returnToken !== false) {
$foundReturnToken = true;
$semicolon = $phpcsFile
->findNext(T_WHITESPACE, $returnToken + 1, null, true);
if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
// Void return is allowed if the @return type has null in it.
if ($hasNull === false) {
$error = 'Function return type is not void, but function is returning void here';
$phpcsFile
->addError($error, $returnToken, 'InvalidReturnNotVoid');
}
}
else {
$foundNonVoidReturn = true;
}
//end if
$searchStart = $returnToken + 1;
}
//end if
}
//end if
} while ($returnToken !== false);
if ($foundNonVoidReturn === false && $foundReturnToken === true) {
$error = 'Function return type is not void, but function does not have a non-void return statement';
$phpcsFile
->addError($error, $return, 'InvalidReturnNotVoid');
}
}
//end if
}
}
//end if
}
}
//end if
$comment = '';
for ($i = $return + 3; $i < $end; $i++) {
if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
$indent = 0;
if ($tokens[$i - 1]['code'] === T_DOC_COMMENT_WHITESPACE) {
$indent = strlen($tokens[$i - 1]['content']);
}
$comment .= ' ' . $tokens[$i]['content'];
$commentLines[] = array(
'comment' => $tokens[$i]['content'],
'token' => $i,
'indent' => $indent,
);
if ($indent < 3) {
$error = 'Return comment indentation must be 3 spaces, found %s spaces';
$fix = $phpcsFile
->addFixableError($error, $i, 'ReturnCommentIndentation', array(
$indent,
));
if ($fix === true) {
$phpcsFile->fixer
->replaceToken($i - 1, ' ');
}
}
}
}
//end for
// The first line of the comment must be indented no more than 3
// spaces, the following lines can be more so we only check the first
// line.
if (empty($commentLines[0]['indent']) === false && $commentLines[0]['indent'] > 3) {
$error = 'Return comment indentation must be 3 spaces, found %s spaces';
$fix = $phpcsFile
->addFixableError($error, $commentLines[0]['token'] - 1, 'ReturnCommentIndentation', array(
$commentLines[0]['indent'],
));
if ($fix === true) {
$phpcsFile->fixer
->replaceToken($commentLines[0]['token'] - 1, ' ');
}
}
if ($comment === '' && $type !== '$this' && $type !== 'static') {
if (strpos($type, ' ') !== false) {
$error = 'Description for the @return value must be on the next line';
}
else {
$error = 'Description for the @return value is missing';
}
$phpcsFile
->addError($error, $return, 'MissingReturnComment');
}
else {
if (strpos($type, ' ') !== false) {
if (preg_match('/^([^\\s]+)[\\s]+(\\$[^\\s]+)[\\s]*$/', $type, $matches) === 1) {
$error = 'Return type must not contain variable name "%s"';
$data = array(
$matches[2],
);
$fix = $phpcsFile
->addFixableError($error, $return + 2, 'ReturnVarName', $data);
if ($fix === true) {
$phpcsFile->fixer
->replaceToken($return + 2, $matches[1]);
}
}
else {
$error = 'Return type "%s" must not contain spaces';
$data = array(
$type,
);
$phpcsFile
->addError($error, $return, 'ReturnTypeSpaces', $data);
}
}
}
//end if
}
//end if
}
else {
// No return tag for constructor and destructor.
if ($return !== null) {
$error = '@return tag is not required for constructor and destructor';
$phpcsFile
->addError($error, $return, 'ReturnNotRequired');
}
}
//end if
}