@@ -21,6 +21,7 @@ newtype SynthKind =
2121 BraceBlockKind ( ) or
2222 CaseMatchKind ( ) or
2323 ClassVariableAccessKind ( ClassVariable v ) or
24+ DefinedExprKind ( ) or
2425 DivExprKind ( ) or
2526 ElseKind ( ) or
2627 ExponentExprKind ( ) or
@@ -40,6 +41,7 @@ newtype SynthKind =
4041 ModuloExprKind ( ) or
4142 MulExprKind ( ) or
4243 NilLiteralKind ( ) or
44+ NotExprKind ( ) or
4345 RangeLiteralKind ( boolean inclusive ) { inclusive in [ false , true ] } or
4446 RShiftExprKind ( ) or
4547 SimpleParameterKind ( ) or
@@ -1258,6 +1260,7 @@ private module HashLiteralDesugar {
12581260 * ```
12591261 * desugars to, roughly,
12601262 * ```rb
1263+ * if not defined? x then x = nil end
12611264 * xs.each { |__synth__0| x = __synth__0; <loop_body> }
12621265 * ```
12631266 *
@@ -1267,58 +1270,160 @@ private module HashLiteralDesugar {
12671270 * scoped to the synthesized block.
12681271 */
12691272private module ForLoopDesugar {
1273+ private Ruby:: AstNode getForLoopPatternChild ( Ruby:: For for ) {
1274+ result = for .getPattern ( )
1275+ or
1276+ result .getParent ( ) = getForLoopPatternChild ( for )
1277+ }
1278+
1279+ /** Holds if `n` is an access to variable `v` in the pattern of `for`. */
1280+ pragma [ nomagic]
1281+ private predicate forLoopVariableAccess ( Ruby:: For for , Ruby:: AstNode n , VariableReal v ) {
1282+ n = getForLoopPatternChild ( for ) and
1283+ access ( n , v )
1284+ }
1285+
1286+ /** Holds if `v` is the `i`th iteration variable of `for`. */
1287+ private predicate forLoopVariable ( Ruby:: For for , VariableReal v , int i ) {
1288+ v =
1289+ rank [ i + 1 ] ( VariableReal v0 , Ruby:: AstNode n , Location l |
1290+ forLoopVariableAccess ( for , n , v0 ) and
1291+ l = n .getLocation ( )
1292+ |
1293+ v0 order by l .getStartLine ( ) , l .getStartColumn ( )
1294+ )
1295+ }
1296+
1297+ /** Gets the number of iteration variables of `for`. */
1298+ private int forLoopVariableCount ( Ruby:: For for ) {
1299+ result = count ( int j | forLoopVariable ( for , _, j ) )
1300+ }
1301+
1302+ private Ruby:: For toTsFor ( ForExpr for ) { for = TForExpr ( result ) }
1303+
1304+ /**
1305+ * Synthesizes an assignment
1306+ * ```rb
1307+ * if not defined? v then v = nil end
1308+ * ```
1309+ * anchored at index `rootIndex` of `root`.
1310+ */
1311+ bindingset [ root, rootIndex, v]
1312+ private predicate nilAssignUndefined (
1313+ AstNode root , int rootIndex , AstNode parent , int i , Child child , VariableReal v
1314+ ) {
1315+ parent = root and
1316+ i = rootIndex and
1317+ child = SynthChild ( IfKind ( ) )
1318+ or
1319+ exists ( AstNode if_ | if_ = TIfSynth ( root , rootIndex ) |
1320+ parent = if_ and
1321+ i = 0 and
1322+ child = SynthChild ( NotExprKind ( ) )
1323+ or
1324+ exists ( AstNode not_ | not_ = TNotExprSynth ( if_ , 0 ) |
1325+ parent = not_ and
1326+ i = 0 and
1327+ child = SynthChild ( DefinedExprKind ( ) )
1328+ or
1329+ parent = TDefinedExprSynth ( not_ , 0 ) and
1330+ i = 0 and
1331+ child = SynthChild ( LocalVariableAccessRealKind ( v ) )
1332+ )
1333+ or
1334+ parent = if_ and
1335+ i = 1 and
1336+ child = SynthChild ( AssignExprKind ( ) )
1337+ or
1338+ parent = TAssignExprSynth ( if_ , 1 ) and
1339+ (
1340+ i = 0 and
1341+ child = SynthChild ( LocalVariableAccessRealKind ( v ) )
1342+ or
1343+ i = 1 and
1344+ child = SynthChild ( NilLiteralKind ( ) )
1345+ )
1346+ )
1347+ }
1348+
12701349 pragma [ nomagic]
12711350 private predicate forLoopSynthesis ( AstNode parent , int i , Child child ) {
12721351 exists ( ForExpr for |
1273- // each call
12741352 parent = for and
12751353 i = - 1 and
1276- child = SynthChild ( MethodCallKind ( "each" , false , 0 ) )
1354+ child = SynthChild ( StmtSequenceKind ( ) )
12771355 or
1278- exists ( MethodCall eachCall | eachCall = TMethodCallSynth ( for , - 1 , "each" , false , 0 ) |
1279- // receiver
1280- parent = eachCall and
1281- i = 0 and
1282- child = childRef ( for .getValue ( ) ) // value is the Enumerable
1283- or
1284- parent = eachCall and
1285- i = 1 and
1286- child = SynthChild ( BraceBlockKind ( ) )
1356+ exists ( AstNode seq | seq = TStmtSequenceSynth ( for , - 1 ) |
1357+ exists ( VariableReal v , int j | forLoopVariable ( toTsFor ( for ) , v , j ) |
1358+ nilAssignUndefined ( seq , j , parent , i , child , v )
1359+ )
12871360 or
1288- exists ( Block block | block = TBraceBlockSynth ( eachCall , 1 ) |
1289- // block params
1290- parent = block and
1291- i = 0 and
1292- child = SynthChild ( SimpleParameterKind ( ) )
1361+ exists ( int numberOfVars | numberOfVars = forLoopVariableCount ( toTsFor ( for ) ) |
1362+ // each call
1363+ parent = seq and
1364+ i = numberOfVars and
1365+ child = SynthChild ( MethodCallKind ( "each" , false , 0 ) )
12931366 or
1294- exists ( SimpleParameter param | param = TSimpleParameterSynth ( block , 0 ) |
1295- parent = param and
1367+ exists ( MethodCall eachCall |
1368+ eachCall = TMethodCallSynth ( seq , numberOfVars , "each" , false , 0 )
1369+ |
1370+ // receiver
1371+ parent = eachCall and
12961372 i = 0 and
1297- child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
1373+ child = childRef ( for . getValue ( ) ) // value is the Enumerable
12981374 or
1299- // assignment to pattern from for loop to synth parameter
1300- parent = block and
1375+ parent = eachCall and
13011376 i = 1 and
1302- child = SynthChild ( AssignExprKind ( ) )
1377+ child = SynthChild ( BraceBlockKind ( ) )
13031378 or
1304- parent = TAssignExprSynth ( block , 1 ) and
1305- (
1379+ exists ( Block block | block = TBraceBlockSynth ( eachCall , 1 ) |
1380+ // block params
1381+ parent = block and
13061382 i = 0 and
1307- child = childRef ( for . getPattern ( ) )
1383+ child = SynthChild ( SimpleParameterKind ( ) )
13081384 or
1309- i = 1 and
1310- child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
1385+ exists ( SimpleParameter param | param = TSimpleParameterSynth ( block , 0 ) |
1386+ parent = param and
1387+ i = 0 and
1388+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
1389+ or
1390+ // assignment to pattern from for loop to synth parameter
1391+ parent = block and
1392+ i = 1 and
1393+ child = SynthChild ( AssignExprKind ( ) )
1394+ or
1395+ parent = TAssignExprSynth ( block , 1 ) and
1396+ (
1397+ i = 0 and
1398+ child = childRef ( for .getPattern ( ) )
1399+ or
1400+ i = 1 and
1401+ child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
1402+ )
1403+ )
1404+ or
1405+ // rest of block body
1406+ parent = block and
1407+ child = childRef ( for .getBody ( ) .( Do ) .getStmt ( i - 2 ) )
13111408 )
13121409 )
1313- or
1314- // rest of block body
1315- parent = block and
1316- child = childRef ( for .getBody ( ) .( Do ) .getStmt ( i - 2 ) )
13171410 )
13181411 )
13191412 )
13201413 }
13211414
1415+ pragma [ nomagic]
1416+ private predicate isDesugaredInitNode ( ForExpr for , Variable v , AstNode n ) {
1417+ exists ( StmtSequence seq , AssignExpr ae |
1418+ seq = for .getDesugared ( ) and
1419+ n = seq .getStmt ( _) and
1420+ ae = n .( IfExpr ) .getThen ( ) and
1421+ v = ae .getLeftOperand ( ) .getAVariable ( )
1422+ )
1423+ or
1424+ isDesugaredInitNode ( for , v , n .getParent ( ) )
1425+ }
1426+
13221427 private class ForLoopSynthesis extends Synthesis {
13231428 final override predicate child ( AstNode parent , int i , Child child ) {
13241429 forLoopSynthesis ( parent , i , child )
@@ -1338,6 +1443,14 @@ private module ForLoopDesugar {
13381443 final override predicate excludeFromControlFlowTree ( AstNode n ) {
13391444 n = any ( ForExpr for ) .getBody ( )
13401445 }
1446+
1447+ final override predicate location ( AstNode n , Location l ) {
1448+ exists ( ForExpr for , Ruby:: AstNode access , Variable v |
1449+ forLoopVariableAccess ( toTsFor ( for ) , access , v ) and
1450+ isDesugaredInitNode ( for , v , n ) and
1451+ l = access .getLocation ( )
1452+ )
1453+ }
13411454 }
13421455}
13431456
0 commit comments