Skip to content

Commit

Permalink
Merge pull request #8024 from weirdan/update-master
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan committed May 29, 2022
2 parents ec9a999 + 9784fe4 commit 3a24488
Show file tree
Hide file tree
Showing 28 changed files with 611 additions and 171 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/shepherd.yml
Expand Up @@ -8,6 +8,9 @@ jobs:

steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: '7.4'

- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
Expand Down
3 changes: 1 addition & 2 deletions composer.json
Expand Up @@ -36,8 +36,7 @@
"sebastian/diff": "^4.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0",
"symfony/polyfill-php80": "^1.25",
"webmozart/path-util": "^2.3"
"symfony/polyfill-php80": "^1.25"
},
"provide": {
"psalm/psalm": "self.version"
Expand Down
4 changes: 3 additions & 1 deletion dictionaries/CallMap.php
Expand Up @@ -418,7 +418,9 @@
'array_uintersect_assoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'],
'array_uintersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'],
'array_uintersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
'array_unique' => ['associative-array', 'array'=>'array', 'flags='=>'int'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'],
'array_unique\'1' => ['array<int|float|string|null>', 'array'=>'array<int|float|string|null>', 'flags='=>'1'],
'array_unique\'2' => ['array<int|float|string|bool|\Stringable|null>', 'array'=>'array<int|float|string|bool|\Stringable|null>', 'flags='=>'2|5'],
'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'],
'array_values' => ['list<mixed>', 'array'=>'array'],
'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'],
Expand Down
4 changes: 3 additions & 1 deletion dictionaries/CallMap_historical.php
Expand Up @@ -9561,7 +9561,9 @@
'array_uintersect_assoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'],
'array_uintersect_uassoc' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'],
'array_uintersect_uassoc\'1' => ['associative-array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'],
'array_unique' => ['associative-array', 'array'=>'array', 'flags='=>'int'],
'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'],
'array_unique\'1' => ['array<int|float|string|null>', 'array'=>'array<int|float|string|null>', 'flags='=>'1'],
'array_unique\'2' => ['array<int|float|string|bool|\Stringable|null>', 'array'=>'array<int|float|string|bool|\Stringable|null>', 'flags='=>'2|5'],
'array_unshift' => ['int', '&rw_array'=>'array', 'values'=>'mixed', '...vars='=>'mixed'],
'array_values' => ['list<mixed>', 'array'=>'array'],
'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'],
Expand Down
5 changes: 5 additions & 0 deletions src/Psalm/Config.php
Expand Up @@ -2130,6 +2130,11 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null):
$this->internal_stubs[] = $ext_phpredis_path;
}

if (extension_loaded('apcu')) {
$ext_apcu_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'ext-apcu.phpstub';
$this->internal_stubs[] = $ext_apcu_path;
}

foreach ($this->internal_stubs as $stub_path) {
if (!file_exists($stub_path)) {
throw new UnexpectedValueException('Cannot locate ' . $stub_path);
Expand Down
57 changes: 42 additions & 15 deletions src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php
Expand Up @@ -192,22 +192,42 @@ public static function analyze(
}

if ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
$was_inside_general_use = $context->inside_general_use;
$context->inside_general_use = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
$context->inside_general_use = $was_inside_general_use;

if (!self::checkExprGeneralUse($statements_analyzer, $stmt, $context)) {
return false;
}
$context->inside_general_use = $was_inside_general_use;

$type = new Union([new TNamedObject('stdClass')]);
$permissible_atomic_types = [];
$all_permissible = false;

$maybe_type = $statements_analyzer->node_data->getType($stmt->expr);
if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
if ($stmt_expr_type->isObjectType()) {
self::handleRedundantCast($stmt_expr_type, $statements_analyzer, $stmt);
}

$all_permissible = true;

foreach ($stmt_expr_type->getAtomicTypes() as $type) {
if ($type instanceof Scalar) {
$objWithProps = new TObjectWithProperties(['scalar' => new Union([$type])]);
$permissible_atomic_types[] = $objWithProps;
} elseif ($type instanceof TKeyedArray) {
$permissible_atomic_types[] = new TObjectWithProperties($type->properties);
} else {
$all_permissible = false;
break;
}
}
}

if ($permissible_atomic_types && $all_permissible) {
$type = TypeCombiner::combine($permissible_atomic_types);
} else {
$type = Type::getObject();
}

if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
) {
$type->parent_nodes = $maybe_type->parent_nodes ?? [];
$type->parent_nodes = $stmt_expr_type->parent_nodes ?? [];
}

