1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

add psalm and phpcs checking and cleanup errors from that

This commit is contained in:
Andrew Nagy 2022-02-02 23:37:20 +00:00
parent 9957f67353
commit 9f6843bbb8
13 changed files with 133 additions and 89 deletions

2
.gitignore vendored
View File

@ -10,5 +10,5 @@
/vendor-bin/*/composer.lock
/vendor-bin/*/vendor/
/tests/fixtures/symlinktest/*
.vscode
.idea/

View File

@ -7,7 +7,6 @@ use InvalidArgumentException;
use LanguageServerProtocol\Command;
use LanguageServerProtocol\CompletionItem;
use LanguageServerProtocol\CompletionItemKind;
use LanguageServerProtocol\InsertTextFormat;
use LanguageServerProtocol\ParameterInformation;
use LanguageServerProtocol\Position;
use LanguageServerProtocol\Range;
@ -413,7 +412,7 @@ class Codebase
{
$this->loadAnalyzer();
if($force) {
if ($force) {
FileReferenceProvider::clearCache();
}
@ -1521,8 +1520,11 @@ class Codebase
/**
* @return list<CompletionItem>
*/
public function getCompletionItemsForClassishThing(string $type_string, string $gap): array
{
public function getCompletionItemsForClassishThing(
string $type_string,
string $gap,
bool $snippets_supported = false
): array {
$completion_items = [];
$type = Type::parseString($type_string);
@ -1543,7 +1545,7 @@ class Codebase
$method_storage->description,
(string)$method_storage->visibility,
$method_storage->cased_name,
$method_storage->cased_name . (count($method_storage->params) !== 0 ? '($0)' : '()'),
$method_storage->cased_name,
null,
null,
new Command('Trigger parameter hints', 'editor.action.triggerParameterHints'),
@ -1551,7 +1553,10 @@ class Codebase
2
);
$completion_item->insertTextFormat = InsertTextFormat::SNIPPET;
if ($snippets_supported && count($method_storage->params) > 0) {
$completion_item->insertText .= '($0)';
$completion_item->insertTextFormat = InsertTextFormat::SNIPPET;
}
$completion_items[] = $completion_item;
}
@ -1868,7 +1873,7 @@ class Codebase
public function addTemporaryFileChanges(string $file_path, string $new_content, ?int $version = null): void
{
$this->file_provider->addTemporaryFileChanges($file_path, $new_content, $version );
$this->file_provider->addTemporaryFileChanges($file_path, $new_content, $version);
}
public function removeTemporaryFileChanges(string $file_path): void

View File

@ -281,7 +281,7 @@ HELP;
if ($cache_directory !== null) {
Config::removeCacheDirectory($cache_directory);
}
if(!isset($options['clear-cache-on-boot'])) {
if (!isset($options['clear-cache-on-boot'])) {
echo 'Cache directory deleted' . PHP_EOL;
exit;
}

View File

@ -4,17 +4,11 @@ declare(strict_types=1);
namespace Psalm\Internal\LanguageServer\Client;
use Amp\Promise;
use Generator;
use JsonMapper;
use LanguageServerProtocol\Diagnostic;
use LanguageServerProtocol\TextDocumentIdentifier;
use LanguageServerProtocol\TextDocumentItem;
use Psalm\Internal\LanguageServer\ClientHandler;
use Psalm\Internal\LanguageServer\LanguageServer;
use function Amp\call;
/**
* Provides method handlers for all textDocument/* methods
*/
@ -47,7 +41,7 @@ class TextDocument
*
* @param Diagnostic[] $diagnostics
*/
public function publishDiagnostics(string $uri, array $diagnostics, ?int $version=null): void
public function publishDiagnostics(string $uri, array $diagnostics, ?int $version = null): void
{
$this->server->logDebug("textDocument/publishDiagnostics");

View File

@ -61,4 +61,4 @@ class Workspace
]
]);
}
}
}

View File

@ -67,4 +67,4 @@ class ClientConfiguration
$this->hideWarnings = $hideWarnings;
$this->provideCompletion = $provideCompletion;
}
}
}

View File

