@@ -155,6 +155,22 @@ module ModelInput {
155
155
*/
156
156
abstract predicate row ( string row ) ;
157
157
}
158
+
159
+ /**
160
+ * A unit class for adding additional type variable model rows.
161
+ */
162
+ class TypeVariableModelCsv extends Unit {
163
+ /**
164
+ * Holds if `row` specifies a path through a type variable.
165
+ *
166
+ * A row of form,
167
+ * ```
168
+ * name;path
169
+ * ```
170
+ * means `path` can be substituted for a token `TypeVar[name]`.
171
+ */
172
+ abstract predicate row ( string row ) ;
173
+ }
158
174
}
159
175
160
176
private import ModelInput
@@ -182,6 +198,8 @@ private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inverseP
182
198
183
199
private predicate typeModel ( string row ) { any ( TypeModelCsv s ) .row ( inversePad ( row ) ) }
184
200
201
+ private predicate typeVariableModel ( string row ) { any ( TypeVariableModelCsv s ) .row ( inversePad ( row ) ) }
202
+
185
203
/** Holds if a source model exists for the given parameters. */
186
204
predicate sourceModel ( string package , string type , string path , string kind ) {
187
205
exists ( string row |
@@ -219,7 +237,7 @@ private predicate summaryModel(
219
237
)
220
238
}
221
239
222
- /** Holds if an type model exists for the given parameters. */
240
+ /** Holds if a type model exists for the given parameters. */
223
241
private predicate typeModel (
224
242
string package1 , string type1 , string package2 , string type2 , string path
225
243
) {
@@ -233,6 +251,15 @@ private predicate typeModel(
233
251
)
234
252
}
235
253
254
+ /** Holds if a type variable model exists for the given parameters. */
255
+ private predicate typeVariableModel ( string name , string path ) {
256
+ exists ( string row |
257
+ typeVariableModel ( row ) and
258
+ row .splitAt ( ";" , 0 ) = name and
259
+ row .splitAt ( ";" , 1 ) = path
260
+ )
261
+ }
262
+
236
263
/**
237
264
* Gets a package that should be seen as an alias for the given other `package`,
238
265
* or the `package` itself.
@@ -253,7 +280,7 @@ private predicate isRelevantPackage(string package) {
253
280
sourceModel ( package , _, _, _) or
254
281
sinkModel ( package , _, _, _) or
255
282
summaryModel ( package , _, _, _, _, _) or
256
- typeModel ( package , _, _ , _, _)
283
+ typeModel ( _ , _, package , _, _)
257
284
) and
258
285
(
259
286
Specific:: isPackageUsed ( package )
@@ -290,6 +317,8 @@ private class AccessPathRange extends AccessPath::Range {
290
317
summaryModel ( package , _, _, this , _, _) or
291
318
summaryModel ( package , _, _, _, this , _)
292
319
)
320
+ or
321
+ typeVariableModel ( _, this )
293
322
}
294
323
}
295
324
@@ -361,13 +390,91 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
361
390
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
362
391
result =
363
392
getSuccessorFromInvoke ( getInvocationFromPath ( package , type , path , n - 1 ) , path .getToken ( n - 1 ) )
393
+ or
394
+ // Apply a subpath
395
+ result =
396
+ getNodeFromSubPath ( getNodeFromPath ( package , type , path , n - 1 ) , getSubPathAt ( path , n - 1 ) )
397
+ or
398
+ // Apply a receiver step
399
+ typeStep ( getNodeFromPath ( package , type , path , n ) , result )
400
+ }
401
+
402
+ /**
403
+ * Gets a subpath for the `TypeVar` token found at the `n`th token of `path`.
404
+ */
405
+ pragma [ nomagic]
406
+ private AccessPath getSubPathAt ( AccessPath path , int n ) {
407
+ exists ( string typeVarName |
408
+ path .getToken ( n ) .getAnArgument ( "TypeVar" ) = typeVarName and
409
+ typeVariableModel ( typeVarName , result )
410
+ )
411
+ }
412
+
413
+ /**
414
+ * Gets a node that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
415
+ */
416
+ pragma [ nomagic]
417
+ private API:: Node getNodeFromSubPath ( API:: Node base , AccessPath subPath , int n ) {
418
+ exists ( AccessPath path , int k |
419
+ base = [ getNodeFromPath ( _, _, path , k ) , getNodeFromSubPath ( _, path , k ) ] and
420
+ subPath = getSubPathAt ( path , k ) and
421
+ result = base and
422
+ n = 0
423
+ )
424
+ or
425
+ exists ( string package , string type , AccessPath basePath |
426
+ typeStepModel ( package , type , basePath , subPath ) and
427
+ base = getNodeFromPath ( package , type , basePath ) and
428
+ result = base and
429
+ n = 0
430
+ )
431
+ or
432
+ result = getSuccessorFromNode ( getNodeFromSubPath ( base , subPath , n - 1 ) , subPath .getToken ( n - 1 ) )
433
+ or
434
+ result =
435
+ getSuccessorFromInvoke ( getInvocationFromSubPath ( base , subPath , n - 1 ) , subPath .getToken ( n - 1 ) )
436
+ or
437
+ result =
438
+ getNodeFromSubPath ( getNodeFromSubPath ( base , subPath , n - 1 ) , getSubPathAt ( subPath , n - 1 ) )
439
+ }
440
+
441
+ /**
442
+ * Gets a call site that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
443
+ */
444
+ private Specific:: InvokeNode getInvocationFromSubPath ( API:: Node base , AccessPath subPath , int n ) {
445
+ result = Specific:: getAnInvocationOf ( getNodeFromSubPath ( base , subPath , n ) )
446
+ or
447
+ result = getInvocationFromSubPath ( base , subPath , n - 1 ) and
448
+ invocationMatchesCallSiteFilter ( result , subPath .getToken ( n - 1 ) )
449
+ }
450
+
451
+ /**
452
+ * Gets a node that is found by evaluating `subPath` starting at `base`.
453
+ */
454
+ pragma [ nomagic]
455
+ private API:: Node getNodeFromSubPath ( API:: Node base , AccessPath subPath ) {
456
+ result = getNodeFromSubPath ( base , subPath , subPath .getNumToken ( ) )
364
457
}
365
458
366
459
/** Gets the node identified by the given `(package, type, path)` tuple. */
367
460
API:: Node getNodeFromPath ( string package , string type , AccessPath path ) {
368
461
result = getNodeFromPath ( package , type , path , path .getNumToken ( ) )
369
462
}
370
463
464
+ pragma [ nomagic]
465
+ private predicate typeStepModel ( string package , string type , AccessPath basePath , AccessPath output ) {
466
+ summaryModel ( package , type , basePath , "" , output , "type" )
467
+ }
468
+
469
+ pragma [ nomagic]
470
+ private predicate typeStep ( API:: Node pred , API:: Node succ ) {
471
+ exists ( string package , string type , AccessPath basePath , AccessPath output |
472
+ typeStepModel ( package , type , basePath , output ) and
473
+ pred = getNodeFromPath ( package , type , basePath ) and
474
+ succ = getNodeFromSubPath ( pred , output )
475
+ )
476
+ }
477
+
371
478
/**
372
479
* Gets an invocation identified by the given `(package, type, path)` tuple.
373
480
*
@@ -390,7 +497,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390
497
*/
391
498
bindingset [ name]
392
499
predicate isValidTokenNameInIdentifyingAccessPath ( string name ) {
393
- name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" ]
500
+ name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" , "TypeVar" ]
394
501
or
395
502
Specific:: isExtraValidTokenNameInIdentifyingAccessPath ( name )
396
503
}
@@ -418,6 +525,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418
525
name = "WithArity" and
419
526
argument .regexpMatch ( "\\d+(\\.\\.(\\d+)?)?" )
420
527
or
528
+ name = "TypeVar" and
529
+ exists ( argument )
530
+ or
421
531
Specific:: isExtraValidTokenArgumentInIdentifyingAccessPath ( name , argument )
422
532
}
423
533
@@ -489,6 +599,8 @@ module ModelOutput {
489
599
any ( SummaryModelCsv csv ) .row ( row ) and kind = "summary" and expectedArity = 6
490
600
or
491
601
any ( TypeModelCsv csv ) .row ( row ) and kind = "type" and expectedArity = 5
602
+ or
603
+ any ( TypeVariableModelCsv csv ) .row ( row ) and kind = "type-variable" and expectedArity = 2
492
604
|
493
605
actualArity = count ( row .indexOf ( ";" ) ) + 1 and
494
606
actualArity != expectedArity and
@@ -499,7 +611,7 @@ module ModelOutput {
499
611
or
500
612
// Check names and arguments of access path tokens
501
613
exists ( AccessPath path , AccessPathToken token |
502
- isRelevantFullPath ( _, _, path ) and
614
+ ( isRelevantFullPath ( _, _, path ) or typeVariableModel ( _ , path ) ) and
503
615
token = path .getToken ( _)
504
616
|
505
617
not isValidTokenNameInIdentifyingAccessPath ( token .getName ( ) ) and
0 commit comments