Skip to content

Commit a88c007

Browse files
committed
python: Allow dotted paths in type column
1 parent ea3cc51 commit a88c007

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
2929
/**
3030
* Holds if models describing `type` may be relevant for the analysis of this database.
3131
*/
32-
predicate isTypeUsed(string type) { API::moduleImportExists(type) }
32+
bindingset[type]
33+
predicate isTypeUsed(string type) {
34+
// If `type` is a path, then it is the first component that should be imported.
35+
API::moduleImportExists(type.splitAt(".", 0))
36+
}
3337

3438
/**
3539
* Holds if `type` can be obtained from an instance of `otherType` due to
@@ -41,8 +45,59 @@ predicate hasImplicitTypeModel(string type, string otherType) { none() }
4145
bindingset[type, path]
4246
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { none() }
4347

48+
/**
49+
* Holds if `type` = `typePath`+`suffix` and `suffix` is either empty or "!".
50+
*/
51+
bindingset[type]
52+
private predicate parseType(string type, string typePath, string suffix) {
53+
exists(string regexp |
54+
regexp = "([^!]+)(!|)" and
55+
typePath = type.regexpCapture(regexp, 1) and
56+
suffix = type.regexpCapture(regexp, 2)
57+
)
58+
}
59+
60+
private predicate parseRelevantType(string type, string typePath, string suffix) {
61+
isRelevantType(type) and
62+
parseType(type, typePath, suffix)
63+
}
64+
65+
pragma[nomagic]
66+
private string getTypePathComponent(string typePath, int n) {
67+
parseRelevantType(_, typePath, _) and
68+
result = typePath.splitAt(".", n)
69+
}
70+
71+
private int getNumTypePathomponents(string typePath) {
72+
result = strictcount(int n | exists(getTypePathComponent(typePath, n)))
73+
}
74+
75+
private API::Node getNodeFromTypePath(string typePath, int n) {
76+
n = 1 and
77+
result = API::moduleImport(getTypePathComponent(typePath, 0))
78+
or
79+
result = getNodeFromTypePath(typePath, n - 1).getMember(getTypePathComponent(typePath, n - 1))
80+
}
81+
82+
private API::Node getNodeFromTypePath(string typePath) {
83+
result = getNodeFromTypePath(typePath, getNumTypePathomponents(typePath))
84+
}
85+
4486
/** Gets a Python-specific interpretation of the given `type`. */
45-
API::Node getExtraNodeFromType(string type) { result = API::moduleImport(type) }
87+
API::Node getExtraNodeFromType(string type) {
88+
result = API::moduleImport(type)
89+
or
90+
exists(string typePath, string suffix, API::Node node |
91+
parseRelevantType(type, typePath, suffix) and
92+
node = getNodeFromTypePath(typePath)
93+
|
94+
suffix = "!" and
95+
result = node
96+
or
97+
suffix = "" and
98+
result = node.getAnInstance()
99+
)
100+
}
46101

47102
/**
48103
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.

0 commit comments

Comments
 (0)