1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Add end line/column data for LSP support

This commit is contained in:
Matthew Brown 2018-02-19 18:16:09 -05:00
parent 04a1583783
commit a99e89495b
6 changed files with 152 additions and 97 deletions

View File

@ -14,6 +14,9 @@ class CodeLocation
/** @var int */ /** @var int */
private $line_number; private $line_number;
/** @var int */
private $end_line_number = -1;
/** @var int */ /** @var int */
private $file_start; private $file_start;
@ -36,7 +39,10 @@ class CodeLocation
private $selection_end = -1; private $selection_end = -1;
/** @var int */ /** @var int */
private $column = -1; private $column_from = -1;
/** @var int */
private $column_to = -1;
/** @var string */ /** @var string */
private $snippet = ''; private $snippet = '';
@ -252,11 +258,17 @@ class CodeLocation
} }
// reset preview start to beginning of line // 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)); (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->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->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 $this->docblock_line_number ?: $this->line_number;
} }
/**
* @return int
*/
public function getEndLineNumber()
{
$this->calculateRealLocation();
return $this->end_line_number;
}
/** /**
* @return string * @return string
*/ */
@ -294,7 +316,17 @@ class CodeLocation
{ {
$this->calculateRealLocation(); $this->calculateRealLocation();
return $this->column; return $this->column_from;
}
/**
* @return int
*/
public function getEndColumn()
{
$this->calculateRealLocation();
return $this->column_to;
} }
/** /**

View File

@ -170,10 +170,10 @@ class Analyzer
// Wait for all tasks to complete and collect the results. // 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, * 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>>, * snippet_from: int, snippet_to: int, column_from: int, column_to: int}>, file_references: array<string,
* mixed_counts: array<string, array{0: int, 1: int}>}> * array<string,bool>>, mixed_counts: array<string, array{0: int, 1: int}>}>
*/ */
$forked_pool_data = $pool->wait(); $forked_pool_data = $pool->wait();

View File

