@@ -411,13 +411,13 @@ impl ReportArgs {
411411 }
412412
413413 /// Classify a single annotation slot: is it typed, any, or untyped?
414- fn classify_slot ( has_annotation : bool , is_type_known : bool ) -> SlotCounts {
415- if !has_annotation {
416- SlotCounts :: untyped ( )
417- } else if is_type_known {
418- SlotCounts :: typed ( )
419- } else {
420- SlotCounts :: any ( )
414+ /// Bare qualifiers (e.g. `Final`) resolve to `None` but are treated as typed (i.e. the type is
415+ /// inferred from the value).
416+ fn classify_slot ( is_type_known : Option < bool > ) -> SlotCounts {
417+ match is_type_known {
418+ Some ( true ) => SlotCounts :: typed ( ) ,
419+ Some ( false ) => SlotCounts :: any ( ) ,
420+ None => SlotCounts :: untyped ( ) ,
421421 }
422422 }
423423
@@ -519,12 +519,12 @@ impl ReportArgs {
519519 }
520520 _ => None ,
521521 } ;
522- let is_type_known = annotation_text . is_some ( )
523- && answers
524- . get_idx ( * annot_idx )
525- . and_then ( |awt| awt . annotation . ty . as_ref ( ) . map ( Self :: is_type_known ) )
526- . unwrap_or ( false ) ;
527- let slots = Self :: classify_slot ( annotation_text . is_some ( ) , is_type_known ) ;
522+ let resolved_ty = answers
523+ . get_idx ( * annot_idx )
524+ . and_then ( |awt| awt . annotation . ty . as_ref ( ) . map ( Self :: is_type_known ) ) ;
525+ let slots = Self :: classify_slot (
526+ resolved_ty . or ( annotation_text . is_some ( ) . then_some ( true ) ) ,
527+ ) ;
528528 variables. push ( Variable {
529529 name : qualified_name,
530530 annotation : annotation_text,
@@ -711,15 +711,13 @@ impl ReportArgs {
711711 }
712712 _ => None ,
713713 } ) ;
714- let is_type_known = annotation_text. is_some ( )
715- && annotation_idx
716- . and_then ( |idx| {
717- answers
718- . get_idx ( idx)
719- . and_then ( |awt| awt. annotation . ty . as_ref ( ) . map ( Self :: is_type_known) )
720- } )
721- . unwrap_or ( false ) ;
722- let slots = Self :: classify_slot ( annotation_text. is_some ( ) , is_type_known) ;
714+ let resolved_ty = annotation_idx. and_then ( |idx| {
715+ answers
716+ . get_idx ( idx)
717+ . and_then ( |awt| awt. annotation . ty . as_ref ( ) . map ( Self :: is_type_known) )
718+ } ) ;
719+ let slots =
720+ Self :: classify_slot ( resolved_ty. or ( annotation_text. is_some ( ) . then_some ( true ) ) ) ;
723721
724722 attrs. push ( Variable {
725723 name : qualified_name,
@@ -841,7 +839,7 @@ impl ReportArgs {
841839 let return_slot = if has_implicit_return {
842840 SlotCounts :: default ( )
843841 } else {
844- Self :: classify_slot ( return_annotation. is_some ( ) , is_return_type_known)
842+ Self :: classify_slot ( return_annotation. is_some ( ) . then_some ( is_return_type_known) )
845843 } ;
846844 let mut func_slots = return_slot;
847845 let mut n_params = 0usize ;
@@ -882,8 +880,9 @@ impl ReportArgs {
882880 }
883881
884882 if !is_self && !is_implicit_param {
885- let param_slot =
886- Self :: classify_slot ( param_annotation. is_some ( ) , is_param_type_known) ;
883+ let param_slot = Self :: classify_slot (
884+ param_annotation. is_some ( ) . then_some ( is_param_type_known) ,
885+ ) ;
887886 func_slots = func_slots. merge ( param_slot) ;
888887 n_params += 1 ;
889888 }
@@ -2255,6 +2254,36 @@ mod tests {
22552254 compare_snapshot ( "partial_any.expected.json" , & report) ;
22562255 }
22572256
2257+ /// Bare `Final` should be typed, not `Any`
2258+ #[ test]
2259+ fn test_report_bare_final ( ) {
2260+ let report = build_module_report_for_test ( "bare_final.py" ) ;
2261+ let attr_slots = |name : & str | {
2262+ report
2263+ . symbol_reports
2264+ . iter ( )
2265+ . find_map ( |s| match s {
2266+ SymbolReport :: Attr { name : n, slots, .. } if n == name => Some ( * slots) ,
2267+ _ => None ,
2268+ } )
2269+ . unwrap_or_else ( || panic ! ( "no attr symbol named {name}" ) )
2270+ } ;
2271+
2272+ for name in [
2273+ "test.golden" ,
2274+ "test.golden_ratio" ,
2275+ "test.pi" ,
2276+ "test.name" ,
2277+ "test.Constants.rate" ,
2278+ "test.Constants.count" ,
2279+ ] {
2280+ let slots = attr_slots ( name) ;
2281+ assert_eq ! ( slots. n_typable, 1 , "{name} should have 1 typable slot" ) ;
2282+ assert_eq ! ( slots. n_typed, 1 , "{name} should be typed" ) ;
2283+ assert_eq ! ( slots. n_any, 0 , "{name} should not be any" ) ;
2284+ }
2285+ }
2286+
22582287 #[ test]
22592288 fn test_full_report_has_schema_version ( ) {
22602289 let report = build_module_report_for_test ( "functions.py" ) ;
0 commit comments