Skip to content

Commit a213982

Browse files
authored
Merge pull request #17222 from hvitved/ruby/hash-splat-param-arg-matching
Ruby: Rework (hash) splat argument/parameter matching
2 parents 09aca6b + cb1b1da commit a213982

File tree

13 files changed

+1040
-1993
lines changed

13 files changed

+1040
-1993
lines changed

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll

+50-12
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,11 @@ private module Cached {
563563
THashSplatArgumentPosition() or
564564
TSynthHashSplatArgumentPosition() or
565565
TSplatArgumentPosition(int pos) { exists(Call c | c.getArgument(pos) instanceof SplatExpr) } or
566-
TSynthSplatArgumentPosition() or
566+
TSynthSplatArgumentPosition(int actualSplatPos) {
567+
actualSplatPos = -1 // represents no actual splat
568+
or
569+
exists(Call c | c.getArgument(actualSplatPos) instanceof SplatExpr)
570+
} or
567571
TAnyArgumentPosition() or
568572
TAnyKeywordArgumentPosition()
569573

@@ -590,11 +594,15 @@ private module Cached {
590594
THashSplatParameterPosition() or
591595
TSynthHashSplatParameterPosition() or
592596
TSplatParameterPosition(int pos) {
593-
pos = 0
597+
pos = 0 // needed for flow summaries
594598
or
595599
exists(Parameter p | p.getPosition() = pos and p instanceof SplatParameter)
596600
} or
597-
TSynthSplatParameterPosition() or
601+
TSynthSplatParameterPosition(int actualSplatPos) {
602+
actualSplatPos = -1 // represents no actual splat
603+
or
604+
exists(Callable c | c.getParameter(actualSplatPos) instanceof SplatParameter)
605+
} or
598606
TAnyParameterPosition() or
599607
TAnyKeywordParameterPosition()
600608
}
@@ -1383,8 +1391,14 @@ class ParameterPosition extends TParameterPosition {
13831391
/** Holds if this position represents a splat parameter at position `n`. */
13841392
predicate isSplat(int n) { this = TSplatParameterPosition(n) }
13851393

1386-
/** Holds if this position represents a synthetic splat parameter. */
1387-
predicate isSynthSplat() { this = TSynthSplatParameterPosition() }
1394+
/**
1395+
* Holds if this position represents a synthetic splat parameter.
1396+
*
1397+
* `actualSplatPos` indicates the position of the (unique) actual splat
1398+
* parameter belonging to the same method, with `-1` representing no actual
1399+
* splat parameter.
1400+
*/
1401+
predicate isSynthSplat(int actualSplatPos) { this = TSynthSplatParameterPosition(actualSplatPos) }
13881402

13891403
/**
13901404
* Holds if this position represents any parameter, except `self` parameters. This
@@ -1419,7 +1433,11 @@ class ParameterPosition extends TParameterPosition {
14191433
or
14201434
exists(int pos | this.isSplat(pos) and result = "* (position " + pos + ")")
14211435
or
1422-
this.isSynthSplat() and result = "synthetic *"
1436+
exists(int actualSplatPos, string suffix |
1437+
this.isSynthSplat(actualSplatPos) and
1438+
result = "synthetic *" + suffix and
1439+
if actualSplatPos = -1 then suffix = "" else suffix = " (actual at " + actualSplatPos + ")"
1440+
)
14231441
}
14241442
}
14251443

@@ -1458,8 +1476,14 @@ class ArgumentPosition extends TArgumentPosition {
14581476
/** Holds if this position represents a splat argument at position `n`. */
14591477
predicate isSplat(int n) { this = TSplatArgumentPosition(n) }
14601478

1461-
/** Holds if this position represents a synthetic splat argument. */
1462-
predicate isSynthSplat() { this = TSynthSplatArgumentPosition() }
1479+
/**
1480+
* Holds if this position represents a synthetic splat argument.
1481+
*
1482+
* `actualSplatPos` indicates the position of the (unique) actual splat
1483+
* argument belonging to the same call, with `-1` representing no actual
1484+
* splat argument.
1485+
*/
1486+
predicate isSynthSplat(int actualSplatPos) { this = TSynthSplatArgumentPosition(actualSplatPos) }
14631487

14641488
/** Gets a textual representation of this position. */
14651489
string toString() {
@@ -1483,7 +1507,11 @@ class ArgumentPosition extends TArgumentPosition {
14831507
or
14841508
exists(int pos | this.isSplat(pos) and result = "* (position " + pos + ")")
14851509
or
1486-
this.isSynthSplat() and result = "synthetic *"
1510+
exists(int actualSplatPos, string suffix |
1511+
this.isSynthSplat(actualSplatPos) and
1512+
result = "synthetic *" + suffix and
1513+
if actualSplatPos = -1 then suffix = "" else suffix = " (actual at " + actualSplatPos + ")"
1514+
)
14871515
}
14881516
}
14891517

@@ -1517,19 +1545,29 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
15171545
exists(string name | ppos.isKeyword(name) and apos.isKeyword(name))
15181546
or
15191547
(ppos.isHashSplat() or ppos.isSynthHashSplat()) and
1520-
(apos.isHashSplat() or apos.isSynthHashSplat())
1548+
(apos.isHashSplat() or apos.isSynthHashSplat()) and
1549+
// prevent synthetic hash-splat parameters from matching synthetic hash-splat
1550+
// arguments when direct keyword matching is possible
1551+
not (ppos.isSynthHashSplat() and apos.isSynthHashSplat())
15211552
or
15221553
exists(int pos |
15231554
(
15241555
ppos.isSplat(pos)
15251556
or
1526-
ppos.isSynthSplat() and pos = 0
1557+
ppos.isSynthSplat(_) and
1558+
pos = 0
15271559
) and
15281560
(
15291561
apos.isSplat(pos)
15301562
or
1531-
apos.isSynthSplat() and pos = 0
1563+
apos.isSynthSplat(_) and pos = 0
15321564
)
1565+
) and
1566+
// prevent synthetic splat parameters from matching synthetic splat arguments
1567+
// when direct positional matching is possible
1568+
not exists(int actualSplatPos |
1569+
ppos.isSynthSplat(actualSplatPos) and
1570+
apos.isSynthSplat(actualSplatPos)
15331571
)
15341572
or
15351573
ppos.isAny() and argumentPositionIsNotSelf(apos)

0 commit comments

Comments
 (0)