@@ -797,3 +797,112 @@ test("while dead code after return", () => {
797797
798798 expect ( result ) . toBe ( 3 ) ;
799799} ) ;
800+
801+ test . each ( [
802+ { args : [ 1 , 10 ] , expectResult : [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] } ,
803+ { args : [ 1 , 10 , 2 ] , expectResult : [ 1 , 3 , 5 , 7 , 9 ] } ,
804+ { args : [ 10 , 1 , - 1 ] , expectResult : [ 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 ] } ,
805+ { args : [ 10 , 1 , - 2 ] , expectResult : [ 10 , 8 , 6 , 4 , 2 ] } ,
806+ ] ) ( "@forRange loop" , ( { args, expectResult } ) => {
807+ const tsHeader = "/** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[];" ;
808+ const code = `
809+ const results: number[] = [];
810+ for (const i of luaRange(${ args } )) {
811+ results.push(i);
812+ }
813+ return JSONStringify(results);` ;
814+
815+ const result = util . transpileAndExecute ( code , undefined , undefined , tsHeader ) ;
816+ expect ( JSON . parse ( result ) ) . toEqual ( expectResult ) ;
817+ } ) ;
818+
819+ test ( "invalid non-ambient @forRange function" , ( ) => {
820+ const code = `
821+ /** @forRange **/ function luaRange(i: number, j: number, k?: number): number[] { return []; }
822+ for (const i of luaRange(1, 10, 2)) {}` ;
823+
824+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
825+ TSTLErrors . InvalidForRangeCall (
826+ ts . createEmptyStatement ( ) ,
827+ "@forRange function can only be used as an iterable in a for...of loop."
828+ ) . message
829+ ) ;
830+ } ) ;
831+
832+ test . each ( [ [ 1 ] , [ 1 , 2 , 3 , 4 ] ] ) ( "invalid @forRange argument count" , args => {
833+ const code = `
834+ /** @forRange **/ declare function luaRange(...args: number[]): number[] { return []; }
835+ for (const i of luaRange(${ args } )) {}` ;
836+
837+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
838+ TSTLErrors . InvalidForRangeCall ( ts . createEmptyStatement ( ) , "@forRange function must take 2 or 3 arguments." )
839+ . message
840+ ) ;
841+ } ) ;
842+
843+ test ( "invalid @forRange control variable" , ( ) => {
844+ const code = `
845+ /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[];
846+ let i: number;
847+ for (i of luaRange(1, 10, 2)) {}` ;
848+
849+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
850+ TSTLErrors . InvalidForRangeCall (
851+ ts . createEmptyStatement ( ) ,
852+ "@forRange loop must declare its own control variable."
853+ ) . message
854+ ) ;
855+ } ) ;
856+
857+ test ( "invalid @forRange argument type" , ( ) => {
858+ const code = `
859+ /** @forRange **/ declare function luaRange(i: string, j: number): number[] { return []; }
860+ for (const i of luaRange("foo", 2)) {}` ;
861+
862+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
863+ TSTLErrors . InvalidForRangeCall ( ts . createEmptyStatement ( ) , "@forRange arguments must be number types." ) . message
864+ ) ;
865+ } ) ;
866+
867+ test ( "invalid @forRange destructuring" , ( ) => {
868+ const code = `
869+ /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[][];
870+ for (const [i] of luaRange(1, 10, 2)) {}` ;
871+
872+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
873+ TSTLErrors . InvalidForRangeCall ( ts . createEmptyStatement ( ) , "@forRange loop cannot use destructuring." ) . message
874+ ) ;
875+ } ) ;
876+
877+ test ( "invalid @forRange return type" , ( ) => {
878+ const code = `
879+ /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): string[];
880+ for (const i of luaRange(1, 10)) {}` ;
881+
882+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
883+ TSTLErrors . InvalidForRangeCall (
884+ ts . createEmptyStatement ( ) ,
885+ "@forRange function must return Iterable<number> or Array<number>."
886+ ) . message
887+ ) ;
888+ } ) ;
889+
890+ test . each ( [
891+ "const range = luaRange(1, 10);" ,
892+ "console.log(luaRange);" ,
893+ "luaRange.call(null, 0, 0, 0);" ,
894+ "let array = [0, luaRange, 1];" ,
895+ "const call: any; call(luaRange);" ,
896+ "for (const i of [...luaRange(1, 10)]) {}" ,
897+ ] ) ( "invalid @forRange reference (%p)" , statement => {
898+ const code = `
899+ /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[];
900+ ${ statement } ` ;
901+
902+ expect ( ( ) => util . transpileString ( code ) ) . toThrow (
903+ TSTLErrors . InvalidForRangeCall (
904+ ts . createEmptyStatement ( ) ,
905+ "@forRange function can only be used as an iterable in a for...of loop."
906+ ) . message
907+ ) ;
908+ } ) ;
0 commit comments