Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/Psalm/Codebase.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
use function str_contains;
use function str_replace;
use function str_starts_with;
use function strcmp;
use function strlen;
use function strpos;
use function strrpos;
Expand Down Expand Up @@ -139,7 +140,7 @@ final class Codebase
public readonly Progress $progress;

/**
* @var array<string, Union>
* @var array<string, ConstantWithLocation>
*/
private static array $stubbed_constants = [];

Expand Down Expand Up @@ -659,26 +660,40 @@ public function getClosureStorage(string $file_path, string $closure_id): Functi
);
}

public function addGlobalConstantType(string $const_id, Union $type): void
public function addGlobalConstantType(string $const_id, Union $type, string $location): void
{
self::$stubbed_constants[$const_id] = $type;
if (isset(self::$stubbed_constants[$const_id])) {
$existing = self::$stubbed_constants[$const_id];
if (strcmp($existing->location, $location) <= 0) {
return;
}
}
self::$stubbed_constants[$const_id] = new ConstantWithLocation($type, $location);
}

public function getStubbedConstantType(string $const_id): ?Union
{
return self::$stubbed_constants[$const_id] ?? null;
return self::$stubbed_constants[$const_id]?->type ?? null;
}

/**
* @param array<string, Union> $stubs
* @param array<string, ConstantWithLocation> $stubs
*/
public function addGlobalConstantTypes(array $stubs): void
{
self::$stubbed_constants += $stubs;
foreach ($stubs as $const_id => $stub) {
if (isset(self::$stubbed_constants[$const_id])) {
$existing = self::$stubbed_constants[$const_id];
if (strcmp($existing->location, $stub->location) <= 0) {
continue;
}
}
self::$stubbed_constants[$const_id] = $stub;
}
}

