From 8859e58464bd5ed9212abea558d59e162be86a40 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 19 Jul 2023 10:51:22 +0200 Subject: [PATCH 01/31] Switch to amp v3 --- composer.json | 6 +- .../LanguageServer/Client/Workspace.php | 5 +- .../Internal/LanguageServer/ClientHandler.php | 27 +- .../LanguageServer/LanguageServer.php | 419 ++++++++---------- .../LanguageServer/ProtocolStreamReader.php | 19 +- .../LanguageServer/ProtocolStreamWriter.php | 11 +- .../LanguageServer/ProtocolWriter.php | 8 +- .../LanguageServer/Server/TextDocument.php | 101 ++--- .../LanguageServer/Server/Workspace.php | 6 +- tests/LanguageServer/DiagnosticTest.php | 9 +- tests/LanguageServer/MockProtocolStream.php | 15 +- 11 files changed, 274 insertions(+), 352 deletions(-) diff --git a/composer.json b/composer.json index 7f4a9f9c8a9..499e888759b 100644 --- a/composer.json +++ b/composer.json @@ -24,8 +24,8 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "composer-runtime-api": "^2", - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", + "amphp/amp": "^3", + "amphp/byte-stream": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", @@ -44,7 +44,7 @@ }, "require-dev": { "ext-curl": "*", - "amphp/phpunit-util": "^2.0", + "amphp/phpunit-util": "^3", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", "mockery/mockery": "^1.5", diff --git a/src/Psalm/Internal/LanguageServer/Client/Workspace.php b/src/Psalm/Internal/LanguageServer/Client/Workspace.php index f9d9cf39e90..cbf526ea12a 100644 --- a/src/Psalm/Internal/LanguageServer/Client/Workspace.php +++ b/src/Psalm/Internal/LanguageServer/Client/Workspace.php @@ -4,7 +4,6 @@ namespace Psalm\Internal\LanguageServer\Client; -use Amp\Promise; use JsonMapper; use Psalm\Internal\LanguageServer\ClientHandler; use Psalm\Internal\LanguageServer\LanguageServer; @@ -42,11 +41,11 @@ public function __construct(ClientHandler $handler, JsonMapper $mapper, Language * @param string $section The configuration section asked for. * @param string|null $scopeUri The scope to get the configuration section for. */ - public function requestConfiguration(string $section, ?string $scopeUri = null): Promise + public function requestConfiguration(string $section, ?string $scopeUri = null): object { $this->server->logDebug("workspace/configuration"); - /** @var Promise */ + /** @var object */ return $this->handler->request('workspace/configuration', [ 'items' => [ [ diff --git a/src/Psalm/Internal/LanguageServer/ClientHandler.php b/src/Psalm/Internal/LanguageServer/ClientHandler.php index 06e48e3d143..d935bef2358 100644 --- a/src/Psalm/Internal/LanguageServer/ClientHandler.php +++ b/src/Psalm/Internal/LanguageServer/ClientHandler.php @@ -8,11 +8,7 @@ use AdvancedJsonRpc\Request; use AdvancedJsonRpc\Response; use AdvancedJsonRpc\SuccessResponse; -use Amp\Deferred; -use Amp\Promise; -use Generator; - -use function Amp\call; +use Amp\DeferredFuture; /** * @internal @@ -37,24 +33,19 @@ public function __construct(ProtocolReader $protocolReader, ProtocolWriter $prot * * @param string $method The method to call * @param array|object $params The method parameters - * @return Promise Resolved with the result of the request or rejected with an error + * @return mixed Resolved with the result of the request or rejected with an error */ - public function request(string $method, $params): Promise + public function request(string $method, $params) { $id = $this->idGenerator->generate(); - return call( - /** - * @return Generator> - */ - function () use ($id, $method, $params): Generator { - yield $this->protocolWriter->write( + $this->protocolWriter->write( new Message( new Request($id, $method, (object) $params), ), ); - $deferred = new Deferred(); + $deferred = new DeferredFuture(); $listener = function (Message $msg) use ($id, $deferred, &$listener): void { @@ -69,17 +60,15 @@ function (Message $msg) use ($id, $deferred, &$listener): void { // Received a response $this->protocolReader->removeListener('message', $listener); if (SuccessResponse::isSuccessResponse($msg->body)) { - $deferred->resolve($msg->body->result); + $deferred->complete($msg->body->result); } else { - $deferred->fail($msg->body->error); + $deferred->error($msg->body->error); } } }; $this->protocolReader->on('message', $listener); - return $deferred->promise(); - }, - ); + return $deferred->getFuture()->await(); } /** diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 54009b9cc5a..87ff272a701 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -11,10 +11,6 @@ use AdvancedJsonRpc\Request; use AdvancedJsonRpc\Response; use AdvancedJsonRpc\SuccessResponse; -use Amp\Loop; -use Amp\Promise; -use Amp\Success; -use Generator; use InvalidArgumentException; use JsonMapper; use LanguageServerProtocol\ClientCapabilities; @@ -56,10 +52,9 @@ use Psalm\Internal\Provider\ProjectCacheProvider; use Psalm\Internal\Provider\Providers; use Psalm\IssueBuffer; +use Revolt\EventLoop; use Throwable; -use function Amp\asyncCoroutine; -use function Amp\call; use function array_combine; use function array_filter; use function array_keys; @@ -170,64 +165,52 @@ function (): void { ); $this->protocolReader->on( 'message', - asyncCoroutine( - /** - * @return Generator - */ - function (Message $msg): Generator { - if (!$msg->body) { - return; - } + function (Message $msg): void { + if (!$msg->body) { + return; + } - // Ignore responses, this is the handler for requests and notifications - if (Response::isResponse($msg->body)) { - return; - } + // Ignore responses, this is the handler for requests and notifications + if (Response::isResponse($msg->body)) { + return; + } - $result = null; - $error = null; - try { - // Invoke the method handler to get a result - /** - * @var Promise|null - */ - $dispatched = $this->dispatch($msg->body); - if ($dispatched !== null) { - $result = yield $dispatched; - } else { - $result = null; - } - } catch (Error $e) { - // If a ResponseError is thrown, send it back in the Response - $error = $e; - } catch (Throwable $e) { - // If an unexpected error occurred, send back an INTERNAL_ERROR error response - $error = new Error( - (string) $e, - ErrorCode::INTERNAL_ERROR, - null, - $e, - ); - } + $result = null; + $error = null; + try { + // Invoke the method handler to get a result + $result = $this->dispatch($msg->body); + } catch (Error $e) { + // If a ResponseError is thrown, send it back in the Response + $error = $e; + } catch (Throwable $e) { + // If an unexpected error occurred, send back an INTERNAL_ERROR error response + $error = new Error( + (string) $e, + ErrorCode::INTERNAL_ERROR, + null, + $e, + ); + } + if ($error !== null) { + $this->logError($error->message); + } + // Only send a Response for a Request + // Notifications do not send Responses + /** + * @psalm-suppress UndefinedPropertyFetch + * @psalm-suppress MixedArgument + */ + if (Request::isRequest($msg->body)) { if ($error !== null) { - $this->logError($error->message); - } - // Only send a Response for a Request - // Notifications do not send Responses - /** - * @psalm-suppress UndefinedPropertyFetch - * @psalm-suppress MixedArgument - */ - if (Request::isRequest($msg->body)) { - if ($error !== null) { - $responseBody = new ErrorResponse($msg->body->id, $error); - } else { - $responseBody = new SuccessResponse($msg->body->id, $result); - } - yield $this->protocolWriter->write(new Message($responseBody)); + $responseBody = new ErrorResponse($msg->body->id, $error); + } else { + $responseBody = new SuccessResponse($msg->body->id, $result); } - }, - ), + $this->protocolWriter->write(new Message($responseBody)); + } + }, + ), ); $this->protocolReader->on( @@ -323,7 +306,7 @@ public static function run( $clientConfiguration, $progress, ); - Loop::run(); + EventLoop::run(); } elseif ($clientConfiguration->TCPServerMode && $clientConfiguration->TCPServerAddress) { // Run a TCP Server $tcpServer = stream_socket_server('tcp://' . $clientConfiguration->TCPServerAddress, $errno, $errstr); @@ -346,7 +329,7 @@ public static function run( $clientConfiguration, $progress, ); - Loop::run(); + EventLoop::run(); } } else { // Use STDIO @@ -359,7 +342,7 @@ public static function run( $clientConfiguration, $progress, ); - Loop::run(); + EventLoop::run(); } } @@ -377,7 +360,6 @@ public static function run( * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. * @param mixed $initializationOptions * @param string|null $trace The initial trace setting. If omitted trace is disabled ('off'). - * @psalm-return Promise * @psalm-suppress PossiblyUnusedParam */ public function initialize( @@ -390,184 +372,174 @@ public function initialize( $initializationOptions = null, ?string $trace = null //?array $workspaceFolders = null //error in json-dispatcher - ): Promise { + ): InitializeResult { $this->clientInfo = $clientInfo; $this->clientCapabilities = $capabilities; $this->trace = $trace; - return call( - /** @return Generator */ - function () { - $this->logInfo("Initializing..."); - $this->clientStatus('initializing'); - - // Eventually, this might block on something. Leave it as a generator. - /** @psalm-suppress TypeDoesNotContainType */ - if (false) { - yield true; - } - - $this->project_analyzer->serverMode($this); - $this->logInfo("Initializing: Getting code base..."); - $this->clientStatus('initializing', 'getting code base'); + $this->logInfo("Initializing..."); + $this->clientStatus('initializing'); - $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); - $this->clientStatus('initializing', 'scanning files'); - $this->codebase->scanFiles($this->project_analyzer->threads); + $this->project_analyzer->serverMode($this); - $this->logInfo("Initializing: Registering stub files..."); - $this->clientStatus('initializing', 'registering stub files'); - $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); + $this->logInfo("Initializing: Getting code base..."); + $this->clientStatus('initializing', 'getting code base'); - if ($this->textDocument === null) { - $this->textDocument = new ServerTextDocument( - $this, - $this->codebase, - $this->project_analyzer, - ); - } + $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); + $this->clientStatus('initializing', 'scanning files'); + $this->codebase->scanFiles($this->project_analyzer->threads); - if ($this->workspace === null) { - $this->workspace = new ServerWorkspace( - $this, - $this->codebase, - $this->project_analyzer, - ); - } + $this->logInfo("Initializing: Registering stub files..."); + $this->clientStatus('initializing', 'registering stub files'); + $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); - $serverCapabilities = new ServerCapabilities(); + if ($this->textDocument === null) { + $this->textDocument = new ServerTextDocument( + $this, + $this->codebase, + $this->project_analyzer, + ); + } - //The server provides execute command support. - $serverCapabilities->executeCommandProvider = new ExecuteCommandOptions(['test']); + if ($this->workspace === null) { + $this->workspace = new ServerWorkspace( + $this, + $this->codebase, + $this->project_analyzer, + ); + } - $textDocumentSyncOptions = new TextDocumentSyncOptions(); + $serverCapabilities = new ServerCapabilities(); - //Open and close notifications are sent to the server. - $textDocumentSyncOptions->openClose = true; + //The server provides execute command support. + $serverCapabilities->executeCommandProvider = new ExecuteCommandOptions(['test']); - $saveOptions = new SaveOptions(); - //The client is supposed to include the content on save. - $saveOptions->includeText = true; - $textDocumentSyncOptions->save = $saveOptions; + $textDocumentSyncOptions = new TextDocumentSyncOptions(); - /** - * Change notifications are sent to the server. See - * TextDocumentSyncKind.None, TextDocumentSyncKind.Full and - * TextDocumentSyncKind.Incremental. If omitted it defaults to - * TextDocumentSyncKind.None. - */ - if ($this->project_analyzer->onchange_line_limit === 0) { - /** - * Documents should not be synced at all. - */ - $textDocumentSyncOptions->change = TextDocumentSyncKind::NONE; - } else { - /** - * Documents are synced by always sending the full content - * of the document. - */ - $textDocumentSyncOptions->change = TextDocumentSyncKind::FULL; - } + //Open and close notifications are sent to the server. + $textDocumentSyncOptions->openClose = true; - /** - * Defines how text documents are synced. Is either a detailed structure - * defining each notification or for backwards compatibility the - * TextDocumentSyncKind number. If omitted it defaults to - * `TextDocumentSyncKind.None`. - */ - $serverCapabilities->textDocumentSync = $textDocumentSyncOptions; + $saveOptions = new SaveOptions(); + //The client is supposed to include the content on save. + $saveOptions->includeText = true; + $textDocumentSyncOptions->save = $saveOptions; - /** - * The server provides document symbol support. - * Support "Find all symbols" - */ - $serverCapabilities->documentSymbolProvider = false; - /** - * The server provides workspace symbol support. - * Support "Find all symbols in workspace" - */ - $serverCapabilities->workspaceSymbolProvider = false; - /** - * The server provides goto definition support. - * Support "Go to definition" - */ - $serverCapabilities->definitionProvider = true; - /** - * The server provides find references support. - * Support "Find all references" - */ - $serverCapabilities->referencesProvider = false; - /** - * The server provides hover support. - * Support "Hover" - */ - $serverCapabilities->hoverProvider = true; + /** + * Change notifications are sent to the server. See + * TextDocumentSyncKind.None, TextDocumentSyncKind.Full and + * TextDocumentSyncKind.Incremental. If omitted it defaults to + * TextDocumentSyncKind.None. + */ + if ($this->project_analyzer->onchange_line_limit === 0) { + /** + * Documents should not be synced at all. + */ + $textDocumentSyncOptions->change = TextDocumentSyncKind::NONE; + } else { + /** + * Documents are synced by always sending the full content + * of the document. + */ + $textDocumentSyncOptions->change = TextDocumentSyncKind::FULL; + } - /** - * The server provides completion support. - * Support "Completion" - */ - if ($this->project_analyzer->provide_completion) { - $serverCapabilities->completionProvider = new CompletionOptions(); - /** - * The server provides support to resolve additional - * information for a completion item. - */ - $serverCapabilities->completionProvider->resolveProvider = false; - /** - * Most tools trigger completion request automatically without explicitly - * requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they - * do so when the user starts to type an identifier. For example if the user - * types `c` in a JavaScript file code complete will automatically pop up - * present `console` besides others as a completion item. Characters that - * make up identifiers don't need to be listed here. - * - * If code complete should automatically be trigger on characters not being - * valid inside an identifier (for example `.` in JavaScript) list them in - * `triggerCharacters`. - */ - $serverCapabilities->completionProvider->triggerCharacters = ['$', '>', ':',"[", "(", ",", " "]; - } + /** + * Defines how text documents are synced. Is either a detailed structure + * defining each notification or for backwards compatibility the + * TextDocumentSyncKind number. If omitted it defaults to + * `TextDocumentSyncKind.None`. + */ + $serverCapabilities->textDocumentSync = $textDocumentSyncOptions; + + /** + * The server provides document symbol support. + * Support "Find all symbols" + */ + $serverCapabilities->documentSymbolProvider = false; + /** + * The server provides workspace symbol support. + * Support "Find all symbols in workspace" + */ + $serverCapabilities->workspaceSymbolProvider = false; + /** + * The server provides goto definition support. + * Support "Go to definition" + */ + $serverCapabilities->definitionProvider = true; + /** + * The server provides find references support. + * Support "Find all references" + */ + $serverCapabilities->referencesProvider = false; + /** + * The server provides hover support. + * Support "Hover" + */ + $serverCapabilities->hoverProvider = true; + + /** + * The server provides completion support. + * Support "Completion" + */ + if ($this->project_analyzer->provide_completion) { + $serverCapabilities->completionProvider = new CompletionOptions(); + /** + * The server provides support to resolve additional + * information for a completion item. + */ + $serverCapabilities->completionProvider->resolveProvider = false; + /** + * Most tools trigger completion request automatically without explicitly + * requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they + * do so when the user starts to type an identifier. For example if the user + * types `c` in a JavaScript file code complete will automatically pop up + * present `console` besides others as a completion item. Characters that + * make up identifiers don't need to be listed here. + * + * If code complete should automatically be trigger on characters not being + * valid inside an identifier (for example `.` in JavaScript) list them in + * `triggerCharacters`. + */ + $serverCapabilities->completionProvider->triggerCharacters = ['$', '>', ':',"[", "(", ",", " "]; + } - /** - * Whether code action supports the `data` property which is - * preserved between a `textDocument/codeAction` and a - * `codeAction/resolve` request. - * - * Support "Code Actions" if we support data - * - * @since LSP 3.16.0 - */ - if ($this->clientCapabilities->textDocument->publishDiagnostics->dataSupport ?? false) { - $serverCapabilities->codeActionProvider = true; - } + /** + * Whether code action supports the `data` property which is + * preserved between a `textDocument/codeAction` and a + * `codeAction/resolve` request. + * + * Support "Code Actions" if we support data + * + * @since LSP 3.16.0 + */ + if ($this->clientCapabilities->textDocument->publishDiagnostics->dataSupport ?? false) { + $serverCapabilities->codeActionProvider = true; + } - /** - * The server provides signature help support. - */ - $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions(['(', ',']); + /** + * The server provides signature help support. + */ + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions(['(', ',']); - if ($this->client->clientConfiguration->baseline !== null) { - $this->logInfo('Utilizing Baseline: '.$this->client->clientConfiguration->baseline); - $this->issue_baseline= ErrorBaseline::read( - new FileProvider, - $this->client->clientConfiguration->baseline, - ); - } + if ($this->client->clientConfiguration->baseline !== null) { + $this->logInfo('Utilizing Baseline: '.$this->client->clientConfiguration->baseline); + $this->issue_baseline= ErrorBaseline::read( + new FileProvider, + $this->client->clientConfiguration->baseline, + ); + } - $this->logInfo("Initializing: Complete."); - $this->clientStatus('initialized'); + $this->logInfo("Initializing: Complete."); + $this->clientStatus('initialized'); - /** - * Information about the server. - * - * @since LSP 3.15.0 - */ - $initializeResultServerInfo = new InitializeResultServerInfo('Psalm Language Server', PSALM_VERSION); + /** + * Information about the server. + * + * @since LSP 3.15.0 + */ + $initializeResultServerInfo = new InitializeResultServerInfo('Psalm Language Server', PSALM_VERSION); - return new InitializeResult($serverCapabilities, $initializeResultServerInfo); - }, - ); + return new InitializeResult($serverCapabilities, $initializeResultServerInfo); } /** @@ -647,12 +619,12 @@ function (array $opened, string $file_path) { */ public function doVersionedAnalysisDebounce(array $files, ?int $version = null): void { - Loop::cancel($this->versionedAnalysisDelayToken); + EventLoop::cancel($this->versionedAnalysisDelayToken); if ($this->client->clientConfiguration->onChangeDebounceMs === null) { $this->doVersionedAnalysis($files, $version); } else { /** @psalm-suppress MixedAssignment,UnusedPsalmSuppress */ - $this->versionedAnalysisDelayToken = Loop::delay( + $this->versionedAnalysisDelayToken = EventLoop::delay( $this->client->clientConfiguration->onChangeDebounceMs, fn() => $this->doVersionedAnalysis($files, $version), ); @@ -666,7 +638,7 @@ public function doVersionedAnalysisDebounce(array $files, ?int $version = null): */ public function doVersionedAnalysis(array $files, ?int $version = null): void { - Loop::cancel($this->versionedAnalysisDelayToken); + EventLoop::cancel($this->versionedAnalysisDelayToken); try { $this->logDebug("Doing Analysis from version: $version"); $this->codebase->reloadFiles( @@ -819,7 +791,7 @@ function (IssueData $issue_data) { * which they have sent a shutdown request. Clients should also wait with sending the exit notification until they * have received a response from the shutdown request. */ - public function shutdown(): Promise + public function shutdown(): void { $this->clientStatus('closing'); $this->logInfo("Shutting down..."); @@ -830,7 +802,6 @@ public function shutdown(): Promise $scanned_files, ); $this->clientStatus('closed'); - return new Success(null); } /** diff --git a/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php b/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php index 35031d8541d..81bd15833b7 100644 --- a/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php +++ b/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php @@ -5,12 +5,10 @@ namespace Psalm\Internal\LanguageServer; use AdvancedJsonRpc\Message as MessageBody; -use Amp\ByteStream\ResourceInputStream; -use Amp\Promise; +use Amp\ByteStream\ReadableResourceStream; use Exception; -use Generator; +use Revolt\EventLoop; -use function Amp\asyncCall; use function explode; use function strlen; use function substr; @@ -45,16 +43,11 @@ class ProtocolStreamReader implements ProtocolReader */ public function __construct($input) { - $input = new ResourceInputStream($input); - asyncCall( - /** - * @return Generator, ?string, void> - */ - function () use ($input): Generator { + $input = new ReadableResourceStream($input); + EventLoop::queue( + function () use ($input): void { while ($this->is_accepting_new_requests) { - $read_promise = $input->read(); - - $chunk = yield $read_promise; + $chunk = $input->read(); if ($chunk === null) { break; diff --git a/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php b/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php index 10d86e14cd9..bcf2f02b129 100644 --- a/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php +++ b/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php @@ -4,29 +4,28 @@ namespace Psalm\Internal\LanguageServer; -use Amp\ByteStream\ResourceOutputStream; -use Amp\Promise; +use Amp\ByteStream\WritableResourceStream; /** * @internal */ class ProtocolStreamWriter implements ProtocolWriter { - private ResourceOutputStream $output; + private WritableResourceStream $output; /** * @param resource $output */ public function __construct($output) { - $this->output = new ResourceOutputStream($output); + $this->output = new WritableResourceStream($output); } /** * {@inheritdoc} */ - public function write(Message $msg): Promise + public function write(Message $msg): void { - return $this->output->write((string)$msg); + $this->output->write((string)$msg); } } diff --git a/src/Psalm/Internal/LanguageServer/ProtocolWriter.php b/src/Psalm/Internal/LanguageServer/ProtocolWriter.php index a512012a369..9f94d6f7aac 100644 --- a/src/Psalm/Internal/LanguageServer/ProtocolWriter.php +++ b/src/Psalm/Internal/LanguageServer/ProtocolWriter.php @@ -4,14 +4,10 @@ namespace Psalm\Internal\LanguageServer; -use Amp\Promise; - interface ProtocolWriter { /** - * Sends a Message to the client - * - * @return Promise Resolved when the message has been fully written out to the output stream + * Sends a Message to the client. */ - public function write(Message $msg): Promise; + public function write(Message $msg): void; } diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index 1ea170a86f0..c2620de1c31 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -4,8 +4,6 @@ namespace Psalm\Internal\LanguageServer\Server; -use Amp\Promise; -use Amp\Success; use LanguageServerProtocol\CodeAction; use LanguageServerProtocol\CodeActionContext; use LanguageServerProtocol\CodeActionKind; @@ -166,12 +164,11 @@ public function didClose(TextDocumentIdentifier $textDocument): void * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position inside the text document - * @psalm-return Promise|Promise */ - public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise + public function definition(TextDocumentIdentifier $textDocument, Position $position): ?Location { if (!$this->server->client->clientConfiguration->provideDefinition) { - return new Success(null); + return null; } $this->server->logDebug( @@ -182,34 +179,32 @@ public function definition(TextDocumentIdentifier $textDocument, Position $posit //This currently doesnt work right with out of project files if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success(null); + return null; } try { $reference = $this->codebase->getReferenceAtPositionAsReference($file_path, $position); } catch (UnanalyzedFileException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } if ($reference === null) { - return new Success(null); + return null; } $code_location = $this->codebase->getSymbolLocationByReference($reference); if (!$code_location) { - return new Success(null); + return null; } - return new Success( - new Location( - LanguageServer::pathToUri($code_location->file_path), - new Range( - new Position($code_location->getLineNumber() - 1, $code_location->getColumn() - 1), - new Position($code_location->getEndLineNumber() - 1, $code_location->getEndColumn() - 1), - ), + return new Location( + LanguageServer::pathToUri($code_location->file_path), + new Range( + new Position($code_location->getLineNumber() - 1, $code_location->getColumn() - 1), + new Position($code_location->getEndLineNumber() - 1, $code_location->getEndColumn() - 1), ), ); } @@ -220,12 +215,11 @@ public function definition(TextDocumentIdentifier $textDocument, Position $posit * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position inside the text document - * @psalm-return Promise|Promise */ - public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise + public function hover(TextDocumentIdentifier $textDocument, Position $position): ?Hover { if (!$this->server->client->clientConfiguration->provideHover) { - return new Success(null); + return null; } $this->server->logDebug( @@ -236,32 +230,32 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): //This currently doesnt work right with out of project files if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success(null); + return null; } try { $reference = $this->codebase->getReferenceAtPositionAsReference($file_path, $position); } catch (UnanalyzedFileException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } if ($reference === null) { - return new Success(null); + return null; } try { $markup = $this->codebase->getMarkupContentForSymbolByReference($reference); } catch (UnexpectedValueException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } if ($markup === null) { - return new Success(null); + return null; } - return new Success(new Hover($markup, $reference->range)); + return new Hover($markup, $reference->range); } /** @@ -276,12 +270,11 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position - * @psalm-return Promise>|Promise|Promise */ - public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise + public function completion(TextDocumentIdentifier $textDocument, Position $position): ?CompletionList { if (!$this->server->client->clientConfiguration->provideCompletion) { - return new Success(null); + return null; } $this->server->logDebug( @@ -292,7 +285,7 @@ public function completion(TextDocumentIdentifier $textDocument, Position $posit //This currently doesnt work right with out of project files if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success(null); + return null; } try { @@ -314,42 +307,42 @@ public function completion(TextDocumentIdentifier $textDocument, Position $posit $file_path, ); } - return new Success(new CompletionList($completion_items, false)); + return new CompletionList($completion_items, false); } } catch (UnanalyzedFileException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } catch (TypeParseTreeException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } try { $type_context = $this->codebase->getTypeContextAtPosition($file_path, $position); if ($type_context) { $completion_items = $this->codebase->getCompletionItemsForType($type_context); - return new Success(new CompletionList($completion_items, false)); + return new CompletionList($completion_items, false); } } catch (UnexpectedValueException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } catch (TypeParseTreeException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } $this->server->logError('completion not found at ' . $position->line . ':' . $position->character); - return new Success(null); + return null; } /** * The signature help request is sent from the client to the server to request signature * information at a given cursor position. */ - public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise + public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): ?SignatureHelp { if (!$this->server->client->clientConfiguration->provideSignatureHelp) { - return new Success(null); + return null; } $this->server->logDebug( @@ -360,37 +353,35 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po //This currently doesnt work right with out of project files if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success(null); + return null; } try { $argument_location = $this->codebase->getFunctionArgumentAtPosition($file_path, $position); } catch (UnanalyzedFileException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } if ($argument_location === null) { - return new Success(null); + return null; } try { $signature_information = $this->codebase->getSignatureInformation($argument_location[0], $file_path); } catch (UnexpectedValueException $e) { $this->server->logThrowable($e); - return new Success(null); + return null; } if (!$signature_information) { - return new Success(null); + return null; } - return new Success( - new SignatureHelp( - [$signature_information], - 0, - $argument_location[1], - ), + return new SignatureHelp( + [$signature_information], + 0, + $argument_location[1], ); } @@ -401,10 +392,10 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po * * @psalm-suppress PossiblyUnusedParam */ - public function codeAction(TextDocumentIdentifier $textDocument, Range $range, CodeActionContext $context): Promise + public function codeAction(TextDocumentIdentifier $textDocument, Range $range, CodeActionContext $context): ?array { if (!$this->server->client->clientConfiguration->provideCodeActions) { - return new Success(null); + return null; } $this->server->logDebug( @@ -415,7 +406,7 @@ public function codeAction(TextDocumentIdentifier $textDocument, Range $range, C //Don't report code actions for files we arent watching if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success(null); + return null; } $fixers = []; @@ -479,11 +470,9 @@ public function codeAction(TextDocumentIdentifier $textDocument, Range $range, C } if (empty($fixers)) { - return new Success(null); + return null; } - return new Success( - array_values($fixers), - ); + return array_values($fixers); } } diff --git a/src/Psalm/Internal/LanguageServer/Server/Workspace.php b/src/Psalm/Internal/LanguageServer/Server/Workspace.php index af49619c356..0efaddd2bc6 100644 --- a/src/Psalm/Internal/LanguageServer/Server/Workspace.php +++ b/src/Psalm/Internal/LanguageServer/Server/Workspace.php @@ -4,8 +4,6 @@ namespace Psalm\Internal\LanguageServer\Server; -use Amp\Promise; -use Amp\Success; use InvalidArgumentException; use LanguageServerProtocol\FileChangeType; use LanguageServerProtocol\FileEvent; @@ -126,7 +124,7 @@ public function didChangeConfiguration($settings): void * @param mixed $arguments * @psalm-suppress PossiblyUnusedMethod */ - public function executeCommand(string $command, $arguments): Promise + public function executeCommand(string $command, $arguments): void { $this->server->logDebug( 'workspace/executeCommand', @@ -155,7 +153,5 @@ public function executeCommand(string $command, $arguments): Promise $this->server->emitVersionedIssues([$file => $arguments['uri']]); break; } - - return new Success(null); } } diff --git a/tests/LanguageServer/DiagnosticTest.php b/tests/LanguageServer/DiagnosticTest.php index 690c008ea95..c3e7ff841fb 100644 --- a/tests/LanguageServer/DiagnosticTest.php +++ b/tests/LanguageServer/DiagnosticTest.php @@ -2,7 +2,7 @@ namespace Psalm\Tests\LanguageServer; -use Amp\Deferred; +use Amp\DeferredFuture; use Psalm\Codebase; use Psalm\Internal\Analyzer\IssueData; use Psalm\Internal\Analyzer\ProjectAnalyzer; @@ -21,7 +21,6 @@ use Psalm\Tests\LanguageServer\MockProtocolStream; use Psalm\Tests\TestConfig; -use function Amp\Promise\wait; use function rand; class DiagnosticTest extends AsyncTestCase @@ -65,7 +64,7 @@ public function setUp(): void public function testSnippetSupportDisabled(): void { // Create a new promisor - $deferred = new Deferred; + $deferred = new DeferredFuture; $this->setTimeout(5000); $clientConfiguration = new ClientConfiguration(); @@ -91,11 +90,11 @@ public function testSnippetSupportDisabled(): void /** @psalm-suppress PossiblyNullPropertyFetch,UndefinedPropertyFetch,MixedPropertyFetch */ if ($message->body->method === 'telemetry/event' && $message->body->params->message === 'initialized') { $this->assertFalse($server->clientCapabilities->textDocument->completion->completionItem->snippetSupport); - $deferred->resolve(null); + $deferred->complete(null); } }); - wait($deferred->promise()); + $deferred->getFuture()->await(); } /** diff --git a/tests/LanguageServer/MockProtocolStream.php b/tests/LanguageServer/MockProtocolStream.php index 34f8072c9cc..b95704d8c82 100644 --- a/tests/LanguageServer/MockProtocolStream.php +++ b/tests/LanguageServer/MockProtocolStream.php @@ -4,14 +4,12 @@ namespace Psalm\Tests\LanguageServer; -use Amp\Deferred; -use Amp\Loop; -use Amp\Promise; use Psalm\Internal\LanguageServer\EmitterInterface; use Psalm\Internal\LanguageServer\EmitterTrait; use Psalm\Internal\LanguageServer\Message; use Psalm\Internal\LanguageServer\ProtocolReader; use Psalm\Internal\LanguageServer\ProtocolWriter; +use Revolt\EventLoop; /** * A fake duplex protocol stream @@ -24,17 +22,10 @@ class MockProtocolStream implements ProtocolReader, ProtocolWriter, EmitterInter * * @psalm-suppress PossiblyUnusedReturnValue */ - public function write(Message $msg): Promise + public function write(Message $msg): void { - Loop::defer(function () use ($msg): void { + EventLoop::queue(function () use ($msg): void { $this->emit('message', [Message::parse((string)$msg)]); }); - - // Create a new promisor - $deferred = new Deferred; - - $deferred->resolve(null); - - return $deferred->promise(); } } From 483fabfe93569da7ba054a2bd5101a7c35552328 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 19 Jul 2023 11:00:58 +0200 Subject: [PATCH 02/31] Fix syntax issue --- src/Psalm/Internal/LanguageServer/LanguageServer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 87ff272a701..3d100cbf2eb 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -210,7 +210,6 @@ function (Message $msg): void { $this->protocolWriter->write(new Message($responseBody)); } }, - ), ); $this->protocolReader->on( From 70319a68a7696105384759252bc55810c0c197e1 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 20 Jul 2023 19:19:12 +0200 Subject: [PATCH 03/31] Switch to 8.1 --- .circleci/config.yml | 13 +++++-------- .github/workflows/build-phar.yml | 2 +- .github/workflows/ci.yml | 2 +- composer.json | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fb7edfe00e2..c34d1d3de4b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,18 +1,15 @@ # Use the latest 2.1 version of CircleCI pipeline processing engine, see https://circleci.com/docs/2.0/configuration-reference/ version: 2.1 executors: - php-74: - docker: - - image: thecodingmachine/php:7.4-v4-cli - php-80: - docker: - - image: thecodingmachine/php:8.0-v4-cli php-81: docker: - image: thecodingmachine/php:8.1-v4-cli + php-82: + docker: + - image: thecodingmachine/php:8.2-v4-cli jobs: "Code Style Analysis": - executor: php-74 + executor: php-81 steps: - checkout @@ -40,7 +37,7 @@ jobs: command: vendor/bin/phpcs -d memory_limit=512M phar-build: - executor: php-74 + executor: php-81 steps: - attach_workspace: at: /home/docker/project/ diff --git a/.github/workflows/build-phar.yml b/.github/workflows/build-phar.yml index 8d75dfe63c3..ba0c5c6d1e9 100644 --- a/.github/workflows/build-phar.yml +++ b/.github/workflows/build-phar.yml @@ -38,7 +38,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' tools: composer:v2 coverage: none env: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0693fb9c4a0..0e6faa7ddcd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' tools: composer:v2 coverage: none env: diff --git a/composer.json b/composer.json index 499e888759b..3dd9d086774 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "php": "~8.1.0 || ~8.2.0", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-dom": "*", From 57dfad65c599bd1192b4a264082b2b05a22bae96 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 21 Jul 2023 14:21:03 +0200 Subject: [PATCH 04/31] Fix e2e tests --- tests/fixtures/DummyProjectWithErrors/composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/fixtures/DummyProjectWithErrors/composer.json b/tests/fixtures/DummyProjectWithErrors/composer.json index af6d9be31ad..905448a4daa 100644 --- a/tests/fixtures/DummyProjectWithErrors/composer.json +++ b/tests/fixtures/DummyProjectWithErrors/composer.json @@ -3,8 +3,7 @@ "description": "A sample project to be used when testing Psalm", "type": "project", "require": { - "php": ">= 7.1", - "vimeo/psalm": "^4.3" + "php": ">= 7.1" }, "autoload": { "psr-4": { From a2e4961dca90d27f2a9c9b1bd55472e6b94581e6 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 21 Jul 2023 14:42:18 +0200 Subject: [PATCH 05/31] Fix CI --- .github/workflows/windows-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 5b6a06c4e08..3d89ba846af 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -54,7 +54,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M tools: composer:v2 coverage: none From 79da33221fc89ccd2f6fbcebe68d506009ccc632 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 09:48:35 +0200 Subject: [PATCH 06/31] Fixes --- psalm-baseline.xml | 15 ++++++++- .../Internal/Algebra/FormulaGenerator.php | 2 -- .../Internal/Analyzer/NamespaceAnalyzer.php | 6 ++-- src/Psalm/Internal/Codebase/Reflection.php | 1 - .../LanguageServer/LanguageClient.php | 13 ++++---- .../ArrayCombineReturnTypeProvider.php | 2 -- .../SprintfReturnTypeProvider.php | 33 ++----------------- src/Psalm/Type/Reconciler.php | 2 +- .../Codebase/InternalCallMapHandlerTest.php | 1 - tests/LanguageServer/MockProtocolStream.php | 2 -- 10 files changed, 27 insertions(+), 50 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 69c51858103..b299a6eedbe 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + tags['variablesfrom'][0]]]> @@ -290,6 +290,19 @@ props[0]]]> + + + $config + + + [$config] + + + + + $result + + $method_id_parts[1] diff --git a/src/Psalm/Internal/Algebra/FormulaGenerator.php b/src/Psalm/Internal/Algebra/FormulaGenerator.php index c5d75f8829c..9c5482c4a57 100644 --- a/src/Psalm/Internal/Algebra/FormulaGenerator.php +++ b/src/Psalm/Internal/Algebra/FormulaGenerator.php @@ -149,7 +149,6 @@ public static function getFormula( $redefined = false; if ($var[0] === '=') { - /** @var string */ $var = substr($var, 1); $redefined = true; } @@ -420,7 +419,6 @@ public static function getFormula( $redefined = false; if ($var[0] === '=') { - /** @var string */ $var = substr($var, 1); $redefined = true; } diff --git a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php index a01318a3e81..e08bffbe29e 100644 --- a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php @@ -244,7 +244,7 @@ public static function getIdentifierParts(string $identifier): array while (($pos = strpos($identifier, "\\")) !== false) { if ($pos > 0) { $part = substr($identifier, 0, $pos); - assert(is_string($part) && $part !== ""); + assert($part !== ""); $parts[] = $part; } $parts[] = "\\"; @@ -253,13 +253,13 @@ public static function getIdentifierParts(string $identifier): array if (($pos = strpos($identifier, "::")) !== false) { if ($pos > 0) { $part = substr($identifier, 0, $pos); - assert(is_string($part) && $part !== ""); + assert($part !== ""); $parts[] = $part; } $parts[] = "::"; $identifier = substr($identifier, $pos + 2); } - if ($identifier !== "" && $identifier !== false) { + if ($identifier !== "") { $parts[] = $identifier; } diff --git a/src/Psalm/Internal/Codebase/Reflection.php b/src/Psalm/Internal/Codebase/Reflection.php index f62cfdc7e28..06f6178844b 100644 --- a/src/Psalm/Internal/Codebase/Reflection.php +++ b/src/Psalm/Internal/Codebase/Reflection.php @@ -426,7 +426,6 @@ public static function getPsalmTypeFromReflectionType(?ReflectionType $reflectio if ($reflection_type instanceof ReflectionNamedType) { $type = $reflection_type->getName(); } elseif ($reflection_type instanceof ReflectionUnionType) { - /** @psalm-suppress MixedArgument */ $type = implode( '|', array_map( diff --git a/src/Psalm/Internal/LanguageServer/LanguageClient.php b/src/Psalm/Internal/LanguageServer/LanguageClient.php index 0aed33950a6..c3f5d60b2f4 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageClient.php +++ b/src/Psalm/Internal/LanguageServer/LanguageClient.php @@ -9,6 +9,7 @@ use LanguageServerProtocol\LogTrace; use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument; use Psalm\Internal\LanguageServer\Client\Workspace as ClientWorkspace; +use Revolt\EventLoop; use function is_null; use function json_decode; @@ -65,13 +66,13 @@ public function refreshConfiguration(): void { $capabilities = $this->server->clientCapabilities; if ($capabilities->workspace->configuration ?? false) { - $this->workspace->requestConfiguration('psalm')->onResolve(function ($error, $value): void { - if ($error) { - $this->server->logError('There was an error getting configuration'); - } else { - /** @var array $value */ - [$config] = $value; + EventLoop::queue(function () { + try { + /** @var object $config */ + [$config] = $this->workspace->requestConfiguration('psalm'); $this->configurationRefreshed((array) $config); + } catch (\Throwable) { + $this->server->logError('There was an error getting configuration'); } }); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php index 88125e398e3..999f0b84ef5 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php @@ -121,8 +121,6 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $values, ); - assert($result !== false); - if (!$result) { return Type::getEmptyArray(); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php index 8c9af9410bc..925bc0c1a92 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/SprintfReturnTypeProvider.php @@ -201,35 +201,6 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - if ($result === false && count($dummy) === $provided_placeholders_count) { - // could be invalid format or too few arguments - // we cannot distinguish this in PHP 7 without additional checks - $max_dummy = array_fill(0, 100, ''); - $result = @sprintf($type->getSingleStringLiteral()->value, ...$max_dummy); - if ($result === false) { - // the format is invalid - IssueBuffer::maybeAdd( - new InvalidArgument( - 'Argument 1 of ' . $event->getFunctionId() . ' is invalid', - $event->getCodeLocation(), - $event->getFunctionId(), - ), - $statements_source->getSuppressedIssues(), - ); - } else { - IssueBuffer::maybeAdd( - new TooFewArguments( - 'Too few arguments for ' . $event->getFunctionId(), - $event->getCodeLocation(), - $event->getFunctionId(), - ), - $statements_source->getSuppressedIssues(), - ); - } - - return Type::getFalse(); - } - // we can only validate the format and arg 1 when using splat if ($has_splat_args === true) { break; @@ -264,13 +235,13 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return null; } - if ($initial_result !== null && $initial_result !== false && $initial_result !== '') { + if ($initial_result !== null && $initial_result !== '') { return Type::getNonEmptyString(); } // if we didn't have any valid result // the pattern is invalid or not yet supported by the return type provider - if ($initial_result === null || $initial_result === false) { + if ($initial_result === null) { return null; } diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 074ef233ab3..8d36c9990db 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -1109,7 +1109,7 @@ private static function adjustTKeyedArrayType( $base_key = implode($key_parts); - if (isset($existing_types[$base_key]) && $array_key_offset !== false) { + if (isset($existing_types[$base_key]) && $array_key_offset !== '') { foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) { if ($base_atomic_type instanceof TList) { $base_atomic_type = $base_atomic_type->getKeyedArray(); diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index b21fc991a6b..58420c14400 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -606,7 +606,6 @@ private function assertParameter(array $normalizedEntry, ReflectionParameter $pa public function assertEntryReturnType(ReflectionFunctionAbstract $function, string $entryReturnType): void { if (version_compare(PHP_VERSION, '8.1.0', '>=')) { - /** @var ReflectionType|null $expectedType */ $expectedType = $function->hasTentativeReturnType() ? $function->getTentativeReturnType() : $function->getReturnType(); } else { $expectedType = $function->getReturnType(); diff --git a/tests/LanguageServer/MockProtocolStream.php b/tests/LanguageServer/MockProtocolStream.php index b95704d8c82..f3db6aebd95 100644 --- a/tests/LanguageServer/MockProtocolStream.php +++ b/tests/LanguageServer/MockProtocolStream.php @@ -19,8 +19,6 @@ class MockProtocolStream implements ProtocolReader, ProtocolWriter, EmitterInter use EmitterTrait; /** * Sends a Message to the client - * - * @psalm-suppress PossiblyUnusedReturnValue */ public function write(Message $msg): void { From f69c3f84589bbe258f64ea872794383a356b465d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 09:53:45 +0200 Subject: [PATCH 07/31] Remove psalm v6 deprecations --- src/Psalm/Codebase.php | 218 ----------------- src/Psalm/Config.php | 6 - src/Psalm/Internal/Algebra.php | 4 +- .../FunctionLike/ReturnTypeCollector.php | 5 - .../Statements/Block/ForeachAnalyzer.php | 5 - .../Statements/Expression/ArrayAnalyzer.php | 4 - .../Statements/Expression/AssertionFinder.php | 4 - .../Assignment/ArrayAssignmentAnalyzer.php | 3 - .../Expression/AssignmentAnalyzer.php | 4 - .../Statements/Expression/CastAnalyzer.php | 14 -- .../Expression/SimpleTypeInferer.php | 3 - .../Analyzer/Statements/UnsetAnalyzer.php | 1 - src/Psalm/Plugin/Shepherd.php | 35 --- src/Psalm/Storage/FunctionLikeStorage.php | 16 -- src/Psalm/Type/Atomic/TCallableList.php | 46 ---- src/Psalm/Type/Atomic/TClassStringMap.php | 4 - src/Psalm/Type/Atomic/TDependentListKey.php | 53 ---- src/Psalm/Type/Atomic/TList.php | 228 ------------------ src/Psalm/Type/Atomic/TNonEmptyList.php | 94 -------- 19 files changed, 1 insertion(+), 746 deletions(-) delete mode 100644 src/Psalm/Type/Atomic/TCallableList.php delete mode 100644 src/Psalm/Type/Atomic/TDependentListKey.php delete mode 100644 src/Psalm/Type/Atomic/TList.php delete mode 100644 src/Psalm/Type/Atomic/TNonEmptyList.php diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index 56a9aab5d34..942569312ac 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -1198,224 +1198,6 @@ public function getMarkupContentForSymbolByReference( return new PHPMarkdownContent($reference->symbol); } - /** - * @psalm-suppress PossiblyUnusedMethod - * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead - */ - public function getSymbolInformation(string $file_path, string $symbol): ?array - { - if (is_numeric($symbol[0])) { - return ['type' => preg_replace('/^[^:]*:/', '', $symbol)]; - } - - try { - if (strpos($symbol, '::')) { - if (strpos($symbol, '()')) { - $symbol = substr($symbol, 0, -2); - - /** @psalm-suppress ArgumentTypeCoercion */ - $method_id = new MethodIdentifier(...explode('::', $symbol)); - - $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); - - if (!$declaring_method_id) { - return null; - } - - $storage = $this->methods->getStorage($declaring_method_id); - - return [ - 'type' => 'getCompletionSignature(), - 'description' => $storage->description, - ]; - } - - [, $symbol_name] = explode('::', $symbol); - - if (strpos($symbol, '$') !== false) { - $storage = $this->properties->getStorage($symbol); - - return [ - 'type' => 'getInfo() . ' ' . $symbol_name, - 'description' => $storage->description, - ]; - } - - [$fq_classlike_name, $const_name] = explode('::', $symbol); - - $class_constants = $this->classlikes->getConstantsForClass( - $fq_classlike_name, - ReflectionProperty::IS_PRIVATE, - ); - - if (!isset($class_constants[$const_name])) { - return null; - } - - return [ - 'type' => ' $class_constants[$const_name]->description, - ]; - } - - if (strpos($symbol, '()')) { - $function_id = strtolower(substr($symbol, 0, -2)); - $file_storage = $this->file_storage_provider->get($file_path); - - if (isset($file_storage->functions[$function_id])) { - $function_storage = $file_storage->functions[$function_id]; - - return [ - 'type' => 'getCompletionSignature(), - 'description' => $function_storage->description, - ]; - } - - if (!$function_id) { - return null; - } - - $function = $this->functions->getStorage(null, $function_id); - return [ - 'type' => 'getCompletionSignature(), - 'description' => $function->description, - ]; - } - - if (strpos($symbol, '$') === 0) { - $type = VariableFetchAnalyzer::getGlobalType($symbol, $this->analysis_php_version_id); - if (!$type->isMixed()) { - return ['type' => 'classlike_storage_provider->get($symbol); - return [ - 'type' => 'abstract ? 'abstract ' : '') . 'class ' . $storage->name, - 'description' => $storage->description, - ]; - } catch (InvalidArgumentException $e) { - } - - if (strpos($symbol, '\\')) { - $const_name_parts = explode('\\', $symbol); - $const_name = array_pop($const_name_parts); - $namespace_name = implode('\\', $const_name_parts); - - $namespace_constants = NamespaceAnalyzer::getConstantsForNamespace( - $namespace_name, - ReflectionProperty::IS_PUBLIC, - ); - if (isset($namespace_constants[$const_name])) { - $type = $namespace_constants[$const_name]; - return ['type' => 'file_storage_provider->get($file_path); - if (isset($file_storage->constants[$symbol])) { - return ['type' => 'constants[$symbol]]; - } - $constant = ConstFetchAnalyzer::getGlobalConstType($this, $symbol, $symbol); - - if ($constant) { - return ['type' => 'getMessage()); - - return null; - } - } - - /** - * @psalm-suppress PossiblyUnusedMethod - * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead - */ - public function getSymbolLocation(string $file_path, string $symbol): ?CodeLocation - { - if (is_numeric($symbol[0])) { - $symbol = preg_replace('/:.*/', '', $symbol); - $symbol_parts = explode('-', $symbol); - - $file_contents = $this->getFileContents($file_path); - - return new Raw( - $file_contents, - $file_path, - $this->config->shortenFileName($file_path), - (int) $symbol_parts[0], - (int) $symbol_parts[1], - ); - } - - try { - if (strpos($symbol, '::')) { - if (strpos($symbol, '()')) { - $symbol = substr($symbol, 0, -2); - - /** @psalm-suppress ArgumentTypeCoercion */ - $method_id = new MethodIdentifier(...explode('::', $symbol)); - - $declaring_method_id = $this->methods->getDeclaringMethodId($method_id); - - if (!$declaring_method_id) { - return null; - } - - $storage = $this->methods->getStorage($declaring_method_id); - - return $storage->location; - } - - if (strpos($symbol, '$') !== false) { - $storage = $this->properties->getStorage($symbol); - - return $storage->location; - } - - [$fq_classlike_name, $const_name] = explode('::', $symbol); - - $class_constants = $this->classlikes->getConstantsForClass( - $fq_classlike_name, - ReflectionProperty::IS_PRIVATE, - ); - - if (!isset($class_constants[$const_name])) { - return null; - } - - return $class_constants[$const_name]->location; - } - - if (strpos($symbol, '()')) { - $file_storage = $this->file_storage_provider->get($file_path); - - $function_id = strtolower(substr($symbol, 0, -2)); - - if (isset($file_storage->functions[$function_id])) { - return $file_storage->functions[$function_id]->location; - } - - if (!$function_id) { - return null; - } - - return $this->functions->getStorage(null, $function_id)->location; - } - - return $this->classlike_storage_provider->get($symbol)->location; - } catch (UnexpectedValueException $e) { - error_log($e->getMessage()); - - return null; - } catch (InvalidArgumentException $e) { - return null; - } - } - public function getSymbolLocationByReference(Reference $reference): ?CodeLocation { if (is_numeric($reference->symbol[0])) { diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index e84a134f37a..2633a8986bb 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -551,12 +551,6 @@ class Config */ public $include_php_versions_in_error_baseline = false; - /** - * @var string - * @deprecated Please use {@see self::$shepherd_endpoint} instead. Property will be removed in Psalm 6. - */ - public $shepherd_host = 'shepherd.dev'; - /** * @var string * @internal diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index d2b16a26c0f..043e8b1e13d 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -6,7 +6,6 @@ use Psalm\Storage\Assertion; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use UnexpectedValueException; use function array_filter; @@ -406,8 +405,7 @@ public static function getTruthsFromFormula( continue; } - if ($assertion->type instanceof TList - || $assertion->type instanceof TArray + if ($assertion->type instanceof TArray || $assertion->type instanceof TKeyedArray) { $has_list_or_array = true; // list/array are collapsed, therefore there can only be 1 and we can abort diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php index 13280aa1746..c707048d1c6 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php @@ -13,7 +13,6 @@ use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Union; @@ -265,10 +264,6 @@ private static function processYieldTypes( $yield_type = Type::combineUnionTypeArray($yield_types, null); foreach ($yield_type->getAtomicTypes() as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } - if ($type instanceof TKeyedArray) { $type = $type->getGenericArrayType(); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index bb55c91f764..82a812243a7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -47,7 +47,6 @@ use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNever; @@ -472,11 +471,7 @@ public static function checkIteratorType( if ($iterator_atomic_type instanceof TArray || $iterator_atomic_type instanceof TKeyedArray - || $iterator_atomic_type instanceof TList ) { - if ($iterator_atomic_type instanceof TList) { - $iterator_atomic_type = $iterator_atomic_type->getKeyedArray(); - } if ($iterator_atomic_type instanceof TKeyedArray) { if (!$iterator_atomic_type->isNonEmpty()) { $always_non_empty_array = false; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index fa1fb7f1248..37f25559847 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -29,7 +29,6 @@ use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; @@ -526,9 +525,6 @@ private static function handleUnpackedArray( $has_possibly_undefined = false; foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) { - if ($unpacked_atomic_type instanceof TList) { - $unpacked_atomic_type = $unpacked_atomic_type->getKeyedArray(); - } if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if ($property_value->possibly_undefined) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index fc57f2177c7..52ea3e529c8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -81,7 +81,6 @@ use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; @@ -3655,9 +3654,6 @@ private static function getInarrayAssertions( && !$expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch ) { foreach ($second_arg_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } if ($atomic_type instanceof TArray || $atomic_type instanceof TKeyedArray ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index c071a741181..b692d169aa2 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -297,9 +297,6 @@ private static function updateTypeWithKeyValues( $changed = false; $types = []; foreach ($child_stmt_type->getAtomicTypes() as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } $old_type = $type; if ($type instanceof TTemplateParam) { $type = $type->replaceAs(self::updateTypeWithKeyValues( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 76c7073c12e..8268587db44 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -78,7 +78,6 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyArray; @@ -1228,9 +1227,6 @@ private static function analyzeDestructuringAssignment( $has_null = false; foreach ($assign_value_type->getAtomicTypes() as $assign_value_atomic_type) { - if ($assign_value_atomic_type instanceof TList) { - $assign_value_atomic_type = $assign_value_atomic_type->getKeyedArray(); - } if ($assign_value_atomic_type instanceof TKeyedArray && !$assign_var_item->key ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 83825307c32..5ba762d5758 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -200,9 +200,6 @@ public static function analyze( $all_permissible = true; foreach ($stmt_expr_type->getAtomicTypes() as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type instanceof Scalar) { $objWithProps = new TObjectWithProperties(['scalar' => new Union([$type])]); $permissible_atomic_types[] = $objWithProps; @@ -247,9 +244,6 @@ public static function analyze( $all_permissible = true; foreach ($stmt_expr_type->getAtomicTypes() as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type instanceof Scalar) { $keyed_array = new TKeyedArray([new Union([$type])], null, null, true); $permissible_atomic_types[] = $keyed_array; @@ -337,10 +331,6 @@ public static function castIntAttempt( while ($atomic_types) { $atomic_type = array_pop($atomic_types); - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } - if ($atomic_type instanceof TInt) { $valid_ints[] = $atomic_type; @@ -527,10 +517,6 @@ public static function castFloatAttempt( while ($atomic_types) { $atomic_type = array_pop($atomic_types); - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } - if ($atomic_type instanceof TFloat) { $valid_floats[] = $atomic_type; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index ab144621edb..6456623b06a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -756,9 +756,6 @@ private static function handleUnpackedArray( Union $unpacked_array_type ): bool { foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) { - if ($unpacked_atomic_type instanceof TList) { - $unpacked_atomic_type = $unpacked_atomic_type->getKeyedArray(); - } if ($unpacked_atomic_type instanceof TKeyedArray) { foreach ($unpacked_atomic_type->properties as $key => $property_value) { if (is_string($key)) { diff --git a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php index b1c12d12cd4..22a54345855 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php @@ -10,7 +10,6 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNever; diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index 096a17bb9cf..37a61c29897 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -82,19 +82,6 @@ public static function afterAnalysis( self::sendPayload($shepherd_endpoint, $rawPayload); } - /** - * @psalm-pure - * @deprecated Will be removed in Psalm 6 - */ - private static function buildShepherdUrlFromHost(string $host): string - { - if (parse_url($host, PHP_URL_SCHEME) === null) { - $host = 'https://' . $host; - } - - return $host . '/hooks/psalm'; - } - /** * @return array{ * build: array, @@ -205,28 +192,6 @@ private static function sendPayload(string $endpoint, array $rawPayload): void fwrite(STDERR, $output); } - /** - * @param mixed $ch - * @psalm-pure - * @deprecated Will be removed in Psalm 6 - */ - public static function getCurlErrorMessage($ch): string - { - /** - * @psalm-suppress MixedArgument - * @var array - */ - $curl_info = curl_getinfo($ch); - - /** @psalm-suppress MixedAssignment */ - $ssl_verify_result = $curl_info['ssl_verify_result'] ?? null; - if (is_int($ssl_verify_result) && $ssl_verify_result > 1) { - return self::getCurlSslErrorMessage($ssl_verify_result); - } - - return ''; - } - /** * @psalm-pure */ diff --git a/src/Psalm/Storage/FunctionLikeStorage.php b/src/Psalm/Storage/FunctionLikeStorage.php index 929e2b84d7a..d158b3d55fb 100644 --- a/src/Psalm/Storage/FunctionLikeStorage.php +++ b/src/Psalm/Storage/FunctionLikeStorage.php @@ -172,13 +172,6 @@ abstract class FunctionLikeStorage implements HasAttributesInterface */ public $return_type_description; - /** - * @psalm-suppress PossiblyUnusedProperty - * @var array|null - * @deprecated will be removed in Psalm 6. use {@see FunctionLikeStorage::$unused_docblock_parameters} instead - */ - public $unused_docblock_params; - /** * @var array */ @@ -346,13 +339,4 @@ public function __toString(): string { return $this->getCompletionSignature(); } - - /** - * @deprecated will be removed in Psalm 6. use {@see FunctionLikeStorage::getCompletionSignature()} instead - * @psalm-suppress PossiblyUnusedParam, PossiblyUnusedMethod - */ - public function getSignature(bool $allow_newlines): string - { - return $this->getCompletionSignature(); - } } diff --git a/src/Psalm/Type/Atomic/TCallableList.php b/src/Psalm/Type/Atomic/TCallableList.php deleted file mode 100644 index 764f1ca2c4d..00000000000 --- a/src/Psalm/Type/Atomic/TCallableList.php +++ /dev/null @@ -1,46 +0,0 @@ -count && !$this->min_count) { - return new TKeyedArray( - [$this->type_param], - null, - [Type::getListKey(), $this->type_param], - true, - $this->from_docblock, - ); - } - if ($this->count) { - return new TCallableKeyedArray( - array_fill(0, $this->count, $this->type_param), - null, - null, - true, - $this->from_docblock, - ); - } - return new TCallableKeyedArray( - array_fill(0, $this->min_count, $this->type_param), - null, - [Type::getListKey(), $this->type_param], - true, - $this->from_docblock, - ); - } -} diff --git a/src/Psalm/Type/Atomic/TClassStringMap.php b/src/Psalm/Type/Atomic/TClassStringMap.php index d15d297f10e..915e794a116 100644 --- a/src/Psalm/Type/Atomic/TClassStringMap.php +++ b/src/Psalm/Type/Atomic/TClassStringMap.php @@ -137,10 +137,6 @@ public function replaceTemplateTypesWithStandins( foreach ([Type::getString(), $this->value_param] as $offset => $type_param) { $input_type_param = null; - if ($input_type instanceof TList) { - $input_type = $input_type->getKeyedArray(); - } - if (($input_type instanceof TGenericObject || $input_type instanceof TIterable || $input_type instanceof TArray) diff --git a/src/Psalm/Type/Atomic/TDependentListKey.php b/src/Psalm/Type/Atomic/TDependentListKey.php deleted file mode 100644 index 338136d84d9..00000000000 --- a/src/Psalm/Type/Atomic/TDependentListKey.php +++ /dev/null @@ -1,53 +0,0 @@ - $value) - * - * @deprecated Will be removed in Psalm v6, use TIntRange instead - * @psalm-immutable - */ -final class TDependentListKey extends TInt implements DependentType -{ - /** - * Used to hold information as to what list variable this refers to - * - * @var string - */ - public $var_id; - - /** - * @param string $var_id the variable id - */ - public function __construct(string $var_id) - { - $this->var_id = $var_id; - parent::__construct(false); - } - - public function getId(bool $exact = true, bool $nested = false): string - { - return 'list-key<' . $this->var_id . '>'; - } - - public function getVarId(): string - { - return $this->var_id; - } - - public function getAssertionString(): string - { - return 'int'; - } - - public function getReplacement(): TInt - { - return new TInt(); - } - - public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool - { - return false; - } -} diff --git a/src/Psalm/Type/Atomic/TList.php b/src/Psalm/Type/Atomic/TList.php deleted file mode 100644 index 13c44e5b453..00000000000 --- a/src/Psalm/Type/Atomic/TList.php +++ /dev/null @@ -1,228 +0,0 @@ -type_param = $type_param; - parent::__construct($from_docblock); - } - - /** - * @return static - */ - public function setTypeParam(Union $type_param): self - { - if ($type_param === $this->type_param) { - return $this; - } - $cloned = clone $this; - $cloned->type_param = $type_param; - return $cloned; - } - - public function getKeyedArray(): TKeyedArray - { - return Type::getListAtomic($this->type_param); - } - - public function getId(bool $exact = true, bool $nested = false): string - { - return static::KEY . '<' . $this->type_param->getId($exact) . '>'; - } - - /** - * @param array $aliased_classes - */ - public function toNamespacedString( - ?string $namespace, - array $aliased_classes, - ?string $this_class, - bool $use_phpdoc_format - ): string { - if ($use_phpdoc_format) { - return (new TArray([Type::getInt(), $this->type_param])) - ->toNamespacedString( - $namespace, - $aliased_classes, - $this_class, - true, - ); - } - - return static::KEY - . '<' - . $this->type_param->toNamespacedString( - $namespace, - $aliased_classes, - $this_class, - false, - ) - . '>'; - } - - /** - * @param array $aliased_classes - */ - public function toPhpString( - ?string $namespace, - array $aliased_classes, - ?string $this_class, - int $analysis_php_version_id - ): string { - return 'array'; - } - - public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool - { - return false; - } - - public function getKey(bool $include_extra = true): string - { - return 'array'; - } - - /** - * @psalm-suppress InaccessibleProperty We're only acting on cloned instances - * @return static - */ - public function replaceTemplateTypesWithStandins( - TemplateResult $template_result, - Codebase $codebase, - ?StatementsAnalyzer $statements_analyzer = null, - ?Atomic $input_type = null, - ?int $input_arg_offset = null, - ?string $calling_class = null, - ?string $calling_function = null, - bool $replace = true, - bool $add_lower_bound = false, - int $depth = 0 - ): self { - $cloned = null; - - foreach ([Type::getInt(), $this->type_param] as $offset => $type_param) { - $input_type_param = null; - - if (($input_type instanceof TGenericObject - || $input_type instanceof TIterable - || $input_type instanceof TArray) - && - isset($input_type->type_params[$offset]) - ) { - $input_type_param = $input_type->type_params[$offset]; - } elseif ($input_type instanceof TKeyedArray) { - if ($offset === 0) { - $input_type_param = $input_type->getGenericKeyType(); - } else { - $input_type_param = $input_type->getGenericValueType(); - } - } elseif ($input_type instanceof TList) { - if ($offset === 0) { - continue; - } - - $input_type_param = $input_type->type_param; - } - - $type_param = TemplateStandinTypeReplacer::replace( - $type_param, - $template_result, - $codebase, - $statements_analyzer, - $input_type_param, - $input_arg_offset, - $calling_class, - $calling_function, - $replace, - $add_lower_bound, - null, - $depth + 1, - ); - - if ($offset === 1 && ($cloned || $this->type_param !== $type_param)) { - $cloned ??= clone $this; - $cloned->type_param = $type_param; - } - } - - return $cloned ?? $this; - } - - /** - * @return static - */ - public function replaceTemplateTypesWithArgTypes( - TemplateResult $template_result, - ?Codebase $codebase - ): self { - return $this->setTypeParam(TemplateInferredTypeReplacer::replace( - $this->type_param, - $template_result, - $codebase, - )); - } - - public function equals(Atomic $other_type, bool $ensure_source_equality): bool - { - if (get_class($other_type) !== static::class) { - return false; - } - - if (!$this->type_param->equals($other_type->type_param, $ensure_source_equality)) { - return false; - } - - return true; - } - - public function getAssertionString(): string - { - if ($this->type_param->isMixed()) { - return 'list'; - } - - return $this->getId(); - } - - protected function getChildNodeKeys(): array - { - return ['type_param']; - } -} diff --git a/src/Psalm/Type/Atomic/TNonEmptyList.php b/src/Psalm/Type/Atomic/TNonEmptyList.php deleted file mode 100644 index 47c628ccd7e..00000000000 --- a/src/Psalm/Type/Atomic/TNonEmptyList.php +++ /dev/null @@ -1,94 +0,0 @@ -count = $count; - $this->min_count = $min_count; - /** @psalm-suppress DeprecatedClass */ - parent::__construct($type_param, $from_docblock); - } - - public function getKeyedArray(): TKeyedArray - { - if (!$this->count && !$this->min_count) { - return Type::getNonEmptyListAtomic($this->type_param); - } - if ($this->count) { - return new TKeyedArray( - array_fill(0, $this->count, $this->type_param), - null, - null, - true, - $this->from_docblock, - ); - } - return new TKeyedArray( - array_fill(0, $this->min_count, $this->type_param), - null, - [Type::getListKey(), $this->type_param], - true, - $this->from_docblock, - ); - } - - - /** - * @param positive-int|null $count - * @return static - */ - public function setCount(?int $count): self - { - if ($count === $this->count) { - return $this; - } - $cloned = clone $this; - $cloned->count = $count; - return $cloned; - } - - public function getAssertionString(): string - { - return 'non-empty-list'; - } -} From 35a11972d4abb3793c705e65a80647c27fe282d1 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 10:03:51 +0200 Subject: [PATCH 08/31] Remove TList leftovers --- src/Psalm/Internal/Algebra.php | 3 +-- .../Statements/Expression/AssertionFinder.php | 4 --- .../Assignment/ArrayAssignmentAnalyzer.php | 12 ++------- .../BinaryOp/ArithmeticOpAnalyzer.php | 9 ------- .../Expression/Call/ArgumentAnalyzer.php | 9 ------- .../Expression/Call/ArgumentsAnalyzer.php | 5 ---- .../Call/ArrayFunctionArgumentsAnalyzer.php | 5 ---- .../Expression/Call/FunctionCallAnalyzer.php | 5 +--- .../Call/FunctionCallReturnTypeFetcher.php | 4 --- .../Statements/Expression/CastAnalyzer.php | 1 - .../Expression/Fetch/ArrayFetchAnalyzer.php | 5 ---- .../Expression/SimpleTypeInferer.php | 1 - .../Analyzer/Statements/UnsetAnalyzer.php | 3 --- src/Psalm/Internal/Cli/Psalm.php | 25 ------------------- .../Reflector/FunctionLikeDocblockScanner.php | 5 ---- .../ArrayMapReturnTypeProvider.php | 5 +--- .../ArrayMergeReturnTypeProvider.php | 4 --- ...rayPointerAdjustmentReturnTypeProvider.php | 5 +--- .../ArrayReduceReturnTypeProvider.php | 6 +---- .../ArraySliceReturnTypeProvider.php | 5 +--- .../Internal/Type/AssertionReconciler.php | 1 - .../Type/Comparator/AtomicTypeComparator.php | 17 +++---------- .../Comparator/CallableTypeComparator.php | 9 ++----- .../Type/NegatedAssertionReconciler.php | 4 +-- .../Type/SimpleAssertionReconciler.php | 24 ++---------------- .../Type/SimpleNegatedAssertionReconciler.php | 4 --- .../Type/TemplateStandinTypeReplacer.php | 15 +---------- src/Psalm/Internal/Type/TypeCombiner.php | 5 +--- src/Psalm/Internal/Type/TypeExpander.php | 5 +--- src/Psalm/Plugin/Shepherd.php | 2 -- src/Psalm/Type/Atomic.php | 7 +----- src/Psalm/Type/Atomic/GenericTrait.php | 5 +--- src/Psalm/Type/Atomic/TClassStringMap.php | 1 - src/Psalm/Type/Atomic/TKeyOf.php | 6 ----- src/Psalm/Type/Atomic/TValueOf.php | 4 --- src/Psalm/Type/Reconciler.php | 8 +----- src/Psalm/Type/UnionTrait.php | 4 --- 37 files changed, 23 insertions(+), 219 deletions(-) diff --git a/src/Psalm/Internal/Algebra.php b/src/Psalm/Internal/Algebra.php index 043e8b1e13d..2fb83de991d 100644 --- a/src/Psalm/Internal/Algebra.php +++ b/src/Psalm/Internal/Algebra.php @@ -432,8 +432,7 @@ public static function getTruthsFromFormula( continue; } - if ($assertion->type instanceof TList - || $assertion->type instanceof TArray + if ($assertion->type instanceof TArray || $assertion->type instanceof TKeyedArray) { unset($truths[$var][$key][$index]); } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index 52ea3e529c8..0c37cd0a7ff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -3737,10 +3737,6 @@ private static function getArrayKeyExistsAssertions( && ($second_var_type = $source->node_data->getType($expr->getArgs()[1]->value)) ) { foreach ($second_var_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } - if ($atomic_type instanceof TArray || $atomic_type instanceof TKeyedArray ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index b692d169aa2..a8c50bb3d90 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -22,10 +22,8 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TClassStringMap; -use Psalm\Type\Atomic\TDependentListKey; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -476,8 +474,6 @@ private static function updateArrayAssignmentChildType( if (($key_type_type instanceof TIntRange && $key_type_type->dependent_list_key === $parent_var_id - ) || ($key_type_type instanceof TDependentListKey - && $key_type_type->var_id === $parent_var_id )) { $offset_already_existed = true; } @@ -581,9 +577,7 @@ private static function updateArrayAssignmentChildType( if (isset($atomic_root_types['array'])) { $atomic_root_type_array = $atomic_root_types['array']; - if ($atomic_root_type_array instanceof TList) { - $atomic_root_type_array = $atomic_root_type_array->getKeyedArray(); - } + if ($array_atomic_type_class_string) { $array_atomic_type = new TNonEmptyArray([ @@ -705,9 +699,7 @@ private static function updateArrayAssignmentChildType( if (isset($atomic_root_types['array'])) { $atomic_root_type_array = $atomic_root_types['array']; - if ($atomic_root_type_array instanceof TList) { - $atomic_root_type_array = $atomic_root_type_array->getKeyedArray(); - } + if ($atomic_root_type_array instanceof TNonEmptyArray && $atomic_root_type_array->count !== null diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index 36ae21d63c8..a12dcd9d2b5 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -31,7 +31,6 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -510,15 +509,7 @@ private static function analyzeOperands( || $right_type_part instanceof TArray || $left_type_part instanceof TKeyedArray || $right_type_part instanceof TKeyedArray - || $left_type_part instanceof TList - || $right_type_part instanceof TList ) { - if ($left_type_part instanceof TList) { - $left_type_part = $left_type_part->getKeyedArray(); - } - if ($right_type_part instanceof TList) { - $right_type_part = $right_type_part->getKeyedArray(); - } if ((!$right_type_part instanceof TArray && !$right_type_part instanceof TKeyedArray) || (!$left_type_part instanceof TArray diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 43da070b4d6..ace1db1880c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -55,7 +55,6 @@ use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; @@ -331,10 +330,6 @@ private static function checkFunctionLikeTypeMatches( $arg_type_param = null; foreach ($arg_value_type->getAtomicTypes() as $arg_atomic_type) { - if ($arg_atomic_type instanceof TList) { - $arg_atomic_type = $arg_atomic_type->getKeyedArray(); - } - if ($arg_atomic_type instanceof TArray || $arg_atomic_type instanceof TKeyedArray ) { @@ -903,10 +898,6 @@ public static function verifyType( $potential_method_ids = []; foreach ($input_type->getAtomicTypes() as $input_type_part) { - if ($input_type_part instanceof TList) { - $input_type_part = $input_type_part->getKeyedArray(); - } - if ($input_type_part instanceof TKeyedArray) { $potential_method_id = CallableTypeComparator::getCallableMethodIdFromTKeyedArray( $input_type_part, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index 653ffedc9ac..cbaab782dd3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -43,7 +43,6 @@ use Psalm\Type\Atomic\TCallableKeyedArray; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TTemplateParam; @@ -1528,10 +1527,6 @@ private static function checkArgCount( } foreach ($arg_value_type->getAtomicTypes() as $atomic_arg_type) { - if ($atomic_arg_type instanceof TList) { - $atomic_arg_type = $atomic_arg_type->getKeyedArray(); - } - $packed_var_definite_args_tmp = []; if ($atomic_arg_type instanceof TCallableArray || $atomic_arg_type instanceof TCallableKeyedArray diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index c1aa8540e1a..7f09b6a9a8c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -36,7 +36,6 @@ use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Union; use UnexpectedValueException; @@ -635,10 +634,6 @@ public static function handleByRefArrayAdjustment( $array_atomic_types = []; foreach ($context->vars_in_scope[$var_id]->getAtomicTypes() as $array_atomic_type) { - if ($array_atomic_type instanceof TList) { - $array_atomic_type = $array_atomic_type->getKeyedArray(); - } - if ($array_atomic_type instanceof TKeyedArray) { if ($is_array_shift && $array_atomic_type->is_list && !$context->inside_loop diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 21bdf3f9ea7..05d8b096ee8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -48,7 +48,6 @@ use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; @@ -667,9 +666,7 @@ private static function getAnalyzeNamedExpression( continue; } - if ($var_type_part instanceof TList) { - $var_type_part = $var_type_part->getKeyedArray(); - } + if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) { if (!$var_type_part->is_pure) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index 34dae01f345..943567b6f8d 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -33,7 +33,6 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyArray; @@ -359,9 +358,6 @@ private static function getReturnTypeFromCallMapWithArgs( if (count($atomic_types) === 1) { if (isset($atomic_types['array'])) { - if ($atomic_types['array'] instanceof TList) { - $atomic_types['array'] = $atomic_types['array']->getKeyedArray(); - } if ($atomic_types['array'] instanceof TCallableArray || $atomic_types['array'] instanceof TCallableKeyedArray ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php index 5ba762d5758..32b6276b1d2 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php @@ -29,7 +29,6 @@ use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 2567ca86ac3..1ec72b4a848 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -61,7 +61,6 @@ use Psalm\Type\Atomic\TFloat; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; @@ -561,10 +560,6 @@ public static function getArrayAccessTypeGivenOffset( $types = $array_type->getAtomicTypes(); $changed = false; foreach ($types as $type_string => $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } - $original_type_real = $type; $original_type = $type; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index 6456623b06a..68129233b37 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -19,7 +19,6 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; diff --git a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php index 22a54345855..47180961335 100644 --- a/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php @@ -63,9 +63,6 @@ public static function analyze( $root_types = []; foreach ($context->vars_in_scope[$root_var_id]->getAtomicTypes() as $atomic_root_type) { - if ($atomic_root_type instanceof TList) { - $atomic_root_type = $atomic_root_type->getKeyedArray(); - } if ($atomic_root_type instanceof TKeyedArray) { $key_value = null; if ($key_type->isSingleIntLiteral()) { diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 4df6a9dd121..faa390caae7 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -1174,16 +1174,6 @@ private static function configureProjectAnalyzer( private static function configureShepherd(Config $config, array $options, array &$plugins): void { - if (is_string(getenv('PSALM_SHEPHERD_HOST'))) { // remove this block in Psalm 6 - fwrite( - STDERR, - 'Warning: PSALM_SHEPHERD_HOST env variable will be removed in Psalm 6.' - .' Please use "--shepherd" cli option or PSALM_SHEPHERD env variable' - .' to specify a custom Shepherd host/endpoint.' - . PHP_EOL, - ); - } - $is_shepherd_enabled = isset($options['shepherd']) || getenv('PSALM_SHEPHERD'); if (! $is_shepherd_enabled) { return; @@ -1198,25 +1188,10 @@ private static function configureShepherd(Config $config, array $options, array $custom_shepherd_endpoint = 'https://' . $custom_shepherd_endpoint; } - /** @psalm-suppress DeprecatedProperty */ - $config->shepherd_host = str_replace('/hooks/psalm', '', $custom_shepherd_endpoint); $config->shepherd_endpoint = $custom_shepherd_endpoint; return; } - - // Legacy part, will be removed in Psalm 6 - $custom_shepherd_host = getenv('PSALM_SHEPHERD_HOST'); - - if (is_string($custom_shepherd_host)) { - if (parse_url($custom_shepherd_host, PHP_URL_SCHEME) === null) { - $custom_shepherd_host = 'https://' . $custom_shepherd_host; - } - - /** @psalm-suppress DeprecatedProperty */ - $config->shepherd_host = $custom_shepherd_host; - $config->shepherd_endpoint = $custom_shepherd_host . '/hooks/psalm'; - } } private static function generateStubs( diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index 9f9a477cb4d..f0791fe010c 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -913,11 +913,6 @@ private static function improveParamsFromDocblock( static fn(FunctionLikeParameter $p): bool => !$p->has_docblock_type && (!$p->type || $p->type->hasArray()) ); - if ($params_without_docblock_type) { - /** @psalm-suppress DeprecatedProperty remove in Psalm 6 */ - $storage->unused_docblock_params = $unused_docblock_params; - } - $storage->has_undertyped_native_parameters = $params_without_docblock_type !== []; $storage->unused_docblock_parameters = $unused_docblock_params; } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php index 92397ebb69f..6d69d05e666 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php @@ -25,7 +25,6 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TTemplateParam; @@ -144,9 +143,7 @@ function (array $sub) use ($null) { if (isset($arg_types['array'])) { $array_arg_atomic_type = $arg_types['array']; - if ($array_arg_atomic_type instanceof TList) { - $array_arg_atomic_type = $array_arg_atomic_type->getKeyedArray(); - } + $array_arg_type = ArrayType::infer($array_arg_atomic_type); } } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 394988baee1..c8f11e9e0b2 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -11,7 +11,6 @@ use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TNull; @@ -69,9 +68,6 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } foreach ($call_arg_type->getAtomicTypes() as $type_part) { - if ($type_part instanceof TList) { - $type_part = $type_part->getKeyedArray(); - } $unpacking_indefinite_number_of_args = false; $unpacking_possibly_empty = false; if ($call_arg->unpack) { diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index a9421acfa4c..7d75ef7b3ff 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -10,7 +10,6 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; @@ -74,9 +73,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev continue; } - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } + if ($atomic_type instanceof TArray) { $value_type = $atomic_type->type_params[1]; diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php index 9b4ee1dd53d..930896f8123 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php @@ -16,7 +16,6 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TNull; use Psalm\Type\Union; @@ -72,14 +71,11 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev if (isset($array_arg_types['array']) && ($array_arg_types['array'] instanceof TArray - || $array_arg_types['array'] instanceof TList || $array_arg_types['array'] instanceof TKeyedArray) ) { $array_arg_atomic_type = $array_arg_types['array']; - if ($array_arg_atomic_type instanceof TList) { - $array_arg_atomic_type = $array_arg_atomic_type->getKeyedArray(); - } + if ($array_arg_atomic_type instanceof TKeyedArray) { $array_arg_atomic_type = $array_arg_atomic_type->getGenericArrayType(); diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php index 64d988b8498..393d36e34c1 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php @@ -8,7 +8,6 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; use UnexpectedValueException; @@ -60,9 +59,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev continue; } - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } + if ($atomic_type instanceof TKeyedArray) { $atomic_type = $atomic_type->getGenericArrayType(); diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 85410c4c09c..6a971c9031f 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -619,7 +619,6 @@ private static function filterAtomicWithAnother( } /*if ($type_2_atomic instanceof TKeyedArray - && $type_1_atomic instanceof \Psalm\Type\Atomic\TList ) { $type_2_key = $type_2_atomic->getGenericKeyType(); $type_2_value = $type_2_atomic->getGenericValueType(); diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index e01ee2d2b8b..6466d91994e 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -22,7 +22,6 @@ use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyOf; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; @@ -65,12 +64,8 @@ public static function isContainedBy( bool $allow_float_int_equality = true, ?TypeComparisonResult $atomic_comparison_result = null ): bool { - if ($input_type_part instanceof TList) { - $input_type_part = $input_type_part->getKeyedArray(); - } - if ($container_type_part instanceof TList) { - $container_type_part = $container_type_part->getKeyedArray(); - } + + if (($container_type_part instanceof TTemplateParam || ($container_type_part instanceof TNamedObject && $container_type_part->extra_types)) @@ -844,12 +839,8 @@ public static function canBeIdentical( Atomic $type2_part, bool $allow_interface_equality = true ): bool { - if ($type1_part instanceof TList) { - $type1_part = $type1_part->getKeyedArray(); - } - if ($type2_part instanceof TList) { - $type2_part = $type2_part->getKeyedArray(); - } + + if ((self::isLegacyTListLike($type1_part) && self::isLegacyTNonEmptyListLike($type2_part)) || (self::isLegacyTListLike($type2_part) diff --git a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php index fa3b5328974..0cfef8a5229 100644 --- a/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php @@ -22,7 +22,6 @@ use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TTemplateParam; @@ -142,9 +141,7 @@ public static function isNotExplicitlyCallableTypeCallable( TCallable $container_type_part, ?TypeComparisonResult $atomic_comparison_result ): bool { - if ($input_type_part instanceof TList) { - $input_type_part = $input_type_part->getKeyedArray(); - } + if ($input_type_part instanceof TArray) { if ($input_type_part->type_params[1]->isMixed() || $input_type_part->type_params[1]->hasScalar() @@ -222,9 +219,7 @@ public static function getCallableFromAtomic( ?StatementsAnalyzer $statements_analyzer = null, bool $expand_callable = false ): ?Atomic { - if ($input_type_part instanceof TList) { - $input_type_part = $input_type_part->getKeyedArray(); - } + if ($input_type_part instanceof TCallable || $input_type_part instanceof TClosure) { return $input_type_part; } diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index f4de672c99e..5909279ed1a 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -21,7 +21,6 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -204,8 +203,7 @@ public static function reconcile( // fall through } elseif ($existing_var_type->isArray() && ($assertion->getAtomicType() instanceof TArray - || $assertion->getAtomicType() instanceof TKeyedArray - || $assertion->getAtomicType() instanceof TList) + || $assertion->getAtomicType() instanceof TKeyedArray) ) { //if both types are arrays, try to combine them $combined_type = TypeCombiner::combine( diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 0602fff82f8..b547e8e9bf7 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -48,7 +48,6 @@ use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TLowercaseString; @@ -366,9 +365,7 @@ public static function reconcile( ); } - if ($assertion_type instanceof TList) { - $assertion_type = $assertion_type->getKeyedArray(); - } + if ($assertion_type instanceof TKeyedArray && $assertion_type->is_list @@ -1945,9 +1942,6 @@ private static function reconcileHasArrayKey( $assertion = $assertion->key; $types = $existing_var_type->getAtomicTypes(); foreach ($types as &$atomic_type) { - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } if ($atomic_type instanceof TKeyedArray) { assert(strpos($assertion, '::class') === (strlen($assertion)-7)); [$assertion] = explode('::', $assertion); @@ -2287,9 +2281,6 @@ private static function reconcileArray( $redundant = true; foreach ($existing_var_atomic_types as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type instanceof TArray) { if ($atomic_assertion_type instanceof TNonEmptyArray) { $array_types[] = new TNonEmptyArray( @@ -2398,9 +2389,6 @@ private static function reconcileList( $redundant = true; foreach ($existing_var_atomic_types as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type instanceof TKeyedArray && $type->is_list) { if ($is_non_empty && !$type->isNonEmpty()) { $properties = $type->properties; @@ -2510,9 +2498,6 @@ private static function reconcileStringArrayAccess( $array_types = []; foreach ($existing_var_atomic_types as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type->isArrayAccessibleWithStringKey($codebase)) { if (get_class($type) === TArray::class) { $array_types[] = new TNonEmptyArray($type->type_params); @@ -2640,9 +2625,6 @@ private static function reconcileCallable( $redundant = true; foreach ($existing_var_atomic_types as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type->isCallableType()) { $callable_types[] = $type; } elseif ($type instanceof TObject) { @@ -2808,9 +2790,7 @@ private static function reconcileTruthyOrNonEmpty( if (isset($types['array'])) { $array_atomic_type = $types['array']; - if ($array_atomic_type instanceof TList) { - $array_atomic_type = $array_atomic_type->getKeyedArray(); - } + if ($array_atomic_type instanceof TArray && !$array_atomic_type instanceof TNonEmptyArray diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 160bdc7482f..26955bf6d9a 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -39,7 +39,6 @@ use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -1665,9 +1664,6 @@ private static function reconcileArray( $redundant = !$existing_var_type->hasScalar(); foreach ($existing_var_type->getAtomicTypes() as $type) { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } if ($type instanceof TTemplateParam) { if (!$is_equality && !$type->as->isMixed()) { $template_did_fail = 0; diff --git a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index 9fa51c420cd..7631f3f4413 100644 --- a/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -19,7 +19,6 @@ use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -293,9 +292,7 @@ private static function handleAtomicStandin( $array_template_type = $array_template_type->getSingleAtomic(); $offset_template_type = $offset_template_type->getSingleAtomic(); - if ($array_template_type instanceof TList) { - $array_template_type = $array_template_type->getKeyedArray(); - } + if ($array_template_type instanceof TKeyedArray && ($offset_template_type instanceof TLiteralString || $offset_template_type instanceof TLiteralInt) @@ -345,9 +342,6 @@ private static function handleAtomicStandin( if ($template_type) { foreach ($template_type->getAtomicTypes() as $template_atomic) { - if ($template_atomic instanceof TList) { - $template_atomic = $template_atomic->getKeyedArray(); - } if (!$template_atomic instanceof TKeyedArray && !$template_atomic instanceof TArray ) { @@ -475,10 +469,6 @@ private static function findMatchingAtomicTypesForTemplate( $matching_atomic_types = []; foreach ($input_type->getAtomicTypes() as $input_key => $atomic_input_type) { - if ($atomic_input_type instanceof TList) { - $atomic_input_type = $atomic_input_type->getKeyedArray(); - } - if ($bracket_pos = strpos($input_key, '<')) { $input_key = substr($input_key, 0, $bracket_pos); } @@ -760,9 +750,6 @@ private static function handleTemplateParamStandin( if ($keyed_template->isSingle()) { $keyed_template = $keyed_template->getSingleAtomic(); } - if ($keyed_template instanceof \Psalm\Type\Atomic\TList) { - $keyed_template = $keyed_template->getKeyedArray(); - } if ($keyed_template instanceof TKeyedArray || $keyed_template instanceof TArray diff --git a/src/Psalm/Internal/Type/TypeCombiner.php b/src/Psalm/Internal/Type/TypeCombiner.php index d091c239961..90028e8f3b9 100644 --- a/src/Psalm/Internal/Type/TypeCombiner.php +++ b/src/Psalm/Internal/Type/TypeCombiner.php @@ -26,7 +26,6 @@ use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; @@ -401,9 +400,7 @@ private static function scrapeTypeProperties( bool $allow_mixed_union, int $literal_limit ): ?Union { - if ($type instanceof TList) { - $type = $type->getKeyedArray(); - } + if ($type instanceof TMixed) { if ($type->from_loop_isset) { if ($combination->mixed_from_loop_isset === null) { diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index e329f92b632..56f558038d5 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -23,7 +23,6 @@ use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyOf; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TNamedObject; @@ -450,9 +449,7 @@ public static function expandAtomic( $throw_on_unresolvable_constant, ); } - if ($return_type instanceof TList) { - $return_type = $return_type->getKeyedArray(); - } + if ($return_type instanceof TArray || $return_type instanceof TGenericObject diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index 37a61c29897..d6b0f0dd5b1 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -21,7 +21,6 @@ use function function_exists; use function fwrite; use function is_array; -use function is_int; use function is_string; use function json_encode; use function parse_url; @@ -40,7 +39,6 @@ use const JSON_THROW_ON_ERROR; use const PHP_EOL; use const PHP_URL_HOST; -use const PHP_URL_SCHEME; use const STDERR; final class Shepherd implements AfterAnalysisInterface diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 5eacc03f575..f815527a179 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -18,7 +18,6 @@ use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TCallableArray; use Psalm\Type\Atomic\TCallableKeyedArray; -use Psalm\Type\Atomic\TCallableList; use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TCallableString; use Psalm\Type\Atomic\TClassString; @@ -36,7 +35,6 @@ use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralClassString; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; @@ -462,7 +460,6 @@ public function isCallableType(): bool || $this instanceof TCallableObject || $this instanceof TCallableString || $this instanceof TCallableArray - || $this instanceof TCallableList || $this instanceof TCallableKeyedArray || $this instanceof TClosure; } @@ -472,7 +469,6 @@ public function isIterable(Codebase $codebase): bool return $this instanceof TIterable || $this->hasTraversableInterface($codebase) || $this instanceof TArray - || $this instanceof TList || $this instanceof TKeyedArray; } @@ -518,8 +514,7 @@ public function isCountable(Codebase $codebase): bool { return $this->hasCountableInterface($codebase) || $this instanceof TArray - || $this instanceof TKeyedArray - || $this instanceof TList; + || $this instanceof TKeyedArray; } /** diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index aa3e5d9e048..b1f3795d8b1 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -9,7 +9,6 @@ use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Type; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TList; use Psalm\Type\Union; use function array_map; @@ -171,9 +170,7 @@ protected function replaceTypeParamsTemplateTypesWithStandins( bool $add_lower_bound = false, int $depth = 0 ): ?array { - if ($input_type instanceof TList) { - $input_type = $input_type->getKeyedArray(); - } + $input_object_type_params = []; diff --git a/src/Psalm/Type/Atomic/TClassStringMap.php b/src/Psalm/Type/Atomic/TClassStringMap.php index 915e794a116..b95e8b6c789 100644 --- a/src/Psalm/Type/Atomic/TClassStringMap.php +++ b/src/Psalm/Type/Atomic/TClassStringMap.php @@ -9,7 +9,6 @@ use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Type; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TList; use Psalm\Type\Union; use function get_class; diff --git a/src/Psalm/Type/Atomic/TKeyOf.php b/src/Psalm/Type/Atomic/TKeyOf.php index 09762e732f9..6928fc8659d 100644 --- a/src/Psalm/Type/Atomic/TKeyOf.php +++ b/src/Psalm/Type/Atomic/TKeyOf.php @@ -2,7 +2,6 @@ namespace Psalm\Type\Atomic; -use Psalm\Type\Atomic\TList; use Psalm\Type\Union; use function array_merge; @@ -57,7 +56,6 @@ public static function isViableTemplateType(Union $template_type): bool if (!$type instanceof TArray && !$type instanceof TClassConstant && !$type instanceof TKeyedArray - && !$type instanceof TList && !$type instanceof TPropertiesOf ) { return false; @@ -73,10 +71,6 @@ public static function getArrayKeyType( $key_types = []; foreach ($type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof TList) { - $atomic_type = $atomic_type->getKeyedArray(); - } - if ($atomic_type instanceof TArray) { $array_key_atomics = $atomic_type->type_params[0]; } elseif ($atomic_type instanceof TKeyedArray) { diff --git a/src/Psalm/Type/Atomic/TValueOf.php b/src/Psalm/Type/Atomic/TValueOf.php index 9220c079dee..5acf7f45ef3 100644 --- a/src/Psalm/Type/Atomic/TValueOf.php +++ b/src/Psalm/Type/Atomic/TValueOf.php @@ -6,7 +6,6 @@ use Psalm\Internal\Codebase\ConstantTypeResolver; use Psalm\Storage\EnumCaseStorage; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TList; use Psalm\Type\Union; use function array_map; @@ -89,7 +88,6 @@ public static function isViableTemplateType(Union $template_type): bool if (!$type instanceof TArray && !$type instanceof TClassConstant && !$type instanceof TKeyedArray - && !$type instanceof TList && !$type instanceof TPropertiesOf && !$type instanceof TNamedObject ) { @@ -109,8 +107,6 @@ public static function getValueType( foreach ($type->getAtomicTypes() as $atomic_type) { if ($atomic_type instanceof TArray) { $value_atomics = $atomic_type->type_params[1]; - } elseif ($atomic_type instanceof TList) { - $value_atomics = $atomic_type->type_param; } elseif ($atomic_type instanceof TKeyedArray) { $value_atomics = $atomic_type->getGenericValueType(); } elseif ($atomic_type instanceof TTemplateParam) { diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 074ef233ab3..258bace61eb 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -44,7 +44,6 @@ use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNever; @@ -698,9 +697,7 @@ private static function getValueForKey( while ($atomic_types) { $existing_key_type_part = array_shift($atomic_types); - if ($existing_key_type_part instanceof TList) { - $existing_key_type_part = $existing_key_type_part->getKeyedArray(); - } + if ($existing_key_type_part instanceof TTemplateParam) { $atomic_types = array_merge($atomic_types, $existing_key_type_part->as->getAtomicTypes()); @@ -1111,9 +1108,6 @@ private static function adjustTKeyedArrayType( if (isset($existing_types[$base_key]) && $array_key_offset !== false) { foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) { - if ($base_atomic_type instanceof TList) { - $base_atomic_type = $base_atomic_type->getKeyedArray(); - } if ($base_atomic_type instanceof TKeyedArray || ($base_atomic_type instanceof TArray && !$base_atomic_type->isEmptyArray()) diff --git a/src/Psalm/Type/UnionTrait.php b/src/Psalm/Type/UnionTrait.php index 3361c2ba873..ccc035f7826 100644 --- a/src/Psalm/Type/UnionTrait.php +++ b/src/Psalm/Type/UnionTrait.php @@ -25,7 +25,6 @@ use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntRange; use Psalm\Type\Atomic\TKeyedArray; -use Psalm\Type\Atomic\TList; use Psalm\Type\Atomic\TLiteralFloat; use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; @@ -407,9 +406,6 @@ public function hasArray(): bool */ public function getArray(): Atomic { - if ($this->types['array'] instanceof TList) { - return $this->types['array']->getKeyedArray(); - } return $this->types['array']; } From f799b68a3cbc91e9056bb972cfe66b7ae0da3f76 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 10:06:46 +0200 Subject: [PATCH 09/31] Remove leftovers --- src/Psalm/Config.php | 39 ++--------------------- src/Psalm/ErrorBaseline.php | 6 ---- src/Psalm/Plugin/Shepherd.php | 16 +--------- src/Psalm/Storage/FunctionLikeStorage.php | 5 +++ src/Psalm/Storage/FunctionStorage.php | 6 ---- 5 files changed, 8 insertions(+), 64 deletions(-) diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 2633a8986bb..61f83ed6115 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -451,11 +451,9 @@ class Config public $forbidden_functions = []; /** - * TODO: Psalm 6: Update default to be true and remove warning. - * * @var bool */ - public $find_unused_code = false; + public $find_unused_code = true; /** * @var bool @@ -467,10 +465,7 @@ class Config */ public $find_unused_psalm_suppress = false; - /** - * TODO: Psalm 6: Update default to be true and remove warning. - */ - public bool $find_unused_baseline_entry = false; + public bool $find_unused_baseline_entry = true; /** * @var bool @@ -994,9 +989,6 @@ private static function processConfigDeprecations( ): void { $config->config_issues = []; - // Attributes to be removed in Psalm 6 - $deprecated_attributes = []; - /** @var list */ $deprecated_elements = []; @@ -1004,12 +996,6 @@ private static function processConfigDeprecations( assert($psalm_element_item !== null); $attributes = $psalm_element_item->attributes; - foreach ($attributes as $attribute) { - if (in_array($attribute->name, $deprecated_attributes, true)) { - self::processDeprecatedAttribute($attribute, $file_contents, $config, $config_path); - } - } - foreach ($deprecated_elements as $deprecated_element) { $deprecated_elements_xml = $dom_document->getElementsByTagNameNS( self::CONFIG_NAMESPACE, @@ -1221,18 +1207,10 @@ private static function fromXmlAndPaths( $config->compressor = 'deflate'; } - if (!isset($config_xml['findUnusedBaselineEntry'])) { - $config->config_warnings[] = '"findUnusedBaselineEntry" will default to "true" in Psalm 6.' - . ' You should explicitly enable or disable this setting.'; - } - if (isset($config_xml['findUnusedCode'])) { $attribute_text = (string) $config_xml['findUnusedCode']; $config->find_unused_code = $attribute_text === 'true' || $attribute_text === '1'; $config->find_unused_variables = $config->find_unused_code; - } else { - $config->config_warnings[] = '"findUnusedCode" will default to "true" in Psalm 6.' - . ' You should explicitly enable or disable this setting.'; } if (isset($config_xml['findUnusedVariablesAndParams'])) { @@ -2278,19 +2256,6 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null): } } - /** @deprecated Will be removed in Psalm 6 */ - $extensions_to_load_stubs_using_deprecated_way = ['apcu', 'random', 'redis']; - foreach ($extensions_to_load_stubs_using_deprecated_way as $ext_name) { - $ext_stub_path = $ext_stubs_dir . DIRECTORY_SEPARATOR . "$ext_name.phpstub"; - $is_stub_already_loaded = in_array($ext_stub_path, $this->internal_stubs, true); - $is_ext_explicitly_disabled = ($this->php_extensions[$ext_name] ?? null) === false; - if (! $is_stub_already_loaded && ! $is_ext_explicitly_disabled && extension_loaded($ext_name)) { - $this->internal_stubs[] = $ext_stub_path; - $this->config_warnings[] = "Psalm 6 will not automatically load stubs for ext-$ext_name." - . " You should explicitly enable or disable this ext in composer.json or Psalm config."; - } - } - foreach ($this->internal_stubs as $stub_path) { if (!file_exists($stub_path)) { throw new UnexpectedValueException('Cannot locate ' . $stub_path); diff --git a/src/Psalm/ErrorBaseline.php b/src/Psalm/ErrorBaseline.php index 9a83b0a2899..9615795d348 100644 --- a/src/Psalm/ErrorBaseline.php +++ b/src/Psalm/ErrorBaseline.php @@ -120,12 +120,6 @@ public static function read(FileProvider $fileProvider, string $baselineFile): a $files[$fileName][$issueType]['o'] += 1; $files[$fileName][$issueType]['s'][] = str_replace("\r\n", "\n", trim($codeSample->textContent)); } - - // TODO: Remove in Psalm 6 - $occurrencesAttr = $issue->getAttribute('occurrences'); - if ($occurrencesAttr !== '') { - $files[$fileName][$issueType]['o'] = (int) $occurrencesAttr; - } } } diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index d6b0f0dd5b1..5a8a30af297 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -63,21 +63,7 @@ public static function afterAnalysis( $config = $event->getCodebase()->config; - /** - * Deprecated logic, in Psalm 6 just use $config->shepherd_endpoint - * '#' here is just a hack/marker to use a custom endpoint instead just a custom domain - * case 1: empty option (use https://shepherd.dev/hooks/psalm/) - * case 2: custom domain (/hooks/psalm should be appended) (use https://custom.domain/hooks/psalm) - * case 3: custom endpoint (/hooks/psalm should be appended) (use custom endpoint) - */ - if (substr_compare($config->shepherd_endpoint, '#', -1) === 0) { - $shepherd_endpoint = $config->shepherd_endpoint; - } else { - /** @psalm-suppress DeprecatedProperty, DeprecatedMethod */ - $shepherd_endpoint = self::buildShepherdUrlFromHost($config->shepherd_host); - } - - self::sendPayload($shepherd_endpoint, $rawPayload); + self::sendPayload($config->shepherd_endpoint, $rawPayload); } /** diff --git a/src/Psalm/Storage/FunctionLikeStorage.php b/src/Psalm/Storage/FunctionLikeStorage.php index d158b3d55fb..7da0a2cea04 100644 --- a/src/Psalm/Storage/FunctionLikeStorage.php +++ b/src/Psalm/Storage/FunctionLikeStorage.php @@ -179,6 +179,11 @@ abstract class FunctionLikeStorage implements HasAttributesInterface public bool $has_undertyped_native_parameters = false; + /** + * @var bool + */ + public $is_static = false; + /** * @var bool */ diff --git a/src/Psalm/Storage/FunctionStorage.php b/src/Psalm/Storage/FunctionStorage.php index 813730f567a..e39e7cc2356 100644 --- a/src/Psalm/Storage/FunctionStorage.php +++ b/src/Psalm/Storage/FunctionStorage.php @@ -6,10 +6,4 @@ final class FunctionStorage extends FunctionLikeStorage { /** @var array */ public $byref_uses = []; - - /** - * @var bool - * @todo lift this property to FunctionLikeStorage in Psalm 6 - */ - public $is_static = false; } From dd15e4768cdc993505a35b4d629269759f3a8c9e Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 10:14:52 +0200 Subject: [PATCH 10/31] Fixes --- psalm-baseline.xml | 48 +-------------------------- src/Psalm/Config.php | 11 +++++- src/Psalm/Type/Atomic/TKeyedArray.php | 14 -------- 3 files changed, 11 insertions(+), 62 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 69c51858103..3ee9afef0af 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + tags['variablesfrom'][0]]]> @@ -12,14 +12,6 @@ $matches[1] - - - $const_name - $const_name - $symbol_name - $symbol_parts[1] - - @@ -366,9 +358,6 @@ - - isContainedBy - properties[0]]]> properties[0]]]> @@ -489,11 +478,6 @@ getMostSpecificTypeFromBounds - - - TNonEmptyList - - replace @@ -515,17 +499,7 @@ replace - - - __construct - - - - TList - getGenericValueType())]]> - getGenericValueType())]]> - combine combine @@ -548,26 +522,6 @@ properties[0]]]> properties[0]]]> - - getList - - - - - replace - replace - - - type_param]]> - - - - - TList - - - setCount - diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 61f83ed6115..fb9cef18822 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -550,7 +550,7 @@ class Config * @var string * @internal */ - public $shepherd_endpoint = 'https://shepherd.dev/hooks/psalm/'; + public $shepherd_endpoint = 'https://shepherd.dev/hooks/psalm'; /** * @var array @@ -989,6 +989,9 @@ private static function processConfigDeprecations( ): void { $config->config_issues = []; + // Attributes to be removed in Psalm 6 + $deprecated_attributes = []; + /** @var list */ $deprecated_elements = []; @@ -996,6 +999,12 @@ private static function processConfigDeprecations( assert($psalm_element_item !== null); $attributes = $psalm_element_item->attributes; + foreach ($attributes as $attribute) { + if (in_array($attribute->name, $deprecated_attributes, true)) { + self::processDeprecatedAttribute($attribute, $file_contents, $config, $config_path); + } + } + foreach ($deprecated_elements as $deprecated_element) { $deprecated_elements_xml = $dom_document->getElementsByTagNameNS( self::CONFIG_NAMESPACE, diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index dfddbe81334..c2f48a3d29c 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -685,20 +685,6 @@ public function getAssertionString(): string return $this->is_list ? 'list' : 'array'; } - /** - * @deprecated Will be removed in Psalm v6 along with the TList type. - */ - public function getList(): TList - { - if (!$this->is_list) { - throw new UnexpectedValueException('Object-like array must be a list for conversion'); - } - - return $this->isNonEmpty() - ? new TNonEmptyList($this->getGenericValueType()) - : new TList($this->getGenericValueType()); - } - /** * @param string|int $name * @return string|int From 544762f6cdfb853cd452bf10e95b6c4024912fff Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 10:22:01 +0200 Subject: [PATCH 11/31] Fix tests --- tests/ErrorBaselineTest.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/ErrorBaselineTest.php b/tests/ErrorBaselineTest.php index 3b4741f5883..6a92782974a 100644 --- a/tests/ErrorBaselineTest.php +++ b/tests/ErrorBaselineTest.php @@ -43,7 +43,6 @@ public function testLoadShouldParseXmlBaselineToPhpArray(): void foo bar - @@ -57,7 +56,6 @@ public function testLoadShouldParseXmlBaselineToPhpArray(): void $expectedParsedBaseline = [ 'sample/sample-file.php' => [ 'MixedAssignment' => ['o' => 2, 's' => ['foo', 'bar']], - 'InvalidReturnStatement' => ['o' => 1, 's' => []], ], 'sample/sample-file2.php' => [ 'PossiblyUnusedMethod' => ['o' => 2, 's' => ['foo', 'bar']], @@ -393,11 +391,18 @@ public function testUpdateShouldRemoveExistingIssuesWithoutAddingNewOnes(): void bar bat - + + Test + - - + + bar + baz + + + bar + @@ -531,7 +536,6 @@ public function testAddingACommentInBaselineDoesntTriggerNotice(): void foo bar - @@ -546,7 +550,6 @@ public function testAddingACommentInBaselineDoesntTriggerNotice(): void $expectedParsedBaseline = [ 'sample/sample-file.php' => [ 'MixedAssignment' => ['o' => 2, 's' => ['foo', 'bar']], - 'InvalidReturnStatement' => ['o' => 1, 's' => []], ], 'sample/sample-file2.php' => [ 'PossiblyUnusedMethod' => ['o' => 2, 's' => ['foo', 'bar']], From 4ec3184c94c71ba49a0fd3e6111d718fbdc7d5ce Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 10:22:47 +0200 Subject: [PATCH 12/31] cs-fix --- src/Psalm/Config.php | 1 - src/Psalm/Internal/Cli/Psalm.php | 1 - src/Psalm/Plugin/Shepherd.php | 1 - src/Psalm/Type/Atomic/TKeyedArray.php | 1 - 4 files changed, 4 deletions(-) diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index fb9cef18822..b39051b6747 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -62,7 +62,6 @@ use function count; use function dirname; use function explode; -use function extension_loaded; use function fclose; use function file_exists; use function file_get_contents; diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index faa390caae7..688d8178495 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -69,7 +69,6 @@ use function realpath; use function setlocale; use function str_repeat; -use function str_replace; use function strlen; use function strpos; use function substr; diff --git a/src/Psalm/Plugin/Shepherd.php b/src/Psalm/Plugin/Shepherd.php index 5a8a30af297..bec5ae6a58c 100644 --- a/src/Psalm/Plugin/Shepherd.php +++ b/src/Psalm/Plugin/Shepherd.php @@ -27,7 +27,6 @@ use function sprintf; use function strip_tags; use function strlen; -use function substr_compare; use function var_export; use const CURLINFO_HEADER_OUT; diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index c2f48a3d29c..776a71b6873 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -11,7 +11,6 @@ use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Union; -use UnexpectedValueException; use function addslashes; use function assert; From 90154af3c9f666aff52e9147573b061cf9ae7b81 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 12:39:34 +0200 Subject: [PATCH 13/31] Increase minimum version --- composer.json | 2 +- .../Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 3dd9d086774..60eaaea7eb5 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "~8.1.0 || ~8.2.0", + "php": "~8.1.17 || ~8.2.4", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-dom": "*", diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php index b90ffbc387d..fa05aef2178 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php @@ -179,9 +179,6 @@ public static function analyze( } if ($literal_concat) { - // Bypass opcache bug: https://github.com/php/php-src/issues/10635 - (function (int $_): void { - })($combinations); if (count($result_type_parts) === 0) { throw new AssertionError("The number of parts cannot be 0!"); } From c754a0ea08bb9713b637a1851d6f37f0f1fde25f Mon Sep 17 00:00:00 2001 From: cgocast Date: Tue, 25 Jul 2023 09:22:49 +0200 Subject: [PATCH 14/31] Remove TaintedSql sink for PDOStatement related methods --- stubs/extensions/pdo.phpstub | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/stubs/extensions/pdo.phpstub b/stubs/extensions/pdo.phpstub index aec4965477c..c156de1bee6 100644 --- a/stubs/extensions/pdo.phpstub +++ b/stubs/extensions/pdo.phpstub @@ -111,15 +111,11 @@ class PDO public function lastInsertId(?string $name = null) {} /** - * @psalm-taint-sink sql $query - * * @return PDOStatement|false */ public function prepare(string $query, array $options = []) {} /** - * @psalm-taint-sink sql $query - * * @return PDOStatement|false */ public function query(string $query, ?int $fetchMode = null) {} @@ -150,16 +146,6 @@ class PDOStatement implements Traversable * @return false|T */ public function fetchObject($class = \stdclass::class, array $ctorArgs = array()) {} - - /** - * @psalm-taint-sink sql $value - */ - public function bindValue(string|int $param, mixed $value, int $type = PDO::PARAM_STR): bool {} - - /** - * @psalm-taint-sink sql $var - */ - public function bindParam(string|int $param, mixed &$var, int $type = PDO::PARAM_STR, int $maxLength = 0, mixed $driverOptions = null): bool {} } class PDOException extends RuntimeException { From 165df42e009925327b47d3b578cdf791c61f4c6d Mon Sep 17 00:00:00 2001 From: cgocast Date: Tue, 25 Jul 2023 10:33:25 +0200 Subject: [PATCH 15/31] Apply code review remarks --- stubs/extensions/pdo.phpstub | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stubs/extensions/pdo.phpstub b/stubs/extensions/pdo.phpstub index c156de1bee6..4169ffbed03 100644 --- a/stubs/extensions/pdo.phpstub +++ b/stubs/extensions/pdo.phpstub @@ -111,11 +111,15 @@ class PDO public function lastInsertId(?string $name = null) {} /** + * @psalm-taint-sink sql $query + * * @return PDOStatement|false */ public function prepare(string $query, array $options = []) {} /** + * @psalm-taint-sink sql $query + * * @return PDOStatement|false */ public function query(string $query, ?int $fetchMode = null) {} @@ -146,6 +150,10 @@ class PDOStatement implements Traversable * @return false|T */ public function fetchObject($class = \stdclass::class, array $ctorArgs = array()) {} + + public function bindValue(string|int $param, mixed $value, int $type = PDO::PARAM_STR): bool {} + + public function bindParam(string|int $param, mixed &$var, int $type = PDO::PARAM_STR, int $maxLength = 0, mixed $driverOptions = null): bool {} } class PDOException extends RuntimeException { From 3aa9a1dc0166e03056e541f4990cb18f286793ab Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 26 Jul 2023 10:22:42 +0200 Subject: [PATCH 16/31] Update BC notes --- UPGRADING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index 01f1a67ddda..2fb773dc2df 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,6 +1,8 @@ # Upgrading from Psalm 5 to Psalm 6 ## Changed +- The minimum PHP version was raised to PHP 8.1.17. + - [BC] Switched the internal representation of `list` and `non-empty-list` from the TList and TNonEmptyList classes to an unsealed list shape: the TList, TNonEmptyList and TCallableList classes were removed. Nothing will change for users: the `list` and `non-empty-list` syntax will remain supported and its semantics unchanged. Psalm 5 already deprecates the `TList`, `TNonEmptyList` and `TCallableList` classes: use `\Psalm\Type::getListAtomic`, `\Psalm\Type::getNonEmptyListAtomic` and `\Psalm\Type::getCallableListAtomic` to instantiate list atomics, or directly instantiate TKeyedArray objects with `is_list=true` where appropriate. @@ -9,6 +11,8 @@ - [BC] The `TDependentListKey` type was removed and replaced with an optional property of the `TIntRange` type. +- [BC] The return type of `Psalm\Internal\LanguageServer\ProtocolWriter#write() changed from `Amp\Promise` to `void` due to the switch to Amp v3 + # Upgrading from Psalm 4 to Psalm 5 ## Changed From aa1f2c730d8b833b32326c442203369c3b23c8e0 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 26 Jul 2023 10:56:13 +0200 Subject: [PATCH 17/31] Update BCC Checks --- UPGRADING.md | 16 ++++++++++++++++ src/Psalm/Type/Atomic/TKeyedArray.php | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 01f1a67ddda..33853b51f12 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -9,6 +9,22 @@ - [BC] The `TDependentListKey` type was removed and replaced with an optional property of the `TIntRange` type. +- [BC] Property `Config::$shepherd_host` was replaced with `Config::$shepherd_endpoint` + +- [BC] Methods `Codebase::getSymbolLocation()` and `Codebase::getSymbolInformation()` were replaced with `Codebase::getSymbolLocationByReference()` + +- [BC] Method `Psalm\Type\Atomic\TKeyedArray::getList()` was removed + +- [BC] Method `Psalm\Storage\FunctionLikeStorage::getSignature()` was replaced with `FunctionLikeStorage::getCompletionSignature()` + +- [BC] Property `Psalm\Storage\FunctionLikeStorage::$unused_docblock_params` was replaced with `FunctionLikeStorage::$unused_docblock_parameters` + +- [BC] Method `Plugin\Shepherd::getCurlErrorMessage()` was removed + +- [BC] Property `Config::$find_unused_code` changed default value from false to true + +- [BC] Property `Config::$find_unused_baseline_entry` changed default value from false to true + # Upgrading from Psalm 4 to Psalm 5 ## Changed diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index 776a71b6873..f3696e144bc 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -359,7 +359,7 @@ public function getGenericValueType(bool $possibly_undefined = false): Union /** * @return TArray|TNonEmptyArray */ - public function getGenericArrayType(bool $allow_non_empty = true, ?string $list_var_id = null): TArray + public function getGenericArrayType(?string $list_var_id = null): TArray { $key_types = []; $value_type = null; @@ -398,7 +398,7 @@ public function getGenericArrayType(bool $allow_non_empty = true, ?string $list_ $key_type = new Union([new TIntRange(0, null, false, $list_var_id)]); } - if ($has_defined_keys && $allow_non_empty) { + if ($has_defined_keys) { return new TNonEmptyArray([$key_type, $value_type]); } return new TArray([$key_type, $value_type]); @@ -414,7 +414,7 @@ public function getGenericArrayType(bool $allow_non_empty = true, ?string $list_ $value_type = $value_type->setPossiblyUndefined(false); - if ($allow_non_empty && ($has_defined_keys || $this->fallback_params !== null)) { + if ($has_defined_keys || $this->fallback_params !== null) { return new TNonEmptyArray([$key_type, $value_type]); } return new TArray([$key_type, $value_type]); From add6f3b4e03d233ee9a3b4a2119407b4ecdb190c Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 26 Jul 2023 11:06:13 +0200 Subject: [PATCH 18/31] Fix --- .../Internal/Analyzer/Statements/Block/ForeachAnalyzer.php | 1 - src/Psalm/Internal/Cli/Psalm.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 82a812243a7..c35183d4f0e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -478,7 +478,6 @@ public static function checkIteratorType( } $iterator_atomic_type = $iterator_atomic_type->getGenericArrayType( - true, ExpressionIdentifier::getExtendedVarId( $expr, $statements_analyzer->getFQCLN(), diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 688d8178495..10ae04aeee8 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -924,7 +924,7 @@ private static function restart(array $options, int $threads, Progress $progress if (!function_exists('opcache_get_status')) { $progress->write(PHP_EOL - . 'Install the opcache extension to make use of JIT on PHP 8.0+ for a 20%+ performance boost!' + . 'Install the opcache extension to make use of JIT for a 20%+ performance boost!' . PHP_EOL . PHP_EOL); } } From 8964f38d5ffe508f90695c7cf5134fe6e2c5fbef Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 28 Jul 2023 11:31:49 +0200 Subject: [PATCH 19/31] Merge --- .../Internal/Analyzer/NamespaceAnalyzer.php | 1 - .../LanguageServer/LanguageClient.php | 5 ++-- .../LanguageServer/LanguageServer.php | 23 +++++++++++-------- .../ArrayCombineReturnTypeProvider.php | 1 - 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php index e08bffbe29e..19f2b640749 100644 --- a/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php @@ -14,7 +14,6 @@ use function assert; use function count; -use function is_string; use function preg_replace; use function strpos; use function strtolower; diff --git a/src/Psalm/Internal/LanguageServer/LanguageClient.php b/src/Psalm/Internal/LanguageServer/LanguageClient.php index 280202fa9da..cd364ea36d3 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageClient.php +++ b/src/Psalm/Internal/LanguageServer/LanguageClient.php @@ -13,6 +13,7 @@ use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument; use Psalm\Internal\LanguageServer\Client\Workspace as ClientWorkspace; use Revolt\EventLoop; +use Throwable; use function is_null; use function json_decode; @@ -69,12 +70,12 @@ public function refreshConfiguration(): void { $capabilities = $this->server->clientCapabilities; if ($capabilities->workspace->configuration ?? false) { - EventLoop::queue(function () { + EventLoop::queue(function (): void { try { /** @var object $config */ [$config] = $this->workspace->requestConfiguration('psalm'); $this->configurationRefreshed((array) $config); - } catch (\Throwable) { + } catch (Throwable) { $this->server->logError('There was an error getting configuration'); } }); diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index a85f412b1c7..b0721aa078e 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -389,9 +389,6 @@ public function initialize( $this->project_analyzer->serverMode($this); - $this->logInfo("Initializing: Getting code base..."); - $this->clientStatus('initializing', 'getting code base'); - $this->logInfo("Initializing: Getting code base..."); $progress->update('getting code base'); @@ -403,6 +400,14 @@ public function initialize( $progress->update('registering stub files'); $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); + if ($this->textDocument === null) { + $this->textDocument = new ServerTextDocument( + $this, + $this->codebase, + $this->project_analyzer, + ); + } + if ($this->workspace === null) { $this->workspace = new ServerWorkspace( $this, @@ -530,12 +535,12 @@ public function initialize( $this->client->clientConfiguration->baseline, ); } - - $this->logInfo("Initializing: Complete."); - $this->clientStatus('initialized'); - - $this->logInfo("Initializing: Complete."); - $progress->end('initialized'); + /** + * Information about the server. + * + * @since LSP 3.15.0 + */ + $initializeResultServerInfo = new InitializeResultServerInfo('Psalm Language Server', PSALM_VERSION); return new InitializeResult($serverCapabilities, $initializeResultServerInfo); } diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php index 999f0b84ef5..62e7cbcb884 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayCombineReturnTypeProvider.php @@ -13,7 +13,6 @@ use Psalm\Type\Union; use function array_combine; -use function assert; use function count; /** From 85c41fa840cf8fef3f9c1dfc7ed213f76380d9eb Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 28 Jul 2023 11:43:55 +0200 Subject: [PATCH 20/31] Fixes --- .github/workflows/ci.yml | 3 +-- src/Psalm/Internal/LanguageServer/LanguageServer.php | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 205d7754113..33f2a6b945b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' tools: composer:v2 coverage: none env: @@ -125,7 +125,6 @@ jobs: fail-fast: false matrix: php-version: - - "8.0" - "8.1" - "8.2" - "8.3" diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index b0721aa078e..20ef9724c97 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -535,6 +535,10 @@ public function initialize( $this->client->clientConfiguration->baseline, ); } + + $this->logInfo("Initializing: Complete."); + $progress->end('initialized'); + /** * Information about the server. * From 6288ef91687e26a9e03d6b1f93828419fa68b873 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 28 Jul 2023 11:45:32 +0200 Subject: [PATCH 21/31] Fixup --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 60eaaea7eb5..6c5c76f8eb5 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "~8.1.17 || ~8.2.4", + "php": "~8.1.17 || ~8.2.4 || ~8.3.0", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-dom": "*", From 7ffba7f611ab5bc3199701aed77e07f75bc2ab20 Mon Sep 17 00:00:00 2001 From: Mark McEver Date: Tue, 8 Aug 2023 15:07:15 -0500 Subject: [PATCH 22/31] Respect stubs in all cases --- src/Psalm/Internal/Codebase/Functions.php | 15 +------ tests/StubTest.php | 44 +++++++++++++++++++ tests/fixtures/stubs/custom_taint_source.php | 3 ++ .../stubs/custom_taint_source.phpstub | 6 +++ .../stubs/define_custom_require_path.php | 3 ++ 5 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 tests/fixtures/stubs/custom_taint_source.php create mode 100644 tests/fixtures/stubs/custom_taint_source.phpstub create mode 100644 tests/fixtures/stubs/define_custom_require_path.php diff --git a/src/Psalm/Internal/Codebase/Functions.php b/src/Psalm/Internal/Codebase/Functions.php index 50e3f60fba5..bd18596c2a7 100644 --- a/src/Psalm/Internal/Codebase/Functions.php +++ b/src/Psalm/Internal/Codebase/Functions.php @@ -79,9 +79,8 @@ public function getStorage( $function_id = substr($function_id, 1); } - $from_stubs = false; if (isset(self::$stubbed_functions[$function_id])) { - $from_stubs = self::$stubbed_functions[$function_id]; + return self::$stubbed_functions[$function_id]; } $file_storage = null; @@ -113,10 +112,6 @@ public function getStorage( return $this->reflection->getFunctionStorage($function_id); } - if ($from_stubs) { - return $from_stubs; - } - throw new UnexpectedValueException( 'Expecting non-empty $root_file_path and $checked_file_path', ); @@ -135,10 +130,6 @@ public function getStorage( } } - if ($from_stubs) { - return $from_stubs; - } - throw new UnexpectedValueException( 'Expecting ' . $function_id . ' to have storage in ' . $checked_file_path, ); @@ -149,10 +140,6 @@ public function getStorage( $declaring_file_storage = $this->file_storage_provider->get($declaring_file_path); if (!isset($declaring_file_storage->functions[$function_id])) { - if ($from_stubs) { - return $from_stubs; - } - throw new UnexpectedValueException( 'Not expecting ' . $function_id . ' to not have storage in ' . $declaring_file_path, ); diff --git a/tests/StubTest.php b/tests/StubTest.php index c52239d68ee..14b3873ebdb 100644 --- a/tests/StubTest.php +++ b/tests/StubTest.php @@ -1514,4 +1514,48 @@ function em(EntityManager $em) : void { $this->analyzeFile($file_path, new Context()); } + + /** + * This covers the following case encountered by mmcev106: + * - A function was defined without a docblock + * - The autoloader defined a global containing the path to that file + * - The code being scanned required the path specified by the autoloader defined global + * - A docblock was added via a stub that marked the function as a taint source + * - The stub docblock was incorrectly ignored, causing the the taint source to be ignored. + */ + public function testAutoloadDefinedRequirePath(): void + { + $this->project_analyzer = $this->getProjectAnalyzerWithConfig( + TestConfig::loadFromXML( + dirname(__DIR__), + ' + + + + + + + + + ', + ), + ); + + $this->project_analyzer->trackTaintedInputs(); + + $file_path = getcwd() . '/src/somefile.php'; + + $this->addFile( + $file_path, + 'expectExceptionMessage('TaintedHtml - /src/somefile.php'); + $this->analyzeFile($file_path, new Context()); + } } diff --git a/tests/fixtures/stubs/custom_taint_source.php b/tests/fixtures/stubs/custom_taint_source.php new file mode 100644 index 00000000000..59eb33da49d --- /dev/null +++ b/tests/fixtures/stubs/custom_taint_source.php @@ -0,0 +1,3 @@ + Date: Tue, 8 Aug 2023 15:34:23 -0500 Subject: [PATCH 23/31] Removed trailing whitespace to follow code style --- tests/StubTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/StubTest.php b/tests/StubTest.php index 14b3873ebdb..b54039aa794 100644 --- a/tests/StubTest.php +++ b/tests/StubTest.php @@ -1521,7 +1521,7 @@ function em(EntityManager $em) : void { * - The autoloader defined a global containing the path to that file * - The code being scanned required the path specified by the autoloader defined global * - A docblock was added via a stub that marked the function as a taint source - * - The stub docblock was incorrectly ignored, causing the the taint source to be ignored. + * - The stub docblock was incorrectly ignored, causing the the taint source to be ignored */ public function testAutoloadDefinedRequirePath(): void { From 2911a670993e444210dd80f4420d7133c318ab49 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 13 Aug 2023 21:05:21 +0200 Subject: [PATCH 24/31] Show PHP version --- .github/workflows/windows-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 3d89ba846af..4834f1b2713 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -62,6 +62,11 @@ jobs: env: fail-fast: true + - name: PHP Version + run: | + php -v + php -r 'var_dump(PHP_VERSION_ID);' + - uses: actions/checkout@v3 - name: Get Composer Cache Directories From 09cdb3563d2a1cae7d3934bf0e49e326e125cf6a Mon Sep 17 00:00:00 2001 From: Mark McEver Date: Tue, 15 Aug 2023 09:29:48 -0500 Subject: [PATCH 25/31] Commit to trigger unit tests after switching the base branch from 5.x to master in the PR From 9aade96444be02fe10303d582832952fcc506baf Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 14 Mar 2023 22:48:40 -0400 Subject: [PATCH 26/31] Make `TLiteralFloat::$value` and `TLiteralInt::$value` typed Fixes vimeo/psalm#9516 --- src/Psalm/Type/Atomic/TLiteralFloat.php | 3 +-- src/Psalm/Type/Atomic/TLiteralInt.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Type/Atomic/TLiteralFloat.php b/src/Psalm/Type/Atomic/TLiteralFloat.php index c4e1a7fe456..a9925825ac5 100644 --- a/src/Psalm/Type/Atomic/TLiteralFloat.php +++ b/src/Psalm/Type/Atomic/TLiteralFloat.php @@ -9,8 +9,7 @@ */ final class TLiteralFloat extends TFloat { - /** @var float */ - public $value; + public float $value; public function __construct(float $value, bool $from_docblock = false) { diff --git a/src/Psalm/Type/Atomic/TLiteralInt.php b/src/Psalm/Type/Atomic/TLiteralInt.php index 8055974d6f7..558019fe92a 100644 --- a/src/Psalm/Type/Atomic/TLiteralInt.php +++ b/src/Psalm/Type/Atomic/TLiteralInt.php @@ -9,8 +9,7 @@ */ final class TLiteralInt extends TInt { - /** @var int */ - public $value; + public int $value; public function __construct(int $value, bool $from_docblock = false) { From c62be2b126e4a449068cb94079cf05673160a6c9 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 20 Aug 2023 07:43:55 +0200 Subject: [PATCH 27/31] Added BC note --- UPGRADING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index 054ff0cfd31..d47bbf45d14 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -29,6 +29,8 @@ - [BC] The return type of `Psalm\Internal\LanguageServer\ProtocolWriter#write() changed from `Amp\Promise` to `void` due to the switch to Amp v3 +- [BC] Properties `Psalm\Type\Atomic\TLiteralFloat::$value` and `Psalm\Type\Atomic\TLiteralInt::$value` became typed (`float` and `int` respectively) + # Upgrading from Psalm 4 to Psalm 5 ## Changed From c99a7b594f18771b05910bc014d1aa4923ae7836 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 20 Aug 2023 22:50:12 +0200 Subject: [PATCH 28/31] Update branch aliases --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3b850beb978..a9568278df7 100644 --- a/composer.json +++ b/composer.json @@ -77,7 +77,8 @@ }, "extra": { "branch-alias": { - "dev-master": "5.x-dev", + "dev-master": "6.x-dev", + "dev-5.x": "5.x-dev", "dev-4.x": "4.x-dev", "dev-3.x": "3.x-dev", "dev-2.x": "2.x-dev", From c16216bc424260368fcccdad12cf6306ae33a55f Mon Sep 17 00:00:00 2001 From: cgocast Date: Wed, 30 Aug 2023 17:22:14 +0200 Subject: [PATCH 29/31] Xpath injection #10162 --- config.xsd | 1 + docs/running_psalm/error_levels.md | 1 + docs/running_psalm/issues.md | 1 + docs/running_psalm/issues/TaintedXpath.md | 12 ++++++ .../Internal/Codebase/TaintFlowGraph.php | 10 +++++ src/Psalm/Issue/TaintedXpath.php | 8 ++++ src/Psalm/Type/TaintKind.php | 1 + src/Psalm/Type/TaintKindGroup.php | 1 + stubs/extensions/dom.phpstub | 5 +++ stubs/extensions/simplexml.phpstub | 5 ++- tests/TaintTest.php | 37 +++++++++++++++++++ 11 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 docs/running_psalm/issues/TaintedXpath.md create mode 100644 src/Psalm/Issue/TaintedXpath.php diff --git a/config.xsd b/config.xsd index 4cf075b6ece..eb5f11e2c21 100644 --- a/config.xsd +++ b/config.xsd @@ -444,6 +444,7 @@ + diff --git a/docs/running_psalm/error_levels.md b/docs/running_psalm/error_levels.md index 55a18b8fa61..90b5d5351b3 100644 --- a/docs/running_psalm/error_levels.md +++ b/docs/running_psalm/error_levels.md @@ -297,6 +297,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even - [TaintedSystemSecret](issues/TaintedSystemSecret.md) - [TaintedUnserialize](issues/TaintedUnserialize.md) - [TaintedUserSecret](issues/TaintedUserSecret.md) + - [TaintedXpath](issues/TaintedXpath.md) - [UncaughtThrowInGlobalScope](issues/UncaughtThrowInGlobalScope.md) - [UnevaluatedCode](issues/UnevaluatedCode.md) - [UnnecessaryVarAnnotation](issues/UnnecessaryVarAnnotation.md) diff --git a/docs/running_psalm/issues.md b/docs/running_psalm/issues.md index d9b3b4f168a..592225002e7 100644 --- a/docs/running_psalm/issues.md +++ b/docs/running_psalm/issues.md @@ -246,6 +246,7 @@ - [TaintedTextWithQuotes](issues/TaintedTextWithQuotes.md) - [TaintedUnserialize](issues/TaintedUnserialize.md) - [TaintedUserSecret](issues/TaintedUserSecret.md) + - [TaintedXpath](issues/TaintedXpath.md) - [TooFewArguments](issues/TooFewArguments.md) - [TooManyArguments](issues/TooManyArguments.md) - [TooManyTemplateParams](issues/TooManyTemplateParams.md) diff --git a/docs/running_psalm/issues/TaintedXpath.md b/docs/running_psalm/issues/TaintedXpath.md new file mode 100644 index 00000000000..c0b16bbbc78 --- /dev/null +++ b/docs/running_psalm/issues/TaintedXpath.md @@ -0,0 +1,12 @@ +# TaintedSql + +Emitted when user-controlled input can be passed into to a xpath query. + +```php +xpath($expression); +} +``` diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php index b18b82eb161..ba4f20fcc53 100644 --- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php +++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php @@ -24,6 +24,7 @@ use Psalm\Issue\TaintedTextWithQuotes; use Psalm\Issue\TaintedUnserialize; use Psalm\Issue\TaintedUserSecret; +use Psalm\Issue\TaintedXpath; use Psalm\IssueBuffer; use Psalm\Type\TaintKind; @@ -449,6 +450,15 @@ private function getChildNodes( ); break; + case TaintKind::INPUT_XPATH: + $issue = new TaintedXpath( + 'Detected tainted xpath query', + $issue_location, + $issue_trace, + $path, + ); + break; + default: $issue = new TaintedCustom( 'Detected tainted ' . $matching_taint, diff --git a/src/Psalm/Issue/TaintedXpath.php b/src/Psalm/Issue/TaintedXpath.php new file mode 100644 index 00000000000..b9e4dbb42cb --- /dev/null +++ b/src/Psalm/Issue/TaintedXpath.php @@ -0,0 +1,8 @@ +|false + * @psalm-taint-sink xpath $expression + */ public function evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {} /** * @return DOMNodeList|false + * @psalm-taint-sink xpath $expression */ public function query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed {} diff --git a/stubs/extensions/simplexml.phpstub b/stubs/extensions/simplexml.phpstub index 7f0bfa2143f..d2501f62096 100644 --- a/stubs/extensions/simplexml.phpstub +++ b/stubs/extensions/simplexml.phpstub @@ -29,7 +29,10 @@ function simplexml_import_dom(SimpleXMLElement|DOMNode $node, ?string $class_nam */ class SimpleXMLElement implements Traversable, Countable { - /** @return array|null|false */ + /** + * @return array|null|false + * @psalm-taint-sink xpath $expression + */ public function xpath(string $expression) {} public function registerXPathNamespace(string $prefix, string $namespace): bool {} diff --git a/tests/TaintTest.php b/tests/TaintTest.php index d5dd0e1dc34..a0cefb61d20 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -749,6 +749,19 @@ function bar(array $arr): void { $d = mysqli_real_escape_string($_GET["d"]); $mysqli->query("$a$b$c$d");', + ], + 'querySimpleXMLElement' => [ + 'code' => 'xpath($expression); + }', ], ]; } @@ -2503,6 +2516,30 @@ public static function getPrevious(string $s): string { $function->invoke();', 'error_message' => 'TaintedCallable', ], + 'querySimpleXMLElement' => [ + 'code' => 'xpath($expression); + }', + 'error_message' => 'TaintedXpath', + ], + 'queryDOMXPath' => [ + 'code' => 'query($expression); + }', + 'error_message' => 'TaintedXpath', + ], + 'evaluateDOMXPath' => [ + 'code' => 'evaluate($expression); + }', + 'error_message' => 'TaintedXpath', + ], ]; } From 5545873f442d8ecc6052f2fcbb9373b7ebc4f19f Mon Sep 17 00:00:00 2001 From: cgocast Date: Thu, 31 Aug 2023 05:20:39 +0200 Subject: [PATCH 30/31] Fix tests --- docs/running_psalm/issues/TaintedXpath.md | 2 +- tests/TaintTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/running_psalm/issues/TaintedXpath.md b/docs/running_psalm/issues/TaintedXpath.md index c0b16bbbc78..22616446aa9 100644 --- a/docs/running_psalm/issues/TaintedXpath.md +++ b/docs/running_psalm/issues/TaintedXpath.md @@ -1,4 +1,4 @@ -# TaintedSql +# TaintedXpath Emitted when user-controlled input can be passed into to a xpath query. diff --git a/tests/TaintTest.php b/tests/TaintTest.php index a0cefb61d20..7efd9dae9c0 100644 --- a/tests/TaintTest.php +++ b/tests/TaintTest.php @@ -749,7 +749,7 @@ function bar(array $arr): void { $d = mysqli_real_escape_string($_GET["d"]); $mysqli->query("$a$b$c$d");', - ], + ], 'querySimpleXMLElement' => [ 'code' => 'xpath($expression); - }', + }', 'error_message' => 'TaintedXpath', ], 'queryDOMXPath' => [ @@ -2529,7 +2529,7 @@ function queryExpression(SimpleXMLElement $xml) : array|false|null { function queryExpression(DOMXPath $xpath) : mixed { $expression = $_GET["expression"]; return $xpath->query($expression); - }', + }', 'error_message' => 'TaintedXpath', ], 'evaluateDOMXPath' => [ @@ -2537,7 +2537,7 @@ function queryExpression(DOMXPath $xpath) : mixed { function evaluateExpression(DOMXPath $xpath) : mixed { $expression = $_GET["expression"]; return $xpath->evaluate($expression); - }', + }', 'error_message' => 'TaintedXpath', ], ]; From e5b912bb2b543bb9c924ccdca2d99789a7d77c6e Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Thu, 31 Aug 2023 16:30:37 +0200 Subject: [PATCH 31/31] Document BC break --- UPGRADING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING.md b/UPGRADING.md index 01f1a67ddda..0bf6ee60503 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -9,6 +9,8 @@ - [BC] The `TDependentListKey` type was removed and replaced with an optional property of the `TIntRange` type. +- [BC] Value of constant `Psalm\Type\TaintKindGroup::ALL_INPUT` changed to reflect a new `TaintKind::INPUT_XPATH` have been added. Accordingly, default values for `$taint` parameters of `Psalm\Codebase::addTaintSource()` and `Psalm\Codebase::addTaintSink()` have been changed as well. + # Upgrading from Psalm 4 to Psalm 5 ## Changed