diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 33c8c32e622..225df472e08 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -6822,7 +6822,7 @@ 'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], 'ldap_rename_ext' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], -'ldap_search' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], +'ldap_search' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], 'ldap_start_tls' => ['bool', 'ldap'=>'resource'], diff --git a/dictionaries/CallMap_81_delta.php b/dictionaries/CallMap_81_delta.php index 00277237a92..3195d08f3be 100644 --- a/dictionaries/CallMap_81_delta.php +++ b/dictionaries/CallMap_81_delta.php @@ -627,7 +627,7 @@ ], 'ldap_search' => [ 'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['LDAP\Connection|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], ], 'ldap_set_option' => [ 'old' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'], diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index 091ed537c2e..c754d17756e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -444,20 +444,26 @@ private static function analyzeOperands( } if ($left_type_part instanceof TTemplateParam || $right_type_part instanceof TTemplateParam) { - if ($left_type_part instanceof TTemplateParam && !$left_type_part->as->isInt()) { + if ($left_type_part instanceof TTemplateParam + && !$left_type_part->as->isInt() + && !$left_type_part->as->isFloat() + ) { if ($statements_source && IssueBuffer::accepts( new MixedOperand( - 'Left operand cannot be a non-int template', + 'Left operand cannot be a non-numeric template', new CodeLocation($statements_source, $left) ), $statements_source->getSuppressedIssues() )) { // fall through } - } elseif ($right_type_part instanceof TTemplateParam && !$right_type_part->as->isInt()) { + } elseif ($right_type_part instanceof TTemplateParam + && !$right_type_part->as->isInt() + && !$right_type_part->as->isFloat() + ) { if ($statements_source && IssueBuffer::accepts( new MixedOperand( - 'Right operand cannot be a non-int template', + 'Right operand cannot be a non-numeric template', new CodeLocation($statements_source, $right) ), $statements_source->getSuppressedIssues() diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 122964f17a8..e3aec2a4777 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -1029,10 +1029,6 @@ private static function initBaseline( $issue_baseline = self::generateBaseline($options, $config, $current_dir, $path_to_config); } - if (isset($options['update-baseline'])) { - $issue_baseline = self::updateBaseline($options, $config); - } - if (isset($options['use-baseline'])) { if (!is_string($options['use-baseline'])) { fwrite(STDERR, '--use-baseline must be a string' . PHP_EOL); @@ -1040,10 +1036,15 @@ private static function initBaseline( } $baseline_file_path = $options['use-baseline']; + $config->error_baseline = $baseline_file_path; } else { $baseline_file_path = $config->error_baseline; } + if (isset($options['update-baseline'])) { + $issue_baseline = self::updateBaseline($options, $config); + } + if (!$issue_baseline && $baseline_file_path && !isset($options['ignore-baseline'])) { try { $issue_baseline = ErrorBaseline::read( diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index cb43637dd49..66882cc1e60 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -142,11 +142,6 @@ function (Message $msg): Generator { return; } - /** @psalm-suppress UndefinedPropertyFetch */ - if ($msg->body->method === 'textDocument/signatureHelp') { - $this->doAnalysis(); - } - $result = null; $error = null; try { diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index 45bad8145c0..664111e9e0e 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -6,7 +6,6 @@ use Amp\Promise; use Amp\Success; -use InvalidArgumentException; use LanguageServerProtocol\CompletionList; use LanguageServerProtocol\Hover; use LanguageServerProtocol\Location; @@ -27,7 +26,6 @@ use Psalm\Internal\LanguageServer\LanguageServer; use UnexpectedValueException; -use function array_combine; use function array_values; use function count; use function error_log; @@ -94,8 +92,9 @@ public function didOpen(TextDocumentItem $textDocument): void * The document save notification is sent from the client to the server when the document was saved in the client * * @param TextDocumentItem $textDocument the document that was opened + * @param ?string $text the content when saved */ - public function didSave(TextDocumentItem $textDocument): void + public function didSave(TextDocumentItem $textDocument, ?string $text): void { $file_path = LanguageServer::uriToPath($textDocument->uri); @@ -105,7 +104,7 @@ public function didSave(TextDocumentItem $textDocument): void // reopen file $this->codebase->removeTemporaryFileChanges($file_path); - $this->codebase->file_provider->setOpenContents($file_path, $textDocument->text); + $this->codebase->file_provider->setOpenContents($file_path, (string) $text); $this->server->queueFileAnalysis($file_path, $textDocument->uri); } @@ -124,10 +123,6 @@ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $ return; } - if ($this->project_analyzer->onchange_line_limit === 0) { - return; - } - if (count($contentChanges) === 1 && $contentChanges[0]->range === null) { $new_content = $contentChanges[0]->text; } else { @@ -268,8 +263,6 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): */ public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise { - $this->server->doAnalysis(); - $file_path = LanguageServer::uriToPath($textDocument->uri); if (!$this->codebase->config->isInProjectDirs($file_path)) { return new Success([]); @@ -363,18 +356,6 @@ public function codeAction(TextDocumentIdentifier $textDocument, Range $range): return new Success(null); } - $all_file_paths_to_analyze = [$file_path]; - $this->codebase->analyzer->addFilesToAnalyze( - array_combine($all_file_paths_to_analyze, $all_file_paths_to_analyze) - ); - - try { - $this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); - } catch (UnexpectedValueException | InvalidArgumentException $e) { - error_log('codeAction errored on file ' . $file_path. ', Reason: '.$e->getMessage()); - return new Success(null); - } - $issues = $this->server->getCurrentIssues(); if (empty($issues[$file_path])) { diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index 37199dbf690..92b93997cea 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -141,7 +141,7 @@ public function getStatementsForFile( if (!$this->parser_cache_provider || (!$config->isInProjectDirs($file_path) && strpos($file_path, 'vendor')) ) { - $cache_key = "${file_content_hash}:${php_version}"; + $cache_key = "${file_content_hash}:${analysis_php_version_id}"; if ($this->statements_volatile_cache->has($cache_key)) { return $this->statements_volatile_cache->get($cache_key); } @@ -150,7 +150,7 @@ public function getStatementsForFile( $has_errors = false; - $stmts = self::parseStatements($file_contents, $php_version, $has_errors, $file_path); + $stmts = self::parseStatements($file_contents, $analysis_php_version_id, $has_errors, $file_path); $this->statements_volatile_cache->set($cache_key, $stmts); diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 5f3896642bf..8d2e1e09929 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -889,10 +889,6 @@ private static function getTypeFromGenericTree( return new TInt(); } - if ($min_bound === 1 && $max_bound === null) { - return new TPositiveInt(); - } - if (is_int($min_bound) && is_int($max_bound) && $min_bound > $max_bound) { throw new TypeParseTreeException( "Min bound can't be greater than max bound, int<$min_bound, $max_bound> given" diff --git a/tests/ArrayAssignmentTest.php b/tests/ArrayAssignmentTest.php index e0c5e84d206..e10802a8257 100644 --- a/tests/ArrayAssignmentTest.php +++ b/tests/ArrayAssignmentTest.php @@ -1741,7 +1741,7 @@ function foobar(): ?array 'php_version' => '8.1' ], 'allowsArrayAccessNullOffset' => [ - ' ' */ diff --git a/tests/AttributeTest.php b/tests/AttributeTest.php index f2b59565c41..f60087d06da 100644 --- a/tests/AttributeTest.php +++ b/tests/AttributeTest.php @@ -731,7 +731,7 @@ class Baz {} 'error_message' => 'InvalidAttribute - src' . DIRECTORY_SEPARATOR . 'somefile.php:5:28 - Attribute Foo is not repeatable', ], 'invalidAttributeConstructionWithReturningFunction' => [ - ' ' 'DocblockTypeContradiction', ], 'maxSpecifiedAsFirst' => [ - ' ' $a */ @@ -907,7 +907,7 @@ function scope(int $a){ 'error_message' => 'InvalidDocblock', ], 'minSpecifiedAsSecond' => [ - ' ' $a */ @@ -917,7 +917,7 @@ function scope(int $a){ 'error_message' => 'InvalidDocblock', ], 'unknownConstant' => [ - ' ' $a */ @@ -927,7 +927,7 @@ function scope(int $a){ 'error_message' => 'InvalidDocblock', ], 'floatAsABoundary' => [ - ' ' $a */ @@ -937,7 +937,7 @@ function scope(int $a){ 'error_message' => 'InvalidDocblock', ], 'stringAsABoundary' => [ - ' ' $a */ @@ -947,7 +947,7 @@ function scope(int $a){ 'error_message' => 'InvalidDocblock', ], 'minGreaterThanMax' => [ - ' ' $a */ diff --git a/tests/Template/FunctionTemplateTest.php b/tests/Template/FunctionTemplateTest.php index 772dee8afa0..04b93c9b447 100644 --- a/tests/Template/FunctionTemplateTest.php +++ b/tests/Template/FunctionTemplateTest.php @@ -1606,6 +1606,22 @@ function foo($p): void { } }' ], + 'dontScreamForArithmeticsOnFloatTemplates' => [ + 'code' => ' [ - ' '