From 1468a2827367df9380f7b0d035fbf3a99c4d0419 Mon Sep 17 00:00:00 2001 From: Brown Date: Tue, 18 Aug 2020 09:34:07 -0400 Subject: [PATCH] Fix #3999 - allow @psalm-type to reference imported type right above --- .../Internal/PhpVisitor/ReflectorVisitor.php | 44 ++++++++++-- tests/TypeAnnotationTest.php | 71 ++++++++++++++----- 2 files changed, 92 insertions(+), 23 deletions(-) diff --git a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index 14f909355..315bc0bbb 100644 --- a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -169,7 +169,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse public function enterNode(PhpParser\Node $node) { foreach ($node->getComments() as $comment) { - if ($comment instanceof PhpParser\Comment\Doc) { + if ($comment instanceof PhpParser\Comment\Doc && !$node instanceof PhpParser\Node\Stmt\ClassLike) { $self_fqcln = $node instanceof PhpParser\Node\Stmt\ClassLike && $node->name !== null ? ($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $node->name->name @@ -189,12 +189,6 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse } $this->type_aliases += $type_aliases; - - if ($type_aliases - && $node instanceof PhpParser\Node\Stmt\ClassLike - ) { - $this->classlike_type_aliases = $type_aliases; - } } catch (DocblockParseException $e) { $this->file_storage->docblock_issues[] = new InvalidDocblock( (string)$e->getMessage(), @@ -1477,6 +1471,42 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse } } + foreach ($node->getComments() as $comment) { + if (!$comment instanceof PhpParser\Comment\Doc) { + continue; + } + + try { + $type_aliases = CommentAnalyzer::getTypeAliasesFromComment( + $comment, + $this->aliases, + $this->type_aliases, + $fq_classlike_name + ); + + foreach ($type_aliases as $type_alias) { + // finds issues, if there are any + TypeParser::parseTokens($type_alias->replacement_tokens); + } + + $this->type_aliases += $type_aliases; + + if ($type_aliases) { + $this->classlike_type_aliases = $type_aliases; + } + } catch (DocblockParseException $e) { + $storage->docblock_issues[] = new InvalidDocblock( + (string)$e->getMessage(), + new CodeLocation($this->file_scanner, $node, null, true) + ); + } catch (TypeParseTreeException $e) { + $storage->docblock_issues[] = new InvalidDocblock( + (string)$e->getMessage(), + new CodeLocation($this->file_scanner, $node, null, true) + ); + } + } + foreach ($node->stmts as $node_stmt) { if ($node_stmt instanceof PhpParser\Node\Stmt\ClassConst) { $this->visitClassConstDeclaration($node_stmt, $storage, $fq_classlike_name); diff --git a/tests/TypeAnnotationTest.php b/tests/TypeAnnotationTest.php index 736e807c4..86a1d0410 100644 --- a/tests/TypeAnnotationTest.php +++ b/tests/TypeAnnotationTest.php @@ -14,6 +14,8 @@ class TypeAnnotationTest extends TestCase return [ 'typeAliasBeforeClass' => [ ' [ ' [ ' [ ' */ class Bar { @@ -155,8 +163,10 @@ class TypeAnnotationTest extends TestCase return $r; }', ], - 'classTypeAlias' => [ + 'classTypeAliasSimple' => [ ' [ ' [ ' [ ' [ ' */ @@ -309,6 +327,8 @@ class TypeAnnotationTest extends TestCase ], 'typeAliasInObjectLike' => [ ' [ ' [ ' "Matt"]; - } - } + namespace Barrr; - /** - * @psalm-import-type PhoneType from Phone - */ - class User {}', + class Phone { + function toArray(): array { + return ["name" => "Matt"]; + } + } + + /** + * @psalm-import-type PhoneType from Phone + */ + class User {}', 'error_message' => 'InvalidTypeImport', ], 'classTypeAliasFromInvalidClass' => [ ' 'UndefinedDocblockClass', ], 'malformedImportMissingFrom' => [ ' [ ' [ ' [ ' [ ' 'InvalidDocblock', + 'error_message' => 'UndefinedDocblockClass', ], 'mergeImportedTypes' => [ '