1use crate::{
11 IndexMap, IndexSet,
12 error::{CodegenError, CodegenErrorType},
13};
14use alloc::{borrow::Cow, fmt};
15use bitflags::bitflags;
16use ruff_python_ast::{self as ast, name::Name};
17use ruff_text_size::{Ranged, TextRange};
18use rustpython_compiler_core::{PositionEncoding, SourceFile, SourceLocation};
19
20const DEFAULT_RECURSION_LIMIT: usize = 1000;
21const RECURSION_ERROR: &str = "maximum recursion depth exceeded during compilation";
22
23#[derive(Clone)]
25pub struct SymbolTable {
26 pub name: Name,
28
29 pub typ: CompilerScope,
31
32 pub line_number: u32,
34
35 pub block_index: usize,
37
38 pub is_nested: bool,
40
41 pub is_method: bool,
43
44 pub symbols: IndexMap<Name, Symbol>,
46
47 pub sub_tables: Vec<Self>,
50
51 pub hidden_annotation_blocks: Vec<Self>,
54
55 pub next_hidden_annotation_block: usize,
57
58 pub inlined_comprehension_blocks: Vec<Self>,
61
62 pub next_inlined_comprehension_block: usize,
64
65 pub next_sub_table: usize,
67
68 pub varnames: Vec<Name>,
70
71 pub needs_class_closure: bool,
73
74 pub needs_classdict: bool,
76
77 pub can_see_class_scope: bool,
79
80 pub is_generator: bool,
82
83 pub is_coroutine: bool,
85
86 pub returns_value: bool,
88
89 pub annotations_used: bool,
91
92 pub scope_info: Option<&'static str>,
94
95 pub in_unevaluated_annotation: bool,
98
99 pub comp_inlined: bool,
102
103 pub annotation_block: Option<Box<Self>>,
106
107 pub skip_enclosing_function_scope: bool,
111
112 pub has_conditional_annotations: bool,
115
116 pub future_annotations: bool,
118
119 pub mangled_names: Option<IndexSet<Name>>,
123}
124
125impl SymbolTable {
126 fn new(
127 name: Name,
128 typ: CompilerScope,
129 line_number: u32,
130 is_nested: bool,
131 block_index: usize,
132 ) -> Self {
133 Self {
134 name,
135 typ,
136 line_number,
137 block_index,
138 is_nested,
139 is_method: false,
140 symbols: IndexMap::default(),
141 sub_tables: vec![],
142 hidden_annotation_blocks: vec![],
143 next_hidden_annotation_block: 0,
144 inlined_comprehension_blocks: vec![],
145 next_inlined_comprehension_block: 0,
146 next_sub_table: 0,
147 varnames: Vec::new(),
148 needs_class_closure: false,
149 needs_classdict: false,
150 can_see_class_scope: false,
151 is_generator: false,
152 is_coroutine: false,
153 returns_value: false,
154 annotations_used: false,
155 scope_info: None,
156 in_unevaluated_annotation: false,
157 comp_inlined: false,
158 annotation_block: None,
159 skip_enclosing_function_scope: false,
160 has_conditional_annotations: false,
161 future_annotations: false,
162 mangled_names: None,
163 }
164 }
165
166 fn add_format_parameter(&mut self) {
167 let name = Name::new_static(".format");
168 let symbol = self
169 .symbols
170 .entry(name.clone())
171 .or_insert_with(|| Symbol::new(name.clone()));
172 symbol
173 .flags
174 .insert(SymbolFlags::DEF_PARAM | SymbolFlags::USE);
175 if !self.varnames.contains(&name) {
176 self.varnames.push(name);
177 }
178 }
179
180 pub fn scan_program(
181 program: &ast::ModModule,
182 source_file: SourceFile,
183 ) -> SymbolTableResult<Self> {
184 Self::scan_program_with_options(program, source_file, false, false, DEFAULT_RECURSION_LIMIT)
185 }
186
187 pub fn scan_program_with_options(
188 program: &ast::ModModule,
189 source_file: SourceFile,
190 allow_top_level_await: bool,
191 future_annotations: bool,
192 recursion_limit: usize,
193 ) -> SymbolTableResult<Self> {
194 let mut builder = SymbolTableBuilder::new(source_file);
195 builder.allow_top_level_await = allow_top_level_await;
196 builder.recursion_limit = recursion_limit;
197 builder.future_annotations = future_annotations
198 || SymbolTableBuilder::future_annotations_from_module_body(program.body.as_ref());
199 builder.scan_statements(program.body.as_ref())?;
200 builder.finish()
201 }
202
203 pub fn scan_expr(
204 expr: &ast::ModExpression,
205 source_file: SourceFile,
206 ) -> SymbolTableResult<Self> {
207 Self::scan_expr_with_options(expr, source_file, false, false, DEFAULT_RECURSION_LIMIT)
208 }
209
210 pub fn scan_expr_with_options(
211 expr: &ast::ModExpression,
212 source_file: SourceFile,
213 allow_top_level_await: bool,
214 future_annotations: bool,
215 recursion_limit: usize,
216 ) -> SymbolTableResult<Self> {
217 let mut builder = SymbolTableBuilder::new(source_file);
218 builder.allow_top_level_await = allow_top_level_await;
219 builder.recursion_limit = recursion_limit;
220 builder.future_annotations = future_annotations;
221 builder.scan_expression(expr.body.as_ref(), ExpressionContext::Load)?;
222 builder.finish()
223 }
224
225 #[must_use]
226 pub fn lookup(&self, name: &Name) -> Option<&Symbol> {
227 self.symbols.get(name)
228 }
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum CompilerScope {
233 Module,
234 Class,
235 Function,
236 AsyncFunction,
237 Lambda,
238 Comprehension,
239 TypeParams,
240 Annotation,
242 TypeAlias,
243 TypeVariable,
244}
245
246impl fmt::Display for CompilerScope {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 Self::Module => write!(f, "module"),
250 Self::Class => write!(f, "class"),
251 Self::Function => write!(f, "function"),
252 Self::AsyncFunction => write!(f, "async function"),
253 Self::Lambda => write!(f, "lambda"),
254 Self::Comprehension => write!(f, "comprehension"),
255 Self::TypeParams => write!(f, "type parameter"),
256 Self::Annotation => write!(f, "annotation"),
257 Self::TypeAlias => write!(f, "type alias"),
258 Self::TypeVariable => write!(f, "TypeVar bound"),
259 }
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub enum SymbolScope {
267 Unknown,
268 Local,
269 GlobalExplicit,
270 GlobalImplicit,
271 Free,
272 Cell,
273}
274
275impl SymbolScope {
276 #[must_use]
281 pub const fn as_i32(&self) -> i32 {
282 match self {
283 Self::Unknown => 0,
284 Self::Local => 1,
285 Self::GlobalExplicit => 2,
286 Self::GlobalImplicit => 3,
287 Self::Free => 4,
288 Self::Cell => 5,
289 }
290 }
291}
292
293impl From<SymbolScope> for i32 {
294 fn from(scope: SymbolScope) -> Self {
295 scope.as_i32()
296 }
297}
298
299bitflags! {
300 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
301 pub struct SymbolFlags: u16 {
302 const DEF_GLOBAL = 1;
303 const DEF_LOCAL = 2;
304 const DEF_PARAM = 2 << 1;
305 const DEF_NONLOCAL = 2 << 2;
306 const USE = 2 << 3;
307 const DEF_FREE_CLASS = 2 << 5;
316 const DEF_IMPORT = 2 << 6;
317 const DEF_ANNOT = 2 << 7;
318 const DEF_COMP_ITER = 2 << 8;
319 const DEF_TYPE_PARAM = 2 << 9;
320 const DEF_COMP_CELL = 2 << 10;
321 const DEF_BOUND = (
322 Self::DEF_LOCAL.bits()
323 | Self::DEF_PARAM.bits()
324 | Self::DEF_IMPORT.bits()
325 | Self::DEF_TYPE_PARAM.bits()
326 );
327 }
328}
329
330#[derive(Debug, Clone)]
333pub struct Symbol {
334 pub name: Name,
335 pub scope: SymbolScope,
336 pub flags: SymbolFlags,
337 pub location: Option<SourceLocation>,
338 pub end_location: Option<SourceLocation>,
339}
340
341impl Symbol {
342 fn new(name: Name) -> Self {
343 Self {
344 name,
345 scope: SymbolScope::Unknown,
347 flags: SymbolFlags::empty(),
348 location: None,
349 end_location: None,
350 }
351 }
352
353 #[must_use]
354 pub const fn is_global(&self) -> bool {
355 matches!(
356 self.scope,
357 SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit
358 )
359 }
360
361 #[must_use]
362 pub const fn is_local(&self) -> bool {
363 matches!(self.scope, SymbolScope::Local | SymbolScope::Cell)
364 }
365
366 #[must_use]
367 pub const fn is_bound(&self) -> bool {
368 self.flags.intersects(SymbolFlags::DEF_BOUND)
369 }
370}
371
372#[derive(Debug)]
373pub struct SymbolTableError {
374 error: String,
375 location: Option<SourceLocation>,
376 end_location: Option<SourceLocation>,
377}
378
379impl SymbolTableError {
380 #[must_use]
381 pub fn into_codegen_error(self, source_path: String) -> CodegenError {
382 let error = if self.error == RECURSION_ERROR {
383 CodegenErrorType::RecursionError
384 } else {
385 CodegenErrorType::SyntaxError(self.error)
386 };
387 CodegenError {
388 location: self.location,
389 end_location: self.end_location,
390 error,
391 source_path,
392 }
393 }
394}
395
396type SymbolTableResult<T = ()> = Result<T, SymbolTableError>;
397
398impl core::fmt::Debug for SymbolTable {
399 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
400 write!(
401 f,
402 "SymbolTable({:?} symbols, {:?} sub scopes)",
403 self.symbols.len(),
404 self.sub_tables.len()
405 )
406 }
407}
408
409fn analyze_symbol_table(symbol_table: &mut SymbolTable) -> SymbolTableResult {
413 let mut analyzer = SymbolTableAnalyzer::default();
414 let _newfree = analyzer.analyze_symbol_table(symbol_table, None)?;
417 Ok(())
418}
419
420fn drop_class_free(symbol_table: &mut SymbolTable, newfree: &mut IndexSet<Name>) {
429 if newfree.shift_remove("__class__") {
432 symbol_table.needs_class_closure = true;
433 }
434
435 if newfree.shift_remove("__classdict__") {
437 symbol_table.needs_classdict = true;
438 }
439
440 if newfree.shift_remove("__conditional_annotations__") {
443 symbol_table.has_conditional_annotations = true;
444 }
445}
446
447fn inline_comprehension(
450 parent_symbols: &mut SymbolMap,
451 comp: &SymbolTable,
452 comp_free: &mut IndexSet<Name>,
453 inlined_cells: &mut IndexSet<Name>,
454 parent_type: CompilerScope,
455) -> IndexSet<Name> {
456 let mut removed_class_implicits = IndexSet::default();
457 for (name, sub_symbol) in &comp.symbols {
458 if sub_symbol.flags.contains(SymbolFlags::DEF_PARAM) {
460 continue;
461 }
462
463 if sub_symbol.scope == SymbolScope::Cell
465 || sub_symbol.flags.contains(SymbolFlags::DEF_COMP_CELL)
466 {
467 inlined_cells.insert(name.clone());
468 }
469
470 let scope = if sub_symbol.scope == SymbolScope::Free
476 && parent_type == CompilerScope::Class
477 && matches!(
478 name.as_str(),
479 "__class__" | "__classdict__" | "__conditional_annotations__"
480 ) {
481 let is_free_in_child = comp.sub_tables.iter().any(|child| {
482 child
483 .symbols
484 .get(name)
485 .is_some_and(|s| s.scope == SymbolScope::Free)
486 });
487 if !is_free_in_child {
488 comp_free.swap_remove(name);
489 }
490 removed_class_implicits.insert(name.clone());
491 SymbolScope::GlobalImplicit
492 } else {
493 sub_symbol.scope
494 };
495
496 if let Some(existing) = parent_symbols.get_mut(name) {
497 if existing
501 .flags
502 .difference(SymbolFlags::DEF_FREE_CLASS)
503 .is_empty()
504 {
505 existing.flags.insert(sub_symbol.flags);
506 existing.scope = scope;
507 }
508 if existing.is_bound() && parent_type != CompilerScope::Class {
510 let is_free_in_child = comp.sub_tables.iter().any(|child| {
512 child
513 .symbols
514 .get(name)
515 .is_some_and(|s| s.scope == SymbolScope::Free)
516 });
517 if !is_free_in_child {
518 comp_free.swap_remove(name);
519 }
520 }
521 } else {
522 let mut symbol = sub_symbol.clone();
526 symbol.scope = scope;
527 parent_symbols.insert(name.clone(), symbol);
528 }
529 }
530 removed_class_implicits
531}
532
533type SymbolMap = IndexMap<Name, Symbol>;
534
535mod stack {
536 use alloc::vec::Vec;
537 use core::ptr::NonNull;
538 pub(super) struct StackStack<T> {
539 v: Vec<NonNull<T>>,
540 }
541 impl<T> Default for StackStack<T> {
542 fn default() -> Self {
543 Self { v: Vec::new() }
544 }
545 }
546 impl<T> StackStack<T> {
547 #[cfg(feature = "std")]
550 pub(super) fn with_append<F, R>(&mut self, x: &mut T, f: F) -> R
551 where
552 F: FnOnce(&mut Self) -> R,
553 {
554 self.v.push(x.into());
555 let res = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self)));
556 self.v.pop();
557 res.unwrap_or_else(|x| std::panic::resume_unwind(x))
558 }
559
560 #[cfg(not(feature = "std"))]
565 pub fn with_append<F, R>(&mut self, x: &mut T, f: F) -> R
566 where
567 F: FnOnce(&mut Self) -> R,
568 {
569 self.v.push(x.into());
570 let result = f(self);
571 self.v.pop();
572 result
573 }
574
575 pub(super) fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + '_ {
576 self.as_ref().iter().copied()
577 }
578 pub(super) fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> + '_ {
579 self.as_mut().iter_mut().map(|x| &mut **x)
580 }
581 pub(super) fn len(&self) -> usize {
582 self.v.len()
583 }
584 pub(super) fn is_empty(&self) -> bool {
585 self.len() == 0
586 }
587
588 pub(super) fn as_ref(&self) -> &[&T] {
589 unsafe { &*(self.v.as_slice() as *const [NonNull<T>] as *const [&T]) }
590 }
591
592 pub(super) fn as_mut(&mut self) -> &mut [&mut T] {
593 unsafe { &mut *(self.v.as_mut_slice() as *mut [NonNull<T>] as *mut [&mut T]) }
594 }
595 }
596}
597use stack::StackStack;
598
599#[derive(Default)]
603#[repr(transparent)]
604struct SymbolTableAnalyzer {
605 tables: StackStack<(SymbolMap, CompilerScope, bool)>,
606}
607
608impl SymbolTableAnalyzer {
609 fn analyze_symbol_table(
613 &mut self,
614 symbol_table: &mut SymbolTable,
615 class_entry: Option<&SymbolMap>,
616 ) -> SymbolTableResult<IndexSet<Name>> {
617 let symbols = core::mem::take(&mut symbol_table.symbols);
618 let sub_tables = &mut *symbol_table.sub_tables;
619
620 let annotation_block = &mut symbol_table.annotation_block;
621
622 let is_class = symbol_table.typ == CompilerScope::Class;
624
625 let needs_class_symbols = (is_class
627 && (sub_tables.iter().any(|st| st.can_see_class_scope)
628 || annotation_block
629 .as_ref()
630 .is_some_and(|b| b.can_see_class_scope)))
631 || (!is_class
632 && class_entry.is_some()
633 && sub_tables.iter().any(|st| st.can_see_class_scope));
634
635 let class_symbols_clone = if is_class && needs_class_symbols {
636 Some(symbols.clone())
637 } else {
638 None
639 };
640
641 let mut child_frees: Vec<(IndexSet<Name>, bool)> = Vec::new();
645 let mut annotation_free: Option<IndexSet<Name>> = None;
646
647 let mut info = (
648 symbols,
649 symbol_table.typ,
650 symbol_table.skip_enclosing_function_scope,
651 );
652 let class_scope_entry = if is_class {
653 class_symbols_clone.as_ref()
654 } else {
655 class_entry
656 };
657 self.tables.with_append(&mut info, |list| {
658 let inner_scope = unsafe { &mut *(list as *mut _ as *mut Self) };
659 for sub_table in sub_tables.iter_mut() {
660 let child_class_entry = sub_table
661 .can_see_class_scope
662 .then_some(class_scope_entry)
663 .flatten();
664 let child_free = inner_scope.analyze_symbol_table(sub_table, child_class_entry)?;
665 child_frees.push((child_free, sub_table.comp_inlined));
666 }
667 if let Some(annotation_table) = annotation_block {
669 let ann_class_entry = annotation_table
670 .can_see_class_scope
671 .then_some(class_scope_entry)
672 .flatten();
673 let child_free =
674 inner_scope.analyze_symbol_table(annotation_table, ann_class_entry)?;
675 annotation_free = Some(child_free);
676 }
677 Ok(())
678 })?;
679
680 symbol_table.symbols = info.0;
681
682 let mut inlined_cells: IndexSet<Name> = IndexSet::default();
685 let mut newfree = IndexSet::default();
686 for (idx, (mut child_free, is_inlined)) in child_frees.into_iter().enumerate() {
687 if is_inlined {
688 let removed_class_implicit = inline_comprehension(
689 &mut symbol_table.symbols,
690 &symbol_table.sub_tables[idx],
691 &mut child_free,
692 &mut inlined_cells,
693 symbol_table.typ,
694 );
695 for name in removed_class_implicit {
696 symbol_table.sub_tables[idx]
697 .symbols
698 .shift_remove(name.as_str());
699 }
700 }
701 newfree.extend(child_free);
702 }
703 if let Some(ann_free) = annotation_free
704 && symbol_table.typ == CompilerScope::Class
705 {
706 newfree.extend(ann_free);
711 }
712
713 let mut inlined_blocks = Vec::new();
714 let mut idx = 0;
715 while idx < symbol_table.sub_tables.len() {
716 if symbol_table.sub_tables[idx].comp_inlined {
717 let comp = symbol_table.sub_tables.remove(idx);
718 let nested_inlined_blocks = comp.inlined_comprehension_blocks.clone();
719 let children = comp.sub_tables.clone();
720 let inserted = children.len();
721 inlined_blocks.push(comp);
722 inlined_blocks.extend(nested_inlined_blocks);
723 symbol_table.sub_tables.splice(idx..idx, children);
724 idx += inserted;
725 } else {
726 idx += 1;
727 }
728 }
729 symbol_table
730 .inlined_comprehension_blocks
731 .extend(inlined_blocks);
732
733 let sub_tables = &*symbol_table.sub_tables;
734
735 for symbol in symbol_table.symbols.values_mut() {
736 if inlined_cells.contains(&symbol.name) {
737 symbol.flags.insert(SymbolFlags::DEF_COMP_CELL);
738 }
739 }
740
741 let function_like_scope = SymbolTableBuilder::is_function_like_scope(symbol_table.typ);
743 for symbol in symbol_table.symbols.values_mut() {
744 self.analyze_symbol(
745 symbol,
746 symbol_table.typ,
747 symbol_table.skip_enclosing_function_scope,
748 sub_tables,
749 class_entry,
750 )?;
751
752 if function_like_scope && symbol.scope == SymbolScope::Cell {
756 newfree.shift_remove(symbol.name.as_str());
757 }
758
759 if symbol.scope == SymbolScope::Free
761 || symbol.flags.contains(SymbolFlags::DEF_FREE_CLASS)
762 {
763 newfree.insert(symbol.name.clone());
764 }
765 }
766
767 for symbol in symbol_table.symbols.values_mut() {
772 if inlined_cells.contains(&symbol.name)
773 && function_like_scope
774 && symbol.scope == SymbolScope::Local
775 {
776 symbol.scope = SymbolScope::Cell;
777 }
778 }
779
780 if symbol_table.typ == CompilerScope::Class {
782 drop_class_free(symbol_table, &mut newfree);
783 }
784
785 if symbol_table.typ == CompilerScope::Class || symbol_table.can_see_class_scope {
793 for name in &newfree {
794 if let Some(symbol) = symbol_table.symbols.get_mut(name) {
795 symbol.flags.insert(SymbolFlags::DEF_FREE_CLASS);
796 }
797 }
798 }
799
800 Ok(newfree)
801 }
802
803 fn analyze_symbol(
804 &mut self,
805 symbol: &mut Symbol,
806 st_typ: CompilerScope,
807 skip_enclosing_function_scope: bool,
808 sub_tables: &[SymbolTable],
809 class_entry: Option<&SymbolMap>,
810 ) -> SymbolTableResult {
811 if symbol
812 .flags
813 .contains(SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL)
814 {
815 return Err(SymbolTableError {
816 error: format!("name '{}' is nonlocal and global", symbol.name),
817 location: symbol.location,
818 end_location: symbol.end_location,
819 });
820 }
821 match symbol.scope {
822 SymbolScope::Free if symbol.flags.contains(SymbolFlags::DEF_NONLOCAL) => {
825 if !self.tables.as_ref().is_empty() {
826 let scope_depth = self.tables.as_ref().len();
827 if scope_depth < 2
829 || self.found_in_outer_scope(
830 &symbol.name,
831 st_typ,
832 skip_enclosing_function_scope,
833 ) != Some(SymbolScope::Free)
834 {
835 return Err(SymbolTableError {
836 error: format!("no binding for nonlocal '{}' found", symbol.name),
837 location: symbol.location,
838 end_location: symbol.end_location,
839 });
840 }
841 for (symbols, _typ, _skip) in self.tables.iter().rev() {
843 if let Some(sym) = symbols.get(&symbol.name) {
844 if sym.flags.contains(SymbolFlags::DEF_TYPE_PARAM) {
845 return Err(SymbolTableError {
846 error: format!(
847 "nonlocal binding not allowed for type parameter '{}'",
848 symbol.name
849 ),
850 location: symbol.location,
851 end_location: symbol.end_location,
852 });
853 }
854 if sym.is_bound() {
855 break;
856 }
857 }
858 }
859 } else {
860 return Err(SymbolTableError {
861 error: format!(
862 "nonlocal {} defined at place without an enclosing scope",
863 symbol.name
864 ),
865 location: symbol.location,
866 end_location: symbol.end_location,
867 });
868 }
869 }
870 SymbolScope::Free => {}
871 SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit => {}
872 SymbolScope::Local | SymbolScope::Cell => {}
873 SymbolScope::Unknown => {
874 let scope = if symbol.is_bound() {
876 if symbol.flags.contains(SymbolFlags::DEF_COMP_CELL)
877 && matches!(st_typ, CompilerScope::Module | CompilerScope::Class)
878 {
879 SymbolScope::Local
885 } else {
886 self.found_in_inner_scope(sub_tables, &symbol.name, st_typ)
887 .unwrap_or(SymbolScope::Local)
888 }
889 } else if let Some(scope) = class_entry
890 .and_then(|class_symbols| class_symbols.get(&symbol.name))
891 .and_then(|class_sym| {
892 if class_sym.flags.contains(SymbolFlags::DEF_GLOBAL) {
893 Some(SymbolScope::GlobalExplicit)
894 } else if class_sym.is_bound() && class_sym.scope != SymbolScope::Free {
895 Some(SymbolScope::GlobalImplicit)
898 } else {
899 None
900 }
901 })
902 {
903 scope
904 } else if let Some(scope) =
905 self.found_in_outer_scope(&symbol.name, st_typ, skip_enclosing_function_scope)
906 {
907 scope
909 } else {
910 SymbolScope::GlobalImplicit
912 };
913 symbol.scope = scope;
914 }
915 }
916 Ok(())
917 }
918
919 fn found_in_outer_scope(
920 &mut self,
921 name: &Name,
922 st_typ: CompilerScope,
923 skip_enclosing_function_scope: bool,
924 ) -> Option<SymbolScope> {
925 let mut decl_depth = None;
926 for (i, (symbols, typ, _skip)) in self.tables.iter().rev().enumerate() {
927 if matches!(typ, CompilerScope::Module)
928 || matches!(typ, CompilerScope::Class if name != "__class__" && name != "__classdict__" && name != "__conditional_annotations__")
929 {
930 continue;
931 }
932
933 if st_typ == CompilerScope::Annotation
938 && skip_enclosing_function_scope
939 && i == 0
940 && matches!(
941 typ,
942 CompilerScope::Function | CompilerScope::AsyncFunction | CompilerScope::Lambda
943 )
944 {
945 continue;
946 }
947
948 if (name == "__class__" || name == "__classdict__")
951 && matches!(typ, CompilerScope::Class)
952 {
953 decl_depth = Some(i);
954 break;
955 }
956
957 if name == "__conditional_annotations__" && matches!(typ, CompilerScope::Class) {
960 decl_depth = Some(i);
961 break;
962 }
963
964 if let Some(sym) = symbols.get(name) {
965 match sym.scope {
966 SymbolScope::GlobalExplicit => return Some(SymbolScope::GlobalImplicit),
969 SymbolScope::GlobalImplicit => {}
970 _ => {
971 if sym.is_bound() {
972 decl_depth = Some(i);
973 break;
974 }
975 }
976 }
977 }
978 }
979
980 if let Some(decl_depth) = decl_depth {
981 let is_class_implicit =
987 name == "__classdict__" || name == "__conditional_annotations__";
988
989 for (table, typ, _skip) in self.tables.iter_mut().rev().take(decl_depth) {
990 if let CompilerScope::Class = typ {
991 if let Some(free_class) = table.get_mut(name) {
992 free_class.flags.insert(SymbolFlags::DEF_FREE_CLASS)
993 } else {
994 let mut symbol = Symbol::new(name.clone());
995 symbol.flags.insert(SymbolFlags::DEF_FREE_CLASS);
996 symbol.scope = SymbolScope::Free;
997 table.insert(name.to_owned(), symbol);
998 }
999 } else if is_class_implicit
1000 && matches!(
1001 typ,
1002 CompilerScope::Function
1003 | CompilerScope::AsyncFunction
1004 | CompilerScope::Lambda
1005 )
1006 {
1007 } else if !table.contains_key(name) {
1010 let mut symbol = Symbol::new(name.clone());
1011 symbol.scope = SymbolScope::Free;
1012 table.insert(name.to_owned(), symbol);
1013 }
1014 }
1015 }
1016
1017 decl_depth.map(|_| SymbolScope::Free)
1018 }
1019
1020 fn found_in_inner_scope(
1021 &self,
1022 sub_tables: &[SymbolTable],
1023 name: &Name,
1024 st_typ: CompilerScope,
1025 ) -> Option<SymbolScope> {
1026 sub_tables.iter().find_map(|st| {
1027 if st.comp_inlined {
1030 return self.found_in_inner_scope(&st.sub_tables, name, st_typ);
1031 }
1032 let sym = st.symbols.get(name)?;
1033 if sym.scope == SymbolScope::Free
1034 || (sym.flags.contains(SymbolFlags::DEF_FREE_CLASS)
1035 && !matches!(st_typ, CompilerScope::Module))
1036 {
1037 if st_typ == CompilerScope::Class {
1038 None
1039 } else {
1040 Some(SymbolScope::Cell)
1041 }
1042 } else if sym.scope == SymbolScope::GlobalExplicit && self.tables.is_empty() {
1043 Some(SymbolScope::GlobalExplicit)
1046 } else {
1047 None
1048 }
1049 })
1050 }
1051}
1052
1053#[derive(Clone, Copy, Debug)]
1054enum SymbolUsage {
1055 Global,
1056 Nonlocal,
1057 Used,
1058 Assigned,
1059 Imported,
1060 AnnotationAssigned,
1061 Parameter,
1062 Iter,
1063 TypeParam,
1064}
1065
1066struct SymbolTableBuilder {
1067 class_name: Option<Name>,
1068 tables: Vec<SymbolTable>,
1070 future_annotations: bool,
1071 allow_top_level_await: bool,
1072 source_file: SourceFile,
1073 current_varnames: Vec<Name>,
1075 varnames_stack: Vec<Vec<Name>>,
1077 in_iter_def_exp: bool,
1079 comprehension_yield_context: Option<&'static str>,
1082 in_conditional_block: bool,
1084 recursion_depth: usize,
1086 recursion_limit: usize,
1087 next_block_index: usize,
1088 done_with_future_stmts: DoneWithFuture,
1089}
1090
1091enum DoneWithFuture {
1094 No,
1095 DoneWithDoc,
1096 Yes,
1097}
1098
1099#[derive(Copy, Clone, PartialEq)]
1104enum ExpressionContext {
1105 Load,
1106 Store,
1107 Delete,
1108 Iter,
1109 IterDefinitionExp,
1110}
1111
1112impl SymbolTableBuilder {
1113 fn new(source_file: SourceFile) -> Self {
1114 let mut this = Self {
1115 class_name: None,
1116 tables: vec![],
1117 future_annotations: false,
1118 allow_top_level_await: false,
1119 source_file,
1120 current_varnames: Vec::new(),
1121 varnames_stack: Vec::new(),
1122 in_iter_def_exp: false,
1123 comprehension_yield_context: None,
1124 in_conditional_block: false,
1125 recursion_depth: 0,
1126 recursion_limit: DEFAULT_RECURSION_LIMIT,
1127 next_block_index: 0,
1128 done_with_future_stmts: DoneWithFuture::No,
1129 };
1130 this.enter_scope(&"top".into(), CompilerScope::Module, 0);
1131 this
1132 }
1133
1134 fn is_function_like_scope(typ: CompilerScope) -> bool {
1135 matches!(
1136 typ,
1137 CompilerScope::Function
1138 | CompilerScope::AsyncFunction
1139 | CompilerScope::Lambda
1140 | CompilerScope::Comprehension
1141 | CompilerScope::Annotation
1142 | CompilerScope::TypeAlias
1143 | CompilerScope::TypeVariable
1144 | CompilerScope::TypeParams
1145 )
1146 }
1147
1148 fn future_annotations_from_module_body(body: &[ast::Stmt]) -> bool {
1149 let mut statements = body.iter();
1150 if let Some(ast::Stmt::Expr(ast::StmtExpr { value, .. })) = statements.clone().next()
1151 && is_docstring_expr(value)
1152 {
1153 statements.next();
1154 }
1155 for statement in statements {
1156 match statement {
1157 ast::Stmt::ImportFrom(ast::StmtImportFrom {
1158 module,
1159 names,
1160 level,
1161 ..
1162 }) if *level == 0
1163 && module.as_ref().map(|id| id.as_str()) == Some("__future__") =>
1164 {
1165 if names
1166 .iter()
1167 .any(|future| future.name.as_str() == "annotations")
1168 {
1169 return true;
1170 }
1171 }
1172 _ => return false,
1173 }
1174 }
1175 false
1176 }
1177
1178 fn finish(mut self) -> Result<SymbolTable, SymbolTableError> {
1179 assert_eq!(self.tables.len(), 1);
1180 let mut symbol_table = self.tables.pop().unwrap();
1181 symbol_table.varnames = self.current_varnames;
1183 symbol_table.future_annotations = self.future_annotations;
1185 analyze_symbol_table(&mut symbol_table)?;
1186 Ok(symbol_table)
1187 }
1188
1189 fn enter_scope(&mut self, name: &Name, typ: CompilerScope, line_number: u32) {
1190 let parent = self.tables.last();
1191 let is_nested =
1192 parent.is_some_and(|table| table.is_nested || Self::is_function_like_scope(table.typ));
1193 let is_method = parent.is_some_and(|table| {
1194 table.typ == CompilerScope::Class
1195 && matches!(
1196 typ,
1197 CompilerScope::Function
1198 | CompilerScope::AsyncFunction
1199 | CompilerScope::Lambda
1200 | CompilerScope::Comprehension
1201 )
1202 });
1203 let inherited_mangled_names = self
1205 .tables
1206 .last()
1207 .and_then(|t| t.mangled_names.clone())
1208 .filter(|_| typ != CompilerScope::Class);
1209 let block_index = self.next_block_index;
1210 self.next_block_index += 1;
1211 let mut table = SymbolTable::new(name.to_owned(), typ, line_number, is_nested, block_index);
1212 table.is_method = is_method;
1213 table.future_annotations = self.future_annotations;
1214 table.mangled_names = inherited_mangled_names;
1215 self.tables.push(table);
1216 self.varnames_stack
1218 .push(core::mem::take(&mut self.current_varnames));
1219 }
1220
1221 fn enter_type_param_block(
1222 &mut self,
1223 name: &Name,
1224 range: TextRange,
1225 for_class: bool,
1226 has_defaults: bool,
1227 has_kwdefaults: bool,
1228 ) -> SymbolTableResult {
1229 let in_class = self
1231 .tables
1232 .last()
1233 .is_some_and(|t| t.typ == CompilerScope::Class);
1234
1235 self.enter_scope(
1236 name,
1237 CompilerScope::TypeParams,
1238 self.line_index_start(range),
1239 );
1240
1241 if let Some(table) = self.tables.last_mut() {
1243 table.can_see_class_scope = in_class;
1244 if for_class {
1247 table.mangled_names = Some(IndexSet::default());
1248 }
1249 }
1250
1251 if in_class {
1253 self.register_name(&"__classdict__".into(), SymbolUsage::Used, range)?;
1254 }
1255
1256 if for_class {
1257 self.register_name(&".type_params".into(), SymbolUsage::Assigned, range)?;
1260 self.register_name(&".type_params".into(), SymbolUsage::Used, range)?;
1261 self.register_name(&".generic_base".into(), SymbolUsage::Assigned, range)?;
1262 self.register_name(&".generic_base".into(), SymbolUsage::Used, range)?;
1263 }
1264 if has_defaults {
1265 self.register_name(&".defaults".into(), SymbolUsage::Parameter, range)?;
1266 }
1267 if has_kwdefaults {
1268 self.register_name(&".kwdefaults".into(), SymbolUsage::Parameter, range)?;
1269 }
1270
1271 Ok(())
1272 }
1273
1274 fn leave_scope(&mut self) {
1276 let mut table = self.tables.pop().unwrap();
1277 table.varnames = core::mem::take(&mut self.current_varnames);
1279 self.tables.last_mut().unwrap().sub_tables.push(table);
1280 self.current_varnames = self.varnames_stack.pop().unwrap_or_default();
1282 }
1283
1284 fn discard_scope(&mut self) -> SymbolTable {
1286 let mut table = self.tables.pop().unwrap();
1287 table.varnames = core::mem::take(&mut self.current_varnames);
1288 self.current_varnames = self.varnames_stack.pop().unwrap_or_default();
1289 table
1290 }
1291
1292 fn resolve_future_annotation_names_as_globals(table: &mut SymbolTable) {
1293 for symbol in table.symbols.values_mut() {
1294 if symbol.scope == SymbolScope::Unknown
1295 && symbol.flags.contains(SymbolFlags::USE)
1296 && !symbol.is_bound()
1297 {
1298 symbol.scope = SymbolScope::GlobalImplicit;
1299 }
1300 }
1301 }
1302
1303 fn enter_annotation_scope(
1306 &mut self,
1307 line_number: u32,
1308 include_classdict_with_future: bool,
1309 include_conditional_annotations: bool,
1310 ) {
1311 let (can_see_class_scope, has_conditional, is_nested, needs_annotation_block) = {
1312 let current = self.tables.last().unwrap();
1313 (
1314 current.typ == CompilerScope::Class || current.can_see_class_scope,
1315 current.has_conditional_annotations,
1316 current.is_nested || Self::is_function_like_scope(current.typ),
1317 current.annotation_block.is_none(),
1318 )
1319 };
1320
1321 if needs_annotation_block {
1323 let block_index = self.next_block_index;
1324 self.next_block_index += 1;
1325 let mut annotation_table = SymbolTable::new(
1326 Name::new_static("__annotate__"),
1327 CompilerScope::Annotation,
1328 line_number,
1329 is_nested,
1330 block_index,
1331 );
1332 annotation_table.can_see_class_scope = can_see_class_scope;
1334 annotation_table.skip_enclosing_function_scope = true;
1335 annotation_table.add_format_parameter();
1336 self.tables.last_mut().unwrap().annotation_block = Some(Box::new(annotation_table));
1337 }
1338
1339 let annotation_table = self
1341 .tables
1342 .last_mut()
1343 .unwrap()
1344 .annotation_block
1345 .take()
1346 .unwrap();
1347 self.tables.push(*annotation_table);
1348 self.varnames_stack
1350 .push(core::mem::take(&mut self.current_varnames));
1351 self.current_varnames = self.tables.last().unwrap().varnames.clone();
1352
1353 if can_see_class_scope && (include_classdict_with_future || !self.future_annotations) {
1354 self.add_classdict_freevar();
1355 if include_conditional_annotations && has_conditional {
1358 self.tables.last_mut().unwrap().has_conditional_annotations = true;
1359 }
1360 }
1361 }
1362
1363 fn leave_annotation_scope(&mut self) {
1366 let mut table = self.tables.pop().unwrap();
1367 table.varnames = core::mem::take(&mut self.current_varnames);
1369 if self.future_annotations {
1370 Self::resolve_future_annotation_names_as_globals(&mut table);
1371 }
1372 let parent = self.tables.last_mut().unwrap();
1374 parent.annotation_block = Some(Box::new(table));
1375 self.current_varnames = self.varnames_stack.pop().unwrap_or_default();
1377 }
1378
1379 fn add_classdict_freevar(&mut self) {
1380 let table = self.tables.last_mut().unwrap();
1381 let name = Name::new_static("__classdict__");
1382 let symbol = table
1383 .symbols
1384 .entry(name.clone())
1385 .or_insert_with(|| Symbol::new(name));
1386 symbol.scope = SymbolScope::Free;
1387 symbol
1388 .flags
1389 .insert(SymbolFlags::USE | SymbolFlags::DEF_FREE_CLASS);
1390 }
1391
1392 fn add_format_parameter(&mut self) {
1393 self.tables.last_mut().unwrap().add_format_parameter();
1394 if !self.current_varnames.iter().any(|name| name == ".format") {
1395 self.current_varnames.push(".format".into());
1396 }
1397 }
1398
1399 fn is_in_async_context(&self) -> bool {
1403 for table in self.tables.iter().rev() {
1404 match table.typ {
1405 CompilerScope::AsyncFunction => return true,
1406 CompilerScope::Function
1407 | CompilerScope::Lambda
1408 | CompilerScope::Class
1409 | CompilerScope::Module
1410 | CompilerScope::Annotation
1411 | CompilerScope::TypeAlias
1412 | CompilerScope::TypeVariable
1413 | CompilerScope::TypeParams => return false,
1414 CompilerScope::Comprehension => continue,
1416 }
1417 }
1418 false
1419 }
1420
1421 fn allows_top_level_await(&self) -> bool {
1422 self.allow_top_level_await
1423 && self
1424 .tables
1425 .last()
1426 .is_some_and(|table| table.typ == CompilerScope::Module)
1427 }
1428
1429 fn line_index_start(&self, range: TextRange) -> u32 {
1430 self.source_file
1431 .to_source_code()
1432 .line_index(range.start())
1433 .get() as _
1434 }
1435
1436 fn scan_statements(&mut self, statements: &[ast::Stmt]) -> SymbolTableResult {
1437 for statement in statements {
1438 self.scan_statement(statement)?;
1439 }
1440 Ok(())
1441 }
1442
1443 fn scan_parameters(&mut self, parameters: &[ast::ParameterWithDefault]) -> SymbolTableResult {
1444 for parameter in parameters {
1445 self.scan_parameter(¶meter.parameter)?;
1446 }
1447 Ok(())
1448 }
1449
1450 fn scan_parameter(&mut self, parameter: &ast::Parameter) -> SymbolTableResult {
1451 let table = self.tables.last().unwrap();
1453 if table.symbols.contains_key(parameter.name.as_str()) {
1454 return Err(self.error_ranged(
1455 format!(
1456 "duplicate argument '{}' in function definition",
1457 parameter.name
1458 ),
1459 parameter.name.range,
1460 ));
1461 }
1462
1463 self.register_ident(¶meter.name, SymbolUsage::Parameter)
1464 }
1465
1466 fn scan_ann_assign_annotation(&mut self, annotation: &ast::Expr) -> SymbolTableResult {
1468 self.scan_annotation_inner(annotation, true)
1469 }
1470
1471 fn scan_function_annotations(
1472 &mut self,
1473 parameters: &ast::Parameters,
1474 returns: Option<&ast::Expr>,
1475 line_number: u32,
1476 ) -> SymbolTableResult {
1477 let current = self.tables.last().unwrap();
1478 let can_see_class_scope =
1479 current.typ == CompilerScope::Class || current.can_see_class_scope;
1480 self.enter_scope(
1481 &"__annotate__".into(),
1482 CompilerScope::Annotation,
1483 line_number,
1484 );
1485 self.tables.last_mut().unwrap().can_see_class_scope = can_see_class_scope;
1486 self.add_format_parameter();
1487 if can_see_class_scope {
1488 self.register_name(
1489 &"__classdict__".into(),
1490 SymbolUsage::Used,
1491 TextRange::default(),
1492 )?;
1493 }
1494
1495 let was_in_unevaluated_annotation = self.tables.last().unwrap().in_unevaluated_annotation;
1496 self.tables.last_mut().unwrap().in_unevaluated_annotation = false;
1497
1498 let result = (|| {
1499 for annotation in parameters
1500 .posonlyargs
1501 .iter()
1502 .chain(parameters.args.iter())
1503 .filter_map(|arg| arg.parameter.annotation.as_ref())
1504 {
1505 self.tables.last_mut().unwrap().annotations_used = true;
1506 self.scan_expression(annotation, ExpressionContext::Load)?;
1507 }
1508 if let Some(annotation) = parameters
1509 .vararg
1510 .as_ref()
1511 .and_then(|arg| arg.annotation.as_ref())
1512 {
1513 self.tables.last_mut().unwrap().annotations_used = true;
1514 self.scan_expression(annotation, ExpressionContext::Load)?;
1515 }
1516 if let Some(annotation) = parameters
1517 .kwarg
1518 .as_ref()
1519 .and_then(|arg| arg.annotation.as_ref())
1520 {
1521 self.tables.last_mut().unwrap().annotations_used = true;
1522 self.scan_expression(annotation, ExpressionContext::Load)?;
1523 }
1524 for annotation in parameters
1525 .kwonlyargs
1526 .iter()
1527 .filter_map(|arg| arg.parameter.annotation.as_ref())
1528 {
1529 self.tables.last_mut().unwrap().annotations_used = true;
1530 self.scan_expression(annotation, ExpressionContext::Load)?;
1531 }
1532 if let Some(annotation) = returns {
1533 self.tables.last_mut().unwrap().annotations_used = true;
1534 self.scan_expression(annotation, ExpressionContext::Load)?;
1535 }
1536 Ok(())
1537 })();
1538
1539 self.tables.last_mut().unwrap().in_unevaluated_annotation = was_in_unevaluated_annotation;
1540 if self.future_annotations {
1541 let mut annotation_block = self.discard_scope();
1542 Self::resolve_future_annotation_names_as_globals(&mut annotation_block);
1543 self.tables
1544 .last_mut()
1545 .unwrap()
1546 .hidden_annotation_blocks
1547 .push(annotation_block);
1548 } else {
1549 self.leave_scope();
1550 }
1551 result
1552 }
1553
1554 fn scan_annotation_inner(
1555 &mut self,
1556 annotation: &ast::Expr,
1557 is_ann_assign: bool,
1558 ) -> SymbolTableResult {
1559 let current_scope = self.tables.last().map(|t| t.typ);
1560 let is_unevaluated = is_ann_assign
1561 && current_scope.is_some_and(|scope| {
1562 matches!(
1563 scope,
1564 CompilerScope::Function | CompilerScope::AsyncFunction | CompilerScope::Lambda
1565 )
1566 });
1567 let needs_conditional_annotations = is_ann_assign
1568 && (matches!(current_scope, Some(CompilerScope::Module))
1569 || (matches!(current_scope, Some(CompilerScope::Class))
1570 && self.in_conditional_block));
1571 let should_register_conditional_annotations = needs_conditional_annotations
1572 && !self.tables.last().unwrap().has_conditional_annotations;
1573
1574 if needs_conditional_annotations {
1577 self.tables.last_mut().unwrap().has_conditional_annotations = true;
1578 }
1579
1580 if should_register_conditional_annotations {
1581 self.register_name(
1582 &"__conditional_annotations__".into(),
1583 SymbolUsage::Used,
1584 annotation.range(),
1585 )?;
1586 }
1587
1588 let line_number = self.line_index_start(annotation.range());
1590 self.enter_annotation_scope(line_number, false, true);
1591
1592 let was_in_unevaluated_annotation = self.tables.last().unwrap().in_unevaluated_annotation;
1595 self.tables.last_mut().unwrap().in_unevaluated_annotation = is_unevaluated;
1596 let result = self.scan_expression(annotation, ExpressionContext::Load);
1597 self.tables.last_mut().unwrap().in_unevaluated_annotation = was_in_unevaluated_annotation;
1598
1599 self.leave_annotation_scope();
1600
1601 result
1602 }
1603
1604 fn track_future_statement(&mut self, statement: &ast::Stmt) -> SymbolTableResult {
1609 match statement {
1610 ast::Stmt::ImportFrom(ast::StmtImportFrom { module, level, .. })
1611 if *level == 0 && module.as_ref().map(|id| id.as_str()) == Some("__future__") =>
1612 {
1613 if matches!(self.done_with_future_stmts, DoneWithFuture::Yes) {
1614 return Err(self.error_ranged(
1615 "from __future__ imports must occur at the beginning of the file"
1616 .to_owned(),
1617 statement.range(),
1618 ));
1619 }
1620 self.done_with_future_stmts = DoneWithFuture::DoneWithDoc;
1621 }
1622 ast::Stmt::Expr(ast::StmtExpr { value, .. })
1623 if is_docstring_expr(value)
1624 && matches!(self.done_with_future_stmts, DoneWithFuture::No) =>
1625 {
1626 self.done_with_future_stmts = DoneWithFuture::DoneWithDoc;
1627 }
1628 _ => self.done_with_future_stmts = DoneWithFuture::Yes,
1629 }
1630 Ok(())
1631 }
1632
1633 fn scan_statement(&mut self, statement: &ast::Stmt) -> SymbolTableResult {
1634 if self.recursion_depth >= self.recursion_limit {
1635 return Err(SymbolTableError {
1636 error: RECURSION_ERROR.to_owned(),
1637 location: None,
1638 end_location: None,
1639 });
1640 }
1641 self.recursion_depth += 1;
1642 let result = (|| {
1643 use ast::*;
1644 self.track_future_statement(statement)?;
1645 match &statement {
1646 Stmt::Global(StmtGlobal { names, .. }) => {
1647 for name in names {
1648 self.register_name(name.id(), SymbolUsage::Global, statement.range())?;
1649 }
1650 }
1651 Stmt::Nonlocal(StmtNonlocal { names, .. }) => {
1652 for name in names {
1653 self.register_name(name.id(), SymbolUsage::Nonlocal, statement.range())?;
1654 }
1655 }
1656 Stmt::FunctionDef(StmtFunctionDef {
1657 name,
1658 body,
1659 parameters,
1660 decorator_list,
1661 type_params,
1662 returns,
1663 range,
1664 is_async,
1665 ..
1666 }) => {
1667 self.register_name(name.id(), SymbolUsage::Assigned, *range)?;
1668
1669 let def_range = crate::decorated_definition_range(
1670 &self.source_file,
1671 *range,
1672 decorator_list,
1673 if *is_async { "async def " } else { "def " },
1674 );
1675
1676 self.scan_parameter_defaults(parameters)?;
1677 self.scan_decorators(decorator_list, ExpressionContext::Load)?;
1678
1679 if let Some(type_params) = type_params {
1682 self.enter_type_param_block(
1683 name.id(),
1684 def_range,
1685 false,
1686 true,
1690 Self::has_kwonlydefaults(parameters),
1691 )?;
1692 self.scan_type_params(type_params)?;
1693 }
1694 self.enter_scope_with_parameters(
1695 name.id(),
1696 parameters,
1697 self.line_index_start(def_range),
1698 returns.as_deref(),
1699 if *is_async {
1700 CompilerScope::AsyncFunction
1701 } else {
1702 CompilerScope::Function
1703 },
1704 true, false,
1706 )?;
1707 if *is_async {
1708 self.tables.last_mut().unwrap().is_coroutine = true;
1709 }
1710 self.scan_statements(body)?;
1711 self.leave_scope();
1712 if type_params.is_some() {
1713 self.leave_scope();
1714 }
1715 }
1716 Stmt::ClassDef(StmtClassDef {
1717 name,
1718 body,
1719 arguments,
1720 decorator_list,
1721 type_params,
1722 range,
1723 ..
1724 }) => {
1725 let prev_class = self.class_name.clone();
1726 self.register_name(name.id(), SymbolUsage::Assigned, *range)?;
1727
1728 let def_range = crate::decorated_definition_range(
1729 &self.source_file,
1730 *range,
1731 decorator_list,
1732 "class ",
1733 );
1734
1735 self.scan_decorators(decorator_list, ExpressionContext::Load)?;
1736
1737 if let Some(type_params) = type_params {
1738 self.enter_type_param_block(
1739 name.id(),
1740 def_range,
1741 true, false,
1743 false,
1744 )?;
1745 self.class_name = Some(name.id().clone());
1747 self.scan_type_params(type_params)?;
1748 }
1749
1750 if type_params.is_none() {
1751 self.class_name.clone_from(&prev_class);
1752 }
1753
1754 if let Some(arguments) = arguments {
1755 self.scan_expressions(&arguments.args, ExpressionContext::Load)?;
1756 for keyword in &arguments.keywords {
1757 if let Some(arg) = &keyword.arg {
1758 self.check_name(arg.id(), ExpressionContext::Store, keyword.range)?;
1759 }
1760 }
1761 for keyword in &arguments.keywords {
1762 self.scan_expression(&keyword.value, ExpressionContext::Load)?;
1763 }
1764 }
1765
1766 self.enter_scope(
1767 name.id(),
1768 CompilerScope::Class,
1769 self.line_index_start(def_range),
1770 );
1771 let saved_in_conditional = self.in_conditional_block;
1773 self.in_conditional_block = false;
1774 self.class_name = Some(name.id().clone());
1775 if type_params.is_some() {
1776 self.register_name(
1777 &"__type_params__".into(),
1778 SymbolUsage::Assigned,
1779 *range,
1780 )?;
1781 self.register_name(&".type_params".into(), SymbolUsage::Used, *range)?;
1782 }
1783 self.scan_statements(body)?;
1784 self.leave_scope();
1785 self.in_conditional_block = saved_in_conditional;
1786 if type_params.is_some() {
1787 self.leave_scope();
1788 }
1789 self.class_name = prev_class;
1791 }
1792 Stmt::Expr(StmtExpr { value, .. }) => {
1793 self.scan_expression(value, ExpressionContext::Load)?
1794 }
1795 Stmt::If(StmtIf {
1796 test,
1797 body,
1798 elif_else_clauses,
1799 ..
1800 }) => {
1801 self.scan_expression(test, ExpressionContext::Load)?;
1802 let saved_in_conditional_block = self.in_conditional_block;
1804 self.in_conditional_block = true;
1805 self.scan_statements(body)?;
1806 for elif in elif_else_clauses {
1807 if let Some(test) = &elif.test {
1808 self.scan_expression(test, ExpressionContext::Load)?;
1809 }
1810 self.scan_statements(&elif.body)?;
1811 }
1812 self.in_conditional_block = saved_in_conditional_block;
1813 }
1814 Stmt::For(StmtFor {
1815 target,
1816 iter,
1817 body,
1818 orelse,
1819 is_async,
1820 ..
1821 }) => {
1822 if *is_async && self.allows_top_level_await() {
1823 self.tables.last_mut().unwrap().is_coroutine = true;
1824 }
1825 if *is_async && !self.tables.last().unwrap().is_coroutine {
1826 return Err(self.error_ranged(
1827 "'async for' outside async function".to_owned(),
1828 statement.range(),
1829 ));
1830 }
1831 self.scan_expression(target, ExpressionContext::Store)?;
1832 self.scan_expression(iter, ExpressionContext::Load)?;
1833 let saved_in_conditional_block = self.in_conditional_block;
1835 self.in_conditional_block = true;
1836 self.scan_statements(body)?;
1837 self.scan_statements(orelse)?;
1838 self.in_conditional_block = saved_in_conditional_block;
1839 }
1840 Stmt::While(StmtWhile {
1841 test, body, orelse, ..
1842 }) => {
1843 self.scan_expression(test, ExpressionContext::Load)?;
1844 let saved_in_conditional_block = self.in_conditional_block;
1846 self.in_conditional_block = true;
1847 self.scan_statements(body)?;
1848 self.scan_statements(orelse)?;
1849 self.in_conditional_block = saved_in_conditional_block;
1850 }
1851 Stmt::Break(_) | Stmt::Continue(_) | Stmt::Pass(_) => {
1852 }
1854 Stmt::Import(StmtImport { names, .. })
1855 | Stmt::ImportFrom(StmtImportFrom { names, .. }) => {
1856 for name in names {
1857 if let Some(alias) = &name.asname {
1858 self.register_name(alias.id(), SymbolUsage::Imported, name.name.range)?;
1860 } else if name.name.as_str() == "*" {
1861 if self.tables.last().unwrap().typ != CompilerScope::Module {
1863 return Err(self.error_ranged(
1864 "import * only allowed at module level".to_string(),
1865 name.name.range,
1866 ));
1867 }
1868 } else {
1870 let imported_name = name.name.split('.').next().unwrap().into();
1872 self.check_name(
1873 &imported_name,
1874 ExpressionContext::Store,
1875 name.name.range,
1876 )?;
1877 self.register_name(
1878 &imported_name,
1879 SymbolUsage::Imported,
1880 name.name.range,
1881 )?;
1882 }
1883 }
1884 }
1885 Stmt::Return(StmtReturn { value, .. }) => {
1886 if let Some(expression) = value {
1887 self.scan_expression(expression, ExpressionContext::Load)?;
1888 self.tables.last_mut().unwrap().returns_value = true;
1889 }
1890 }
1891 Stmt::Assert(StmtAssert { test, msg, .. }) => {
1892 self.scan_expression(test, ExpressionContext::Load)?;
1893 if let Some(expression) = msg {
1894 self.scan_expression(expression, ExpressionContext::Load)?;
1895 }
1896 }
1897 Stmt::Delete(StmtDelete { targets, .. }) => {
1898 self.scan_expressions(targets, ExpressionContext::Delete)?;
1899 }
1900 Stmt::Assign(StmtAssign { targets, value, .. }) => {
1901 self.scan_expressions(targets, ExpressionContext::Store)?;
1902 self.scan_expression(value, ExpressionContext::Load)?;
1903 }
1904 Stmt::AugAssign(StmtAugAssign { target, value, .. }) => {
1905 self.scan_expression(target, ExpressionContext::Store)?;
1906 self.scan_expression(value, ExpressionContext::Load)?;
1907 }
1908 Stmt::AnnAssign(StmtAnnAssign {
1909 target,
1910 annotation,
1911 value,
1912 simple,
1913 range,
1914 ..
1915 }) => {
1916 self.tables.last_mut().unwrap().annotations_used = true;
1917 match &**target {
1919 Expr::Name(ast::ExprName {
1920 id,
1921 range: target_range,
1922 ..
1923 }) => {
1924 if *simple {
1925 let existing_flags = self.tables.last().and_then(|table| {
1926 let name = maybe_mangle_name(
1927 self.class_name.as_ref(),
1928 table.mangled_names.as_ref(),
1929 id,
1930 );
1931 table.symbols.get(name.as_ref()).map(|symbol| symbol.flags)
1932 });
1933 if self
1934 .tables
1935 .last()
1936 .is_some_and(|table| table.typ != CompilerScope::Module)
1937 && let Some(flags) = existing_flags
1938 && flags.intersects(
1939 SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL,
1940 )
1941 {
1942 let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) {
1943 "global"
1944 } else {
1945 "nonlocal"
1946 };
1947 return Err(self.error_ranged(
1948 format!("annotated name '{id}' can't be {usage}"),
1949 *range,
1950 ));
1951 }
1952
1953 self.register_name(
1954 id,
1955 SymbolUsage::AnnotationAssigned,
1956 *target_range,
1957 )?;
1958 } else if value.is_some() {
1959 self.register_name(id, SymbolUsage::Assigned, *target_range)?;
1960 }
1961 }
1962 _ => {
1963 self.scan_expression(target, ExpressionContext::Store)?;
1964 }
1965 }
1966 self.scan_ann_assign_annotation(annotation)?;
1967 if let Some(value) = value {
1968 self.scan_expression(value, ExpressionContext::Load)?;
1969 }
1970 }
1971 Stmt::With(StmtWith {
1972 items,
1973 body,
1974 is_async,
1975 ..
1976 }) => {
1977 if *is_async && self.allows_top_level_await() {
1978 self.tables.last_mut().unwrap().is_coroutine = true;
1979 }
1980 if *is_async && !self.tables.last().unwrap().is_coroutine {
1981 return Err(self.error_ranged(
1982 "'async with' outside async function".to_owned(),
1983 statement.range(),
1984 ));
1985 }
1986 let saved_in_conditional_block = self.in_conditional_block;
1988 self.in_conditional_block = true;
1989 for item in items {
1990 self.scan_expression(&item.context_expr, ExpressionContext::Load)?;
1991 if let Some(expression) = &item.optional_vars {
1992 self.scan_expression(expression, ExpressionContext::Store)?;
1993 }
1994 }
1995 self.scan_statements(body)?;
1996 self.in_conditional_block = saved_in_conditional_block;
1997 }
1998 Stmt::Try(StmtTry {
1999 body,
2000 handlers,
2001 orelse,
2002 finalbody,
2003 ..
2004 }) => {
2005 let saved_in_conditional_block = self.in_conditional_block;
2007 self.in_conditional_block = true;
2008 self.scan_statements(body)?;
2009 for handler in handlers {
2010 let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
2011 type_,
2012 name,
2013 body,
2014 ..
2015 }) = &handler;
2016 if let Some(expression) = type_ {
2017 self.scan_expression(expression, ExpressionContext::Load)?;
2018 }
2019 if let Some(name) = name {
2020 self.register_name(name.id(), SymbolUsage::Assigned, handler.range())?;
2021 }
2022 self.scan_statements(body)?;
2023 }
2024 self.scan_statements(orelse)?;
2025 self.scan_statements(finalbody)?;
2026 self.in_conditional_block = saved_in_conditional_block;
2027 }
2028 Stmt::Match(StmtMatch { subject, cases, .. }) => {
2029 self.scan_expression(subject, ExpressionContext::Load)?;
2030 let saved_in_conditional_block = self.in_conditional_block;
2032 self.in_conditional_block = true;
2033 for case in cases {
2034 self.scan_pattern(&case.pattern)?;
2035 if let Some(guard) = &case.guard {
2036 self.scan_expression(guard, ExpressionContext::Load)?;
2037 }
2038 self.scan_statements(&case.body)?;
2039 }
2040 self.in_conditional_block = saved_in_conditional_block;
2041 }
2042 Stmt::Raise(StmtRaise { exc, cause, .. }) => {
2043 if let Some(expression) = exc {
2044 self.scan_expression(expression, ExpressionContext::Load)?;
2045 if let Some(expression) = cause {
2046 self.scan_expression(expression, ExpressionContext::Load)?;
2047 }
2048 }
2049 }
2050 Stmt::TypeAlias(StmtTypeAlias {
2051 name,
2052 value,
2053 type_params,
2054 range,
2055 ..
2056 }) => {
2057 let Some(name_expr) = name.as_name_expr() else {
2058 return Err(
2059 self.error_ranged("type alias expects name".to_owned(), name.range())
2060 );
2061 };
2062 let alias_name = name_expr.id();
2063 self.scan_expression(name, ExpressionContext::Store)?;
2064 let in_class = self
2066 .tables
2067 .last()
2068 .is_some_and(|t| t.typ == CompilerScope::Class);
2069 let is_generic = type_params.is_some();
2070 if let Some(type_params) = type_params {
2071 self.enter_type_param_block(alias_name, *range, false, false, false)?;
2072 self.scan_type_params(type_params)?;
2073 }
2074 self.enter_scope(
2076 alias_name,
2077 CompilerScope::TypeAlias,
2078 self.line_index_start(*range),
2079 );
2080 self.register_name(&".format".into(), SymbolUsage::Parameter, *range)?;
2082 self.register_name(&".format".into(), SymbolUsage::Used, *range)?;
2083 if in_class {
2084 if let Some(table) = self.tables.last_mut() {
2085 table.can_see_class_scope = true;
2086 }
2087 self.register_name(
2088 &"__classdict__".into(),
2089 SymbolUsage::Used,
2090 value.range(),
2091 )?;
2092 }
2093 self.scan_expression(value, ExpressionContext::Load)?;
2094 self.leave_scope();
2095 if is_generic {
2096 self.leave_scope();
2097 }
2098 }
2099 Stmt::IpyEscapeCommand(stmt) => {
2100 return Err(self.error_ranged("invalid syntax".to_owned(), stmt.range));
2101 }
2102 }
2103 Ok(())
2104 })();
2105 self.recursion_depth -= 1;
2106 result
2107 }
2108
2109 fn scan_decorators(
2110 &mut self,
2111 decorators: &[ast::Decorator],
2112 context: ExpressionContext,
2113 ) -> SymbolTableResult {
2114 for decorator in decorators {
2115 self.scan_expression(&decorator.expression, context)?;
2116 }
2117 Ok(())
2118 }
2119
2120 fn scan_expressions(
2121 &mut self,
2122 expressions: &[ast::Expr],
2123 context: ExpressionContext,
2124 ) -> SymbolTableResult {
2125 for expression in expressions {
2126 self.scan_expression(expression, context)?;
2127 }
2128 Ok(())
2129 }
2130
2131 fn scan_format_spec(
2134 &mut self,
2135 format_spec: &ast::InterpolatedStringFormatSpec,
2136 ) -> SymbolTableResult {
2137 for element in format_spec.elements.interpolations() {
2138 self.scan_expression(&element.expression, ExpressionContext::Load)?;
2139 if let Some(nested) = &element.format_spec {
2140 self.scan_format_spec(nested)?;
2141 }
2142 }
2143 Ok(())
2144 }
2145
2146 fn scan_expression(
2147 &mut self,
2148 expression: &ast::Expr,
2149 context: ExpressionContext,
2150 ) -> SymbolTableResult {
2151 if self.recursion_depth >= self.recursion_limit {
2152 return Err(SymbolTableError {
2153 error: RECURSION_ERROR.to_owned(),
2154 location: None,
2155 end_location: None,
2156 });
2157 }
2158 self.recursion_depth += 1;
2159 let result = (|| {
2160 use ast::*;
2161
2162 if expression.is_constant_expr() {
2163 return Ok(());
2164 }
2165
2166 if let Some(keyword) = match expression {
2169 Expr::Yield(_) | Expr::YieldFrom(_) => Some("yield"),
2170 Expr::Await(_) => Some("await"),
2171 Expr::Named(_) => Some("named"),
2172 _ => None,
2173 } {
2174 let current_is_comprehension = self
2177 .tables
2178 .last()
2179 .is_some_and(|table| table.typ == CompilerScope::Comprehension);
2180 let context_name = if keyword == "named" && current_is_comprehension {
2181 None
2182 } else if let Some(table) = self.tables.last() {
2183 match table.typ {
2184 CompilerScope::Annotation => Some("an annotation"),
2185 CompilerScope::TypeVariable => table.scope_info,
2186 CompilerScope::TypeAlias => Some("a type alias"),
2187 CompilerScope::TypeParams => Some("the definition of a generic"),
2188 _ => None,
2189 }
2190 } else {
2191 None
2192 };
2193
2194 if let Some(context_name) = context_name {
2195 return Err(self.error_ranged(
2196 format!("{keyword} expression cannot be used within {context_name}"),
2197 expression.range(),
2198 ));
2199 }
2200 }
2201
2202 match expression {
2203 Expr::BinOp(ExprBinOp { left, right, .. }) => {
2204 self.scan_expression(left, context)?;
2205 self.scan_expression(right, context)?;
2206 }
2207 Expr::BoolOp(ExprBoolOp { values, .. }) => {
2208 self.scan_expressions(values, context)?;
2209 }
2210 Expr::Compare(ExprCompare {
2211 left, comparators, ..
2212 }) => {
2213 self.scan_expression(left, context)?;
2214 self.scan_expressions(comparators, context)?;
2215 }
2216 Expr::Subscript(ExprSubscript { value, slice, .. }) => {
2217 self.scan_expression(value, ExpressionContext::Load)?;
2218 self.scan_expression(slice, ExpressionContext::Load)?;
2219 }
2220 Expr::Attribute(ExprAttribute {
2221 value, attr, range, ..
2222 }) => {
2223 self.check_name(attr.id(), context, *range)?;
2224 self.scan_expression(value, ExpressionContext::Load)?;
2225 }
2226 Expr::Dict(ExprDict { items, .. }) => {
2227 for item in items {
2228 if let Some(key) = &item.key {
2229 self.scan_expression(key, context)?;
2230 }
2231 }
2232 for item in items {
2233 self.scan_expression(&item.value, context)?;
2234 }
2235 }
2236 Expr::Await(ExprAwait { value, .. }) => {
2237 let current_scope = self.tables.last().unwrap().typ;
2238 if !self.allows_top_level_await()
2239 && !Self::is_function_like_scope(current_scope)
2240 {
2241 return Err(self.error_ranged(
2242 "'await' outside function".to_owned(),
2243 expression.range(),
2244 ));
2245 }
2246 if current_scope != CompilerScope::AsyncFunction
2247 && current_scope != CompilerScope::Comprehension
2248 && !self.allows_top_level_await()
2249 {
2250 return Err(self.error_ranged(
2251 "'await' outside async function".to_owned(),
2252 expression.range(),
2253 ));
2254 }
2255 self.scan_expression(value, context)?;
2256 self.tables.last_mut().unwrap().is_coroutine = true;
2257 }
2258 Expr::Yield(ExprYield { value, .. }) => {
2259 if let Some(expression) = value {
2260 self.scan_expression(expression, context)?;
2261 }
2262 self.tables.last_mut().unwrap().is_generator = true;
2263 if let Some(context_name) = self.comprehension_yield_context
2264 && self
2265 .tables
2266 .last()
2267 .is_some_and(|table| table.typ == CompilerScope::Comprehension)
2268 {
2269 return Err(self.error_ranged(
2270 format!("'yield' inside {context_name}"),
2271 expression.range(),
2272 ));
2273 }
2274 }
2275 Expr::YieldFrom(ExprYieldFrom { value, .. }) => {
2276 self.scan_expression(value, context)?;
2277 self.tables.last_mut().unwrap().is_generator = true;
2278 if let Some(context_name) = self.comprehension_yield_context
2279 && self
2280 .tables
2281 .last()
2282 .is_some_and(|table| table.typ == CompilerScope::Comprehension)
2283 {
2284 return Err(self.error_ranged(
2285 format!("'yield' inside {context_name}"),
2286 expression.range(),
2287 ));
2288 }
2289 }
2290 Expr::UnaryOp(ExprUnaryOp { operand, .. }) => {
2291 self.scan_expression(operand, context)?;
2292 }
2293 Expr::Starred(ExprStarred { value, .. }) => {
2294 self.scan_expression(value, context)?;
2295 }
2296 Expr::Tuple(ExprTuple { elts, .. })
2297 | Expr::Set(ExprSet { elts, .. })
2298 | Expr::List(ExprList { elts, .. }) => {
2299 self.scan_expressions(elts, context)?;
2300 }
2301 Expr::Slice(ExprSlice {
2302 lower, upper, step, ..
2303 }) => {
2304 if let Some(lower) = lower {
2305 self.scan_expression(lower, context)?;
2306 }
2307 if let Some(upper) = upper {
2308 self.scan_expression(upper, context)?;
2309 }
2310 if let Some(step) = step {
2311 self.scan_expression(step, context)?;
2312 }
2313 }
2314 Expr::Generator(ExprGenerator {
2315 elt,
2316 generators,
2317 range,
2318 ..
2319 }) => {
2320 let was_in_iter_def_exp = self.in_iter_def_exp;
2321 if context == ExpressionContext::IterDefinitionExp {
2322 self.in_iter_def_exp = true;
2323 }
2324 self.scan_comprehension(
2326 &"genexpr".into(),
2327 elt,
2328 None,
2329 generators,
2330 *range,
2331 true,
2332 )?;
2333 self.in_iter_def_exp = was_in_iter_def_exp;
2334 }
2335 Expr::ListComp(ExprListComp {
2336 elt,
2337 generators,
2338 range,
2339 ..
2340 }) => {
2341 let was_in_iter_def_exp = self.in_iter_def_exp;
2342 if context == ExpressionContext::IterDefinitionExp {
2343 self.in_iter_def_exp = true;
2344 }
2345 self.scan_comprehension(
2347 &"<listcomp>".into(),
2348 elt,
2349 None,
2350 generators,
2351 *range,
2352 false,
2353 )?;
2354 self.in_iter_def_exp = was_in_iter_def_exp;
2355 }
2356 Expr::SetComp(ExprSetComp {
2357 elt,
2358 generators,
2359 range,
2360 ..
2361 }) => {
2362 let was_in_iter_def_exp = self.in_iter_def_exp;
2363 if context == ExpressionContext::IterDefinitionExp {
2364 self.in_iter_def_exp = true;
2365 }
2366 self.scan_comprehension(
2368 &"<setcomp>".into(),
2369 elt,
2370 None,
2371 generators,
2372 *range,
2373 false,
2374 )?;
2375 self.in_iter_def_exp = was_in_iter_def_exp;
2376 }
2377 Expr::DictComp(ExprDictComp {
2378 key,
2379 value,
2380 generators,
2381 range,
2382 ..
2383 }) => {
2384 let was_in_iter_def_exp = self.in_iter_def_exp;
2385 if context == ExpressionContext::IterDefinitionExp {
2386 self.in_iter_def_exp = true;
2387 }
2388 let Some(key) = key.as_deref() else {
2390 return Err(self.error_ranged(
2391 "dict unpacking cannot be used in dict comprehension".to_owned(),
2392 *range,
2393 ));
2394 };
2395 self.scan_comprehension(
2396 &"<dictcomp>".into(),
2397 key,
2398 Some(value),
2399 generators,
2400 *range,
2401 false,
2402 )?;
2403 self.in_iter_def_exp = was_in_iter_def_exp;
2404 }
2405 Expr::Call(ExprCall {
2406 func, arguments, ..
2407 }) => {
2408 match context {
2409 ExpressionContext::IterDefinitionExp => {
2410 self.scan_expression(func, ExpressionContext::IterDefinitionExp)?;
2411 }
2412 _ => {
2413 self.scan_expression(func, ExpressionContext::Load)?;
2414 }
2415 }
2416
2417 self.scan_expressions(&arguments.args, ExpressionContext::Load)?;
2418 for keyword in &arguments.keywords {
2419 if let Some(arg) = &keyword.arg {
2420 self.check_name(arg.id(), ExpressionContext::Store, keyword.range)?;
2421 }
2422 }
2423 for keyword in &arguments.keywords {
2424 self.scan_expression(&keyword.value, ExpressionContext::Load)?;
2425 }
2426 }
2427 Expr::Name(ExprName { id, range, .. }) => {
2428 self.check_name(id, context, *range)?;
2429
2430 if !self
2431 .tables
2432 .last()
2433 .is_some_and(|table| table.in_unevaluated_annotation)
2434 {
2435 match context {
2437 ExpressionContext::Delete => {
2438 self.register_name(id, SymbolUsage::Assigned, *range)?;
2439 }
2440 ExpressionContext::Load | ExpressionContext::IterDefinitionExp => {
2441 self.register_name(id, SymbolUsage::Used, *range)?;
2442 }
2443 ExpressionContext::Store => {
2444 self.register_name(id, SymbolUsage::Assigned, *range)?;
2445 }
2446 ExpressionContext::Iter => {
2447 self.register_name(id, SymbolUsage::Iter, *range)?;
2448 }
2449 }
2450 if context == ExpressionContext::Load
2453 && Self::is_function_like_scope(self.tables.last().unwrap().typ)
2454 && id == "super"
2455 {
2456 self.register_name(&"__class__".into(), SymbolUsage::Used, *range)?;
2457 }
2458 }
2459 }
2460 Expr::Lambda(ExprLambda {
2461 body, parameters, ..
2462 }) => {
2463 let was_in_iter_def_exp = self.in_iter_def_exp;
2464 if let Some(parameters) = parameters {
2465 if was_in_iter_def_exp {
2466 self.scan_parameter_defaults(parameters)?;
2467 }
2468 self.enter_scope_with_parameters(
2469 &"lambda".into(),
2470 parameters,
2471 self.line_index_start(expression.range()),
2472 None, CompilerScope::Lambda,
2474 was_in_iter_def_exp,
2475 false,
2476 )?;
2477 } else {
2478 self.enter_scope(
2479 &"lambda".into(),
2480 CompilerScope::Lambda,
2481 self.line_index_start(expression.range()),
2482 );
2483 }
2484 self.scan_expression(body, ExpressionContext::Load)?;
2485 self.in_iter_def_exp = was_in_iter_def_exp;
2486 self.leave_scope();
2487 }
2488 Expr::FString(fstring) => {
2489 if let Some(joined_str) = &fstring.runtime_joined_str {
2490 for expr in joined_str {
2491 self.scan_expression(expr, ExpressionContext::Load)?;
2492 }
2493 return Ok(());
2494 }
2495 for expr in fstring
2496 .value
2497 .elements()
2498 .filter_map(|x| x.as_interpolation())
2499 {
2500 self.scan_expression(&expr.expression, ExpressionContext::Load)?;
2501 if let Some(format_spec) = &expr.runtime_formatted_value_format_spec {
2502 self.scan_expression(format_spec, ExpressionContext::Load)?;
2503 } else if let Some(format_spec) = &expr.format_spec {
2504 self.scan_format_spec(format_spec)?;
2505 }
2506 }
2507 }
2508 Expr::TString(tstring) => {
2509 if let Some(template_str) = &tstring.runtime_template_str {
2510 for expr in template_str {
2511 self.scan_expression(expr, ExpressionContext::Load)?;
2512 }
2513 return Ok(());
2514 }
2515 for expr in tstring
2517 .value
2518 .elements()
2519 .filter_map(|x| x.as_interpolation())
2520 {
2521 self.scan_expression(&expr.expression, ExpressionContext::Load)?;
2522 if expr.runtime_str.is_some() {
2523 if let Some(format_spec) = &expr.runtime_interpolation_format_spec {
2524 self.scan_expression(format_spec, ExpressionContext::Load)?;
2525 }
2526 } else if let Some(format_spec) = &expr.format_spec {
2527 self.scan_format_spec(format_spec)?;
2528 }
2529 }
2530 }
2531 Expr::StringLiteral(_)
2533 | Expr::BytesLiteral(_)
2534 | Expr::NumberLiteral(_)
2535 | Expr::Constant(_)
2536 | Expr::BooleanLiteral(_)
2537 | Expr::NoneLiteral(_)
2538 | Expr::EllipsisLiteral(_) => {}
2539 Expr::IpyEscapeCommand(expr) => {
2540 return Err(self.error_ranged("invalid syntax".to_owned(), expr.range));
2541 }
2542 Expr::If(ExprIf {
2543 test, body, orelse, ..
2544 }) => {
2545 self.scan_expression(test, ExpressionContext::Load)?;
2546 self.scan_expression(body, ExpressionContext::Load)?;
2547 self.scan_expression(orelse, ExpressionContext::Load)?;
2548 }
2549
2550 Expr::Named(ExprNamed {
2551 target,
2552 value,
2553 range,
2554 ..
2555 }) => {
2556 if context == ExpressionContext::IterDefinitionExp || self.in_iter_def_exp {
2559 return Err(self.error_ranged(
2560 "assignment expression cannot be used in a comprehension iterable expression"
2561 .to_string(),
2562 *range,
2563 ));
2564 }
2565
2566 let named_target = if let Expr::Name(ExprName {
2567 id,
2568 range: target_range,
2569 ..
2570 }) = &**target
2571 {
2572 self.check_name(id, ExpressionContext::Store, *target_range)?;
2573 let table = self.tables.last().unwrap();
2574 if table.typ == CompilerScope::Comprehension {
2575 self.extend_namedexpr_scope(id, *target_range)?;
2576 }
2577 Some((id, *target_range))
2578 } else {
2579 None
2580 };
2581
2582 self.scan_expression(value, ExpressionContext::Load)?;
2583
2584 if let Some((id, target_range)) = named_target {
2585 self.register_name(id, SymbolUsage::Assigned, target_range)?;
2586 } else {
2587 self.scan_expression(target, ExpressionContext::Store)?;
2588 }
2589 }
2590 }
2591 Ok(())
2592 })();
2593 self.recursion_depth -= 1;
2594 result
2595 }
2596
2597 fn scan_comprehension(
2598 &mut self,
2599 scope_name: &Name,
2600 elt1: &ast::Expr,
2601 elt2: Option<&ast::Expr>,
2602 generators: &[ast::Comprehension],
2603 range: TextRange,
2604 is_generator: bool,
2605 ) -> SymbolTableResult {
2606 assert!(!generators.is_empty());
2607 let outermost = &generators[0];
2608
2609 let was_in_iter_def_exp = self.in_iter_def_exp;
2612 self.in_iter_def_exp = true;
2613 self.scan_expression(&outermost.iter, ExpressionContext::IterDefinitionExp)?;
2614 self.in_iter_def_exp = was_in_iter_def_exp;
2615
2616 self.enter_scope(
2618 scope_name,
2619 CompilerScope::Comprehension,
2620 self.line_index_start(range),
2621 );
2622 if outermost.is_async {
2623 self.tables.last_mut().unwrap().is_coroutine = true;
2624 }
2625
2626 if !is_generator {
2631 let parent = self.tables.iter().rev().nth(1);
2632 let parent_can_see_class = parent.is_some_and(|t| t.can_see_class_scope);
2633 if !parent_can_see_class {
2634 self.tables.last_mut().unwrap().comp_inlined = true;
2635 }
2636 }
2637
2638 self.register_name(&".0".into(), SymbolUsage::Parameter, range)?;
2640
2641 let saved_comprehension_yield_context = self.comprehension_yield_context;
2642 self.comprehension_yield_context = Some(match scope_name.as_ref() {
2643 "<listcomp>" => "list comprehension",
2644 "<setcomp>" => "set comprehension",
2645 "<dictcomp>" => "dict comprehension",
2646 "genexpr" => "generator expression",
2647 _ => "comprehension",
2648 });
2649
2650 self.scan_expression(&outermost.target, ExpressionContext::Iter)?;
2651 for if_expr in &outermost.ifs {
2652 self.scan_expression(if_expr, ExpressionContext::Load)?;
2653 }
2654
2655 for generator in &generators[1..] {
2656 self.scan_expression(&generator.target, ExpressionContext::Iter)?;
2657 let was_in_iter_def_exp = self.in_iter_def_exp;
2658 self.in_iter_def_exp = true;
2659 self.scan_expression(&generator.iter, ExpressionContext::IterDefinitionExp)?;
2660 self.in_iter_def_exp = was_in_iter_def_exp;
2661 for if_expr in &generator.ifs {
2662 self.scan_expression(if_expr, ExpressionContext::Load)?;
2663 }
2664 if generator.is_async {
2665 self.tables.last_mut().unwrap().is_coroutine = true;
2666 }
2667 }
2668
2669 if let Some(elt2) = elt2 {
2670 self.scan_expression(elt2, ExpressionContext::Load)?;
2671 }
2672 self.scan_expression(elt1, ExpressionContext::Load)?;
2673 self.tables.last_mut().unwrap().is_generator = is_generator;
2674 self.comprehension_yield_context = saved_comprehension_yield_context;
2675
2676 let propagate_coroutine = self.tables.last().unwrap().is_coroutine && !is_generator;
2680 self.leave_scope();
2681 if propagate_coroutine
2682 && self
2683 .tables
2684 .last()
2685 .is_none_or(|table| table.typ != CompilerScope::Comprehension)
2686 && !self.is_in_async_context()
2687 && !self.allows_top_level_await()
2688 {
2689 return Err(self.error_ranged(
2690 "asynchronous comprehension outside of an asynchronous function".to_owned(),
2691 range,
2692 ));
2693 }
2694 if propagate_coroutine {
2695 self.tables.last_mut().unwrap().is_coroutine = true;
2696 }
2697
2698 Ok(())
2699 }
2700
2701 fn scan_type_param_bound_or_default(
2704 &mut self,
2705 expr: &ast::Expr,
2706 scope_name: &Name,
2707 scope_info: &'static str,
2708 ) -> SymbolTableResult {
2709 let in_class = self.tables.last().is_some_and(|t| t.can_see_class_scope);
2711 let line_number = self.line_index_start(expr.range());
2712 self.enter_scope(scope_name, CompilerScope::TypeVariable, line_number);
2713 self.register_name(&".format".into(), SymbolUsage::Parameter, expr.range())?;
2715 self.register_name(&".format".into(), SymbolUsage::Used, expr.range())?;
2716
2717 if in_class {
2718 if let Some(table) = self.tables.last_mut() {
2719 table.can_see_class_scope = true;
2720 }
2721 self.register_name(&"__classdict__".into(), SymbolUsage::Used, expr.range())?;
2722 }
2723
2724 self.tables.last_mut().unwrap().scope_info = Some(scope_info);
2725
2726 let result = self.scan_expression(expr, ExpressionContext::Load);
2728
2729 self.leave_scope();
2730
2731 result
2732 }
2733
2734 fn scan_type_params(&mut self, type_params: &ast::TypeParams) -> SymbolTableResult {
2735 for type_param in &type_params.type_params {
2737 if self.recursion_depth >= self.recursion_limit {
2738 return Err(SymbolTableError {
2739 error: RECURSION_ERROR.to_owned(),
2740 location: None,
2741 end_location: None,
2742 });
2743 }
2744 self.recursion_depth += 1;
2745 let result = (|| {
2746 match type_param {
2747 ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
2748 name,
2749 bound,
2750 range: type_var_range,
2751 default,
2752 ..
2753 }) => {
2754 self.register_name(name.id(), SymbolUsage::TypeParam, *type_var_range)?;
2755 if name.as_str() == "__classdict__" {
2756 return Err(self.error_ranged(
2757 format!(
2758 "reserved name '{}' cannot be used for type parameter",
2759 name.as_str()
2760 ),
2761 *type_var_range,
2762 ));
2763 }
2764
2765 if let Some(binding) = bound {
2767 let scope_info = if binding.is_tuple_expr() {
2768 "a TypeVar constraint"
2769 } else {
2770 "a TypeVar bound"
2771 };
2772 self.scan_type_param_bound_or_default(binding, name.id(), scope_info)?;
2773 }
2774
2775 if let Some(default_value) = default {
2777 self.scan_type_param_bound_or_default(
2778 default_value,
2779 name.id(),
2780 "a TypeVar default",
2781 )?;
2782 }
2783 }
2784 ast::TypeParam::ParamSpec(ast::TypeParamParamSpec {
2785 name,
2786 range: param_spec_range,
2787 default,
2788 ..
2789 }) => {
2790 self.register_name(name.id(), SymbolUsage::TypeParam, *param_spec_range)?;
2791 if name == "__classdict__" {
2792 return Err(self.error_ranged(
2793 format!("reserved name '{name}' cannot be used for type parameter"),
2794 *param_spec_range,
2795 ));
2796 }
2797
2798 if let Some(default_value) = default {
2800 self.scan_type_param_bound_or_default(
2801 default_value,
2802 name.id(),
2803 "a ParamSpec default",
2804 )?;
2805 }
2806 }
2807 ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
2808 name,
2809 range: type_var_tuple_range,
2810 default,
2811 ..
2812 }) => {
2813 self.register_name(
2814 name.id(),
2815 SymbolUsage::TypeParam,
2816 *type_var_tuple_range,
2817 )?;
2818 if name == "__classdict__" {
2819 return Err(self.error_ranged(
2820 format!("reserved name '{name}' cannot be used for type parameter"),
2821 *type_var_tuple_range,
2822 ));
2823 }
2824
2825 if let Some(default_value) = default {
2827 self.scan_type_param_bound_or_default(
2828 default_value,
2829 name.id(),
2830 "a TypeVarTuple default",
2831 )?;
2832 }
2833 }
2834 }
2835 Ok(())
2836 })();
2837 self.recursion_depth -= 1;
2838 result?;
2839 }
2840 Ok(())
2841 }
2842
2843 fn scan_patterns(&mut self, patterns: &[ast::Pattern]) -> SymbolTableResult {
2844 for pattern in patterns {
2845 self.scan_pattern(pattern)?;
2846 }
2847 Ok(())
2848 }
2849
2850 fn scan_pattern(&mut self, pattern: &ast::Pattern) -> SymbolTableResult {
2851 if self.recursion_depth >= self.recursion_limit {
2852 return Err(SymbolTableError {
2853 error: RECURSION_ERROR.to_owned(),
2854 location: None,
2855 end_location: None,
2856 });
2857 }
2858 self.recursion_depth += 1;
2859 let result = (|| {
2860 use ast::Pattern::{
2861 MatchAs, MatchClass, MatchMapping, MatchOr, MatchSequence, MatchSingleton,
2862 MatchStar, MatchValue,
2863 };
2864 match pattern {
2865 MatchValue(ast::PatternMatchValue { value, .. }) => {
2866 self.scan_expression(value, ExpressionContext::Load)?
2867 }
2868 MatchSingleton(_) => {}
2869 MatchSequence(ast::PatternMatchSequence { patterns, .. }) => {
2870 self.scan_patterns(patterns)?
2871 }
2872 MatchMapping(ast::PatternMatchMapping {
2873 keys,
2874 patterns,
2875 rest,
2876 ..
2877 }) => {
2878 self.scan_expressions(keys, ExpressionContext::Load)?;
2879 self.scan_patterns(patterns)?;
2880 if let Some(rest) = rest {
2881 if rest.as_str() == "_" {
2882 return Err(self.error_ranged("invalid syntax".to_owned(), rest.range));
2883 }
2884 self.register_name(rest.id(), SymbolUsage::Assigned, pattern.range())?;
2885 }
2886 }
2887 MatchClass(ast::PatternMatchClass { cls, arguments, .. }) => {
2888 self.scan_expression(cls, ExpressionContext::Load)?;
2889 self.scan_patterns(&arguments.patterns)?;
2890 for kw in &arguments.keywords {
2891 self.check_name(
2892 kw.attr.id(),
2893 ExpressionContext::Store,
2894 kw.pattern.range(),
2895 )?;
2896 }
2897 for kw in &arguments.keywords {
2898 self.scan_pattern(&kw.pattern)?;
2899 }
2900 }
2901 MatchStar(ast::PatternMatchStar { name, .. }) => {
2902 if let Some(name) = name {
2903 self.register_name(name.id(), SymbolUsage::Assigned, pattern.range())?;
2904 }
2905 }
2906 MatchAs(ast::PatternMatchAs {
2907 pattern: as_pattern,
2908 name,
2909 ..
2910 }) => {
2911 if let Some(as_pattern) = as_pattern {
2912 self.scan_pattern(as_pattern)?;
2913 }
2914 if let Some(name) = name {
2915 self.register_name(name.id(), SymbolUsage::Assigned, pattern.range())?;
2916 }
2917 }
2918 MatchOr(ast::PatternMatchOr { patterns, .. }) => self.scan_patterns(patterns)?,
2919 }
2920 Ok(())
2921 })();
2922 self.recursion_depth -= 1;
2923 result
2924 }
2925
2926 fn scan_parameter_defaults(&mut self, parameters: &ast::Parameters) -> SymbolTableResult {
2928 for default in parameters
2929 .posonlyargs
2930 .iter()
2931 .chain(parameters.args.iter())
2932 .chain(parameters.kwonlyargs.iter())
2933 .filter_map(|arg| arg.default.as_ref())
2934 {
2935 self.scan_expression(default, ExpressionContext::Load)?;
2936 }
2937 Ok(())
2938 }
2939
2940 fn has_kwonlydefaults(parameters: &ast::Parameters) -> bool {
2941 parameters
2942 .kwonlyargs
2943 .iter()
2944 .any(|arg| arg.default.is_some())
2945 }
2946
2947 #[expect(
2948 clippy::too_many_arguments,
2949 reason = "keeps parameter/default scanning options explicit at call sites"
2950 )]
2951 fn enter_scope_with_parameters(
2952 &mut self,
2953 name: &Name,
2954 parameters: &ast::Parameters,
2955 line_number: u32,
2956 returns: Option<&ast::Expr>,
2957 scope_type: CompilerScope,
2958 skip_defaults: bool,
2959 skip_annotations: bool,
2960 ) -> SymbolTableResult {
2961 if !skip_defaults {
2963 self.scan_parameter_defaults(parameters)?;
2964 }
2965
2966 let is_function_scope = matches!(
2967 scope_type,
2968 CompilerScope::Function | CompilerScope::AsyncFunction
2969 );
2970 if is_function_scope && !skip_annotations {
2971 self.scan_function_annotations(parameters, returns, line_number)?;
2972 }
2973
2974 self.enter_scope(name, scope_type, line_number);
2975
2976 self.scan_parameters(¶meters.posonlyargs)?;
2978 self.scan_parameters(¶meters.args)?;
2979 self.scan_parameters(¶meters.kwonlyargs)?;
2980 if let Some(name) = ¶meters.vararg {
2981 self.scan_parameter(name)?;
2982 }
2983 if let Some(name) = ¶meters.kwarg {
2984 self.scan_parameter(name)?;
2985 }
2986 Ok(())
2987 }
2988
2989 fn error_ranged(&self, error: String, range: TextRange) -> SymbolTableError {
2990 let source_code = self.source_file.to_source_code();
2991 SymbolTableError {
2992 error,
2993 location: Some(source_code.source_location(range.start(), PositionEncoding::Utf8)),
2994 end_location: Some(source_code.source_location(range.end(), PositionEncoding::Utf8)),
2995 }
2996 }
2997
2998 fn register_ident(&mut self, ident: &ast::Identifier, role: SymbolUsage) -> SymbolTableResult {
2999 self.register_name(ident.id(), role, ident.range)
3000 }
3001
3002 fn check_name(
3003 &self,
3004 name: &Name,
3005 context: ExpressionContext,
3006 range: TextRange,
3007 ) -> SymbolTableResult {
3008 if name == "__debug__" {
3009 match context {
3010 ExpressionContext::Store | ExpressionContext::Iter => {
3011 return Err(self.error_ranged("cannot assign to __debug__".to_owned(), range));
3012 }
3013 ExpressionContext::Delete => {
3014 return Err(self.error_ranged("cannot delete __debug__".to_owned(), range));
3015 }
3016 _ => {}
3017 }
3018 }
3019 Ok(())
3020 }
3021
3022 fn extend_namedexpr_scope(&mut self, name: &Name, range: TextRange) -> SymbolTableResult {
3026 for table_idx in (0..self.tables.len()).rev() {
3027 let table_type = self.tables[table_idx].typ;
3028 let mangled = maybe_mangle_name(
3029 self.class_name.as_ref(),
3030 self.tables[table_idx].mangled_names.as_ref(),
3031 name,
3032 )
3033 .into_owned();
3034
3035 if table_type == CompilerScope::Comprehension {
3036 if self.tables[table_idx]
3037 .symbols
3038 .get(mangled.as_str())
3039 .is_some_and(|symbol| {
3040 symbol
3041 .flags
3042 .contains(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_COMP_ITER)
3043 })
3044 {
3045 return Err(self.error_ranged(format!( "assignment expression cannot rebind comprehension iteration variable '{name}'" ), range));
3046 }
3047 continue;
3048 }
3049
3050 match table_type {
3051 CompilerScope::Function | CompilerScope::AsyncFunction | CompilerScope::Lambda => {
3052 let parent_is_global = self.tables[table_idx]
3053 .symbols
3054 .get(mangled.as_str())
3055 .is_some_and(|symbol| symbol.flags.contains(SymbolFlags::DEF_GLOBAL));
3056 let current = self.tables.last_mut().unwrap();
3057 let current_symbol = current
3058 .symbols
3059 .entry(mangled.clone())
3060 .or_insert_with(|| Symbol::new(mangled.clone()));
3061 if parent_is_global {
3062 current_symbol.flags.insert(SymbolFlags::DEF_GLOBAL);
3063 current_symbol.scope = SymbolScope::GlobalExplicit;
3064 } else {
3065 current_symbol.flags.insert(SymbolFlags::DEF_NONLOCAL);
3066 current_symbol.scope = SymbolScope::Free;
3067 }
3068
3069 let symbol = self.tables[table_idx]
3070 .symbols
3071 .entry(mangled.clone())
3072 .or_insert_with(|| Symbol::new(mangled.clone()));
3073 symbol.flags.insert(SymbolFlags::DEF_LOCAL);
3074 return Ok(());
3075 }
3076 CompilerScope::Module => {
3077 let current = self.tables.last_mut().unwrap();
3078 let current_symbol = current
3079 .symbols
3080 .entry(mangled.clone())
3081 .or_insert_with(|| Symbol::new(mangled.clone()));
3082 current_symbol.flags.insert(SymbolFlags::DEF_GLOBAL);
3083 current_symbol.scope = SymbolScope::GlobalExplicit;
3084
3085 let symbol = self.tables[table_idx]
3086 .symbols
3087 .entry(mangled.clone())
3088 .or_insert_with(|| Symbol::new(mangled.clone()));
3089 symbol.flags.insert(SymbolFlags::DEF_GLOBAL);
3090 symbol.scope = SymbolScope::GlobalExplicit;
3091 return Ok(());
3092 }
3093 CompilerScope::Class => {
3094 return Err(self.error_ranged("assignment expression within a comprehension cannot be used in a class body".to_string(), range));
3095 }
3096 CompilerScope::TypeParams => {
3097 return Err(self.error_ranged("assignment expression within a comprehension cannot be used within the definition of a generic".to_string(), range));
3098 }
3099 CompilerScope::TypeAlias => {
3100 return Err(self.error_ranged("assignment expression within a comprehension cannot be used in a type alias" .to_string(), range));
3101 }
3102 CompilerScope::TypeVariable => {
3103 return Err(self.error_ranged("assignment expression within a comprehension cannot be used in a TypeVar bound" .to_string(), range));
3104 }
3105 CompilerScope::Annotation => {}
3106 CompilerScope::Comprehension => unreachable!(),
3107 }
3108 }
3109
3110 unreachable!("named expression scope extension requires an enclosing scope")
3111 }
3112
3113 fn register_name(
3114 &mut self,
3115 name: &Name,
3116 role: SymbolUsage,
3117 range: TextRange,
3118 ) -> SymbolTableResult {
3119 let source_code = self.source_file.to_source_code();
3120 let location = Some(source_code.source_location(range.start(), PositionEncoding::Utf8));
3121 let end_location = Some(source_code.source_location(range.end(), PositionEncoding::Utf8));
3122
3123 if matches!(
3128 role,
3129 SymbolUsage::Assigned
3130 | SymbolUsage::Imported
3131 | SymbolUsage::AnnotationAssigned
3132 | SymbolUsage::Parameter
3133 | SymbolUsage::Iter
3134 | SymbolUsage::TypeParam
3135 ) {
3136 self.check_name(name, ExpressionContext::Store, range)?;
3137 }
3138
3139 let scope_depth = self.tables.len();
3140 let table = self.tables.last_mut().unwrap();
3141 let current_scope = table.typ;
3142
3143 if matches!(role, SymbolUsage::TypeParam)
3145 && let Some(ref mut set) = table.mangled_names
3146 {
3147 set.insert(name.to_owned());
3148 }
3149
3150 let original_name = name;
3151 let name = &maybe_mangle_name(self.class_name.as_ref(), table.mangled_names.as_ref(), name);
3152 let symbol = if let Some(symbol) = table.symbols.get_mut(name.as_ref()) {
3154 let flags = &symbol.flags;
3155
3156 if matches!(role, SymbolUsage::Iter)
3161 && flags.intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL)
3162 {
3163 return Err(SymbolTableError {
3164 error: format!(
3165 "comprehension inner loop cannot rebind assignment expression target '{original_name}'"
3166 ),
3167 location,
3168 end_location,
3169 });
3170 }
3171
3172 if matches!(role, SymbolUsage::Parameter) && flags.contains(SymbolFlags::DEF_PARAM) {
3173 return Err(SymbolTableError {
3174 error: format!("duplicate argument '{original_name}' in function definition"),
3175 location,
3176 end_location,
3177 });
3178 }
3179
3180 if matches!(role, SymbolUsage::TypeParam) && flags.contains(SymbolFlags::DEF_TYPE_PARAM)
3182 {
3183 return Err(SymbolTableError {
3184 error: format!("duplicate type parameter '{name}'"),
3185 location,
3186 end_location,
3187 });
3188 }
3189 match role {
3190 SymbolUsage::Global if !symbol.is_global() => {
3191 if flags.contains(SymbolFlags::DEF_PARAM) {
3192 return Err(SymbolTableError {
3193 error: format!("name '{name}' is parameter and global"),
3194 location,
3195 end_location,
3196 });
3197 }
3198 if flags.contains(SymbolFlags::USE) {
3199 return Err(SymbolTableError {
3200 error: format!("name '{name}' is used prior to global declaration"),
3201 location,
3202 end_location,
3203 });
3204 }
3205 if flags.contains(SymbolFlags::DEF_ANNOT) {
3206 return Err(SymbolTableError {
3207 error: format!("annotated name '{name}' can't be global"),
3208 location,
3209 end_location,
3210 });
3211 }
3212 if flags.contains(SymbolFlags::DEF_LOCAL) {
3213 return Err(SymbolTableError {
3214 error: format!(
3215 "name '{name}' is assigned to before global declaration"
3216 ),
3217 location,
3218 end_location,
3219 });
3220 }
3221 }
3222 SymbolUsage::Nonlocal => {
3223 if flags.contains(SymbolFlags::DEF_PARAM) {
3224 return Err(SymbolTableError {
3225 error: format!("name '{name}' is parameter and nonlocal"),
3226 location,
3227 end_location,
3228 });
3229 }
3230 if flags.contains(SymbolFlags::USE) {
3231 return Err(SymbolTableError {
3232 error: format!("name '{name}' is used prior to nonlocal declaration"),
3233 location,
3234 end_location,
3235 });
3236 }
3237 if flags.contains(SymbolFlags::DEF_ANNOT) {
3238 return Err(SymbolTableError {
3239 error: format!("annotated name '{name}' can't be nonlocal"),
3240 location,
3241 end_location,
3242 });
3243 }
3244 if flags.contains(SymbolFlags::DEF_LOCAL) {
3245 return Err(SymbolTableError {
3246 error: format!(
3247 "name '{name}' is assigned to before nonlocal declaration"
3248 ),
3249 location,
3250 end_location,
3251 });
3252 }
3253 }
3254 SymbolUsage::AnnotationAssigned
3255 if current_scope != CompilerScope::Module
3256 && flags
3257 .intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL) =>
3258 {
3259 let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) {
3260 "global"
3261 } else {
3262 "nonlocal"
3263 };
3264 return Err(SymbolTableError {
3265 error: format!("annotated name '{name}' can't be {usage}"),
3266 location,
3267 end_location,
3268 });
3269 }
3270 _ => {
3271 }
3273 }
3274 symbol
3275 } else {
3276 match role {
3279 SymbolUsage::Nonlocal if scope_depth < 2 => {
3280 return Err(SymbolTableError {
3281 error: "nonlocal declaration not allowed at module level".into(),
3282 location,
3283 end_location,
3284 });
3285 }
3286 _ => {
3287 }
3289 }
3290 let symbol = Symbol::new(name.clone().into_owned());
3292 table
3293 .symbols
3294 .entry(name.clone().into_owned())
3295 .or_insert(symbol)
3296 };
3297
3298 if matches!(role, SymbolUsage::Global | SymbolUsage::Nonlocal) && symbol.location.is_none()
3299 {
3300 symbol.location = location;
3301 symbol.end_location = end_location;
3302 }
3303
3304 let flags = &mut symbol.flags;
3306 match role {
3307 SymbolUsage::Nonlocal => {
3308 symbol.scope = SymbolScope::Free;
3309 flags.insert(SymbolFlags::DEF_NONLOCAL);
3310 }
3311 SymbolUsage::Imported => {
3312 flags.insert(SymbolFlags::DEF_IMPORT);
3313 }
3314 SymbolUsage::Parameter => {
3315 flags.insert(SymbolFlags::DEF_PARAM);
3316 let name_str = symbol.name.clone();
3318 if !self.current_varnames.contains(&name_str) {
3319 self.current_varnames.push(name_str);
3320 }
3321 }
3322 SymbolUsage::AnnotationAssigned => {
3323 flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_ANNOT);
3324 }
3325 SymbolUsage::Assigned => {
3326 flags.insert(SymbolFlags::DEF_LOCAL);
3327 }
3328 SymbolUsage::Global => {
3329 symbol.scope = SymbolScope::GlobalExplicit;
3330 flags.insert(SymbolFlags::DEF_GLOBAL);
3331 }
3332 SymbolUsage::Used => {
3333 flags.insert(SymbolFlags::USE);
3334 }
3335 SymbolUsage::Iter => {
3336 flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_COMP_ITER);
3337 }
3338 SymbolUsage::TypeParam => {
3339 flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_TYPE_PARAM);
3340 }
3341 }
3342
3343 if matches!(role, SymbolUsage::Global) {
3346 let module_table = self.tables.first_mut().expect("no module symbol table");
3347 let symbol = module_table
3348 .symbols
3349 .entry(name.clone().into_owned())
3350 .or_insert_with(|| Symbol::new(name.clone().into_owned()));
3351 symbol.flags.insert(SymbolFlags::DEF_GLOBAL);
3352 symbol.scope = SymbolScope::GlobalExplicit;
3353 }
3354
3355 Ok(())
3356 }
3357}
3358
3359fn is_docstring_expr(expr: &ast::Expr) -> bool {
3360 matches!(
3361 expr,
3362 ast::Expr::StringLiteral(_)
3363 | ast::Expr::Constant(ast::ExprConstant {
3364 value: ast::ConstantValue::Str(_),
3365 ..
3366 })
3367 )
3368}
3369
3370pub(crate) fn mangle_name<'a>(class_name: Option<&Name>, name: &'a Name) -> Cow<'a, Name> {
3371 let Some(class_name) = class_name else {
3372 return Cow::Borrowed(name);
3373 };
3374
3375 if !name.starts_with("__") || name.ends_with("__") || name.contains('.') {
3376 return Cow::Borrowed(name);
3377 }
3378 let class_name = class_name.trim_start_matches('_');
3380 if class_name.is_empty() {
3381 return Cow::Borrowed(name);
3382 }
3383 let mut ret = String::with_capacity(1 + class_name.len() + name.len());
3384 ret.push('_');
3385 ret.push_str(class_name);
3386 ret.push_str(name);
3387 Cow::Owned(ret.into())
3388}
3389
3390pub(crate) fn maybe_mangle_name<'a>(
3394 class_name: Option<&Name>,
3395 mangled_names: Option<&IndexSet<Name>>,
3396 name: &'a Name,
3397) -> Cow<'a, Name> {
3398 if let Some(set) = mangled_names
3399 && !set.contains(name)
3400 {
3401 return Cow::Borrowed(name);
3402 }
3403 mangle_name(class_name, name)
3404}
3405
3406#[cfg(test)]
3407mod tests {
3408 use super::{CompilerScope, SymbolFlags, SymbolTable, mangle_name};
3409 use rustpython_compiler_core::SourceFileBuilder;
3410
3411 fn scan_source(source: &str) -> SymbolTable {
3412 scan_source_result(source).unwrap()
3413 }
3414
3415 fn scan_source_result(source: &str) -> Result<SymbolTable, super::SymbolTableError> {
3416 let source_file = SourceFileBuilder::new("source_path", source).finish();
3417 let parsed = ruff_python_parser::parse(
3418 source_file.source_text(),
3419 ruff_python_parser::Mode::Module.into(),
3420 )
3421 .unwrap()
3422 .into_syntax();
3423 let module = match parsed {
3424 ruff_python_ast::Mod::Module(module) => module,
3425 _ => unreachable!(),
3426 };
3427 SymbolTable::scan_program(&module, source_file)
3428 }
3429
3430 #[test]
3431 fn mangle_name_leaves_private_name_in_underscore_only_class() {
3432 assert_eq!(mangle_name(Some(&"_".into()), &"__a".into()), "__a".into());
3433 assert_eq!(mangle_name(Some(&"__".into()), &"__a".into()), "__a".into());
3434 assert_eq!(
3435 mangle_name(Some(&"___".into()), &"__a".into()),
3436 "__a".into()
3437 );
3438 }
3439
3440 #[test]
3441 fn mangle_name_strips_leading_class_underscores() {
3442 assert_eq!(
3443 mangle_name(Some(&"_a".into()), &"__a".into()),
3444 "_a__a".into()
3445 );
3446 assert_eq!(
3447 mangle_name(Some(&"__a".into()), &"__a".into()),
3448 "_a__a".into()
3449 );
3450 }
3451
3452 #[test]
3453 fn duplicate_parameter_check_uses_mangled_name_like_cpython() {
3454 let err = scan_source_result("class C:\n def f(__x, _C__x):\n pass\n")
3455 .expect_err("expected duplicate argument after class-private mangling");
3456
3457 assert_eq!(
3458 err.error,
3459 "duplicate argument '_C__x' in function definition"
3460 );
3461 }
3462
3463 #[test]
3464 fn super_name_marks_class_use_in_lambda_scope_like_cpython() {
3465 let table = scan_source("def f():\n return lambda: super()\n");
3466 let function = table
3467 .sub_tables
3468 .iter()
3469 .find(|table| table.name == "f")
3470 .expect("missing function scope");
3471 let lambda = function
3472 .sub_tables
3473 .iter()
3474 .find(|table| table.typ == CompilerScope::Lambda)
3475 .expect("missing lambda scope");
3476
3477 assert!(
3478 lambda.lookup(&"__class__".into()).is_some(),
3479 "CPython symtable Name_kind treats super as a __class__ use in any function-like scope"
3480 );
3481 }
3482
3483 #[test]
3484 fn comprehension_iteration_target_sets_comp_iter_flag_like_cpython() {
3485 let table = scan_source("result = [i for i in xs]\n");
3486 let comprehension = table
3487 .inlined_comprehension_blocks
3488 .iter()
3489 .find(|table| table.typ == CompilerScope::Comprehension)
3490 .expect("missing comprehension scope");
3491 let symbol = comprehension
3492 .lookup(&"i".into())
3493 .expect("missing comprehension iteration target");
3494
3495 assert!(
3496 symbol.flags.contains(SymbolFlags::DEF_COMP_ITER),
3497 "CPython symtable_add_def_helper sets DEF_COMP_ITER on comprehension iteration targets"
3498 );
3499 }
3500
3501 #[test]
3502 fn inlined_comprehension_children_are_spliced_like_cpython() {
3503 let table = scan_source("result = [(lambda: i) for i in xs]\n");
3504
3505 assert!(
3506 !table
3507 .sub_tables
3508 .iter()
3509 .any(|table| table.typ == CompilerScope::Comprehension),
3510 "CPython removes inlined comprehension entries from ste_children"
3511 );
3512 assert!(
3513 table
3514 .sub_tables
3515 .iter()
3516 .any(|table| table.typ == CompilerScope::Lambda),
3517 "CPython splices children of inlined comprehensions into the parent children list"
3518 );
3519
3520 let comprehension = table
3521 .inlined_comprehension_blocks
3522 .iter()
3523 .find(|table| table.typ == CompilerScope::Comprehension)
3524 .expect("missing inlined comprehension block");
3525 assert!(
3526 comprehension.comp_inlined,
3527 "CPython keeps the comprehension entry addressable through st_blocks with ste_comp_inlined set"
3528 );
3529 }
3530
3531 #[test]
3532 fn future_annotations_annassign_still_scans_annotation_symbols_like_cpython() {
3533 let table = scan_source("from __future__ import annotations\nx: T\n");
3534 let annotation_block = table
3535 .annotation_block
3536 .as_ref()
3537 .expect("CPython still creates an AnnotationBlock for future annotations");
3538
3539 assert!(
3540 annotation_block.lookup(&"T".into()).is_some(),
3541 "CPython symtable_visit_annotation still visits the annotation expression with future annotations"
3542 );
3543 }
3544
3545 #[test]
3546 fn annotation_like_format_parameter_is_marked_used_like_cpython() {
3547 let table = scan_source("def f(x: T): pass\n");
3548 let annotation_block = table
3549 .sub_tables
3550 .iter()
3551 .find(|table| table.typ == CompilerScope::Annotation)
3552 .expect("missing function annotation block");
3553 let format = annotation_block
3554 .lookup(&".format".into())
3555 .expect("missing annotation .format parameter");
3556 assert_eq!(annotation_block.varnames, [".format"]);
3557 assert!(
3558 format
3559 .flags
3560 .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE),
3561 "CPython symtable_enter_block() adds both DEF_PARAM and USE for annotation-like .format"
3562 );
3563
3564 let table = scan_source("type A = T\n");
3565 let alias = table
3566 .sub_tables
3567 .iter()
3568 .find(|table| table.typ == CompilerScope::TypeAlias)
3569 .expect("missing type alias scope");
3570 let format = alias
3571 .lookup(&".format".into())
3572 .expect("missing type alias .format parameter");
3573 assert!(
3574 format
3575 .flags
3576 .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE),
3577 "CPython TypeAliasBlock .format has DEF_PARAM | USE"
3578 );
3579
3580 let table = scan_source("def f[T: B](): pass\n");
3581 let type_params = table
3582 .sub_tables
3583 .iter()
3584 .find(|table| table.typ == CompilerScope::TypeParams)
3585 .expect("missing type params scope");
3586 let type_variable = type_params
3587 .sub_tables
3588 .iter()
3589 .find(|table| table.typ == CompilerScope::TypeVariable)
3590 .expect("missing type variable scope");
3591 let format = type_variable
3592 .lookup(&".format".into())
3593 .expect("missing type variable .format parameter");
3594 assert!(
3595 format
3596 .flags
3597 .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE),
3598 "CPython TypeVariableBlock .format has DEF_PARAM | USE"
3599 );
3600 }
3601
3602 #[test]
3603 fn deferred_annotation_store_names_are_not_public_symbols() {
3604 let module = scan_source("x: int\n");
3605 assert!(module.lookup(&"__annotate__".into()).is_none());
3606 assert!(module.annotation_block.is_some());
3607
3608 let module = scan_source("class C:\n y: str\n");
3609 let class = module
3610 .sub_tables
3611 .iter()
3612 .find(|table| table.typ == CompilerScope::Class)
3613 .expect("missing class scope");
3614 assert!(class.lookup(&"__annotate_func__".into()).is_none());
3615 assert!(class.annotation_block.is_some());
3616 }
3617
3618 #[test]
3619 fn generic_class_symbols_follow_cpython_insertion_order() {
3620 let module = scan_source("class C[T]:\n q = [lambda: i for i in range(2)]\n");
3621 let type_params = module
3622 .sub_tables
3623 .iter()
3624 .find(|table| table.typ == CompilerScope::TypeParams)
3625 .expect("missing type parameter scope");
3626 let class = type_params
3627 .sub_tables
3628 .iter()
3629 .find(|table| table.typ == CompilerScope::Class)
3630 .expect("missing generic class scope");
3631 assert_eq!(
3632 class.symbols.keys().collect::<Vec<_>>(),
3633 ["__type_params__", ".type_params", "q", "range", "i"]
3634 );
3635 }
3636
3637 #[test]
3638 fn function_signature_annotation_block_is_sibling_like_cpython() {
3639 let table = scan_source("def f(x: T): pass\n");
3640 assert_eq!(table.sub_tables[0].typ, CompilerScope::Annotation);
3641 assert!(table.sub_tables[0].annotations_used);
3642 assert_eq!(table.sub_tables[1].typ, CompilerScope::Function);
3643 assert!(
3644 table.sub_tables[1].annotation_block.is_none(),
3645 "CPython stores the function signature AnnotationBlock as a child keyed by arguments, not on the function block"
3646 );
3647
3648 let table = scan_source("def f(x): pass\n");
3649 assert_eq!(table.sub_tables[0].typ, CompilerScope::Annotation);
3650 assert!(!table.sub_tables[0].annotations_used);
3651 assert_eq!(table.sub_tables[1].typ, CompilerScope::Function);
3652 }
3653
3654 #[test]
3655 fn future_function_signature_annotation_block_is_hidden_like_cpython() {
3656 let table = scan_source("from __future__ import annotations\ndef f(x: T): pass\n");
3657 assert_eq!(table.sub_tables[0].typ, CompilerScope::Function);
3658 assert_eq!(
3659 table.hidden_annotation_blocks[0].typ,
3660 CompilerScope::Annotation
3661 );
3662 assert!(table.hidden_annotation_blocks[0].annotations_used);
3663 assert!(
3664 table.sub_tables[0].annotation_block.is_none(),
3665 "CPython future AnnotationBlock stays in st_blocks and is not attached to the FunctionBlock"
3666 );
3667
3668 let table = scan_source("from __future__ import annotations\ndef f(x): pass\n");
3669 assert_eq!(table.sub_tables[0].typ, CompilerScope::Function);
3670 assert_eq!(
3671 table.hidden_annotation_blocks[0].typ,
3672 CompilerScope::Annotation
3673 );
3674 assert!(!table.hidden_annotation_blocks[0].annotations_used);
3675 }
3676
3677 #[test]
3678 fn annassign_marks_current_scope_annotations_used_like_cpython() {
3679 let table = scan_source("x: int\n");
3680 assert!(
3681 table.annotations_used,
3682 "CPython AnnAssign_kind sets ste_annotations_used on the current scope"
3683 );
3684
3685 let table = scan_source("class C:\n x: int\n");
3686 let class = table
3687 .sub_tables
3688 .iter()
3689 .find(|table| table.typ == CompilerScope::Class)
3690 .expect("missing class scope");
3691 assert!(
3692 class.annotations_used,
3693 "CPython AnnAssign_kind sets ste_annotations_used on class scopes"
3694 );
3695
3696 let table = scan_source("def f():\n x: int\n");
3697 let function = table
3698 .sub_tables
3699 .iter()
3700 .find(|table| table.typ == CompilerScope::Function)
3701 .expect("missing function scope");
3702 assert!(
3703 function.annotations_used,
3704 "CPython AnnAssign_kind also marks function-local annotations"
3705 );
3706 }
3707
3708 #[test]
3709 fn class_base_child_scope_precedes_class_scope_like_cpython() {
3710 let table = scan_source("class C((lambda: Base)()):\n pass\n");
3711 assert_eq!(table.sub_tables[0].typ, CompilerScope::Lambda);
3712 assert_eq!(table.sub_tables[1].typ, CompilerScope::Class);
3713 }
3714
3715 #[test]
3716 fn try_handler_child_scope_precedes_else_scope_like_cpython() {
3717 let table = scan_source(
3718 "\
3719def f(x):
3720 try:
3721 pass
3722 except Exception:
3723 y = 1
3724 def h():
3725 return y
3726 else:
3727 def e():
3728 return x
3729",
3730 );
3731 let function = table
3732 .sub_tables
3733 .iter()
3734 .find(|table| table.name == "f")
3735 .expect("missing function scope");
3736
3737 let function_child_names = function
3738 .sub_tables
3739 .iter()
3740 .filter(|table| table.typ == CompilerScope::Function)
3741 .map(|table| table.name.as_str())
3742 .collect::<Vec<_>>();
3743 assert_eq!(function_child_names, vec!["h", "e"]);
3744 }
3745
3746 #[test]
3747 fn function_default_child_scope_precedes_decorator_scope_like_cpython() {
3748 let table = scan_source(
3749 "\
3750@(lambda decorator_arg: decorator_arg)
3751def f(x=(lambda: 1)()):
3752 pass
3753",
3754 );
3755 let lambdas = table
3756 .sub_tables
3757 .iter()
3758 .filter(|table| table.typ == CompilerScope::Lambda)
3759 .collect::<Vec<_>>();
3760
3761 assert_eq!(lambdas.len(), 2);
3762 assert!(
3763 lambdas[0].varnames.is_empty(),
3764 "CPython symtable visits function defaults before decorators"
3765 );
3766 assert_eq!(lambdas[1].varnames, vec!["decorator_arg"]);
3767 }
3768
3769 #[test]
3770 fn future_annotations_still_rejects_named_expr_in_annotation_like_cpython() {
3771 let err =
3772 scan_source_result("from __future__ import annotations\nx: (y := int)\n").unwrap_err();
3773
3774 assert_eq!(
3775 err.error,
3776 "named expression cannot be used within an annotation"
3777 );
3778 }
3779
3780 #[test]
3781 fn import_star_outside_module_uses_cpython_symtable_message() {
3782 let err = scan_source_result("def f():\n from m import *\n").unwrap_err();
3783
3784 assert_eq!(err.error, "import * only allowed at module level");
3785 }
3786
3787 #[test]
3788 fn import_as_error_location_uses_alias_location_like_cpython() {
3789 let source = "import module as __debug__\n";
3790 let err = scan_source_result(source).unwrap_err();
3791
3792 assert_eq!(err.error, "cannot assign to __debug__");
3793 let location = err.location.unwrap();
3794 assert_eq!(location.line.get(), 1);
3795 assert_eq!(
3796 location.character_offset.get(),
3797 8,
3798 "CPython reports LOCATION(a) for import aliases, at the imported name"
3799 );
3800 }
3801
3802 #[test]
3803 fn function_def_error_location_uses_statement_location_like_cpython() {
3804 let source = "def __debug__():\n pass\n";
3805 let err = scan_source_result(source).unwrap_err();
3806
3807 assert_eq!(err.error, "cannot assign to __debug__");
3808 let location = err.location.unwrap();
3809 assert_eq!(location.line.get(), 1);
3810 assert_eq!(
3811 location.character_offset.get(),
3812 1,
3813 "CPython reports LOCATION(s) for FunctionDef, at 'def'"
3814 );
3815 }
3816
3817 #[test]
3818 fn global_after_assign_error_location_uses_statement_location_like_cpython() {
3819 let source = "def f():\n x = 1\n global x\n";
3820 let err = scan_source_result(source).unwrap_err();
3821
3822 assert_eq!(
3823 err.error,
3824 "name 'x' is assigned to before global declaration"
3825 );
3826 let location = err.location.unwrap();
3827 assert_eq!(location.line.get(), 3);
3828 assert_eq!(
3829 location.character_offset.get(),
3830 5,
3831 "CPython reports LOCATION(s) for global directives, at 'global'"
3832 );
3833 }
3834
3835 #[test]
3836 fn type_param_debug_name_is_checked_like_cpython_add_def_ctx() {
3837 let source = "class C[__debug__]:\n pass\n";
3838 let err = scan_source_result(source).unwrap_err();
3839
3840 assert_eq!(err.error, "cannot assign to __debug__");
3841 let location = err.location.unwrap();
3842 assert_eq!(location.line.get(), 1);
3843 assert_eq!(
3844 location.character_offset.get(),
3845 9,
3846 "CPython symtable_add_def_ctx checks DEF_TYPE_PARAM | DEF_LOCAL at LOCATION(tp)"
3847 );
3848 }
3849
3850 #[test]
3851 fn except_handler_name_error_location_uses_handler_location_like_cpython() {
3852 let source = "try:\n pass\nexcept Exception as __debug__:\n pass\n";
3853 let err = scan_source_result(source).unwrap_err();
3854
3855 assert_eq!(err.error, "cannot assign to __debug__");
3856 let location = err.location.unwrap();
3857 assert_eq!(location.line.get(), 3);
3858 assert_eq!(
3859 location.character_offset.get(),
3860 1,
3861 "CPython reports LOCATION(eh) for except-handler names, at 'except'"
3862 );
3863 }
3864
3865 #[test]
3866 fn match_star_capture_error_location_uses_pattern_location_like_cpython() {
3867 let source = "match subject:\n case [*__debug__]:\n pass\n";
3868 let err = scan_source_result(source).unwrap_err();
3869
3870 assert_eq!(err.error, "cannot assign to __debug__");
3871 let location = err.location.unwrap();
3872 assert_eq!(location.line.get(), 2);
3873 assert_eq!(
3874 location.character_offset.get(),
3875 11,
3876 "CPython reports LOCATION(p) for MatchStar, at the '*'"
3877 );
3878 }
3879
3880 #[test]
3881 fn named_expr_in_lambda_inside_comprehension_iter_is_rejected_like_cpython() {
3882 let err = scan_source_result("[x for x in (lambda: (y := 1))()]\n").unwrap_err();
3883
3884 assert_eq!(
3885 err.error,
3886 "assignment expression cannot be used in a comprehension iterable expression"
3887 );
3888 }
3889
3890 #[test]
3891 fn yield_in_lambda_inside_comprehension_body_is_not_comprehension_yield_like_cpython() {
3892 scan_source_result("[(lambda: (yield x)) for x in xs]\n").expect(
3893 "CPython checks ste_comprehension on the current lambda block, not the enclosing comprehension",
3894 );
3895 }
3896
3897 #[test]
3898 fn yield_in_comprehension_scans_value_before_comprehension_error_like_cpython() {
3899 let err = scan_source_result("[(yield (x := 1)) for x in xs]\n").unwrap_err();
3900
3901 assert_eq!(
3902 err.error,
3903 "assignment expression cannot rebind comprehension iteration variable 'x'"
3904 );
3905 }
3906
3907 #[test]
3908 fn named_expr_in_function_annotation_comprehension_is_allowed_like_cpython() {
3909 scan_source_result("def f(x: [(y := int) for _ in xs]): pass\n").expect(
3910 "CPython skips AnnotationBlock while extending namedexpr scope from a comprehension",
3911 );
3912 }
3913
3914 #[test]
3915 fn named_expr_in_class_annotation_comprehension_uses_cpython_message() {
3916 let err = scan_source_result("class C:\n x: [(y := int) for _ in xs]\n").unwrap_err();
3917
3918 assert_eq!(
3919 err.error,
3920 "assignment expression within a comprehension cannot be used in a class body"
3921 );
3922 }
3923
3924 #[test]
3925 fn named_expr_in_type_alias_comprehension_uses_cpython_message() {
3926 let err = scan_source_result("type A = [(y := int) for _ in xs]\n").unwrap_err();
3927
3928 assert_eq!(
3929 err.error,
3930 "assignment expression within a comprehension cannot be used in a type alias"
3931 );
3932 }
3933
3934 #[test]
3935 fn named_expr_in_type_parameters_block_uses_cpython_message() {
3936 let err = scan_source_result("class C[T]((base := object)): pass\n").unwrap_err();
3937
3938 assert_eq!(
3939 err.error,
3940 "named expression cannot be used within the definition of a generic"
3941 );
3942 }
3943
3944 #[test]
3945 fn named_expr_in_typevar_bound_comprehension_uses_cpython_message() {
3946 let err = scan_source_result("def f[T: [(y := int) for _ in xs]](): pass\n").unwrap_err();
3947
3948 assert_eq!(
3949 err.error,
3950 "assignment expression within a comprehension cannot be used in a TypeVar bound"
3951 );
3952 }
3953}