1
0
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:
Brown 2019-02-21 18:19:12 -05:00
parent 653555a8cc
commit 154e1fa38b
4 changed files with 104 additions and 25 deletions

View File

@ -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(),

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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',
],
];
}
}