mirror of
https://github.com/github/codeql.git
synced 2026-05-27 17:41:24 +02:00
Option 2: eliminates the AST→CFG bridge from the AST layer. Previously
'AstNode.getAFlowNode()' returned a 'ControlFlowNode' from the legacy
'Flow.qll' CFG via 'py_flow_bb_node' — this hardcoded the AST to know
about the legacy CFG, preventing files from cleanly switching to the
new shared CFG.
Removes:
* 'AstNode.getAFlowNode()' from 'AstExtended.qll'
* Type-narrowing overrides on 'Attribute' / 'Subscript' / 'Call' /
'IfExp' / 'Name' / 'NameConstant' / 'ImportMember' (in Exprs.qll
and Import.qll)
Rewrites ~130 call sites across 'python/ql/lib/' and 'python/ql/src/'
to bridge from the CFG side instead:
Before: node = expr.getAFlowNode()
After: node.getNode() = expr
Before: expr.getAFlowNode().(DefinitionNode).getValue()
After: exists(DefinitionNode d | d.getNode() = expr | d.getValue())
Before: cn.operands(const.getAFlowNode(), op, x)
After: exists(ControlFlowNode c | c.getNode() = const | cn.operands(c, op, x))
This is semantically a no-op — both forms are duals of the same predicate.
Verified by passing all library tests:
* 64 dataflow tests
* 28 ControlFlow + dataflow-new-ssa tests
* 1 essa SSA-compute test
* 93 tests total in the focused suite
Once committed, files that want to switch from the legacy 'Flow' CFG
to the new 'Cfg' facade only need to change their imports — the
bridge sites are CFG-side and respect whichever ControlFlowNode is in
scope.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
106 lines
2.9 KiB
Plaintext
106 lines
2.9 KiB
Plaintext
/**
|
|
* Symbols for cross-project jump-to-definition resolution.
|
|
*/
|
|
|
|
import python
|
|
private import LegacyPointsTo
|
|
|
|
private newtype TSymbol =
|
|
TModule(Module m) or
|
|
TMember(Symbol outer, string part) {
|
|
exists(Object o | outer.resolvesTo() = o |
|
|
o.(ModuleObject).hasAttribute(part)
|
|
or
|
|
o.(ClassObject).hasAttribute(part)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* A "symbol" referencing an object in another module
|
|
* Symbols are represented by the module name and the dotted name by which the
|
|
* object would be referred to in that module.
|
|
* For example for the code:
|
|
* ```
|
|
* class C:
|
|
* def m(self): pass
|
|
* ```
|
|
* If the code were in a module `mod`,
|
|
* then symbol for the method `m` would be "mod/C.m"
|
|
*/
|
|
class Symbol extends TSymbol {
|
|
string toString() {
|
|
exists(Module m | this = TModule(m) and result = m.getName())
|
|
or
|
|
exists(TModule outer, string part |
|
|
this = TMember(outer, part) and
|
|
outer = TModule(_) and
|
|
result = outer.(Symbol).toString() + "/" + part
|
|
)
|
|
or
|
|
exists(TMember outer, string part |
|
|
this = TMember(outer, part) and
|
|
outer = TMember(_, _) and
|
|
result = outer.(Symbol).toString() + "." + part
|
|
)
|
|
}
|
|
|
|
/** Finds the `AstNode` that this `Symbol` refers to. */
|
|
AstNode find() {
|
|
this = TModule(result)
|
|
or
|
|
exists(Symbol s, string name, ControlFlowNode resultCfg |
|
|
this = TMember(s, name) and resultCfg.getNode() = result
|
|
|
|
|
exists(ClassObject cls |
|
|
s.resolvesTo() = cls and
|
|
cls.attributeRefersTo(name, _, resultCfg)
|
|
)
|
|
or
|
|
exists(ModuleObject m |
|
|
s.resolvesTo() = m and
|
|
m.attributeRefersTo(name, _, resultCfg)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Find the class or module `Object` that this `Symbol` refers to, if
|
|
* this `Symbol` refers to a class or module.
|
|
*/
|
|
Object resolvesTo() {
|
|
this = TModule(result.(ModuleObject).getModule())
|
|
or
|
|
exists(Symbol s, string name, Object o |
|
|
this = TMember(s, name) and
|
|
o = s.resolvesTo() and
|
|
result = attribute_in_scope(o, name)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the `Module` for the module part of this `Symbol`.
|
|
* For example, this would return the `os` module for the `Symbol` "os/environ".
|
|
*/
|
|
Module getModule() {
|
|
this = TModule(result)
|
|
or
|
|
exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule())
|
|
}
|
|
|
|
/** Gets the `Symbol` that is the named member of this `Symbol`. */
|
|
Symbol getMember(string name) { result = TMember(this, name) }
|
|
}
|
|
|
|
/* Helper for `Symbol`.resolvesTo() */
|
|
private Object attribute_in_scope(Object obj, string name) {
|
|
exists(ClassObject cls | cls = obj |
|
|
cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass()
|
|
)
|
|
or
|
|
exists(ModuleObject mod | mod = obj |
|
|
mod.attr(name) = result and
|
|
result.(ControlFlowNode).getScope() = mod.getModule() and
|
|
not result.(ControlFlowNode).isEntryNode()
|
|
)
|
|
}
|