11use infer:: nearest_enclosing_class;
2- use itertools:: Either ;
2+ use itertools:: { Either , Itertools } ;
33use ruff_db:: parsed:: parsed_module;
44
55use std:: slice:: Iter ;
@@ -7535,6 +7535,23 @@ impl<'db> ModuleLiteralType<'db> {
75357535 self . _importing_file ( db)
75367536 }
75377537
7538+ fn available_submodule_attributes ( & self , db : & ' db dyn Db ) -> impl Iterator < Item = Name > {
7539+ self . importing_file ( db)
7540+ . into_iter ( )
7541+ . flat_map ( |file| imported_modules ( db, file) )
7542+ . filter_map ( move |submodule_name| submodule_name. relative_to ( self . module ( db) . name ( ) ) )
7543+ . filter_map ( |relative_submodule| relative_submodule. components ( ) . next ( ) . map ( Name :: from) )
7544+ }
7545+
7546+ fn resolve_submodule ( self , db : & ' db dyn Db , name : & str ) -> Option < Type < ' db > > {
7547+ let importing_file = self . importing_file ( db) ?;
7548+ let relative_submodule_name = ModuleName :: new ( name) ?;
7549+ let mut absolute_submodule_name = self . module ( db) . name ( ) . clone ( ) ;
7550+ absolute_submodule_name. extend ( & relative_submodule_name) ;
7551+ let submodule = resolve_module ( db, & absolute_submodule_name) ?;
7552+ Some ( Type :: module_literal ( db, importing_file, & submodule) )
7553+ }
7554+
75387555 fn static_member ( self , db : & ' db dyn Db , name : & str ) -> PlaceAndQualifiers < ' db > {
75397556 // `__dict__` is a very special member that is never overridden by module globals;
75407557 // we should always look it up directly as an attribute on `types.ModuleType`,
@@ -7554,17 +7571,9 @@ impl<'db> ModuleLiteralType<'db> {
75547571 // the parent module's `__init__.py` file being evaluated. That said, we have
75557572 // chosen to always have the submodule take priority. (This matches pyright's
75567573 // current behavior, but is the opposite of mypy's current behavior.)
7557- if let Some ( importing_file) = self . importing_file ( db) {
7558- if let Some ( submodule_name) = ModuleName :: new ( name) {
7559- let imported_submodules = imported_modules ( db, importing_file) ;
7560- let mut full_submodule_name = self . module ( db) . name ( ) . clone ( ) ;
7561- full_submodule_name. extend ( & submodule_name) ;
7562- if imported_submodules. contains ( & full_submodule_name) {
7563- if let Some ( submodule) = resolve_module ( db, & full_submodule_name) {
7564- return Place :: bound ( Type :: module_literal ( db, importing_file, & submodule) )
7565- . into ( ) ;
7566- }
7567- }
7574+ if self . available_submodule_attributes ( db) . contains ( name) {
7575+ if let Some ( submodule) = self . resolve_submodule ( db, name) {
7576+ return Place :: bound ( submodule) . into ( ) ;
75687577 }
75697578 }
75707579
0 commit comments