mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Add end line/column data for LSP support
This commit is contained in:
parent
04a1583783
commit
a99e89495b
@ -14,6 +14,9 @@ class CodeLocation
|
||||
/** @var int */
|
||||
private $line_number;
|
||||
|
||||
/** @var int */
|
||||
private $end_line_number = -1;
|
||||
|
||||
/** @var int */
|
||||
private $file_start;
|
||||
|
||||
@ -36,7 +39,10 @@ class CodeLocation
|
||||
private $selection_end = -1;
|
||||
|
||||
/** @var int */
|
||||
private $column = -1;
|
||||
private $column_from = -1;
|
||||
|
||||
/** @var int */
|
||||
private $column_to = -1;
|
||||
|
||||
/** @var string */
|
||||
private $snippet = '';
|
||||
@ -252,11 +258,17 @@ class CodeLocation
|
||||
}
|
||||
|
||||
// reset preview start to beginning of line
|
||||
$this->column = $this->selection_start -
|
||||
$this->column_from = $this->selection_start -
|
||||
(int)strrpos($file_contents, "\n", $this->selection_start - strlen($file_contents));
|
||||
|
||||
// reset preview start to beginning of line
|
||||
$this->column_to = $this->selection_end -
|
||||
(int)strrpos($file_contents, "\n", $this->selection_end - strlen($file_contents));
|
||||
|
||||
$this->snippet = substr($file_contents, $this->preview_start, $this->preview_end - $this->preview_start);
|
||||
$this->text = substr($file_contents, $this->selection_start, $this->selection_end - $this->selection_start);
|
||||
|
||||
$this->end_line_number = $this->line_number + substr_count($this->snippet, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,6 +279,16 @@ class CodeLocation
|
||||
return $this->docblock_line_number ?: $this->line_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEndLineNumber()
|
||||
{
|
||||
$this->calculateRealLocation();
|
||||
|
||||
return $this->end_line_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@ -294,7 +316,17 @@ class CodeLocation
|
||||
{
|
||||
$this->calculateRealLocation();
|
||||
|
||||
return $this->column;
|
||||
return $this->column_from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEndColumn()
|
||||
{
|
||||
$this->calculateRealLocation();
|
||||
|
||||
return $this->column_to;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,10 +170,10 @@ class Analyzer
|
||||
|
||||
// Wait for all tasks to complete and collect the results.
|
||||
/**
|
||||
* @var array<array{issues: array<int, array{severity: string, line_number: string, type: string,
|
||||
* @var array<array{issues: array<int, array{severity: string, line_from: int, line_to: int, type: string,
|
||||
* message: string, file_name: string, file_path: string, snippet: string, from: int, to: int,
|
||||
* snippet_from: int, snippet_to: int, column: int}>, file_references: array<string, array<string,bool>>,
|
||||
* mixed_counts: array<string, array{0: int, 1: int}>}>
|
||||
* snippet_from: int, snippet_to: int, column_from: int, column_to: int}>, file_references: array<string,
|
||||
* array<string,bool>>, mixed_counts: array<string, array{0: int, 1: int}>}>
|
||||
*/
|
||||
$forked_pool_data = $pool->wait();
|
||||
|
||||
|
@ -82,9 +82,9 @@ abstract class CodeIssue
|
||||
/**
|
||||
* @param string $severity
|
||||
*
|
||||
* @return array{severity: string, line_number: int, type: string, message: string, file_name: string,
|
||||
* @return array{severity: string, line_from: int, line_to: int, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, selected_text: string, from: int, to: int, snippet_from: int,
|
||||
* snippet_to: int, column: int}
|
||||
* snippet_to: int, column_from: int, column_to: int}
|
||||
*/
|
||||
public function toArray($severity = Config::REPORT_ERROR)
|
||||
{
|
||||
@ -97,7 +97,8 @@ abstract class CodeIssue
|
||||
|
||||
return [
|
||||
'severity' => $severity,
|
||||
'line_number' => $location->getLineNumber(),
|
||||
'line_from' => $location->getLineNumber(),
|
||||
'line_to' => $location->getEndLineNumber(),
|
||||
'type' => $issue_type,
|
||||
'message' => $this->getMessage(),
|
||||
'file_name' => $location->file_name,
|
||||
@ -108,7 +109,8 @@ abstract class CodeIssue
|
||||
'to' => $selection_bounds[1],
|
||||
'snippet_from' => $snippet_bounds[0],
|
||||
'snippet_to' => $snippet_bounds[1],
|
||||
'column' => $location->getColumn(),
|
||||
'column_from' => $location->getColumn(),
|
||||
'column_to' => $location->getEndColumn(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ use Psalm\Issue\CodeIssue;
|
||||
class IssueBuffer
|
||||
{
|
||||
/**
|
||||
* @var array<int, array{severity: string, line_number: string, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int, column: int}>
|
||||
* @var array<int, array{severity: string, line_from: int, line_to: int, type: string, message: string,
|
||||
* file_name: string, file_path: string, snippet: string, from: int, to: int,
|
||||
* snippet_from: int, snippet_to: int, column_from: int, column_to: int}>
|
||||
*/
|
||||
protected static $issues_data = [];
|
||||
|
||||
@ -112,22 +113,22 @@ class IssueBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{severity: string, line_number: string, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int,
|
||||
* column: int} $issue_data
|
||||
* @param array{severity: string, line_from: int, line_to: int, type: string, message: string,
|
||||
* file_name: string, file_path: string, snippet: string, from: int, to: int,
|
||||
* snippet_from: int, snippet_to: int, column_from: int, column_to: int} $issue_data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getEmacsOutput(array $issue_data)
|
||||
{
|
||||
return $issue_data['file_path'] . ':' . $issue_data['line_number'] . ':' . $issue_data['column'] . ':' .
|
||||
return $issue_data['file_path'] . ':' . $issue_data['line_from'] . ':' . $issue_data['column_from'] . ':' .
|
||||
($issue_data['severity'] === Config::REPORT_ERROR ? 'error' : 'warning') . ' - ' . $issue_data['message'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{severity: string, line_number: string, type: string, message: string, file_name: string,
|
||||
* @param array{severity: string, line_from: int, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int,
|
||||
* column: int} $issue_data
|
||||
* column_from: int, column_to: int} $issue_data
|
||||
* @param bool $use_color
|
||||
*
|
||||
* @return string
|
||||
@ -145,7 +146,7 @@ class IssueBuffer
|
||||
}
|
||||
|
||||
$issue_string .= ': ' . $issue_data['type'] . ' - ' . $issue_data['file_name'] . ':' .
|
||||
$issue_data['line_number'] . ':' . $issue_data['column'] . ' - ' . $issue_data['message'] . PHP_EOL;
|
||||
$issue_data['line_from'] . ':' . $issue_data['column_from'] . ' - ' . $issue_data['message'] . PHP_EOL;
|
||||
|
||||
$snippet = $issue_data['snippet'];
|
||||
|
||||
@ -164,8 +165,9 @@ class IssueBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{severity: string, line_number: string, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int, column: int}>
|
||||
* @return array<int, array{severity: string, line_from: int, type: string, message: string, file_name: string,
|
||||
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int, column_from: int,
|
||||
* column_to: int}>
|
||||
*/
|
||||
public static function getIssuesData()
|
||||
{
|
||||
@ -173,9 +175,9 @@ class IssueBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{severity: string, line_number: string, type: string, message: string,
|
||||
* @param array<int, array{severity: string, line_from: int, type: string, message: string,
|
||||
* file_name: string, file_path: string, snippet: string, from: int, to: int, snippet_from: int,
|
||||
* snippet_to: int, column: int}> $issues_data
|
||||
* snippet_to: int, column_from: int, column_to: int}> $issues_data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -209,15 +211,15 @@ class IssueBuffer
|
||||
/** @return int */
|
||||
function (array $d1, array $d2) {
|
||||
if ($d1['file_path'] === $d2['file_path']) {
|
||||
if ($d1['line_number'] === $d2['line_number']) {
|
||||
if ($d1['column'] === $d2['column']) {
|
||||
if ($d1['line_from'] === $d2['line_from']) {
|
||||
if ($d1['column_from'] === $d2['column_from']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $d1['column'] > $d2['column'] ? 1 : -1;
|
||||
return $d1['column_from'] > $d2['column_from'] ? 1 : -1;
|
||||
}
|
||||
|
||||
return $d1['line_number'] > $d2['line_number'] ? 1 : -1;
|
||||
return $d1['line_from'] > $d2['line_from'] ? 1 : -1;
|
||||
}
|
||||
|
||||
return $d1['file_path'] > $d2['file_path'] ? 1 : -1;
|
||||
|
@ -54,7 +54,7 @@ class JsonOutputTest extends TestCase
|
||||
$this->assertSame('somefile.php', $issue_data['file_path']);
|
||||
$this->assertSame('error', $issue_data['severity']);
|
||||
$this->assertSame($message, $issue_data['message']);
|
||||
$this->assertSame($line_number, $issue_data['line_number']);
|
||||
$this->assertSame($line_number, $issue_data['line_from']);
|
||||
$this->assertSame(
|
||||
$error,
|
||||
substr($code, $issue_data['from'], $issue_data['to'] - $issue_data['from'])
|
||||
@ -96,7 +96,8 @@ echo $a;';
|
||||
[
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 7,
|
||||
'line_from' => 7,
|
||||
'line_to' => 7,
|
||||
'type' => 'UndefinedConstant',
|
||||
'message' => 'Const CHANGE_ME is not defined',
|
||||
'file_name' => 'somefile.php',
|
||||
@ -107,11 +108,13 @@ echo $a;';
|
||||
'to' => 134,
|
||||
'snippet_from' => 120,
|
||||
'snippet_to' => 135,
|
||||
'column' => 6,
|
||||
'column_from' => 6,
|
||||
'column_to' => 15
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 15,
|
||||
'line_from' => 15,
|
||||
'line_to' => 15,
|
||||
'type' => 'PossiblyUndefinedGlobalVariable',
|
||||
'message' => 'Possibly undefined global variable $a, first seen on line 10',
|
||||
'file_name' => 'somefile.php',
|
||||
@ -122,11 +125,13 @@ echo $a;';
|
||||
'to' => 203,
|
||||
'snippet_from' => 196,
|
||||
'snippet_to' => 203,
|
||||
'column' => 6,
|
||||
'column_from' => 6,
|
||||
'column_to' => 8
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 3,
|
||||
'line_from' => 3,
|
||||
'line_to' => 3,
|
||||
'type' => 'UndefinedVariable',
|
||||
'message' => 'Cannot find referenced variable $as_you',
|
||||
'file_name' => 'somefile.php',
|
||||
@ -137,11 +142,13 @@ echo $a;';
|
||||
'to' => 73,
|
||||
'snippet_from' => 57,
|
||||
'snippet_to' => 83,
|
||||
'column' => 10,
|
||||
'column_from' => 10,
|
||||
'column_to' => 17
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 2,
|
||||
'line_from' => 2,
|
||||
'line_to' => 2,
|
||||
'type' => 'UnusedParam',
|
||||
'message' => 'Param $your_code is never referenced in this method',
|
||||
'file_name' => 'somefile.php',
|
||||
@ -152,11 +159,13 @@ echo $a;';
|
||||
'to' => 44,
|
||||
'snippet_from' => 6,
|
||||
'snippet_to' => 56,
|
||||
'column' => 29,
|
||||
'column_from' => 29,
|
||||
'column_to' => 39
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 2,
|
||||
'line_from' => 2,
|
||||
'line_to' => 4,
|
||||
'type' => 'MixedInferredReturnType',
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'file_name' => 'somefile.php',
|
||||
@ -169,7 +178,8 @@ echo $a;';
|
||||
'to' => 54,
|
||||
'snippet_from' => 6,
|
||||
'snippet_to' => 85,
|
||||
'column' => 42,
|
||||
'column_from' => 42,
|
||||
'column_to' => 49
|
||||
],
|
||||
],
|
||||
$issue_data
|
||||
|
@ -114,68 +114,77 @@ echo $a;';
|
||||
|
||||
$issue_data = [
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 7,
|
||||
'type' => 'UndefinedConstant',
|
||||
'message' => 'Const CHANGE_ME is not defined',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'echo CHANGE_ME;',
|
||||
'selected_text' => 'CHANGE_ME',
|
||||
'from' => 125,
|
||||
'to' => 134,
|
||||
'snippet_from' => 120,
|
||||
'snippet_to' => 135,
|
||||
'column' => 6,
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 15,
|
||||
'type' => 'PossiblyUndefinedGlobalVariable',
|
||||
'message' => 'Possibly undefined global variable $a, first seen on line 10',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'echo $a',
|
||||
'selected_text' => '$a',
|
||||
'from' => 201,
|
||||
'to' => 203,
|
||||
'snippet_from' => 196,
|
||||
'snippet_to' => 203,
|
||||
'column' => 6,
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 3,
|
||||
'type' => 'UndefinedVariable',
|
||||
'message' => 'Cannot find referenced variable $as_you',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => ' return $as_you . "type";',
|
||||
'selected_text' => '$as_you',
|
||||
'from' => 66,
|
||||
'to' => 73,
|
||||
'snippet_from' => 57,
|
||||
'snippet_to' => 83,
|
||||
'column' => 10,
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_number' => 2,
|
||||
'type' => 'MixedInferredReturnType',
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'function psalmCanVerify(int $your_code): ?string {
|
||||
'severity' => 'error',
|
||||
'line_from' => 7,
|
||||
'line_to' => 7,
|
||||
'type' => 'UndefinedConstant',
|
||||
'message' => 'Const CHANGE_ME is not defined',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'echo CHANGE_ME;',
|
||||
'selected_text' => 'CHANGE_ME',
|
||||
'from' => 125,
|
||||
'to' => 134,
|
||||
'snippet_from' => 120,
|
||||
'snippet_to' => 135,
|
||||
'column_from' => 6,
|
||||
'column_to' => 15
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_from' => 15,
|
||||
'line_to' => 15,
|
||||
'type' => 'PossiblyUndefinedGlobalVariable',
|
||||
'message' => 'Possibly undefined global variable $a, first seen on line 10',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'echo $a',
|
||||
'selected_text' => '$a',
|
||||
'from' => 201,
|
||||
'to' => 203,
|
||||
'snippet_from' => 196,
|
||||
'snippet_to' => 203,
|
||||
'column_from' => 6,
|
||||
'column_to' => 8
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_from' => 3,
|
||||
'line_to' => 3,
|
||||
'type' => 'UndefinedVariable',
|
||||
'message' => 'Cannot find referenced variable $as_you',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => ' return $as_you . "type";',
|
||||
'selected_text' => '$as_you',
|
||||
'from' => 66,
|
||||
'to' => 73,
|
||||
'snippet_from' => 57,
|
||||
'snippet_to' => 83,
|
||||
'column_from' => 10,
|
||||
'column_to' => 17
|
||||
],
|
||||
[
|
||||
'severity' => 'error',
|
||||
'line_from' => 2,
|
||||
'line_to' => 4,
|
||||
'type' => 'MixedInferredReturnType',
|
||||
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
|
||||
'file_name' => 'somefile.php',
|
||||
'file_path' => 'somefile.php',
|
||||
'snippet' => 'function psalmCanVerify(int $your_code): ?string {
|
||||
return $as_you . "type";
|
||||
}',
|
||||
'selected_text' => '?string',
|
||||
'from' => 47,
|
||||
'to' => 54,
|
||||
'snippet_from' => 6,
|
||||
'snippet_to' => 85,
|
||||
'column' => 42,
|
||||
],
|
||||
'selected_text' => '?string',
|
||||
'from' => 47,
|
||||
'to' => 54,
|
||||
'snippet_from' => 6,
|
||||
'snippet_to' => 85,
|
||||
'column_from' => 42,
|
||||
'column_to' => 49
|
||||
],
|
||||
];
|
||||
|
||||
$emacs = 'somefile.php:7:6:error - Const CHANGE_ME is not defined
|
||||
somefile.php:15:6:error - Possibly undefined global variable $a, first seen on line 10
|
||||
somefile.php:3:10:error - Cannot find referenced variable $as_you
|
||||
|
Loading…
Reference in New Issue
Block a user