@@ -686,12 +686,24 @@ private function getMapInputUsages(InClassMethodNode $node): array
686686
687687 /**
688688 * @param list<ClassMethodUsage|ClassPropertyUsage> $usages
689+ * @param array<string, true> $visited
689690 */
690691 private function collectMapInputDtoUsages (
691692 string $ dtoClassName ,
692693 array &$ usages ,
694+ array &$ visited = [],
693695 ): void
694696 {
697+ if (isset ($ visited [$ dtoClassName ])) {
698+ return ;
699+ }
700+
701+ $ visited [$ dtoClassName ] = true ;
702+
703+ if (!$ this ->reflectionProvider ->hasClass ($ dtoClassName )) {
704+ return ;
705+ }
706+
695707 $ dtoReflection = $ this ->reflectionProvider ->getClass ($ dtoClassName );
696708 $ origin = UsageOrigin::createVirtual ($ this , VirtualUsageData::withNote ('Console input DTO via #[MapInput] ' ));
697709
@@ -702,8 +714,9 @@ private function collectMapInputDtoUsages(
702714
703715 $ isInputProperty = $ property ->getAttributes ('Symfony\Component\Console\Attribute\Argument ' ) !== []
704716 || $ property ->getAttributes ('Symfony\Component\Console\Attribute\Option ' ) !== [];
717+ $ nestedMapInput = $ property ->getAttributes ('Symfony\Component\Console\Attribute\MapInput ' ) !== [];
705718
706- if (!$ isInputProperty ) {
719+ if (!$ isInputProperty && ! $ nestedMapInput ) {
707720 continue ;
708721 }
709722
@@ -717,6 +730,18 @@ private function collectMapInputDtoUsages(
717730 new ClassPropertyRef ($ dtoClassName , $ property ->getName (), possibleDescendant: false ),
718731 AccessType::READ ,
719732 );
733+
734+ if (!$ nestedMapInput ) {
735+ continue ;
736+ }
737+
738+ $ propertyType = $ property ->getType ();
739+
740+ if (!$ propertyType instanceof ReflectionNamedType || $ propertyType ->isBuiltin ()) {
741+ continue ;
742+ }
743+
744+ $ this ->collectMapInputDtoUsages ($ propertyType ->getName (), $ usages , $ visited );
720745 }
721746
722747 foreach ($ dtoReflection ->getNativeReflection ()->getMethods () as $ dtoMethod ) {
0 commit comments