@@ -64,7 +64,14 @@ newtype TParameterPosition =
6464 index = any ( Parameter p ) .getPosition ( ) + 1
6565 } or
6666 TSynthStarArgsElementParameterPosition ( int index ) { exists ( TStarArgsParameterPosition ( index ) ) } or
67- TDictSplatParameterPosition ( )
67+ TDictSplatParameterPosition ( ) or
68+ // To get flow from a **kwargs argument to a keyword parameter, we add a read-step
69+ // from a synthetic **kwargs parameter. We need this separate synthetic ParameterNode,
70+ // since we clear content of the normal **kwargs parameter for the names that
71+ // correspond to normal keyword parameters. Since we cannot re-use the same parameter
72+ // position for multiple parameter nodes in the same callable, we introduce this
73+ // synthetic parameter position.
74+ TSynthDictSplatParameterPosition ( )
6875
6976/** A parameter position. */
7077class ParameterPosition extends TParameterPosition {
@@ -92,6 +99,12 @@ class ParameterPosition extends TParameterPosition {
9299 /** Holds if this position represents a `**kwargs` parameter. */
93100 predicate isDictSplat ( ) { this = TDictSplatParameterPosition ( ) }
94101
102+ /**
103+ * Holds if this position represents a **synthetic** `**kwargs` parameter
104+ * (see comment for `TSynthDictSplatParameterPosition`).
105+ */
106+ predicate isSynthDictSplat ( ) { this = TSynthDictSplatParameterPosition ( ) }
107+
95108 /** Gets a textual representation of this element. */
96109 string toString ( ) {
97110 this .isSelf ( ) and result = "self"
@@ -108,6 +121,8 @@ class ParameterPosition extends TParameterPosition {
108121 )
109122 or
110123 this .isDictSplat ( ) and result = "**"
124+ or
125+ this .isSynthDictSplat ( ) and result = "synthetic **"
111126 }
112127}
113128
@@ -179,6 +194,8 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
179194 )
180195 or
181196 ppos .isDictSplat ( ) and apos .isDictSplat ( )
197+ or
198+ ppos .isSynthDictSplat ( ) and apos .isDictSplat ( )
182199}
183200
184201// =============================================================================
@@ -324,16 +341,9 @@ abstract class DataFlowFunction extends DataFlowCallable, TFunction {
324341 )
325342 or
326343 // `**kwargs`
327- // since the dataflow library has the restriction that we can only have ONE result per
328- // parameter position, if there is both a synthetic **kwargs and a real **kwargs
329- // parameter, we only give the result for the synthetic, and add local flow from the
330- // synthetic to the real. It might seem more natural to do it in the other
331- // direction, but since we have a clearStep on the real **kwargs parameter, we would have that
332- // content-clearing would also affect the synthetic parameter, which we don't want.
333- ppos .isDictSplat ( ) and
334- if exists ( func .getArgByName ( _) )
335- then result = TSynthDictSplatParameterNode ( this )
336- else result .getParameter ( ) = func .getKwarg ( )
344+ ppos .isDictSplat ( ) and result .getParameter ( ) = func .getKwarg ( )
345+ or
346+ ppos .isSynthDictSplat ( ) and result = TSynthDictSplatParameterNode ( this )
337347 }
338348}
339349
@@ -1460,16 +1470,7 @@ class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
14601470 override Parameter getParameter ( ) { none ( ) }
14611471
14621472 override predicate isParameterOf ( DataFlowCallable c , ParameterPosition ppos ) {
1463- sc = c .asLibraryCallable ( ) and
1464- ppos = pos and
1465- // avoid overlap with `SynthDictSplatParameterNode`
1466- not (
1467- pos .isDictSplat ( ) and
1468- exists ( ParameterPosition keywordPos |
1469- FlowSummaryImpl:: Private:: summaryParameterNodeRange ( sc , keywordPos ) and
1470- keywordPos .isKeyword ( _)
1471- )
1472- )
1473+ sc = c .asLibraryCallable ( ) and ppos = pos
14731474 }
14741475
14751476 override DataFlowCallable getEnclosingCallable ( ) { result .asLibraryCallable ( ) = sc }
0 commit comments