Skip to content

Commit d5d1365

Browse files
committed
Synchronize ApiGraphModels.qll
1 parent 654c4ee commit d5d1365

File tree

2 files changed

+232
-8
lines changed

2 files changed

+232
-8
lines changed

python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

160176
private import ModelInput
@@ -182,6 +198,8 @@ private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inverseP
182198

183199
private 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. */
186204
predicate 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. */
223241
private 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,91 @@ 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 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())
364457
}
365458

366459
/** Gets the node identified by the given `(package, type, path)` tuple. */
367460
API::Node getNodeFromPath(string package, string type, AccessPath path) {
368461
result = getNodeFromPath(package, type, path, path.getNumToken())
369462
}
370463

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+
371478
/**
372479
* Gets an invocation identified by the given `(package, type, path)` tuple.
373480
*
@@ -390,7 +497,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
390497
*/
391498
bindingset[name]
392499
predicate isValidTokenNameInIdentifyingAccessPath(string name) {
393-
name = ["Argument", "Parameter", "ReturnValue", "WithArity"]
500+
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"]
394501
or
395502
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
396503
}
@@ -418,6 +525,9 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
418525
name = "WithArity" and
419526
argument.regexpMatch("\\d+(\\.\\.(\\d+)?)?")
420527
or
528+
name = "TypeVar" and
529+
exists(argument)
530+
or
421531
Specific::isExtraValidTokenArgumentInIdentifyingAccessPath(name, argument)
422532
}
423533

@@ -489,6 +599,8 @@ module ModelOutput {
489599
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
490600
or
491601
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
492604
|
493605
actualArity = count(row.indexOf(";")) + 1 and
494606
actualArity != expectedArity and
@@ -499,7 +611,7 @@ module ModelOutput {
499611
or
500612
// Check names and arguments of access path tokens
501613
exists(AccessPath path, AccessPathToken token |
502-
isRelevantFullPath(_, _, path) and
614+
(isRelevantFullPath(_, _, path) or typeVariableModel(_, path)) and
503615
token = path.getToken(_)
504616
|
505617
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and

0 commit comments

Comments
 (0)