Merge pull request #7663 from github/hmac/api-graph-subclass

Ruby: Add basic subclassing support to API Graphs
This commit is contained in:
Harry Maclean
2022-02-04 10:19:07 +13:00
committed by GitHub
15 changed files with 256 additions and 135 deletions

View File

@@ -0,0 +1,6 @@
classMethodCalls
| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getReturn("m") |
| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getReturn("m") |
instanceMethodCalls
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").instance.getReturn("m") |
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").instance.getReturn("m") |

View File

@@ -0,0 +1,13 @@
/**
* Tests of the public API of API Graphs
*/
import codeql.ruby.ApiGraphs
query predicate classMethodCalls(API::Node node) {
node = API::getTopLevelMember("M1").getMember("C1").getReturn("m")
}
query predicate instanceMethodCalls(API::Node node) {
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
}

View File

@@ -29,3 +29,34 @@ module Outer
end
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getReturn("foo")
module M1
class C1
def self.m
end
def m
end
end
end
class C2 < M1::C1 #$ use=getMember("M1").getMember("C1")
end
module M2
class C3 < M1::C1 #$ use=getMember("M1").getMember("C1")
end
class C4 < C2 #$ use=getMember("C2")
end
end
C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass()
M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass()
M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass()
M1::C1.m #$ use=getMember("M1").getMember("C1").getReturn("m")
M2::C3.m #$ use=getMember("M2").getMember("C3").getReturn("m") use=getMember("M1").getMember("C1").getASubclass().getReturn("m")
M1::C1.new.m #$ use=getMember("M1").getMember("C1").instance.getReturn("m")
M2::C3.new.m #$ use=getMember("M2").getMember("C3").instance.getReturn("m")

View File

@@ -30,6 +30,38 @@ class ApiUseTest extends InlineExpectationsTest {
element = n.toString()
)
}
// We also permit optional annotations for any other path on the line.
// This is used to test subclass paths, which typically have a shorter canonical path.
override predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location) |
tag = "use" and
element = n.toString() and
value = getAPath(a, _)
)
}
}
private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) }
/**
* Gets a path of the given `length` from the root to the given node.
* This is a copy of `API::getAPath()` without the restriction on path length,
* which would otherwise rule out paths involving `getASubclass()`.
*/
string getAPath(API::Node node, int length) {
node instanceof API::Root and
length = 0 and
result = ""
or
exists(API::Node pred, string lbl, string predpath |
pred.getASuccessor(lbl) = node and
lbl != "" and
predpath = getAPath(pred, length - 1) and
exists(string dot | if length = 1 then dot = "" else dot = "." |
result = predpath + dot + lbl and
// avoid producing strings longer than 1MB
result.length() < 1000 * 1000
)
)
}

View File

@@ -93,4 +93,4 @@ class BazController < BarController
def yet_another_handler
Admin.delete_by(params[:admin_condition])
end
end
end