@@ -155,6 +155,22 @@ module ModelInput {
155155 */
156156 abstract predicate row ( string row ) ;
157157 }
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+ }
158174}
159175
160176private import ModelInput
@@ -182,6 +198,8 @@ private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inverseP
182198
183199private predicate typeModel ( string row ) { any ( TypeModelCsv s ) .row ( inversePad ( row ) ) }
184200
201+ private predicate typeVariableModel ( string row ) { any ( TypeVariableModelCsv s ) .row ( inversePad ( row ) ) }
202+
185203/** Holds if a source model exists for the given parameters. */
186204predicate sourceModel ( string package , string type , string path , string kind ) {
187205 exists ( string row |
@@ -219,7 +237,7 @@ private predicate summaryModel(
219237 )
220238}
221239
222- /** Holds if an type model exists for the given parameters. */
240+ /** Holds if a type model exists for the given parameters. */
223241private predicate typeModel (
224242 string package1 , string type1 , string package2 , string type2 , string path
225243) {
@@ -233,6 +251,15 @@ private predicate typeModel(
233251 )
234252}
235253
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+
236263/**
237264 * Gets a package that should be seen as an alias for the given other `package`,
238265 * or the `package` itself.
@@ -290,6 +317,8 @@ private class AccessPathRange extends AccessPath::Range {
290317 summaryModel ( package , _, _, this , _, _) or
291318 summaryModel ( package , _, _, _, this , _)
292319 )
320+ or
321+ typeVariableModel ( _, this )
293322 }
294323}
295324
@@ -361,6 +390,60 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
361390 // Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
362391 result =
363392 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 ( ) )
364447}
365448
366449/** Gets the node identified by the given `(package, type, path)` tuple. */
@@ -390,7 +473,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390473 */
391474bindingset [ name]
392475predicate isValidTokenNameInIdentifyingAccessPath ( string name ) {
393- name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" ]
476+ name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" , "TypeVar" ]
394477 or
395478 Specific:: isExtraValidTokenNameInIdentifyingAccessPath ( name )
396479}
@@ -418,6 +501,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418501 name = "WithArity" and
419502 argument .regexpMatch ( "\\d+(\\.\\.(\\d+)?)?" )
420503 or
504+ name = "TypeVar" and
505+ exists ( argument )
506+ or
421507 Specific:: isExtraValidTokenArgumentInIdentifyingAccessPath ( name , argument )
422508}
423509
@@ -489,6 +575,8 @@ module ModelOutput {
489575 any ( SummaryModelCsv csv ) .row ( row ) and kind = "summary" and expectedArity = 6
490576 or
491577 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
492580 |
493581 actualArity = count ( row .indexOf ( ";" ) ) + 1 and
494582 actualArity != expectedArity and
@@ -499,7 +587,7 @@ module ModelOutput {
499587 or
500588 // Check names and arguments of access path tokens
501589 exists ( AccessPath path , AccessPathToken token |
502- isRelevantFullPath ( _, _, path ) and
590+ ( isRelevantFullPath ( _, _, path ) or typeVariableModel ( _ , path ) ) and
503591 token = path .getToken ( _)
504592 |
505593 not isValidTokenNameInIdentifyingAccessPath ( token .getName ( ) ) and
0 commit comments