[ty] Support using legacy typing aliases for generic classes in type annotations (#18404)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
lipefree 2025-06-03 13:09:51 +02:00 committed by GitHub
parent 67d94d9ec8
commit f23d2c9b9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 158 additions and 63 deletions

View file

@ -2426,7 +2426,7 @@ impl<'db> KnownClass {
return Type::unknown();
};
let Some(generic_context) = class_literal.generic_context(db) else {
return Type::unknown();
return Type::instance(db, ClassType::NonGeneric(class_literal));
};
let types = specialization.into_iter().collect::<Box<[_]>>();
@ -2437,11 +2437,11 @@ impl<'db> KnownClass {
if MESSAGES.lock().unwrap().insert(self) {
tracing::info!(
"Wrong number of types when specializing {}. \
Falling back to `Unknown` for the symbol instead.",
Falling back to default specialization for the symbol instead.",
self.display(db)
);
}
return Type::unknown();
return Type::instance(db, class_literal.default_specialization(db));
}
let specialization = generic_context.specialize(db, types);

View file

@ -7975,7 +7975,7 @@ impl<'db> TypeInferenceBuilder<'db> {
match value_ty {
Type::SpecialForm(SpecialFormType::Annotated) => {
// This branch is similar to the corresponding branch in `infer_parameterized_known_instance_type_expression`, but
// This branch is similar to the corresponding branch in `infer_parameterized_special_form_type_expression`, but
// `Annotated[…]` can appear both in annotation expressions and in type expressions, and needs to be handled slightly
// differently in each case (calling either `infer_type_expression_*` or `infer_annotation_expression_*`).
if let ast::Expr::Tuple(ast::ExprTuple {
@ -8701,6 +8701,43 @@ impl<'db> TypeInferenceBuilder<'db> {
}
}
fn infer_parameterized_legacy_typing_alias(
&mut self,
subscript_node: &ast::ExprSubscript,
expected_arg_count: usize,
alias: SpecialFormType,
class: KnownClass,
) -> Type<'db> {
let arguments = &*subscript_node.slice;
let (args, args_number) = if let ast::Expr::Tuple(t) = arguments {
(Either::Left(t), t.len())
} else {
(Either::Right([arguments]), 1)
};
if args_number != expected_arg_count {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript_node) {
let noun = if expected_arg_count == 1 {
"argument"
} else {
"arguments"
};
builder.into_diagnostic(format_args!(
"Legacy alias `{alias}` expected exactly {expected_arg_count} {noun}, \
got {args_number}",
));
}
}
let ty = class.to_specialized_instance(
self.db(),
args.into_iter()
.map(|node| self.infer_type_expression(node)),
);
if arguments.is_tuple_expr() {
self.store_expression_type(arguments, ty);
}
ty
}
fn infer_parameterized_special_form_type_expression(
&mut self,
subscript: &ast::ExprSubscript,
@ -8916,43 +8953,60 @@ impl<'db> TypeInferenceBuilder<'db> {
}
},
// TODO: Generics
SpecialFormType::ChainMap => {
self.infer_type_expression(arguments_slice);
KnownClass::ChainMap.to_instance(db)
}
SpecialFormType::OrderedDict => {
self.infer_type_expression(arguments_slice);
KnownClass::OrderedDict.to_instance(db)
}
SpecialFormType::Dict => {
self.infer_type_expression(arguments_slice);
KnownClass::Dict.to_instance(db)
}
SpecialFormType::List => {
self.infer_type_expression(arguments_slice);
KnownClass::List.to_instance(db)
}
SpecialFormType::DefaultDict => {
self.infer_type_expression(arguments_slice);
KnownClass::DefaultDict.to_instance(db)
}
SpecialFormType::Counter => {
self.infer_type_expression(arguments_slice);
KnownClass::Counter.to_instance(db)
}
SpecialFormType::Set => {
self.infer_type_expression(arguments_slice);
KnownClass::Set.to_instance(db)
}
SpecialFormType::FrozenSet => {
self.infer_type_expression(arguments_slice);
KnownClass::FrozenSet.to_instance(db)
}
SpecialFormType::Deque => {
self.infer_type_expression(arguments_slice);
KnownClass::Deque.to_instance(db)
}
SpecialFormType::ChainMap => self.infer_parameterized_legacy_typing_alias(
subscript,
2,
SpecialFormType::ChainMap,
KnownClass::ChainMap,
),
SpecialFormType::OrderedDict => self.infer_parameterized_legacy_typing_alias(
subscript,
2,
SpecialFormType::OrderedDict,
KnownClass::OrderedDict,
),
SpecialFormType::Dict => self.infer_parameterized_legacy_typing_alias(
subscript,
2,
SpecialFormType::Dict,
KnownClass::Dict,
),
SpecialFormType::List => self.infer_parameterized_legacy_typing_alias(
subscript,
1,
SpecialFormType::List,
KnownClass::List,
),
SpecialFormType::DefaultDict => self.infer_parameterized_legacy_typing_alias(
subscript,
2,
SpecialFormType::DefaultDict,
KnownClass::DefaultDict,
),
SpecialFormType::Counter => self.infer_parameterized_legacy_typing_alias(
subscript,
1,
SpecialFormType::Counter,
KnownClass::Counter,
),
SpecialFormType::Set => self.infer_parameterized_legacy_typing_alias(
subscript,
1,
SpecialFormType::Set,
KnownClass::Set,
),
SpecialFormType::FrozenSet => self.infer_parameterized_legacy_typing_alias(
subscript,
1,
SpecialFormType::FrozenSet,
KnownClass::FrozenSet,
),
SpecialFormType::Deque => self.infer_parameterized_legacy_typing_alias(
subscript,
1,
SpecialFormType::Deque,
KnownClass::Deque,
),
SpecialFormType::ReadOnly => {
self.infer_type_expression(arguments_slice);