mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 12:24:49 +01:00
Track references on global variables (#5122)
* Track references on global variables Add global type references to the type map, and fix up unused detection on global variables. * Add null assertions * PHPCS
This commit is contained in:
parent
6953658961
commit
417704ac23
@ -21,6 +21,7 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
|||||||
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
|
||||||
|
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
|
||||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||||
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
use Psalm\Internal\Codebase\InternalCallMapHandler;
|
||||||
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
||||||
@ -1004,6 +1005,13 @@ class Codebase
|
|||||||
return '<?php ' . $function->getSignature(true);
|
return '<?php ' . $function->getSignature(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strpos($symbol, '$') === 0) {
|
||||||
|
$type = VariableFetchAnalyzer::getGlobalType($symbol);
|
||||||
|
if (!$type->isMixed()) {
|
||||||
|
return '<?php ' . $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$storage = $this->classlike_storage_provider->get($symbol);
|
$storage = $this->classlike_storage_provider->get($symbol);
|
||||||
return '<?php ' . ($storage->abstract ? 'abstract ' : '') . 'class ' . $storage->name;
|
return '<?php ' . ($storage->abstract ? 'abstract ' : '') . 'class ' . $storage->name;
|
||||||
|
@ -165,6 +165,12 @@ class VariableFetchAnalyzer
|
|||||||
$context->vars_in_scope[$var_name] = clone $type;
|
$context->vars_in_scope[$var_name] = clone $type;
|
||||||
$context->vars_possibly_in_scope[$var_name] = true;
|
$context->vars_possibly_in_scope[$var_name] = true;
|
||||||
|
|
||||||
|
$codebase->analyzer->addNodeReference(
|
||||||
|
$statements_analyzer->getFilePath(),
|
||||||
|
$stmt,
|
||||||
|
$var_name
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use PhpParser;
|
|||||||
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||||
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
|
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
|
||||||
|
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||||
use Psalm\CodeLocation;
|
use Psalm\CodeLocation;
|
||||||
use Psalm\Context;
|
use Psalm\Context;
|
||||||
use Psalm\Issue\InvalidGlobal;
|
use Psalm\Issue\InvalidGlobal;
|
||||||
@ -56,6 +57,23 @@ class GlobalAnalyzer
|
|||||||
|
|
||||||
$context->byref_constraints[$var_id] = new \Psalm\Internal\ReferenceConstraint();
|
$context->byref_constraints[$var_id] = new \Psalm\Internal\ReferenceConstraint();
|
||||||
}
|
}
|
||||||
|
$assignment_node = DataFlowNode::getForAssignment(
|
||||||
|
$var_id,
|
||||||
|
new CodeLocation($statements_analyzer, $var)
|
||||||
|
);
|
||||||
|
$context->vars_in_scope[$var_id]->parent_nodes = [
|
||||||
|
$assignment_node->id => $assignment_node,
|
||||||
|
];
|
||||||
|
$statements_analyzer->registerVariable(
|
||||||
|
$var_id,
|
||||||
|
new CodeLocation($statements_analyzer, $var),
|
||||||
|
$context->branch_point
|
||||||
|
);
|
||||||
|
$statements_analyzer->getCodebase()->analyzer->addNodeReference(
|
||||||
|
$statements_analyzer->getFilePath(),
|
||||||
|
$var,
|
||||||
|
$var_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
|||||||
|
|
||||||
public function testSimpleSymbolLookup(): void
|
public function testSimpleSymbolLookup(): void
|
||||||
{
|
{
|
||||||
|
$codebase = $this->project_analyzer->getCodebase();
|
||||||
|
$config = $codebase->config;
|
||||||
|
$config->globals['$my_global'] = 'string';
|
||||||
$this->addFile(
|
$this->addFile(
|
||||||
'somefile.php',
|
'somefile.php',
|
||||||
'<?php
|
'<?php
|
||||||
@ -68,7 +71,9 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
|||||||
|
|
||||||
function qux(int $a, int $b) : int {
|
function qux(int $a, int $b) : int {
|
||||||
return $a + $b;
|
return $a + $b;
|
||||||
}'
|
}
|
||||||
|
|
||||||
|
$_SERVER;'
|
||||||
);
|
);
|
||||||
|
|
||||||
new FileAnalyzer($this->project_analyzer, 'somefile.php', 'somefile.php');
|
new FileAnalyzer($this->project_analyzer, 'somefile.php', 'somefile.php');
|
||||||
@ -83,7 +88,8 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
|||||||
$this->assertSame('<?php BANANA', $codebase->getSymbolInformation('somefile.php', 'B\A::BANANA'));
|
$this->assertSame('<?php BANANA', $codebase->getSymbolInformation('somefile.php', 'B\A::BANANA'));
|
||||||
$this->assertSame("<?php function B\baz(\n int \$a\n) : int", $codebase->getSymbolInformation('somefile.php', 'B\baz()'));
|
$this->assertSame("<?php function B\baz(\n int \$a\n) : int", $codebase->getSymbolInformation('somefile.php', 'B\baz()'));
|
||||||
$this->assertSame("<?php function B\qux(\n int \$a,\n int \$b\n) : int", $codebase->getSymbolInformation('somefile.php', 'B\qux()'));
|
$this->assertSame("<?php function B\qux(\n int \$a,\n int \$b\n) : int", $codebase->getSymbolInformation('somefile.php', 'B\qux()'));
|
||||||
$this->assertSame("<?php const B\APPLE string", $codebase->getSymbolInformation('somefile.php', 'B\APPLE'));
|
$this->assertSame("<?php array<array-key, mixed>", $codebase->getSymbolInformation('somefile.php', '$_SERVER'));
|
||||||
|
$this->assertSame("<?php string", $codebase->getSymbolInformation('somefile.php', '$my_global'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSimpleSymbolLookupGlobalConst(): void
|
public function testSimpleSymbolLookupGlobalConst(): void
|
||||||
@ -279,6 +285,36 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
|||||||
$this->assertSame('B\A::foo()', $symbol_at_position[0]);
|
$this->assertSame('B\A::foo()', $symbol_at_position[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetSymbolPositionGlobalVariable(): void
|
||||||
|
{
|
||||||
|
$codebase = $this->project_analyzer->getCodebase();
|
||||||
|
$codebase->reportUnusedVariables();
|
||||||
|
$config = $codebase->config;
|
||||||
|
$config->throw_exception = false;
|
||||||
|
$config->globals['$my_global'] = 'string';
|
||||||
|
|
||||||
|
$this->addFile(
|
||||||
|
'somefile.php',
|
||||||
|
'<?php
|
||||||
|
function foo() : void {
|
||||||
|
global $my_global;
|
||||||
|
echo $my_global;
|
||||||
|
}'
|
||||||
|
);
|
||||||
|
|
||||||
|
$codebase->file_provider->openFile('somefile.php');
|
||||||
|
$codebase->scanFiles();
|
||||||
|
$this->analyzeFile('somefile.php', new Context());
|
||||||
|
|
||||||
|
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(2, 31));
|
||||||
|
$this->assertNotNull($symbol_at_position);
|
||||||
|
$this->assertSame('$my_global', $symbol_at_position[0]);
|
||||||
|
|
||||||
|
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(3, 28));
|
||||||
|
$this->assertNotNull($symbol_at_position);
|
||||||
|
$this->assertSame('73-82:string', $symbol_at_position[0]);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetSymbolPositionNullableArg(): void
|
public function testGetSymbolPositionNullableArg(): void
|
||||||
{
|
{
|
||||||
$codebase = $this->project_analyzer->getCodebase();
|
$codebase = $this->project_analyzer->getCodebase();
|
||||||
|
@ -3114,6 +3114,15 @@ class UnusedVariableTest extends TestCase
|
|||||||
};',
|
};',
|
||||||
'error_message' => 'UnusedVariable',
|
'error_message' => 'UnusedVariable',
|
||||||
],
|
],
|
||||||
|
'globalVariableUsage' => [
|
||||||
|
'<?php
|
||||||
|
$a = "hello";
|
||||||
|
function example() : void {
|
||||||
|
global $a;
|
||||||
|
}
|
||||||
|
example();',
|
||||||
|
'error_message' => 'UnusedVariable',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user