@@ -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.
@@ -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,6 +390,60 @@ 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
+ }
398
+
399
+ /**
400
+ * Gets a subpath for the `TypeVar` token found at the `n`th token of `path`.
401
+ */
402
+ pragma [ nomagic]
403
+ private AccessPath getSubPathAt ( AccessPath path , int n ) {
404
+ exists ( string typeVarName |
405
+ path .getToken ( n ) .getAnArgument ( "TypeVar" ) = typeVarName and
406
+ typeVariableModel ( typeVarName , result )
407
+ )
408
+ }
409
+
410
+ /**
411
+ * Gets a node that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
412
+ */
413
+ pragma [ nomagic]
414
+ private API:: Node getNodeFromSubPath ( API:: Node base , AccessPath subPath , int n ) {
415
+ exists ( AccessPath path , int k |
416
+ base = [ getNodeFromPath ( _, _, path , k ) , getNodeFromSubPath ( _, path , k ) ] and
417
+ subPath = getSubPathAt ( path , k ) and
418
+ result = base and
419
+ n = 0
420
+ )
421
+ or
422
+ result = getSuccessorFromNode ( getNodeFromSubPath ( base , subPath , n - 1 ) , subPath .getToken ( n - 1 ) )
423
+ or
424
+ result =
425
+ getSuccessorFromInvoke ( getInvocationFromSubPath ( base , subPath , n - 1 ) , subPath .getToken ( n - 1 ) )
426
+ or
427
+ result =
428
+ getNodeFromSubPath ( getNodeFromSubPath ( base , subPath , n - 1 ) , getSubPathAt ( subPath , n - 1 ) )
429
+ }
430
+
431
+ /**
432
+ * Gets a call site that is found by evaluating the first `n` tokens of `subPath` starting at `base`.
433
+ */
434
+ private Specific:: InvokeNode getInvocationFromSubPath ( API:: Node base , AccessPath subPath , int n ) {
435
+ result = Specific:: getAnInvocationOf ( getNodeFromSubPath ( base , subPath , n ) )
436
+ or
437
+ result = getInvocationFromSubPath ( base , subPath , n - 1 ) and
438
+ invocationMatchesCallSiteFilter ( result , subPath .getToken ( n - 1 ) )
439
+ }
440
+
441
+ /**
442
+ * Gets a node that is found by evaluating `subPath` starting at `base`.
443
+ */
444
+ pragma [ nomagic]
445
+ private API:: Node getNodeFromSubPath ( API:: Node base , AccessPath subPath ) {
446
+ result = getNodeFromSubPath ( base , subPath , subPath .getNumToken ( ) )
364
447
}
365
448
366
449
/** Gets the node identified by the given `(package, type, path)` tuple. */
@@ -390,7 +473,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390
473
*/
391
474
bindingset [ name]
392
475
predicate isValidTokenNameInIdentifyingAccessPath ( string name ) {
393
- name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" ]
476
+ name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" , "TypeVar" ]
394
477
or
395
478
Specific:: isExtraValidTokenNameInIdentifyingAccessPath ( name )
396
479
}
@@ -418,6 +501,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418
501
name = "WithArity" and
419
502
argument .regexpMatch ( "\\d+(\\.\\.(\\d+)?)?" )
420
503
or
504
+ name = "TypeVar" and
505
+ exists ( argument )
506
+ or
421
507
Specific:: isExtraValidTokenArgumentInIdentifyingAccessPath ( name , argument )
422
508
}
423
509
@@ -489,6 +575,8 @@ module ModelOutput {
489
575
any ( SummaryModelCsv csv ) .row ( row ) and kind = "summary" and expectedArity = 6
490
576
or
491
577
any ( TypeModelCsv csv ) .row ( row ) and kind = "type" and expectedArity = 5
578
+ or
579
+ any ( TypeVariableModelCsv csv ) .row ( row ) and kind = "type-variable" and expectedArity = 2
492
580
|
493
581
actualArity = count ( row .indexOf ( ";" ) ) + 1 and
494
582
actualArity != expectedArity and
@@ -499,7 +587,7 @@ module ModelOutput {
499
587
or
500
588
// Check names and arguments of access path tokens
501
589
exists ( AccessPath path , AccessPathToken token |
502
- isRelevantFullPath ( _, _, path ) and
590
+ ( isRelevantFullPath ( _, _, path ) or typeVariableModel ( _ , path ) ) and
503
591
token = path .getToken ( _)
504
592
|
505
593
not isValidTokenNameInIdentifyingAccessPath ( token .getName ( ) ) and
0 commit comments