@@ -816,3 +816,90 @@ private module ArrayLiteralDesugar {
816
816
final override predicate constantReadAccess ( string name ) { name = "::Array" }
817
817
}
818
818
}
819
+
820
+ /**
821
+ * ```rb
822
+ * for x in xs
823
+ * <loop_body>
824
+ * end
825
+ * ```
826
+ * desugars to, roughly,
827
+ * ```rb
828
+ * xs.each { |__synth__0| x = __synth__0; <loop_body> }
829
+ * ```
830
+ *
831
+ * Note that for-loops, unlike blocks, do not create a new variable scope, so
832
+ * variables within this block inherit the enclosing scope. The exception to
833
+ * this is the synthesized variable declared by the block parameter, which is
834
+ * scoped to the synthesized block.
835
+ */
836
+ private module ForLoopDesugar {
837
+ private predicate forLoopSynthesis ( AstNode parent , int i , Child child ) {
838
+ exists ( ForExpr for |
839
+ // each call
840
+ parent = for and
841
+ i = - 1 and
842
+ child = SynthChild ( MethodCallKind ( "each" , false , 0 ) )
843
+ or
844
+ exists ( MethodCall eachCall | eachCall = TMethodCallSynth ( for , - 1 , "each" , false , 0 ) |
845
+ // receiver
846
+ parent = eachCall and
847
+ i = 0 and
848
+ child = RealChild ( for .getValue ( ) ) // value is the Enumerable
849
+ or
850
+ parent = eachCall and
851
+ i = - 2 and
852
+ child = SynthChild ( BlockKind ( ) )
853
+ or
854
+ exists ( Block block | block = TBlockSynth ( eachCall , - 2 ) |
855
+ // block params
856
+ parent = block and
857
+ i = 0 and
858
+ child = SynthChild ( SimpleParameterKind ( ) )
859
+ or
860
+ exists ( SimpleParameter param | param = TSimpleParameterSynth ( block , 0 ) |
861
+ parent = param and
862
+ i = 0 and
863
+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
864
+ or
865
+ // assignment to pattern from for loop to synth parameter
866
+ parent = block and
867
+ i = 1 and
868
+ child = SynthChild ( AssignExprKind ( ) )
869
+ or
870
+ parent = TAssignExprSynth ( block , 1 ) and
871
+ (
872
+ i = 0 and
873
+ child = RealChild ( for .getPattern ( ) )
874
+ or
875
+ i = 1 and
876
+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
877
+ )
878
+ )
879
+ or
880
+ // rest of block body
881
+ parent = block and
882
+ i = 2 and
883
+ child = RealChild ( for .getBody ( ) )
884
+ )
885
+ )
886
+ )
887
+ }
888
+
889
+ private class ForLoopSynthesis extends Synthesis {
890
+ final override predicate child ( AstNode parent , int i , Child child ) {
891
+ forLoopSynthesis ( parent , i , child )
892
+ }
893
+
894
+ final override predicate methodCall ( string name , boolean setter , int arity ) {
895
+ name = "each" and
896
+ setter = false and
897
+ arity = 0
898
+ }
899
+
900
+ final override predicate localVariable ( AstNode n , int i ) {
901
+ n instanceof TSimpleParameterSynth and
902
+ i = 0
903
+ }
904
+ }
905
+ }
0 commit comments