diff --git a/src/Psalm/Internal/Codebase/Scanner.php b/src/Psalm/Internal/Codebase/Scanner.php index ff582a2fe3a..86181db3d16 100644 --- a/src/Psalm/Internal/Codebase/Scanner.php +++ b/src/Psalm/Internal/Codebase/Scanner.php @@ -280,6 +280,7 @@ public function getClassLikeFilePath($fq_classlike_name_lc) * @param string|null $referencing_file_path * @param bool $analyze_too * @param bool $store_failure + * @param array $phantom_classes * * @return void */ @@ -287,7 +288,8 @@ public function queueClassLikeForScanning( $fq_classlike_name, $referencing_file_path = null, $analyze_too = false, - $store_failure = true + $store_failure = true, + array $phantom_classes = [] ) { if ($fq_classlike_name[0] === '\\') { $fq_classlike_name = substr($fq_classlike_name, 1); @@ -325,7 +327,7 @@ public function queueClassLikeForScanning( $property_type->queueClassLikesForScanning( $this->codebase, null, - [$fq_classlike_name_lc => true] + $phantom_classes + [$fq_classlike_name_lc => true] ); } } diff --git a/src/Psalm/Internal/PropertyMap.php b/src/Psalm/Internal/PropertyMap.php index a35a1a18dcd..3652789e004 100644 --- a/src/Psalm/Internal/PropertyMap.php +++ b/src/Psalm/Internal/PropertyMap.php @@ -268,7 +268,7 @@ 'domelement' => [ 'schemaTypeInfo' => 'bool', 'tagName' => 'string', - 'attributes' => 'DOMNamedNodeMap', + 'attributes' => 'DOMNamedNodeMap', ], 'tidynode' => [ 'value' => 'string', diff --git a/src/Psalm/Internal/Stubs/CoreGenericClasses.php b/src/Psalm/Internal/Stubs/CoreGenericClasses.php index 322f06fa975..9dd437be4cb 100644 --- a/src/Psalm/Internal/Stubs/CoreGenericClasses.php +++ b/src/Psalm/Internal/Stubs/CoreGenericClasses.php @@ -946,6 +946,32 @@ class DOMNodeList implements Traversable, Countable { public function item($index) {} } +/** + * @template-covariant TNode as DOMNode + * @template-implements Traversable + */ +class DOMNamedNodeMap implements Traversable, Countable { + /** + * @var int + */ + public $length; + + /** + * @return TNode|null + */ + public function getNamedItem(string $name): ?DOMNode {} + + /** + * @return TNode|null + */ + public function getNamedItemNS(string $namespaceURI, string $localName): ?DOMNode {} + + /** + * @return TNode|null + */ + public function item(int $index): ?DOMNode {} +} + /** * The SplDoublyLinkedList class provides the main functionalities of a doubly linked list. * @link https://php.net/manual/en/class.spldoublylinkedlist.php diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index d26b9a1cc1f..cc58188977b 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -458,7 +458,8 @@ public function queueClassLikesForScanning( $this->value, $file_storage ? $file_storage->file_path : null, false, - !$this->from_docblock + !$this->from_docblock, + $phantom_classes ); if ($file_storage) { @@ -487,7 +488,8 @@ public function queueClassLikesForScanning( $this->fq_classlike_name, $file_storage ? $file_storage->file_path : null, false, - !$this->from_docblock + !$this->from_docblock, + $phantom_classes ); if ($file_storage) { $file_storage->referenced_classlikes[strtolower($this->fq_classlike_name)] = $this->fq_classlike_name; @@ -500,7 +502,8 @@ public function queueClassLikesForScanning( $this->as, $file_storage ? $file_storage->file_path : null, false, - !$this->from_docblock + !$this->from_docblock, + $phantom_classes ); if ($file_storage) { $file_storage->referenced_classlikes[strtolower($this->as)] = $this->as; @@ -521,7 +524,8 @@ public function queueClassLikesForScanning( $this->value, $file_storage ? $file_storage->file_path : null, false, - !$this->from_docblock + !$this->from_docblock, + $phantom_classes ); if ($file_storage) { $file_storage->referenced_classlikes[strtolower($this->value)] = $this->value; diff --git a/tests/PropertyTypeTest.php b/tests/PropertyTypeTest.php index 0118dbdec66..4ed5c2825f9 100644 --- a/tests/PropertyTypeTest.php +++ b/tests/PropertyTypeTest.php @@ -344,6 +344,12 @@ function foo(DOMElement $e) : void { echo $e->attributes->length; }', ], + 'genericTypeFromPropertyMap' => [ + 'attributes->item(0); + }' + ], 'goodArrayProperties' => [ '