@@ -866,7 +866,26 @@ impl<'db> TypeInferenceBuilder<'db> {
866
866
asname : _,
867
867
} = alias;
868
868
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
+
870
889
self . types . definitions . insert ( definition, module_ty) ;
871
890
}
872
891
@@ -914,32 +933,38 @@ impl<'db> TypeInferenceBuilder<'db> {
914
933
/// - `tail` is the relative module name stripped of all leading dots:
915
934
/// - `from .foo import bar` => `tail == "foo"`
916
935
/// - `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 > {
918
941
let Some ( module) = file_to_module ( self . db , self . file ) else {
919
942
tracing:: debug!(
920
943
"Relative module resolution '{}' failed; could not resolve file '{}' to a module" ,
921
944
format_import_from_module( level. get( ) , tail) ,
922
945
self . file. path( self . db)
923
946
) ;
924
- return None ;
947
+ return Err ( ModuleResolutionError :: UnresolvedModule ) ;
925
948
} ;
926
949
let mut level = level. get ( ) ;
927
950
if module. kind ( ) . is_package ( ) {
928
951
level -= 1 ;
929
952
}
930
953
let mut module_name = module. name ( ) . to_owned ( ) ;
931
954
for _ in 0 ..level {
932
- module_name = module_name. parent ( ) ?;
955
+ module_name = module_name
956
+ . parent ( )
957
+ . ok_or ( ModuleResolutionError :: UnresolvedModule ) ?;
933
958
}
934
959
if let Some ( tail) = tail {
935
960
if let Some ( valid_tail) = ModuleName :: new ( tail) {
936
961
module_name. extend ( & valid_tail) ;
937
962
} else {
938
963
tracing:: debug!( "Relative module resolution failed: invalid syntax" ) ;
939
- return None ;
964
+ return Err ( ModuleResolutionError :: InvalidSyntax ) ;
940
965
}
941
966
}
942
- Some ( module_name)
967
+ Ok ( module_name)
943
968
}
944
969
945
970
fn infer_import_from_definition (
@@ -974,12 +999,12 @@ impl<'db> TypeInferenceBuilder<'db> {
974
999
alias. name,
975
1000
format_import_from_module( * level, module) ,
976
1001
) ;
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 )
980
1005
} ;
981
1006
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 ) ) ;
983
1008
984
1009
let ast:: Alias {
985
1010
range : _,
@@ -992,11 +1017,34 @@ impl<'db> TypeInferenceBuilder<'db> {
992
1017
// the runtime error will occur immediately (rather than when the symbol is *used*,
993
1018
// as would be the case for a symbol with type `Unbound`), so it's appropriate to
994
1019
// 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 )
996
1022
. member ( self . db , & Name :: new ( & name. id ) )
997
1023
. replace_unbound_with ( self . db , Type :: Unknown ) ;
998
1024
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) ;
1000
1048
}
1001
1049
1002
1050
fn infer_return_statement ( & mut self , ret : & ast:: StmtReturn ) {
@@ -1011,25 +1059,12 @@ impl<'db> TypeInferenceBuilder<'db> {
1011
1059
}
1012
1060
1013
1061
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 )
1033
1068
}
1034
1069
1035
1070
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 {
1795
1830
)
1796
1831
}
1797
1832
1833
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
1834
+ enum ModuleResolutionError {
1835
+ InvalidSyntax ,
1836
+ UnresolvedModule ,
1837
+ }
1838
+
1798
1839
#[ cfg( test) ]
1799
1840
mod tests {
1800
1841
use anyhow:: Context ;
@@ -2048,6 +2089,16 @@ mod tests {
2048
2089
Ok ( ( ) )
2049
2090
}
2050
2091
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
+
2051
2102
#[ test]
2052
2103
fn resolve_base_class_by_name ( ) -> anyhow:: Result < ( ) > {
2053
2104
let mut db = setup_db ( ) ;
0 commit comments