/**
  * Process the return comment of this function comment.
  *
  * @param int $commentStart The position in the stack where the comment started.
  * @param int $commentEnd   The position in the stack where the comment ended.
  *
  * @return void
  */
 protected function processReturn($commentStart, $commentEnd)
 {
     // Skip constructor and destructor.
     $className = '';
     if ($this->_classToken !== null) {
         $className = $this->currentFile->getDeclarationName($this->_classToken);
         $className = strtolower(ltrim($className, '_'));
     }
     $methodName = strtolower(ltrim($this->_methodName, '_'));
     $isSpecialMethod = $this->_methodName === '__construct' || $this->_methodName === '__destruct';
     if ($isSpecialMethod === false && $methodName !== $className) {
         // Report missing return tag.
         if ($this->commentParser->getReturn() === null) {
             $error = 'Missing @return tag in function comment';
             $this->currentFile->addError($error, $commentEnd, 'MissingReturn');
         } else {
             if (trim($this->commentParser->getReturn()->getRawContent()) === '') {
                 $error = '@return tag is empty in function comment';
                 $errorPos = $commentStart + $this->commentParser->getReturn()->getLine();
                 $this->currentFile->addError($error, $errorPos, 'EmptyReturn');
             }
         }
     }
 }
 /**
  * Process the return comment of this function comment.
  *
  * @param int $commentStart The position in the stack where the comment started.
  * @param int $commentEnd   The position in the stack where the comment ended.
  *
  * @return void
  */
 protected function processReturn($commentStart, $commentEnd)
 {
     // Skip constructor and destructor.
     $className = '';
     if ($this->_classToken !== null) {
         $className = $this->helper->getCurrentFile()->getDeclarationName($this->_classToken);
         $className = strtolower(ltrim($className, '_'));
     }
     $methodName = strtolower(ltrim($this->_methodName, '_'));
     $return = $this->commentParser->getReturn();
     if ($this->_methodName !== '__construct' && $this->_methodName !== '__destruct') {
         if ($return !== null) {
             $tagOrder = $this->commentParser->getTagOrders();
             $index = array_keys($tagOrder, 'return');
             $errorPos = $commentStart + $return->getLine();
             $content = trim($return->getRawContent());
             if (count($index) > 1) {
                 $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::DUPLICATE_RETURN);
                 return;
             }
             $since = array_keys($tagOrder, 'since');
             if (count($since) === 1 && $this->_tagIndex !== 0) {
                 $this->_tagIndex++;
                 if ($index[0] !== $this->_tagIndex) {
                     $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::RETURN_ORDER);
                 }
             }
             if (empty($content) === true) {
                 $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::MISSING_RETURN_TYPE);
             } else {
                 // Strip off any comments attached to our content
                 $parts = explode(' ', $content);
                 $content = $parts[0];
                 // Check return type (can be multiple, separated by '|').
                 $typeNames = explode('|', $content);
                 $suggestedNames = [];
                 foreach ($typeNames as $i => $typeName) {
                     $suggestedName = $this->helper->suggestType($typeName);
                     if (in_array($suggestedName, $suggestedNames) === false) {
                         $suggestedNames[] = $suggestedName;
                     }
                 }
                 $suggestedType = implode('|', $suggestedNames);
                 if ($content !== $suggestedType) {
                     $data = [$content];
                     $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::INVALID_RETURN, $data);
                 } elseif ($this->helper->isAmbiguous($typeName, $matches)) {
                     // Warn about ambiguous types ie array or mixed
                     $data = [$matches[1], '@return'];
                     $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::AMBIGUOUS_TYPE, $data);
                 }
                 $tokens = $this->helper->getCurrentFile()->getTokens();
                 // If the return type is void, make sure there is
                 // no return statement in the function.
                 if ($content === 'void') {
                     if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                         $endToken = $tokens[$this->_functionToken]['scope_closer'];
                         $tokens = $this->helper->getCurrentFile()->getTokens();
                         for ($returnToken = $this->_functionToken; $returnToken < $endToken; $returnToken++) {
                             if ($tokens[$returnToken]['code'] === T_CLOSURE) {
                                 $returnToken = $tokens[$returnToken]['scope_closer'];
                                 continue;
                             }
                             if ($tokens[$returnToken]['code'] === T_RETURN) {
                                 break;
                             }
                         }
                         if ($returnToken !== $endToken) {
                             // If the function is not returning anything, just
                             // exiting, then there is no problem.
                             $semicolon = $this->helper->getCurrentFile()->findNext(T_WHITESPACE, $returnToken + 1, null, true);
                             if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
                                 $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::INVALID_RETURN_VOID);
                             }
                         }
                     }
                 } elseif ($content !== 'mixed') {
                     // If return type is not void, there needs to be a
                     // returns statement somewhere in the function that
                     // returns something.
                     if (isset($tokens[$this->_functionToken]['scope_closer']) === true) {
                         $endToken = $tokens[$this->_functionToken]['scope_closer'];
                         $returnToken = $this->helper->getCurrentFile()->findNext(T_RETURN, $this->_functionToken, $endToken);
                         if ($returnToken === false) {
                             $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::INVALID_NO_RETURN);
                         } else {
                             $semicolon = $this->helper->getCurrentFile()->findNext(T_WHITESPACE, $returnToken + 1, null, true);
                             if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
                                 $this->helper->addMessage($returnToken, M2_Sniffs_Annotations_Helper::INVALID_RETURN_NOT_VOID);
                             }
                         }
                     }
                 }
                 $spacing = substr_count($return->getWhitespaceBeforeValue(), ' ');
                 if ($spacing !== 1) {
                     $data = [$spacing];
                     $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::RETURN_INDENT, $data);
                 }
             }
         } else {
             $this->helper->addMessage($commentEnd, M2_Sniffs_Annotations_Helper::MISSING_RETURN);
         }
     } elseif ($return !== null) {
         // No return tag for constructor and destructor.
         $errorPos = $commentStart + $return->getLine();
         $this->helper->addMessage($errorPos, M2_Sniffs_Annotations_Helper::RETURN_NOT_REQUIRED);
     }
 }
Esempio n. 3
0
 /**
  * {@inheritdoc}
  */
 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 {
     if ($this->should_ignore_use($phpcsFile, $stackPtr) === true) {
         return;
     }
     $tokens = $phpcsFile->getTokens();
     $class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $stackPtr + 1);
     $find = array(T_NS_SEPARATOR, T_STRING, T_WHITESPACE);
     $class_name_end = $phpcsFile->findNext($find, $stackPtr + 1, null, true);
     $aliasing_as_position = $phpcsFile->findNext(T_AS, $class_name_end, null, false, null, true);
     if ($aliasing_as_position !== false) {
         $alias_position = $phpcsFile->findNext(T_STRING, $aliasing_as_position, null, false, null, true);
         $class_name_short = $tokens[$alias_position]['content'];
         $class_name_full = $phpcsFile->getTokensAsString($class_name_start, $class_name_end - $class_name_start - 1);
     } else {
         $class_name_full = $phpcsFile->getTokensAsString($class_name_start, $class_name_end - $class_name_start);
         $class_name_short = $tokens[$class_name_end - 1]['content'];
     }
     $ok = false;
     // Checks in simple statements (new, instanceof and extends)
     foreach (array(T_INSTANCEOF, T_NEW, T_EXTENDS) as $keyword) {
         $old_simple_statement = $stackPtr;
         while (($simple_statement = $phpcsFile->findNext($keyword, $old_simple_statement + 1)) !== false) {
             $old_simple_statement = $simple_statement;
             $simple_class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $simple_statement + 1);
             $simple_class_name_end = $phpcsFile->findNext($find, $simple_statement + 1, null, true);
             $simple_class_name = trim($phpcsFile->getTokensAsString($simple_class_name_start, $simple_class_name_end - $simple_class_name_start));
             $ok = $this->check($phpcsFile, $simple_class_name, $class_name_full, $class_name_short, $simple_statement) ? true : $ok;
         }
     }
     // Checks paamayim nekudotayim
     $old_paamayim_nekudotayim = $stackPtr;
     while (($paamayim_nekudotayim = $phpcsFile->findNext(T_PAAMAYIM_NEKUDOTAYIM, $old_paamayim_nekudotayim + 1)) !== false) {
         $old_paamayim_nekudotayim = $paamayim_nekudotayim;
         $paamayim_nekudotayim_class_name_start = $phpcsFile->findPrevious($find, $paamayim_nekudotayim - 1, null, true);
         $paamayim_nekudotayim_class_name_end = $paamayim_nekudotayim - 1;
         $paamayim_nekudotayim_class_name = trim($phpcsFile->getTokensAsString($paamayim_nekudotayim_class_name_start + 1, $paamayim_nekudotayim_class_name_end - $paamayim_nekudotayim_class_name_start));
         $ok = $this->check($phpcsFile, $paamayim_nekudotayim_class_name, $class_name_full, $class_name_short, $paamayim_nekudotayim) ? true : $ok;
     }
     // Checks in implements
     $old_implements = $stackPtr;
     while (($implements = $phpcsFile->findNext(T_IMPLEMENTS, $old_implements + 1)) !== false) {
         $old_implements = $implements;
         $old_implemented_class = $implements;
         while (($implemented_class = $phpcsFile->findNext(T_STRING, $old_implemented_class + 1, null, false, null, true)) !== false) {
             $old_implemented_class = $implemented_class;
             $implements_class_name_start = $phpcsFile->findNext(array(T_NS_SEPARATOR, T_STRING), $implemented_class - 1);
             $implements_class_name_end = $phpcsFile->findNext($find, $implemented_class - 1, null, true);
             $implements_class_name = trim($phpcsFile->getTokensAsString($implements_class_name_start, $implements_class_name_end - $implements_class_name_start));
             $ok = $this->check($phpcsFile, $implements_class_name, $class_name_full, $class_name_short, $implements) ? true : $ok;
         }
     }
     // Checks in type hinting
     $old_function_declaration = $stackPtr;
     while (($function_declaration = $phpcsFile->findNext(T_FUNCTION, $old_function_declaration + 1)) !== false) {
         $old_function_declaration = $function_declaration;
         // Check docblocks
         $find = array(T_COMMENT, T_DOC_COMMENT, T_CLASS, T_FUNCTION, T_OPEN_TAG);
         $comment_end = $phpcsFile->findPrevious($find, $function_declaration - 1);
         if ($comment_end !== false) {
             if (!$tokens[$comment_end]['code'] !== T_DOC_COMMENT) {
                 $comment_start = $phpcsFile->findPrevious(T_DOC_COMMENT, $comment_end - 1, null, true) + 1;
                 $comment = $phpcsFile->getTokensAsString($comment_start, $comment_end - $comment_start + 1);
                 try {
                     $comment_parser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
                     $comment_parser->parse();
                     // Check @param
                     foreach ($comment_parser->getParams() as $param) {
                         $type = $param->getType();
                         $types = explode('|', str_replace('[]', '', $type));
                         foreach ($types as $type) {
                             $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $param->getLine() + $comment_start) ? true : $ok;
                         }
                     }
                     // Check @return
                     $return = $comment_parser->getReturn();
                     if ($return !== null) {
                         $type = $return->getValue();
                         $types = explode('|', str_replace('[]', '', $type));
                         foreach ($types as $type) {
                             $ok = $this->check($phpcsFile, $type, $class_name_full, $class_name_short, $return->getLine() + $comment_start) ? true : $ok;
                         }
                     }
                 } catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
                     $line = $e->getLineWithinComment() + $comment_start;
                     $phpcsFile->addError($e->getMessage(), $line, 'FailedParse');
                 }
             }
         }
         // Check type hint
         $params = $phpcsFile->getMethodParameters($function_declaration);
         foreach ($params as $param) {
             $ok = $this->check($phpcsFile, $param['type_hint'], $class_name_full, $class_name_short, $function_declaration) ? true : $ok;
         }
     }
     if (!$ok) {
         $error = 'There must not be unused USE statements.';
         $phpcsFile->addError($error, $stackPtr, 'Unused');
     }
 }