mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #2431 from tausbn/python-cyclic-import-future-annotations
Python: Account for non-evaluation of annotations in cyclic imports.
This commit is contained in:
@@ -60,7 +60,8 @@ predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use,
|
||||
exists(Expr mod |
|
||||
use.getEnclosingModule() = enclosing.getScope() and
|
||||
not use.getScope+() instanceof Function and
|
||||
mod.pointsTo(m)
|
||||
mod.pointsTo(m) and
|
||||
not is_annotation_with_from_future_import_annotations(use)
|
||||
|
|
||||
// either 'M.foo'
|
||||
use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr
|
||||
@@ -70,6 +71,30 @@ predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` appears inside an annotation.
|
||||
*/
|
||||
predicate is_used_in_annotation(Expr use) {
|
||||
exists(FunctionExpr f |
|
||||
f.getReturns().getASubExpression*() = use or
|
||||
f.getArgs().getAnAnnotation().getASubExpression*() = use
|
||||
)
|
||||
or
|
||||
exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` appears as a subexpression of an annotation, _and_ if the
|
||||
* postponed evaluation of annotations presented in PEP 563 is in effect.
|
||||
* See https://www.python.org/dev/peps/pep-0563/
|
||||
*/
|
||||
predicate is_annotation_with_from_future_import_annotations(Expr use) {
|
||||
exists(ImportMember i | i.getScope() = use.getEnclosingModule() |
|
||||
i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations"
|
||||
) and
|
||||
is_used_in_annotation(use)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether importing module 'first' before importing module 'other' will fail at runtime, due to an
|
||||
* AttributeError at 'use' (in module 'other') caused by 'first.attr' not being defined as its definition can
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| module3.py:8:23:8:33 | Attribute | 'Bar' may not be defined if module $@ is imported before module $@, as the $@ of Bar occurs after the cyclic $@ of module3. | module4.py:0:0:0:0 | Module module4 | module4 | module3.py:0:0:0:0 | Module module3 | module3 | module4.py:7:7:7:9 | ControlFlowNode for Bar | definition | module4.py:4:1:4:14 | Import | import |
|
||||
| module4.py:8:30:8:40 | Attribute | 'Foo' may not be defined if module $@ is imported before module $@, as the $@ of Foo occurs after the cyclic $@ of module4. | module3.py:0:0:0:0 | Module module3 | module3 | module4.py:0:0:0:0 | Module module4 | module4 | module3.py:7:7:7:9 | ControlFlowNode for Foo | definition | module3.py:4:1:4:14 | Import | import |
|
||||
@@ -0,0 +1 @@
|
||||
Imports/ModuleLevelCyclicImport.ql
|
||||
@@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import typing
|
||||
|
||||
import module2
|
||||
|
||||
@dataclasses.dataclass()
|
||||
class Foo:
|
||||
bars: typing.List[module2.Bar]
|
||||
@@ -0,0 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import typing
|
||||
|
||||
import module1
|
||||
|
||||
@dataclasses.dataclass()
|
||||
class Bar:
|
||||
def is_in_foo(self, foo: module1.Foo):
|
||||
return self in foo.bars
|
||||
@@ -0,0 +1,8 @@
|
||||
import dataclasses
|
||||
import typing
|
||||
|
||||
import module4
|
||||
|
||||
@dataclasses.dataclass()
|
||||
class Foo:
|
||||
bars: typing.List[module4.Bar]
|
||||
@@ -0,0 +1,9 @@
|
||||
import dataclasses
|
||||
import typing
|
||||
|
||||
import module3
|
||||
|
||||
@dataclasses.dataclass()
|
||||
class Bar:
|
||||
def is_in_foo(self, foo: module3.Foo):
|
||||
return self in foo.bars
|
||||
Reference in New Issue
Block a user