mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
Python: Add support for late *args arguments
This commit is contained in:
@@ -50,6 +50,7 @@ newtype TParameterPosition =
|
||||
pos = any(Parameter p).getPosition() + 1
|
||||
} or
|
||||
TSynthStarArgsElementParameterPosition(int pos) { exists(TStarArgsParameterPosition(pos)) } or
|
||||
TSynthLateStarArgsParameterPosition(int pos) { exists(TStarArgsParameterPosition(pos)) } or
|
||||
TDictSplatParameterPosition()
|
||||
|
||||
/** A parameter position. */
|
||||
@@ -75,6 +76,14 @@ class ParameterPosition extends TParameterPosition {
|
||||
this = TSynthStarArgsElementParameterPosition(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this position represents a synthetic `*args` parameter after the real
|
||||
* `*args` parameter. The real `*args` parameter is at the 0-based index `index`.
|
||||
*/
|
||||
predicate isSynthLateStarArgsParameterPosition(int index) {
|
||||
this = TSynthLateStarArgsParameterPosition(index)
|
||||
}
|
||||
|
||||
/** Holds if this position represents a `**kwargs` parameter. */
|
||||
predicate isDictSplat() { this = TDictSplatParameterPosition() }
|
||||
|
||||
@@ -93,6 +102,11 @@ class ParameterPosition extends TParameterPosition {
|
||||
result = "synthetic *args element at (or after) " + index
|
||||
)
|
||||
or
|
||||
exists(int index |
|
||||
this.isSynthLateStarArgsParameterPosition(index) and
|
||||
result = "synthetic late *args after " + index
|
||||
)
|
||||
or
|
||||
this.isDictSplat() and result = "**"
|
||||
}
|
||||
}
|
||||
@@ -151,6 +165,10 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
|
||||
ppos.isSynthStarArgsElement(paramIndex) and apos.isPositional(argIndex)
|
||||
)
|
||||
or
|
||||
exists(int realStarArgsIndex, int argIndex | argIndex > realStarArgsIndex |
|
||||
ppos.isSynthLateStarArgsParameterPosition(realStarArgsIndex) and apos.isStarArgs(argIndex)
|
||||
)
|
||||
or
|
||||
ppos.isDictSplat() and apos.isDictSplat()
|
||||
}
|
||||
|
||||
@@ -244,6 +262,9 @@ abstract class DataFlowFunction extends DataFlowCallable, TFunction {
|
||||
or
|
||||
ppos.isSynthStarArgsElement(index) and
|
||||
result = TSynthStarArgsElementParameterNode(this)
|
||||
or
|
||||
ppos.isSynthLateStarArgsParameterPosition(index) and
|
||||
result = TSynthLateStarArgsParameterNode(this)
|
||||
)
|
||||
|
|
||||
// a `*args` parameter comes after the last positional parameter. We need to take
|
||||
|
||||
@@ -129,6 +129,49 @@ predicate synthStarArgsElementParameterNodeStoreStep(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthetic node to capture a `*args` argument that is passed to a `*args`
|
||||
* parameter, but "too late" in the argument list, so we cannot just do a 1-1 mapping
|
||||
* without messing up the indexes; instead we make a list/tuple/set read step to
|
||||
* `SynthStarArgsElementParameterNode`.
|
||||
*
|
||||
* Example. The `*args` arguments starts at index 1, while the `*args` parameter accepts
|
||||
* arguments starting at index 0.
|
||||
*
|
||||
* ```py
|
||||
* def func(*args): ...
|
||||
* func(1, *args)
|
||||
*/
|
||||
class SynthLateStarArgsParameterNode extends ParameterNodeImpl, TSynthLateStarArgsParameterNode {
|
||||
DataFlowCallable callable;
|
||||
|
||||
SynthLateStarArgsParameterNode() { this = TSynthLateStarArgsParameterNode(callable) }
|
||||
|
||||
override string toString() { result = "SynthLateStarArgsParameterNode" }
|
||||
|
||||
override Scope getScope() { result = callable.getScope() }
|
||||
|
||||
override Location getLocation() { result = callable.getLocation() }
|
||||
|
||||
override Parameter getParameter() { none() }
|
||||
}
|
||||
|
||||
predicate synthLateStarArgsParameterNodeReadStep(
|
||||
SynthLateStarArgsParameterNode nodeFrom, Content c, ParameterNode nodeTo
|
||||
) {
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof TupleElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
) and
|
||||
exists(DataFlowCallable callable |
|
||||
nodeFrom = TSynthLateStarArgsParameterNode(callable) and
|
||||
nodeTo = TSynthStarArgsElementParameterNode(callable)
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// **kwargs (DictSplat) related
|
||||
// =============================================================================
|
||||
@@ -764,6 +807,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
synthLateStarArgsParameterNodeReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
synthDictSplatParameterNodeReadStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
@@ -925,6 +970,8 @@ predicate nodeIsHidden(Node n) {
|
||||
or
|
||||
n instanceof SynthStarArgsElementParameterNode
|
||||
or
|
||||
n instanceof SynthLateStarArgsParameterNode
|
||||
or
|
||||
n instanceof SynthDictSplatArgumentNode
|
||||
or
|
||||
n instanceof SynthDictSplatParameterNode
|
||||
|
||||
@@ -117,6 +117,14 @@ newtype TNode =
|
||||
TSynthStarArgsElementParameterNode(DataFlowCallable callable) {
|
||||
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
||||
} or
|
||||
/**
|
||||
* A synthetic node to capture a `*args` argument that is passed to a `*args`
|
||||
* parameter, but "too late" in the argument list, so we cannot just do a 1-1 mapping
|
||||
* without messing up the indexes.
|
||||
*/
|
||||
TSynthLateStarArgsParameterNode(DataFlowCallable callable) {
|
||||
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
||||
} or
|
||||
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
|
||||
TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) } or
|
||||
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
|
||||
|
||||
@@ -211,8 +211,8 @@ def starargs_only(*args):
|
||||
def test_only_starargs():
|
||||
starargs_only(arg1, arg2, "safe") # $ arg1 arg2 SPURIOUS: bad2,bad3="arg1" bad1,bad3="arg2"
|
||||
|
||||
args = (arg2, "safe")
|
||||
starargs_only(arg1, *args) # $ arg1 SPURIOUS: bad2,bad3="arg1" MISSING: arg2
|
||||
args = (arg2, "safe") # $ arg2 func=starargs_only SPURIOUS: bad1,bad3="arg2"
|
||||
starargs_only(arg1, *args) # $ arg1 SPURIOUS: bad2,bad3="arg1"
|
||||
|
||||
args = (arg1, arg2, "safe") # $ arg1 arg2 func=starargs_only
|
||||
starargs_only(*args)
|
||||
|
||||
Reference in New Issue
Block a user