mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
[ty] Emit invalid type form for stringified annotations (#22752)
## Summary Closes https://github.com/astral-sh/ty/issues/2553.
This commit is contained in:
@@ -155,6 +155,52 @@ def invalid_binary_operators(
|
||||
reveal_type(l) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Invalid AST nodes in string annotations
|
||||
|
||||
Invalid AST nodes should also be rejected when they appear in string annotations:
|
||||
|
||||
```py
|
||||
def bar() -> None:
|
||||
return None
|
||||
|
||||
async def baz(): ...
|
||||
async def outer_async(): # avoid unrelated syntax errors on `yield` and `await`
|
||||
def _(
|
||||
a: "1", # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression"
|
||||
b: "2.3", # error: [invalid-type-form] "Float literals are not allowed in type expressions"
|
||||
c: "4j", # error: [invalid-type-form] "Complex literals are not allowed in type expressions"
|
||||
d: "True", # error: [invalid-type-form] "Boolean literals are not allowed in this context in a type expression"
|
||||
e: "1 and 2", # error: [invalid-type-form] "Boolean operations are not allowed in type expressions"
|
||||
f: "1 or 2", # error: [invalid-type-form] "Boolean operations are not allowed in type expressions"
|
||||
g: "(foo := 1)", # error: [invalid-type-form] "Named expressions are not allowed in type expressions"
|
||||
h: "not 1", # error: [invalid-type-form] "Unary operations are not allowed in type expressions"
|
||||
i: "lambda: 1", # error: [invalid-type-form] "`lambda` expressions are not allowed in type expressions"
|
||||
j: "1 if True else 2", # error: [invalid-type-form] "`if` expressions are not allowed in type expressions"
|
||||
k: "await baz()", # error: [invalid-type-form] "`await` expressions are not allowed in type expressions"
|
||||
l: "(yield 1)", # error: [invalid-type-form] "`yield` expressions are not allowed in type expressions"
|
||||
m: "1 < 2", # error: [invalid-type-form] "Comparison expressions are not allowed in type expressions"
|
||||
n: "bar()", # error: [invalid-type-form] "Function calls are not allowed in type expressions"
|
||||
# error: [invalid-type-form] "Slices are not allowed in type expressions"
|
||||
# error: [invalid-type-form] "Invalid subscript"
|
||||
o: "[1, 2, 3][1:2]",
|
||||
):
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
reveal_type(c) # revealed: Unknown
|
||||
reveal_type(d) # revealed: Unknown
|
||||
reveal_type(e) # revealed: Unknown
|
||||
reveal_type(f) # revealed: Unknown
|
||||
reveal_type(g) # revealed: Unknown
|
||||
reveal_type(h) # revealed: Unknown
|
||||
reveal_type(i) # revealed: Unknown
|
||||
reveal_type(j) # revealed: Unknown
|
||||
reveal_type(k) # revealed: Unknown
|
||||
reveal_type(l) # revealed: Unknown
|
||||
reveal_type(m) # revealed: Unknown
|
||||
reveal_type(n) # revealed: Unknown
|
||||
reveal_type(o) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Invalid Collection based AST nodes
|
||||
|
||||
```toml
|
||||
|
||||
@@ -166,29 +166,48 @@ stringified_fstring_with_dict_comprehension: "f'{ {i: i for i in range(5)} }'"
|
||||
# error: [fstring-type-annotation]
|
||||
stringified_fstring_with_set_comprehension: "f'{ {i for i in range(5)} }'"
|
||||
|
||||
# error: [invalid-type-form]
|
||||
a: "1 or 2"
|
||||
# error: [invalid-type-form]
|
||||
b: "(x := 1)"
|
||||
# error: [invalid-type-form]
|
||||
c: "1 + 2"
|
||||
# Regression test for https://github.com/astral-sh/ty/issues/1847
|
||||
# error: [invalid-type-form]
|
||||
c2: "a*(i for i in [])"
|
||||
# error: [invalid-type-form]
|
||||
d: "lambda x: x"
|
||||
# error: [invalid-type-form]
|
||||
e: "x if True else y"
|
||||
# error: [invalid-type-form]
|
||||
f: "{'a': 1, 'b': 2}"
|
||||
# error: [invalid-type-form]
|
||||
g: "{1, 2}"
|
||||
# error: [invalid-type-form]
|
||||
h: "[i for i in range(5)]"
|
||||
# error: [invalid-type-form]
|
||||
i: "{i for i in range(5)}"
|
||||
# error: [invalid-type-form]
|
||||
j: "{i: i for i in range(5)}"
|
||||
# error: [invalid-type-form]
|
||||
k: "(i for i in range(5))"
|
||||
# error: [invalid-type-form]
|
||||
l: "await 1"
|
||||
# error: [invalid-syntax-in-forward-annotation]
|
||||
m: "yield 1"
|
||||
# error: [invalid-syntax-in-forward-annotation]
|
||||
n: "yield from 1"
|
||||
# error: [invalid-type-form]
|
||||
o: "1 < 2"
|
||||
# error: [invalid-type-form]
|
||||
p: "call()"
|
||||
# error: [invalid-type-form] "List literals are not allowed"
|
||||
# error: [invalid-type-form] "Int literals are not allowed"
|
||||
# error: [invalid-type-form] "Int literals are not allowed"
|
||||
r: "[1, 2]"
|
||||
# error: [invalid-type-form] "Tuple literals are not allowed"
|
||||
# error: [invalid-type-form] "Int literals are not allowed"
|
||||
# error: [invalid-type-form] "Int literals are not allowed"
|
||||
s: "(1, 2)"
|
||||
```
|
||||
|
||||
|
||||
@@ -173,10 +173,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid inferring the types of invalid type expressions that have been parsed from a
|
||||
// string annotation, as they are not present in the semantic index.
|
||||
_ if self.deferred_state.in_string_annotation() => Type::unknown(),
|
||||
|
||||
// =====================================================================================
|
||||
// Forms which are invalid in the context of annotation expressions: we infer their
|
||||
// nested expressions as normal expressions, but the type of the top-level expression is
|
||||
@@ -308,7 +304,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::BoolOp(bool_op) => {
|
||||
self.infer_boolean_expression(bool_op);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_boolean_expression(bool_op);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Boolean operations are not allowed in type expressions"),
|
||||
@@ -317,7 +315,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Named(named) => {
|
||||
self.infer_named_expression(named);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_named_expression(named);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Named expressions are not allowed in type expressions"),
|
||||
@@ -326,7 +326,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::UnaryOp(unary) => {
|
||||
self.infer_unary_expression(unary);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_unary_expression(unary);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Unary operations are not allowed in type expressions"),
|
||||
@@ -335,7 +337,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Lambda(lambda_expression) => {
|
||||
self.infer_lambda_expression(lambda_expression);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_lambda_expression(lambda_expression);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("`lambda` expressions are not allowed in type expressions"),
|
||||
@@ -344,7 +348,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::If(if_expression) => {
|
||||
self.infer_if_expression(if_expression, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_if_expression(if_expression, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("`if` expressions are not allowed in type expressions"),
|
||||
@@ -353,7 +359,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Dict(dict) => {
|
||||
self.infer_dict_expression(dict, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_dict_expression(dict, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Dict literals are not allowed in type expressions"),
|
||||
@@ -362,7 +370,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Set(set) => {
|
||||
self.infer_set_expression(set, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_set_expression(set, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Set literals are not allowed in type expressions"),
|
||||
@@ -371,7 +381,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::DictComp(dictcomp) => {
|
||||
self.infer_dict_comprehension_expression(dictcomp, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_dict_comprehension_expression(dictcomp, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Dict comprehensions are not allowed in type expressions"),
|
||||
@@ -380,7 +392,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::ListComp(listcomp) => {
|
||||
self.infer_list_comprehension_expression(listcomp, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_list_comprehension_expression(listcomp, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("List comprehensions are not allowed in type expressions"),
|
||||
@@ -389,7 +403,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::SetComp(setcomp) => {
|
||||
self.infer_set_comprehension_expression(setcomp, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_set_comprehension_expression(setcomp, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Set comprehensions are not allowed in type expressions"),
|
||||
@@ -398,7 +414,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Generator(generator) => {
|
||||
self.infer_generator_expression(generator);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_generator_expression(generator);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Generator expressions are not allowed in type expressions"),
|
||||
@@ -407,7 +425,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Await(await_expression) => {
|
||||
self.infer_await_expression(await_expression);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_await_expression(await_expression);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("`await` expressions are not allowed in type expressions"),
|
||||
@@ -416,7 +436,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Yield(yield_expression) => {
|
||||
self.infer_yield_expression(yield_expression);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_yield_expression(yield_expression);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("`yield` expressions are not allowed in type expressions"),
|
||||
@@ -425,7 +447,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::YieldFrom(yield_from) => {
|
||||
self.infer_yield_from_expression(yield_from);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_yield_from_expression(yield_from);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("`yield from` expressions are not allowed in type expressions"),
|
||||
@@ -434,7 +458,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Compare(compare) => {
|
||||
self.infer_compare_expression(compare);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_compare_expression(compare);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Comparison expressions are not allowed in type expressions"),
|
||||
@@ -443,7 +469,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Call(call_expr) => {
|
||||
self.infer_call_expression(call_expr, TypeContext::default());
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_call_expression(call_expr, TypeContext::default());
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Function calls are not allowed in type expressions"),
|
||||
@@ -452,7 +480,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::FString(fstring) => {
|
||||
self.infer_fstring_expression(fstring);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_fstring_expression(fstring);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("F-strings are not allowed in type expressions"),
|
||||
@@ -461,7 +491,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::TString(tstring) => {
|
||||
self.infer_tstring_expression(tstring);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_tstring_expression(tstring);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("T-strings are not allowed in type expressions"),
|
||||
@@ -470,7 +502,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
|
||||
ast::Expr::Slice(slice) => {
|
||||
self.infer_slice_expression(slice);
|
||||
if !self.deferred_state.in_string_annotation() {
|
||||
self.infer_slice_expression(slice);
|
||||
}
|
||||
self.report_invalid_type_expression(
|
||||
expression,
|
||||
format_args!("Slices are not allowed in type expressions"),
|
||||
|
||||
Reference in New Issue
Block a user