mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix #3999 - allow @psalm-type to reference imported type right above
This commit is contained in:
parent
134955a5f4
commit
1468a28273
@ -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);
|
||||
|
@ -14,6 +14,8 @@ class TypeAnnotationTest extends TestCase
|
||||
return [
|
||||
'typeAliasBeforeClass' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type CoolType = A|B|null
|
||||
*/
|
||||
@ -41,6 +43,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'typeAliasBeforeFunction' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type A_OR_B = A|B
|
||||
* @psalm-type CoolType = A_OR_B|null
|
||||
@ -68,6 +72,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'typeAliasInSeparateBlockBeforeFunction' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type CoolType = A|B|null
|
||||
*/
|
||||
@ -125,6 +131,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'typeAliasUsedTwice' => [
|
||||
'<?php
|
||||
namespace Baz;
|
||||
|
||||
/** @psalm-type TA = array<int, string> */
|
||||
|
||||
class Bar {
|
||||
@ -155,8 +163,10 @@ class TypeAnnotationTest extends TestCase
|
||||
return $r;
|
||||
}',
|
||||
],
|
||||
'classTypeAlias' => [
|
||||
'classTypeAliasSimple' => [
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/** @psalm-type PhoneType = array{phone: string} */
|
||||
class Phone {
|
||||
/** @psalm-return PhoneType */
|
||||
@ -191,6 +201,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'classTypeAliasImportWithAlias' => [
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/** @psalm-type PhoneType = array{phone: string} */
|
||||
class Phone {
|
||||
/** @psalm-return PhoneType */
|
||||
@ -211,6 +223,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'classTypeAliasDirectUsage' => [
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/** @psalm-type PhoneType = array{phone: string} */
|
||||
class Phone {
|
||||
/** @psalm-return PhoneType */
|
||||
@ -255,6 +269,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'importTypeForParam' => [
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/**
|
||||
* @psalm-type Type = self::NULL|self::BOOL|self::INT|self::STRING
|
||||
*/
|
||||
@ -300,6 +316,8 @@ class TypeAnnotationTest extends TestCase
|
||||
return [
|
||||
'invalidTypeAlias' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type CoolType = A|B>
|
||||
*/
|
||||
@ -309,6 +327,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'typeAliasInObjectLike' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type aType null|"a"|"b"|"c"|"d"
|
||||
*/
|
||||
@ -321,6 +341,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'classTypeAliasInvalidReturn' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/** @psalm-type PhoneType = array{phone: string} */
|
||||
class Phone {
|
||||
/** @psalm-return PhoneType */
|
||||
@ -356,28 +378,34 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'classTypeInvalidAliasImport' => [
|
||||
'<?php
|
||||
class Phone {
|
||||
function toArray(): array {
|
||||
return ["name" => "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' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-import-type PhoneType from Phone
|
||||
*/
|
||||
class User {}',
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-import-type PhoneType from Phone
|
||||
*/
|
||||
class User {}',
|
||||
'error_message' => 'UndefinedDocblockClass',
|
||||
],
|
||||
'malformedImportMissingFrom' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/** @psalm-import-type Thing */
|
||||
class C {}
|
||||
',
|
||||
@ -385,6 +413,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'malformedImportMissingSourceClass' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/** @psalm-import-type Thing from */
|
||||
class C {}
|
||||
',
|
||||
@ -392,6 +422,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'malformedImportMisspelledFrom' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/** @psalm-import-type Thing morf */
|
||||
class C {}
|
||||
',
|
||||
@ -399,6 +431,8 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'malformedImportMissingAlias' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/** @psalm-import-type Thing from Somewhere as */
|
||||
class C {}
|
||||
',
|
||||
@ -406,14 +440,19 @@ class TypeAnnotationTest extends TestCase
|
||||
],
|
||||
'noCrashWithPriorReference' => [
|
||||
'<?php
|
||||
namespace Barrr;
|
||||
|
||||
/**
|
||||
* @psalm-type _C=array{c:_CC}
|
||||
* @psalm-type _CC=float
|
||||
*/
|
||||
class A {
|
||||
|
||||
/**
|
||||
* @param _C $arr
|
||||
*/
|
||||
public function foo(array $arr) : void {}
|
||||
}',
|
||||
'error_message' => 'InvalidDocblock',
|
||||
'error_message' => 'UndefinedDocblockClass',
|
||||
],
|
||||
'mergeImportedTypes' => [
|
||||
'<?php
|
||||
|
Loading…
Reference in New Issue
Block a user