rustpython_codegen/
symboltable.rs

1/* Python code is pre-scanned for symbols in the ast.
2
3This ensures that global and nonlocal keywords are picked up.
4Then the compiler can use the symbol table to generate proper
5load and store instructions for names.
6
7Inspirational file: https://github.com/python/cpython/blob/main/Python/symtable.c
8*/
9
10use crate::{
11    error::{CodegenError, CodegenErrorType},
12    IndexMap,
13};
14use bitflags::bitflags;
15use rustpython_ast::{self as ast, located::Located};
16use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
17use std::{borrow::Cow, fmt};
18
19/// Captures all symbols in the current scope, and has a list of sub-scopes in this scope.
20#[derive(Clone)]
21pub struct SymbolTable {
22    /// The name of this symbol table. Often the name of the class or function.
23    pub name: String,
24
25    /// The type of symbol table
26    pub typ: SymbolTableType,
27
28    /// The line number in the source code where this symboltable begins.
29    pub line_number: u32,
30
31    // Return True if the block is a nested class or function
32    pub is_nested: bool,
33
34    /// A set of symbols present on this scope level.
35    pub symbols: IndexMap<String, Symbol>,
36
37    /// A list of sub-scopes in the order as found in the
38    /// AST nodes.
39    pub sub_tables: Vec<SymbolTable>,
40}
41
42impl SymbolTable {
43    fn new(name: String, typ: SymbolTableType, line_number: u32, is_nested: bool) -> Self {
44        SymbolTable {
45            name,
46            typ,
47            line_number,
48            is_nested,
49            symbols: IndexMap::default(),
50            sub_tables: vec![],
51        }
52    }
53
54    pub fn scan_program(program: &[ast::located::Stmt]) -> SymbolTableResult<Self> {
55        let mut builder = SymbolTableBuilder::new();
56        builder.scan_statements(program)?;
57        builder.finish()
58    }
59
60    pub fn scan_expr(expr: &ast::located::Expr) -> SymbolTableResult<Self> {
61        let mut builder = SymbolTableBuilder::new();
62        builder.scan_expression(expr, ExpressionContext::Load)?;
63        builder.finish()
64    }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum SymbolTableType {
69    Module,
70    Class,
71    Function,
72    Comprehension,
73    TypeParams,
74}
75
76impl fmt::Display for SymbolTableType {
77    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78        match self {
79            SymbolTableType::Module => write!(f, "module"),
80            SymbolTableType::Class => write!(f, "class"),
81            SymbolTableType::Function => write!(f, "function"),
82            SymbolTableType::Comprehension => write!(f, "comprehension"),
83            SymbolTableType::TypeParams => write!(f, "type parameter"),
84            // TODO missing types from the C implementation
85            // if self._table.type == _symtable.TYPE_ANNOTATION:
86            //     return "annotation"
87            // if self._table.type == _symtable.TYPE_TYPE_VAR_BOUND:
88            //     return "TypeVar bound"
89            // if self._table.type == _symtable.TYPE_TYPE_ALIAS:
90            //     return "type alias"
91        }
92    }
93}
94
95/// Indicator for a single symbol what the scope of this symbol is.
96/// The scope can be unknown, which is unfortunate, but not impossible.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum SymbolScope {
99    Unknown,
100    Local,
101    GlobalExplicit,
102    GlobalImplicit,
103    Free,
104    Cell,
105}
106
107bitflags! {
108    #[derive(Copy, Clone, Debug, PartialEq)]
109    pub struct SymbolFlags: u16 {
110        const REFERENCED = 0x001;
111        const ASSIGNED = 0x002;
112        const PARAMETER = 0x004;
113        const ANNOTATED = 0x008;
114        const IMPORTED = 0x010;
115        const NONLOCAL = 0x020;
116        // indicates if the symbol gets a value assigned by a named expression in a comprehension
117        // this is required to correct the scope in the analysis.
118        const ASSIGNED_IN_COMPREHENSION = 0x040;
119        // indicates that the symbol is used a bound iterator variable. We distinguish this case
120        // from normal assignment to detect disallowed re-assignment to iterator variables.
121        const ITER = 0x080;
122        /// indicates that the symbol is a free variable in a class method from the scope that the
123        /// class is defined in, e.g.:
124        /// ```python
125        /// def foo(x):
126        ///     class A:
127        ///         def method(self):
128        ///             return x // is_free_class
129        /// ```
130        const FREE_CLASS = 0x100;
131        const BOUND = Self::ASSIGNED.bits() | Self::PARAMETER.bits() | Self::IMPORTED.bits() | Self::ITER.bits();
132    }
133}
134
135/// A single symbol in a table. Has various properties such as the scope
136/// of the symbol, and also the various uses of the symbol.
137#[derive(Debug, Clone)]
138pub struct Symbol {
139    pub name: String,
140    pub scope: SymbolScope,
141    pub flags: SymbolFlags,
142}
143
144impl Symbol {
145    fn new(name: &str) -> Self {
146        Symbol {
147            name: name.to_owned(),
148            // table,
149            scope: SymbolScope::Unknown,
150            flags: SymbolFlags::empty(),
151        }
152    }
153
154    pub fn is_global(&self) -> bool {
155        matches!(
156            self.scope,
157            SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit
158        )
159    }
160
161    pub fn is_local(&self) -> bool {
162        matches!(self.scope, SymbolScope::Local | SymbolScope::Cell)
163    }
164
165    pub fn is_bound(&self) -> bool {
166        self.flags.intersects(SymbolFlags::BOUND)
167    }
168}
169
170#[derive(Debug)]
171pub struct SymbolTableError {
172    error: String,
173    location: Option<SourceLocation>,
174}
175
176impl SymbolTableError {
177    pub fn into_codegen_error(self, source_path: String) -> CodegenError {
178        CodegenError {
179            error: CodegenErrorType::SyntaxError(self.error),
180            location: self.location.map(|l| SourceLocation {
181                row: l.row,
182                column: l.column,
183            }),
184            source_path,
185        }
186    }
187}
188
189type SymbolTableResult<T = ()> = Result<T, SymbolTableError>;
190
191impl SymbolTable {
192    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
193        self.symbols.get(name)
194    }
195}
196
197impl std::fmt::Debug for SymbolTable {
198    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
199        write!(
200            f,
201            "SymbolTable({:?} symbols, {:?} sub scopes)",
202            self.symbols.len(),
203            self.sub_tables.len()
204        )
205    }
206}
207
208/* Perform some sort of analysis on nonlocals, globals etc..
209  See also: https://github.com/python/cpython/blob/main/Python/symtable.c#L410
210*/
211fn analyze_symbol_table(symbol_table: &mut SymbolTable) -> SymbolTableResult {
212    let mut analyzer = SymbolTableAnalyzer::default();
213    analyzer.analyze_symbol_table(symbol_table)
214}
215
216type SymbolMap = IndexMap<String, Symbol>;
217
218mod stack {
219    use std::panic;
220    use std::ptr::NonNull;
221    pub struct StackStack<T> {
222        v: Vec<NonNull<T>>,
223    }
224    impl<T> Default for StackStack<T> {
225        fn default() -> Self {
226            Self { v: Vec::new() }
227        }
228    }
229    impl<T> StackStack<T> {
230        /// Appends a reference to this stack for the duration of the function `f`. When `f`
231        /// returns, the reference will be popped off the stack.
232        pub fn with_append<F, R>(&mut self, x: &mut T, f: F) -> R
233        where
234            F: FnOnce(&mut Self) -> R,
235        {
236            self.v.push(x.into());
237            let res = panic::catch_unwind(panic::AssertUnwindSafe(|| f(self)));
238            self.v.pop();
239            res.unwrap_or_else(|x| panic::resume_unwind(x))
240        }
241
242        pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + '_ {
243            self.as_ref().iter().copied()
244        }
245        pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> + '_ {
246            self.as_mut().iter_mut().map(|x| &mut **x)
247        }
248        // pub fn top(&self) -> Option<&T> {
249        //     self.as_ref().last().copied()
250        // }
251        // pub fn top_mut(&mut self) -> Option<&mut T> {
252        //     self.as_mut().last_mut().map(|x| &mut **x)
253        // }
254        pub fn len(&self) -> usize {
255            self.v.len()
256        }
257        pub fn is_empty(&self) -> bool {
258            self.len() == 0
259        }
260
261        pub fn as_ref(&self) -> &[&T] {
262            unsafe { &*(self.v.as_slice() as *const [NonNull<T>] as *const [&T]) }
263        }
264
265        pub fn as_mut(&mut self) -> &mut [&mut T] {
266            unsafe { &mut *(self.v.as_mut_slice() as *mut [NonNull<T>] as *mut [&mut T]) }
267        }
268    }
269}
270use stack::StackStack;
271
272/// Symbol table analysis. Can be used to analyze a fully
273/// build symbol table structure. It will mark variables
274/// as local variables for example.
275#[derive(Default)]
276#[repr(transparent)]
277struct SymbolTableAnalyzer {
278    tables: StackStack<(SymbolMap, SymbolTableType)>,
279}
280
281impl SymbolTableAnalyzer {
282    fn analyze_symbol_table(&mut self, symbol_table: &mut SymbolTable) -> SymbolTableResult {
283        let symbols = std::mem::take(&mut symbol_table.symbols);
284        let sub_tables = &mut *symbol_table.sub_tables;
285
286        let mut info = (symbols, symbol_table.typ);
287        self.tables.with_append(&mut info, |list| {
288            let inner_scope = unsafe { &mut *(list as *mut _ as *mut SymbolTableAnalyzer) };
289            // Analyze sub scopes:
290            for sub_table in sub_tables.iter_mut() {
291                inner_scope.analyze_symbol_table(sub_table)?;
292            }
293            Ok(())
294        })?;
295
296        symbol_table.symbols = info.0;
297
298        // Analyze symbols:
299        for symbol in symbol_table.symbols.values_mut() {
300            self.analyze_symbol(symbol, symbol_table.typ, sub_tables)?;
301        }
302        Ok(())
303    }
304
305    fn analyze_symbol(
306        &mut self,
307        symbol: &mut Symbol,
308        st_typ: SymbolTableType,
309        sub_tables: &[SymbolTable],
310    ) -> SymbolTableResult {
311        if symbol
312            .flags
313            .contains(SymbolFlags::ASSIGNED_IN_COMPREHENSION)
314            && st_typ == SymbolTableType::Comprehension
315        {
316            // propagate symbol to next higher level that can hold it,
317            // i.e., function or module. Comprehension is skipped and
318            // Class is not allowed and detected as error.
319            //symbol.scope = SymbolScope::Nonlocal;
320            self.analyze_symbol_comprehension(symbol, 0)?
321        } else {
322            match symbol.scope {
323                SymbolScope::Free => {
324                    if !self.tables.as_ref().is_empty() {
325                        let scope_depth = self.tables.as_ref().len();
326                        // check if the name is already defined in any outer scope
327                        // therefore
328                        if scope_depth < 2
329                            || self.found_in_outer_scope(&symbol.name) != Some(SymbolScope::Free)
330                        {
331                            return Err(SymbolTableError {
332                                error: format!("no binding for nonlocal '{}' found", symbol.name),
333                                // TODO: accurate location info, somehow
334                                location: None,
335                            });
336                        }
337                    } else {
338                        return Err(SymbolTableError {
339                            error: format!(
340                                "nonlocal {} defined at place without an enclosing scope",
341                                symbol.name
342                            ),
343                            // TODO: accurate location info, somehow
344                            location: None,
345                        });
346                    }
347                }
348                SymbolScope::GlobalExplicit | SymbolScope::GlobalImplicit => {
349                    // TODO: add more checks for globals?
350                }
351                SymbolScope::Local | SymbolScope::Cell => {
352                    // all is well
353                }
354                SymbolScope::Unknown => {
355                    // Try hard to figure out what the scope of this symbol is.
356                    let scope = if symbol.is_bound() {
357                        self.found_in_inner_scope(sub_tables, &symbol.name, st_typ)
358                            .unwrap_or(SymbolScope::Local)
359                    } else if let Some(scope) = self.found_in_outer_scope(&symbol.name) {
360                        scope
361                    } else if self.tables.is_empty() {
362                        // Don't make assumptions when we don't know.
363                        SymbolScope::Unknown
364                    } else {
365                        // If there are scopes above we assume global.
366                        SymbolScope::GlobalImplicit
367                    };
368                    symbol.scope = scope;
369                }
370            }
371        }
372        Ok(())
373    }
374
375    fn found_in_outer_scope(&mut self, name: &str) -> Option<SymbolScope> {
376        let mut decl_depth = None;
377        for (i, (symbols, typ)) in self.tables.iter().rev().enumerate() {
378            if matches!(typ, SymbolTableType::Module)
379                || matches!(typ, SymbolTableType::Class if name != "__class__")
380            {
381                continue;
382            }
383            if let Some(sym) = symbols.get(name) {
384                match sym.scope {
385                    SymbolScope::GlobalExplicit => return Some(SymbolScope::GlobalExplicit),
386                    SymbolScope::GlobalImplicit => {}
387                    _ => {
388                        if sym.is_bound() {
389                            decl_depth = Some(i);
390                            break;
391                        }
392                    }
393                }
394            }
395        }
396
397        if let Some(decl_depth) = decl_depth {
398            // decl_depth is the number of tables between the current one and
399            // the one that declared the cell var
400            for (table, typ) in self.tables.iter_mut().rev().take(decl_depth) {
401                if let SymbolTableType::Class = typ {
402                    if let Some(free_class) = table.get_mut(name) {
403                        free_class.flags.insert(SymbolFlags::FREE_CLASS)
404                    } else {
405                        let mut symbol = Symbol::new(name);
406                        symbol.flags.insert(SymbolFlags::FREE_CLASS);
407                        symbol.scope = SymbolScope::Free;
408                        table.insert(name.to_owned(), symbol);
409                    }
410                } else if !table.contains_key(name) {
411                    let mut symbol = Symbol::new(name);
412                    symbol.scope = SymbolScope::Free;
413                    // symbol.is_referenced = true;
414                    table.insert(name.to_owned(), symbol);
415                }
416            }
417        }
418
419        decl_depth.map(|_| SymbolScope::Free)
420    }
421
422    fn found_in_inner_scope(
423        &self,
424        sub_tables: &[SymbolTable],
425        name: &str,
426        st_typ: SymbolTableType,
427    ) -> Option<SymbolScope> {
428        sub_tables.iter().find_map(|st| {
429            let sym = st.symbols.get(name)?;
430            if sym.scope == SymbolScope::Free || sym.flags.contains(SymbolFlags::FREE_CLASS) {
431                if st_typ == SymbolTableType::Class && name != "__class__" {
432                    None
433                } else {
434                    Some(SymbolScope::Cell)
435                }
436            } else if sym.scope == SymbolScope::GlobalExplicit && self.tables.is_empty() {
437                // the symbol is defined on the module level, and an inner scope declares
438                // a global that points to it
439                Some(SymbolScope::GlobalExplicit)
440            } else {
441                None
442            }
443        })
444    }
445
446    // Implements the symbol analysis and scope extension for names
447    // assigned by a named expression in a comprehension. See:
448    // https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Python/symtable.c#L1435
449    fn analyze_symbol_comprehension(
450        &mut self,
451        symbol: &mut Symbol,
452        parent_offset: usize,
453    ) -> SymbolTableResult {
454        // when this is called, we expect to be in the direct parent scope of the scope that contains 'symbol'
455        let last = self.tables.iter_mut().rev().nth(parent_offset).unwrap();
456        let symbols = &mut last.0;
457        let table_type = last.1;
458
459        // it is not allowed to use an iterator variable as assignee in a named expression
460        if symbol.flags.contains(SymbolFlags::ITER) {
461            return Err(SymbolTableError {
462                error: format!(
463                    "assignment expression cannot rebind comprehension iteration variable {}",
464                    symbol.name
465                ),
466                // TODO: accurate location info, somehow
467                location: None,
468            });
469        }
470
471        match table_type {
472            SymbolTableType::Module => {
473                symbol.scope = SymbolScope::GlobalImplicit;
474            }
475            SymbolTableType::Class => {
476                // named expressions are forbidden in comprehensions on class scope
477                return Err(SymbolTableError {
478                    error: "assignment expression within a comprehension cannot be used in a class body".to_string(),
479                    // TODO: accurate location info, somehow
480                    location: None,
481                });
482            }
483            SymbolTableType::Function => {
484                if let Some(parent_symbol) = symbols.get_mut(&symbol.name) {
485                    if let SymbolScope::Unknown = parent_symbol.scope {
486                        // this information is new, as the assignment is done in inner scope
487                        parent_symbol.flags.insert(SymbolFlags::ASSIGNED);
488                    }
489
490                    symbol.scope = if parent_symbol.is_global() {
491                        parent_symbol.scope
492                    } else {
493                        SymbolScope::Free
494                    };
495                } else {
496                    let mut cloned_sym = symbol.clone();
497                    cloned_sym.scope = SymbolScope::Cell;
498                    last.0.insert(cloned_sym.name.to_owned(), cloned_sym);
499                }
500            }
501            SymbolTableType::Comprehension => {
502                // TODO check for conflicts - requires more context information about variables
503                match symbols.get_mut(&symbol.name) {
504                    Some(parent_symbol) => {
505                        // check if assignee is an iterator in top scope
506                        if parent_symbol.flags.contains(SymbolFlags::ITER) {
507                            return Err(SymbolTableError {
508                                error: format!("assignment expression cannot rebind comprehension iteration variable {}", symbol.name),
509                                location: None,
510                            });
511                        }
512
513                        // we synthesize the assignment to the symbol from inner scope
514                        parent_symbol.flags.insert(SymbolFlags::ASSIGNED); // more checks are required
515                    }
516                    None => {
517                        // extend the scope of the inner symbol
518                        // as we are in a nested comprehension, we expect that the symbol is needed
519                        // outside, too, and set it therefore to non-local scope. I.e., we expect to
520                        // find a definition on a higher level
521                        let mut cloned_sym = symbol.clone();
522                        cloned_sym.scope = SymbolScope::Free;
523                        last.0.insert(cloned_sym.name.to_owned(), cloned_sym);
524                    }
525                }
526
527                self.analyze_symbol_comprehension(symbol, parent_offset + 1)?;
528            }
529            SymbolTableType::TypeParams => {
530                todo!("analyze symbol comprehension for type params");
531            }
532        }
533        Ok(())
534    }
535}
536
537#[derive(Debug, Clone)]
538enum SymbolUsage {
539    Global,
540    Nonlocal,
541    Used,
542    Assigned,
543    Imported,
544    AnnotationAssigned,
545    Parameter,
546    AnnotationParameter,
547    AssignedNamedExprInComprehension,
548    Iter,
549}
550
551struct SymbolTableBuilder {
552    class_name: Option<String>,
553    // Scope stack.
554    tables: Vec<SymbolTable>,
555    future_annotations: bool,
556}
557
558/// Enum to indicate in what mode an expression
559/// was used.
560/// In cpython this is stored in the AST, but I think this
561/// is not logical, since it is not context free.
562#[derive(Copy, Clone, PartialEq)]
563enum ExpressionContext {
564    Load,
565    Store,
566    Delete,
567    Iter,
568    IterDefinitionExp,
569}
570
571impl SymbolTableBuilder {
572    fn new() -> Self {
573        let mut this = Self {
574            class_name: None,
575            tables: vec![],
576            future_annotations: false,
577        };
578        this.enter_scope("top", SymbolTableType::Module, 0);
579        this
580    }
581}
582
583impl SymbolTableBuilder {
584    fn finish(mut self) -> Result<SymbolTable, SymbolTableError> {
585        assert_eq!(self.tables.len(), 1);
586        let mut symbol_table = self.tables.pop().unwrap();
587        analyze_symbol_table(&mut symbol_table)?;
588        Ok(symbol_table)
589    }
590
591    fn enter_scope(&mut self, name: &str, typ: SymbolTableType, line_number: u32) {
592        let is_nested = self
593            .tables
594            .last()
595            .map(|table| table.is_nested || table.typ == SymbolTableType::Function)
596            .unwrap_or(false);
597        let table = SymbolTable::new(name.to_owned(), typ, line_number, is_nested);
598        self.tables.push(table);
599    }
600
601    /// Pop symbol table and add to sub table of parent table.
602    fn leave_scope(&mut self) {
603        let table = self.tables.pop().unwrap();
604        self.tables.last_mut().unwrap().sub_tables.push(table);
605    }
606
607    fn scan_statements(&mut self, statements: &[ast::located::Stmt]) -> SymbolTableResult {
608        for statement in statements {
609            self.scan_statement(statement)?;
610        }
611        Ok(())
612    }
613
614    fn scan_parameters(
615        &mut self,
616        parameters: &[ast::located::ArgWithDefault],
617    ) -> SymbolTableResult {
618        for parameter in parameters {
619            let usage = if parameter.def.annotation.is_some() {
620                SymbolUsage::AnnotationParameter
621            } else {
622                SymbolUsage::Parameter
623            };
624            self.register_name(parameter.def.arg.as_str(), usage, parameter.def.location())?;
625        }
626        Ok(())
627    }
628
629    fn scan_parameter(&mut self, parameter: &ast::located::Arg) -> SymbolTableResult {
630        let usage = if parameter.annotation.is_some() {
631            SymbolUsage::AnnotationParameter
632        } else {
633            SymbolUsage::Parameter
634        };
635        self.register_name(parameter.arg.as_str(), usage, parameter.location())
636    }
637
638    fn scan_annotation(&mut self, annotation: &ast::located::Expr) -> SymbolTableResult {
639        if self.future_annotations {
640            Ok(())
641        } else {
642            self.scan_expression(annotation, ExpressionContext::Load)
643        }
644    }
645
646    fn scan_statement(&mut self, statement: &ast::located::Stmt) -> SymbolTableResult {
647        use ast::located::*;
648        if let Stmt::ImportFrom(StmtImportFrom { module, names, .. }) = &statement {
649            if module.as_ref().map(|id| id.as_str()) == Some("__future__") {
650                for feature in names {
651                    if &feature.name == "annotations" {
652                        self.future_annotations = true;
653                    }
654                }
655            }
656        }
657        match &statement {
658            Stmt::Global(StmtGlobal { names, range }) => {
659                for name in names {
660                    self.register_name(name.as_str(), SymbolUsage::Global, range.start)?;
661                }
662            }
663            Stmt::Nonlocal(StmtNonlocal { names, range }) => {
664                for name in names {
665                    self.register_name(name.as_str(), SymbolUsage::Nonlocal, range.start)?;
666                }
667            }
668            Stmt::FunctionDef(StmtFunctionDef {
669                name,
670                body,
671                args,
672                decorator_list,
673                type_params,
674                returns,
675                range,
676                ..
677            })
678            | Stmt::AsyncFunctionDef(StmtAsyncFunctionDef {
679                name,
680                body,
681                args,
682                decorator_list,
683                type_params,
684                returns,
685                range,
686                ..
687            }) => {
688                self.scan_expressions(decorator_list, ExpressionContext::Load)?;
689                self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
690                if let Some(expression) = returns {
691                    self.scan_annotation(expression)?;
692                }
693                if !type_params.is_empty() {
694                    self.enter_scope(
695                        &format!("<generic parameters of {}>", name.as_str()),
696                        SymbolTableType::TypeParams,
697                        range.start.row.get(),
698                    );
699                    self.scan_type_params(type_params)?;
700                }
701                self.enter_function(name.as_str(), args, range.start.row)?;
702                self.scan_statements(body)?;
703                self.leave_scope();
704                if !type_params.is_empty() {
705                    self.leave_scope();
706                }
707            }
708            Stmt::ClassDef(StmtClassDef {
709                name,
710                body,
711                bases,
712                keywords,
713                decorator_list,
714                type_params,
715                range,
716            }) => {
717                if !type_params.is_empty() {
718                    self.enter_scope(
719                        &format!("<generic parameters of {}>", name.as_str()),
720                        SymbolTableType::TypeParams,
721                        range.start.row.get(),
722                    );
723                    self.scan_type_params(type_params)?;
724                }
725                self.enter_scope(name.as_str(), SymbolTableType::Class, range.start.row.get());
726                let prev_class = std::mem::replace(&mut self.class_name, Some(name.to_string()));
727                self.register_name("__module__", SymbolUsage::Assigned, range.start)?;
728                self.register_name("__qualname__", SymbolUsage::Assigned, range.start)?;
729                self.register_name("__doc__", SymbolUsage::Assigned, range.start)?;
730                self.register_name("__class__", SymbolUsage::Assigned, range.start)?;
731                self.scan_statements(body)?;
732                self.leave_scope();
733                self.class_name = prev_class;
734                self.scan_expressions(bases, ExpressionContext::Load)?;
735                for keyword in keywords {
736                    self.scan_expression(&keyword.value, ExpressionContext::Load)?;
737                }
738                if !type_params.is_empty() {
739                    self.leave_scope();
740                }
741                self.scan_expressions(decorator_list, ExpressionContext::Load)?;
742                self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
743            }
744            Stmt::Expr(StmtExpr { value, .. }) => {
745                self.scan_expression(value, ExpressionContext::Load)?
746            }
747            Stmt::If(StmtIf {
748                test, body, orelse, ..
749            }) => {
750                self.scan_expression(test, ExpressionContext::Load)?;
751                self.scan_statements(body)?;
752                self.scan_statements(orelse)?;
753            }
754            Stmt::For(StmtFor {
755                target,
756                iter,
757                body,
758                orelse,
759                ..
760            })
761            | Stmt::AsyncFor(StmtAsyncFor {
762                target,
763                iter,
764                body,
765                orelse,
766                ..
767            }) => {
768                self.scan_expression(target, ExpressionContext::Store)?;
769                self.scan_expression(iter, ExpressionContext::Load)?;
770                self.scan_statements(body)?;
771                self.scan_statements(orelse)?;
772            }
773            Stmt::While(StmtWhile {
774                test, body, orelse, ..
775            }) => {
776                self.scan_expression(test, ExpressionContext::Load)?;
777                self.scan_statements(body)?;
778                self.scan_statements(orelse)?;
779            }
780            Stmt::Break(_) | Stmt::Continue(_) | Stmt::Pass(_) => {
781                // No symbols here.
782            }
783            Stmt::Import(StmtImport { names, range })
784            | Stmt::ImportFrom(StmtImportFrom { names, range, .. }) => {
785                for name in names {
786                    if let Some(alias) = &name.asname {
787                        // `import my_module as my_alias`
788                        self.register_name(alias.as_str(), SymbolUsage::Imported, range.start)?;
789                    } else {
790                        // `import module`
791                        self.register_name(
792                            name.name.split('.').next().unwrap(),
793                            SymbolUsage::Imported,
794                            range.start,
795                        )?;
796                    }
797                }
798            }
799            Stmt::Return(StmtReturn { value, .. }) => {
800                if let Some(expression) = value {
801                    self.scan_expression(expression, ExpressionContext::Load)?;
802                }
803            }
804            Stmt::Assert(StmtAssert { test, msg, .. }) => {
805                self.scan_expression(test, ExpressionContext::Load)?;
806                if let Some(expression) = msg {
807                    self.scan_expression(expression, ExpressionContext::Load)?;
808                }
809            }
810            Stmt::Delete(StmtDelete { targets, .. }) => {
811                self.scan_expressions(targets, ExpressionContext::Delete)?;
812            }
813            Stmt::Assign(StmtAssign { targets, value, .. }) => {
814                self.scan_expressions(targets, ExpressionContext::Store)?;
815                self.scan_expression(value, ExpressionContext::Load)?;
816            }
817            Stmt::AugAssign(StmtAugAssign { target, value, .. }) => {
818                self.scan_expression(target, ExpressionContext::Store)?;
819                self.scan_expression(value, ExpressionContext::Load)?;
820            }
821            Stmt::AnnAssign(StmtAnnAssign {
822                target,
823                annotation,
824                value,
825                simple,
826                range,
827            }) => {
828                // https://github.com/python/cpython/blob/main/Python/symtable.c#L1233
829                match &**target {
830                    Expr::Name(ast::ExprName { id, .. }) if *simple => {
831                        self.register_name(
832                            id.as_str(),
833                            SymbolUsage::AnnotationAssigned,
834                            range.start,
835                        )?;
836                    }
837                    _ => {
838                        self.scan_expression(target, ExpressionContext::Store)?;
839                    }
840                }
841                self.scan_annotation(annotation)?;
842                if let Some(value) = value {
843                    self.scan_expression(value, ExpressionContext::Load)?;
844                }
845            }
846            Stmt::With(StmtWith { items, body, .. })
847            | Stmt::AsyncWith(StmtAsyncWith { items, body, .. }) => {
848                for item in items {
849                    self.scan_expression(&item.context_expr, ExpressionContext::Load)?;
850                    if let Some(expression) = &item.optional_vars {
851                        self.scan_expression(expression, ExpressionContext::Store)?;
852                    }
853                }
854                self.scan_statements(body)?;
855            }
856            Stmt::Try(StmtTry {
857                body,
858                handlers,
859                orelse,
860                finalbody,
861                range,
862            })
863            | Stmt::TryStar(StmtTryStar {
864                body,
865                handlers,
866                orelse,
867                finalbody,
868                range,
869            }) => {
870                self.scan_statements(body)?;
871                for handler in handlers {
872                    let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
873                        type_,
874                        name,
875                        body,
876                        ..
877                    }) = &handler;
878                    if let Some(expression) = type_ {
879                        self.scan_expression(expression, ExpressionContext::Load)?;
880                    }
881                    if let Some(name) = name {
882                        self.register_name(name.as_str(), SymbolUsage::Assigned, range.start)?;
883                    }
884                    self.scan_statements(body)?;
885                }
886                self.scan_statements(orelse)?;
887                self.scan_statements(finalbody)?;
888            }
889            Stmt::Match(StmtMatch { subject, .. }) => {
890                return Err(SymbolTableError {
891                    error: "match expression is not implemented yet".to_owned(),
892                    location: Some(subject.location()),
893                });
894            }
895            Stmt::Raise(StmtRaise { exc, cause, .. }) => {
896                if let Some(expression) = exc {
897                    self.scan_expression(expression, ExpressionContext::Load)?;
898                }
899                if let Some(expression) = cause {
900                    self.scan_expression(expression, ExpressionContext::Load)?;
901                }
902            }
903            Stmt::TypeAlias(StmtTypeAlias {
904                name,
905                value,
906                type_params,
907                range,
908            }) => {
909                if !type_params.is_empty() {
910                    self.enter_scope(
911                        &name.to_string(),
912                        SymbolTableType::TypeParams,
913                        range.start.row.get(),
914                    );
915                    self.scan_type_params(type_params)?;
916                    self.scan_expression(value, ExpressionContext::Load)?;
917                    self.leave_scope();
918                } else {
919                    self.scan_expression(value, ExpressionContext::Load)?;
920                }
921                self.scan_expression(name, ExpressionContext::Store)?;
922            }
923        }
924        Ok(())
925    }
926
927    fn scan_expressions(
928        &mut self,
929        expressions: &[ast::located::Expr],
930        context: ExpressionContext,
931    ) -> SymbolTableResult {
932        for expression in expressions {
933            self.scan_expression(expression, context)?;
934        }
935        Ok(())
936    }
937
938    fn scan_expression(
939        &mut self,
940        expression: &ast::located::Expr,
941        context: ExpressionContext,
942    ) -> SymbolTableResult {
943        use ast::located::*;
944        match expression {
945            Expr::BinOp(ExprBinOp {
946                left,
947                right,
948                range: _,
949                ..
950            }) => {
951                self.scan_expression(left, context)?;
952                self.scan_expression(right, context)?;
953            }
954            Expr::BoolOp(ExprBoolOp {
955                values, range: _, ..
956            }) => {
957                self.scan_expressions(values, context)?;
958            }
959            Expr::Compare(ExprCompare {
960                left,
961                comparators,
962                range: _,
963                ..
964            }) => {
965                self.scan_expression(left, context)?;
966                self.scan_expressions(comparators, context)?;
967            }
968            Expr::Subscript(ExprSubscript {
969                value,
970                slice,
971                range: _,
972                ..
973            }) => {
974                self.scan_expression(value, ExpressionContext::Load)?;
975                self.scan_expression(slice, ExpressionContext::Load)?;
976            }
977            Expr::Attribute(ExprAttribute {
978                value, range: _, ..
979            }) => {
980                self.scan_expression(value, ExpressionContext::Load)?;
981            }
982            Expr::Dict(ExprDict {
983                keys,
984                values,
985                range: _,
986            }) => {
987                for (key, value) in keys.iter().zip(values.iter()) {
988                    if let Some(key) = key {
989                        self.scan_expression(key, context)?;
990                    }
991                    self.scan_expression(value, context)?;
992                }
993            }
994            Expr::Await(ExprAwait { value, range: _ }) => {
995                self.scan_expression(value, context)?;
996            }
997            Expr::Yield(ExprYield { value, range: _ }) => {
998                if let Some(expression) = value {
999                    self.scan_expression(expression, context)?;
1000                }
1001            }
1002            Expr::YieldFrom(ExprYieldFrom { value, range: _ }) => {
1003                self.scan_expression(value, context)?;
1004            }
1005            Expr::UnaryOp(ExprUnaryOp {
1006                operand, range: _, ..
1007            }) => {
1008                self.scan_expression(operand, context)?;
1009            }
1010            Expr::Constant(ExprConstant { range: _, .. }) => {}
1011            Expr::Starred(ExprStarred {
1012                value, range: _, ..
1013            }) => {
1014                self.scan_expression(value, context)?;
1015            }
1016            Expr::Tuple(ExprTuple { elts, range: _, .. })
1017            | Expr::Set(ExprSet { elts, range: _, .. })
1018            | Expr::List(ExprList { elts, range: _, .. }) => {
1019                self.scan_expressions(elts, context)?;
1020            }
1021            Expr::Slice(ExprSlice {
1022                lower,
1023                upper,
1024                step,
1025                range: _,
1026            }) => {
1027                if let Some(lower) = lower {
1028                    self.scan_expression(lower, context)?;
1029                }
1030                if let Some(upper) = upper {
1031                    self.scan_expression(upper, context)?;
1032                }
1033                if let Some(step) = step {
1034                    self.scan_expression(step, context)?;
1035                }
1036            }
1037            Expr::GeneratorExp(ExprGeneratorExp {
1038                elt,
1039                generators,
1040                range,
1041            }) => {
1042                self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
1043            }
1044            Expr::ListComp(ExprListComp {
1045                elt,
1046                generators,
1047                range,
1048            }) => {
1049                self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
1050            }
1051            Expr::SetComp(ExprSetComp {
1052                elt,
1053                generators,
1054                range,
1055            }) => {
1056                self.scan_comprehension("genexpr", elt, None, generators, range.start)?;
1057            }
1058            Expr::DictComp(ExprDictComp {
1059                key,
1060                value,
1061                generators,
1062                range,
1063            }) => {
1064                self.scan_comprehension("genexpr", key, Some(value), generators, range.start)?;
1065            }
1066            Expr::Call(ExprCall {
1067                func,
1068                args,
1069                keywords,
1070                range: _,
1071            }) => {
1072                match context {
1073                    ExpressionContext::IterDefinitionExp => {
1074                        self.scan_expression(func, ExpressionContext::IterDefinitionExp)?;
1075                    }
1076                    _ => {
1077                        self.scan_expression(func, ExpressionContext::Load)?;
1078                    }
1079                }
1080
1081                self.scan_expressions(args, ExpressionContext::Load)?;
1082                for keyword in keywords {
1083                    self.scan_expression(&keyword.value, ExpressionContext::Load)?;
1084                }
1085            }
1086            Expr::FormattedValue(ExprFormattedValue {
1087                value,
1088                format_spec,
1089                range: _,
1090                ..
1091            }) => {
1092                self.scan_expression(value, ExpressionContext::Load)?;
1093                if let Some(spec) = format_spec {
1094                    self.scan_expression(spec, ExpressionContext::Load)?;
1095                }
1096            }
1097            Expr::JoinedStr(ExprJoinedStr { values, range: _ }) => {
1098                for value in values {
1099                    self.scan_expression(value, ExpressionContext::Load)?;
1100                }
1101            }
1102            Expr::Name(ExprName { id, range, .. }) => {
1103                let id = id.as_str();
1104                // Determine the contextual usage of this symbol:
1105                match context {
1106                    ExpressionContext::Delete => {
1107                        self.register_name(id, SymbolUsage::Assigned, range.start)?;
1108                        self.register_name(id, SymbolUsage::Used, range.start)?;
1109                    }
1110                    ExpressionContext::Load | ExpressionContext::IterDefinitionExp => {
1111                        self.register_name(id, SymbolUsage::Used, range.start)?;
1112                    }
1113                    ExpressionContext::Store => {
1114                        self.register_name(id, SymbolUsage::Assigned, range.start)?;
1115                    }
1116                    ExpressionContext::Iter => {
1117                        self.register_name(id, SymbolUsage::Iter, range.start)?;
1118                    }
1119                }
1120                // Interesting stuff about the __class__ variable:
1121                // https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
1122                if context == ExpressionContext::Load
1123                    && self.tables.last().unwrap().typ == SymbolTableType::Function
1124                    && id == "super"
1125                {
1126                    self.register_name("__class__", SymbolUsage::Used, range.start)?;
1127                }
1128            }
1129            Expr::Lambda(ExprLambda {
1130                args,
1131                body,
1132                range: _,
1133            }) => {
1134                self.enter_function("lambda", args, expression.location().row)?;
1135                match context {
1136                    ExpressionContext::IterDefinitionExp => {
1137                        self.scan_expression(body, ExpressionContext::IterDefinitionExp)?;
1138                    }
1139                    _ => {
1140                        self.scan_expression(body, ExpressionContext::Load)?;
1141                    }
1142                }
1143                self.leave_scope();
1144            }
1145            Expr::IfExp(ExprIfExp {
1146                test,
1147                body,
1148                orelse,
1149                range: _,
1150            }) => {
1151                self.scan_expression(test, ExpressionContext::Load)?;
1152                self.scan_expression(body, ExpressionContext::Load)?;
1153                self.scan_expression(orelse, ExpressionContext::Load)?;
1154            }
1155
1156            Expr::NamedExpr(ExprNamedExpr {
1157                target,
1158                value,
1159                range,
1160            }) => {
1161                // named expressions are not allowed in the definition of
1162                // comprehension iterator definitions
1163                if let ExpressionContext::IterDefinitionExp = context {
1164                    return Err(SymbolTableError {
1165                        error: "assignment expression cannot be used in a comprehension iterable expression".to_string(),
1166                        location: Some(target.location()),
1167                    });
1168                }
1169
1170                self.scan_expression(value, ExpressionContext::Load)?;
1171
1172                // special handling for assigned identifier in named expressions
1173                // that are used in comprehensions. This required to correctly
1174                // propagate the scope of the named assigned named and not to
1175                // propagate inner names.
1176                if let Expr::Name(ExprName { id, .. }) = &**target {
1177                    let id = id.as_str();
1178                    let table = self.tables.last().unwrap();
1179                    if table.typ == SymbolTableType::Comprehension {
1180                        self.register_name(
1181                            id,
1182                            SymbolUsage::AssignedNamedExprInComprehension,
1183                            range.start,
1184                        )?;
1185                    } else {
1186                        // omit one recursion. When the handling of an store changes for
1187                        // Identifiers this needs adapted - more forward safe would be
1188                        // calling scan_expression directly.
1189                        self.register_name(id, SymbolUsage::Assigned, range.start)?;
1190                    }
1191                } else {
1192                    self.scan_expression(target, ExpressionContext::Store)?;
1193                }
1194            }
1195        }
1196        Ok(())
1197    }
1198
1199    fn scan_comprehension(
1200        &mut self,
1201        scope_name: &str,
1202        elt1: &ast::located::Expr,
1203        elt2: Option<&ast::located::Expr>,
1204        generators: &[ast::located::Comprehension],
1205        location: SourceLocation,
1206    ) -> SymbolTableResult {
1207        // Comprehensions are compiled as functions, so create a scope for them:
1208        self.enter_scope(
1209            scope_name,
1210            SymbolTableType::Comprehension,
1211            location.row.get(),
1212        );
1213
1214        // Register the passed argument to the generator function as the name ".0"
1215        self.register_name(".0", SymbolUsage::Parameter, location)?;
1216
1217        self.scan_expression(elt1, ExpressionContext::Load)?;
1218        if let Some(elt2) = elt2 {
1219            self.scan_expression(elt2, ExpressionContext::Load)?;
1220        }
1221
1222        let mut is_first_generator = true;
1223        for generator in generators {
1224            self.scan_expression(&generator.target, ExpressionContext::Iter)?;
1225            if is_first_generator {
1226                is_first_generator = false;
1227            } else {
1228                self.scan_expression(&generator.iter, ExpressionContext::IterDefinitionExp)?;
1229            }
1230
1231            for if_expr in &generator.ifs {
1232                self.scan_expression(if_expr, ExpressionContext::Load)?;
1233            }
1234        }
1235
1236        self.leave_scope();
1237
1238        // The first iterable is passed as an argument into the created function:
1239        assert!(!generators.is_empty());
1240        self.scan_expression(&generators[0].iter, ExpressionContext::IterDefinitionExp)?;
1241
1242        Ok(())
1243    }
1244
1245    fn scan_type_params(&mut self, type_params: &[ast::located::TypeParam]) -> SymbolTableResult {
1246        for type_param in type_params {
1247            match type_param {
1248                ast::located::TypeParam::TypeVar(ast::TypeParamTypeVar {
1249                    name,
1250                    bound,
1251                    range: type_var_range,
1252                }) => {
1253                    self.register_name(name.as_str(), SymbolUsage::Assigned, type_var_range.start)?;
1254                    if let Some(binding) = bound {
1255                        self.scan_expression(binding, ExpressionContext::Load)?;
1256                    }
1257                }
1258                ast::located::TypeParam::ParamSpec(_) => todo!(),
1259                ast::located::TypeParam::TypeVarTuple(_) => todo!(),
1260            }
1261        }
1262        Ok(())
1263    }
1264
1265    fn enter_function(
1266        &mut self,
1267        name: &str,
1268        args: &ast::located::Arguments,
1269        line_number: LineNumber,
1270    ) -> SymbolTableResult {
1271        // Evaluate eventual default parameters:
1272        for default in args
1273            .posonlyargs
1274            .iter()
1275            .chain(args.args.iter())
1276            .chain(args.kwonlyargs.iter())
1277            .filter_map(|arg| arg.default.as_ref())
1278        {
1279            self.scan_expression(default, ExpressionContext::Load)?; // not ExprContext?
1280        }
1281
1282        // Annotations are scanned in outer scope:
1283        for annotation in args
1284            .posonlyargs
1285            .iter()
1286            .chain(args.args.iter())
1287            .chain(args.kwonlyargs.iter())
1288            .filter_map(|arg| arg.def.annotation.as_ref())
1289        {
1290            self.scan_annotation(annotation)?;
1291        }
1292        if let Some(annotation) = args.vararg.as_ref().and_then(|arg| arg.annotation.as_ref()) {
1293            self.scan_annotation(annotation)?;
1294        }
1295        if let Some(annotation) = args.kwarg.as_ref().and_then(|arg| arg.annotation.as_ref()) {
1296            self.scan_annotation(annotation)?;
1297        }
1298
1299        self.enter_scope(name, SymbolTableType::Function, line_number.get());
1300
1301        // Fill scope with parameter names:
1302        self.scan_parameters(&args.posonlyargs)?;
1303        self.scan_parameters(&args.args)?;
1304        self.scan_parameters(&args.kwonlyargs)?;
1305        if let Some(name) = &args.vararg {
1306            self.scan_parameter(name)?;
1307        }
1308        if let Some(name) = &args.kwarg {
1309            self.scan_parameter(name)?;
1310        }
1311        Ok(())
1312    }
1313
1314    fn register_name(
1315        &mut self,
1316        name: &str,
1317        role: SymbolUsage,
1318        location: SourceLocation,
1319    ) -> SymbolTableResult {
1320        let location = Some(location);
1321        let scope_depth = self.tables.len();
1322        let table = self.tables.last_mut().unwrap();
1323
1324        let name = mangle_name(self.class_name.as_deref(), name);
1325        // Some checks for the symbol that present on this scope level:
1326        let symbol = if let Some(symbol) = table.symbols.get_mut(name.as_ref()) {
1327            let flags = &symbol.flags;
1328            // Role already set..
1329            match role {
1330                SymbolUsage::Global if !symbol.is_global() => {
1331                    if flags.contains(SymbolFlags::PARAMETER) {
1332                        return Err(SymbolTableError {
1333                            error: format!("name '{name}' is parameter and global"),
1334                            location,
1335                        });
1336                    }
1337                    if flags.contains(SymbolFlags::REFERENCED) {
1338                        return Err(SymbolTableError {
1339                            error: format!("name '{name}' is used prior to global declaration"),
1340                            location,
1341                        });
1342                    }
1343                    if flags.contains(SymbolFlags::ANNOTATED) {
1344                        return Err(SymbolTableError {
1345                            error: format!("annotated name '{name}' can't be global"),
1346                            location,
1347                        });
1348                    }
1349                    if flags.contains(SymbolFlags::ASSIGNED) {
1350                        return Err(SymbolTableError {
1351                            error: format!(
1352                                "name '{name}' is assigned to before global declaration"
1353                            ),
1354                            location,
1355                        });
1356                    }
1357                }
1358                SymbolUsage::Nonlocal => {
1359                    if flags.contains(SymbolFlags::PARAMETER) {
1360                        return Err(SymbolTableError {
1361                            error: format!("name '{name}' is parameter and nonlocal"),
1362                            location,
1363                        });
1364                    }
1365                    if flags.contains(SymbolFlags::REFERENCED) {
1366                        return Err(SymbolTableError {
1367                            error: format!("name '{name}' is used prior to nonlocal declaration"),
1368                            location,
1369                        });
1370                    }
1371                    if flags.contains(SymbolFlags::ANNOTATED) {
1372                        return Err(SymbolTableError {
1373                            error: format!("annotated name '{name}' can't be nonlocal"),
1374                            location,
1375                        });
1376                    }
1377                    if flags.contains(SymbolFlags::ASSIGNED) {
1378                        return Err(SymbolTableError {
1379                            error: format!(
1380                                "name '{name}' is assigned to before nonlocal declaration"
1381                            ),
1382                            location,
1383                        });
1384                    }
1385                }
1386                _ => {
1387                    // Ok?
1388                }
1389            }
1390            symbol
1391        } else {
1392            // The symbol does not present on this scope level.
1393            // Some checks to insert new symbol into symbol table:
1394            match role {
1395                SymbolUsage::Nonlocal if scope_depth < 2 => {
1396                    return Err(SymbolTableError {
1397                        error: format!("cannot define nonlocal '{name}' at top level."),
1398                        location,
1399                    })
1400                }
1401                _ => {
1402                    // Ok!
1403                }
1404            }
1405            // Insert symbol when required:
1406            let symbol = Symbol::new(name.as_ref());
1407            table.symbols.entry(name.into_owned()).or_insert(symbol)
1408        };
1409
1410        // Set proper scope and flags on symbol:
1411        let flags = &mut symbol.flags;
1412        match role {
1413            SymbolUsage::Nonlocal => {
1414                symbol.scope = SymbolScope::Free;
1415                flags.insert(SymbolFlags::NONLOCAL);
1416            }
1417            SymbolUsage::Imported => {
1418                flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::IMPORTED);
1419            }
1420            SymbolUsage::Parameter => {
1421                flags.insert(SymbolFlags::PARAMETER);
1422            }
1423            SymbolUsage::AnnotationParameter => {
1424                flags.insert(SymbolFlags::PARAMETER | SymbolFlags::ANNOTATED);
1425            }
1426            SymbolUsage::AnnotationAssigned => {
1427                flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ANNOTATED);
1428            }
1429            SymbolUsage::Assigned => {
1430                flags.insert(SymbolFlags::ASSIGNED);
1431            }
1432            SymbolUsage::AssignedNamedExprInComprehension => {
1433                flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ASSIGNED_IN_COMPREHENSION);
1434            }
1435            SymbolUsage::Global => {
1436                symbol.scope = SymbolScope::GlobalExplicit;
1437            }
1438            SymbolUsage::Used => {
1439                flags.insert(SymbolFlags::REFERENCED);
1440            }
1441            SymbolUsage::Iter => {
1442                flags.insert(SymbolFlags::ITER);
1443            }
1444        }
1445
1446        // and even more checking
1447        // it is not allowed to assign to iterator variables (by named expressions)
1448        if flags.contains(SymbolFlags::ITER | SymbolFlags::ASSIGNED)
1449        /*&& symbol.is_assign_named_expr_in_comprehension*/
1450        {
1451            return Err(SymbolTableError {
1452                error:
1453                    "assignment expression cannot be used in a comprehension iterable expression"
1454                        .to_string(),
1455                location,
1456            });
1457        }
1458        Ok(())
1459    }
1460}
1461
1462pub(crate) fn mangle_name<'a>(class_name: Option<&str>, name: &'a str) -> Cow<'a, str> {
1463    let class_name = match class_name {
1464        Some(n) => n,
1465        None => return name.into(),
1466    };
1467    if !name.starts_with("__") || name.ends_with("__") || name.contains('.') {
1468        return name.into();
1469    }
1470    // strip leading underscore
1471    let class_name = class_name.strip_prefix(|c| c == '_').unwrap_or(class_name);
1472    let mut ret = String::with_capacity(1 + class_name.len() + name.len());
1473    ret.push('_');
1474    ret.push_str(class_name);
1475    ret.push_str(name);
1476    ret.into()
1477}