@@ -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.
@@ -253,7 +280,7 @@ private predicate isRelevantPackage(string package) {
253280 sourceModel ( package , _, _, _) or
254281 sinkModel ( package , _, _, _) or
255282 summaryModel ( package , _, _, _, _, _) or
256- typeModel ( package , _, _ , _, _)
283+ typeModel ( _ , _, package , _, _)
257284 ) and
258285 (
259286 Specific:: isPackageUsed ( package )
@@ -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,13 +390,93 @@ 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+ 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 ( ) )
364459}
365460
366461/** Gets the node identified by the given `(package, type, path)` tuple. */
367462API:: Node getNodeFromPath ( string package , string type , AccessPath path ) {
368463 result = getNodeFromPath ( package , type , path , path .getNumToken ( ) )
369464}
370465
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+
371480/**
372481 * Gets an invocation identified by the given `(package, type, path)` tuple.
373482 *
@@ -390,7 +499,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390499 */
391500bindingset [ name]
392501predicate isValidTokenNameInIdentifyingAccessPath ( string name ) {
393- name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" ]
502+ name = [ "Argument" , "Parameter" , "ReturnValue" , "WithArity" , "TypeVar" ]
394503 or
395504 Specific:: isExtraValidTokenNameInIdentifyingAccessPath ( name )
396505}
@@ -418,6 +527,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418527 name = "WithArity" and
419528 argument .regexpMatch ( "\\d+(\\.\\.(\\d+)?)?" )
420529 or
530+ name = "TypeVar" and
531+ exists ( argument )
532+ or
421533 Specific:: isExtraValidTokenArgumentInIdentifyingAccessPath ( name , argument )
422534}
423535
@@ -489,6 +601,8 @@ module ModelOutput {
489601 any ( SummaryModelCsv csv ) .row ( row ) and kind = "summary" and expectedArity = 6
490602 or
491603 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
492606 |
493607 actualArity = count ( row .indexOf ( ";" ) ) + 1 and
494608 actualArity != expectedArity and
@@ -499,7 +613,7 @@ module ModelOutput {
499613 or
500614 // Check names and arguments of access path tokens
501615 exists ( AccessPath path , AccessPathToken token |
502- isRelevantFullPath ( _, _, path ) and
616+ ( isRelevantFullPath ( _, _, path ) or typeVariableModel ( _ , path ) ) and
503617 token = path .getToken ( _)
504618 |
505619 not isValidTokenNameInIdentifyingAccessPath ( token .getName ( ) ) and
0 commit comments