77use PhpParser \Node ;
88use PHPStan \Analyser \IntermediaryNameScope ;
99use PHPStan \Analyser \NameScope ;
10+ use PHPStan \Analyser \NamespaceUsesTracker ;
1011use PHPStan \BetterReflection \Util \GetLastDocComment ;
1112use PHPStan \Broker \AnonymousClassNameHelper ;
1213use PHPStan \Cache \Cache ;
5051use function sprintf ;
5152use function str_contains ;
5253use function str_starts_with ;
53- use function strtolower ;
5454
5555#[AutowiredService]
5656final class FileTypeMapper
@@ -369,7 +369,9 @@ private function getNameScopeMap(string $fileName): array
369369 // (https://github.com/phpstan/phpstan/issues/15037) - a parse that
370370 // silently dropped every comment cached PHPDoc-less name-scope maps,
371371 // and the content hashes cannot tell them apart from real ones.
372- $ variableCacheKey = sprintf ('v6-%s ' , ComposerHelper::getPhpDocParserVersion ());
372+ // v7: IntermediaryNameScope refers to a NamespaceUses shared with the other name scopes
373+ // created under the same use statements
374+ $ variableCacheKey = sprintf ('v7-%s ' , ComposerHelper::getPhpDocParserVersion ());
373375 $ cached = $ this ->loadCachedPhpDocNodeMap ($ cacheKey , $ variableCacheKey );
374376 if ($ cached === null ) {
375377 [$ nameScopeMap , $ files ] = $ this ->createPhpDocNodeMap ($ fileName , null , null , [], $ fileName );
@@ -415,11 +417,6 @@ private function loadCachedPhpDocNodeMap(string $cacheKey, string $variableCache
415417 }
416418
417419 if ($ useCache ) {
418- $ pool = [];
419- foreach ($ nameScopeMap as $ nameScopeKey => $ intermediaryNameScope ) {
420- $ nameScopeMap [$ nameScopeKey ] = $ intermediaryNameScope ->intern ($ pool );
421- }
422-
423420 return [$ nameScopeMap , array_keys ($ filesWithHashes )];
424421 }
425422 }
@@ -449,19 +446,18 @@ private function createPhpDocNodeMap(string $fileName, ?string $lookForTrait, ?s
449446 $ classStack [] = $ traitUseClass ;
450447 $ typeAliasStack [] = [];
451448 }
452- $ namespace = null ;
449+ $ namespaceUsesTracker = new NamespaceUsesTracker () ;
453450
454451 $ traitFound = false ;
455452
456453 $ files = [$ fileName ];
457454
458455 /** @var array<string|null> $functionStack */
459456 $ functionStack = [];
460- $ uses = [];
461- $ constUses = [];
462457 $ this ->processNodes (
463458 $ this ->phpParser ->parseFile ($ fileName ),
464- function (Node $ node ) use ($ fileName , $ lookForTrait , &$ traitFound , $ traitMethodAliases , $ originalClassFileName , $ activeTraitResolutions , &$ nameScopeMap , &$ typeMapStack , &$ typeAliasStack , &$ classStack , &$ namespace , &$ functionStack , &$ uses , &$ constUses , &$ files ): ?int {
459+ function (Node $ node ) use ($ fileName , $ lookForTrait , &$ traitFound , $ traitMethodAliases , $ originalClassFileName , $ activeTraitResolutions , &$ nameScopeMap , &$ typeMapStack , &$ typeAliasStack , &$ classStack , $ namespaceUsesTracker , &$ functionStack , &$ files ): ?int {
460+ $ namespaceUsesTracker ->enterNode ($ node );
465461 if ($ node instanceof Node \Stmt \ClassLike) {
466462 if ($ traitFound && $ fileName === $ originalClassFileName ) {
467463 return self ::SKIP_NODE ;
@@ -490,7 +486,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
490486 if ($ traitFound ) {
491487 return self ::SKIP_NODE ;
492488 }
493- $ className = ltrim (sprintf ('%s \\%s ' , $ namespace , $ node ->name ->name ), '\\' );
489+ $ className = ltrim (sprintf ('%s \\%s ' , $ namespaceUsesTracker -> getNamespaceUses ()-> getNamespace () , $ node ->name ->name ), '\\' );
494490 }
495491 $ classStack [] = $ className ;
496492 $ functionStack [] = null ;
@@ -502,7 +498,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
502498 $ functionStack [] = $ node ->name ->name ;
503499 }
504500 } elseif ($ node instanceof Node \Stmt \Function_) {
505- $ functionStack [] = ltrim (sprintf ('%s \\%s ' , $ namespace , $ node ->name ->name ), '\\' );
501+ $ functionStack [] = ltrim (sprintf ('%s \\%s ' , $ namespaceUsesTracker -> getNamespaceUses ()-> getNamespace () , $ node ->name ->name ), '\\' );
506502 } elseif ($ node instanceof Node \PropertyHook) {
507503 $ propertyName = $ node ->getAttribute ('propertyName ' );
508504 if ($ propertyName !== null ) {
@@ -535,29 +531,25 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
535531 $ parentNameScope = array_last ($ typeMapStack );
536532
537533 $ typeMapStack [] = new IntermediaryNameScope (
538- $ namespace ,
539- $ uses ,
534+ $ namespaceUsesTracker ->getNamespaceUses (),
540535 $ className ,
541536 $ functionName ,
542537 $ this ->chooseTemplateTagValueNodesByPriority ($ phpDocNode ->getTags ()),
543538 $ parentNameScope ,
544539 array_last ($ typeAliasStack ) ?? [],
545- constUses: $ constUses ,
546540 typeAliasClassName: $ lookForTrait ,
547541 );
548542 } elseif ($ node instanceof Node \Stmt \ClassLike) {
549543 $ typeAliasStack [] = [];
550544 } else {
551545 $ parentNameScope = array_last ($ typeMapStack );
552546 $ typeMapStack [] = new IntermediaryNameScope (
553- $ namespace ,
554- $ uses ,
547+ $ namespaceUsesTracker ->getNamespaceUses (),
555548 $ className ,
556549 $ functionName ,
557550 [],
558551 $ parentNameScope ,
559552 array_last ($ typeAliasStack ) ?? [],
560- constUses: $ constUses ,
561553 typeAliasClassName: $ lookForTrait ,
562554 );
563555 }
@@ -582,14 +574,12 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
582574 $ parentNameScope = array_last ($ typeMapStack );
583575 $ typeAliasesMap = array_last ($ typeAliasStack ) ?? [];
584576 $ nameScopeMap [$ nameScopeKey ] = new IntermediaryNameScope (
585- $ namespace ,
586- $ uses ,
577+ $ namespaceUsesTracker ->getNamespaceUses (),
587578 $ className ,
588579 $ functionName ,
589580 $ parentNameScope !== null ? $ parentNameScope ->getTemplatePhpDocNodes () : [],
590581 $ parentNameScope !== null ? $ parentNameScope ->getParent () : null ,
591582 $ typeAliasesMap ,
592- constUses: $ constUses ,
593583 typeAliasClassName: $ lookForTrait ,
594584 );
595585 }
@@ -602,28 +592,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
602592 return null ;
603593 }
604594
605- if ($ node instanceof Node \Stmt \Namespace_) {
606- $ namespace = $ node ->name !== null ? (string ) $ node ->name : null ;
607- } elseif ($ node instanceof Node \Stmt \Use_) {
608- if ($ node ->type === Node \Stmt \Use_::TYPE_NORMAL ) {
609- foreach ($ node ->uses as $ use ) {
610- $ uses [strtolower ($ use ->getAlias ()->name )] = (string ) $ use ->name ;
611- }
612- } elseif ($ node ->type === Node \Stmt \Use_::TYPE_CONSTANT ) {
613- foreach ($ node ->uses as $ use ) {
614- $ constUses [strtolower ($ use ->getAlias ()->name )] = (string ) $ use ->name ;
615- }
616- }
617- } elseif ($ node instanceof Node \Stmt \GroupUse) {
618- $ prefix = (string ) $ node ->prefix ;
619- foreach ($ node ->uses as $ use ) {
620- if ($ node ->type === Node \Stmt \Use_::TYPE_NORMAL || $ use ->type === Node \Stmt \Use_::TYPE_NORMAL ) {
621- $ uses [strtolower ($ use ->getAlias ()->name )] = sprintf ('%s \\%s ' , $ prefix , (string ) $ use ->name );
622- } elseif ($ node ->type === Node \Stmt \Use_::TYPE_CONSTANT || $ use ->type === Node \Stmt \Use_::TYPE_CONSTANT ) {
623- $ constUses [strtolower ($ use ->getAlias ()->name )] = sprintf ('%s \\%s ' , $ prefix , (string ) $ use ->name );
624- }
625- }
626- } elseif ($ node instanceof Node \Stmt \TraitUse) {
595+ if ($ node instanceof Node \Stmt \TraitUse) {
627596 $ traitMethodAliases = [];
628597 foreach ($ node ->adaptations as $ traitUseAdaptation ) {
629598 if (!$ traitUseAdaptation instanceof Node \Stmt \TraitUseAdaptation \Alias) {
@@ -694,7 +663,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
694663
695664 return null ;
696665 },
697- static function (Node $ node , $ callbackResult ) use (&$ namespace , & $ functionStack , &$ classStack , &$ typeAliasStack , &$ uses , & $ typeMapStack, & $ constUses ): void {
666+ static function (Node $ node , $ callbackResult ) use (&$ functionStack , &$ classStack , &$ typeAliasStack , &$ typeMapStack ): void {
698667 if ($ node instanceof Node \Stmt \ClassLike) {
699668 if (count ($ classStack ) === 0 ) {
700669 throw new ShouldNotHappenException ();
@@ -712,10 +681,6 @@ static function (Node $node, $callbackResult) use (&$namespace, &$functionStack,
712681 }
713682
714683 array_pop ($ functionStack );
715- } elseif ($ node instanceof Node \Stmt \Namespace_) {
716- $ namespace = null ;
717- $ uses = [];
718- $ constUses = [];
719684 } elseif ($ node instanceof Node \Stmt \ClassMethod || $ node instanceof Node \Stmt \Function_) {
720685 if (count ($ functionStack ) === 0 ) {
721686 throw new ShouldNotHappenException ();
0 commit comments