1010$ TL = new TL (null );
1111$ TL ->init (new TLSchema );
1212
13- $ final = [];
1413$ locations = [];
1514
1615final class TLContext
@@ -67,7 +66,8 @@ public function getTypeAtPosition(SimpleExtractorOp $_path): string
6766 $ hadFlag = false ;
6867 do {
6968 if ($ type !== null
70- && !isset (self ::getConstructorsOfType ($ this ->tl , $ type )[$ path [$ idx ]])
69+ && !isset (self ::getConstructorsOfType ($ this ->tl , $ type , true , true )[$ path [$ idx ]])
70+ && !isset (self ::getConstructorsOfType ($ this ->tl , $ type , false , true )[$ path [$ idx ]])
7171 ) {
7272 throw new AssertionError ("{$ path [$ idx ]} is a constructor of type $ type, path: " . json_encode ($ path ));
7373 }
@@ -98,20 +98,26 @@ public function getTypeAtPosition(SimpleExtractorOp $_path): string
9898
9999 return $ type ;
100100 }
101- public static function getConstructorsOfType (TLInterface $ tl , string $ type ): array
101+ public static function getConstructorsOfType (TLInterface $ tl , string $ type, bool $ methods , bool $ ignoreEmpty = false ): array
102102 {
103103 $ constructors = [];
104- foreach ($ tl ->getConstructors ()->by_id as $ constructor ) {
105- if ($ constructor ['type ' ] === $ type ) {
106- $ constructors [$ constructor ['predicate ' ]] = true ;
104+ if ($ methods ) {
105+ foreach ($ tl ->getMethods ()->by_id as $ method ) {
106+ if ($ method ['type ' ] === $ type ) {
107+ $ constructors [$ method ['method ' ]] = false ;
108+ }
107109 }
108- }
109- foreach ($ tl ->getMethods ()->by_id as $ method ) {
110- if ($ method ['type ' ] === $ type ) {
111- $ constructors [$ method ['method ' ]] = false ;
110+ } else {
111+ foreach ($ tl ->getConstructors ()->by_id as $ constructor ) {
112+ if ($ constructor ['type ' ] === $ type ) {
113+ $ constructors [$ constructor ['predicate ' ]] = true ;
114+ }
112115 }
113116 }
114- Assert::notEmpty ($ constructors , "No constructors found for type: $ type " );
117+ $ methods = $ methods ? 'methods ' : 'constructors ' ;
118+ if (!$ ignoreEmpty ) {
119+ Assert::notEmpty ($ constructors , "No {$ methods } found for type: $ type " );
120+ }
115121 return $ constructors ;
116122 }
117123}
@@ -133,6 +139,18 @@ interface ActionOp extends Op
133139{
134140}
135141
142+ final class Noop implements ActionOp {
143+ public function getType (TLContext $ tl ): string
144+ {
145+ return '' ;
146+ }
147+
148+ public function build (TLContext $ tl ): array
149+ {
150+ return ['op ' => 'noop ' ];
151+ }
152+ }
153+
136154final class CopyMethodCallOp implements ActionOp
137155{
138156 public function __construct (private readonly string $ method )
@@ -500,7 +518,7 @@ public function build(TLContext $tl): array
500518
501519$ populateFileRefContext = static function (string $ type ) use ($ TL , &$ locations ): bool {
502520 if ($ type === 'Message ' ) {
503- foreach (TLContext::getConstructorsOfType ($ TL , $ type ) as $ constructor => $ _ ) {
521+ foreach (TLContext::getConstructorsOfType ($ TL , ' Message ' , false ) as $ constructor => $ _ ) {
504522 if ($ constructor === 'messageEmpty ' ) {
505523 continue ;
506524 }
@@ -509,6 +527,7 @@ public function build(TLContext $tl): array
509527 new ExtractFromHereOp ([$ constructor , 'id ' ]),
510528 );
511529 }
530+ $ locations ['messageEmpty ' ][] = new Noop ;
512531 return true ;
513532 }
514533 if ($ type === 'WebPage ' ) {
@@ -603,7 +622,7 @@ public function build(TLContext $tl): array
603622 return true ;
604623 }
605624 if ($ type === 'StarsTransaction ' ) {
606- foreach (TLContext::getConstructorsOfType ($ TL , $ type ) as $ constructor => $ isConstructor ) {
625+ foreach (TLContext::getConstructorsOfType ($ TL , ' StarsTransaction ' , false ) as $ constructor => $ isConstructor ) {
607626 if ($ isConstructor ) {
608627 continue ;
609628 }
@@ -802,10 +821,10 @@ public function build(TLContext $tl): array
802821 );
803822 return true ;
804823 }
805- return false ;
806- } ;
824+ if ( $ type === ' Document ' ) {
825+ $ locations [ ' messages.getDocumentByHash ' ] = new CopyMethodCallOp ( ' messages.getDocumentByHash ' ) ;
807826
808- $ recurse = static function (Closure $ populator , string $ type , array $ stack = []) use ($ TL , &$ recurse, & $ final , & $ locations ): void {
827+ $ recurse = static function (Closure $ populator , Closure $ onStackEnd , string $ type , array $ stack = []) use ($ TL , &$ recurse ): void {
809828 if ($ populator ($ type )) {
810829 return ;
811830 }
@@ -816,24 +835,29 @@ public function build(TLContext $tl): array
816835 foreach ($ constructor ['params ' ] as $ param ) {
817836 if ($ param ['type ' ] === $ type && !in_array ($ name , $ stack , true )) {
818837 $ stack [$ pos ] = $ name ;
819- $ recurse ($ populator , $ constructor ['type ' ], $ stack );
838+ $ recurse ($ populator , $ onStackEnd , $ constructor ['type ' ], $ stack );
820839 $ found = true ;
821840 }
822841 if (isset ($ param ['subtype ' ])
823842 && $ param ['subtype ' ] === $ type
824843 && !in_array ($ name , $ stack , true )
825844 ) {
826845 $ stack [$ pos ] = $ name ;
827- $ recurse ($ populator , $ constructor ['type ' ], $ stack );
846+ $ recurse ($ populator , $ onStackEnd , $ constructor ['type ' ], $ stack );
828847 $ found = true ;
829848 }
830849 }
831850 }
832- if (!$ found ) {
851+ unset($ stack [$ pos ]);
852+ if (!$ found
853+ || $ type === 'Update '
854+ || $ type === 'Updates '
855+ || TLContext::getConstructorsOfType ($ TL , $ type , true , true )
856+ ) {
833857 if (
834858 (
835859 in_array ($ stack [0 ], ['photo ' , 'document ' ], true )
836- && $ stack [1 ] === 'game '
860+ && ( $ stack [1 ] ?? null ) === 'game '
837861 && in_array (end ($ stack ), [
838862 'messages.webPagePreview ' ,
839863 'payments.starsStatus ' ,
@@ -853,20 +877,49 @@ public function build(TLContext $tl): array
853877 ) {
854878 return ;
855879 }
856- $ final [ json_encode ($ stack )]= $ stack ;
880+ $ onStackEnd ($ stack );
857881 }
858882};
859883
860- foreach (['Document ' => 'document ' , 'Photo ' => 'photo ' ] as $ type => $ constructor ) {
861- $ recurse ($ populateFileRefContext , $ type , [$ constructor ]);
884+ $ fileRefs = ['Document ' => 'document ' , 'Photo ' => 'photo ' ];
885+
886+ $ leftover = [];
887+
888+ foreach ($ fileRefs as $ type => $ constructor ) {
889+ $ recurse (
890+ $ populateFileRefContext ,
891+ function (array $ stack ) use (&$ leftover ): void {
892+ $ leftover [json_encode ($ stack )] = $ stack ;
893+ },
894+ $ type ,
895+ [$ constructor ]
896+ );
862897}
863898
864- if ($ final ) {
899+ if ($ leftover ) {
900+ var_dump ("Have leftover reference paths! " );
901+ var_dump (array_values ($ leftover ));
865902 var_dump ("Have leftover reference paths! " );
866- var_dump (array_values ($ final ));
867903 die (1 );
868904}
869905
906+ foreach ($ fileRefs as $ type => $ constructor ) {
907+ $ recurse (
908+ static fn () => false ,
909+ function (array $ stack ) use ($ TL , $ locations ): void {
910+ $ ok = true ;
911+ foreach ($ stack as $ constructor ) {
912+ $ ok = $ ok && isset ($ locations [$ constructor ]);
913+ }
914+ if (!$ ok ) {
915+ throw new AssertionError ("Uncovered path: " . json_encode ($ stack ));
916+ }
917+ },
918+ $ type ,
919+ [$ constructor ]
920+ );
921+ }
922+
870923foreach ($ locations as $ constructor => $ ops ) {
871924 var_dump ("Processing $ constructor " );
872925 foreach ($ ops as $ op ) {
0 commit comments