@@ -16,65 +16,44 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch
1616 * in `Array.qll`.
1717 */
1818module Hash {
19- // cannot use API graphs due to negative recursion
20- private predicate isHashLiteralPair ( Pair pair , ConstantValue key ) {
21- key = DataFlow:: Content:: getKnownElementIndex ( pair .getKey ( ) ) and
22- pair = any ( MethodCall mc | mc .getMethodName ( ) = "[]" ) .getAnArgument ( )
23- }
24-
25- private class HashLiteralSymbolSummary extends SummarizedCallable {
26- private ConstantValue:: ConstantSymbolValue symbol ;
27-
28- HashLiteralSymbolSummary ( ) {
29- isHashLiteralPair ( _, symbol ) and
30- this = "Hash.[" + symbol .serialize ( ) + "]"
31- }
32-
33- final override MethodCall getACall ( ) {
34- result = API:: getTopLevelMember ( "Hash" ) .getAMethodCall ( "[]" ) .getExprNode ( ) .getExpr ( ) and
35- exists ( result .getKeywordArgument ( symbol .getSymbol ( ) ) )
36- }
37-
38- override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
39- // { symbol: x }
40- input = "Argument[" + symbol .getSymbol ( ) + ":]" and
41- output = "ReturnValue.Element[" + symbol .serialize ( ) + "]" and
42- preservesValue = true
43- }
44- }
45-
46- private class HashLiteralNonSymbolSummary extends SummarizedCallable {
47- private ConstantValue key ;
48-
49- HashLiteralNonSymbolSummary ( ) {
50- this = "Hash.[]" and
51- isHashLiteralPair ( _, key ) and
19+ /**
20+ * Holds if `key` is used as the non-symbol key in a hash literal. For example
21+ *
22+ * ```rb
23+ * {
24+ * :a => 1, # symbol
25+ * "b" => 2 # non-symbol, "b" is the key
26+ * }
27+ * ```
28+ */
29+ private predicate isHashLiteralNonSymbolKey ( ConstantValue key ) {
30+ exists ( Pair pair |
31+ key = DataFlow:: Content:: getKnownElementIndex ( pair .getKey ( ) ) and
32+ // cannot use API graphs due to negative recursion
33+ pair = any ( MethodCall mc | mc .getMethodName ( ) = "[]" ) .getAnArgument ( ) and
5234 not key .isSymbol ( _)
53- }
54-
55- final override MethodCall getACall ( ) {
56- result = API:: getTopLevelMember ( "Hash" ) .getAMethodCall ( "[]" ) .getExprNode ( ) .getExpr ( ) and
57- isHashLiteralPair ( result .getAnArgument ( ) , key )
58- }
59-
60- override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
61- // { 'nonsymbol' => x }
62- input = "Argument[0..].PairValue[" + key .serialize ( ) + "]" and
63- output = "ReturnValue.Element[" + key .serialize ( ) + "]" and
64- preservesValue = true
65- }
35+ )
6636 }
6737
68- private class HashLiteralHashSplatSummary extends SummarizedCallable {
69- HashLiteralHashSplatSummary ( ) { this = "Hash.[** ]" }
38+ private class HashLiteralSummary extends SummarizedCallable {
39+ HashLiteralSummary ( ) { this = "Hash.[]" }
7040
7141 final override MethodCall getACall ( ) {
72- result = API:: getTopLevelMember ( "Hash" ) .getAMethodCall ( "[]" ) .getExprNode ( ) .getExpr ( ) and
73- result .getAnArgument ( ) instanceof HashSplatExpr
42+ result = API:: getTopLevelMember ( "Hash" ) .getAMethodCall ( "[]" ) .getExprNode ( ) .getExpr ( )
7443 }
7544
7645 override predicate propagatesFlowExt ( string input , string output , boolean preservesValue ) {
77- // { **hash }
46+ // { 'nonsymbol' => x }
47+ exists ( ConstantValue key |
48+ isHashLiteralNonSymbolKey ( key ) and
49+ input = "Argument[0..].PairValue[" + key .serialize ( ) + "]" and
50+ output = "ReturnValue.Element[" + key .serialize ( ) + "]" and
51+ preservesValue = true
52+ )
53+ or
54+ // { symbol: x }
55+ // we make use of the special `hash-splat` argument kind, which contains all keyword
56+ // arguments wrapped in an implicit hash, as well as explicit hash splat arguments
7857 input = "Argument[hash-splat].WithElement[any]" and
7958 output = "ReturnValue" and
8059 preservesValue = true
0 commit comments