Skip to content

Commit

Permalink
Merge pull request #11259 from vimeo/allFunctionsGlobal
Browse files Browse the repository at this point in the history
Add allFunctionsGlobal and allConstantsGlobal options
  • Loading branch information
danog authored Feb 7, 2025
2 parents a515bda + def7293 commit 4bc6e99
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 56 deletions.
2 changes: 2 additions & 0 deletions config.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
<xs:attribute name="ensureOverrideAttribute" type="xs:boolean" default="false" />
<xs:attribute name="findUnusedCode" type="xs:boolean" default="true" />
<xs:attribute name="disallowLiteralKeysOnUnshapedArrays" type="xs:boolean" default="false" />
<xs:attribute name="allFunctionsGlobal" type="xs:boolean" default="false" />
<xs:attribute name="allConstantsGlobal" type="xs:boolean" default="false" />
<xs:attribute name="findUnusedVariablesAndParams" type="xs:boolean" default="false" />
<xs:attribute name="findUnusedPsalmSuppress" type="xs:boolean" default="false" />
<xs:attribute name="findUnusedBaselineEntry" type="xs:boolean" default="true" />
Expand Down
17 changes: 17 additions & 0 deletions docs/running_psalm/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,23 @@ When `true`, Psalm will attempt to find all unused code (including unused variab
```
When `true`, Psalm will emit issues when using literal keys on unshaped arrays (useful to enforce usage of shaped arrays). Defaults to `false`.

#### allFunctionsGlobal
```xml
<psalm
allFunctionsGlobal="[bool]"
>
```
When `true`, Psalm will treat all functions declared in scanned files as available to all files, even if they aren't loaded by the Composer autoloader (useful for legacy codebases which make heavy use of `require`/`include`, instead of Composer's autoloader). Defaults to `false`.

#### allConstantsGlobal
```xml
<psalm
allConstantsGlobal="[bool]"
>
```

When `true`, Psalm will treat all constants declared in scanned files as available to all files, even if they aren't loaded by the Composer autoloader (useful for legacy codebases which make heavy use of `require`/`include`, instead of Composer's autoloader). Defaults to `false`.

#### findUnusedPsalmSuppress
```xml
<psalm
Expand Down
6 changes: 4 additions & 2 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.x-dev@897f124d9f0034b025e246809077e828f3537f7f">
<files psalm-version="6.x-dev@d5ee11cc5a050760e4ac0b0801f43c3780a66fda">
<file src="examples/TemplateChecker.php">
<PossiblyUndefinedIntArrayOffset>
<code><![CDATA[$comment_block->tags['variablesfrom'][0]]]></code>
Expand Down Expand Up @@ -61,6 +61,9 @@
<PossiblyNullReference>
<code><![CDATA[addAttribute]]></code>
</PossiblyNullReference>
<PossiblyUnusedMethod>
<code><![CDATA[hasStubFile]]></code>
</PossiblyUnusedMethod>
<PropertyTypeCoercion>
<code><![CDATA[$this]]></code>
</PropertyTypeCoercion>
Expand Down Expand Up @@ -1263,7 +1266,6 @@
<RiskyTruthyFalsyComparison>
<code><![CDATA[$composer_file_path]]></code>
<code><![CDATA[$function_storage->cased_name]]></code>
<code><![CDATA[$function_storage->cased_name]]></code>
</RiskyTruthyFalsyComparison>
</file>
<file src="src/Psalm/Internal/Codebase/TaintFlowGraph.php">
Expand Down
2 changes: 2 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
xsi:schemaLocation="https://getpsalm.org/schema/config config.xsd"
limitMethodComplexity="true"
errorBaseline="psalm-baseline.xml"
allFunctionsGlobal="true"
allConstantsGlobal="true"
findUnusedPsalmSuppress="true"
findUnusedBaselineEntry="true"
findUnusedIssueHandlerSuppression="true"
Expand Down
12 changes: 12 additions & 0 deletions src/Psalm/Codebase.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ final class Codebase
*/
public bool $register_stub_files = false;

public bool $all_functions_global = false;

public bool $all_constants_global = false;

public bool $find_unused_variables = false;

public Scanner $scanner;
Expand Down Expand Up @@ -585,6 +589,14 @@ public function getStubbedConstantType(string $const_id): ?Union
return self::$stubbed_constants[$const_id] ?? null;
}

