@@ -1009,24 +1009,6 @@ fn return_annotation_range(bindings: &Bindings, return_idx: Idx<Key>) -> Option<
10091009 }
10101010}
10111011
1012- /// Only the first parameter (`self`/`cls`) is allowed to be unannotated.
1013- fn is_function_completely_annotated (
1014- bindings : & Bindings ,
1015- answers : & Answers ,
1016- undecorated_idx : Idx < KeyUndecoratedFunction > ,
1017- ) -> bool {
1018- let fun = bindings. get ( undecorated_idx) ;
1019- let return_idx = bindings. key_to_idx ( & Key :: ReturnType ( ShortIdentifier :: new ( & fun. def . name ) ) ) ;
1020- if return_annotation_range ( bindings, return_idx) . is_none ( ) {
1021- return false ;
1022- }
1023-
1024- let implicit_receiver = has_implicit_receiver ( fun, answers, undecorated_idx) ;
1025- params_with_keys ( & fun. def . parameters , implicit_receiver)
1026- . iter ( )
1027- . all ( |( key, param) | key. is_none ( ) || param. annotation . is_some ( ) )
1028- }
1029-
10301012/// Only a bare `Any` counts as unknown; container types like `list[Any]` are known.
10311013fn is_type_known ( ty : & Type ) -> bool {
10321014 !ty. is_any ( )
@@ -1224,36 +1206,16 @@ fn parse_classes(
12241206 module : & Module ,
12251207 bindings : & Bindings ,
12261208 answers : & Answers ,
1227- transaction : & Transaction ,
1228- handle : & Handle ,
12291209 tco_classes : & SmallSet < Idx < KeyClass > > ,
12301210) -> Vec < ReportClass > {
12311211 let mut classes = Vec :: new ( ) ;
12321212
1233- // group method definitions by class
1234- let mut methods_by_class: HashMap < Idx < KeyClass > , Vec < Idx < KeyUndecoratedFunction > > > =
1235- HashMap :: new ( ) ;
1236- for idx in bindings. keys :: < Key > ( ) {
1237- if let Key :: Definition ( _) = bindings. idx_to_key ( idx)
1238- && let Binding :: Function ( x, _pred, _class_meta) = bindings. get ( idx)
1239- {
1240- let undecorated_idx = bindings. get ( * x) . undecorated_idx ;
1241- if let Some ( class_key) = bindings. get ( undecorated_idx) . class_key {
1242- methods_by_class
1243- . entry ( class_key)
1244- . or_default ( )
1245- . push ( undecorated_idx) ;
1246- }
1247- }
1248- }
1249-
12501213 for class_idx in bindings. keys :: < KeyClass > ( ) {
12511214 // Skip @type_check_only classes.
12521215 if tco_classes. contains ( & class_idx) {
12531216 continue ;
12541217 }
1255- let binding_class = bindings. get ( class_idx) ;
1256- let cls_binding = match binding_class {
1218+ let cls_binding = match bindings. get ( class_idx) {
12571219 BindingClass :: ClassDef ( cls) => cls,
12581220 BindingClass :: FunctionalClassDef ( ..) => continue ,
12591221 } ;
@@ -1266,65 +1228,13 @@ fn parse_classes(
12661228 if is_deleted_class ( module, bindings, cls_binding) {
12671229 continue ;
12681230 }
1269- let class_type = match answers. get_idx ( class_idx) {
1270- Some ( result) => match & result. 0 {
1271- Some ( cls) => cls. clone ( ) ,
1272- None => continue ,
1273- } ,
1274- None => continue ,
1275- } ;
1276- let class_name = class_fqn ( module, parent, name) ;
1277- let mro = class_mro ( bindings, answers, & class_type) ;
1278- // Check methods defined directly on this class
1279- let mut incomplete_attributes = Vec :: new ( ) ;
1280- for & undecorated_idx in methods_by_class. get ( & class_idx) . into_iter ( ) . flatten ( ) {
1281- if !is_function_completely_annotated ( bindings, answers, undecorated_idx) {
1282- incomplete_attributes. push ( IncompleteAttribute {
1283- name : bindings. get ( undecorated_idx) . def . name . to_string ( ) ,
1284- declared_in : class_name. clone ( ) ,
1285- } ) ;
1286- }
1287- }
1288- // Check inherited methods
1289- for ancestor_class_type in mro. ancestors_no_object ( ) {
1290- let ancestor_class = ancestor_class_type. class_object ( ) ;
1291- // Skip methods inherited from builtins
1292- if ancestor_class. module_name ( ) . as_str ( ) == "builtins" {
1293- continue ;
1294- }
1295- let ancestor_name = class_fqn (
1296- ancestor_class. module ( ) ,
1297- ancestor_class. qname ( ) . parent ( ) ,
1298- ancestor_class. name ( ) ,
1299- ) ;
1300- let Some ( ancestor_class_fields) = transaction. get_class_fields ( handle, ancestor_class)
1301- else {
1302- continue ;
1303- } ;
1304- for field_name in ancestor_class_fields. names ( ) {
1305- let field_name_str = field_name. to_string ( ) ;
1306- // Skip if we already have this attribute listed (it has been overridden
1307- // by the current class or another class in the MRO)
1308- if incomplete_attributes
1309- . iter ( )
1310- . any ( |a| a. name == field_name_str)
1311- {
1312- continue ;
1313- }
1314- if !ancestor_class_fields. is_field_annotated ( field_name) {
1315- incomplete_attributes. push ( IncompleteAttribute {
1316- name : field_name_str,
1317- declared_in : ancestor_name. clone ( ) ,
1318- } ) ;
1319- }
1320- }
1231+ // Skip classes that fail to resolve.
1232+ if answers. get_idx ( class_idx) . is_none_or ( |c| c. 0 . is_none ( ) ) {
1233+ continue ;
13211234 }
1322- let location = range_to_location ( module, cls_binding. def . range ) ;
1323- incomplete_attributes. sort ( ) ;
13241235 classes. push ( ReportClass {
1325- name : class_name,
1326- incomplete_attributes,
1327- location,
1236+ name : class_fqn ( module, parent, name) ,
1237+ location : range_to_location ( module, cls_binding. def . range ) ,
13281238 } ) ;
13291239 }
13301240 classes. sort ( ) ;
@@ -1442,14 +1352,7 @@ impl ModuleSymbols {
14421352 & tco_classes,
14431353 ) ;
14441354 merge_overloads ( & mut functions) ;
1445- let classes = parse_classes (
1446- & module,
1447- & bindings,
1448- & answers,
1449- transaction,
1450- handle,
1451- & tco_classes,
1452- ) ;
1355+ let classes = parse_classes ( & module, & bindings, & answers, & tco_classes) ;
14531356 let mut variables = parse_variables (
14541357 & module,
14551358 & bindings,
@@ -3127,20 +3030,19 @@ def g(x: int) -> int:
31273030 ( env, base, derived)
31283031 }
31293032
3130- /// Collect `derived` from a run that retains all bindings/answers, as ground truth.
3131- fn base_derived_reference ( ) -> ModuleSymbols {
3033+ /// Collect `derived`'s class members from a run that retains all bindings/answers,
3034+ /// as ground truth. Inherited members are looked up in the base module via
3035+ /// `get_class_fields`, so they exercise the cross-module path.
3036+ fn base_derived_reference ( ) -> SmallSet < String > {
31323037 let ( env, _, derived) = base_derived_env ( ) ;
31333038 let ( state, _) = env. to_state ( ) ;
3134- let symbols = ModuleSymbols :: collect ( & state. transaction ( ) , & derived, false ) . unwrap ( ) ;
3039+ let symbols = ModuleSymbols :: collect ( & state. transaction ( ) , & derived, true ) . unwrap ( ) ;
3040+ let members = symbols. stub_merge . unwrap ( ) . class_members ;
31353041 assert ! (
3136- symbols
3137- . classes
3138- . iter( )
3139- . any( |c| !c. incomplete_attributes. is_empty( ) ) ,
3140- "Derived should report an inherited incomplete attribute from Base: {:?}" ,
3141- symbols. classes
3042+ members. contains( "derived.Derived.x" ) ,
3043+ "Derived should report the member `x` inherited from Base: {members:?}"
31423044 ) ;
3143- symbols
3045+ members
31443046 }
31453047
31463048 /// Inherited members survive base-bindings eviction via retained `Solutions` metadata.
@@ -3159,11 +3061,11 @@ def g(x: int) -> int:
31593061 ) ;
31603062
31613063 transaction. run ( & [ derived. dupe ( ) ] , Require :: Everything , None ) ;
3162- let symbols = ModuleSymbols :: collect ( & transaction, & derived, false ) . unwrap ( ) ;
3064+ let symbols = ModuleSymbols :: collect ( & transaction, & derived, true ) . unwrap ( ) ;
31633065 assert_eq ! (
3164- symbols. classes ,
3165- base_derived_reference( ) . classes ,
3166- "evicting the base module's bindings must not change the derived class report "
3066+ symbols. stub_merge . unwrap ( ) . class_members ,
3067+ base_derived_reference( ) ,
3068+ "evicting the base module's bindings must not change the derived class members "
31673069 ) ;
31683070 }
31693071
@@ -3176,7 +3078,7 @@ def g(x: int) -> int:
31763078 let mut transaction = state. new_transaction ( Require :: Exports , None ) ;
31773079 transaction. set_memory ( env. get_memory ( ) ) ;
31783080 transaction. set_solutions_hook ( Some ( Box :: new ( |handle, transaction| {
3179- if let Some ( symbols) = ModuleSymbols :: collect ( transaction, handle, false ) {
3081+ if let Some ( symbols) = ModuleSymbols :: collect ( transaction, handle, true ) {
31803082 collected. lock ( ) . insert ( handle. dupe ( ) , symbols) ;
31813083 }
31823084 } ) ) ) ;
@@ -3189,9 +3091,9 @@ def g(x: int) -> int:
31893091
31903092 let symbols = collected. lock ( ) . remove ( & derived) . unwrap ( ) ;
31913093 assert_eq ! (
3192- symbols. classes ,
3193- base_derived_reference( ) . classes ,
3194- "hook-collected symbols must match symbols collected with bindings retained"
3094+ symbols. stub_merge . unwrap ( ) . class_members ,
3095+ base_derived_reference( ) ,
3096+ "hook-collected members must match members collected with bindings retained"
31953097 ) ;
31963098 }
31973099}
0 commit comments