diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 15f0d4f08a..19d832be36 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1669,7 +1669,15 @@ impl<'a> Visitor<'a> for Checker<'a> { } self.visit_expr_context(ctx); } else { - debug!("Found non-Expr::Tuple argument to PEP 593 Annotation."); + if self.semantic.in_type_definition() { + // this should potentially trigger some kind of violation in the + // future, since it would indicate an invalid type expression + debug!("Found non-Expr::Tuple argument to PEP 593 Annotation."); + } + // even if the expression is invalid as a type expression, we should + // still visit it so we don't accidentally treat variables as unused + self.visit_expr(slice); + self.visit_expr_context(ctx); } } Some(typing::SubscriptKind::TypedDict) => { diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index 717821e95f..273e10e9da 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -4263,4 +4263,22 @@ lambda: fu &[], ); } + + #[test] + fn gh_issue_17196_regression_test() { + flakes( + r#" + from typing import Annotated + + def type_annotations_from_tuple(): + annos = (str, "foo", "bar") + return Annotated[annos] + + def type_annotations_from_filtered_tuple(): + annos = (str, None, "foo", None, "bar") + return Annotated[tuple([a for a in annos if a is not None])] + "#, + &[], + ); + } }