/**
* @return array<string, Union>
* @return array<string, ConstantWithLocation>
*/
public function getAllStubbedConstants(): array
{
Expand Down
16 changes: 16 additions & 0 deletions src/Psalm/ConstantWithLocation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Psalm;

use Psalm\Type\Union;

final class ConstantWithLocation
{
public function __construct(
public readonly Union $type,
public readonly string $location,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -453,14 +453,14 @@ private static function handleNamedFunction(
&& !$function_name instanceof PhpParser\Node\Name\FullyQualified
) {
$function_call_info->in_call_map = InternalCallMapHandler::inCallMap($original_function_id);
$function_call_info->is_stubbed = $codebase_functions->hasStubbedFunction($original_function_id);
$function_call_info->is_stubbed = $codebase_functions->hasGlobalFunction(strtolower($original_function_id));

if ($function_call_info->is_stubbed || $function_call_info->in_call_map) {
$function_call_info->function_id = $original_function_id;
}
} else {
$function_call_info->in_call_map = InternalCallMapHandler::inCallMap($function_call_info->function_id);
$function_call_info->is_stubbed = $codebase_functions->hasStubbedFunction($function_call_info->function_id);
$function_call_info->is_stubbed = $codebase_functions->hasGlobalFunction(strtolower($function_call_info->function_id));
}

$function_call_info->function_exists
Expand Down
44 changes: 31 additions & 13 deletions src/Psalm/Internal/Codebase/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use function str_contains;
use function str_ends_with;
use function str_starts_with;
use function strcmp;
use function strtolower;
use function substr;

Expand All @@ -44,7 +45,7 @@
/**
* @var array<lowercase-string, FunctionStorage>
*/
private static array $stubbed_functions;
private static array $global_functions;

public FunctionReturnTypeProvider $return_type_provider;

Expand All @@ -63,7 +64,7 @@
$this->params_provider = new FunctionParamsProvider();
$this->dynamic_storage_provider = new DynamicFunctionStorageProvider();

self::$stubbed_functions = [];
self::$global_functions = [];
}

/**
Expand All @@ -79,8 +80,8 @@
$function_id = substr($function_id, 1);
}

if (isset(self::$stubbed_functions[$function_id])) {
return self::$stubbed_functions[$function_id];
if (isset(self::$global_functions[$function_id])) {
return self::$global_functions[$function_id];
}

$file_storage = null;
Expand Down Expand Up @@ -148,30 +149,47 @@
return $declaring_file_storage->functions[$function_id];
}

/**
* @param lowercase-string $function_id
*/
public function addGlobalFunction(string $function_id, FunctionStorage $storage): void
{
self::$stubbed_functions[strtolower($function_id)] = $storage;
$path = $storage->location ? $storage->location->getHash() : '';
if (isset(self::$global_functions[$function_id])) {
$existing = self::$global_functions[$function_id];
$existing_path = $existing->location ? $existing->location->getHash() : '';
if (strcmp($existing_path, $path) <= 0) {
var_dump("Function $function_id already exists in global functions from $existing_path, skipping $path");

Check failure on line 162 in src/Psalm/Internal/Codebase/Functions.php

View workflow job for this annotation

GitHub Actions / build

ForbiddenCode

src/Psalm/Internal/Codebase/Functions.php:162:17: ForbiddenCode: Unsafe var_dump (see https://psalm.dev/002)

Check failure on line 162 in src/Psalm/Internal/Codebase/Functions.php

View workflow job for this annotation

GitHub Actions / build

ForbiddenCode

src/Psalm/Internal/Codebase/Functions.php:162:17: ForbiddenCode: Unsafe var_dump (see https://psalm.dev/002)
return;
}
}
self::$global_functions[$function_id] = $storage;
}

/**
* @param array<lowercase-string, FunctionStorage> $stubs
*/
public function addGlobalFunctions(array $stubs): void
{
self::$stubbed_functions += $stubs;
foreach ($stubs as $function_id => $storage) {
self::addGlobalFunction($function_id, $storage);
}
}

public function hasStubbedFunction(string $function_id): bool
/**
* @param lowercase-string $function_id
*/
public function hasGlobalFunction(string $function_id): bool
{
return isset(self::$stubbed_functions[strtolower($function_id)]);
return isset(self::$global_functions[$function_id]);
}

/**
* @return array<lowercase-string, FunctionStorage>
*/
public function getAllStubbedFunctions(): array
public function getAllGlobalFunctions(): array
{
return self::$stubbed_functions;
return self::$global_functions;
}

/**
Expand Down Expand Up @@ -199,7 +217,7 @@
return true;
}

if (isset(self::$stubbed_functions[$function_id])) {
if (isset(self::$global_functions[$function_id])) {
return true;
}

Expand Down Expand Up @@ -333,7 +351,7 @@
}

$function_map = $file_storage->functions
+ $this->getAllStubbedFunctions()
+ $this->getAllGlobalFunctions()
+ $this->reflection->getFunctions()
+ $codebase->config->getPredefinedFunctions();

Expand Down Expand Up @@ -499,6 +517,6 @@

public static function clearCache(): void
{
self::$stubbed_functions = [];
self::$global_functions = [];
}
}
16 changes: 9 additions & 7 deletions src/Psalm/Internal/Codebase/Scanner.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?php

declare(strict_types=1);
Expand All @@ -6,6 +6,7 @@

use Psalm\Codebase;
use Psalm\Config;
use Psalm\ConstantWithLocation;
use Psalm\Internal\Analyzer\IssueData;
use Psalm\Internal\ErrorHandler;
use Psalm\Internal\Fork\InitScannerTask;
Expand All @@ -22,7 +23,6 @@
use Psalm\Storage\FileStorage;
use Psalm\Storage\FunctionStorage;
use Psalm\Type;
use Psalm\Type\Union;
use ReflectionClass;
use Throwable;
use UnexpectedValueException;
Expand Down Expand Up @@ -80,7 +80,7 @@
* classlike_storage:array<string, ClassLikeStorage>,
* file_storage:array<lowercase-string, FileStorage>,
* taint_data: ?TaintFlowGraph,
* global_constants: array<string, Union>,
* global_constants: array<string, ConstantWithLocation>,
* global_functions: array<lowercase-string, FunctionStorage>
* }
*/
Expand Down Expand Up @@ -499,11 +499,9 @@
|| $this->codebase->all_functions_global
) {
foreach ($file_storage->functions as $function_storage) {
if ($function_storage->cased_name
&& !$this->codebase->functions->hasStubbedFunction($function_storage->cased_name)
) {
if ($function_storage->cased_name !== null) {
$this->codebase->functions->addGlobalFunction(
$function_storage->cased_name,
strtolower($function_storage->cased_name),
$function_storage,
);
}
Expand All @@ -513,7 +511,11 @@
|| $this->codebase->all_constants_global
) {
foreach ($file_storage->constants as $name => $type) {
$this->codebase->addGlobalConstantType($name, $type);
$this->codebase->addGlobalConstantType(
$name,
$type,
$file_storage->constants_location[$name],
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Fork/ShutdownScannerTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function run(Channel $channel, Cancellation $cancellation): mixed
'file_storage' => $codebase->file_storage_provider->getAll(),
'taint_data' => $codebase->taint_flow_graph,
'global_constants' => $codebase->getAllStubbedConstants(),
'global_functions' => $codebase->functions->getAllStubbedFunctions(),
'global_functions' => $codebase->functions->getAllGlobalFunctions(),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpParser;
use Psalm\Aliases;
use Psalm\CodeLocation;
use Psalm\Codebase;
use Psalm\Config;
use Psalm\Exception\DocblockParseException;
Expand Down Expand Up @@ -186,11 +187,13 @@ private static function registerClassMapFunctionCall(
}

$config = Config::getInstance();
$loc = new CodeLocation($file_scanner, $node, null, true);

if ($functionlike_storage && !$config->hoist_constants) {
$functionlike_storage->defined_constants[$const_name] = $const_type;
} else {
$file_storage->constants[$const_name] = $const_type;
$file_storage->constants_location[$const_name] = $loc->getHash();
$file_storage->declaring_constants[$const_name] = $file_storage->file_path;
}

Expand All @@ -199,7 +202,7 @@ private static function registerClassMapFunctionCall(
|| $codebase->all_constants_global
) && (!defined($const_name) || !$const_type->isMixed())
) {
$codebase->addGlobalConstantType($const_name, $const_type);
$codebase->addGlobalConstantType($const_name, $const_type, $loc->getHash());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,13 @@ public function start(
&& $function_id
&& $storage instanceof FunctionStorage
) {
$lc_function_id = strtolower($function_id);
if ($this->codebase->all_functions_global
|| $this->codebase->register_stub_files
|| ($this->codebase->register_autoload_files
&& !$this->codebase->functions->hasStubbedFunction($function_id))
&& !$this->codebase->functions->hasGlobalFunction($lc_function_id))
) {
$this->codebase->functions->addGlobalFunction($function_id, $storage);
$this->codebase->functions->addGlobalFunction($lc_function_id, $storage);
}

$this->file_storage->functions[$function_id] = $storage;
Expand Down Expand Up @@ -928,7 +929,7 @@ private function createStorageForFunctionLike(
) {
if (isset($this->file_storage->functions[$function_id])
&& ($this->codebase->register_stub_files
|| !$this->codebase->functions->hasStubbedFunction($function_id))
|| !$this->codebase->functions->hasGlobalFunction($function_id))
) {
$this->codebase->functions->addGlobalFunction(
$function_id,
Expand Down
8 changes: 7 additions & 1 deletion src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,16 +290,22 @@ public function enterNode(PhpParser\Node $node): ?int
) ?? Type::getMixed();

$fq_const_name = Type::getFQCLNFromString($const->name->name, $this->aliases);
$loc = new CodeLocation($this->file_scanner, $const, null, true);

if (($this->codebase->register_stub_files
|| $this->codebase->register_autoload_files
|| $this->codebase->all_constants_global
) && (!defined($fq_const_name) || !$const_type->isMixed())
) {
$this->codebase->addGlobalConstantType($fq_const_name, $const_type);
$this->codebase->addGlobalConstantType(
$fq_const_name,
$const_type,
$loc->getHash(),
);
}

$this->file_storage->constants[$fq_const_name] = $const_type;
$this->file_storage->constants_location[$fq_const_name] = $loc->getHash();
$this->file_storage->declaring_constants[$fq_const_name] = $this->file_path;
}
} elseif ($node instanceof PhpParser\Node\Stmt\If_ && !$this->skip_if_descendants) {
Expand Down
3 changes: 2 additions & 1 deletion src/Psalm/Internal/Stubs/Generator/StubsGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static function getAll(

$all_function_names = [];

foreach ($codebase->functions->getAllStubbedFunctions() as $function_storage) {
foreach ($codebase->functions->getAllGlobalFunctions() as $function_storage) {
if ($function_storage->location
&& str_starts_with($function_storage->location->file_path, $psalm_base)
) {
Expand Down Expand Up @@ -123,6 +123,7 @@ public static function getAll(
}

foreach ($codebase->getAllStubbedConstants() as $fq_name => $type) {
$type = $type->type;
if ($type->isMixed()) {
continue;
}
Expand Down
7 changes: 7 additions & 0 deletions src/Psalm/Storage/FileStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ final class FileStorage
*/
public array $constants = [];

/**
* Just storing the hash of the location to save memory.
*
* @var array<string, string>
*/
public array $constants_location = [];

/** @var array<string, string> */
public array $declaring_constants = [];

Expand Down
Loading
Loading