@@ -16,65 +16,44 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch
16
16
* in `Array.qll`.
17
17
*/
18
18
module 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
52
34
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
+ )
66
36
}
67
37
68
- private class HashLiteralHashSplatSummary extends SummarizedCallable {
69
- HashLiteralHashSplatSummary ( ) { this = "Hash.[** ]" }
38
+ private class HashLiteralSummary extends SummarizedCallable {
39
+ HashLiteralSummary ( ) { this = "Hash.[]" }
70
40
71
41
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 ( )
74
43
}
75
44
76
45
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
78
57
input = "Argument[hash-splat].WithElement[any]" and
79
58
output = "ReturnValue" and
80
59
preservesValue = true
0 commit comments