Python: Distinguish between Python 2 and 3

Also moves the filtering on `name` to before the big disjunction in
`MkModuleImport`.
This commit is contained in:
Taus Brock-Nannestad
2021-03-12 12:35:23 +01:00
parent c7b2b719cf
commit 978200e2ad
6 changed files with 41 additions and 5 deletions

View File

@@ -303,15 +303,15 @@ module API {
MkRoot() or
/** An abstract representative for imports of the module called `name`. */
MkModuleImport(string name) {
// Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
(name != "__builtin__" or major_version() = 3) and
(
imports(_, name)
or
// When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
// `foo` and `foo.bar`:
name = any(ImportExpr e | not e.isRelative()).getAnImportedModuleName()
) and
// Ignore the following module name, as we alias `__builtin__` to `builtins` elsewhere
name != "__builtin__"
)
or
// The `builtins` module should always be implicitly available
name = "builtins"
@@ -414,6 +414,7 @@ module API {
)
or
// Ensure the Python 2 `__builtin__` module gets the name of the Python 3 `builtins` module.
major_version() = 2 and
nd = MkModuleImport("builtins") and
imports(ref, "__builtin__")
or

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=2 --max-import-depth=1

View File

@@ -0,0 +1,3 @@
def python2_style():
from __builtin__ import open #$ use=moduleImport("builtins").getMember("open")
open("hello.txt") #$ use=moduleImport("builtins").getMember("open").getReturn()

View File

@@ -0,0 +1,30 @@
import python
import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
import semmle.python.ApiGraphs
class ApiUseTest extends InlineExpectationsTest {
ApiUseTest() { this = "ApiUseTest" }
override string getARelevantTag() { result = "use" }
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
n = a.getAUse() and l = n.getLocation()
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(API::Node a, DataFlow::Node n | relevant_node(a, n, location) |
tag = "use" and
// Only report the longest path on this line:
value =
max(API::Node a2, Location l2 |
relevant_node(a2, _, l2) and
l2.getFile() = location.getFile() and
l2.getStartLine() = location.getStartLine()
|
a2.getPath()
) and
element = n.toString()
)
}
}

View File

@@ -136,5 +136,6 @@ def obscured_print():
p("Can you see me?") #$ use=moduleImport("builtins").getMember("print").getReturn()
def python2_style():
from __builtin__ import open #$ use=moduleImport("builtins").getMember("open")
open("hello.txt") #$ use=moduleImport("builtins").getMember("open").getReturn()
# In Python 3, `__builtin__` has no special meaning.
from __builtin__ import open #$ use=moduleImport("__builtin__").getMember("open")
open("hello.txt") #$ use=moduleImport("__builtin__").getMember("open").getReturn()