Skip to content

Commit 8361efc

Browse files
authored
Merge pull request #15503 from github/koesie10/ruby-access-paths
Ruby: Add query for access paths in model editor
2 parents cec124c + 6a09812 commit 8361efc

File tree

5 files changed

+174
-6
lines changed

5 files changed

+174
-6
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @name Fetch a subset of valid access paths of input and output parameters of a method (framework mode).
3+
* @description A list of access paths for input and output parameters of a method. Excludes test and generated code.
4+
* @kind table
5+
* @id ruby/utils/modeleditor/framework-mode-access-paths
6+
* @tags modeleditor access-paths framework-mode
7+
*/
8+
9+
private import ruby
10+
private import codeql.ruby.AST
11+
private import codeql.ruby.ApiGraphs
12+
private import queries.modeling.internal.Util as Util
13+
14+
predicate simpleParameters(string type, string path, string value, DataFlow::Node node) {
15+
exists(DataFlow::MethodNode methodNode, DataFlow::ParameterNode paramNode |
16+
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
17+
(
18+
// Check that this parameter belongs to this method
19+
// Block parameter explicitly excluded because it's already included
20+
// as part of the blockArguments predicate
21+
paramNode = Util::getAnyParameter(methodNode) and
22+
paramNode != methodNode.getBlockParameter()
23+
)
24+
|
25+
Util::pathToMethod(methodNode, type, path) and
26+
value = Util::getArgumentPath(paramNode) and
27+
node = paramNode
28+
)
29+
}
30+
31+
predicate blockArguments(string type, string path, string value, DataFlow::Node node) {
32+
exists(DataFlow::MethodNode methodNode, DataFlow::CallNode callNode |
33+
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
34+
callNode = methodNode.getABlockCall()
35+
|
36+
(
37+
exists(DataFlow::VariableAccessNode argNode, int i |
38+
argNode = callNode.getPositionalArgument(i)
39+
|
40+
value = "Argument[block].Parameter[" + i + "]" and
41+
node = argNode
42+
)
43+
or
44+
exists(DataFlow::ExprNode argNode, string keyword |
45+
argNode = callNode.getKeywordArgument(keyword)
46+
|
47+
value = "Argument[block].Parameter[" + keyword + ":]" and
48+
node = argNode
49+
)
50+
or
51+
value = "Argument[block]" and
52+
node = callNode
53+
) and
54+
Util::pathToMethod(methodNode, type, path)
55+
)
56+
}
57+
58+
predicate returnValue(string type, string path, string value, DataFlow::Node node) {
59+
exists(DataFlow::MethodNode methodNode, DataFlow::Node returnNode |
60+
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
61+
returnNode = methodNode.getAReturnNode()
62+
|
63+
Util::pathToMethod(methodNode, type, path) and
64+
value = "ReturnValue" and
65+
node = returnNode
66+
)
67+
}
68+
69+
predicate inputAccessPaths(
70+
string type, string path, string value, DataFlow::Node node, string defType
71+
) {
72+
simpleParameters(type, path, value, node) and defType = "parameter"
73+
or
74+
blockArguments(type, path, value, node) and defType = "parameter"
75+
}
76+
77+
predicate outputAccessPaths(
78+
string type, string path, string value, DataFlow::Node node, string defType
79+
) {
80+
simpleParameters(type, path, value, node) and defType = "parameter"
81+
or
82+
blockArguments(type, path, value, node) and defType = "parameter"
83+
or
84+
returnValue(type, path, value, node) and defType = "return"
85+
}
86+
87+
query predicate input = inputAccessPaths/5;
88+
89+
query predicate output = outputAccessPaths/5;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
input
2+
| A | Method[bar] | Argument[0] | lib/mylib.rb:13:11:13:11 | x | parameter |
3+
| A | Method[bar] | Argument[self] | lib/mylib.rb:13:3:14:5 | self in bar | parameter |
4+
| A | Method[foo] | Argument[0] | lib/mylib.rb:7:11:7:11 | x | parameter |
5+
| A | Method[foo] | Argument[1] | lib/mylib.rb:7:14:7:14 | y | parameter |
6+
| A | Method[foo] | Argument[2] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
7+
| A | Method[foo] | Argument[block] | lib/mylib.rb:8:5:8:32 | call to call | parameter |
8+
| A | Method[foo] | Argument[block] | lib/mylib.rb:10:5:10:26 | yield ... | parameter |
9+
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:8:16:8:16 | x | parameter |
10+
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:10:11:10:11 | x | parameter |
11+
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:8:19:8:19 | y | parameter |
12+
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:10:14:10:14 | y | parameter |
13+
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:8:28:8:31 | key1 | parameter |
14+
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:10:23:10:26 | key1 | parameter |
15+
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
16+
| A | Method[foo] | Argument[self] | lib/mylib.rb:7:3:11:5 | self in foo | parameter |
17+
| A! | Method[new] | Argument[0] | lib/mylib.rb:4:18:4:18 | x | parameter |
18+
| A! | Method[new] | Argument[1] | lib/mylib.rb:4:21:4:21 | y | parameter |
19+
| A! | Method[new] | Argument[self] | lib/mylib.rb:4:3:5:5 | self in initialize | parameter |
20+
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:16:21:16:21 | x | parameter |
21+
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:16:24:16:24 | y | parameter |
22+
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:16:3:17:5 | self in self_foo | parameter |
23+
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:25:13:25:13 | x | parameter |
24+
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:25:16:25:16 | y | parameter |
25+
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:25:5:26:7 | self in foo | parameter |
26+
| B | Method[foo] | Argument[0] | lib/other.rb:6:11:6:11 | x | parameter |
27+
| B | Method[foo] | Argument[1] | lib/other.rb:6:14:6:14 | y | parameter |
28+
| B | Method[foo] | Argument[self] | lib/other.rb:6:3:7:5 | self in foo | parameter |
29+
| M1 | Method[foo] | Argument[0] | lib/module.rb:2:11:2:11 | x | parameter |
30+
| M1 | Method[foo] | Argument[1] | lib/module.rb:2:14:2:14 | y | parameter |
31+
| M1 | Method[foo] | Argument[self] | lib/module.rb:2:3:3:5 | self in foo | parameter |
32+
| M1! | Method[self_foo] | Argument[0] | lib/module.rb:5:21:5:21 | x | parameter |
33+
| M1! | Method[self_foo] | Argument[1] | lib/module.rb:5:24:5:24 | y | parameter |
34+
| M1! | Method[self_foo] | Argument[self] | lib/module.rb:5:3:6:5 | self in self_foo | parameter |
35+
| OtherLib::A | Method[foo] | Argument[0] | other_lib/lib/other_gem.rb:3:17:3:17 | x | parameter |
36+
| OtherLib::A | Method[foo] | Argument[1] | other_lib/lib/other_gem.rb:3:20:3:20 | y | parameter |
37+
| OtherLib::A | Method[foo] | Argument[self] | other_lib/lib/other_gem.rb:3:9:4:11 | self in foo | parameter |
38+
output
39+
| A | Method[bar] | Argument[0] | lib/mylib.rb:13:11:13:11 | x | parameter |
40+
| A | Method[bar] | Argument[self] | lib/mylib.rb:13:3:14:5 | self in bar | parameter |
41+
| A | Method[foo] | Argument[0] | lib/mylib.rb:7:11:7:11 | x | parameter |
42+
| A | Method[foo] | Argument[1] | lib/mylib.rb:7:14:7:14 | y | parameter |
43+
| A | Method[foo] | Argument[2] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
44+
| A | Method[foo] | Argument[block] | lib/mylib.rb:8:5:8:32 | call to call | parameter |
45+
| A | Method[foo] | Argument[block] | lib/mylib.rb:10:5:10:26 | yield ... | parameter |
46+
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:8:16:8:16 | x | parameter |
47+
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:10:11:10:11 | x | parameter |
48+
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:8:19:8:19 | y | parameter |
49+
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:10:14:10:14 | y | parameter |
50+
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:8:28:8:31 | key1 | parameter |
51+
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:10:23:10:26 | key1 | parameter |
52+
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
53+
| A | Method[foo] | Argument[self] | lib/mylib.rb:7:3:11:5 | self in foo | parameter |
54+
| A | Method[foo] | ReturnValue | lib/mylib.rb:10:5:10:26 | yield ... | return |
55+
| A! | Method[new] | Argument[0] | lib/mylib.rb:4:18:4:18 | x | parameter |
56+
| A! | Method[new] | Argument[1] | lib/mylib.rb:4:21:4:21 | y | parameter |
57+
| A! | Method[new] | Argument[self] | lib/mylib.rb:4:3:5:5 | self in initialize | parameter |
58+
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:16:21:16:21 | x | parameter |
59+
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:16:24:16:24 | y | parameter |
60+
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:16:3:17:5 | self in self_foo | parameter |
61+
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:25:13:25:13 | x | parameter |
62+
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:25:16:25:16 | y | parameter |
63+
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:25:5:26:7 | self in foo | parameter |
64+
| B | Method[foo] | Argument[0] | lib/other.rb:6:11:6:11 | x | parameter |
65+
| B | Method[foo] | Argument[1] | lib/other.rb:6:14:6:14 | y | parameter |
66+
| B | Method[foo] | Argument[self] | lib/other.rb:6:3:7:5 | self in foo | parameter |
67+
| M1 | Method[foo] | Argument[0] | lib/module.rb:2:11:2:11 | x | parameter |
68+
| M1 | Method[foo] | Argument[1] | lib/module.rb:2:14:2:14 | y | parameter |
69+
| M1 | Method[foo] | Argument[self] | lib/module.rb:2:3:3:5 | self in foo | parameter |
70+
| M1! | Method[self_foo] | Argument[0] | lib/module.rb:5:21:5:21 | x | parameter |
71+
| M1! | Method[self_foo] | Argument[1] | lib/module.rb:5:24:5:24 | y | parameter |
72+
| M1! | Method[self_foo] | Argument[self] | lib/module.rb:5:3:6:5 | self in self_foo | parameter |
73+
| OtherLib::A | Method[foo] | Argument[0] | other_lib/lib/other_gem.rb:3:17:3:17 | x | parameter |
74+
| OtherLib::A | Method[foo] | Argument[1] | other_lib/lib/other_gem.rb:3:20:3:20 | y | parameter |
75+
| OtherLib::A | Method[foo] | Argument[self] | other_lib/lib/other_gem.rb:3:9:4:11 | self in foo | parameter |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/FrameworkModeAccessPaths.ql

