mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Fix #1832 - add references to docblock types
This commit is contained in:
parent
4735955829
commit
37ffd6dad6
@ -5,6 +5,15 @@ use PhpParser;
|
||||
|
||||
class DocblockTypeLocation extends \Psalm\CodeLocation
|
||||
{
|
||||
/** @var int */
|
||||
public $raw_file_start;
|
||||
|
||||
/** @var int */
|
||||
public $raw_file_end;
|
||||
|
||||
/** @var int */
|
||||
public $raw_line_number;
|
||||
|
||||
public function __construct(
|
||||
\Psalm\FileSource $file_source,
|
||||
int $file_start,
|
||||
@ -14,6 +23,11 @@ class DocblockTypeLocation extends \Psalm\CodeLocation
|
||||
$this->file_start = $file_start;
|
||||
// matches how CodeLocation works
|
||||
$this->file_end = $file_end - 1;
|
||||
|
||||
$this->raw_file_start = $file_start;
|
||||
$this->raw_file_end = $file_end;
|
||||
$this->raw_line_number = $line_number;
|
||||
|
||||
$this->file_path = $file_source->getFilePath();
|
||||
$this->file_name = $file_source->getFileName();
|
||||
$this->single_line = false;
|
||||
|
@ -1028,6 +1028,20 @@ class Analyzer
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addDocblockType(
|
||||
string $file_path,
|
||||
\Psalm\CodeLocation\DocblockTypeLocation $code_location,
|
||||
string $docblock_type
|
||||
) {
|
||||
$this->type_map[$file_path][$code_location->raw_file_start] = [
|
||||
$code_location->raw_file_end,
|
||||
$docblock_type
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $aliases
|
||||
* @return void
|
||||
|
@ -502,6 +502,7 @@ class ParseTree
|
||||
$new_leaf = new ParseTree\Value(
|
||||
$type_token[0] . '::' . $nexter_token[0],
|
||||
$type_token[1],
|
||||
$type_token[1] + 2 + strlen($nexter_token[0]),
|
||||
$new_parent
|
||||
);
|
||||
|
||||
@ -517,6 +518,7 @@ class ParseTree
|
||||
$new_leaf = new ParseTree\Value(
|
||||
$type_token[0],
|
||||
$type_token[1],
|
||||
$type_token[1] + strlen($type_token[0]),
|
||||
$new_parent
|
||||
);
|
||||
break;
|
||||
|
@ -14,15 +14,25 @@ class Value extends \Psalm\Internal\Type\ParseTree
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $offset;
|
||||
public $offset_start;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $offset_end;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param \Psalm\Internal\Type\ParseTree|null $parent
|
||||
*/
|
||||
public function __construct(string $value, int $offset, \Psalm\Internal\Type\ParseTree $parent = null)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
public function __construct(
|
||||
string $value,
|
||||
int $offset_start,
|
||||
int $offset_end,
|
||||
\Psalm\Internal\Type\ParseTree $parent = null
|
||||
) {
|
||||
$this->offset_start = $offset_start;
|
||||
$this->offset_end = $offset_end;
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
@ -128,7 +128,8 @@ abstract class Type
|
||||
$only_token[0] = self::fixScalarTerms($only_token[0], $php_version);
|
||||
|
||||
$atomic = Atomic::create($only_token[0], $php_version, $template_type_map);
|
||||
$atomic->offset = 0;
|
||||
$atomic->offset_start = 0;
|
||||
$atomic->offset_end = strlen($only_token[0]);
|
||||
|
||||
return new Union([$atomic]);
|
||||
}
|
||||
@ -656,7 +657,8 @@ abstract class Type
|
||||
|
||||
$atomic_type = Atomic::create($atomic_type_string, $php_version, $template_type_map);
|
||||
|
||||
$atomic_type->offset = $parse_tree->offset;
|
||||
$atomic_type->offset_start = $parse_tree->offset_start;
|
||||
$atomic_type->offset_end = $parse_tree->offset_end;
|
||||
|
||||
return $atomic_type;
|
||||
}
|
||||
|
@ -70,7 +70,12 @@ abstract class Atomic
|
||||
/**
|
||||
* @var ?int
|
||||
*/
|
||||
public $offset;
|
||||
public $offset_start;
|
||||
|
||||
/**
|
||||
* @var ?int
|
||||
*/
|
||||
public $offset_end;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
@ -343,6 +348,25 @@ abstract class Atomic
|
||||
}
|
||||
|
||||
if ($this instanceof TNamedObject) {
|
||||
$codebase = $source->getCodebase();
|
||||
|
||||
if ($code_location instanceof CodeLocation\DocblockTypeLocation
|
||||
&& $codebase->store_node_types
|
||||
&& $this->offset_start !== null
|
||||
&& $this->offset_end !== null
|
||||
) {
|
||||
$codebase->analyzer->addDocblockType(
|
||||
$source->getFilePath(),
|
||||
new CodeLocation\DocblockTypeLocation(
|
||||
$source,
|
||||
$code_location->raw_file_start + $this->offset_start,
|
||||
$code_location->raw_file_start + $this->offset_end,
|
||||
$code_location->raw_line_number
|
||||
),
|
||||
$this->value
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($phantom_classes[strtolower($this->value)]) &&
|
||||
ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
|
||||
$source,
|
||||
@ -360,10 +384,29 @@ abstract class Atomic
|
||||
|
||||
if ($this->extra_types) {
|
||||
foreach ($this->extra_types as $extra_type) {
|
||||
if ($extra_type instanceof TTemplateParam) {
|
||||
if ($extra_type instanceof TTemplateParam
|
||||
|| $extra_type instanceof Type\Atomic\TObjectWithProperties
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($code_location instanceof CodeLocation\DocblockTypeLocation
|
||||
&& $codebase->store_node_types
|
||||
&& $extra_type->offset_start !== null
|
||||
&& $extra_type->offset_end !== null
|
||||
) {
|
||||
$codebase->analyzer->addDocblockType(
|
||||
$source->getFilePath(),
|
||||
new CodeLocation\DocblockTypeLocation(
|
||||
$source,
|
||||
$code_location->raw_file_start + $extra_type->offset_start,
|
||||
$code_location->raw_file_start + $extra_type->offset_end,
|
||||
$code_location->raw_line_number
|
||||
),
|
||||
$extra_type->value
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($phantom_classes[strtolower($extra_type->value)]) &&
|
||||
ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
|
||||
$source,
|
||||
|
@ -244,9 +244,6 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
||||
namespace B;
|
||||
|
||||
class A {
|
||||
/** @var int|null */
|
||||
protected $a;
|
||||
|
||||
public function foo(int $i) : string {
|
||||
return "hello";
|
||||
}
|
||||
@ -261,10 +258,41 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
||||
$codebase->scanFiles();
|
||||
$this->analyzeFile('somefile.php', new Context());
|
||||
|
||||
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(12, 33));
|
||||
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(9, 33));
|
||||
|
||||
$this->assertNotNull($symbol_at_position);
|
||||
|
||||
$this->assertSame('B\A::foo()', $symbol_at_position[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testGetTypeInDocblock()
|
||||
{
|
||||
$codebase = $this->project_analyzer->getCodebase();
|
||||
$config = $codebase->config;
|
||||
$config->throw_exception = false;
|
||||
|
||||
$this->addFile(
|
||||
'somefile.php',
|
||||
'<?php
|
||||
namespace B;
|
||||
|
||||
class A {
|
||||
/** @var \Exception|null */
|
||||
public $prop;
|
||||
}'
|
||||
);
|
||||
|
||||
$codebase->file_provider->openFile('somefile.php');
|
||||
$codebase->scanFiles();
|
||||
$this->analyzeFile('somefile.php', new Context());
|
||||
|
||||
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(4, 35));
|
||||
|
||||
$this->assertNotNull($symbol_at_position);
|
||||
|
||||
$this->assertSame('type: Exception', $symbol_at_position[0]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user