Implements the symbol analysis and scope extension for names assigned by a named expression in a comprehension. See: https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Python/symtable.c#L1435
(
&mut self,
symbol: &mut Symbol,
parent_offset: usize,
)
| 832 | // assigned by a named expression in a comprehension. See: |
| 833 | // https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Python/symtable.c#L1435 |
| 834 | fn analyze_symbol_comprehension( |
| 835 | &mut self, |
| 836 | symbol: &mut Symbol, |
| 837 | parent_offset: usize, |
| 838 | ) -> SymbolTableResult { |
| 839 | // when this is called, we expect to be in the direct parent scope of the scope that contains 'symbol' |
| 840 | let last = self.tables.iter_mut().rev().nth(parent_offset).unwrap(); |
| 841 | let symbols = &mut last.0; |
| 842 | let table_type = last.1; |
| 843 | |
| 844 | // it is not allowed to use an iterator variable as assignee in a named expression |
| 845 | if symbol.flags.contains(SymbolFlags::ITER) { |
| 846 | return Err(SymbolTableError { |
| 847 | error: format!( |
| 848 | "assignment expression cannot rebind comprehension iteration variable {}", |
| 849 | symbol.name |
| 850 | ), |
| 851 | // TODO: accurate location info, somehow |
| 852 | location: None, |
| 853 | }); |
| 854 | } |
| 855 | |
| 856 | match table_type { |
| 857 | CompilerScope::Module => { |
| 858 | symbol.scope = SymbolScope::GlobalImplicit; |
| 859 | } |
| 860 | CompilerScope::Class => { |
| 861 | // named expressions are forbidden in comprehensions on class scope |
| 862 | return Err(SymbolTableError { |
| 863 | error: "assignment expression within a comprehension cannot be used in a class body".to_string(), |
| 864 | // TODO: accurate location info, somehow |
| 865 | location: None, |
| 866 | }); |
| 867 | } |
| 868 | CompilerScope::Function | CompilerScope::AsyncFunction | CompilerScope::Lambda => { |
| 869 | if let Some(parent_symbol) = symbols.get_mut(&symbol.name) { |
| 870 | if let SymbolScope::Unknown = parent_symbol.scope { |
| 871 | // this information is new, as the assignment is done in inner scope |
| 872 | parent_symbol.flags.insert(SymbolFlags::ASSIGNED); |
| 873 | } |
| 874 | |
| 875 | symbol.scope = if parent_symbol.is_global() { |
| 876 | parent_symbol.scope |
| 877 | } else { |
| 878 | SymbolScope::Free |
| 879 | }; |
| 880 | } else { |
| 881 | let mut cloned_sym = symbol.clone(); |
| 882 | cloned_sym.scope = SymbolScope::Cell; |
| 883 | last.0.insert(cloned_sym.name.to_owned(), cloned_sym); |
| 884 | } |
| 885 | } |
| 886 | CompilerScope::Comprehension => { |
| 887 | // TODO check for conflicts - requires more context information about variables |
| 888 | match symbols.get_mut(&symbol.name) { |
| 889 | Some(parent_symbol) => { |
| 890 | // check if assignee is an iterator in top scope |
| 891 | if parent_symbol.flags.contains(SymbolFlags::ITER) { |