$statements_analyzer->node_data->setType($stmt, $type);
Expand All @@ -216,14 +236,9 @@ public static function analyze(
}

if ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
$was_inside_general_use = $context->inside_general_use;
$context->inside_general_use = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
$context->inside_general_use = $was_inside_general_use;

if (!self::checkExprGeneralUse($statements_analyzer, $stmt, $context)) {
return false;
}
$context->inside_general_use = $was_inside_general_use;

$permissible_atomic_types = [];
$all_permissible = false;
Expand Down Expand Up @@ -466,6 +481,18 @@ public static function castStringAttempt(
return $str_type;
}

private static function checkExprGeneralUse(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Expr\Cast $stmt,
Context $context
): bool {
$was_inside_general_use = $context->inside_general_use;
$context->inside_general_use = true;
$retVal = ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context);
$context->inside_general_use = $was_inside_general_use;
return $retVal;
}

private static function handleRedundantCast(
Union $maybe_type,
StatementsAnalyzer $statements_analyzer,
Expand Down
5 changes: 0 additions & 5 deletions src/Psalm/Internal/Cli/Plugin.php
Expand Up @@ -9,7 +9,6 @@
use Psalm\Internal\PluginManager\Command\ShowCommand;
use Psalm\Internal\PluginManager\PluginListFactory;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputOption;

use function dirname;
use function getcwd;
Expand Down Expand Up @@ -44,10 +43,6 @@ public static function run(): void
new DisableCommand($plugin_list_factory),
]);

$app->getDefinition()->addOption(
new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
);

$app->setDefaultCommand('show');
$app->run();
}
Expand Down
1 change: 1 addition & 0 deletions src/Psalm/Internal/Codebase/Reflection.php
Expand Up @@ -419,6 +419,7 @@ public function registerFunction(string $function_id): ?bool
return null;
}

/** @psalm-suppress UndefinedClass,TypeDoesNotContainType 7.4 has no ReflectionUnionType */
public static function getPsalmTypeFromReflectionType(?ReflectionType $reflection_type = null): Union
{
if (!$reflection_type) {
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/LanguageServer/LanguageServer.php
Expand Up @@ -418,7 +418,7 @@ public function emitIssues(array $uris): void
} else {
// the Diagnostic constructor only takes `int` for the code, but the property can be
// `int` or `string`, so we set the property directly because we want to use a `string`
/** @psalm-suppress InvalidPropertyAssignmentValue */
/** @psalm-suppress InvalidPropertyAssignmentValue */
$diagnostic->code = $code;
}

Expand Down
1 change: 0 additions & 1 deletion src/Psalm/Internal/LanguageServer/Server/TextDocument.php
Expand Up @@ -380,7 +380,6 @@ public function codeAction(TextDocumentIdentifier $textDocument, Range $range):
$indentation = $matches[1] ?? '';
}


