mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Python: Support flow to **kwargs param from keyword arg
This commit is contained in:
@@ -910,7 +910,12 @@ private predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos)
|
||||
arg.asCfgNode() = call.getArgByName(name)
|
||||
)
|
||||
or
|
||||
apos.isDictSplat() and arg.asCfgNode() = call.getKwargs()
|
||||
apos.isDictSplat() and
|
||||
(
|
||||
arg.asCfgNode() = call.getKwargs()
|
||||
or
|
||||
arg = TSynthDictSplatArgumentNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,6 +54,33 @@ class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
|
||||
override Location getLocation() { result = node.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A (synthetic) data-flow node that represents all keyword arguments, as if they had
|
||||
* been passed in a `**kwargs` argument.
|
||||
*/
|
||||
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
|
||||
CallNode node;
|
||||
|
||||
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
|
||||
|
||||
override string toString() { result = "SynthDictSplatArgumentNode" }
|
||||
|
||||
override Scope getScope() { result = node.getScope() }
|
||||
|
||||
override Location getLocation() { result = node.getLocation() }
|
||||
}
|
||||
|
||||
private predicate synthDictSplatArgumentNodeStoreStep(
|
||||
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
|
||||
) {
|
||||
exists(string name, CallNode call, ArgumentPosition keywordPos |
|
||||
nodeTo = TSynthDictSplatArgumentNode(call) and
|
||||
getCallArg(call, _, _, nodeFrom, keywordPos) and
|
||||
keywordPos.isKeyword(name) and
|
||||
c.getKey() = name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the a `**kwargs` parameter will not contain elements with names of
|
||||
* keyword parameters.
|
||||
@@ -426,6 +453,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
any(Orm::AdditionalOrmSteps es).storeStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
synthDictSplatArgumentNodeStoreStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -752,6 +781,8 @@ predicate nodeIsHidden(Node n) {
|
||||
n instanceof SummaryNode
|
||||
or
|
||||
n instanceof SummaryParameterNode
|
||||
or
|
||||
n instanceof SynthDictSplatArgumentNode
|
||||
}
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
@@ -112,7 +112,8 @@ newtype TNode =
|
||||
} or
|
||||
TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) {
|
||||
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
|
||||
}
|
||||
} or
|
||||
TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) }
|
||||
|
||||
class TParameterNode = TCfgNode or TSummaryParameterNode;
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ def test_kw_doublestar():
|
||||
def with_doublestar(**kwargs):
|
||||
SINK1(kwargs["a"])
|
||||
|
||||
with_doublestar(a=arg1) #$ MISSING: arg1 func=test_kw_doublestar.with_doublestar
|
||||
with_doublestar(a=arg1) #$ arg1 func=test_kw_doublestar.with_doublestar
|
||||
|
||||
|
||||
def only_kwargs(**kwargs):
|
||||
@@ -193,7 +193,7 @@ def mixed(a, **kwargs):
|
||||
|
||||
@expects(4*3)
|
||||
def test_mixed():
|
||||
mixed(a=arg1, b=arg2, c="safe") # $ arg1 MISSING: arg2
|
||||
mixed(a=arg1, b=arg2, c="safe") # $ arg1 arg2
|
||||
|
||||
args = {"b": arg2, "c": "safe"} # $ arg2 func=mixed
|
||||
mixed(a=arg1, **args) # $ arg1
|
||||
|
||||
@@ -409,7 +409,7 @@ def f_extra_keyword(a, **b):
|
||||
|
||||
|
||||
def test_call_extra_keyword():
|
||||
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ MISSING: flow="SOURCE -> f_extra_keyword(..)"
|
||||
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> f_extra_keyword(..)"
|
||||
|
||||
|
||||
# return the name of the first extra keyword argument
|
||||
@@ -519,7 +519,7 @@ def test_lambda_extra_pos():
|
||||
|
||||
def test_lambda_extra_keyword():
|
||||
f_extra_keyword = lambda a, **b: b["b"]
|
||||
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ MISSING: flow="SOURCE -> f_extra_keyword(..)"
|
||||
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> f_extra_keyword(..)"
|
||||
|
||||
|
||||
# call the function with our source as the name of the keyword argument
|
||||
|
||||
Reference in New Issue
Block a user