@ -82,9 +82,9 @@ abstract class CodeIssue
/** /**
* @param string $severity * @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, * 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) public function toArray($severity = Config::REPORT_ERROR)
{ {
@ -97,7 +97,8 @@ abstract class CodeIssue
return [ return [
'severity' => $severity, 'severity' => $severity,
'line_number' => $location->getLineNumber(), 'line_from' => $location->getLineNumber(),
'line_to' => $location->getEndLineNumber(),
'type' => $issue_type, 'type' => $issue_type,
'message' => $this->getMessage(), 'message' => $this->getMessage(),
'file_name' => $location->file_name, 'file_name' => $location->file_name,
@ -108,7 +109,8 @@ abstract class CodeIssue
'to' => $selection_bounds[1], 'to' => $selection_bounds[1],
'snippet_from' => $snippet_bounds[0], 'snippet_from' => $snippet_bounds[0],
'snippet_to' => $snippet_bounds[1], 'snippet_to' => $snippet_bounds[1],
'column' => $location->getColumn(), 'column_from' => $location->getColumn(),
'column_to' => $location->getEndColumn(),
]; ];
} }
} }

View File

@ -8,8 +8,9 @@ use Psalm\Issue\CodeIssue;
class IssueBuffer class IssueBuffer
{ {
/** /**
* @var array<int, array{severity: string, line_number: string, type: string, message: string, file_name: string, * @var array<int, array{severity: string, line_from: int, line_to: int, type: string, message: string,
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int, column: int}> * 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 = []; protected static $issues_data = [];
@ -112,22 +113,22 @@ class IssueBuffer
} }
/** /**
* @param array{severity: string, line_number: string, type: string, message: string, file_name: string, * @param array{severity: string, line_from: int, line_to: int, type: string, message: string,
* file_path: string, snippet: string, from: int, to: int, snippet_from: int, snippet_to: int, * file_name: string, file_path: string, snippet: string, from: int, to: int,
* column: int} $issue_data * snippet_from: int, snippet_to: int, column_from: int, column_to: int} $issue_data
* *
* @return string * @return string
*/ */
protected static function getEmacsOutput(array $issue_data) 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']; ($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, * 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 * @param bool $use_color
* *
* @return string * @return string
@ -145,7 +146,7 @@ class IssueBuffer
} }
$issue_string .= ': ' . $issue_data['type'] . ' - ' . $issue_data['file_name'] . ':' . $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']; $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, * @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: int}> * 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() 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, * 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 * @return void
*/ */
@ -209,15 +211,15 @@ class IssueBuffer
/** @return int */ /** @return int */
function (array $d1, array $d2) { function (array $d1, array $d2) {
if ($d1['file_path'] === $d2['file_path']) { if ($d1['file_path'] === $d2['file_path']) {
if ($d1['line_number'] === $d2['line_number']) { if ($d1['line_from'] === $d2['line_from']) {
if ($d1['column'] === $d2['column']) { if ($d1['column_from'] === $d2['column_from']) {
return 0; 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; return $d1['file_path'] > $d2['file_path'] ? 1 : -1;

View File

@ -54,7 +54,7 @@ class JsonOutputTest extends TestCase
$this->assertSame('somefile.php', $issue_data['file_path']); $this->assertSame('somefile.php', $issue_data['file_path']);
$this->assertSame('error', $issue_data['severity']); $this->assertSame('error', $issue_data['severity']);
$this->assertSame($message, $issue_data['message']); $this->assertSame($message, $issue_data['message']);
$this->assertSame($line_number, $issue_data['line_number']); $this->assertSame($line_number, $issue_data['line_from']);
$this->assertSame( $this->assertSame(
$error, $error,
substr($code, $issue_data['from'], $issue_data['to'] - $issue_data['from']) substr($code, $issue_data['from'], $issue_data['to'] - $issue_data['from'])
@ -96,7 +96,8 @@ echo $a;';
[ [
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 7, 'line_from' => 7,
'line_to' => 7,
'type' => 'UndefinedConstant', 'type' => 'UndefinedConstant',
'message' => 'Const CHANGE_ME is not defined', 'message' => 'Const CHANGE_ME is not defined',
'file_name' => 'somefile.php', 'file_name' => 'somefile.php',
@ -107,11 +108,13 @@ echo $a;';
'to' => 134, 'to' => 134,
'snippet_from' => 120, 'snippet_from' => 120,
'snippet_to' => 135, 'snippet_to' => 135,
'column' => 6, 'column_from' => 6,
'column_to' => 15
], ],
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 15, 'line_from' => 15,
'line_to' => 15,
'type' => 'PossiblyUndefinedGlobalVariable', 'type' => 'PossiblyUndefinedGlobalVariable',
'message' => 'Possibly undefined global variable $a, first seen on line 10', 'message' => 'Possibly undefined global variable $a, first seen on line 10',
'file_name' => 'somefile.php', 'file_name' => 'somefile.php',
@ -122,11 +125,13 @@ echo $a;';
'to' => 203, 'to' => 203,
'snippet_from' => 196, 'snippet_from' => 196,
'snippet_to' => 203, 'snippet_to' => 203,
'column' => 6, 'column_from' => 6,
'column_to' => 8
], ],
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 3, 'line_from' => 3,
'line_to' => 3,
'type' => 'UndefinedVariable', 'type' => 'UndefinedVariable',
'message' => 'Cannot find referenced variable $as_you', 'message' => 'Cannot find referenced variable $as_you',
'file_name' => 'somefile.php', 'file_name' => 'somefile.php',
@ -137,11 +142,13 @@ echo $a;';
'to' => 73, 'to' => 73,
'snippet_from' => 57, 'snippet_from' => 57,
'snippet_to' => 83, 'snippet_to' => 83,
'column' => 10, 'column_from' => 10,
'column_to' => 17
], ],
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 2, 'line_from' => 2,
'line_to' => 2,
'type' => 'UnusedParam', 'type' => 'UnusedParam',
'message' => 'Param $your_code is never referenced in this method', 'message' => 'Param $your_code is never referenced in this method',
'file_name' => 'somefile.php', 'file_name' => 'somefile.php',
@ -152,11 +159,13 @@ echo $a;';
'to' => 44, 'to' => 44,
'snippet_from' => 6, 'snippet_from' => 6,
'snippet_to' => 56, 'snippet_to' => 56,
'column' => 29, 'column_from' => 29,
'column_to' => 39
], ],
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 2, 'line_from' => 2,
'line_to' => 4,
'type' => 'MixedInferredReturnType', 'type' => 'MixedInferredReturnType',
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify', 'message' => 'Could not verify return type \'string|null\' for psalmCanVerify',
'file_name' => 'somefile.php', 'file_name' => 'somefile.php',
@ -169,7 +178,8 @@ echo $a;';
'to' => 54, 'to' => 54,
'snippet_from' => 6, 'snippet_from' => 6,
'snippet_to' => 85, 'snippet_to' => 85,
'column' => 42, 'column_from' => 42,
'column_to' => 49
], ],
], ],
$issue_data $issue_data

