diff --git a/composer.json b/composer.json index 914304006aa..c45f194c036 100644 --- a/composer.json +++ b/composer.json @@ -118,6 +118,7 @@ ], "verify-callmap": "phpunit tests/Internal/Codebase/InternalCallMapHandlerTest.php", "psalm": "@php ./psalm", + "psalm-set-baseline": "@php ./psalm --set-baseline=psalm-baseline.xml", "tests": [ "@lint", "@cs", diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 562b94c110e..999d2e210d2 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $comment_block->tags['variablesfrom'][0] @@ -271,6 +271,12 @@ $function_callables[0] + + + $storage->trait_alias_map + $storage->trait_alias_map + + $property_name @@ -362,6 +368,9 @@ + + $storage->trait_alias_map + $l[4] $r[4] @@ -537,10 +546,6 @@ replace replace - - TTypeParams|null - TTypeParams|null - $this->type_params[1] diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index c18a2c7c49d..6efcabdf9ec 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -24,6 +24,7 @@ use UnitEnum; use function array_filter; +use function array_flip; use function array_intersect_key; use function array_keys; use function array_merge; @@ -439,6 +440,7 @@ private function populateDataFromTrait( $this->populateClassLikeStorage($trait_storage, $dependent_classlikes); + $this->inheritConstantsFromParent($storage, $trait_storage); $this->inheritMethodsFromParent($storage, $trait_storage); $this->inheritPropertiesFromParent($storage, $trait_storage); @@ -874,6 +876,40 @@ private function populateFileStorage(FileStorage $storage, array $dependent_file $storage->populated = true; } + private function inheritConstantsFromParent( + ClassLikeStorage $storage, + ClassLikeStorage $parent_storage + ): void { + foreach ($parent_storage->constants as $constant_name => $class_constant_storage) { + if ($parent_storage->is_trait) { + $trait_alias_map_cased = array_flip($storage->trait_alias_map_cased); + if (isset($trait_alias_map_cased[$constant_name])) { + $aliased_constant_name_lc = strtolower($trait_alias_map_cased[$constant_name]); + $aliased_constant_name = $trait_alias_map_cased[$constant_name]; + } else { + $aliased_constant_name_lc = strtolower($constant_name); + $aliased_constant_name = $constant_name; + } + $visibility = $storage->trait_visibility_map[$aliased_constant_name_lc] ?? $class_constant_storage->visibility; + $final = $storage->trait_final_map[$aliased_constant_name_lc] ?? $class_constant_storage->final; + $storage->constants[$aliased_constant_name] = new ClassConstantStorage( + $class_constant_storage->type, + $class_constant_storage->inferred_type, + $visibility, + $class_constant_storage->location, + $class_constant_storage->type_location, + $class_constant_storage->stmt_location, + $class_constant_storage->deprecated, + $final, + $class_constant_storage->unresolved_node, + $class_constant_storage->attributes, + $class_constant_storage->suppressed_issues, + $class_constant_storage->description, + ); + } + } + } + protected function inheritMethodsFromParent( ClassLikeStorage $storage, ClassLikeStorage $parent_storage @@ -890,7 +926,7 @@ protected function inheritMethodsFromParent( $aliased_method_names = [$method_name_lc]; if ($parent_storage->is_trait - && $storage->trait_alias_map + && $storage->trait_alias_map_cased ) { $aliased_method_names = array_merge( $aliased_method_names, @@ -960,7 +996,7 @@ protected function inheritMethodsFromParent( $aliased_method_names = [$method_name_lc]; if ($parent_storage->is_trait - && $storage->trait_alias_map + && $storage->trait_alias_map_cased ) { $aliased_method_names = array_merge( $aliased_method_names, diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 52821adfb91..12bd2344539 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -843,10 +843,6 @@ public function handleTraitUse(PhpParser\Node\Stmt\TraitUse $node): void throw new UnexpectedValueException('bad'); } - $method_map = $storage->trait_alias_map ?: []; - $visibility_map = $storage->trait_visibility_map ?: []; - $final_map = $storage->trait_final_map ?: []; - foreach ($node->adaptations as $adaptation) { if ($adaptation instanceof PhpParser\Node\Stmt\TraitUseAdaptation\Alias) { $old_name = strtolower($adaptation->method->name); @@ -856,36 +852,33 @@ public function handleTraitUse(PhpParser\Node\Stmt\TraitUse $node): void $new_name = strtolower($adaptation->newName->name); if ($new_name !== $old_name) { - $method_map[$new_name] = $old_name; + $storage->trait_alias_map[$new_name] = $old_name; + $storage->trait_alias_map_cased[$adaptation->newName->name] = $adaptation->method->name; } } if ($adaptation->newModifier) { switch ($adaptation->newModifier) { case 1: - $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PUBLIC; + $storage->trait_visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PUBLIC; break; case 2: - $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PROTECTED; + $storage->trait_visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PROTECTED; break; case 4: - $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PRIVATE; + $storage->trait_visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PRIVATE; break; case 32: - $final_map[$new_name] = true; + $storage->trait_final_map[$new_name] = true; break; } } } } - $storage->trait_alias_map = $method_map; - $storage->trait_visibility_map = $visibility_map; - $storage->trait_final_map = $final_map; - foreach ($node->traits as $trait) { $trait_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($trait, $this->aliases); $this->codebase->scanner->queueClassLikeForScanning($trait_fqcln, $this->file_scanner->will_analyze); diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 6d6d6f1b5cd..aaf968e00f7 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -179,17 +179,21 @@ final class ClassLikeStorage implements HasAttributesInterface public $used_traits = []; /** + * @deprecated Will be removed Psalm 6. Use {@see self::$trait_alias_map_cased} instead. * @var array */ public $trait_alias_map = []; + /** @var array */ + public array $trait_alias_map_cased = []; + /** * @var array */ public $trait_final_map = []; /** - * @var array + * @var array */ public $trait_visibility_map = []; diff --git a/tests/TraitTest.php b/tests/TraitTest.php index 20e5c8cae3f..a38b69a6249 100644 --- a/tests/TraitTest.php +++ b/tests/TraitTest.php @@ -1001,6 +1001,41 @@ trait T {} } ', ], + 'constant in trait' => [ + 'code' => <<<'PHP' + [ + 'code' => <<<'PHP' + [ + '$c' => 'string', + ], + ], ]; }