diff --git a/crates/ty_python_semantic/src/place.rs b/crates/ty_python_semantic/src/place.rs index 4ba6a98225..33a50d28b9 100644 --- a/crates/ty_python_semantic/src/place.rs +++ b/crates/ty_python_semantic/src/place.rs @@ -823,7 +823,9 @@ fn place_by_id<'db>( }); if scope.node(db).scope_kind().is_module() { - inferred.map_type(|ty| ty.promote_literals(db)).into() + inferred + .map_type(|ty| ty.promote_literals(db, false)) + .into() } else if scope.file(db).is_stub(db) || scope.scope(db).visibility().is_private() { // We generally trust module-level undeclared places in stubs and do not union // with `Unknown`. If we don't do this, simple aliases like `IOError = OSError` in diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3021acd23c..5a25adc46c 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1171,20 +1171,37 @@ impl<'db> Type<'db> { /// Note that this function tries to promote literals to a more user-friendly form than their /// fallback instance type. For example, `def _() -> int` is promoted to `Callable[[], int]`, /// as opposed to `FunctionType`. - pub(crate) fn promote_literals(self, db: &'db dyn Db) -> Type<'db> { - self.apply_type_mapping(db, &TypeMapping::PromoteLiterals) + pub(crate) fn promote_literals( + self, + db: &'db dyn Db, + promote_modules_and_functions: bool, + ) -> Type<'db> { + self.apply_type_mapping( + db, + &TypeMapping::PromoteLiterals { + promote_modules_and_functions, + }, + ) } /// Like [`Type::promote_literals`], but does not recurse into nested types. - fn promote_literals_impl(self, db: &'db dyn Db) -> Type<'db> { + fn promote_literals_impl( + self, + db: &'db dyn Db, + promote_modules_and_functions: bool, + ) -> Type<'db> { match self { Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_instance(db), Type::BooleanLiteral(_) => KnownClass::Bool.to_instance(db), Type::IntLiteral(_) => KnownClass::Int.to_instance(db), Type::BytesLiteral(_) => KnownClass::Bytes.to_instance(db), - Type::ModuleLiteral(_) => KnownClass::ModuleType.to_instance(db), Type::EnumLiteral(literal) => literal.enum_class_instance(db), - Type::FunctionLiteral(literal) => Type::Callable(literal.into_callable_type(db)), + Type::ModuleLiteral(_) if promote_modules_and_functions => { + KnownClass::ModuleType.to_instance(db) + } + Type::FunctionLiteral(literal) if promote_modules_and_functions => { + Type::Callable(literal.into_callable_type(db)) + } _ => self, } } @@ -6037,7 +6054,7 @@ impl<'db> Type<'db> { self } } - TypeMapping::PromoteLiterals + TypeMapping::PromoteLiterals { .. } | TypeMapping::BindLegacyTypevars(_) | TypeMapping::MarkTypeVarsInferable(_) => self, TypeMapping::Materialize(materialization_kind) => { @@ -6059,7 +6076,7 @@ impl<'db> Type<'db> { self } } - TypeMapping::PromoteLiterals + TypeMapping::PromoteLiterals { .. } | TypeMapping::BindLegacyTypevars(_) | TypeMapping::BindSelf(_) | TypeMapping::ReplaceSelf { .. } @@ -6074,7 +6091,7 @@ impl<'db> Type<'db> { } TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) | - TypeMapping::PromoteLiterals | + TypeMapping::PromoteLiterals { .. } | TypeMapping::BindSelf(_) | TypeMapping::ReplaceSelf { .. } | TypeMapping::MarkTypeVarsInferable(_) | @@ -6085,7 +6102,7 @@ impl<'db> Type<'db> { let function = Type::FunctionLiteral(function.apply_type_mapping_impl(db, type_mapping, visitor)); match type_mapping { - TypeMapping::PromoteLiterals => function.promote_literals_impl(db), + TypeMapping::PromoteLiterals { promote_modules_and_functions } => function.promote_literals_impl(db, *promote_modules_and_functions), _ => function } } @@ -6193,7 +6210,7 @@ impl<'db> Type<'db> { TypeMapping::ReplaceSelf { .. } | TypeMapping::MarkTypeVarsInferable(_) | TypeMapping::Materialize(_) => self, - TypeMapping::PromoteLiterals => self.promote_literals_impl(db) + TypeMapping::PromoteLiterals { promote_modules_and_functions } => self.promote_literals_impl(db, *promote_modules_and_functions) } Type::Dynamic(_) => match type_mapping { @@ -6203,7 +6220,7 @@ impl<'db> Type<'db> { TypeMapping::BindSelf(_) | TypeMapping::ReplaceSelf { .. } | TypeMapping::MarkTypeVarsInferable(_) | - TypeMapping::PromoteLiterals => self, + TypeMapping::PromoteLiterals { .. } => self, TypeMapping::Materialize(materialization_kind) => match materialization_kind { MaterializationKind::Top => Type::object(), MaterializationKind::Bottom => Type::Never, @@ -6227,12 +6244,6 @@ impl<'db> Type<'db> { } } - /// Replaces any literal types with their corresponding promoted type form (e.g. `Literal["string"]` - /// to `str`, or `def _() -> int` to `Callable[[], int]`). - pub(crate) fn promote_literals(self, db: &'db dyn Db) -> Type<'db> { - self.apply_type_mapping(db, &TypeMapping::PromoteLiterals) - } - /// Locates any legacy `TypeVar`s in this type, and adds them to a set. This is used to build /// up a generic context from any legacy `TypeVar`s that appear in a function parameter list or /// `Generic` specialization. @@ -6736,7 +6747,7 @@ pub enum TypeMapping<'a, 'db> { PartialSpecialization(PartialSpecialization<'a, 'db>), /// Replaces any literal types with their corresponding promoted type form (e.g. `Literal["string"]` /// to `str`, or `def _() -> int` to `Callable[[], int]`). - PromoteLiterals, + PromoteLiterals { promote_modules_and_functions: bool }, /// Binds a legacy typevar with the generic context (class, function, type alias) that it is /// being used in. BindLegacyTypevars(BindingContext<'db>), @@ -6769,7 +6780,7 @@ impl<'db> TypeMapping<'_, 'db> { match self { TypeMapping::Specialization(_) | TypeMapping::PartialSpecialization(_) - | TypeMapping::PromoteLiterals + | TypeMapping::PromoteLiterals { .. } | TypeMapping::BindLegacyTypevars(_) | TypeMapping::MarkTypeVarsInferable(_) | TypeMapping::Materialize(_) => context, diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 59a38e303b..94348dffb0 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -2498,9 +2498,12 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { // The inherited generic context is used when inferring the specialization of a generic // class from a constructor call. In this case (only), we promote any typevars that are // inferred as a literal to the corresponding instance type. - builder - .build(gc) - .apply_type_mapping(self.db, &TypeMapping::PromoteLiterals) + builder.build(gc).apply_type_mapping( + self.db, + &TypeMapping::PromoteLiterals { + promote_modules_and_functions: true, + }, + ) }); } diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index ea7d54841c..de65181fa1 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -2662,7 +2662,7 @@ pub(crate) fn report_undeclared_protocol_member( if definition.kind(db).is_unannotated_assignment() { let binding_type = binding_type(db, definition); - let suggestion = binding_type.promote_literals(db); + let suggestion = binding_type.promote_literals(db, true); if should_give_hint(db, suggestion) { diagnostic.set_primary_message(format_args!( diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 204d14d570..a45ad91e0b 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -5432,7 +5432,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // Convert any element literals to their promoted type form to avoid excessively large // unions for large nested list literals, which the constraint solver struggles with. - let inferred_elt_ty = inferred_elt_ty.promote_literals(self.db()); + let inferred_elt_ty = inferred_elt_ty.promote_literals(self.db(), true); builder .infer(Type::TypeVar(*elt_ty), inferred_elt_ty)