@@ -816,3 +816,90 @@ private module ArrayLiteralDesugar {
816816 final override predicate constantReadAccess ( string name ) { name = "::Array" }
817817 }
818818}
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