1
0
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:
Matthew Brown 2019-06-23 08:47:49 -04:00
parent 4735955829
commit 37ffd6dad6
7 changed files with 125 additions and 12 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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]);
}
}