@@ -102,7 +102,8 @@ use crate::{Db, FxOrderSet, Program};
102102use super :: context:: { InNoTypeCheck , InferContext } ;
103103use super :: diagnostic:: {
104104 INVALID_METACLASS , INVALID_OVERLOAD , INVALID_PROTOCOL , REDUNDANT_CAST , STATIC_ASSERT_ERROR ,
105- SUBCLASS_OF_FINAL_CLASS , TYPE_ASSERTION_FAILURE , report_attempted_protocol_instantiation,
105+ SUBCLASS_OF_FINAL_CLASS , TYPE_ASSERTION_FAILURE ,
106+ hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
106107 report_bad_argument_to_get_protocol_members, report_duplicate_bases,
107108 report_index_out_of_bounds, report_invalid_exception_caught, report_invalid_exception_cause,
108109 report_invalid_exception_raised, report_invalid_or_unsupported_base,
@@ -4122,11 +4123,13 @@ impl<'db> TypeInferenceBuilder<'db> {
41224123 return ;
41234124 } ;
41244125
4125- let Some ( module_ty ) = self . module_type_from_name ( & module_name) else {
4126+ let Some ( module ) = resolve_module ( self . db ( ) , & module_name) else {
41264127 self . add_unknown_declaration_with_binding ( alias. into ( ) , definition) ;
41274128 return ;
41284129 } ;
41294130
4131+ let module_ty = Type :: module_literal ( self . db ( ) , self . file ( ) , & module) ;
4132+
41304133 // The indirection of having `star_import_info` as a separate variable
41314134 // is required in order to make the borrow checker happy.
41324135 let star_import_info = definition
@@ -4175,6 +4178,15 @@ impl<'db> TypeInferenceBuilder<'db> {
41754178 }
41764179 }
41774180
4181+ // Evaluate whether `X.Y` would constitute a valid submodule name,
4182+ // given a `from X import Y` statement. If it is valid, this will be `Some()`;
4183+ // else, it will be `None`.
4184+ let full_submodule_name = ModuleName :: new ( name) . map ( |final_part| {
4185+ let mut ret = module_name. clone ( ) ;
4186+ ret. extend ( & final_part) ;
4187+ ret
4188+ } ) ;
4189+
41784190 // If the module doesn't bind the symbol, check if it's a submodule. This won't get
41794191 // handled by the `Type::member` call because it relies on the semantic index's
41804192 // `imported_modules` set. The semantic index does not include information about
@@ -4190,35 +4202,47 @@ impl<'db> TypeInferenceBuilder<'db> {
41904202 //
41914203 // Regardless, for now, we sidestep all of that by repeating the submodule-or-attribute
41924204 // check here when inferring types for a `from...import` statement.
4193- if let Some ( submodule_name) = ModuleName :: new ( name) {
4194- let mut full_submodule_name = module_name. clone ( ) ;
4195- full_submodule_name. extend ( & submodule_name) ;
4196- if let Some ( submodule_ty) = self . module_type_from_name ( & full_submodule_name) {
4197- self . add_declaration_with_binding (
4198- alias. into ( ) ,
4199- definition,
4200- & DeclaredAndInferredType :: AreTheSame ( submodule_ty) ,
4201- ) ;
4202- return ;
4203- }
4205+ if let Some ( submodule_type) = full_submodule_name
4206+ . as_ref ( )
4207+ . and_then ( |submodule_name| self . module_type_from_name ( submodule_name) )
4208+ {
4209+ self . add_declaration_with_binding (
4210+ alias. into ( ) ,
4211+ definition,
4212+ & DeclaredAndInferredType :: AreTheSame ( submodule_type) ,
4213+ ) ;
4214+ return ;
42044215 }
42054216
4206- if & alias. name != "*" {
4207- let is_import_reachable = self . is_reachable ( import_from) ;
4217+ self . add_unknown_declaration_with_binding ( alias. into ( ) , definition) ;
42084218
4209- if is_import_reachable {
4210- if let Some ( builder) = self
4211- . context
4212- . report_lint ( & UNRESOLVED_IMPORT , AnyNodeRef :: Alias ( alias) )
4213- {
4214- builder. into_diagnostic ( format_args ! (
4215- "Module `{module_name}` has no member `{name}`"
4216- ) ) ;
4217- }
4218- }
4219+ if & alias. name == "*" {
4220+ return ;
42194221 }
42204222
4221- self . add_unknown_declaration_with_binding ( alias. into ( ) , definition) ;
4223+ if !self . is_reachable ( import_from) {
4224+ return ;
4225+ }
4226+
4227+ let Some ( builder) = self
4228+ . context
4229+ . report_lint ( & UNRESOLVED_IMPORT , AnyNodeRef :: Alias ( alias) )
4230+ else {
4231+ return ;
4232+ } ;
4233+
4234+ let diagnostic = builder. into_diagnostic ( format_args ! (
4235+ "Module `{module_name}` has no member `{name}`"
4236+ ) ) ;
4237+
4238+ if let Some ( full_submodule_name) = full_submodule_name {
4239+ hint_if_stdlib_submodule_exists_on_other_versions (
4240+ self . db ( ) ,
4241+ diagnostic,
4242+ & full_submodule_name,
4243+ & module,
4244+ ) ;
4245+ }
42224246 }
42234247
42244248 fn infer_return_statement ( & mut self , ret : & ast:: StmtReturn ) {
@@ -4242,7 +4266,7 @@ impl<'db> TypeInferenceBuilder<'db> {
42424266
42434267 fn module_type_from_name ( & self , module_name : & ModuleName ) -> Option < Type < ' db > > {
42444268 resolve_module ( self . db ( ) , module_name)
4245- . map ( |module| Type :: module_literal ( self . db ( ) , self . file ( ) , module) )
4269+ . map ( |module| Type :: module_literal ( self . db ( ) , self . file ( ) , & module) )
42464270 }
42474271
42484272 fn infer_decorator ( & mut self , decorator : & ast:: Decorator ) -> Type < ' db > {
0 commit comments