diff --git a/src/Psalm/DocComment.php b/src/Psalm/DocComment.php index 6d6cc074a..7fd719fc9 100644 --- a/src/Psalm/DocComment.php +++ b/src/Psalm/DocComment.php @@ -49,7 +49,8 @@ class DocComment $last = false; } elseif ($last !== false) { $old_last_line = $lines[$last]; - $lines[$last] = rtrim($old_last_line) . ($preserve_format ? "\n" . $line : ' ' . trim($line)); + $lines[$last] = rtrim($old_last_line) + . ($preserve_format || trim($old_last_line) === '@return' ? "\n" . $line : ' ' . trim($line)); if ($line_number) { $old_line_number = $line_map[$old_last_line]; diff --git a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php index ac9db9cb2..370fd3dd6 100644 --- a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php @@ -256,36 +256,7 @@ class CommentAnalyzer ? $comments['specials']['psalm-return'] : $comments['specials']['return']; - $return_block = trim((string)reset($return_specials)); - - if (!$return_block) { - throw new DocblockParseException('Missing @return type'); - } - - try { - $line_parts = self::splitDocLine($return_block); - } catch (DocblockParseException $e) { - throw $e; - } - - if (!preg_match('/\[[^\]]+\]/', $line_parts[0]) - && $line_parts[0][0] !== '{' - ) { - if ($line_parts[0][0] === '$' && !preg_match('/^\$this(\||$)/', $line_parts[0])) { - throw new IncorrectDocblockException('Misplaced variable'); - } - - $info->return_type = array_shift($line_parts); - $info->return_type_description = $line_parts ? implode(' ', $line_parts) : null; - - $line_number = array_keys($return_specials)[0]; - - if ($line_number) { - $info->return_type_line_number = $line_number; - } - } else { - throw new DocblockParseException('Badly-formatted @return type'); - } + self::extractReturnType((string) reset($return_specials), array_keys($return_specials)[0], $info); } if (isset($comments['specials']['param']) || isset($comments['specials']['psalm-param'])) { @@ -485,6 +456,47 @@ class CommentAnalyzer return $info; } + /** + * @return void + */ + private static function extractReturnType(string $return_block, int $line_number, FunctionDocblockComment $info) + { + $return_lines = explode("\n", $return_block); + + if (!trim($return_lines[0])) { + return; + } + + $return_block = trim($return_block); + + if (!$return_block) { + return; + } + + try { + $line_parts = self::splitDocLine($return_block); + } catch (DocblockParseException $e) { + throw $e; + } + + if (!preg_match('/\[[^\]]+\]/', $line_parts[0]) + && $line_parts[0][0] !== '{' + ) { + if ($line_parts[0][0] === '$' && !preg_match('/^\$this(\||$)/', $line_parts[0])) { + throw new IncorrectDocblockException('Misplaced variable'); + } + + $info->return_type = array_shift($line_parts); + $info->return_type_description = $line_parts ? implode(' ', $line_parts) : null; + + if ($line_number) { + $info->return_type_line_number = $line_number; + } + } else { + throw new DocblockParseException('Badly-formatted @return type'); + } + } + /** * @param string $comment * @param int $line_number diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 3d6bc5ee6..00ab0de1d 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1153,6 +1153,15 @@ class AnnotationTest extends TestCase function foo() : void {}', 'error_message' => 'InvalidDocblock', ], + 'returnTypeNewLineIsIgnored' => [ + ' 'MissingReturnType', + ], ]; } }