/**
* Suppress Psalm because ther are bugs in how
* LanguageServer's signature of WorkspaceEdit is declared:
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/PluginManager/Command/DisableCommand.php
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use UnexpectedValueException;
Expand Down Expand Up @@ -41,6 +42,7 @@ protected function configure(): void
InputArgument::REQUIRED,
'Plugin name (fully qualified class name or composer package name)'
)
->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
->addUsage('vendor/plugin-package-name [-c path/to/psalm.xml]');
$this->addUsage('\'Plugin\Class\Name\' [-c path/to/psalm.xml]');
}
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/PluginManager/Command/EnableCommand.php
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use UnexpectedValueException;
Expand Down Expand Up @@ -41,6 +42,7 @@ protected function configure(): void
InputArgument::REQUIRED,
'Plugin name (fully qualified class name or composer package name)'
)
->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
->addUsage('vendor/plugin-package-name [-c path/to/psalm.xml]');
$this->addUsage('\'Plugin\Class\Name\' [-c path/to/psalm.xml]');
}
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/PluginManager/Command/ShowCommand.php
Expand Up @@ -5,6 +5,7 @@
use Psalm\Internal\PluginManager\PluginListFactory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use UnexpectedValueException;
Expand Down Expand Up @@ -37,6 +38,7 @@ protected function configure(): void
$this
->setName('show')
->setDescription('Lists enabled and available plugins')
->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
->addUsage('[-c path/to/psalm.xml]');
}

Expand Down
12 changes: 9 additions & 3 deletions src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php
Expand Up @@ -13,17 +13,18 @@
use function file_put_contents;
use function filemtime;
use function get_class;
use function hash;
use function igbinary_serialize;
use function igbinary_unserialize;
use function is_dir;
use function mkdir;
use function serialize;
use function sha1;
use function strtolower;
use function unlink;
use function unserialize;

use const DIRECTORY_SEPARATOR;
use const PHP_VERSION_ID;

/**
* @internal
Expand Down Expand Up @@ -111,7 +112,8 @@ public function getLatestFromCache(

private function getCacheHash(?string $file_path, ?string $file_contents): string
{
return sha1(($file_path ? $file_contents : '') . $this->modified_timestamps);
$data = ($file_path ? $file_contents : '') . $this->modified_timestamps;
return PHP_VERSION_ID >= 8_01_00 ? hash('xxh128', $data) : hash('md4', $data);
}

/**
Expand Down Expand Up @@ -161,9 +163,13 @@ private function getCacheLocationForClass(
mkdir($parser_cache_directory, 0777, true);
}

$data = $file_path ? strtolower($file_path) . ' ' : '';
$data .= $fq_classlike_name_lc;
$file_path_sha = PHP_VERSION_ID >= 8_01_00 ? hash('xxh128', $data) : hash('md4', $data);

return $parser_cache_directory
. DIRECTORY_SEPARATOR
. sha1(($file_path ? strtolower($file_path) . ' ' : '') . $fq_classlike_name_lc)
. $file_path_sha
. ($this->config->use_igbinary ? '-igbinary' : '');
}
}
11 changes: 5 additions & 6 deletions src/Psalm/Internal/Provider/FakeFileProvider.php
Expand Up @@ -4,7 +4,6 @@

use function microtime;
use function strpos;
use function strtolower;

/**
* @internal
Expand All @@ -28,8 +27,8 @@ public function fileExists(string $file_path): bool

public function getContents(string $file_path, bool $go_to_source = false): string
{
if (!$go_to_source && isset($this->temp_files[strtolower($file_path)])) {
return $this->temp_files[strtolower($file_path)];
if (!$go_to_source && isset($this->temp_files[$file_path])) {
return $this->temp_files[$file_path];
}

return $this->fake_files[$file_path] ?? parent::getContents($file_path);
Expand All @@ -42,8 +41,8 @@ public function setContents(string $file_path, string $file_contents): void

public function setOpenContents(string $file_path, string $file_contents): void
{
if (isset($this->fake_files[strtolower($file_path)])) {
$this->fake_files[strtolower($file_path)] = $file_contents;
if (isset($this->fake_files[$file_path])) {
$this->fake_files[$file_path] = $file_contents;
}
}

Expand All @@ -69,7 +68,7 @@ public function getFilesInDir(string $dir_path, array $file_extensions, callable
$file_paths = parent::getFilesInDir($dir_path, $file_extensions, $filter);

foreach ($this->fake_files as $file_path => $_) {
if (strpos(strtolower($file_path), strtolower($dir_path)) === 0) {
if (strpos($file_path, $dir_path) === 0) {
$file_paths[] = $file_path;
}
}
Expand Down

0 comments on commit 3a24488

Please sign in to comment.