ruby/ql/test/query-tests/utils/modeleditor/FrameworkModeEndpoints.expected

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
| lib/module.rb:1:1:7:3 | M1 | mylib | M1 | | | false | module.rb | |
22
| lib/module.rb:2:3:3:5 | foo | mylib | M1 | foo | (x,y) | false | module.rb | |
33
| lib/module.rb:5:3:6:5 | self_foo | mylib | M1! | self_foo | (x,y) | false | module.rb | |
4-
| lib/mylib.rb:3:1:30:3 | A | mylib | A | | | false | mylib.rb | |
4+
| lib/mylib.rb:3:1:33:3 | A | mylib | A | | | false | mylib.rb | |
55
| lib/mylib.rb:4:3:5:5 | initialize | mylib | A! | new | (x,y) | false | mylib.rb | |
6-
| lib/mylib.rb:7:3:8:5 | foo | mylib | A | foo | (x,y,key1:) | false | mylib.rb | |
7-
| lib/mylib.rb:10:3:11:5 | bar | mylib | A | bar | (x) | false | mylib.rb | |
8-
| lib/mylib.rb:13:3:14:5 | self_foo | mylib | A! | self_foo | (x,y) | false | mylib.rb | |
9-
| lib/mylib.rb:21:3:29:5 | ANested | mylib | A::ANested | | | false | mylib.rb | |
10-
| lib/mylib.rb:22:5:23:7 | foo | mylib | A::ANested | foo | (x,y) | false | mylib.rb | |
6+
| lib/mylib.rb:7:3:11:5 | foo | mylib | A | foo | (x,y,key1:) | false | mylib.rb | |
7+
| lib/mylib.rb:13:3:14:5 | bar | mylib | A | bar | (x) | false | mylib.rb | |
8+
| lib/mylib.rb:16:3:17:5 | self_foo | mylib | A! | self_foo | (x,y) | false | mylib.rb | |
9+
| lib/mylib.rb:24:3:32:5 | ANested | mylib | A::ANested | | | false | mylib.rb | |
10+
| lib/mylib.rb:25:5:26:7 | foo | mylib | A::ANested | foo | (x,y) | false | mylib.rb | |
1111
| lib/other.rb:3:1:8:3 | B | mylib | B | | | false | other.rb | |
1212
| lib/other.rb:6:3:7:5 | foo | mylib | B | foo | (x,y) | false | other.rb | |
1313
| lib/other.rb:10:1:12:3 | C | mylib | C | | | false | other.rb | |

ruby/ql/test/query-tests/utils/modeleditor/lib/mylib.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ def initialize(x, y)
55
end
66

77
def foo(x, y, key1:, **kwargs, &block)
8+
block.call(x, y, key2: key1)
9+
10+
yield x, y, key2: key1
811
end
912

1013
def bar(x, *args)

0 commit comments

Comments
 (0)