@ -13,7 +13,6 @@ use Amp\Promise;
use Generator;
use function Amp\call;
use function error_log;
/**
* @internal

View File

@ -10,6 +10,8 @@ use LanguageServerProtocol\LogTrace;
use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument;
use Psalm\Internal\LanguageServer\Client\Workspace as ClientWorkspace;
use function is_null;
/**
* @internal
*/
@ -95,16 +97,16 @@ class LanguageClient
* The amount and content of these notifications depends on the current trace configuration.
*
* @param LogTrace $logTrace
* @return void
*/
public function logTrace(LogTrace $logTrace): void {
public function logTrace(LogTrace $logTrace): void
{
//If trace is 'off', the server should not send any logTrace notification.
if(is_null($this->server->trace) || $this->server->trace === 'off') {
if (is_null($this->server->trace) || $this->server->trace === 'off') {
return;
}
//If trace is 'messages', the server should not add the 'verbose' field in the LogTraceParams.
if($this->server->trace === 'messages') {
if ($this->server->trace === 'messages') {
$logTrace->verbose = null;
}
@ -139,19 +141,20 @@ class LanguageClient
* server issuing the event.
*
* @param LogMessage $logMessage
* @return void
*/
public function event(LogMessage $logMessage) {
public function event(LogMessage $logMessage): void
{
$this->handler->notify(
'telemetry/event',
$logMessage
);
}
private function configurationRefreshed() {
private function configurationRefreshed(): void
{
//do things when the config is refreshed
if(!is_null($this->clientConfiguration->provideCompletion)) {
if (!is_null($this->clientConfiguration->provideCompletion)) {
//$this->server->project_analyzer->provide_completion = $this->clientConfiguration->provideCompletion;
}
}

View File

@ -33,7 +33,6 @@ use LanguageServerProtocol\ServerCapabilities;
use LanguageServerProtocol\SignatureHelpOptions;
use LanguageServerProtocol\TextDocumentSyncKind;
use LanguageServerProtocol\TextDocumentSyncOptions;
use LanguageServerProtocol\WorkspaceFolder;
use Psalm\Config;
use Psalm\Internal\Analyzer\IssueData;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
@ -45,12 +44,15 @@ use Throwable;
use function Amp\asyncCoroutine;
use function Amp\call;
use function array_combine;
use function array_filter;
use function array_keys;
use function array_map;
use function array_reduce;
use function array_shift;
use function array_unshift;
use function explode;
use function implode;
use function json_encode;
use function max;
use function parse_url;
use function rawurlencode;
@ -61,6 +63,8 @@ use function substr;
use function trim;
use function urldecode;
use const JSON_PRETTY_PRINT;
/**
* @internal
*/
@ -153,7 +157,7 @@ class LanguageServer extends Dispatcher
* @var Promise|null
*/
$dispatched = $this->dispatch($msg->body);
if($dispatched !== null) {
if ($dispatched !== null) {
/** @psalm-suppress MixedAssignment */
$result = yield $dispatched;
} else {
@ -244,7 +248,7 @@ class LanguageServer extends Dispatcher
$this->trace = $trace;
return call(
/** @return Generator<int, true, mixed, InitializeResult> */
function () use($capabilities) {
function () use ($capabilities) {
$this->logInfo("Initializing...");
$this->clientStatus('initializing');
@ -315,8 +319,7 @@ class LanguageServer extends Dispatcher
// Support "Completion"
// Support "Code Actions" if we support data
if(
$this->clientCapabilities &&
if ($this->clientCapabilities &&
$this->clientCapabilities->textDocument &&
$this->clientCapabilities->textDocument->publishDiagnostics &&
$this->clientCapabilities->textDocument->publishDiagnostics->dataSupport
@ -355,17 +358,19 @@ class LanguageServer extends Dispatcher
{
try {
$this->client->refreshConfiguration();
} catch(Throwable $e) {
$this->server->logError((string) $e);
} catch (Throwable $e) {
$this->logError((string) $e);
}
$this->clientStatus('running');
}
public function queueChangeFileAnalysis(string $file_path, string $uri, ?int $version=null) {
public function queueChangeFileAnalysis(string $file_path, string $uri, ?int $version = null): void
{
$this->doVersionedAnalysis([$file_path => $uri], $version);
}
public function queueOpenFileAnalysis(string $file_path, string $uri, ?int $version=null) {
public function queueOpenFileAnalysis(string $file_path, string $uri, ?int $version = null): void
{
$this->doVersionedAnalysis([$file_path => $uri], $version);
}
@ -373,9 +378,31 @@ class LanguageServer extends Dispatcher
* Queue Saved File Analysis
* @param string $file_path
* @param string $uri
* @return void
*/
public function queueSaveFileAnalysis(string $file_path, string $uri) {
public function queueSaveFileAnalysis(string $file_path, string $uri): void
{
//Always reanalzye open files because of things changing elsewhere
$this->doVersionedAnalysis(
$this->queueFileAnalysisWithOpenedFiles([$file_path => $this->pathToUri($file_path)])
);
}
public function queueFileAnalysisWithOpenedFiles(array $files = []): void
{
$opened = array_reduce(
$this->project_analyzer->getCodebase()->file_provider->getOpenFilesPath(),
function (array $opened, string $file_path) {
$opened[$file_path] = $this->pathToUri($file_path);
return $opened;
},
$files
);
$this->doVersionedAnalysis($opened);
}
public function queueClosedFileAnalysis(string $file_path, string $uri): void
{
//Always reanalzye open files because of things changing elsewhere
$opened = array_reduce(
$this->project_analyzer->getCodebase()->file_provider->getOpenFilesPath(),
@ -383,14 +410,16 @@ class LanguageServer extends Dispatcher
$opened[$file_path] = $this->pathToUri($file_path);
return $opened;
},
[$file_path => $this->pathToUri($file_path)]);
[$file_path => $this->pathToUri($file_path)]
);
$this->doVersionedAnalysis($opened);
}
public function doVersionedAnalysis($all_files_to_analyze, ?int $version=null):void {
public function doVersionedAnalysis($all_files_to_analyze, ?int $version = null): void
{
try {
if(empty($all_files_to_analyze)) {
if (empty($all_files_to_analyze)) {
$this->logWarning("No versioned analysis to do.");
return;
}
@ -410,14 +439,15 @@ class LanguageServer extends Dispatcher
);
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$this->emitVersionedIssues($files,$version);
} catch(Throwable $e) {
$this->emitVersionedIssues($files, $version);
} catch (Throwable $e) {
$this->server->logError((string) $e);
}
}
public function emitVersionedIssues(array $files, ?int $version = null): void {
$this->logDebug("Perform Analysis",[
public function emitVersionedIssues(array $files, ?int $version = null): void
{
$this->logDebug("Perform Analysis", [
'files' => array_keys($files),
'version' => $version
]);
@ -541,8 +571,8 @@ class LanguageServer extends Dispatcher
*/
public function log(int $type, string $message, array $context = []): void
{
if(!empty($context)) {
$message .= "\n" . \json_encode($context, JSON_PRETTY_PRINT);
if (!empty($context)) {
$message .= "\n" . json_encode($context, JSON_PRETTY_PRINT);
}
try {
$this->client->logMessage(
@ -557,19 +587,23 @@ class LanguageServer extends Dispatcher
}
}
public function logError(string $message, array $context = []) {
public function logError(string $message, array $context = []): void
{
$this->log(MessageType::ERROR, $message, $context);
}
public function logWarning(string $message, array $context = []) {
public function logWarning(string $message, array $context = []): void
{
$this->log(MessageType::WARNING, $message, $context);
}
public function logInfo(string $message, array $context = []) {
public function logInfo(string $message, array $context = []): void
{
$this->log(MessageType::INFO, $message, $context);
}
public function logDebug(string $message, array $context = []) {
public function logDebug(string $message, array $context = []): void
{
$this->log(MessageType::LOG, $message, $context);
}

View File

@ -1,9 +1,7 @@
<?php
namespace Psalm\Internal\LanguageServer;
use LanguageServerProtocol\MessageType;
use Psalm\Progress\Progress as Base;
/**
@ -17,7 +15,8 @@ class Progress extends Base
*/
private $server;
public function __construct(LanguageServer $server) {
public function __construct(LanguageServer $server)
{
$this->server = $server;
}
@ -30,4 +29,4 @@ class Progress extends Base
{
$this->server->logInfo($message);
}
}
}

View File

@ -6,11 +6,9 @@ namespace Psalm\Internal\LanguageServer\Server;
use Amp\Promise;
use Amp\Success;
use InvalidArgumentException;
use LanguageServerProtocol\CodeAction;
use LanguageServerProtocol\CodeActionContext;
use LanguageServerProtocol\CodeActionKind;
use LanguageServerProtocol\Command;
use LanguageServerProtocol\CompletionList;
use LanguageServerProtocol\Hover;
use LanguageServerProtocol\Location;
@ -31,10 +29,8 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\LanguageServer\LanguageServer;
use UnexpectedValueException;
use function array_combine;
use function array_values;
use function count;
use function error_log;
use function preg_match;
use function substr_count;
@ -137,7 +133,7 @@ class TextDocument
return;
}
if (count($contentChanges) === 1 && $contentChanges[0]->range === null) {
if (count($contentChanges) === 1 && isset($contentChanges[0]) && $contentChanges[0]->range === null) {
$new_content = $contentChanges[0]->text;
} else {
throw new UnexpectedValueException('Not expecting partial diff');
@ -264,7 +260,7 @@ class TextDocument
try {
$symbol_information = $this->codebase->getSymbolInformation($file_path, $reference);
} catch(UnexpectedValueException $e) {
} catch (UnexpectedValueException $e) {
$this->server->logError((string) $e);
return new Success(null);
}
@ -297,7 +293,7 @@ class TextDocument
*
* @param TextDocumentIdentifier $textDocument The text document
* @param Position $position The position
* @psalm-return Promise<array<empty, empty>>|Promise<CompletionList>
* @psalm-return Promise<array<empty, empty>>|Promise<CompletionList>|Promise<null>
*/
public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise
{
@ -335,7 +331,12 @@ class TextDocument
[$recent_type, $gap, $offset] = $completion_data;
if ($gap === '->' || $gap === '::') {
$completion_items = $this->codebase->getCompletionItemsForClassishThing($recent_type, $gap);
$snippetSupport = ($this->server->clientCapabilities->textDocument &&
$this->server->clientCapabilities->textDocument->completion &&
$this->server->clientCapabilities->textDocument->completion->completionItem &&
$this->server->clientCapabilities->textDocument->completion->completionItem->snippetSupport)
? true : false;
$completion_items = $this->codebase->getCompletionItemsForClassishThing($recent_type, $gap, $snippetSupport);
} elseif ($gap === '[') {
$completion_items = $this->codebase->getCompletionItemsForArrayKeys($recent_type);
} else {
@ -382,7 +383,7 @@ class TextDocument
try {
$signature_information = $this->codebase->getSignatureInformation($argument_location[0], $file_path);
} catch(UnexpectedValueException $e) {
} catch (UnexpectedValueException $e) {
$this->server->logError((string) $e);
return new Success(null);
}
@ -420,23 +421,27 @@ class TextDocument
}
$fixers = [];
foreach($context->diagnostics as $diagnostic) {
if($diagnostic->source !== 'psalm') {
foreach ($context->diagnostics as $diagnostic) {
if ($diagnostic->source !== 'psalm') {
continue;
}
/** @var array{type: string, snippet: string, line_from: int, line_to: int} */
$data = (array)$diagnostic->data;
$snippetRange = new Range(
new Position($diagnostic->data->line_from-1),
new Position($diagnostic->data->line_to)
new Position($data['line_from']-1),
new Position($data['line_to'])
);
$indentation = '';
if (preg_match('/^(\s*)/', $diagnostic->data->snippet, $matches)) {
if (preg_match('/^(\s*)/', $data['snippet'], $matches)) {
$indentation = $matches[1] ?? '';
}
//Suppress Ability
$fixers["suppress.{$diagnostic->data->type}"] = new CodeAction(
"Suppress {$diagnostic->data->type} for this line",
$fixers["suppress.{$data['type']}"] = new CodeAction(
"Suppress {$data['type']} for this line",
CodeActionKind::QUICK_FIX,
null,
null,
@ -446,9 +451,9 @@ class TextDocument
new TextEdit(
$snippetRange,
"{$indentation}/**\n".
"{$indentation} * @psalm-suppress {$diagnostic->data->type}\n".
"{$indentation} * @psalm-suppress {$data['type']}\n".
"{$indentation} */\n".
"{$diagnostic->data->snippet}\n"
"{$data['snippet']}\n"
)
]
])

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Psalm\Internal\LanguageServer\Server;
use Amp\Promise;
use Amp\Success;
use LanguageServerProtocol\FileChangeType;
use LanguageServerProtocol\FileEvent;
@ -13,6 +14,10 @@ use Psalm\Internal\Composer;
use Psalm\Internal\LanguageServer\LanguageServer;
use Psalm\Internal\Provider\FileReferenceProvider;
use function array_map;
use function in_array;
use function realpath;
/**
* Provides method handlers for all workspace/* methods
*/
@ -60,24 +65,22 @@ class Workspace
'workspace/didChangeWatchedFiles'
);
$realFiles = array_map(function(FileEvent $change) {
$realFiles = array_map(function (FileEvent $change) {
return LanguageServer::uriToPath($change->uri);
}, $changes);
$composerLockFile = realpath(Composer::getLockFilePath($this->codebase->config->base_dir));
if(in_array($composerLockFile, $realFiles)) {
if (in_array($composerLockFile, $realFiles)) {
$this->server->logInfo('Composer.lock file changed. Reloading codebase');
FileReferenceProvider::clearCache();
foreach($this->codebase->file_provider->getOpenFiles() as $file) {
$this->server->queueFileAnalysis($file, $this->server->pathToUri($file));
}
$this->server->queueFileAnalysisWithOpenedFiles();
return;
}
foreach ($changes as $change) {
$file_path = LanguageServer::uriToPath($change->uri);
if($composerLockFile === $file_path) {
if ($composerLockFile === $file_path) {
continue;
}
@ -96,7 +99,7 @@ class Workspace
//If the file is currently open then dont analize it because its tracked in didChange
if (!$this->codebase->file_provider->isOpen($file_path)) {
$this->server->queueFileAnalysis($file_path, $change->uri);
$this->server->queueClosedFileAnalysis($file_path, $change->uri);
}
}
}
@ -123,7 +126,8 @@ class Workspace
* @param mixed $arguments
* @psalm-suppress PossiblyUnusedMethod
*/
public function executeCommand($command, $arguments) {
public function executeCommand($command, $arguments): Promise
{
$this->server->logDebug(
'workspace/executeCommand',
[
@ -132,9 +136,11 @@ class Workspace
]
);
switch($command) {
switch ($command) {
case 'psalm.analyze.uri':
$file = LanguageServer::uriToPath($arguments->uri);
/** @var array{uri: string} */
$arguments = (array) $arguments;
$file = LanguageServer::uriToPath($arguments['uri']);
$codebase = $this->project_analyzer->getCodebase();
$codebase->reloadFiles(
$this->project_analyzer,
@ -147,8 +153,8 @@ class Workspace
);
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$this->server->emitVersionedIssues([$file => $arguments->uri]);
break;
$this->server->emitVersionedIssues([$file => $arguments['uri']]);
break;
}
return new Success(null);

View File

@ -72,11 +72,11 @@ class FileProvider
file_put_contents($file_path, $file_contents);
}
public function setOpenContents(string $file_path, string $file_contents): void
public function setOpenContents(string $file_path, ?string $file_contents = null): void
{
$file_path_lc = strtolower($file_path);
if (isset($this->open_files[$file_path_lc])) {
$this->open_files[$file_path_lc] = $file_contents;
$this->open_files[$file_path_lc] = $file_contents ?? $this->getContents($file_path, true);
}
}
@ -91,8 +91,7 @@ class FileProvider
public function addTemporaryFileChanges(string $file_path, string $new_content, ?int $version = null): void
{
if(
isset($this->temp_files[strtolower($file_path)]) &&
if (isset($this->temp_files[strtolower($file_path)]) &&
$version !== null &&
$this->temp_files[strtolower($file_path)]['version'] !== null &&
$version < $this->temp_files[strtolower($file_path)]['version']