Skip to content

Commit 19043bd

Browse files
authored
Merge pull request #9976 from hvitved/ruby/hash-literal-summary-simplification
Ruby: Simplify flow summaries for hash literals
2 parents d008975 + 28c8d9b commit 19043bd

File tree

3 files changed

+1189
-1182
lines changed

3 files changed

+1189
-1182
lines changed

ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,65 +16,44 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch
1616
* in `Array.qll`.
1717
*/
1818
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
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

Comments
 (0)