diff --git a/UPGRADING.md b/UPGRADING.md index 5ddf250b0d1..db47c80157a 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -120,6 +120,7 @@ - [BC] Method `Psalm\Type\Union::isFormerStaticObject()` was renamed to `isStaticObject()` - [BC] Method `Psalm\Type\Union::hasFormerStaticObject()` was renamed to `hasStaticObject()` - [BC] Function assertions (from `@psalm-assert Foo $bar`) have been converted from strings to specific `Assertion` objects. + - [BC] Property `Psalm\Storage\ClassLikeStorage::$invalid_dependencies` changed from `array` to `array`. ## Removed - [BC] Property `Psalm\Codebase::$php_major_version` was removed, use diff --git a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index c7d608722a8..339d5079f10 100644 --- a/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -336,7 +336,7 @@ public static function checkFullyQualifiedClassLikeName( return null; } - foreach ($class_storage->invalid_dependencies as $dependency_class_name) { + foreach ($class_storage->invalid_dependencies as $dependency_class_name => $_) { // if the implemented/extended class is stubbed, it may not yet have // been hydrated if ($codebase->classlike_storage_provider->has($dependency_class_name)) { diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 6937916bee5..7abd7713f2c 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -20,8 +20,10 @@ use function array_intersect_key; use function array_keys; use function array_merge; +use function array_splice; use function count; use function in_array; +use function key; use function reset; use function strlen; use function strpos; @@ -139,10 +141,17 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe $dependent_classlikes[$fq_classlike_name_lc] = true; - $this->populateDataFromTraits($storage, $storage_provider, $dependent_classlikes); + foreach ($storage->used_traits as $used_trait_lc => $_) { + $this->populateDataFromTrait($storage, $storage_provider, $dependent_classlikes, $used_trait_lc); + } if ($storage->parent_classes) { - $this->populateDataFromParentClass($storage, $storage_provider, $dependent_classlikes); + $this->populateDataFromParentClass( + $storage, + $storage_provider, + $dependent_classlikes, + reset($storage->parent_classes) + ); } if (!strpos($fq_classlike_name_lc, '\\') @@ -154,9 +163,23 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe $storage->methods['__construct'] = $storage->methods[$fq_classlike_name_lc]; } - $this->populateInterfaceDataFromParentInterfaces($storage, $storage_provider, $dependent_classlikes); + foreach ($storage->direct_interface_parents as $parent_interface_lc => $_) { + $this->populateInterfaceDataFromParentInterface( + $storage, + $storage_provider, + $dependent_classlikes, + $parent_interface_lc + ); + } - $this->populateDataFromImplementedInterfaces($storage, $storage_provider, $dependent_classlikes); + foreach ($storage->direct_class_interfaces as $implemented_interface_lc => $_) { + $this->populateDataFromImplementedInterface( + $storage, + $storage_provider, + $dependent_classlikes, + $implemented_interface_lc + ); + } if ($storage->location) { $file_path = $storage->location->file_path; @@ -222,7 +245,7 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe } } - $this->populateOverriddenMethods($storage); + $this->populateOverriddenMethods($storage, $storage_provider); $this->progress->debug('Have populated ' . $storage->name . "\n"); @@ -230,7 +253,29 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe if (isset($this->invalid_class_storages[$fq_classlike_name_lc])) { foreach ($this->invalid_class_storages[$fq_classlike_name_lc] as $dependency) { + // Dependencies may not be fully set yet, so we have to loop through dependencies of dependencies + $dependencies = [strtolower($dependency->name) => true]; + do { + $current_dependency_name = key(array_splice($dependencies, 0, 1)); // Key shift + $current_dependency = $storage_provider->get($current_dependency_name); + $dependencies += $current_dependency->dependent_classlikes; + + if (isset($current_dependency->dependent_classlikes[$fq_classlike_name_lc])) { + if ($dependency->location) { + IssueBuffer::maybeAdd( + new CircularReference( + 'Circular reference discovered when loading ' . $dependency->name, + $dependency->location + ) + ); + } + + continue 2; + } + } while (!empty($dependencies)); + $dependency->populated = false; + unset($dependency->invalid_dependencies[$fq_classlike_name_lc]); $this->populateClassLikeStorage($dependency, $dependent_classlikes); } @@ -239,8 +284,65 @@ private function populateClassLikeStorage(ClassLikeStorage $storage, array $depe } private function populateOverriddenMethods( - ClassLikeStorage $storage + ClassLikeStorage $storage, + ClassLikeStorageProvider $storage_provider ): void { + $interface_method_implementers = []; + foreach ($storage->class_implements as $interface) { + try { + $implemented_interface = strtolower( + $this->classlikes->getUnAliasedName( + $interface + ) + ); + $implemented_interface_storage = $storage_provider->get($implemented_interface); + } catch (InvalidArgumentException $e) { + continue; + } + + $implemented_interface_storage->dependent_classlikes[strtolower($storage->name)] = true; + + foreach ($implemented_interface_storage->methods as $method_name => $method) { + if ($method->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) { + $interface_method_implementers[$method_name][] = new MethodIdentifier( + $implemented_interface_storage->name, + $method_name + ); + } + } + } + + foreach ($interface_method_implementers as $method_name => $interface_method_ids) { + if (count($interface_method_ids) === 1) { + if (isset($storage->methods[$method_name])) { + $method_storage = $storage->methods[$method_name]; + + if ($method_storage->signature_return_type + && !$method_storage->signature_return_type->isVoid() + && $method_storage->return_type === $method_storage->signature_return_type + ) { + $interface_fqcln = $interface_method_ids[0]->fq_class_name; + $interface_storage = $storage_provider->get($interface_fqcln); + + if (isset($interface_storage->methods[$method_name])) { + $interface_method_storage = $interface_storage->methods[$method_name]; + + if ($interface_method_storage->throws + && (!$method_storage->throws || $method_storage->inheritdoc) + ) { + $method_storage->throws += $interface_method_storage->throws; + } + } + } + } + } + + foreach ($interface_method_ids as $interface_method_id) { + $storage->overridden_method_ids[$method_name][$interface_method_id->fq_class_name] + = $interface_method_id; + } + } + $storage->documenting_method_ids = []; foreach ($storage->methods as $method_name => $method_storage) { @@ -332,75 +434,74 @@ private function populateOverriddenMethods( } } - private function populateDataFromTraits( + private function populateDataFromTrait( ClassLikeStorage $storage, ClassLikeStorageProvider $storage_provider, - array $dependent_classlikes + array $dependent_classlikes, + string $used_trait_lc ): void { - foreach ($storage->used_traits as $used_trait_lc => $_) { - try { - $used_trait_lc = strtolower( - $this->classlikes->getUnAliasedName( - $used_trait_lc - ) - ); - $trait_storage = $storage_provider->get($used_trait_lc); - } catch (InvalidArgumentException $e) { - continue; - } + try { + $used_trait_lc = strtolower( + $this->classlikes->getUnAliasedName( + $used_trait_lc + ) + ); + $trait_storage = $storage_provider->get($used_trait_lc); + } catch (InvalidArgumentException $e) { + return; + } - $this->populateClassLikeStorage($trait_storage, $dependent_classlikes); + $this->populateClassLikeStorage($trait_storage, $dependent_classlikes); - $this->inheritMethodsFromParent($storage, $trait_storage); - $this->inheritPropertiesFromParent($storage, $trait_storage); + $this->inheritMethodsFromParent($storage, $trait_storage); + $this->inheritPropertiesFromParent($storage, $trait_storage); - if ($trait_storage->template_types) { - $storage->template_extended_params[$trait_storage->name] = []; + if ($trait_storage->template_types) { + $storage->template_extended_params[$trait_storage->name] = []; - if (isset($storage->template_extended_offsets[$trait_storage->name])) { - foreach ($storage->template_extended_offsets[$trait_storage->name] as $i => $type) { - $trait_template_type_names = array_keys($trait_storage->template_types); + if (isset($storage->template_extended_offsets[$trait_storage->name])) { + foreach ($storage->template_extended_offsets[$trait_storage->name] as $i => $type) { + $trait_template_type_names = array_keys($trait_storage->template_types); - $mapped_name = $trait_template_type_names[$i] ?? null; + $mapped_name = $trait_template_type_names[$i] ?? null; - if ($mapped_name) { - $storage->template_extended_params[$trait_storage->name][$mapped_name] = $type; - } + if ($mapped_name) { + $storage->template_extended_params[$trait_storage->name][$mapped_name] = $type; } + } - if ($trait_storage->template_extended_params) { - foreach ($trait_storage->template_extended_params as $t_storage_class => $type_map) { - foreach ($type_map as $i => $type) { - $storage->template_extended_params[$t_storage_class][$i] = self::extendType( - $type, - $storage - ); - } + if ($trait_storage->template_extended_params) { + foreach ($trait_storage->template_extended_params as $t_storage_class => $type_map) { + foreach ($type_map as $i => $type) { + $storage->template_extended_params[$t_storage_class][$i] = self::extendType( + $type, + $storage + ); } } - } else { - foreach ($trait_storage->template_types as $template_name => $template_type_map) { - foreach ($template_type_map as $template_type) { - $default_param = clone $template_type; - $default_param->from_docblock = false; - $storage->template_extended_params[$trait_storage->name][$template_name] - = $default_param; - } + } + } else { + foreach ($trait_storage->template_types as $template_name => $template_type_map) { + foreach ($template_type_map as $template_type) { + $default_param = clone $template_type; + $default_param->from_docblock = false; + $storage->template_extended_params[$trait_storage->name][$template_name] + = $default_param; } } - } elseif ($trait_storage->template_extended_params) { - $storage->template_extended_params = array_merge( - $storage->template_extended_params ?: [], - $trait_storage->template_extended_params - ); } + } elseif ($trait_storage->template_extended_params) { + $storage->template_extended_params = array_merge( + $storage->template_extended_params ?: [], + $trait_storage->template_extended_params + ); + } - $storage->pseudo_property_get_types += $trait_storage->pseudo_property_get_types; - $storage->pseudo_property_set_types += $trait_storage->pseudo_property_set_types; + $storage->pseudo_property_get_types += $trait_storage->pseudo_property_get_types; + $storage->pseudo_property_set_types += $trait_storage->pseudo_property_set_types; - $storage->pseudo_methods += $trait_storage->pseudo_methods; - $storage->declaring_pseudo_method_ids += $trait_storage->declaring_pseudo_method_ids; - } + $storage->pseudo_methods += $trait_storage->pseudo_methods; + $storage->declaring_pseudo_method_ids += $trait_storage->declaring_pseudo_method_ids; } private static function extendType( @@ -437,10 +538,9 @@ private static function extendType( private function populateDataFromParentClass( ClassLikeStorage $storage, ClassLikeStorageProvider $storage_provider, - array $dependent_classlikes + array $dependent_classlikes, + string $parent_storage_class ): void { - $parent_storage_class = reset($storage->parent_classes); - $parent_storage_class = strtolower( $this->classlikes->getUnAliasedName( $parent_storage_class @@ -452,7 +552,7 @@ private function populateDataFromParentClass( } catch (InvalidArgumentException $e) { $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n"); - $storage->invalid_dependencies[] = $parent_storage_class; + $storage->invalid_dependencies[$parent_storage_class] = true; $this->invalid_class_storages[$parent_storage_class][] = $storage; @@ -559,252 +659,151 @@ private function populateDataFromParentClass( $storage->declaring_pseudo_method_ids += $parent_storage->declaring_pseudo_method_ids; } - private function populateInterfaceDataFromParentInterfaces( + private function populateInterfaceData( ClassLikeStorage $storage, + ClassLikeStorage $interface_storage, ClassLikeStorageProvider $storage_provider, array $dependent_classlikes ): void { - $parent_interfaces = []; - - foreach ($storage->direct_interface_parents as $parent_interface_lc => $_) { - try { - $parent_interface_lc = strtolower( - $this->classlikes->getUnAliasedName( - $parent_interface_lc - ) - ); - $parent_interface_storage = $storage_provider->get($parent_interface_lc); - } catch (InvalidArgumentException $e) { - $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n"); - - $storage->invalid_dependencies[] = $parent_interface_lc; - continue; - } + $this->populateClassLikeStorage($interface_storage, $dependent_classlikes); - $this->populateClassLikeStorage($parent_interface_storage, $dependent_classlikes); - - // copy over any constants - $storage->constants = array_merge( - array_filter( - $parent_interface_storage->constants, - fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC - ), - $storage->constants - ); + // copy over any constants + $storage->constants = array_merge( + array_filter( + $interface_storage->constants, + fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC + ), + $storage->constants + ); - $storage->invalid_dependencies = array_merge( - $storage->invalid_dependencies, - $parent_interface_storage->invalid_dependencies - ); + $storage->invalid_dependencies = array_merge( + $storage->invalid_dependencies, + $interface_storage->invalid_dependencies + ); - if ($parent_interface_storage->template_types) { - $storage->template_extended_params[$parent_interface_storage->name] = []; + if ($interface_storage->template_types) { + $storage->template_extended_params[$interface_storage->name] = []; - if (isset($storage->template_extended_offsets[$parent_interface_storage->name])) { - foreach ($storage->template_extended_offsets[$parent_interface_storage->name] as $i => $type) { - $parent_template_type_names = array_keys($parent_interface_storage->template_types); + if (isset($storage->template_extended_offsets[$interface_storage->name])) { + foreach ($storage->template_extended_offsets[$interface_storage->name] as $i => $type) { + $parent_template_type_names = array_keys($interface_storage->template_types); - $mapped_name = $parent_template_type_names[$i] ?? null; + $mapped_name = $parent_template_type_names[$i] ?? null; - if ($mapped_name) { - $storage->template_extended_params[$parent_interface_storage->name][$mapped_name] = $type; - } + if ($mapped_name) { + $storage->template_extended_params[$interface_storage->name][$mapped_name] = $type; } + } - if ($parent_interface_storage->template_extended_params) { - foreach ($parent_interface_storage->template_extended_params as $t_storage_class => $type_map) { - foreach ($type_map as $i => $type) { - $storage->template_extended_params[$t_storage_class][$i] = self::extendType( - $type, - $storage - ); - } + if ($interface_storage->template_extended_params) { + foreach ($interface_storage->template_extended_params as $t_storage_class => $type_map) { + foreach ($type_map as $i => $type) { + $storage->template_extended_params[$t_storage_class][$i] = self::extendType( + $type, + $storage + ); } } - } else { - foreach ($parent_interface_storage->template_types as $template_name => $template_type_map) { - foreach ($template_type_map as $template_type) { - $default_param = clone $template_type; - $default_param->from_docblock = false; - $storage->template_extended_params[$parent_interface_storage->name][$template_name] - = $default_param; - } + } + } else { + foreach ($interface_storage->template_types as $template_name => $template_type_map) { + foreach ($template_type_map as $template_type) { + $default_param = clone $template_type; + $default_param->from_docblock = false; + $storage->template_extended_params[$interface_storage->name][$template_name] + = $default_param; } } - } elseif ($parent_interface_storage->template_extended_params) { - $storage->template_extended_params = array_merge( - $storage->template_extended_params ?: [], - $parent_interface_storage->template_extended_params - ); } - - $parent_interfaces = array_merge($parent_interfaces, $parent_interface_storage->parent_interfaces); - - $this->inheritMethodsFromParent($storage, $parent_interface_storage); - - $storage->pseudo_methods += $parent_interface_storage->pseudo_methods; - $storage->declaring_pseudo_method_ids += $parent_interface_storage->declaring_pseudo_method_ids; + } elseif ($interface_storage->template_extended_params) { + $storage->template_extended_params = array_merge( + $storage->template_extended_params ?: [], + $interface_storage->template_extended_params + ); } - $storage->parent_interfaces = array_merge($parent_interfaces, $storage->parent_interfaces); - - foreach ($storage->parent_interfaces as $parent_interface_lc => $_) { + $new_parents = array_keys($interface_storage->parent_interfaces); + $new_parents[] = $interface_storage->name; + foreach ($new_parents as $new_parent) { try { - $parent_interface_lc = strtolower( + $new_parent = strtolower( $this->classlikes->getUnAliasedName( - $parent_interface_lc + $new_parent ) ); - $parent_interface_storage = $storage_provider->get($parent_interface_lc); + $new_parent_interface_storage = $storage_provider->get($new_parent); } catch (InvalidArgumentException $e) { continue; } - $parent_interface_storage->dependent_classlikes[strtolower($storage->name)] = true; + $new_parent_interface_storage->dependent_classlikes[strtolower($storage->name)] = true; } } - private function populateDataFromImplementedInterfaces( + private function populateInterfaceDataFromParentInterface( ClassLikeStorage $storage, ClassLikeStorageProvider $storage_provider, - array $dependent_classlikes + array $dependent_classlikes, + string $parent_interface_lc ): void { - $extra_interfaces = []; - - foreach ($storage->direct_class_interfaces as $implemented_interface_lc => $_) { - try { - $implemented_interface_lc = strtolower( - $this->classlikes->getUnAliasedName( - $implemented_interface_lc - ) - ); - $implemented_interface_storage = $storage_provider->get($implemented_interface_lc); - } catch (InvalidArgumentException $e) { - $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n"); - - $storage->invalid_dependencies[] = $implemented_interface_lc; - continue; - } - - $this->populateClassLikeStorage($implemented_interface_storage, $dependent_classlikes); - - // copy over any constants - $storage->constants = array_merge( - array_filter( - $implemented_interface_storage->constants, - fn($constant) => $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC - ), - $storage->constants - ); - - $storage->invalid_dependencies = array_merge( - $storage->invalid_dependencies, - $implemented_interface_storage->invalid_dependencies + try { + $parent_interface_lc = strtolower( + $this->classlikes->getUnAliasedName( + $parent_interface_lc + ) ); + $parent_interface_storage = $storage_provider->get($parent_interface_lc); + } catch (InvalidArgumentException $e) { + $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n"); - if ($implemented_interface_storage->template_types) { - $storage->template_extended_params[$implemented_interface_storage->name] = []; - - if (isset($storage->template_extended_offsets[$implemented_interface_storage->name])) { - foreach ($storage->template_extended_offsets[$implemented_interface_storage->name] as $i => $type) { - $parent_template_type_names = array_keys($implemented_interface_storage->template_types); - - $mapped_name = $parent_template_type_names[$i] ?? null; - - if ($mapped_name) { - $storage->template_extended_params[$implemented_interface_storage->name][$mapped_name] - = $type; - } - } - - if ($implemented_interface_storage->template_extended_params) { - foreach ($implemented_interface_storage->template_extended_params as $e_i => $type_map) { - foreach ($type_map as $i => $type) { - $storage->template_extended_params[$e_i][$i] = self::extendType( - $type, - $storage - ); - } - } - } - } else { - foreach ($implemented_interface_storage->template_types as $template_name => $template_type_map) { - foreach ($template_type_map as $template_type) { - $default_param = clone $template_type; - $default_param->from_docblock = false; - $storage->template_extended_params[$implemented_interface_storage->name][$template_name] - = $default_param; - } - } - } - } elseif ($implemented_interface_storage->template_extended_params) { - $storage->template_extended_params = array_merge( - $storage->template_extended_params ?: [], - $implemented_interface_storage->template_extended_params - ); - } - - $extra_interfaces = array_merge($extra_interfaces, $implemented_interface_storage->parent_interfaces); + $storage->invalid_dependencies[$parent_interface_lc] = true; + return; } - $storage->class_implements = array_merge($storage->class_implements, $extra_interfaces); - - $interface_method_implementers = []; - - foreach ($storage->class_implements as $implemented_interface_lc => $_) { - try { - $implemented_interface = strtolower( - $this->classlikes->getUnAliasedName( - $implemented_interface_lc - ) - ); - $implemented_interface_storage = $storage_provider->get($implemented_interface); - } catch (InvalidArgumentException $e) { - continue; - } + $this->populateInterfaceData($storage, $parent_interface_storage, $storage_provider, $dependent_classlikes); - $implemented_interface_storage->dependent_classlikes[strtolower($storage->name)] = true; + $this->inheritMethodsFromParent($storage, $parent_interface_storage); - foreach ($implemented_interface_storage->methods as $method_name => $method) { - if ($method->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) { - $interface_method_implementers[$method_name][] = new MethodIdentifier( - $implemented_interface_storage->name, - $method_name - ); - } - } - } + $storage->pseudo_methods += $parent_interface_storage->pseudo_methods; + $storage->declaring_pseudo_method_ids += $parent_interface_storage->declaring_pseudo_method_ids; - foreach ($interface_method_implementers as $method_name => $interface_method_ids) { - if (count($interface_method_ids) === 1) { - if (isset($storage->methods[$method_name])) { - $method_storage = $storage->methods[$method_name]; + $storage->parent_interfaces = array_merge( + $parent_interface_storage->parent_interfaces, + $storage->parent_interfaces + ); + } - if ($method_storage->signature_return_type - && !$method_storage->signature_return_type->isVoid() - && $method_storage->return_type === $method_storage->signature_return_type - ) { - $interface_fqcln = $interface_method_ids[0]->fq_class_name; - $interface_storage = $storage_provider->get($interface_fqcln); + private function populateDataFromImplementedInterface( + ClassLikeStorage $storage, + ClassLikeStorageProvider $storage_provider, + array $dependent_classlikes, + string $implemented_interface_lc + ): void { + try { + $implemented_interface_lc = strtolower( + $this->classlikes->getUnAliasedName( + $implemented_interface_lc + ) + ); + $implemented_interface_storage = $storage_provider->get($implemented_interface_lc); + } catch (InvalidArgumentException $e) { + $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n"); - if (isset($interface_storage->methods[$method_name])) { - $interface_method_storage = $interface_storage->methods[$method_name]; + $storage->invalid_dependencies[$implemented_interface_lc] = true; + return; + } - if ($interface_method_storage->throws - && (!$method_storage->throws || $method_storage->inheritdoc) - ) { - $method_storage->throws += $interface_method_storage->throws; - } - } - } - } - } + $this->populateInterfaceData( + $storage, + $implemented_interface_storage, + $storage_provider, + $dependent_classlikes + ); - foreach ($interface_method_ids as $interface_method_id) { - $storage->overridden_method_ids[$method_name][$interface_method_id->fq_class_name] - = $interface_method_id; - } - } + $storage->class_implements = array_merge( + $storage->class_implements, + $implemented_interface_storage->parent_interfaces + ); } /** diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 0119b950aea..d8dcdf0c617 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -386,7 +386,7 @@ class ClassLikeStorage public $initialized_properties = []; /** - * @var array + * @var array */ public $invalid_dependencies = []; diff --git a/tests/StubTest.php b/tests/StubTest.php index aea8834ce9f..f2b8f3d33f1 100644 --- a/tests/StubTest.php +++ b/tests/StubTest.php @@ -212,6 +212,82 @@ public function testStubFileConstant(): void $this->analyzeFile($file_path, new Context()); } + public function testStubFileParentClass(): void + { + $this->expectException(CodeException::class); + $this->expectExceptionMessage('ImplementedParamTypeMismatch'); + $this->project_analyzer = $this->getProjectAnalyzerWithConfig( + TestConfig::loadFromXML( + dirname(__DIR__), + ' + + + + + + + + + ' + ) + ); + + $file_path = getcwd() . '/src/somefile.php'; + + $this->addFile( + $file_path, + 'analyzeFile($file_path, new Context()); + } + + public function testStubFileCircularReference(): void + { + $this->expectException(CodeException::class); + $this->expectExceptionMessage('CircularReference'); + $this->project_analyzer = $this->getProjectAnalyzerWithConfig( + TestConfig::loadFromXML( + dirname(__DIR__), + ' + + + + + + + + + ' + ) + ); + + $file_path = getcwd() . '/src/somefile.php'; + + $this->addFile( + $file_path, + 'analyzeFile($file_path, new Context()); + } + public function testPhpStormMetaParsingFile(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( diff --git a/tests/fixtures/stubs/CircularReference.phpstub b/tests/fixtures/stubs/CircularReference.phpstub new file mode 100644 index 00000000000..7eadeb4e26e --- /dev/null +++ b/tests/fixtures/stubs/CircularReference.phpstub @@ -0,0 +1,5 @@ +