Merge pull request #15541 from github/koesie10/ruby-access-path-constructor-returnvalue

Ruby: Remove `ReturnValue` as access path for constructors
This commit is contained in:
Koen Vlaswinkel
2024-02-08 16:25:34 +01:00
committed by GitHub
6 changed files with 80 additions and 58 deletions

View File

@@ -10,6 +10,7 @@ private import ruby
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import queries.modeling.internal.Util as Util
private import ModelEditor
predicate simpleParameters(string type, string path, string value, DataFlow::Node node) {
exists(DataFlow::MethodNode methodNode, DataFlow::ParameterNode paramNode |
@@ -19,7 +20,9 @@ predicate simpleParameters(string type, string path, string value, DataFlow::Nod
// Block parameter explicitly excluded because it's already included
// as part of the blockArguments predicate
paramNode = Util::getAnyParameter(methodNode) and
paramNode != methodNode.getBlockParameter()
paramNode != methodNode.getBlockParameter() and
// The self parameter of a constructor is not a parameter that can be used in any models
(not isConstructor(methodNode) or paramNode != methodNode.getSelfParameter())
)
|
Util::pathToMethod(methodNode, type, path) and
@@ -58,12 +61,25 @@ predicate blockArguments(string type, string path, string value, DataFlow::Node
predicate returnValue(string type, string path, string value, DataFlow::Node node) {
exists(DataFlow::MethodNode methodNode, DataFlow::Node returnNode |
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
returnNode = methodNode.getAReturnNode()
returnNode = methodNode.getAReturnNode() and
not isConstructor(methodNode)
|
Util::pathToMethod(methodNode, type, path) and
value = "ReturnValue" and
node = returnNode
)
or
// A constructor has a return node for every statement, but we always want
// to return 1 node for the ReturnValue, so we return the self parameter
// instead.
exists(DataFlow::MethodNode methodNode |
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
isConstructor(methodNode)
|
Util::pathToMethod(methodNode, type, path) and
value = "ReturnValue" and
node = methodNode.getSelfParameter()
)
}
predicate inputAccessPaths(

View File

@@ -32,6 +32,14 @@ string getNamespace(File file) {
)
}
/**
* Holds if this method is a constructor for a module.
*/
predicate isConstructor(DataFlow::MethodNode method) {
method.getMethodName() = "initialize" and
exists(DataFlow::ModuleNode m | m.getOwnInstanceMethod(method.getMethodName()) = method)
}
abstract class Endpoint instanceof DataFlow::Node {
string getNamespace() { result = getNamespace(super.getLocation().getFile()) }
@@ -153,10 +161,7 @@ class MethodEndpoint extends Endpoint instanceof DataFlow::MethodNode {
/**
* Holds if this method is a constructor for a module.
*/
private predicate isConstructor() {
super.getMethodName() = "initialize" and
exists(DataFlow::ModuleNode m | m.getOwnInstanceMethod(super.getMethodName()) = this)
}
private predicate isConstructor() { isConstructor(this) }
}
string methodClassification(Call method) {

View File

@@ -1,28 +1,27 @@
input
| A | Method[bar] | Argument[0] | lib/mylib.rb:13:11:13:11 | x | parameter |
| A | Method[bar] | Argument[self] | lib/mylib.rb:13:3:14:5 | self in bar | parameter |
| A | Method[foo] | Argument[0] | lib/mylib.rb:7:11:7:11 | x | parameter |
| A | Method[foo] | Argument[1] | lib/mylib.rb:7:14:7:14 | y | parameter |
| A | Method[foo] | Argument[2] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:8:5:8:32 | call to call | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:10:5:10:26 | yield ... | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:8:16:8:16 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:10:11:10:11 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:8:19:8:19 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:10:14:10:14 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:8:28:8:31 | key1 | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:10:23:10:26 | key1 | parameter |
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
| A | Method[foo] | Argument[self] | lib/mylib.rb:7:3:11:5 | self in foo | parameter |
| A | Method[bar] | Argument[0] | lib/mylib.rb:14:11:14:11 | x | parameter |
| A | Method[bar] | Argument[self] | lib/mylib.rb:14:3:15:5 | self in bar | parameter |
| A | Method[foo] | Argument[0] | lib/mylib.rb:8:11:8:11 | x | parameter |
| A | Method[foo] | Argument[1] | lib/mylib.rb:8:14:8:14 | y | parameter |
| A | Method[foo] | Argument[2] | lib/mylib.rb:8:17:8:20 | key1 | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:9:5:9:32 | call to call | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:11:5:11:26 | yield ... | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:9:16:9:16 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:11:11:11:11 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:9:19:9:19 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:11:14:11:14 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:9:28:9:31 | key1 | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:11:23:11:26 | key1 | parameter |
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:8:17:8:20 | key1 | parameter |
| A | Method[foo] | Argument[self] | lib/mylib.rb:8:3:12:5 | self in foo | parameter |
| A! | Method[new] | Argument[0] | lib/mylib.rb:4:18:4:18 | x | parameter |
| A! | Method[new] | Argument[1] | lib/mylib.rb:4:21:4:21 | y | parameter |
| A! | Method[new] | Argument[self] | lib/mylib.rb:4:3:5:5 | self in initialize | parameter |
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:16:21:16:21 | x | parameter |
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:16:24:16:24 | y | parameter |
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:16:3:17:5 | self in self_foo | parameter |
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:25:13:25:13 | x | parameter |
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:25:16:25:16 | y | parameter |
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:25:5:26:7 | self in foo | parameter |
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:17:21:17:21 | x | parameter |
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:17:24:17:24 | y | parameter |
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:17:3:18:5 | self in self_foo | parameter |
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:26:13:26:13 | x | parameter |
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:26:16:26:16 | y | parameter |
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:26:5:27:7 | self in foo | parameter |
| B | Method[foo] | Argument[0] | lib/other.rb:6:11:6:11 | x | parameter |
| B | Method[foo] | Argument[1] | lib/other.rb:6:14:6:14 | y | parameter |
| B | Method[foo] | Argument[self] | lib/other.rb:6:3:7:5 | self in foo | parameter |
@@ -36,31 +35,31 @@ input
| OtherLib::A | Method[foo] | Argument[1] | other_lib/lib/other_gem.rb:3:20:3:20 | y | parameter |
| OtherLib::A | Method[foo] | Argument[self] | other_lib/lib/other_gem.rb:3:9:4:11 | self in foo | parameter |
output
| A | Method[bar] | Argument[0] | lib/mylib.rb:13:11:13:11 | x | parameter |
| A | Method[bar] | Argument[self] | lib/mylib.rb:13:3:14:5 | self in bar | parameter |
| A | Method[foo] | Argument[0] | lib/mylib.rb:7:11:7:11 | x | parameter |
| A | Method[foo] | Argument[1] | lib/mylib.rb:7:14:7:14 | y | parameter |
| A | Method[foo] | Argument[2] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:8:5:8:32 | call to call | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:10:5:10:26 | yield ... | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:8:16:8:16 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:10:11:10:11 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:8:19:8:19 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:10:14:10:14 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:8:28:8:31 | key1 | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:10:23:10:26 | key1 | parameter |
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:7:17:7:20 | key1 | parameter |
| A | Method[foo] | Argument[self] | lib/mylib.rb:7:3:11:5 | self in foo | parameter |
| A | Method[foo] | ReturnValue | lib/mylib.rb:10:5:10:26 | yield ... | return |
| A | Method[bar] | Argument[0] | lib/mylib.rb:14:11:14:11 | x | parameter |
| A | Method[bar] | Argument[self] | lib/mylib.rb:14:3:15:5 | self in bar | parameter |
| A | Method[foo] | Argument[0] | lib/mylib.rb:8:11:8:11 | x | parameter |
| A | Method[foo] | Argument[1] | lib/mylib.rb:8:14:8:14 | y | parameter |
| A | Method[foo] | Argument[2] | lib/mylib.rb:8:17:8:20 | key1 | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:9:5:9:32 | call to call | parameter |
| A | Method[foo] | Argument[block] | lib/mylib.rb:11:5:11:26 | yield ... | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:9:16:9:16 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[0] | lib/mylib.rb:11:11:11:11 | x | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:9:19:9:19 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[1] | lib/mylib.rb:11:14:11:14 | y | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:9:28:9:31 | key1 | parameter |
| A | Method[foo] | Argument[block].Parameter[key2:] | lib/mylib.rb:11:23:11:26 | key1 | parameter |
| A | Method[foo] | Argument[key1:] | lib/mylib.rb:8:17:8:20 | key1 | parameter |
| A | Method[foo] | Argument[self] | lib/mylib.rb:8:3:12:5 | self in foo | parameter |
| A | Method[foo] | ReturnValue | lib/mylib.rb:11:5:11:26 | yield ... | return |
| A! | Method[new] | Argument[0] | lib/mylib.rb:4:18:4:18 | x | parameter |
| A! | Method[new] | Argument[1] | lib/mylib.rb:4:21:4:21 | y | parameter |
| A! | Method[new] | Argument[self] | lib/mylib.rb:4:3:5:5 | self in initialize | parameter |
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:16:21:16:21 | x | parameter |
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:16:24:16:24 | y | parameter |
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:16:3:17:5 | self in self_foo | parameter |
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:25:13:25:13 | x | parameter |
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:25:16:25:16 | y | parameter |
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:25:5:26:7 | self in foo | parameter |
| A! | Method[new] | ReturnValue | lib/mylib.rb:4:3:6:5 | self in initialize | return |
| A! | Method[self_foo] | Argument[0] | lib/mylib.rb:17:21:17:21 | x | parameter |
| A! | Method[self_foo] | Argument[1] | lib/mylib.rb:17:24:17:24 | y | parameter |
| A! | Method[self_foo] | Argument[self] | lib/mylib.rb:17:3:18:5 | self in self_foo | parameter |
| A::ANested | Method[foo] | Argument[0] | lib/mylib.rb:26:13:26:13 | x | parameter |
| A::ANested | Method[foo] | Argument[1] | lib/mylib.rb:26:16:26:16 | y | parameter |
| A::ANested | Method[foo] | Argument[self] | lib/mylib.rb:26:5:27:7 | self in foo | parameter |
| B | Method[foo] | Argument[0] | lib/other.rb:6:11:6:11 | x | parameter |
| B | Method[foo] | Argument[1] | lib/other.rb:6:14:6:14 | y | parameter |
| B | Method[foo] | Argument[self] | lib/other.rb:6:3:7:5 | self in foo | parameter |

View File

@@ -1,13 +1,13 @@
| lib/module.rb:1:1:7:3 | M1 | mylib | M1 | | | false | module.rb | |
| lib/module.rb:2:3:3:5 | foo | mylib | M1 | foo | (x,y) | false | module.rb | |
| lib/module.rb:5:3:6:5 | self_foo | mylib | M1! | self_foo | (x,y) | false | module.rb | |
| lib/mylib.rb:3:1:33:3 | A | mylib | A | | | false | mylib.rb | |
| lib/mylib.rb:4:3:5:5 | initialize | mylib | A! | new | (x,y) | false | mylib.rb | |
| lib/mylib.rb:7:3:11:5 | foo | mylib | A | foo | (x,y,key1:) | false | mylib.rb | |
| lib/mylib.rb:13:3:14:5 | bar | mylib | A | bar | (x) | false | mylib.rb | |
| lib/mylib.rb:16:3:17:5 | self_foo | mylib | A! | self_foo | (x,y) | false | mylib.rb | |
| lib/mylib.rb:24:3:32:5 | ANested | mylib | A::ANested | | | false | mylib.rb | |
| lib/mylib.rb:25:5:26:7 | foo | mylib | A::ANested | foo | (x,y) | false | mylib.rb | |
| lib/mylib.rb:3:1:34:3 | A | mylib | A | | | false | mylib.rb | |
| lib/mylib.rb:4:3:6:5 | initialize | mylib | A! | new | (x,y) | false | mylib.rb | |
| lib/mylib.rb:8:3:12:5 | foo | mylib | A | foo | (x,y,key1:) | false | mylib.rb | |
| lib/mylib.rb:14:3:15:5 | bar | mylib | A | bar | (x) | false | mylib.rb | |
| lib/mylib.rb:17:3:18:5 | self_foo | mylib | A! | self_foo | (x,y) | false | mylib.rb | |
| lib/mylib.rb:25:3:33:5 | ANested | mylib | A::ANested | | | false | mylib.rb | |
| lib/mylib.rb:26:5:27:7 | foo | mylib | A::ANested | foo | (x,y) | false | mylib.rb | |
| lib/other.rb:3:1:8:3 | B | mylib | B | | | false | other.rb | |
| lib/other.rb:6:3:7:5 | foo | mylib | B | foo | (x,y) | false | other.rb | |
| lib/other.rb:10:1:12:3 | C | mylib | C | | | false | other.rb | |

View File

@@ -4,3 +4,4 @@ typeVariableModel
typeModel
| M1 | B | |
summaryModel
| A! | Method[new] | Argument[0] | ReturnValue | value |

View File

@@ -2,6 +2,7 @@ require_relative "./other"
class A
def initialize(x, y)
@x = x
end
def foo(x, y, key1:, **kwargs, &block)