Skip to content

Commit

Permalink
Merge pull request #10665 from issidorov/parsing-magic-methods
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan committed Feb 6, 2024
2 parents 508da9a + afaaa3d commit 2fc2a37
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -322,14 +322,19 @@ public static function parse(

$has_return = false;

if (!preg_match('/^([a-z_A-Z][a-z_0-9A-Z]+) *\(/', $method_entry, $matches)) {
$doc_line_parts = CommentAnalyzer::splitDocLine($method_entry);
$doc_line_parts = CommentAnalyzer::splitDocLine($method_entry);

if ($doc_line_parts[0] === 'static' && !strpos($doc_line_parts[1], '(')) {
$is_static = true;
array_shift($doc_line_parts);
}
if (count($doc_line_parts) > 2
&& $doc_line_parts[0] === 'static'
&& !strpos($doc_line_parts[1], '(')
) {
$is_static = true;
array_shift($doc_line_parts);
$method_entry = implode(' ', $doc_line_parts);
$doc_line_parts = CommentAnalyzer::splitDocLine($method_entry);
}

if (!preg_match('/^([a-z_A-Z][a-z_0-9A-Z]+) *\(/', $method_entry, $matches)) {
if (count($doc_line_parts) > 1) {
$docblock_lines[] = '@return ' . array_shift($doc_line_parts);
$has_return = true;
Expand Down
184 changes: 184 additions & 0 deletions tests/ClassLikeDocblockParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Psalm\Aliases;
use Psalm\Internal\PhpVisitor\Reflector\ClassLikeDocblockParser;

use function array_values;

class ClassLikeDocblockParserTest extends TestCase
{
public function testDocblockDescription(): void
Expand Down Expand Up @@ -35,4 +37,186 @@ public function testPreferPsalmPrefixedAnnotationsOverPhpstanOnes(): void
$class_docblock = ClassLikeDocblockParser::parse($node, $php_parser_doc, new Aliases());
$this->assertSame([['T', 'of', 'string', true, 33]], $class_docblock->templates);
}

/**
* @return iterable<array-key, array{annotation: string, expected: array}>
*/
public function providerMethodAnnotation(): iterable
{
$data = [
'foo()' => [
'name' => 'foo',
'returnType' => '',
'is_static' => false,
'params' => [],
],
'foo($a)' => [
'name' => 'foo',
'returnType' => '',
'is_static' => false,
'params' => [
'a' => ['type' => ''],
],
],
'string foo()' => [
'name' => 'foo',
'returnType' => 'string',
'is_static' => false,
'params' => [],
],
'static string foo()' => [
'name' => 'foo',
'returnType' => 'string',
'is_static' => true,
'params' => [],
],
'string foo(string $a, int $b)' => [
'name' => 'foo',
'returnType' => 'string',
'is_static' => false,
'params' => [
'a' => ['type' => 'string'],
'b' => ['type' => 'int'],
],
],
'static string foo(string $a, int $b)' => [
'name' => 'foo',
'returnType' => 'string',
'is_static' => true,
'params' => [
'a' => ['type' => 'string'],
'b' => ['type' => 'int'],
],
],
'static foo()' => [
'name' => 'foo',
'returnType' => 'static',
'is_static' => false,
'params' => [],
],
'static static foo()' => [
'name' => 'foo',
'returnType' => 'static',
'is_static' => true,
'params' => [],
],
'static foo(string $z)' => [
'name' => 'foo',
'returnType' => 'static',
'is_static' => false,
'params' => [
'z' => ['type' => 'string'],
],
],
'static static foo(string $z)' => [
'name' => 'foo',
'returnType' => 'static',
'is_static' => true,
'params' => [
'z' => ['type' => 'string'],
],
],
'self foo()' => [
'name' => 'foo',
'returnType' => 'MyClass',
'is_static' => false,
'params' => [],
],
'static self foo()' => [
'name' => 'foo',
'returnType' => 'MyClass',
'is_static' => true,
'params' => [],
],
'self foo(string $z)' => [
'name' => 'foo',
'returnType' => 'MyClass',
'is_static' => false,
'params' => [
'z' => ['type' => 'string'],
],
],
'static self foo(string $z)' => [
'name' => 'foo',
'returnType' => 'MyClass',
'is_static' => true,
'params' => [
'z' => ['type' => 'string'],
],
],
'(string|int)[] getArray()' => [
'name' => 'getArray',
'returnType' => 'array<array-key, int|string>',
'is_static' => false,
'params' => [],
],
'static (string|int)[] getArray()' => [
'name' => 'getArray',
'returnType' => 'array<array-key, int|string>',
'is_static' => true,
'params' => [],
],
'(callable() : string) getCallable()' => [
'name' => 'getCallable',
'returnType' => 'callable():string',
'is_static' => false,
'params' => [],
],
'static (callable() : string) getCallable()' => [
'name' => 'getCallable',
'returnType' => 'callable():string',
'is_static' => true,
'params' => [],
],
];

$res = [];
foreach ($data as $key => $item) {
$res[$key] = [
'annotation' => $key,
'expected' => $item,
];
}

return $res;
}

/**
* @dataProvider providerMethodAnnotation
*/
public function testMethodAnnotation(string $annotation, array $expected): void
{
$full_content = <<<EOF
<?php
/**
* @method $annotation
*/
class MyClass {}
EOF;

$this->addFile('somefile.php', $full_content);

$codebase = $this->project_analyzer->getCodebase();
$codebase->scanFiles();

$class_storage = $codebase->classlike_storage_provider->get('MyClass');
$methods = $expected['is_static']
? $class_storage->pseudo_static_methods
: $class_storage->pseudo_methods;
$method = array_values($methods)[0];

$actual = [
'name' => $method->cased_name,
'returnType' => (string) $method->return_type,
'is_static' => $method->is_static,
'params' => [],
];
foreach ($method->params as $param) {
$actual['params'][$param->name] = [
'type' => (string) $param->type,
];
}

$this->assertEquals($expected, $actual);
}
}

0 comments on commit 2fc2a37

Please sign in to comment.