Use assignment definition as typevar binding context
This commit is contained in:
@@ -90,6 +90,12 @@ impl<'db> Definition<'db> {
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
DefinitionKind::Assignment(assignment) => {
|
||||
let target_node = assignment.target.node(&module);
|
||||
target_node
|
||||
.as_name_expr()
|
||||
.map(|name_expr| name_expr.id.as_str().to_string())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4739,6 +4739,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
unpacked.expression_type(target)
|
||||
}
|
||||
TargetKind::Single => {
|
||||
// This could be an implicit type alias (OptionalList = list[T] | None). Use the definition
|
||||
// of `OptionalList` as the typevar binding context while inferring the RHS (`list[T] | None`),
|
||||
// in order to bind `T@OptionalList`.
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(definition);
|
||||
|
||||
let value_ty = if let Some(standalone_expression) = self.index.try_expression(value)
|
||||
{
|
||||
self.infer_standalone_expression_impl(value, standalone_expression, tcx)
|
||||
@@ -4777,6 +4783,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
self.infer_expression(value, tcx)
|
||||
};
|
||||
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
|
||||
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
||||
// at runtime, but is always considered `True` in type checking.
|
||||
// See mdtest/known_constants.md#user-defined-type_checking for details.
|
||||
|
||||
@@ -2,7 +2,6 @@ use itertools::Either;
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
use super::{DeferredExpressionState, TypeInferenceBuilder};
|
||||
use crate::FxOrderSet;
|
||||
use crate::types::diagnostic::{
|
||||
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
|
||||
report_invalid_arguments_to_callable,
|
||||
@@ -16,6 +15,7 @@ use crate::types::{
|
||||
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
|
||||
Type, TypeAliasType, TypeContext, TypeIsType, TypeMapping, UnionBuilder, UnionType, todo_type,
|
||||
};
|
||||
use crate::{FxOrderSet, ResolvedDefinition, definitions_for_name};
|
||||
|
||||
/// Type expressions
|
||||
impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
@@ -759,9 +759,32 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
) -> Type<'db> {
|
||||
let db = self.db();
|
||||
|
||||
let Some(value) = subscript.value.as_name_expr() else {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic("Cannot specialize a non-name node in a type expression");
|
||||
}
|
||||
return Type::unknown();
|
||||
};
|
||||
|
||||
// TODO: This is an expensive call to an API that was never meant to be called from
|
||||
// type inference. We plan to rework how `in_type_expression` works in the future.
|
||||
// This new approach will make this call unnecessary, so for now, we accept the hit
|
||||
// in performance.
|
||||
let definitions = definitions_for_name(self.db(), self.file(), value);
|
||||
let Some(type_alias_definition) =
|
||||
definitions.iter().find_map(ResolvedDefinition::definition)
|
||||
else {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic(
|
||||
"Cannot specialize implicit type alias with unknown definition",
|
||||
);
|
||||
}
|
||||
return Type::unknown();
|
||||
};
|
||||
|
||||
let generic_type_alias = value_ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::BindLegacyTypevars(BindingContext::Synthetic),
|
||||
&TypeMapping::BindLegacyTypevars(BindingContext::Definition(type_alias_definition)),
|
||||
TypeContext::default(),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user