@@ -2175,7 +2175,7 @@ impl<'warnings> Compiler<'warnings> {
21752175 /// On success, returns the saved CompileContext to pass to exit_annotation_scope.
21762176 fn enter_annotation_scope(
21772177 &mut self,
2178- _func_name : &str,
2178+ func_name : &str,
21792179 loc: TextRange,
21802180 ) -> CompileResult<Option<CompileContext>> {
21812181 if !self.push_annotation_symbol_table() {
@@ -2200,6 +2200,12 @@ impl<'warnings> Compiler<'warnings> {
22002200 lineno.to_u32(),
22012201 )?;
22022202
2203+ // enter_scope() qualified the scope by the enclosing scope only; redo it
2204+ // now that the annotated function is known. Only signature annotations
2205+ // get this treatment - deferred class and module annotations are
2206+ // compiled inside the scope they belong to and are already qualified.
2207+ self.set_annotation_qualname(func_name);
2208+
22032209 // Keep the internal ".format" name; exit_annotation_scope()
22042210 // renames it to "format" on the final code object.
22052211 self.current_code_info()
@@ -2605,11 +2611,24 @@ impl<'warnings> Compiler<'warnings> {
26052611 /// Set the qualified name for the current code object
26062612 // = compiler_set_qualname
26072613 fn set_qualname(&mut self) -> String {
2608- let qualname = self.make_qualname();
2614+ self.set_qualname_for_function(None)
2615+ }
2616+
2617+ /// Set the qualname of an annotation scope, qualified by the function whose
2618+ /// signature it annotates. CPython records that name on the annotation
2619+ /// block's symbol table entry (`ste_function_name`) and folds it into the
2620+ /// qualname, so `f`'s annotation scope is named `f.__annotate__`.
2621+ fn set_annotation_qualname(&mut self, function_name: &str) {
2622+ self.set_qualname_for_function(Some(function_name));
2623+ }
2624+
2625+ fn set_qualname_for_function(&mut self, function_name: Option<&str>) -> String {
2626+ let qualname = self.make_qualname(function_name);
26092627 self.current_code_info().metadata.qualname = Some(qualname.clone());
26102628 qualname
26112629 }
2612- fn make_qualname(&mut self) -> String {
2630+
2631+ fn make_qualname(&mut self, function_name: Option<&str>) -> String {
26132632 let stack_size = self.code_stack.len();
26142633 assert!(stack_size >= 1);
26152634
@@ -2693,10 +2712,10 @@ impl<'warnings> Compiler<'warnings> {
26932712 }
26942713 }
26952714
2696- // Build the qualified name
2697- if force_global {
2715+ // Build the prefix the current name is qualified by, if any
2716+ let base = if force_global {
26982717 // For global symbols, qualname is just the name
2699- current_obj_name
2718+ None
27002719 } else {
27012720 // Check parent scope type
27022721 let parent_obj_name = &parent.metadata.name;
@@ -2709,23 +2728,32 @@ impl<'warnings> Compiler<'warnings> {
27092728 )
27102729 );
27112730
2731+ // Use parent's qualname if available, otherwise use parent_obj_name
2732+ let parent_qualname = parent.metadata.qualname.as_ref().unwrap_or(parent_obj_name);
2733+
27122734 if is_function_parent {
27132735 // For functions, append .<locals> to parent qualname
2714- // Use parent's qualname if available, otherwise use parent_obj_name
2715- let parent_qualname = parent.metadata.qualname.as_ref().unwrap_or(parent_obj_name);
2716- format!("{parent_qualname}.<locals>.{current_obj_name}")
2736+ Some(format!("{parent_qualname}.<locals>"))
2737+ } else if parent_qualname == "<module>" {
2738+ // Module level, nothing to qualify by
2739+ None
27172740 } else {
27182741 // For classes and other scopes, use parent's qualname directly
2719- // Use parent's qualname if available, otherwise use parent_obj_name
2720- let parent_qualname = parent.metadata.qualname.as_ref().unwrap_or(parent_obj_name);
2721- if parent_qualname == "<module>" {
2722- // Module level, just use the name
2723- current_obj_name
2724- } else {
2725- // Concatenate parent qualname with current name
2726- format!("{parent_qualname}.{current_obj_name}")
2727- }
2742+ Some(parent_qualname.clone())
27282743 }
2744+ };
2745+
2746+ // An annotation scope is compiled in the scope enclosing the function it
2747+ // annotates, so the function itself is missing from the prefix above.
2748+ let base = match (base, function_name) {
2749+ (Some(base), Some(function_name)) => Some(format!("{base}.{function_name}")),
2750+ (None, Some(function_name)) => Some(function_name.to_owned()),
2751+ (base, None) => base,
2752+ };
2753+
2754+ match base {
2755+ Some(base) => format!("{base}.{current_obj_name}"),
2756+ None => current_obj_name,
27292757 }
27302758 }
27312759
0 commit comments