@@ -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,93 @@ 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 type 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
+ or
440
+ typeStep ( getNodeFromSubPath ( base , subPath , n ) , result )
441
+ }
442
+
443
+ /**
444
+ * Gets a call site that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
445
+ */
446
+ private Specific:: InvokeNode getInvocationFromSubPath ( API:: Node base , AccessPath subPath , int n ) {
447
+ result = Specific:: getAnInvocationOf ( getNodeFromSubPath ( base , subPath , n ) )
448
+ or
449
+ result = getInvocationFromSubPath ( base , subPath , n - 1 ) and
450
+ invocationMatchesCallSiteFilter ( result , subPath .getToken ( n - 1 ) )
451
+ }
452
+
453
+ /**
454
+ * Gets a node that is found by evaluating `subPath` starting at `base`.
455
+ */
456
+ pragma [ nomagic]
457
+ private API:: Node getNodeFromSubPath ( API:: Node base , AccessPath subPath ) {
458
+ result = getNodeFromSubPath ( base , subPath , subPath .getNumToken ( ) )
364
459
}
365
460
366
461
/** Gets the node identified by the given `(package, type, path)` tuple. */
367
462
API:: Node getNodeFromPath ( string package , string type , AccessPath path ) {
368
463
result = getNodeFromPath ( package , type , path , path .getNumToken ( ) )
369
464
}
370
465
466
+ pragma [ nomagic]
467
+ private predicate typeStepModel ( string package , string type , AccessPath basePath , AccessPath output ) {
468
+ summaryModel ( package , type , basePath , "" , output , "type" )
469
+ }
470
+
471
+ pragma [ nomagic]
472
+ private predicate typeStep ( API:: Node pred , API:: Node succ ) {
473
+ exists ( string package , string type , AccessPath basePath , AccessPath output |
474
+ typeStepModel ( package , type , basePath , output ) and
475
+ pred = getNodeFromPath ( package , type , basePath ) and
476
+ succ = getNodeFromSubPath ( pred , output )
477
+ )
478
+ }
479
+
371
480
/**
372
481
* Gets an invocation identified by the given `(package, type, path)` tuple.
373
482
*
@@ -390,7 +499,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390
499
*/
391
500
bindingset [ name]
392
501
predicate isValidTokenNameInIdentifyingAccessPath ( string name ) {
393
- name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" ]
502
+ name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" , "TypeVar" ]
394
503
or
395
504
Specific:: isExtraValidTokenNameInIdentifyingAccessPath ( name )
396
505
}
@@ -418,6 +527,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418
527
name = "WithArity" and
419
528
argument .regexpMatch ( "\\d+(\\.\\.(\\d+)?)?" )
420
529
or
530
+ name = "TypeVar" and
531
+ exists ( argument )
532
+ or
421
533
Specific:: isExtraValidTokenArgumentInIdentifyingAccessPath ( name , argument )
422
534
}
423
535
@@ -489,6 +601,8 @@ module ModelOutput {
489
601
any ( SummaryModelCsv csv ) .row ( row ) and kind = "summary" and expectedArity = 6
490
602
or
491
603
any ( TypeModelCsv csv ) .row ( row ) and kind = "type" and expectedArity = 5
604
+ or
605
+ any ( TypeVariableModelCsv csv ) .row ( row ) and kind = "type-variable" and expectedArity = 2
492
606
|
493
607
actualArity = count ( row .indexOf ( ";" ) ) + 1 and
494
608
actualArity != expectedArity and
@@ -499,7 +613,7 @@ module ModelOutput {
499
613
or
500
614
// Check names and arguments of access path tokens
501
615
exists ( AccessPath path , AccessPathToken token |
502
- isRelevantFullPath ( _, _, path ) and
616
+ ( isRelevantFullPath ( _, _, path ) or typeVariableModel ( _ , path ) ) and
503
617
token = path .getToken ( _)
504
618
|
505
619
not isValidTokenNameInIdentifyingAccessPath ( token .getName ( ) ) and
0 commit comments