@@ -21,6 +21,7 @@ newtype SynthKind =
21
21
BraceBlockKind ( ) or
22
22
CaseMatchKind ( ) or
23
23
ClassVariableAccessKind ( ClassVariable v ) or
24
+ DefinedExprKind ( ) or
24
25
DivExprKind ( ) or
25
26
ElseKind ( ) or
26
27
ExponentExprKind ( ) or
@@ -40,6 +41,7 @@ newtype SynthKind =
40
41
ModuloExprKind ( ) or
41
42
MulExprKind ( ) or
42
43
NilLiteralKind ( ) or
44
+ NotExprKind ( ) or
43
45
RangeLiteralKind ( boolean inclusive ) { inclusive in [ false , true ] } or
44
46
RShiftExprKind ( ) or
45
47
SimpleParameterKind ( ) or
@@ -1258,6 +1260,7 @@ private module HashLiteralDesugar {
1258
1260
* ```
1259
1261
* desugars to, roughly,
1260
1262
* ```rb
1263
+ * if not defined? x then x = nil end
1261
1264
* xs.each { |__synth__0| x = __synth__0; <loop_body> }
1262
1265
* ```
1263
1266
*
@@ -1267,58 +1270,160 @@ private module HashLiteralDesugar {
1267
1270
* scoped to the synthesized block.
1268
1271
*/
1269
1272
private 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
+
1270
1349
pragma [ nomagic]
1271
1350
private predicate forLoopSynthesis ( AstNode parent , int i , Child child ) {
1272
1351
exists ( ForExpr for |
1273
- // each call
1274
1352
parent = for and
1275
1353
i = - 1 and
1276
- child = SynthChild ( MethodCallKind ( "each" , false , 0 ) )
1354
+ child = SynthChild ( StmtSequenceKind ( ) )
1277
1355
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
+ )
1287
1360
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 ) )
1293
1366
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
1296
1372
i = 0 and
1297
- child = SynthChild ( LocalVariableAccessSynthKind ( TLocalVariableSynth ( param , 0 ) ) )
1373
+ child = childRef ( for . getValue ( ) ) // value is the Enumerable
1298
1374
or
1299
- // assignment to pattern from for loop to synth parameter
1300
- parent = block and
1375
+ parent = eachCall and
1301
1376
i = 1 and
1302
- child = SynthChild ( AssignExprKind ( ) )
1377
+ child = SynthChild ( BraceBlockKind ( ) )
1303
1378
or
1304
- parent = TAssignExprSynth ( block , 1 ) and
1305
- (
1379
+ exists ( Block block | block = TBraceBlockSynth ( eachCall , 1 ) |
1380
+ // block params
1381
+ parent = block and
1306
1382
i = 0 and
1307
- child = childRef ( for . getPattern ( ) )
1383
+ child = SynthChild ( SimpleParameterKind ( ) )
1308
1384
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 ) )
1311
1408
)
1312
1409
)
1313
- or
1314
- // rest of block body
1315
- parent = block and
1316
- child = childRef ( for .getBody ( ) .( Do ) .getStmt ( i - 2 ) )
1317
1410
)
1318
1411
)
1319
1412
)
1320
1413
}
1321
1414
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
+
1322
1427
private class ForLoopSynthesis extends Synthesis {
1323
1428
final override predicate child ( AstNode parent , int i , Child child ) {
1324
1429
forLoopSynthesis ( parent , i , child )
@@ -1338,6 +1443,14 @@ private module ForLoopDesugar {
1338
1443
final override predicate excludeFromControlFlowTree ( AstNode n ) {
1339
1444
n = any ( ForExpr for ) .getBody ( )
1340
1445
}
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
+ }
1341
1454
}
1342
1455
}
1343
1456
0 commit comments