Skip to content

Commit

Permalink
Fix #3999 - allow @psalm-type to reference imported type right above
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Aug 18, 2020
1 parent 134955a commit 1468a28
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 22 deletions.
44 changes: 37 additions & 7 deletions src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php
Expand Up @@ -169,7 +169,7 @@ public function __construct(
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
Expand All @@ -189,12 +189,6 @@ public function enterNode(PhpParser\Node $node)
}

$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(),
Expand Down Expand Up @@ -1477,6 +1471,42 @@ function (array $l, array $r) : int {
}
}

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);
Expand Down
69 changes: 54 additions & 15 deletions tests/TypeAnnotationTest.php
Expand Up @@ -14,6 +14,8 @@ public function providerValidCodeParse()
return [
'typeAliasBeforeClass' => [
'<?php
namespace Barrr;
/**
* @psalm-type CoolType = A|B|null
*/
Expand Down Expand Up @@ -41,6 +43,8 @@ function bar ($a) : void { }
],
'typeAliasBeforeFunction' => [
'<?php
namespace Barrr;
/**
* @psalm-type A_OR_B = A|B
* @psalm-type CoolType = A_OR_B|null
Expand Down Expand Up @@ -68,6 +72,8 @@ function bar ($a) : void { }
],
'typeAliasInSeparateBlockBeforeFunction' => [
'<?php
namespace Barrr;
/**
* @psalm-type CoolType = A|B|null
*/
Expand Down Expand Up @@ -125,6 +131,8 @@ function bar ($a) : void { }
],
'typeAliasUsedTwice' => [
'<?php
namespace Baz;
/** @psalm-type TA = array<int, string> */
class Bar {
Expand Down Expand Up @@ -155,8 +163,10 @@ function f($p) {
return $r;
}',
],
'classTypeAlias' => [
'classTypeAliasSimple' => [
'<?php
namespace Bar;
/** @psalm-type PhoneType = array{phone: string} */
class Phone {
/** @psalm-return PhoneType */
Expand Down Expand Up @@ -191,6 +201,8 @@ function toArray(): array {
],
'classTypeAliasImportWithAlias' => [
'<?php
namespace Bar;
/** @psalm-type PhoneType = array{phone: string} */
class Phone {
/** @psalm-return PhoneType */
Expand All @@ -211,6 +223,8 @@ function toArray(): array {
],
'classTypeAliasDirectUsage' => [
'<?php
namespace Bar;
/** @psalm-type PhoneType = array{phone: string} */
class Phone {
/** @psalm-return PhoneType */
Expand Down Expand Up @@ -255,6 +269,8 @@ function toArray(): array {
],
'importTypeForParam' => [
'<?php
namespace Bar;
/**
* @psalm-type Type = self::NULL|self::BOOL|self::INT|self::STRING
*/
Expand Down Expand Up @@ -300,6 +316,8 @@ public function providerInvalidCodeParse()
return [
'invalidTypeAlias' => [
'<?php
namespace Barrr;
/**
* @psalm-type CoolType = A|B>
*/
Expand All @@ -309,6 +327,8 @@ class A {}',
],
'typeAliasInObjectLike' => [
'<?php
namespace Barrr;
/**
* @psalm-type aType null|"a"|"b"|"c"|"d"
*/
Expand All @@ -321,6 +341,8 @@ function f(): array {
],
'classTypeAliasInvalidReturn' => [
'<?php
namespace Barrr;
/** @psalm-type PhoneType = array{phone: string} */
class Phone {
/** @psalm-return PhoneType */
Expand Down Expand Up @@ -356,64 +378,81 @@ function toArray(): array {
],
'classTypeInvalidAliasImport' => [
'<?php
class Phone {
function toArray(): array {
return ["name" => "Matt"];
namespace Barrr;
class Phone {
function toArray(): array {
return ["name" => "Matt"];
}
}
}
/**
* @psalm-import-type PhoneType from Phone
*/
class User {}',
/**
* @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 {}
',
'error_message' => 'InvalidTypeImport',
],
'malformedImportMissingSourceClass' => [
'<?php
namespace Barrr;
/** @psalm-import-type Thing from */
class C {}
',
'error_message' => 'InvalidTypeImport',
],
'malformedImportMisspelledFrom' => [
'<?php
namespace Barrr;
/** @psalm-import-type Thing morf */
class C {}
',
'error_message' => 'InvalidTypeImport',
],
'malformedImportMissingAlias' => [
'<?php
namespace Barrr;
/** @psalm-import-type Thing from Somewhere as */
class C {}
',
'error_message' => 'InvalidTypeImport',
],
'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
Expand Down

0 comments on commit 1468a28

Please sign in to comment.