mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
WIP: working without standalone
This commit is contained in:
parent
dd8b555fa5
commit
bc4930e9a3
9 changed files with 376 additions and 240 deletions
|
@ -1,5 +1,12 @@
|
||||||
# Generic classes: Legacy syntax
|
# Generic classes: Legacy syntax
|
||||||
|
|
||||||
|
We use TypeVar defaults here, which was added in Python 3.13 for legacy typevars.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.13"
|
||||||
|
```
|
||||||
|
|
||||||
## Defining a generic class
|
## Defining a generic class
|
||||||
|
|
||||||
At its simplest, to define a generic class using the legacy syntax, you inherit from the
|
At its simplest, to define a generic class using the legacy syntax, you inherit from the
|
||||||
|
|
|
@ -33,13 +33,12 @@ reveal_type(T.__name__) # revealed: Literal["T"]
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
# TODO: no error
|
|
||||||
# error: [invalid-legacy-type-variable]
|
# error: [invalid-legacy-type-variable]
|
||||||
U: TypeVar = TypeVar("U")
|
U: TypeVar = TypeVar("U")
|
||||||
|
|
||||||
# error: [invalid-legacy-type-variable] "A legacy `typing.TypeVar` must be immediately assigned to a variable"
|
# error: [invalid-legacy-type-variable]
|
||||||
# error: [invalid-type-form] "Function calls are not allowed in type expressions"
|
tuple_with_typevar = ("foo", TypeVar("W"))
|
||||||
TestList = list[TypeVar("W")]
|
reveal_type(tuple_with_typevar[1]) # revealed: TypeVar
|
||||||
```
|
```
|
||||||
|
|
||||||
### `TypeVar` parameter must match variable name
|
### `TypeVar` parameter must match variable name
|
||||||
|
@ -49,7 +48,7 @@ TestList = list[TypeVar("W")]
|
||||||
```py
|
```py
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
# error: [invalid-legacy-type-variable] "The name of a legacy `typing.TypeVar` (`Q`) must match the name of the variable it is assigned to (`T`)"
|
# error: [invalid-legacy-type-variable]
|
||||||
T = TypeVar("Q")
|
T = TypeVar("Q")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -66,6 +65,22 @@ T = TypeVar("T")
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### No variadic arguments
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
types = (int, str)
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
T = TypeVar("T", *types)
|
||||||
|
reveal_type(T) # revealed: TypeVar
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
S = TypeVar("S", **{"bound": int})
|
||||||
|
reveal_type(S) # revealed: TypeVar
|
||||||
|
```
|
||||||
|
|
||||||
### Type variables with a default
|
### Type variables with a default
|
||||||
|
|
||||||
Note that the `__default__` property is only available in Python ≥3.13.
|
Note that the `__default__` property is only available in Python ≥3.13.
|
||||||
|
@ -91,6 +106,11 @@ reveal_type(S.__default__) # revealed: NoDefault
|
||||||
|
|
||||||
### Using other typevars as a default
|
### Using other typevars as a default
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.13"
|
||||||
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Generic, TypeVar, Union
|
from typing import Generic, TypeVar, Union
|
||||||
|
|
||||||
|
@ -122,6 +142,20 @@ reveal_type(T.__constraints__) # revealed: tuple[()]
|
||||||
|
|
||||||
S = TypeVar("S")
|
S = TypeVar("S")
|
||||||
reveal_type(S.__bound__) # revealed: None
|
reveal_type(S.__bound__) # revealed: None
|
||||||
|
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
# error: [invalid-type-form]
|
||||||
|
T = TypeVar("T", bound=TypedDict)
|
||||||
|
```
|
||||||
|
|
||||||
|
The upper bound must be a valid type expression:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
# error: [invalid-type-form]
|
||||||
|
T = TypeVar("T", bound=TypedDict)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Type variables with constraints
|
### Type variables with constraints
|
||||||
|
@ -144,8 +178,8 @@ Constraints are not simplified relative to each other, even if one is a subtype
|
||||||
T = TypeVar("T", int, bool)
|
T = TypeVar("T", int, bool)
|
||||||
reveal_type(T.__constraints__) # revealed: tuple[int, bool]
|
reveal_type(T.__constraints__) # revealed: tuple[int, bool]
|
||||||
|
|
||||||
S = TypeVar("S", float)
|
S = TypeVar("S", float, str)
|
||||||
reveal_type(S.__constraints__) # revealed: tuple[int | float]
|
reveal_type(S.__constraints__) # revealed: tuple[int | float, str]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cannot have only one constraint
|
### Cannot have only one constraint
|
||||||
|
@ -156,10 +190,19 @@ reveal_type(S.__constraints__) # revealed: tuple[int | float]
|
||||||
```py
|
```py
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
# TODO: error: [invalid-type-variable-constraints]
|
# error: [invalid-legacy-type-variable]
|
||||||
T = TypeVar("T", int)
|
T = TypeVar("T", int)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Cannot have both bound and constraint
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
T = TypeVar("T", int, str, bound=bytes)
|
||||||
|
```
|
||||||
|
|
||||||
### Cannot be both covariant and contravariant
|
### Cannot be both covariant and contravariant
|
||||||
|
|
||||||
> To facilitate the declaration of container types where covariant or contravariant type checking is
|
> To facilitate the declaration of container types where covariant or contravariant type checking is
|
||||||
|
@ -188,6 +231,46 @@ T = TypeVar("T", covariant=cond())
|
||||||
U = TypeVar("U", contravariant=cond())
|
U = TypeVar("U", contravariant=cond())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Invalid keyword arguments
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
T = TypeVar("T", invalid_keyword=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
```pyi
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
T = TypeVar("T", invalid_keyword=True)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Constructor signature versioning
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.10"
|
||||||
|
```
|
||||||
|
|
||||||
|
In a stub file, features from the latest supported Python version can be used on any version:
|
||||||
|
|
||||||
|
```pyi
|
||||||
|
from typing import TypeVar
|
||||||
|
T = TypeVar("T", default=int)
|
||||||
|
```
|
||||||
|
|
||||||
|
But this raises an error in a non-stub file:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
# error: [invalid-legacy-type-variable]
|
||||||
|
T = TypeVar("T", default=int)
|
||||||
|
```
|
||||||
|
|
||||||
## Callability
|
## Callability
|
||||||
|
|
||||||
A typevar bound to a Callable type is callable:
|
A typevar bound to a Callable type is callable:
|
||||||
|
@ -256,13 +339,10 @@ S = TypeVar("S")
|
||||||
T = TypeVar("T", bound=list[S])
|
T = TypeVar("T", bound=list[S])
|
||||||
|
|
||||||
# TODO: error
|
# TODO: error
|
||||||
U = TypeVar("U", list["T"])
|
U = TypeVar("U", list["T"], str)
|
||||||
|
|
||||||
# TODO: error
|
# TODO: error
|
||||||
V = TypeVar("V", list[S], str)
|
V = TypeVar("V", list["V"], str)
|
||||||
|
|
||||||
# TODO: error
|
|
||||||
W = TypeVar("W", list["W"], str)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
However, they are lazily evaluated and can cyclically refer to their own type:
|
However, they are lazily evaluated and can cyclically refer to their own type:
|
||||||
|
@ -280,6 +360,11 @@ reveal_type(G[list[G]]().x) # revealed: list[G[Unknown]]
|
||||||
|
|
||||||
### Defaults
|
### Defaults
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.13"
|
||||||
|
```
|
||||||
|
|
||||||
Defaults can be generic, but can only refer to earlier typevars:
|
Defaults can be generic, but can only refer to earlier typevars:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
|
@ -343,7 +343,7 @@ def _[T]() -> None:
|
||||||
reveal_type(negated_range_constraint(Sub, T, Base) & negated_range_constraint(Base, T, Super))
|
reveal_type(negated_range_constraint(Sub, T, Base) & negated_range_constraint(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Sub))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Base ≤ T@_ ≤ Super) ∧ ¬(SubSub ≤ T@_ ≤ Sub))]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Base, T, Super))
|
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(¬(SubSub ≤ T@_ ≤ Sub) ∧ ¬(Unrelated ≤ T@_))]
|
# revealed: ty_extensions.ConstraintSet[(¬(Unrelated ≤ T@_) ∧ ¬(SubSub ≤ T@_ ≤ Sub))]
|
||||||
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Unrelated, T, object))
|
reveal_type(negated_range_constraint(SubSub, T, Sub) & negated_range_constraint(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -421,7 +421,7 @@ def _[T]() -> None:
|
||||||
reveal_type(range_constraint(Sub, T, Base) | range_constraint(Base, T, Super))
|
reveal_type(range_constraint(Sub, T, Base) | range_constraint(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Sub)]
|
# revealed: ty_extensions.ConstraintSet[(Base ≤ T@_ ≤ Super) ∨ (SubSub ≤ T@_ ≤ Sub)]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Base, T, Super))
|
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Base, T, Super))
|
||||||
# revealed: ty_extensions.ConstraintSet[(SubSub ≤ T@_ ≤ Sub) ∨ (Unrelated ≤ T@_)]
|
# revealed: ty_extensions.ConstraintSet[(Unrelated ≤ T@_) ∨ (SubSub ≤ T@_ ≤ Sub)]
|
||||||
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Unrelated, T, object))
|
reveal_type(range_constraint(SubSub, T, Sub) | range_constraint(Unrelated, T, object))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1626,9 +1626,10 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||||
self.visit_expr(&node.value);
|
self.visit_expr(&node.value);
|
||||||
|
|
||||||
// Optimization for the common case: if there's just one target, and it's not an
|
// Optimization for the common case: if there's just one target, and it's not an
|
||||||
// unpacking, we don't need the RHS to be a standalone expression at all.
|
// unpacking, and the target is a simple name, we don't need the RHS to be a
|
||||||
|
// standalone expression at all.
|
||||||
if let [target] = &node.targets[..]
|
if let [target] = &node.targets[..]
|
||||||
&& !matches!(target, ast::Expr::List(_) | ast::Expr::Tuple(_))
|
&& target.is_name_expr()
|
||||||
{
|
{
|
||||||
self.push_assignment(CurrentAssignment::Assign { node, unpack: None });
|
self.push_assignment(CurrentAssignment::Assign { node, unpack: None });
|
||||||
self.visit_expr(target);
|
self.visit_expr(target);
|
||||||
|
|
|
@ -4493,56 +4493,6 @@ impl<'db> Type<'db> {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(KnownClass::TypeVar) => {
|
|
||||||
// ```py
|
|
||||||
// class TypeVar:
|
|
||||||
// def __new__(
|
|
||||||
// cls,
|
|
||||||
// name: str,
|
|
||||||
// *constraints: Any,
|
|
||||||
// bound: Any | None = None,
|
|
||||||
// contravariant: bool = False,
|
|
||||||
// covariant: bool = False,
|
|
||||||
// infer_variance: bool = False,
|
|
||||||
// default: Any = ...,
|
|
||||||
// ) -> Self: ...
|
|
||||||
// ```
|
|
||||||
Binding::single(
|
|
||||||
self,
|
|
||||||
Signature::new(
|
|
||||||
Parameters::new([
|
|
||||||
Parameter::positional_or_keyword(Name::new_static("name"))
|
|
||||||
.with_annotated_type(Type::LiteralString),
|
|
||||||
Parameter::variadic(Name::new_static("constraints"))
|
|
||||||
.deferred_type_form()
|
|
||||||
.with_annotated_type(Type::any()),
|
|
||||||
Parameter::keyword_only(Name::new_static("bound"))
|
|
||||||
.deferred_type_form()
|
|
||||||
.with_annotated_type(UnionType::from_elements(
|
|
||||||
db,
|
|
||||||
[Type::any(), Type::none(db)],
|
|
||||||
))
|
|
||||||
.with_default_type(Type::none(db)),
|
|
||||||
Parameter::keyword_only(Name::new_static("default"))
|
|
||||||
.deferred_type_form()
|
|
||||||
.with_annotated_type(Type::any())
|
|
||||||
.with_default_type(KnownClass::NoneType.to_instance(db)),
|
|
||||||
Parameter::keyword_only(Name::new_static("contravariant"))
|
|
||||||
.with_annotated_type(KnownClass::Bool.to_instance(db))
|
|
||||||
.with_default_type(Type::BooleanLiteral(false)),
|
|
||||||
Parameter::keyword_only(Name::new_static("covariant"))
|
|
||||||
.with_annotated_type(KnownClass::Bool.to_instance(db))
|
|
||||||
.with_default_type(Type::BooleanLiteral(false)),
|
|
||||||
Parameter::keyword_only(Name::new_static("infer_variance"))
|
|
||||||
.with_annotated_type(KnownClass::Bool.to_instance(db))
|
|
||||||
.with_default_type(Type::BooleanLiteral(false)),
|
|
||||||
]),
|
|
||||||
Some(KnownClass::TypeVar.to_instance(db)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(KnownClass::Deprecated) => {
|
Some(KnownClass::Deprecated) => {
|
||||||
// ```py
|
// ```py
|
||||||
// class deprecated:
|
// class deprecated:
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::semantic_index::{
|
||||||
};
|
};
|
||||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||||
use crate::types::context::InferContext;
|
use crate::types::context::InferContext;
|
||||||
use crate::types::diagnostic::{INVALID_LEGACY_TYPE_VARIABLE, INVALID_TYPE_ALIAS_TYPE};
|
use crate::types::diagnostic::INVALID_TYPE_ALIAS_TYPE;
|
||||||
use crate::types::enums::enum_metadata;
|
use crate::types::enums::enum_metadata;
|
||||||
use crate::types::function::{DataclassTransformerParams, KnownFunction};
|
use crate::types::function::{DataclassTransformerParams, KnownFunction};
|
||||||
use crate::types::generics::{GenericContext, Specialization, walk_specialization};
|
use crate::types::generics::{GenericContext, Specialization, walk_specialization};
|
||||||
|
@ -29,9 +29,8 @@ use crate::types::{
|
||||||
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind,
|
IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType, MaterializationKind,
|
||||||
NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, TypeContext,
|
NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeAliasType, TypeContext,
|
||||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation,
|
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, declaration_type,
|
||||||
TypeVarInstance, TypeVarKind, TypedDictParams, UnionBuilder, VarianceInferable,
|
determine_upper_bound, infer_definition_types,
|
||||||
declaration_type, determine_upper_bound, infer_definition_types,
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxIndexMap, FxOrderSet, Program,
|
Db, FxIndexMap, FxOrderSet, Program,
|
||||||
|
@ -4980,6 +4979,7 @@ impl KnownClass {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KnownClass::Deprecated => {
|
KnownClass::Deprecated => {
|
||||||
// Parsing something of the form:
|
// Parsing something of the form:
|
||||||
//
|
//
|
||||||
|
@ -5006,136 +5006,6 @@ impl KnownClass {
|
||||||
DeprecatedInstance::new(db, message.into_string_literal()),
|
DeprecatedInstance::new(db, message.into_string_literal()),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
KnownClass::TypeVar => {
|
|
||||||
let assigned_to = index
|
|
||||||
.try_expression(ast::ExprRef::from(call_expression))
|
|
||||||
.and_then(|expr| expr.assigned_to(db));
|
|
||||||
|
|
||||||
let Some(target) = assigned_to.as_ref().and_then(|assigned_to| {
|
|
||||||
match assigned_to.node(module).targets.as_slice() {
|
|
||||||
[ast::Expr::Name(target)] => Some(target),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}) else {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(
|
|
||||||
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let [
|
|
||||||
Some(name_param),
|
|
||||||
constraints,
|
|
||||||
bound,
|
|
||||||
default,
|
|
||||||
contravariant,
|
|
||||||
covariant,
|
|
||||||
_infer_variance,
|
|
||||||
] = overload.parameter_types()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let covariant = covariant
|
|
||||||
.map(|ty| ty.bool(db))
|
|
||||||
.unwrap_or(Truthiness::AlwaysFalse);
|
|
||||||
|
|
||||||
let contravariant = contravariant
|
|
||||||
.map(|ty| ty.bool(db))
|
|
||||||
.unwrap_or(Truthiness::AlwaysFalse);
|
|
||||||
|
|
||||||
let variance = match (contravariant, covariant) {
|
|
||||||
(Truthiness::Ambiguous, _) => {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(
|
|
||||||
"The `contravariant` parameter of a legacy `typing.TypeVar` \
|
|
||||||
cannot have an ambiguous value",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(_, Truthiness::Ambiguous) => {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(
|
|
||||||
"The `covariant` parameter of a legacy `typing.TypeVar` \
|
|
||||||
cannot have an ambiguous value",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysTrue) => {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(
|
|
||||||
"A legacy `typing.TypeVar` cannot be both \
|
|
||||||
covariant and contravariant",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysFalse) => {
|
|
||||||
TypeVarVariance::Contravariant
|
|
||||||
}
|
|
||||||
(Truthiness::AlwaysFalse, Truthiness::AlwaysTrue) => TypeVarVariance::Covariant,
|
|
||||||
(Truthiness::AlwaysFalse, Truthiness::AlwaysFalse) => {
|
|
||||||
TypeVarVariance::Invariant
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let name_param = name_param.into_string_literal().map(|name| name.value(db));
|
|
||||||
|
|
||||||
if name_param.is_none_or(|name_param| name_param != target.id) {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(format_args!(
|
|
||||||
"The name of a legacy `typing.TypeVar`{} must match \
|
|
||||||
the name of the variable it is assigned to (`{}`)",
|
|
||||||
if let Some(name_param) = name_param {
|
|
||||||
format!(" (`{name_param}`)")
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
},
|
|
||||||
target.id,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bound_or_constraint = match (bound, constraints) {
|
|
||||||
(Some(_), None) => Some(TypeVarBoundOrConstraintsEvaluation::LazyUpperBound),
|
|
||||||
|
|
||||||
(None, Some(_)) => Some(TypeVarBoundOrConstraintsEvaluation::LazyConstraints),
|
|
||||||
|
|
||||||
// TODO: Emit a diagnostic that TypeVar cannot be both bounded and
|
|
||||||
// constrained
|
|
||||||
(Some(_), Some(_)) => return,
|
|
||||||
|
|
||||||
(None, None) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let containing_assignment = index.expect_single_definition(target);
|
|
||||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeVar(
|
|
||||||
TypeVarInstance::new(
|
|
||||||
db,
|
|
||||||
&target.id,
|
|
||||||
Some(containing_assignment),
|
|
||||||
bound_or_constraint,
|
|
||||||
Some(variance),
|
|
||||||
default.map(|_| TypeVarDefaultEvaluation::Lazy),
|
|
||||||
TypeVarKind::Legacy,
|
|
||||||
),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
KnownClass::TypeAliasType => {
|
KnownClass::TypeAliasType => {
|
||||||
let assigned_to = index
|
let assigned_to = index
|
||||||
|
|
|
@ -53,11 +53,12 @@ use crate::types::diagnostic::{
|
||||||
CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION,
|
CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION,
|
||||||
DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE,
|
DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE,
|
||||||
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
||||||
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_NAMED_TUPLE, INVALID_PARAMETER_DEFAULT,
|
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE, INVALID_NAMED_TUPLE,
|
||||||
INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS,
|
INVALID_PARAMETER_DEFAULT, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL,
|
||||||
IncompatibleBases, NON_SUBSCRIPTABLE, POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT,
|
INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NON_SUBSCRIPTABLE,
|
||||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, UNDEFINED_REVEAL,
|
||||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_bad_dunder_set_call,
|
UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE,
|
||||||
|
UNSUPPORTED_OPERATOR, report_bad_dunder_set_call,
|
||||||
report_cannot_pop_required_field_on_typed_dict, report_implicit_return_type,
|
report_cannot_pop_required_field_on_typed_dict, report_implicit_return_type,
|
||||||
report_instance_layout_conflict, report_invalid_assignment,
|
report_instance_layout_conflict, report_invalid_assignment,
|
||||||
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
||||||
|
@ -95,7 +96,7 @@ use crate::types::{
|
||||||
Parameters, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
|
Parameters, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
|
||||||
TypeAliasType, TypeAndQualifiers, TypeContext, TypeMapping, TypeQualifiers,
|
TypeAliasType, TypeAndQualifiers, TypeContext, TypeMapping, TypeQualifiers,
|
||||||
TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarInstance, TypeVarKind,
|
TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarInstance, TypeVarKind,
|
||||||
UnionBuilder, UnionType, binding_type, todo_type,
|
TypeVarVariance, UnionBuilder, UnionType, binding_type, todo_type,
|
||||||
};
|
};
|
||||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||||
use crate::unpack::{EvaluationMode, UnpackPosition};
|
use crate::unpack::{EvaluationMode, UnpackPosition};
|
||||||
|
@ -375,15 +376,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
self.scope
|
self.scope
|
||||||
}
|
}
|
||||||
|
|
||||||
fn definition(&self) -> Option<Definition<'db>> {
|
|
||||||
match self.region {
|
|
||||||
InferenceRegion::Definition(definition) | InferenceRegion::Deferred(definition) => {
|
|
||||||
Some(definition)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Are we currently inferring types in file with deferred types?
|
/// Are we currently inferring types in file with deferred types?
|
||||||
/// This is true for stub files and files with `__future__.annotations`
|
/// This is true for stub files and files with `__future__.annotations`
|
||||||
fn defer_annotations(&self) -> bool {
|
fn defer_annotations(&self) -> bool {
|
||||||
|
@ -3967,8 +3959,33 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
unpacked.expression_type(target)
|
unpacked.expression_type(target)
|
||||||
}
|
}
|
||||||
TargetKind::Single => {
|
TargetKind::Single => {
|
||||||
let value_ty =
|
let tcx = TypeContext::default();
|
||||||
self.infer_maybe_standalone_expression(value, TypeContext::default());
|
let value_ty = if let Some(standalone_expression) = self.index.try_expression(value)
|
||||||
|
{
|
||||||
|
self.infer_standalone_expression_impl(value, standalone_expression, tcx)
|
||||||
|
} else {
|
||||||
|
// If the RHS is not a standalone expression, this is a simple assignment
|
||||||
|
// (single target, no unpackings). That means it's a valid syntactic form
|
||||||
|
// for a legacy TypeVar creation; check for that.
|
||||||
|
if let Some(call_expr) = value.as_call_expr() {
|
||||||
|
let callable_type = self.infer_maybe_standalone_expression(
|
||||||
|
call_expr.func.as_ref(),
|
||||||
|
TypeContext::default(),
|
||||||
|
);
|
||||||
|
let ty = if callable_type
|
||||||
|
.into_class_literal()
|
||||||
|
.is_some_and(|cls| cls.is_known(self.db(), KnownClass::TypeVar))
|
||||||
|
{
|
||||||
|
self.infer_legacy_typevar(target, call_expr, definition)
|
||||||
|
} else {
|
||||||
|
self.infer_call_expression_impl(call_expr, callable_type, tcx)
|
||||||
|
};
|
||||||
|
self.store_expression_type(value, ty);
|
||||||
|
ty
|
||||||
|
} else {
|
||||||
|
self.infer_expression(value, tcx)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
||||||
// at runtime, but is always considered `True` in type checking.
|
// at runtime, but is always considered `True` in type checking.
|
||||||
|
@ -3999,8 +4016,199 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
self.add_binding(target.into(), definition, target_ty);
|
self.add_binding(target.into(), definition, target_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_legacy_typevar(
|
||||||
|
&mut self,
|
||||||
|
target: &ast::Expr,
|
||||||
|
call_expr: &ast::ExprCall,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) -> Type<'db> {
|
||||||
|
fn error<'db>(
|
||||||
|
context: &InferContext<'db, '_>,
|
||||||
|
message: impl std::fmt::Display,
|
||||||
|
node: impl Ranged,
|
||||||
|
) -> Type<'db> {
|
||||||
|
if let Some(builder) = context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, node) {
|
||||||
|
builder.into_diagnostic(message);
|
||||||
|
}
|
||||||
|
// If the call doesn't create a valid typevar, we'll emit diagnostics and fall back to
|
||||||
|
// just creating a regular instance of `typing.TypeVar`.
|
||||||
|
KnownClass::TypeVar.to_instance(context.db())
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = self.db();
|
||||||
|
let arguments = &call_expr.arguments;
|
||||||
|
|
||||||
|
let mut has_bound = false;
|
||||||
|
let mut default = None;
|
||||||
|
let mut covariant = false;
|
||||||
|
let mut contravariant = false;
|
||||||
|
|
||||||
|
for kwarg in &arguments.keywords {
|
||||||
|
let Some(identifier) = kwarg.arg.as_ref() else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"Starred arguments are not supported in legacy `typing.TypeVar` creation",
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
match identifier.id().as_str() {
|
||||||
|
"bound" => has_bound = true,
|
||||||
|
"covariant" => {
|
||||||
|
match self
|
||||||
|
.infer_expression(&kwarg.value, TypeContext::default())
|
||||||
|
.bool(db)
|
||||||
|
{
|
||||||
|
Truthiness::AlwaysTrue => covariant = true,
|
||||||
|
Truthiness::AlwaysFalse => {}
|
||||||
|
Truthiness::Ambiguous => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"The `covariant` parameter of a legacy `typing.TypeVar` \
|
||||||
|
cannot have an ambiguous value",
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"contravariant" => {
|
||||||
|
match self
|
||||||
|
.infer_expression(&kwarg.value, TypeContext::default())
|
||||||
|
.bool(db)
|
||||||
|
{
|
||||||
|
Truthiness::AlwaysTrue => contravariant = true,
|
||||||
|
Truthiness::AlwaysFalse => {}
|
||||||
|
Truthiness::Ambiguous => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"The `contravariant` parameter of a legacy `typing.TypeVar` \
|
||||||
|
cannot have an ambiguous value",
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"default" => {
|
||||||
|
if !self.in_stub() && Program::get(db).python_version(db) < PythonVersion::PY313
|
||||||
|
{
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"TypeVar `default` keyword was added in Python 3.13",
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
default = Some(TypeVarDefaultEvaluation::Lazy);
|
||||||
|
}
|
||||||
|
"infer_variance" => {
|
||||||
|
if !self.in_stub() && Program::get(db).python_version(db) < PythonVersion::PY312
|
||||||
|
{
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"TypeVar `infer_variance` keyword was added in Python 3.12",
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO support `infer_variance` in legacy TypeVars
|
||||||
|
}
|
||||||
|
name => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
format_args!(
|
||||||
|
"Unknown keyword argument `{name}` in legacy `typing.TypeVar` creation",
|
||||||
|
),
|
||||||
|
kwarg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let variance = match (covariant, contravariant) {
|
||||||
|
(true, true) => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"A legacy `typing.TypeVar` cannot be both covariant and contravariant",
|
||||||
|
call_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(true, false) => TypeVarVariance::Covariant,
|
||||||
|
(false, true) => TypeVarVariance::Contravariant,
|
||||||
|
(false, false) => TypeVarVariance::Invariant,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(name_param) = arguments
|
||||||
|
.find_positional(0)
|
||||||
|
.map(|arg| self.infer_expression(arg, TypeContext::default()))
|
||||||
|
.and_then(Type::into_string_literal)
|
||||||
|
.map(|name| name.value(db))
|
||||||
|
else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"The first argument to a legacy `typing.TypeVar` must be a string literal.",
|
||||||
|
call_expr,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(target_name) = target.as_name_expr().map(|n| &n.id) else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"A legacy `typing.TypeVar` must be assigned to a variable",
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if name_param != target_name {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
format_args!(
|
||||||
|
"The name of a legacy `typing.TypeVar` (`{name_param}`) must match \
|
||||||
|
the name of the variable it is assigned to (`{target_name}`)"
|
||||||
|
),
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inference of bounds, constraints, and defaults must be deferred, to avoid cycles. So we
|
||||||
|
// only check presence/absence/number here.
|
||||||
|
|
||||||
|
let num_constraints = arguments.args.len() - 1;
|
||||||
|
|
||||||
|
let bound_or_constraints = match (has_bound, num_constraints) {
|
||||||
|
(false, 0) => None,
|
||||||
|
(true, 0) => Some(TypeVarBoundOrConstraintsEvaluation::LazyUpperBound),
|
||||||
|
(true, _) => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"A legacy `typing.TypeVar` cannot have both a bound and constraints",
|
||||||
|
call_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(_, 1) => {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"A legacy `typing.TypeVar` cannot have exactly one constraint",
|
||||||
|
call_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(false, _) => Some(TypeVarBoundOrConstraintsEvaluation::LazyConstraints),
|
||||||
|
};
|
||||||
|
|
||||||
|
if bound_or_constraints.is_some() || default.is_some() {
|
||||||
|
self.deferred.insert(definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
|
||||||
|
db,
|
||||||
|
target_name,
|
||||||
|
Some(definition),
|
||||||
|
bound_or_constraints,
|
||||||
|
Some(variance),
|
||||||
|
default,
|
||||||
|
TypeVarKind::Legacy,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_assignment_deferred(&mut self, value: &ast::Expr) {
|
fn infer_assignment_deferred(&mut self, value: &ast::Expr) {
|
||||||
// infer deferred bounds/constraints/defaults of a legacy TypeVar
|
// Infer deferred bounds/constraints/defaults of a legacy TypeVar.
|
||||||
let Some(call_expr) = value.as_call_expr() else {
|
let Some(call_expr) = value.as_call_expr() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -4987,12 +5195,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
match form {
|
match form {
|
||||||
None | Some(ParameterForm::Value) => self.infer_expression(ast_argument, tcx),
|
None | Some(ParameterForm::Value) => self.infer_expression(ast_argument, tcx),
|
||||||
Some(ParameterForm::Type) => self.infer_type_expression(ast_argument),
|
Some(ParameterForm::Type) => self.infer_type_expression(ast_argument),
|
||||||
Some(ParameterForm::TypeDeferred) => {
|
|
||||||
if let Some(definition) = self.definition() {
|
|
||||||
self.deferred.insert(definition);
|
|
||||||
}
|
|
||||||
Type::unknown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5861,6 +6063,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
&mut self,
|
&mut self,
|
||||||
call_expression: &ast::ExprCall,
|
call_expression: &ast::ExprCall,
|
||||||
tcx: TypeContext<'db>,
|
tcx: TypeContext<'db>,
|
||||||
|
) -> Type<'db> {
|
||||||
|
// TODO: Use the type context for more precise inference.
|
||||||
|
let callable_type =
|
||||||
|
self.infer_maybe_standalone_expression(&call_expression.func, TypeContext::default());
|
||||||
|
|
||||||
|
self.infer_call_expression_impl(call_expression, callable_type, tcx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_call_expression_impl(
|
||||||
|
&mut self,
|
||||||
|
call_expression: &ast::ExprCall,
|
||||||
|
callable_type: Type<'db>,
|
||||||
|
tcx: TypeContext<'db>,
|
||||||
) -> Type<'db> {
|
) -> Type<'db> {
|
||||||
let ast::ExprCall {
|
let ast::ExprCall {
|
||||||
range: _,
|
range: _,
|
||||||
|
@ -5881,9 +6096,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
ty
|
ty
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Use the type context for more precise inference.
|
|
||||||
let callable_type = self.infer_maybe_standalone_expression(func, TypeContext::default());
|
|
||||||
|
|
||||||
// Special handling for `TypedDict` method calls
|
// Special handling for `TypedDict` method calls
|
||||||
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
||||||
let value_type = self.expression_type(value);
|
let value_type = self.expression_type(value);
|
||||||
|
@ -5987,7 +6199,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
| KnownClass::Object
|
| KnownClass::Object
|
||||||
| KnownClass::Property
|
| KnownClass::Property
|
||||||
| KnownClass::Super
|
| KnownClass::Super
|
||||||
| KnownClass::TypeVar
|
|
||||||
| KnownClass::TypeAliasType
|
| KnownClass::TypeAliasType
|
||||||
| KnownClass::Deprecated
|
| KnownClass::Deprecated
|
||||||
)
|
)
|
||||||
|
@ -6010,6 +6221,21 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||||
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
||||||
|
|
||||||
|
if class.is_known(self.db(), KnownClass::TypeVar) {
|
||||||
|
// Inference of correctly-placed `TypeVar` definitions is done in
|
||||||
|
// `TypeInferenceBuilder::infer_legacy_typevar`, and doesn't use the full
|
||||||
|
// call-binding machinery. If we reach here, it means that someone is trying to
|
||||||
|
// instantiate a `typing.TypeVar` in an invalid context.
|
||||||
|
if let Some(builder) = self
|
||||||
|
.context
|
||||||
|
.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||||
|
{
|
||||||
|
builder.into_diagnostic(
|
||||||
|
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return callable_type
|
return callable_type
|
||||||
.try_call_constructor(self.db(), call_arguments)
|
.try_call_constructor(self.db(), call_arguments)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
|
|
|
@ -418,7 +418,8 @@ fn dependency_implicit_instance_attribute() -> anyhow::Result<()> {
|
||||||
"/src/main.py",
|
"/src/main.py",
|
||||||
r#"
|
r#"
|
||||||
from mod import C
|
from mod import C
|
||||||
x = C().attr
|
# multiple targets ensures RHS is a standalone expression, relied on by this test
|
||||||
|
x = y = C().attr
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -508,7 +509,8 @@ fn dependency_own_instance_member() -> anyhow::Result<()> {
|
||||||
"/src/main.py",
|
"/src/main.py",
|
||||||
r#"
|
r#"
|
||||||
from mod import C
|
from mod import C
|
||||||
x = C().attr
|
# multiple targets ensures RHS is a standalone expression, relied on by this test
|
||||||
|
x = y = C().attr
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -603,7 +605,8 @@ fn dependency_implicit_class_member() -> anyhow::Result<()> {
|
||||||
r#"
|
r#"
|
||||||
from mod import C
|
from mod import C
|
||||||
C.method()
|
C.method()
|
||||||
x = C().class_attr
|
# multiple targets ensures RHS is a standalone expression, relied on by this test
|
||||||
|
x = y = C().class_attr
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -688,7 +691,7 @@ fn call_type_doesnt_rerun_when_only_callee_changed() -> anyhow::Result<()> {
|
||||||
r#"
|
r#"
|
||||||
from foo import foo
|
from foo import foo
|
||||||
|
|
||||||
a = foo()
|
a = b = foo()
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -1032,7 +1032,7 @@ impl<'db> VarianceInferable<'db> for &Signature<'db> {
|
||||||
self.parameters
|
self.parameters
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|parameter| match parameter.form {
|
.filter_map(|parameter| match parameter.form {
|
||||||
ParameterForm::Type | ParameterForm::TypeDeferred => None,
|
ParameterForm::Type => None,
|
||||||
ParameterForm::Value => parameter.annotated_type().map(|ty| {
|
ParameterForm::Value => parameter.annotated_type().map(|ty| {
|
||||||
ty.with_polarity(TypeVarVariance::Contravariant)
|
ty.with_polarity(TypeVarVariance::Contravariant)
|
||||||
.variance_of(db, typevar)
|
.variance_of(db, typevar)
|
||||||
|
@ -1472,11 +1472,6 @@ impl<'db> Parameter<'db> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deferred_type_form(mut self) -> Self {
|
|
||||||
self.form = ParameterForm::TypeDeferred;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_type_mapping_impl<'a>(
|
fn apply_type_mapping_impl<'a>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
@ -1728,7 +1723,6 @@ impl<'db> ParameterKind<'db> {
|
||||||
pub(crate) enum ParameterForm {
|
pub(crate) enum ParameterForm {
|
||||||
Value,
|
Value,
|
||||||
Type,
|
Type,
|
||||||
TypeDeferred,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue