Provides the actual implementation of API graphs, cached for performance.
Ideally, we’d like nodes to correspond to (global) access paths, with edge labels corresponding to extending the access path by one element. We also want to be able to map nodes to their definitions and uses in the data-flow graph, and this should happen modulo (inter-procedural) data flow.
This, however, is not easy to implement, since access paths can have unbounded length and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing a condition like “this node hasn’t been involved in constructing any predecessor of this node in the API graph” without negative recursion is tricky.
So instead most nodes are directly associated with a data-flow node, representing either a use or a definition of an API component. This ensures that we only have a finite number of nodes. However, we can now have multiple nodes with the same access path, which are essentially indistinguishable for a client of the API.
On the other hand, a single node can have multiple access paths (which is, of course, unavoidable). We pick as canonical the alphabetically least access path with shortest length.
Import path
import semmle.python.ApiGraphs
Predicates
distanceFromRoot | Gets the shortest distance from the root to |
edge | Holds if there is an edge from |
isImported | Holds if the module |
prefix_member | Holds if the dotted module name |
rhs | Holds if |
rhs | Holds if |
trackDefNode | Gets a node that inter-procedurally flows into |
trackUseNode | Gets a data-flow node to which |
use | Holds if |
use | Holds if |