mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Fix #1375 - improve treatment of dynamically-declared namespaces
This commit is contained in:
parent
653555a8cc
commit
154e1fa38b
@ -608,14 +608,18 @@ class FunctionCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expressio
|
||||
}
|
||||
} elseif ($function->parts === ['define']) {
|
||||
if ($first_arg) {
|
||||
$const_name = StatementsAnalyzer::getConstName($first_arg->value);
|
||||
$fq_const_name = StatementsAnalyzer::getConstName(
|
||||
$first_arg->value,
|
||||
$codebase,
|
||||
$statements_analyzer->getAliases()
|
||||
);
|
||||
|
||||
if ($const_name !== null) {
|
||||
if ($fq_const_name !== null) {
|
||||
$second_arg = $stmt->args[1];
|
||||
ExpressionAnalyzer::analyze($statements_analyzer, $second_arg->value, $context);
|
||||
|
||||
$statements_analyzer->setConstType(
|
||||
$const_name,
|
||||
$fq_const_name,
|
||||
isset($second_arg->value->inferredType) ?
|
||||
$second_arg->value->inferredType :
|
||||
Type::getMixed(),
|
||||
|
@ -2,6 +2,7 @@
|
||||
namespace Psalm\Internal\Analyzer;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Aliases;
|
||||
use Psalm\Internal\Analyzer\Statements\Block\DoAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Block\ForAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
||||
@ -144,7 +145,7 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
&& $stmt->expr->name->parts === ['define']
|
||||
&& isset($stmt->expr->args[1])
|
||||
) {
|
||||
$const_name = static::getConstName($stmt->expr->args[0]->value);
|
||||
$const_name = static::getConstName($stmt->expr->args[0]->value, $codebase, $this->getAliases());
|
||||
if ($const_name !== null) {
|
||||
$this->setConstType(
|
||||
$const_name,
|
||||
@ -909,6 +910,35 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
) {
|
||||
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
|
||||
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
|
||||
$left = self::getSimpleType(
|
||||
$codebase,
|
||||
$stmt->left,
|
||||
$aliases,
|
||||
$file_source,
|
||||
$existing_class_constants,
|
||||
$fq_classlike_name
|
||||
);
|
||||
$right = self::getSimpleType(
|
||||
$codebase,
|
||||
$stmt->right,
|
||||
$aliases,
|
||||
$file_source,
|
||||
$existing_class_constants,
|
||||
$fq_classlike_name
|
||||
);
|
||||
|
||||
if ($left
|
||||
&& $right
|
||||
&& $left->isSingleStringLiteral()
|
||||
&& $right->isSingleStringLiteral()
|
||||
) {
|
||||
$result = $left->getSingleStringLiteral()->value . $right->getSingleStringLiteral()->value;
|
||||
|
||||
if (strlen($result) < 50) {
|
||||
return Type::getString($result);
|
||||
}
|
||||
}
|
||||
|
||||
return Type::getString();
|
||||
}
|
||||
|
||||
@ -993,11 +1023,17 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
return Type::getTrue();
|
||||
} elseif (strtolower($stmt->name->parts[0]) === 'null') {
|
||||
return Type::getNull();
|
||||
} elseif ($stmt->name->parts[0] === '__NAMESPACE__') {
|
||||
return Type::getString($aliases->namespace);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) {
|
||||
return Type::getString($aliases->namespace);
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
|
||||
if ($stmt->class instanceof PhpParser\Node\Name
|
||||
&& $stmt->name instanceof PhpParser\Node\Identifier
|
||||
@ -1335,6 +1371,12 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
return $file_storage_provider->get($constant_file_path)->constants[$const_name];
|
||||
}
|
||||
|
||||
if (isset($file_storage->declaring_constants[$fq_const_name])) {
|
||||
$constant_file_path = $file_storage->declaring_constants[$fq_const_name];
|
||||
|
||||
return $file_storage_provider->get($constant_file_path)->constants[$fq_const_name];
|
||||
}
|
||||
|
||||
return ConstFetchAnalyzer::getGlobalConstType($codebase, $fq_const_name, $const_name);
|
||||
}
|
||||
|
||||
@ -1470,7 +1512,7 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public static function getConstName($first_arg_value)
|
||||
public static function getConstName($first_arg_value, Codebase $codebase, Aliases $aliases)
|
||||
{
|
||||
$const_name = null;
|
||||
|
||||
@ -1480,12 +1522,10 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
if ($first_arg_value->inferredType->isSingleStringLiteral()) {
|
||||
$const_name = $first_arg_value->inferredType->getSingleStringLiteral()->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($const_name !== null) {
|
||||
$namespace_pos = strrpos($const_name, '\\');
|
||||
if (false !== $namespace_pos) {
|
||||
$const_name = substr($const_name, $namespace_pos + 1);
|
||||
} else {
|
||||
$simple_type = self::getSimpleType($codebase, $first_arg_value, $aliases);
|
||||
if ($simple_type && $simple_type->isSingleStringLiteral()) {
|
||||
$const_name = $simple_type->getSingleStringLiteral()->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,7 +589,8 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
$first_arg_value = isset($node->args[0]) ? $node->args[0]->value : null;
|
||||
$second_arg_value = isset($node->args[1]) ? $node->args[1]->value : null;
|
||||
if ($first_arg_value && $second_arg_value) {
|
||||
$const_name = StatementsAnalyzer::getConstName($first_arg_value);
|
||||
$const_name = StatementsAnalyzer::getConstName($first_arg_value, $this->codebase, $this->aliases);
|
||||
|
||||
if ($const_name !== null) {
|
||||
$const_type = StatementsAnalyzer::getSimpleType(
|
||||
$this->codebase,
|
||||
|
@ -327,27 +327,51 @@ class ConstantTest extends TestCase
|
||||
namespace Foo;
|
||||
echo DIRECTORY_SEPARATOR;',
|
||||
],
|
||||
'constantDefinedInNamespace' => [
|
||||
'constantDefinedInRootNamespace' => [
|
||||
'<?php
|
||||
namespace {
|
||||
define("ns1\\cons1", 0);
|
||||
define("ns1\\cons1", 0);
|
||||
|
||||
echo \ns1\cons1;
|
||||
echo ns1\cons1;
|
||||
}
|
||||
echo \ns1\cons1;
|
||||
echo ns1\cons1;
|
||||
}',
|
||||
],
|
||||
'constantDynamicallyDefinedInNamespaceReferencedInSame' => [
|
||||
'<?php
|
||||
namespace ns2 {
|
||||
define(__NAMESPACE__."\\cons2", 0);
|
||||
define(__NAMESPACE__."\\cons2", 0);
|
||||
|
||||
echo \ns2\cons2;
|
||||
echo cons2;
|
||||
}
|
||||
echo \ns2\cons2;
|
||||
echo cons2;
|
||||
}',
|
||||
],
|
||||
'constantDynamicallyDefinedInNamespaceReferencedInRoot' => [
|
||||
'<?php
|
||||
namespace ns2 {
|
||||
echo \ns2\cons2;
|
||||
echo cons2;
|
||||
define(__NAMESPACE__."\\cons2", 0);
|
||||
}
|
||||
namespace {
|
||||
echo \ns2\cons2;
|
||||
echo ns2\cons2;
|
||||
echo \ns2\cons2;
|
||||
echo ns2\cons2;
|
||||
}',
|
||||
],
|
||||
'constantExplicitlyDefinedInNamespaceReferencedInSame' => [
|
||||
'<?php
|
||||
namespace ns2 {
|
||||
define("ns2\\cons2", 0);
|
||||
|
||||
echo \ns2\cons2;
|
||||
echo cons2;
|
||||
}',
|
||||
],
|
||||
'constantExplicitlyDefinedInNamespaceReferencedInRoot' => [
|
||||
'<?php
|
||||
namespace ns2 {
|
||||
define("ns2\\cons2", 0);
|
||||
}
|
||||
namespace {
|
||||
echo \ns2\cons2;
|
||||
echo ns2\cons2;
|
||||
}',
|
||||
],
|
||||
];
|
||||
@ -446,6 +470,16 @@ class ConstantTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'InvalidReturnStatement',
|
||||
],
|
||||
'outOfScopeDefinedConstant' => [
|
||||
'<?php
|
||||
namespace {
|
||||
define("A\\B", 0);
|
||||
}
|
||||
namespace C {
|
||||
echo A\B;
|
||||
}',
|
||||
'UndefinedConstant',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user