View File

@ -114,68 +114,77 @@ echo $a;';
$issue_data = [ $issue_data = [
[ [
'severity' => 'error', 'severity' => 'error',
'line_number' => 7, 'line_from' => 7,
'type' => 'UndefinedConstant', 'line_to' => 7,
'message' => 'Const CHANGE_ME is not defined', 'type' => 'UndefinedConstant',
'file_name' => 'somefile.php', 'message' => 'Const CHANGE_ME is not defined',
'file_path' => 'somefile.php', 'file_name' => 'somefile.php',
'snippet' => 'echo CHANGE_ME;', 'file_path' => 'somefile.php',
'selected_text' => 'CHANGE_ME', 'snippet' => 'echo CHANGE_ME;',
'from' => 125, 'selected_text' => 'CHANGE_ME',
'to' => 134, 'from' => 125,
'snippet_from' => 120, 'to' => 134,
'snippet_to' => 135, 'snippet_from' => 120,
'column' => 6, 'snippet_to' => 135,
], 'column_from' => 6,
[ 'column_to' => 15
'severity' => 'error', ],
'line_number' => 15, [
'type' => 'PossiblyUndefinedGlobalVariable', 'severity' => 'error',
'message' => 'Possibly undefined global variable $a, first seen on line 10', 'line_from' => 15,
'file_name' => 'somefile.php', 'line_to' => 15,
'file_path' => 'somefile.php', 'type' => 'PossiblyUndefinedGlobalVariable',
'snippet' => 'echo $a', 'message' => 'Possibly undefined global variable $a, first seen on line 10',
'selected_text' => '$a', 'file_name' => 'somefile.php',
'from' => 201, 'file_path' => 'somefile.php',
'to' => 203, 'snippet' => 'echo $a',
'snippet_from' => 196, 'selected_text' => '$a',
'snippet_to' => 203, 'from' => 201,
'column' => 6, 'to' => 203,
], 'snippet_from' => 196,
[ 'snippet_to' => 203,
'severity' => 'error', 'column_from' => 6,
'line_number' => 3, 'column_to' => 8
'type' => 'UndefinedVariable', ],
'message' => 'Cannot find referenced variable $as_you', [
'file_name' => 'somefile.php', 'severity' => 'error',
'file_path' => 'somefile.php', 'line_from' => 3,
'snippet' => ' return $as_you . "type";', 'line_to' => 3,
'selected_text' => '$as_you', 'type' => 'UndefinedVariable',
'from' => 66, 'message' => 'Cannot find referenced variable $as_you',
'to' => 73, 'file_name' => 'somefile.php',
'snippet_from' => 57, 'file_path' => 'somefile.php',
'snippet_to' => 83, 'snippet' => ' return $as_you . "type";',
'column' => 10, 'selected_text' => '$as_you',
], 'from' => 66,
[ 'to' => 73,
'severity' => 'error', 'snippet_from' => 57,
'line_number' => 2, 'snippet_to' => 83,
'type' => 'MixedInferredReturnType', 'column_from' => 10,
'message' => 'Could not verify return type \'string|null\' for psalmCanVerify', 'column_to' => 17
'file_name' => 'somefile.php', ],
'file_path' => 'somefile.php', [
'snippet' => 'function psalmCanVerify(int $your_code): ?string { '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"; return $as_you . "type";
}', }',
'selected_text' => '?string', 'selected_text' => '?string',
'from' => 47, 'from' => 47,
'to' => 54, 'to' => 54,
'snippet_from' => 6, 'snippet_from' => 6,
'snippet_to' => 85, 'snippet_to' => 85,
'column' => 42, 'column_from' => 42,
], 'column_to' => 49
],
]; ];
$emacs = 'somefile.php:7:6:error - Const CHANGE_ME is not defined $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:15:6:error - Possibly undefined global variable $a, first seen on line 10
somefile.php:3:10:error - Cannot find referenced variable $as_you somefile.php:3:10:error - Cannot find referenced variable $as_you