1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 12:24:49 +01:00

Allow setting globals in config

This commit is contained in:
bugreportuser 2019-03-05 17:08:41 -06:00 committed by Matthew Brown
parent 9442805763
commit 056e5a5b1e
5 changed files with 132 additions and 21 deletions

View File

@ -18,6 +18,7 @@
<xs:element name="forbiddenFunctions" type="ExitFunctionsType" minOccurs="0" maxOccurs="1" />
<xs:element name="issueHandlers" type="IssueHandlersType" minOccurs="0" maxOccurs="1" />
<xs:element name="ignoreExceptions" type="ExceptionsType" minOccurs="0" maxOccurs="1" />
<xs:element name="globals" type="GlobalsType" minOccurs="0" maxOccurs="1" />
</xs:choice>
<xs:attribute name="name" type="xs:string" />
@ -431,4 +432,15 @@
<xs:attribute name="errorLevel" type="xs:string" />
</xs:complexType>
<xs:complexType name="GlobalsType">
<xs:sequence>
<xs:element name="var" maxOccurs="unbounded" type="IdentifierType" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="IdentifierType">
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
</xs:complexType>
</xs:schema>

View File

@ -346,6 +346,11 @@ class Config
/** @var string|null */
public $error_baseline = null;
/**
* @var array<string, string>
*/
public $globals = [];
protected function __construct()
{
self::$instance = $this;
@ -760,6 +765,13 @@ class Config
}
}
if (isset($config_xml->globals) && isset($config_xml->globals->var)) {
/** @var \SimpleXMLElement $var */
foreach ($config_xml->globals->var as $var) {
$config->globals[(string) $var['name']] = (string) $var['type'];
}
}
return $config;
}

View File

@ -110,6 +110,20 @@ class VariableFetchAnalyzer
return null;
}
if ($context->is_global && is_string($stmt->name)) {
$var_name = '$' . $stmt->name;
if (!$context->hasVariable($var_name, $statements_analyzer)) {
$type = $statements_analyzer->getGlobalType($stmt->name);
if ($type) {
$context->vars_in_scope[$var_name] = $type;
$context->vars_possibly_in_scope[$var_name] = true;
$stmt->inferredType = clone $type;
return null;
}
}
}
if (in_array(
$stmt->name,
[
@ -140,27 +154,6 @@ class VariableFetchAnalyzer
return null;
}
if ($context->is_global && ($stmt->name === 'argv' || $stmt->name === 'argc')) {
$var_name = '$' . $stmt->name;
if (!$context->hasVariable($var_name, $statements_analyzer)) {
if ($stmt->name === 'argv') {
$context->vars_in_scope[$var_name] = new Type\Union([
new Type\Atomic\TArray([
Type::getInt(),
Type::getString(),
]),
]);
} else {
$context->vars_in_scope[$var_name] = Type::getInt();
}
}
$context->vars_possibly_in_scope[$var_name] = true;
$stmt->inferredType = clone $context->vars_in_scope[$var_name];
return null;
}
if (!is_string($stmt->name)) {
return ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context);
}

View File

@ -1530,4 +1530,26 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
return $const_name;
}
/**
* @return Type\Union|null
*/
public function getGlobalType(string $name)
{
$config = Config::getInstance();
if (isset($config->globals[$name])) {
return Type::parseString($config->globals[$name]);
}
if ($name === 'argv') {
return new Type\Union([
new Type\Atomic\TArray([Type::getInt(), Type::getString()]),
]);
}
if ($name === 'argc') {
return Type::getInt();
}
}
}

View File

@ -1117,4 +1117,76 @@ class ConfigTest extends TestCase
);
}
}
/**
* @return void
*/
public function testGlobals()
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'<?xml version="1.0"?>
<psalm>
<globals>
<var name="glob1" type="string" />
<var name="glob2" type="array{str:string}" />
<var name="glob3" type="ns\Clazz" />
<var name="glob4" type="string|null" />
</globals>
</psalm>'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'<?php
namespace {
ord($glob1);
ord($glob2["str"]);
$glob3->func();
assert($glob4 !== null);
ord($glob4);
function example1(): void {
global $glob1, $glob2, $glob3, $glob4;
ord($glob1);
ord($glob2["str"]);
$glob3->func();
ord($glob4);
}
$glob1 = 0;
error_reporting($glob1);
function example2(): void {
global $glob1, $glob2, $glob3;
error_reporting($glob1);
ord($glob2["str"]);
$glob3->func();
}
}
namespace ns {
ord($glob1);
ord($glob2["str"]);
$glob3->func();
class Clazz {
public function func(): void {}
}
function example3(): void {
global $glob1, $glob2, $glob3;
ord($glob1);
ord($glob2["str"]);
$glob3->func();
}
}'
);
$this->analyzeFile($file_path, new Context());
}
}