Ruby: Add subclassing support to API Graphs

Given the code

    class A; end
    class B < A; end
    class C < A; end

You can find uses of B and C with the expression

    API::getTopLevelMember("A").getASubclass()
This commit is contained in:
Harry Maclean
2022-01-19 19:36:00 +13:00
parent 8e40899dfd
commit 8419daad03
2 changed files with 19 additions and 4 deletions

View File

@@ -254,10 +254,9 @@ module API {
*/
pragma[nomagic]
private predicate useRoot(string lbl, DataFlow::Node ref) {
exists(string name, ExprNodes::ConstantAccessCfgNode access, ConstantReadAccess read |
access = ref.asExpr() and
lbl = Label::member(read.getName()) and
read = access.getExpr()
exists(string name, ConstantReadAccess read |
read = ref.asExpr().getExpr() and
lbl = Label::member(read.getName())
|
name = resolveTopLevel(read)
or
@@ -389,6 +388,17 @@ module API {
useStep(lbl, node, ref)
)
)
or
// `pred` is a use of class A
// `succ` is a use of class B
// there exists a class declaration B < A
exists(ClassDeclaration c, DataFlow::Node a, DataFlow::Node b |
use(pred, a) and
use(succ, b) and
resolveConstant(b.asExpr().getExpr()) = resolveConstantWriteAccess(c) and
c.getSuperclassExpr() = a.asExpr().getExpr() and
lbl = Label::subclass()
)
}
/**

View File

@@ -233,12 +233,17 @@ private module ResolveImpl {
pragma[nomagic]
private string resolveConstantReadAccessNonRec(ConstantReadAccess c, int priority) {
// ::B
c.hasGlobalScope() and result = c.getName() and priority = 0
or
// A::B
exists(string name, string s | result = isDefinedConstantNonRec(s, name) |
s = resolveConstantReadAccessScopeNonRec(c, priority, name)
)
or
// module A
// B
// end
exists(string name |
exists(Namespace n, string qname |
n = constantReadAccessEnclosingNameSpace(c, priority, name) and