Skip to content

Commit e93c46a

Browse files
authored
Merge pull request #7811 from erik-krogh/pyApiIpa
Python: refactor API-graph labels to an IPA type
2 parents 5284bbb + e06f652 commit e93c46a

File tree

1 file changed

+125
-35
lines changed

1 file changed

+125
-35
lines changed

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 125 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,13 @@ module API {
114114
* Gets a node such that there is an edge in the API graph between this node and the other
115115
* one, and that edge is labeled with `lbl`.
116116
*/
117-
Node getASuccessor(string lbl) { Impl::edge(this, lbl, result) }
117+
Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) }
118118

119119
/**
120120
* Gets a node such that there is an edge in the API graph between that other node and
121121
* this one, and that edge is labeled with `lbl`
122122
*/
123-
Node getAPredecessor(string lbl) { this = result.getASuccessor(lbl) }
123+
Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) }
124124

125125
/**
126126
* Gets a node such that there is an edge in the API graph between this node and the other
@@ -174,9 +174,8 @@ module API {
174174
length = 0 and
175175
result = ""
176176
or
177-
exists(Node pred, string lbl, string predpath |
177+
exists(Node pred, Label::ApiLabel lbl, string predpath |
178178
Impl::edge(pred, lbl, this) and
179-
lbl != "" and
180179
predpath = pred.getAPath(length - 1) and
181180
exists(string dot | if length = 1 then dot = "" else dot = "." |
182181
result = predpath + dot + lbl and
@@ -335,7 +334,8 @@ module API {
335334
*
336335
* For instance, `prefix_member("foo.bar", "baz", "foo.bar.baz")` would hold.
337336
*/
338-
private predicate prefix_member(TApiNode base, string member, TApiNode sub) {
337+
cached
338+
predicate prefix_member(TApiNode base, string member, TApiNode sub) {
339339
exists(string sub_str, string regexp |
340340
regexp = "(.+)[.]([^.]+)" and
341341
base = MkModuleImport(sub_str.regexpCapture(regexp, 1)) and
@@ -386,7 +386,7 @@ module API {
386386
* `lbl` in the API graph.
387387
*/
388388
cached
389-
predicate use(TApiNode base, string lbl, DataFlow::Node ref) {
389+
predicate use(TApiNode base, Label::ApiLabel lbl, DataFlow::Node ref) {
390390
exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode pred |
391391
// First, we find a predecessor of the node `ref` that we want to determine. The predecessor
392392
// is any node that is a type-tracked use of a data flow node (`src`), which is itself a
@@ -481,7 +481,7 @@ module API {
481481
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
482482
*/
483483
cached
484-
predicate edge(TApiNode pred, string lbl, TApiNode succ) {
484+
predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) {
485485
/* There's an edge from the root node for each imported module. */
486486
exists(string m |
487487
pred = MkRoot() and
@@ -514,36 +514,126 @@ module API {
514514
cached
515515
int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result)
516516
}
517-
}
518517

519-
private module Label {
520-
/** Gets the edge label for the module `m`. */
521-
bindingset[m]
522-
bindingset[result]
523-
string mod(string m) { result = "moduleImport(\"" + m + "\")" }
524-
525-
/** Gets the `member` edge label for member `m`. */
526-
bindingset[m]
527-
bindingset[result]
528-
string member(string m) { result = "getMember(\"" + m + "\")" }
529-
530-
/** Gets the `member` edge label for the unknown member. */
531-
string unknownMember() { result = "getUnknownMember()" }
532-
533-
/** Gets the `member` edge label for the given attribute reference. */
534-
string memberFromRef(DataFlow::AttrRef pr) {
535-
result = member(pr.getAttributeName())
536-
or
537-
not exists(pr.getAttributeName()) and
538-
result = unknownMember()
539-
}
518+
/** Provides classes modeling the various edges (labels) in the API graph. */
519+
module Label {
520+
/** A label in the API-graph */
521+
class ApiLabel extends TLabel {
522+
/** Gets a string representation of this label. */
523+
string toString() { result = "???" }
524+
}
525+
526+
private import LabelImpl
527+
528+
private module LabelImpl {
529+
private import semmle.python.dataflow.new.internal.Builtins
530+
private import semmle.python.dataflow.new.internal.ImportStar
531+
532+
newtype TLabel =
533+
MkLabelModule(string mod) { exists(Impl::MkModuleImport(mod)) } or
534+
MkLabelMember(string member) {
535+
member = any(DataFlow::AttrRef pr).getAttributeName() or
536+
exists(Builtins::likelyBuiltin(member)) or
537+
ImportStar::namePossiblyDefinedInImportStar(_, member, _) or
538+
Impl::prefix_member(_, member, _)
539+
} or
540+
MkLabelUnknownMember() or
541+
MkLabelParameter(int i) {
542+
none() // TODO: Fill in when adding def nodes
543+
} or
544+
MkLabelReturn() or
545+
MkLabelSubclass() or
546+
MkLabelAwait()
547+
548+
/** A label for a module. */
549+
class LabelModule extends ApiLabel {
550+
string mod;
551+
552+
LabelModule() { this = MkLabelModule(mod) }
553+
554+
/** Gets the module associated with this label. */
555+
string getMod() { result = mod }
556+
557+
override string toString() { result = "moduleImport(\"" + mod + "\")" }
558+
}
559+
560+
/** A label for the member named `prop`. */
561+
class LabelMember extends ApiLabel {
562+
string member;
563+
564+
LabelMember() { this = MkLabelMember(member) }
565+
566+
/** Gets the property associated with this label. */
567+
string getMember() { result = member }
568+
569+
override string toString() { result = "getMember(\"" + member + "\")" }
570+
}
571+
572+
/** A label for a member with an unknown name. */
573+
class LabelUnknownMember extends ApiLabel {
574+
LabelUnknownMember() { this = MkLabelUnknownMember() }
575+
576+
override string toString() { result = "getUnknownMember()" }
577+
}
578+
579+
/** A label for parameter `i`. */
580+
class LabelParameter extends ApiLabel {
581+
int i;
540582

541-
/** Gets the `return` edge label. */
542-
string return() { result = "getReturn()" }
583+
LabelParameter() { this = MkLabelParameter(i) }
543584

544-
/** Gets the `subclass` edge label. */
545-
string subclass() { result = "getASubclass()" }
585+
override string toString() { result = "getParameter(" + i + ")" }
546586

547-
/** Gets the `await` edge label. */
548-
string await() { result = "getAwaited()" }
587+
/** Gets the index of the parameter for this label. */
588+
int getIndex() { result = i }
589+
}
590+
591+
/** A label that gets the return value of a function. */
592+
class LabelReturn extends ApiLabel {
593+
LabelReturn() { this = MkLabelReturn() }
594+
595+
override string toString() { result = "getReturn()" }
596+
}
597+
598+
/** A label that gets the subclass of a class. */
599+
class LabelSubclass extends ApiLabel {
600+
LabelSubclass() { this = MkLabelSubclass() }
601+
602+
override string toString() { result = "getASubclass()" }
603+
}
604+
605+
/** A label for awaited values. */
606+
class LabelAwait extends ApiLabel {
607+
LabelAwait() { this = MkLabelAwait() }
608+
609+
override string toString() { result = "getAwaited()" }
610+
}
611+
}
612+
613+
/** Gets the edge label for the module `m`. */
614+
LabelModule mod(string m) { result.getMod() = m }
615+
616+
/** Gets the `member` edge label for member `m`. */
617+
LabelMember member(string m) { result.getMember() = m }
618+
619+
/** Gets the `member` edge label for the unknown member. */
620+
LabelUnknownMember unknownMember() { any() }
621+
622+
/** Gets the `member` edge label for the given attribute reference. */
623+
ApiLabel memberFromRef(DataFlow::AttrRef pr) {
624+
result = member(pr.getAttributeName())
625+
or
626+
not exists(pr.getAttributeName()) and
627+
result = unknownMember()
628+
}
629+
630+
/** Gets the `return` edge label. */
631+
LabelReturn return() { any() }
632+
633+
/** Gets the `subclass` edge label. */
634+
LabelSubclass subclass() { any() }
635+
636+
/** Gets the `await` edge label. */
637+
LabelAwait await() { any() }
638+
}
549639
}

0 commit comments

Comments
 (0)