@@ -87,6 +87,46 @@ class CallNode extends LocalSourceNode, ExprNode {
8787
8888 /** Gets the block of this call. */
8989 Node getBlock ( ) { result .asExpr ( ) = node .getBlock ( ) }
90+
91+ /**
92+ * Gets the data-flow node corresponding to the named argument of the call
93+ * corresponding to this data-flow node, also including values passed with (pre Ruby
94+ * 2.0) hash arguments.
95+ *
96+ * Such hash arguments are tracked back to their source location within functions, but
97+ * no inter-procedural analysis occurs.
98+ *
99+ * This means all 3 variants below will be handled by this predicate:
100+ *
101+ * ```ruby
102+ * foo(..., some_option: 42)
103+ * foo(..., { some_option: 42 })
104+ * options = { some_option: 42 }
105+ * foo(..., options)
106+ * ```
107+ */
108+ Node getKeywordArgumentIncludeHashArgument ( string name ) {
109+ // to reduce number of computed tuples, I have put bindingset on both this and name,
110+ // meaning we only do the local backwards tracking for known calls and known names.
111+ // (not because a performance problem was seen, it just seemed right).
112+ result = this .getKeywordArgument ( name )
113+ or
114+ exists ( CfgNodes:: ExprNodes:: PairCfgNode pair |
115+ pair =
116+ this .getArgument ( _)
117+ .getALocalSource ( )
118+ .asExpr ( )
119+ .( CfgNodes:: ExprNodes:: HashLiteralCfgNode )
120+ .getAKeyValuePair ( ) and
121+ exprNode ( pair .getKey ( ) )
122+ .getALocalSource ( )
123+ .asExpr ( )
124+ .getExpr ( )
125+ .getConstantValue ( )
126+ .isStringlikeValue ( name ) and
127+ result .asExpr ( ) = pair .getValue ( )
128+ )
129+ }
90130}
91131
92132/**
0 commit comments