mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Add hints to invalid-type-form
for common mistakes (#18543)
Co-authored-by: Ben Bar-Or <ben.baror@ridewithvia.com> Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
301b9f4135
commit
1dc8f8f903
7 changed files with 462 additions and 74 deletions
|
@ -47,7 +47,7 @@ def _(flag: bool):
|
||||||
def _(x: Annotated | bool):
|
def _(x: Annotated | bool):
|
||||||
reveal_type(x) # revealed: Unknown | bool
|
reveal_type(x) # revealed: Unknown | bool
|
||||||
|
|
||||||
# error: [invalid-type-form]
|
# error: [invalid-type-form] "Special form `typing.Annotated` expected at least 2 arguments (one type and at least one metadata element)"
|
||||||
def _(x: Annotated[()]):
|
def _(x: Annotated[()]):
|
||||||
reveal_type(x) # revealed: Unknown
|
reveal_type(x) # revealed: Unknown
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,21 @@ def _(c: Callable[
|
||||||
reveal_type(c) # revealed: (...) -> Unknown
|
reveal_type(c) # revealed: (...) -> Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Tuple as the second argument
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
|
||||||
|
def _(c: Callable[
|
||||||
|
int, # error: [invalid-type-form] "The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`"
|
||||||
|
(str, ) # error: [invalid-type-form] "Tuple literals are not allowed in this context in a type expression"
|
||||||
|
]
|
||||||
|
):
|
||||||
|
reveal_type(c) # revealed: (...) -> Unknown
|
||||||
|
```
|
||||||
|
|
||||||
### List as both arguments
|
### List as both arguments
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
|
@ -95,6 +95,11 @@ async def outer(): # avoid unrelated syntax errors on yield, yield from, and aw
|
||||||
|
|
||||||
## Invalid Collection based AST nodes
|
## Invalid Collection based AST nodes
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.12"
|
||||||
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(
|
def _(
|
||||||
a: {1: 2}, # error: [invalid-type-form] "Dict literals are not allowed in type expressions"
|
a: {1: 2}, # error: [invalid-type-form] "Dict literals are not allowed in type expressions"
|
||||||
|
@ -103,7 +108,11 @@ def _(
|
||||||
d: [k for k in [1, 2]], # error: [invalid-type-form] "List comprehensions are not allowed in type expressions"
|
d: [k for k in [1, 2]], # error: [invalid-type-form] "List comprehensions are not allowed in type expressions"
|
||||||
e: {k for k in [1, 2]}, # error: [invalid-type-form] "Set comprehensions are not allowed in type expressions"
|
e: {k for k in [1, 2]}, # error: [invalid-type-form] "Set comprehensions are not allowed in type expressions"
|
||||||
f: (k for k in [1, 2]), # error: [invalid-type-form] "Generator expressions are not allowed in type expressions"
|
f: (k for k in [1, 2]), # error: [invalid-type-form] "Generator expressions are not allowed in type expressions"
|
||||||
g: [int, str], # error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
|
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?"
|
||||||
|
g: [int, str],
|
||||||
|
# error: [invalid-type-form] "Tuple literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?"
|
||||||
|
h: (int, str),
|
||||||
|
i: (), # error: [invalid-type-form] "Tuple literals are not allowed in this context in a type expression: Did you mean `tuple[()]`?"
|
||||||
):
|
):
|
||||||
reveal_type(a) # revealed: Unknown
|
reveal_type(a) # revealed: Unknown
|
||||||
reveal_type(b) # revealed: Unknown
|
reveal_type(b) # revealed: Unknown
|
||||||
|
@ -112,6 +121,17 @@ def _(
|
||||||
reveal_type(e) # revealed: Unknown
|
reveal_type(e) # revealed: Unknown
|
||||||
reveal_type(f) # revealed: Unknown
|
reveal_type(f) # revealed: Unknown
|
||||||
reveal_type(g) # revealed: Unknown
|
reveal_type(g) # revealed: Unknown
|
||||||
|
reveal_type(h) # revealed: Unknown
|
||||||
|
reveal_type(i) # revealed: Unknown
|
||||||
|
|
||||||
|
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `list[int]`?"
|
||||||
|
class name_0[name_2: [int]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
|
||||||
|
# error: [invalid-type-form] "Dict literals are not allowed in type expressions"
|
||||||
|
class name_4[name_1: [{}]]:
|
||||||
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
## Diagnostics for common errors
|
## Diagnostics for common errors
|
||||||
|
@ -145,3 +165,42 @@ from PIL import Image
|
||||||
|
|
||||||
def g(x: Image): ... # error: [invalid-type-form]
|
def g(x: Image): ... # error: [invalid-type-form]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### List-literal used when you meant to use a list or tuple
|
||||||
|
|
||||||
|
```py
|
||||||
|
def _(
|
||||||
|
x: [int], # error: [invalid-type-form]
|
||||||
|
) -> [int]: # error: [invalid-type-form]
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
def _(
|
||||||
|
x: [int, str], # error: [invalid-type-form]
|
||||||
|
) -> [int, str]: # error: [invalid-type-form]
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tuple-literal used when you meant to use a tuple
|
||||||
|
|
||||||
|
```py
|
||||||
|
def _(
|
||||||
|
x: (), # error: [invalid-type-form]
|
||||||
|
) -> (): # error: [invalid-type-form]
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
def _(
|
||||||
|
x: (int,), # error: [invalid-type-form]
|
||||||
|
) -> (int,): # error: [invalid-type-form]
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
def _(
|
||||||
|
x: (int, str), # error: [invalid-type-form]
|
||||||
|
) -> (int, str): # error: [invalid-type-form]
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: invalid.md - Tests for invalid types in type expressions - Diagnostics for common errors - List-literal used when you meant to use a list or tuple
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/invalid.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | def _(
|
||||||
|
2 | x: [int], # error: [invalid-type-form]
|
||||||
|
3 | ) -> [int]: # error: [invalid-type-form]
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
6 | x: [int, str], # error: [invalid-type-form]
|
||||||
|
7 | ) -> [int, str]: # error: [invalid-type-form]
|
||||||
|
8 | return x
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: List literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:2:8
|
||||||
|
|
|
||||||
|
1 | def _(
|
||||||
|
2 | x: [int], # error: [invalid-type-form]
|
||||||
|
| ^^^^^ Did you mean `list[int]`?
|
||||||
|
3 | ) -> [int]: # error: [invalid-type-form]
|
||||||
|
4 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: List literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:3:6
|
||||||
|
|
|
||||||
|
1 | def _(
|
||||||
|
2 | x: [int], # error: [invalid-type-form]
|
||||||
|
3 | ) -> [int]: # error: [invalid-type-form]
|
||||||
|
| ^^^^^ Did you mean `list[int]`?
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: List literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:6:8
|
||||||
|
|
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
6 | x: [int, str], # error: [invalid-type-form]
|
||||||
|
| ^^^^^^^^^^ Did you mean `tuple[int, str]`?
|
||||||
|
7 | ) -> [int, str]: # error: [invalid-type-form]
|
||||||
|
8 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: List literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:7:6
|
||||||
|
|
|
||||||
|
5 | def _(
|
||||||
|
6 | x: [int, str], # error: [invalid-type-form]
|
||||||
|
7 | ) -> [int, str]: # error: [invalid-type-form]
|
||||||
|
| ^^^^^^^^^^ Did you mean `tuple[int, str]`?
|
||||||
|
8 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: invalid.md - Tests for invalid types in type expressions - Diagnostics for common errors - Tuple-literal used when you meant to use a tuple
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/invalid.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | def _(
|
||||||
|
2 | x: (), # error: [invalid-type-form]
|
||||||
|
3 | ) -> (): # error: [invalid-type-form]
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
6 | x: (int,), # error: [invalid-type-form]
|
||||||
|
7 | ) -> (int,): # error: [invalid-type-form]
|
||||||
|
8 | return x
|
||||||
|
9 | def _(
|
||||||
|
10 | x: (int, str), # error: [invalid-type-form]
|
||||||
|
11 | ) -> (int, str): # error: [invalid-type-form]
|
||||||
|
12 | return x
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:2:8
|
||||||
|
|
|
||||||
|
1 | def _(
|
||||||
|
2 | x: (), # error: [invalid-type-form]
|
||||||
|
| ^^ Did you mean `tuple[()]`?
|
||||||
|
3 | ) -> (): # error: [invalid-type-form]
|
||||||
|
4 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:3:6
|
||||||
|
|
|
||||||
|
1 | def _(
|
||||||
|
2 | x: (), # error: [invalid-type-form]
|
||||||
|
3 | ) -> (): # error: [invalid-type-form]
|
||||||
|
| ^^ Did you mean `tuple[()]`?
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:6:8
|
||||||
|
|
|
||||||
|
4 | return x
|
||||||
|
5 | def _(
|
||||||
|
6 | x: (int,), # error: [invalid-type-form]
|
||||||
|
| ^^^^^^ Did you mean `tuple[int]`?
|
||||||
|
7 | ) -> (int,): # error: [invalid-type-form]
|
||||||
|
8 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:7:6
|
||||||
|
|
|
||||||
|
5 | def _(
|
||||||
|
6 | x: (int,), # error: [invalid-type-form]
|
||||||
|
7 | ) -> (int,): # error: [invalid-type-form]
|
||||||
|
| ^^^^^^ Did you mean `tuple[int]`?
|
||||||
|
8 | return x
|
||||||
|
9 | def _(
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:10:8
|
||||||
|
|
|
||||||
|
8 | return x
|
||||||
|
9 | def _(
|
||||||
|
10 | x: (int, str), # error: [invalid-type-form]
|
||||||
|
| ^^^^^^^^^^ Did you mean `tuple[int, str]`?
|
||||||
|
11 | ) -> (int, str): # error: [invalid-type-form]
|
||||||
|
12 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-type-form]: Tuple literals are not allowed in this context in a type expression
|
||||||
|
--> src/mdtest_snippet.py:11:6
|
||||||
|
|
|
||||||
|
9 | def _(
|
||||||
|
10 | x: (int, str), # error: [invalid-type-form]
|
||||||
|
11 | ) -> (int, str): # error: [invalid-type-form]
|
||||||
|
| ^^^^^^^^^^ Did you mean `tuple[int, str]`?
|
||||||
|
12 | return x
|
||||||
|
|
|
||||||
|
info: See the following page for a reference on valid type expressions:
|
||||||
|
info: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions
|
||||||
|
info: rule `invalid-type-form` is enabled by default
|
||||||
|
|
||||||
|
```
|
|
@ -1878,11 +1878,14 @@ pub(crate) fn report_invalid_arguments_to_callable(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_type_expression_reference_link(mut diag: LintDiagnosticGuard) {
|
pub(crate) fn add_type_expression_reference_link<'db, 'ctx>(
|
||||||
|
mut diag: LintDiagnosticGuard<'db, 'ctx>,
|
||||||
|
) -> LintDiagnosticGuard<'db, 'ctx> {
|
||||||
diag.info("See the following page for a reference on valid type expressions:");
|
diag.info("See the following page for a reference on valid type expressions:");
|
||||||
diag.info(
|
diag.info(
|
||||||
"https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions",
|
"https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions",
|
||||||
);
|
);
|
||||||
|
diag
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn report_runtime_check_against_non_runtime_checkable_protocol(
|
pub(crate) fn report_runtime_check_against_non_runtime_checkable_protocol(
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
//! the query cycle until a fixed-point is reached. Salsa has a built-in fixed limit on the number
|
//! the query cycle until a fixed-point is reached. Salsa has a built-in fixed limit on the number
|
||||||
//! of iterations, so if we fail to converge, Salsa will eventually panic. (This should of course
|
//! of iterations, so if we fail to converge, Salsa will eventually panic. (This should of course
|
||||||
//! be considered a bug.)
|
//! be considered a bug.)
|
||||||
|
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use ruff_db::diagnostic::{Annotation, DiagnosticId, Severity};
|
use ruff_db::diagnostic::{Annotation, DiagnosticId, Severity};
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
|
@ -95,11 +96,11 @@ use crate::types::unpacker::{UnpackResult, Unpacker};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BareTypeAliasType, CallDunderError, CallableType, ClassLiteral, ClassType, DataclassParams,
|
BareTypeAliasType, CallDunderError, CallableType, ClassLiteral, ClassType, DataclassParams,
|
||||||
DynamicType, GenericAlias, IntersectionBuilder, IntersectionType, KnownClass,
|
DynamicType, GenericAlias, IntersectionBuilder, IntersectionType, KnownClass,
|
||||||
KnownInstanceType, MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter,
|
KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate,
|
||||||
ParameterForm, Parameters, SpecialFormType, StringLiteralType, SubclassOfType, Truthiness,
|
PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType, StringLiteralType,
|
||||||
TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers,
|
SubclassOfType, Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers,
|
||||||
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder,
|
TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
||||||
UnionType, binding_type, todo_type,
|
TypeVarVariance, UnionBuilder, UnionType, binding_type, todo_type,
|
||||||
};
|
};
|
||||||
use crate::unpack::{Unpack, UnpackPosition};
|
use crate::unpack::{Unpack, UnpackPosition};
|
||||||
use crate::util::subscript::{PyIndex, PySlice};
|
use crate::util::subscript::{PyIndex, PySlice};
|
||||||
|
@ -8308,7 +8309,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.store_expression_type(slice, inner_annotation_ty.inner_type());
|
self.store_expression_type(slice, inner_annotation_ty.inner_type());
|
||||||
inner_annotation_ty
|
inner_annotation_ty
|
||||||
} else {
|
} else {
|
||||||
self.infer_type_expression(slice);
|
for argument in arguments {
|
||||||
|
self.infer_expression(argument);
|
||||||
|
}
|
||||||
|
self.store_expression_type(slice, Type::unknown());
|
||||||
TypeAndQualifiers::unknown()
|
TypeAndQualifiers::unknown()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -8416,15 +8420,15 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_invalid_type_expression(
|
fn report_invalid_type_expression(
|
||||||
&mut self,
|
&self,
|
||||||
expression: &ast::Expr,
|
expression: &ast::Expr,
|
||||||
message: std::fmt::Arguments,
|
message: std::fmt::Arguments,
|
||||||
) -> Type<'db> {
|
) -> Option<LintDiagnosticGuard> {
|
||||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, expression) {
|
self.context
|
||||||
let diag = builder.into_diagnostic(message);
|
.report_lint(&INVALID_TYPE_FORM, expression)
|
||||||
diagnostic::add_type_expression_reference_link(diag);
|
.map(|builder| {
|
||||||
}
|
diagnostic::add_type_expression_reference_link(builder.into_diagnostic(message))
|
||||||
Type::unknown()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Infer the type of a type expression without storing the result.
|
/// Infer the type of a type expression without storing the result.
|
||||||
|
@ -8511,56 +8515,126 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
|
|
||||||
// TODO: add a subdiagnostic linking to type-expression grammar
|
// TODO: add a subdiagnostic linking to type-expression grammar
|
||||||
// and stating that it is only valid in `typing.Literal[]` or `typing.Annotated[]`
|
// and stating that it is only valid in `typing.Literal[]` or `typing.Annotated[]`
|
||||||
ast::Expr::BytesLiteral(_) => self.report_invalid_type_expression(
|
ast::Expr::BytesLiteral(_) => {
|
||||||
expression,
|
self.report_invalid_type_expression(
|
||||||
format_args!("Bytes literals are not allowed in this context in a type expression"),
|
expression,
|
||||||
),
|
format_args!(
|
||||||
|
"Bytes literals are not allowed in this context in a type expression"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add a subdiagnostic linking to type-expression grammar
|
|
||||||
// and stating that it is only valid in `typing.Literal[]` or `typing.Annotated[]`
|
|
||||||
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: ast::Number::Int(_),
|
value: ast::Number::Int(_),
|
||||||
..
|
..
|
||||||
}) => self.report_invalid_type_expression(
|
}) => {
|
||||||
expression,
|
self.report_invalid_type_expression(
|
||||||
format_args!("Int literals are not allowed in this context in a type expression"),
|
expression,
|
||||||
),
|
format_args!(
|
||||||
|
"Int literals are not allowed in this context in a type expression"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: ast::Number::Float(_),
|
value: ast::Number::Float(_),
|
||||||
..
|
..
|
||||||
}) => self.report_invalid_type_expression(
|
}) => {
|
||||||
expression,
|
self.report_invalid_type_expression(
|
||||||
format_args!("Float literals are not allowed in type expressions"),
|
expression,
|
||||||
),
|
format_args!("Float literals are not allowed in type expressions"),
|
||||||
|
);
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: ast::Number::Complex { .. },
|
value: ast::Number::Complex { .. },
|
||||||
..
|
..
|
||||||
}) => self.report_invalid_type_expression(
|
}) => {
|
||||||
expression,
|
|
||||||
format_args!("Complex literals are not allowed in type expressions"),
|
|
||||||
),
|
|
||||||
|
|
||||||
// TODO: add a subdiagnostic linking to type-expression grammar
|
|
||||||
// and stating that it is only valid in `typing.Literal[]` or `typing.Annotated[]`
|
|
||||||
ast::Expr::BooleanLiteral(_) => self.report_invalid_type_expression(
|
|
||||||
expression,
|
|
||||||
format_args!(
|
|
||||||
"Boolean literals are not allowed in this context in a type expression"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// TODO: add a subdiagnostic linking to type-expression grammar
|
|
||||||
// and stating that it is only valid as first argument to `typing.Callable[]`
|
|
||||||
ast::Expr::List(list) => {
|
|
||||||
self.infer_list_expression(list);
|
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
|
expression,
|
||||||
|
format_args!("Complex literals are not allowed in type expressions"),
|
||||||
|
);
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::Expr::BooleanLiteral(_) => {
|
||||||
|
self.report_invalid_type_expression(
|
||||||
|
expression,
|
||||||
|
format_args!(
|
||||||
|
"Boolean literals are not allowed in this context in a type expression"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::Expr::List(list) => {
|
||||||
|
let db = self.db();
|
||||||
|
|
||||||
|
let inner_types: Vec<Type<'db>> = list
|
||||||
|
.iter()
|
||||||
|
.map(|element| self.infer_type_expression(element))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(mut diagnostic) = self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!(
|
format_args!(
|
||||||
"List literals are not allowed in this context in a type expression"
|
"List literals are not allowed in this context in a type expression"
|
||||||
),
|
),
|
||||||
)
|
) {
|
||||||
|
if !inner_types.iter().any(|ty| {
|
||||||
|
matches!(
|
||||||
|
ty,
|
||||||
|
Type::Dynamic(DynamicType::Todo(_) | DynamicType::Unknown)
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
let hinted_type = if list.len() == 1 {
|
||||||
|
KnownClass::List.to_specialized_instance(db, inner_types)
|
||||||
|
} else {
|
||||||
|
TupleType::from_elements(db, inner_types)
|
||||||
|
};
|
||||||
|
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"Did you mean `{}`?",
|
||||||
|
hinted_type.display(self.db()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::Expr::Tuple(tuple) => {
|
||||||
|
let inner_types: Vec<Type<'db>> = tuple
|
||||||
|
.elts
|
||||||
|
.iter()
|
||||||
|
.map(|expr| self.infer_type_expression(expr))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if tuple.parenthesized {
|
||||||
|
if let Some(mut diagnostic) = self.report_invalid_type_expression(
|
||||||
|
expression,
|
||||||
|
format_args!(
|
||||||
|
"Tuple literals are not allowed in this context in a type expression"
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
if !inner_types.iter().any(|ty| {
|
||||||
|
matches!(
|
||||||
|
ty,
|
||||||
|
Type::Dynamic(DynamicType::Todo(_) | DynamicType::Unknown)
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
let hinted_type = TupleType::from_elements(self.db(), inner_types);
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"Did you mean `{}`?",
|
||||||
|
hinted_type.display(self.db()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::BoolOp(bool_op) => {
|
ast::Expr::BoolOp(bool_op) => {
|
||||||
|
@ -8568,7 +8642,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Boolean operations are not allowed in type expressions"),
|
format_args!("Boolean operations are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Named(named) => {
|
ast::Expr::Named(named) => {
|
||||||
|
@ -8576,7 +8651,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Named expressions are not allowed in type expressions"),
|
format_args!("Named expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::UnaryOp(unary) => {
|
ast::Expr::UnaryOp(unary) => {
|
||||||
|
@ -8584,7 +8660,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Unary operations are not allowed in type expressions"),
|
format_args!("Unary operations are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Lambda(lambda_expression) => {
|
ast::Expr::Lambda(lambda_expression) => {
|
||||||
|
@ -8592,7 +8669,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("`lambda` expressions are not allowed in type expressions"),
|
format_args!("`lambda` expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::If(if_expression) => {
|
ast::Expr::If(if_expression) => {
|
||||||
|
@ -8600,7 +8678,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("`if` expressions are not allowed in type expressions"),
|
format_args!("`if` expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Dict(dict) => {
|
ast::Expr::Dict(dict) => {
|
||||||
|
@ -8608,7 +8687,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Dict literals are not allowed in type expressions"),
|
format_args!("Dict literals are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Set(set) => {
|
ast::Expr::Set(set) => {
|
||||||
|
@ -8616,7 +8696,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Set literals are not allowed in type expressions"),
|
format_args!("Set literals are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::DictComp(dictcomp) => {
|
ast::Expr::DictComp(dictcomp) => {
|
||||||
|
@ -8624,7 +8705,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Dict comprehensions are not allowed in type expressions"),
|
format_args!("Dict comprehensions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::ListComp(listcomp) => {
|
ast::Expr::ListComp(listcomp) => {
|
||||||
|
@ -8632,7 +8714,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("List comprehensions are not allowed in type expressions"),
|
format_args!("List comprehensions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::SetComp(setcomp) => {
|
ast::Expr::SetComp(setcomp) => {
|
||||||
|
@ -8640,7 +8723,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Set comprehensions are not allowed in type expressions"),
|
format_args!("Set comprehensions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Generator(generator) => {
|
ast::Expr::Generator(generator) => {
|
||||||
|
@ -8648,7 +8732,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Generator expressions are not allowed in type expressions"),
|
format_args!("Generator expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Await(await_expression) => {
|
ast::Expr::Await(await_expression) => {
|
||||||
|
@ -8656,7 +8741,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("`await` expressions are not allowed in type expressions"),
|
format_args!("`await` expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Yield(yield_expression) => {
|
ast::Expr::Yield(yield_expression) => {
|
||||||
|
@ -8664,7 +8750,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("`yield` expressions are not allowed in type expressions"),
|
format_args!("`yield` expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::YieldFrom(yield_from) => {
|
ast::Expr::YieldFrom(yield_from) => {
|
||||||
|
@ -8672,7 +8759,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("`yield from` expressions are not allowed in type expressions"),
|
format_args!("`yield from` expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Compare(compare) => {
|
ast::Expr::Compare(compare) => {
|
||||||
|
@ -8680,7 +8768,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Comparison expressions are not allowed in type expressions"),
|
format_args!("Comparison expressions are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Call(call_expr) => {
|
ast::Expr::Call(call_expr) => {
|
||||||
|
@ -8688,7 +8777,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Function calls are not allowed in type expressions"),
|
format_args!("Function calls are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::FString(fstring) => {
|
ast::Expr::FString(fstring) => {
|
||||||
|
@ -8696,7 +8786,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("F-strings are not allowed in type expressions"),
|
format_args!("F-strings are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::TString(tstring) => {
|
ast::Expr::TString(tstring) => {
|
||||||
|
@ -8704,7 +8795,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("T-strings are not allowed in type expressions"),
|
format_args!("T-strings are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Slice(slice) => {
|
ast::Expr::Slice(slice) => {
|
||||||
|
@ -8712,7 +8804,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.report_invalid_type_expression(
|
self.report_invalid_type_expression(
|
||||||
expression,
|
expression,
|
||||||
format_args!("Slices are not allowed in type expressions"),
|
format_args!("Slices are not allowed in type expressions"),
|
||||||
)
|
);
|
||||||
|
Type::unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
@ -8724,11 +8817,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
todo_type!("ellipsis literal in type expression")
|
todo_type!("ellipsis literal in type expression")
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Tuple(tuple) => {
|
|
||||||
self.infer_tuple_expression(tuple);
|
|
||||||
Type::unknown()
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::Expr::Starred(starred) => {
|
ast::Expr::Starred(starred) => {
|
||||||
self.infer_starred_expression(starred);
|
self.infer_starred_expression(starred);
|
||||||
todo_type!("PEP 646")
|
todo_type!("PEP 646")
|
||||||
|
@ -9076,7 +9164,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let [type_expr, metadata @ ..] = &arguments[..] else {
|
let [type_expr, metadata @ ..] = &arguments[..] else {
|
||||||
self.infer_type_expression(arguments_slice);
|
for argument in arguments {
|
||||||
|
self.infer_expression(argument);
|
||||||
|
}
|
||||||
|
self.store_expression_type(arguments_slice, Type::unknown());
|
||||||
return Type::unknown();
|
return Type::unknown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue