@@ -866,7 +866,26 @@ impl<'db> TypeInferenceBuilder<'db> {
866866 asname : _,
867867 } = alias;
868868
869- let module_ty = self . module_ty_from_name ( ModuleName :: new ( name) , alias. into ( ) ) ;
869+ let module_ty = ModuleName :: new ( name)
870+ . ok_or ( ModuleResolutionError :: InvalidSyntax )
871+ . and_then ( |module_name| self . module_ty_from_name ( module_name) ) ;
872+
873+ let module_ty = match module_ty {
874+ Ok ( ty) => ty,
875+ Err ( ModuleResolutionError :: InvalidSyntax ) => {
876+ tracing:: debug!( "Failed to resolve import due to invalid syntax" ) ;
877+ Type :: Unknown
878+ }
879+ Err ( ModuleResolutionError :: UnresolvedModule ) => {
880+ self . add_diagnostic (
881+ AnyNodeRef :: Alias ( alias) ,
882+ "unresolved-import" ,
883+ format_args ! ( "Import '{name}' could not be resolved." ) ,
884+ ) ;
885+ Type :: Unknown
886+ }
887+ } ;
888+
870889 self . types . definitions . insert ( definition, module_ty) ;
871890 }
872891
@@ -914,32 +933,38 @@ impl<'db> TypeInferenceBuilder<'db> {
914933 /// - `tail` is the relative module name stripped of all leading dots:
915934 /// - `from .foo import bar` => `tail == "foo"`
916935 /// - `from ..foo.bar import baz` => `tail == "foo.bar"`
917- fn relative_module_name ( & self , tail : Option < & str > , level : NonZeroU32 ) -> Option < ModuleName > {
936+ fn relative_module_name (
937+ & self ,
938+ tail : Option < & str > ,
939+ level : NonZeroU32 ,
940+ ) -> Result < ModuleName , ModuleResolutionError > {
918941 let Some ( module) = file_to_module ( self . db , self . file ) else {
919942 tracing:: debug!(
920943 "Relative module resolution '{}' failed; could not resolve file '{}' to a module" ,
921944 format_import_from_module( level. get( ) , tail) ,
922945 self . file. path( self . db)
923946 ) ;
924- return None ;
947+ return Err ( ModuleResolutionError :: UnresolvedModule ) ;
925948 } ;
926949 let mut level = level. get ( ) ;
927950 if module. kind ( ) . is_package ( ) {
928951 level -= 1 ;
929952 }
930953 let mut module_name = module. name ( ) . to_owned ( ) ;
931954 for _ in 0 ..level {
932- module_name = module_name. parent ( ) ?;
955+ module_name = module_name
956+ . parent ( )
957+ . ok_or ( ModuleResolutionError :: UnresolvedModule ) ?;
933958 }
934959 if let Some ( tail) = tail {
935960 if let Some ( valid_tail) = ModuleName :: new ( tail) {
936961 module_name. extend ( & valid_tail) ;
937962 } else {
938963 tracing:: debug!( "Relative module resolution failed: invalid syntax" ) ;
939- return None ;
964+ return Err ( ModuleResolutionError :: InvalidSyntax ) ;
940965 }
941966 }
942- Some ( module_name)
967+ Ok ( module_name)
943968 }
944969
945970 fn infer_import_from_definition (
@@ -974,12 +999,12 @@ impl<'db> TypeInferenceBuilder<'db> {
974999 alias. name,
9751000 format_import_from_module( * level, module) ,
9761001 ) ;
977- let module_name =
978- module . expect ( "Non-relative import should always have a non-None `module`!" ) ;
979- ModuleName :: new ( module_name )
1002+ module
1003+ . and_then ( ModuleName :: new )
1004+ . ok_or ( ModuleResolutionError :: InvalidSyntax )
9801005 } ;
9811006
982- let module_ty = self . module_ty_from_name ( module_name, import_from . into ( ) ) ;
1007+ let module_ty = module_name . and_then ( | module_name| self . module_ty_from_name ( module_name ) ) ;
9831008
9841009 let ast:: Alias {
9851010 range : _,
@@ -992,11 +1017,34 @@ impl<'db> TypeInferenceBuilder<'db> {
9921017 // the runtime error will occur immediately (rather than when the symbol is *used*,
9931018 // as would be the case for a symbol with type `Unbound`), so it's appropriate to
9941019 // think of the type of the imported symbol as `Unknown` rather than `Unbound`
995- let ty = module_ty
1020+ let member_ty = module_ty
1021+ . unwrap_or ( Type :: Unbound )
9961022 . member ( self . db , & Name :: new ( & name. id ) )
9971023 . replace_unbound_with ( self . db , Type :: Unknown ) ;
9981024
999- self . types . definitions . insert ( definition, ty) ;
1025+ if matches ! ( module_ty, Err ( ModuleResolutionError :: UnresolvedModule ) ) {
1026+ self . add_diagnostic (
1027+ AnyNodeRef :: StmtImportFrom ( import_from) ,
1028+ "unresolved-import" ,
1029+ format_args ! (
1030+ "Import '{}{}' could not be resolved." ,
1031+ "." . repeat( * level as usize ) ,
1032+ module. unwrap_or_default( )
1033+ ) ,
1034+ ) ;
1035+ } else if module_ty. is_ok ( ) && member_ty. is_unknown ( ) {
1036+ self . add_diagnostic (
1037+ AnyNodeRef :: Alias ( alias) ,
1038+ "unresolved-import" ,
1039+ format_args ! (
1040+ "Could not resolve import of '{name}' from '{}{}'" ,
1041+ "." . repeat( * level as usize ) ,
1042+ module. unwrap_or_default( )
1043+ ) ,
1044+ ) ;
1045+ }
1046+
1047+ self . types . definitions . insert ( definition, member_ty) ;
10001048 }
10011049
10021050 fn infer_return_statement ( & mut self , ret : & ast:: StmtReturn ) {
@@ -1011,25 +1059,12 @@ impl<'db> TypeInferenceBuilder<'db> {
10111059 }
10121060
10131061 fn module_ty_from_name (
1014- & mut self ,
1015- module_name : Option < ModuleName > ,
1016- node : AnyNodeRef ,
1017- ) -> Type < ' db > {
1018- let Some ( module_name) = module_name else {
1019- return Type :: Unknown ;
1020- } ;
1021-
1022- if let Some ( module) = resolve_module ( self . db , module_name. clone ( ) ) {
1023- Type :: Module ( module. file ( ) )
1024- } else {
1025- self . add_diagnostic (
1026- node,
1027- "unresolved-import" ,
1028- format_args ! ( "Import '{module_name}' could not be resolved." ) ,
1029- ) ;
1030-
1031- Type :: Unknown
1032- }
1062+ & self ,
1063+ module_name : ModuleName ,
1064+ ) -> Result < Type < ' db > , ModuleResolutionError > {
1065+ resolve_module ( self . db , module_name)
1066+ . map ( |module| Type :: Module ( module. file ( ) ) )
1067+ . ok_or ( ModuleResolutionError :: UnresolvedModule )
10331068 }
10341069
10351070 fn infer_decorator ( & mut self , decorator : & ast:: Decorator ) -> Type < ' db > {
@@ -1795,6 +1830,12 @@ fn format_import_from_module(level: u32, module: Option<&str>) -> String {
17951830 )
17961831}
17971832
1833+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
1834+ enum ModuleResolutionError {
1835+ InvalidSyntax ,
1836+ UnresolvedModule ,
1837+ }
1838+
17981839#[ cfg( test) ]
17991840mod tests {
18001841 use anyhow:: Context ;
@@ -2048,6 +2089,16 @@ mod tests {
20482089 Ok ( ( ) )
20492090 }
20502091
2092+ #[ test]
2093+ fn from_import_with_no_module_name ( ) -> anyhow:: Result < ( ) > {
2094+ // This test checks that invalid syntax in a `StmtImportFrom` node
2095+ // leads to the type being inferred as `Unknown`
2096+ let mut db = setup_db ( ) ;
2097+ db. write_file ( "src/foo.py" , "from import bar" ) ?;
2098+ assert_public_ty ( & db, "src/foo.py" , "bar" , "Unknown" ) ;
2099+ Ok ( ( ) )
2100+ }
2101+
20512102 #[ test]
20522103 fn resolve_base_class_by_name ( ) -> anyhow:: Result < ( ) > {
20532104 let mut db = setup_db ( ) ;
0 commit comments