diff --git a/src/Psalm/Checker/ProjectChecker.php b/src/Psalm/Checker/ProjectChecker.php index bf66f64b2..de5a6388a 100644 --- a/src/Psalm/Checker/ProjectChecker.php +++ b/src/Psalm/Checker/ProjectChecker.php @@ -643,6 +643,11 @@ class ProjectChecker $included_file_storage->declaring_function_ids, $storage->declaring_function_ids ); + + $storage->declaring_constants = array_merge( + $included_file_storage->declaring_constants, + $storage->declaring_constants + ); } $storage->populated = true; diff --git a/src/Psalm/Checker/Statements/Expression/FetchChecker.php b/src/Psalm/Checker/Statements/Expression/FetchChecker.php index 8168da37d..7142a86fd 100644 --- a/src/Psalm/Checker/Statements/Expression/FetchChecker.php +++ b/src/Psalm/Checker/Statements/Expression/FetchChecker.php @@ -392,6 +392,7 @@ class FetchChecker default: $const_type = $statements_checker->getConstType( + $statements_checker, $const_name, $stmt->name instanceof PhpParser\Node\Name\FullyQualified, $context diff --git a/src/Psalm/Checker/StatementsChecker.php b/src/Psalm/Checker/StatementsChecker.php index 4e218412d..df5fa94c8 100644 --- a/src/Psalm/Checker/StatementsChecker.php +++ b/src/Psalm/Checker/StatementsChecker.php @@ -35,11 +35,6 @@ class StatementsChecker extends SourceChecker implements StatementsSource */ private $all_vars = []; - /** - * @var array> - */ - public static $user_constants = []; - /** * @var array */ @@ -737,8 +732,12 @@ class StatementsChecker extends SourceChecker implements StatementsSource * * @return Type\Union|null */ - public function getConstType($const_name, $is_fully_qualified, Context $context) - { + public function getConstType( + StatementsChecker $statements_checker, + $const_name, + $is_fully_qualified, + Context $context + ) { $fq_const_name = null; $aliased_constants = $this->getAliases()->constants; @@ -769,6 +768,16 @@ class StatementsChecker extends SourceChecker implements StatementsSource return $context->vars_in_scope[$const_name]; } + $file_path = $statements_checker->getFilePath(); + + $file_storage = FileChecker::$storage[strtolower($file_path)]; + + if (isset($file_storage->declaring_constants[$const_name])) { + $constant_file_path = $file_storage->declaring_constants[$const_name]; + + return FileChecker::$storage[strtolower($constant_file_path)]->constants[$const_name]; + } + $predefined_constants = Config::getInstance()->getPredefinedConstants(); if (isset($predefined_constants[$fq_const_name ?: $const_name])) { @@ -796,8 +805,6 @@ class StatementsChecker extends SourceChecker implements StatementsSource if ($this->source instanceof NamespaceChecker) { $this->source->setConstType($const_name, $const_type); - } else { - self::$user_constants[$this->getFilePath()][$const_name] = $const_type; } } @@ -1060,7 +1067,6 @@ class StatementsChecker extends SourceChecker implements StatementsSource */ public static function clearCache() { - self::$user_constants = []; self::$stub_constants = []; ExpressionChecker::clearCache(); diff --git a/src/Psalm/Storage/FileStorage.php b/src/Psalm/Storage/FileStorage.php index 2f0b1f24b..60ffc03d8 100644 --- a/src/Psalm/Storage/FileStorage.php +++ b/src/Psalm/Storage/FileStorage.php @@ -12,13 +12,21 @@ class FileStorage public $file_path; /** - * @var array + * @var array */ public $functions = []; /** @var array */ public $declaring_function_ids = []; + /** + * @var array + */ + public $constants = []; + + /** @var array */ + public $declaring_constants = []; + /** @var array */ public $included_file_paths = []; diff --git a/src/Psalm/Visitor/DependencyFinderVisitor.php b/src/Psalm/Visitor/DependencyFinderVisitor.php index f5b24d236..1b9e2b19d 100644 --- a/src/Psalm/Visitor/DependencyFinderVisitor.php +++ b/src/Psalm/Visitor/DependencyFinderVisitor.php @@ -299,12 +299,20 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P $this->queue_strings_as_possible_type = true; } - if ($function_id === 'define' && $this->functionlike_storage) { + if ($function_id === 'define') { $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 instanceof PhpParser\Node\Scalar\String_ && $second_arg_value) { - $this->functionlike_storage->defined_constants[$first_arg_value->value] = - StatementsChecker::getSimpleType($second_arg_value) ?: Type::getMixed(); + $const_type = StatementsChecker::getSimpleType($second_arg_value) ?: Type::getMixed(); + $const_name = $first_arg_value->value; + + if ($this->functionlike_storage) { + $this->functionlike_storage->defined_constants[$const_name] = $const_type; + } else { + $file_storage = FileChecker::$storage[strtolower($this->file_path)]; + $file_storage->constants[$const_name] = $const_type; + $file_storage->declaring_constants[$const_name] = $this->file_path; + } } } } @@ -363,10 +371,16 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P } } } elseif ($node instanceof PhpParser\Node\Stmt\Const_) { - if ($this->project_checker->register_global_functions) { - foreach ($node->consts as $const) { - StatementsChecker::$stub_constants[$const->name] = - StatementsChecker::getSimpleType($const->value) ?: Type::getMixed(); + foreach ($node->consts as $const) { + $const_type = StatementsChecker::getSimpleType($const->value) ?: Type::getMixed(); + + if ($this->project_checker->register_global_functions) { + StatementsChecker::$stub_constants[$const->name] = $const_type; + } else { + $file_storage = FileChecker::$storage[strtolower($this->file_path)]; + + $file_storage->constants[$const->name] = $const_type; + $file_storage->declaring_constants[$const->name] = $this->file_path; } } } diff --git a/tests/IncludeTest.php b/tests/IncludeTest.php index 74db65f8b..7040aa314 100644 --- a/tests/IncludeTest.php +++ b/tests/IncludeTest.php @@ -143,6 +143,21 @@ class IncludeTest extends TestCase getcwd() . DIRECTORY_SEPARATOR . 'file2.php', ], ], + 'requireConstant' => [ + 'files' => [ + getcwd() . DIRECTORY_SEPARATOR . 'file1.php' => ' ' [ + getcwd() . DIRECTORY_SEPARATOR . 'file2.php', + ], + ], 'requireNamespacedWithUse' => [ 'files' => [ getcwd() . DIRECTORY_SEPARATOR . 'file1.php' => '