/**
* @param array<string, Union> $stubs
*/
public function addGlobalConstantTypes(array $stubs): void
{
self::$stubbed_constants += $stubs;
}

/**
* @return array<string, Union>
*/
Expand Down
14 changes: 14 additions & 0 deletions src/Psalm/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ final class Config

public bool $literal_array_key_check = false;

public bool $all_functions_global = false;

public bool $all_constants_global = false;

public int $max_graph_size = 200;

public int $max_avg_path_length = 70;
Expand Down Expand Up @@ -1111,6 +1115,16 @@ private static function fromXmlAndPaths(
$config->literal_array_key_check = $attribute_text === 'true' || $attribute_text === '1';
}

if (isset($config_xml['allFunctionsGlobal'])) {
$attribute_text = (string) $config_xml['allFunctionsGlobal'];
$config->all_functions_global = $attribute_text === 'true' || $attribute_text === '1';
}

if (isset($config_xml['allConstantsGlobal'])) {
$attribute_text = (string) $config_xml['allConstantsGlobal'];
$config->all_constants_global = $attribute_text === 'true' || $attribute_text === '1';
}

if (isset($config_xml['findUnusedVariablesAndParams'])) {
$attribute_text = (string) $config_xml['findUnusedVariablesAndParams'];
$config->find_unused_variables = $attribute_text === 'true' || $attribute_text === '1';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ public static function checkFunctionExists(
} else {
IssueBuffer::maybeAdd(
new UndefinedFunction(
'Function ' . $cased_function_id . ' does not exist',
'Function ' . $cased_function_id . ' does not exist'
.', consider enabling the allFunctionsGlobal config option if scanning legacy codebases',
$code_location,
$function_id,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public static function analyze(
} elseif ($context->check_consts) {
IssueBuffer::maybeAdd(
new UndefinedConstant(
'Const ' . $const_name . ' is not defined',
'Const ' . $const_name . ' is not defined'.
', consider enabling the allConstantsGlobal config option if scanning legacy codebases',
new CodeLocation($statements_analyzer->getSource(), $stmt),
),
$statements_analyzer->getSuppressedIssues(),
Expand Down Expand Up @@ -151,10 +152,12 @@ public static function getGlobalConstType(
|| array_key_exists($const_name, $predefined_constants)
) {
switch ($const_name) {
case 'PHP_VERSION':
case 'DIRECTORY_SEPARATOR':
case 'PATH_SEPARATOR':
case 'PHP_EOL':
return Type::getSingleLetter();

case 'PHP_VERSION':
return Type::getNonEmptyString();

case 'PEAR_EXTENSION_DIR':
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/Cli/Psalm.php
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,8 @@ private static function configureProjectAnalyzer(
if ($config->literal_array_key_check) {
$project_analyzer->getCodebase()->literal_array_key_check = true;
}
$project_analyzer->getCodebase()->all_constants_global = $config->all_constants_global;
$project_analyzer->getCodebase()->all_functions_global = $config->all_functions_global;

if ($config->run_taint_analysis || $run_taint_analysis) {
$project_analyzer->trackTaintedInputs();
Expand Down
10 changes: 9 additions & 1 deletion src/Psalm/Internal/Codebase/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,21 @@ public function addGlobalFunction(string $function_id, FunctionStorage $storage)
self::$stubbed_functions[strtolower($function_id)] = $storage;
}

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

public function hasStubbedFunction(string $function_id): bool
{
return isset(self::$stubbed_functions[strtolower($function_id)]);
}

/**
* @return array<string, FunctionStorage>
* @return array<lowercase-string, FunctionStorage>
*/
public function getAllStubbedFunctions(): array
{
Expand Down
39 changes: 15 additions & 24 deletions src/Psalm/Internal/Codebase/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
use Psalm\Progress\Progress;
use Psalm\Storage\ClassLikeStorage;
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 @@ -78,7 +80,9 @@
* classlike_storage:array<string, ClassLikeStorage>,
* file_storage:array<lowercase-string, FileStorage>,
* new_file_content_hashes: array<string, string>,
* taint_data: ?TaintFlowGraph
* taint_data: ?TaintFlowGraph,
* global_constants: array<string, Union>,
* global_functions: array<lowercase-string, FunctionStorage>
* }
*/

Expand Down Expand Up @@ -352,6 +356,9 @@ private function scanFilePaths(int $pool_size): bool
$pool_data['new_file_content_hashes'],
);
}

$this->codebase->addGlobalConstantTypes($pool_data['global_constants']);
$this->codebase->functions->addGlobalFunctions($pool_data['global_functions']);
}
} else {
foreach ($files_to_scan as $file_path => $_) {
Expand All @@ -364,27 +371,6 @@ private function scanFilePaths(int $pool_size): bool
$this->codebase->statements_provider->parser_cache_provider->saveFileContentHashes();
}

foreach ($files_to_scan as $scanned_file) {
if ($this->config->hasStubFile($scanned_file)) {
$file_storage = $this->file_storage_provider->get($scanned_file);

foreach ($file_storage->functions as $function_storage) {
if ($function_storage->cased_name
&& !$this->codebase->functions->hasStubbedFunction($function_storage->cased_name)
) {
$this->codebase->functions->addGlobalFunction(
$function_storage->cased_name,
$function_storage,
);
}
}

foreach ($file_storage->constants as $name => $type) {
$this->codebase->addGlobalConstantType($name, $type);
}
}
}

$this->file_reference_provider->addClassLikeFiles($this->classlike_files);

return true;
Expand Down Expand Up @@ -519,7 +505,9 @@ private function scanFile(
$this->queueClassLikeForScanning($fq_classlike_name, false, false);
}

if ($this->codebase->register_autoload_files) {
if ($this->codebase->register_autoload_files
|| $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)
Expand All @@ -530,7 +518,10 @@ private function scanFile(
);
}
}

}
if ($this->codebase->register_autoload_files
|| $this->codebase->all_constants_global
) {
foreach ($file_storage->constants as $name => $type) {
$this->codebase->addGlobalConstantType($name, $type);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/Fork/ShutdownScannerTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public function run(Channel $channel, Cancellation $cancellation): mixed
? $statements_provider->parser_cache_provider->getNewFileContentHashes()
: [],
'taint_data' => $codebase->taint_flow_graph,
'global_constants' => $codebase->getAllStubbedConstants(),
'global_functions' => $codebase->functions->getAllStubbedFunctions(),
];
}
}
6 changes: 4 additions & 2 deletions src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,10 @@ private static function registerClassMapFunctionCall(
$file_storage->declaring_constants[$const_name] = $file_storage->file_path;
}

if (($codebase->register_stub_files || $codebase->register_autoload_files)
&& (!defined($const_name) || $const_type->isMixed())
if (($codebase->register_stub_files
|| $codebase->register_autoload_files
|| $codebase->all_constants_global
) && (!defined($const_name) || !$const_type->isMixed())
) {
$codebase->addGlobalConstantType($const_name, $const_type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ public function start(
&& $function_id
&& $storage instanceof FunctionStorage
) {
if ($this->codebase->register_stub_files
if ($this->codebase->all_functions_global
|| $this->codebase->register_stub_files
|| ($this->codebase->register_autoload_files
&& !$this->codebase->functions->hasStubbedFunction($function_id))
) {
Expand Down Expand Up @@ -914,7 +915,10 @@ private function createStorageForFunctionLike(

$storage = $this->storage = new FunctionStorage();

if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
if ($this->codebase->register_stub_files
|| $this->codebase->register_autoload_files
|| $this->codebase->all_functions_global
) {
if (isset($this->file_storage->functions[$function_id])
&& ($this->codebase->register_stub_files
|| !$this->codebase->functions->hasStubbedFunction($function_id))
Expand Down
7 changes: 6 additions & 1 deletion src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use UnexpectedValueException;

use function array_pop;
use function defined;
use function end;
use function explode;
use function in_array;
Expand Down Expand Up @@ -288,7 +289,11 @@ public function enterNode(PhpParser\Node $node): ?int

$fq_const_name = Type::getFQCLNFromString($const->name->name, $this->aliases);

if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
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);
}

Expand Down
Loading

0 comments on commit 4bc6e99

Please sign in to comment.