diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index 74405aa63..1f938ab48 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -21,6 +21,7 @@ use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\Analyzer\NamespaceAnalyzer; use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer; 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\Codebase\InternalCallMapHandler; use Psalm\Internal\Provider\ClassLikeStorageProvider; @@ -1004,6 +1005,13 @@ class Codebase return 'getSignature(true); } + if (strpos($symbol, '$') === 0) { + $type = VariableFetchAnalyzer::getGlobalType($symbol); + if (!$type->isMixed()) { + return 'classlike_storage_provider->get($symbol); return 'abstract ? 'abstract ' : '') . 'class ' . $storage->name; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php index e55c71394..5a3c5e4e6 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php @@ -165,6 +165,12 @@ class VariableFetchAnalyzer $context->vars_in_scope[$var_name] = clone $type; $context->vars_possibly_in_scope[$var_name] = true; + $codebase->analyzer->addNodeReference( + $statements_analyzer->getFilePath(), + $stmt, + $var_name + ); + return true; } diff --git a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php index 9edc4af37..f6dd98e41 100644 --- a/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php @@ -5,6 +5,7 @@ use PhpParser; use Psalm\Internal\Analyzer\FunctionLikeAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer; +use Psalm\Internal\DataFlow\DataFlowNode; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Issue\InvalidGlobal; @@ -56,6 +57,23 @@ class GlobalAnalyzer $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 + ); } } } diff --git a/tests/LanguageServer/SymbolLookupTest.php b/tests/LanguageServer/SymbolLookupTest.php index 0d3e10e3a..5448cf585 100644 --- a/tests/LanguageServer/SymbolLookupTest.php +++ b/tests/LanguageServer/SymbolLookupTest.php @@ -39,6 +39,9 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase public function testSimpleSymbolLookup(): void { + $codebase = $this->project_analyzer->getCodebase(); + $config = $codebase->config; + $config->globals['$my_global'] = 'string'; $this->addFile( 'somefile.php', 'project_analyzer, 'somefile.php', 'somefile.php'); @@ -83,7 +88,8 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase $this->assertSame('getSymbolInformation('somefile.php', 'B\A::BANANA')); $this->assertSame("getSymbolInformation('somefile.php', 'B\baz()')); $this->assertSame("getSymbolInformation('somefile.php', 'B\qux()')); - $this->assertSame("getSymbolInformation('somefile.php', 'B\APPLE')); + $this->assertSame("", $codebase->getSymbolInformation('somefile.php', '$_SERVER')); + $this->assertSame("getSymbolInformation('somefile.php', '$my_global')); } public function testSimpleSymbolLookupGlobalConst(): void @@ -279,6 +285,36 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase $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', + '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 { $codebase = $this->project_analyzer->getCodebase(); diff --git a/tests/UnusedVariableTest.php b/tests/UnusedVariableTest.php index af97c8991..85435e732 100644 --- a/tests/UnusedVariableTest.php +++ b/tests/UnusedVariableTest.php @@ -3114,6 +3114,15 @@ class UnusedVariableTest extends TestCase };', 'error_message' => 'UnusedVariable', ], + 'globalVariableUsage' => [ + ' 'UnusedVariable', + ], ]; } }