Python: Add support for forward declarations in unused var query

Fixes the false positive reported in
https://github.com/github/codeql/issues/18910

Adds a new `Annotation` class (subclass of `Expr`) which encompasses all
possible kinds of annotations in Python.

Using this, we look for string literals which are part of an annotation,
and which have the same content as the name of a (potentially) unused
global variable, and in that case we do not produce an alert.

In future, we may want to support inspecting such string literals more
deeply (e.g. to support stuff like "list[unused_var]"), but I think for
now this level of support is sufficient.
This commit is contained in:
Taus
2025-03-04 14:03:33 +00:00
parent 301ebcb12b
commit 88615f427b
3 changed files with 28 additions and 4 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 | args = any(FunctionExpr f).getArgs() |
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

@@ -4,6 +4,3 @@
| variables_test.py:86:3:86:3 | b | The global variable 'b' is not used. |
| variables_test.py:86:5:86:5 | c | The global variable 'c' is not used. |
| variables_test.py:100:1:100:8 | glob_var | The global variable 'glob_var' is not used. |
| variables_test.py:147:5:147:26 | ForwardParamAnnotation | The global variable 'ForwardParamAnnotation' is not used. |
| variables_test.py:148:5:148:27 | ForwardReturnAnnotation | The global variable 'ForwardReturnAnnotation' is not used. |
| variables_test.py:149:5:149:31 | ForwardAssignmentAnnotation | The global variable 'ForwardAssignmentAnnotation' is not used. |