Merge pull request #18921 from github/tausbn/python-fix-unused-global-variable-in-forward-annotation-fp

Python: Add support for forward references in unused var query
This commit is contained in:
Taus
2025-03-13 16:37:25 +01:00
committed by GitHub
4 changed files with 51 additions and 1 deletions

View File

@@ -746,6 +746,24 @@ class Guard extends Guard_ {
override Expr getASubExpression() { result = this.getTest() }
}
/** An annotation, such as the `int` part of `x: int` */
class Annotation extends Expr {
Annotation() {
this = any(AnnAssign a).getAnnotation()
or
exists(Arguments args |
this in [
args.getAnAnnotation(),
args.getAKwAnnotation(),
args.getKwargannotation(),
args.getVarargannotation()
]
)
or
this = any(FunctionExpr f).getReturns()
}
}
/* Expression Contexts */
/** A context in which an expression used */
class ExprContext extends ExprContext_ { }

View File

@@ -34,6 +34,14 @@ predicate complex_all(Module m) {
)
}
predicate used_in_forward_declaration(Name used, Module mod) {
exists(StringLiteral s, Annotation annotation |
s.getS() = used.getId() and
s.getEnclosingModule() = mod and
annotation.getASubExpression*() = s
)
}
predicate unused_global(Name unused, GlobalVariable v) {
not exists(ImportingStmt is | is.contains(unused)) and
forex(DefinitionNode defn | defn.getNode() = unused |
@@ -55,7 +63,8 @@ predicate unused_global(Name unused, GlobalVariable v) {
unused.defines(v) and
not name_acceptable_for_unused_variable(v) and
not complex_all(unused.getEnclosingModule())
)
) and
not used_in_forward_declaration(unused, unused.getEnclosingModule())
}
from Name unused, GlobalVariable v

View File

@@ -0,0 +1,5 @@
---
category: fix
---
- The `py/unused-global-variable` now no longer flags variables that are only used in forward references (e.g. the `Foo` in `def bar(x: "Foo"): ...`).

View File

@@ -137,3 +137,21 @@ def test_dict_unpacking(queryset, field_name, value):
for tag in value.split(','):
queryset = queryset.filter(**{field_name + '__name': tag})
return queryset
from typing import TYPE_CHECKING
if TYPE_CHECKING:
ParamAnnotation = int
ReturnAnnotation = int
AssignmentAnnotation = int
ForwardParamAnnotation = int
ForwardReturnAnnotation = int
ForwardAssignmentAnnotation = int
def test_direct_annotation(x: ParamAnnotation) -> ReturnAnnotation:
if x:
y : AssignmentAnnotation = 1
def test_forward_annotation(x: "ForwardParamAnnotation") -> "ForwardReturnAnnotation":
if x:
y : "ForwardAssignmentAnnotation" = 1