mirror of
https://github.com/RustPython/Parser.git
synced 2025-07-14 00:25:17 +00:00
Merge pull request #1964 from TheAnyKey/TheAnyKey/p38_named_expression_completion_step2
Py3.8 Named expression completion - error handling and scoping
This commit is contained in:
commit
949c37620e
1 changed files with 102 additions and 10 deletions
|
@ -105,6 +105,10 @@ pub struct Symbol {
|
|||
// indicates if the symbol gets a value assigned by a named expression in a comprehension
|
||||
// this is required to correct the scope in the analysis.
|
||||
pub is_assign_namedexpr_in_comprehension: bool,
|
||||
|
||||
// inidicates that the symbol is used a bound iterator variable. We distinguish this case
|
||||
// from normal assignment to detect unallowed re-assignment to iterator variables.
|
||||
pub is_iter: bool,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
|
@ -118,6 +122,7 @@ impl Symbol {
|
|||
is_parameter: false,
|
||||
is_free: false,
|
||||
is_assign_namedexpr_in_comprehension: false,
|
||||
is_iter: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,6 +222,10 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
|||
if symbol.is_assign_namedexpr_in_comprehension
|
||||
&& curr_st_typ == SymbolTableType::Comprehension
|
||||
{
|
||||
// propagate symbol to next higher level that can hold it,
|
||||
// i.e., function or module. Comprehension is skipped and
|
||||
// Class is not allowed and detected as error.
|
||||
//symbol.scope = SymbolScope::Nonlocal;
|
||||
self.analyze_symbol_comprehension(symbol, 0)?
|
||||
} else {
|
||||
match symbol.scope {
|
||||
|
@ -303,16 +312,33 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
|||
let symbols = &mut last.0;
|
||||
let table_type = last.1;
|
||||
|
||||
// it is not allowed to use an iterator variable as assignee in a named expression
|
||||
if symbol.is_iter {
|
||||
return Err(SymbolTableError {
|
||||
error: format!(
|
||||
"assignment expression cannot rebind comprehension iteration variable {}",
|
||||
symbol.name
|
||||
),
|
||||
location: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
match table_type {
|
||||
SymbolTableType::Module => {
|
||||
symbol.scope = SymbolScope::Global;
|
||||
}
|
||||
SymbolTableType::Class => {}
|
||||
SymbolTableType::Class => {
|
||||
// named expressions are forbidden in comprehensions on class scope
|
||||
return Err(SymbolTableError {
|
||||
error: "assignment expression within a comprehension cannot be used in a class body".to_string(),
|
||||
location: Default::default(),
|
||||
} );
|
||||
}
|
||||
SymbolTableType::Function => {
|
||||
if let Some(parent_symbol) = symbols.get_mut(&symbol.name) {
|
||||
if let SymbolScope::Unknown = parent_symbol.scope {
|
||||
parent_symbol.is_assigned = true; // this information is new, as the asignment is done in inner scope
|
||||
self.analyze_unknown_symbol(symbol);
|
||||
//self.analyze_unknown_symbol(symbol); // not needed, symbol is analyzed anyhow when its scope is analyzed
|
||||
}
|
||||
|
||||
match symbol.scope {
|
||||
|
@ -323,17 +349,34 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
|||
symbol.scope = SymbolScope::Nonlocal;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut cloned_sym = symbol.clone();
|
||||
cloned_sym.scope = SymbolScope::Local;
|
||||
last.0.insert(cloned_sym.name.to_owned(), cloned_sym);
|
||||
}
|
||||
}
|
||||
SymbolTableType::Comprehension => {
|
||||
// TODO check for conflicts - requires more context information about variables
|
||||
match symbols.get_mut(&symbol.name) {
|
||||
Some(parent_symbol) => {
|
||||
// check if assignee is an iterator in top scope
|
||||
if parent_symbol.is_iter {
|
||||
return Err(SymbolTableError {
|
||||
error: format!("assignment expression cannot rebind comprehension iteration variable {}", symbol.name),
|
||||
location: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
// we synthesize the assignment to the symbol from inner scope
|
||||
parent_symbol.is_assigned = true; // more checks are required
|
||||
}
|
||||
None => {
|
||||
let cloned_sym = symbol.clone();
|
||||
|
||||
// extend the scope of the inner symbol
|
||||
// as we are in a nested comprehension, we expect that the symbol is needed
|
||||
// ouside, too, and set it therefore to non-local scope. I.e., we expect to
|
||||
// find a definition on a higher level
|
||||
let mut cloned_sym = symbol.clone();
|
||||
cloned_sym.scope = SymbolScope::Nonlocal;
|
||||
last.0.insert(cloned_sym.name.to_owned(), cloned_sym);
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +396,7 @@ enum SymbolUsage {
|
|||
Assigned,
|
||||
Parameter,
|
||||
AssignedNamedExprInCompr,
|
||||
Iter,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -369,6 +413,8 @@ enum ExpressionContext {
|
|||
Load,
|
||||
Store,
|
||||
Delete,
|
||||
Iter,
|
||||
IterDefinitionExp,
|
||||
}
|
||||
|
||||
impl SymbolTableBuilder {
|
||||
|
@ -705,11 +751,14 @@ impl SymbolTableBuilder {
|
|||
|
||||
let mut is_first_generator = true;
|
||||
for generator in generators {
|
||||
self.scan_expression(&generator.target, &ExpressionContext::Store)?;
|
||||
self.scan_expression(&generator.target, &ExpressionContext::Iter)?;
|
||||
if is_first_generator {
|
||||
is_first_generator = false;
|
||||
} else {
|
||||
self.scan_expression(&generator.iter, &ExpressionContext::Load)?;
|
||||
self.scan_expression(
|
||||
&generator.iter,
|
||||
&ExpressionContext::IterDefinitionExp,
|
||||
)?;
|
||||
}
|
||||
|
||||
for if_expr in &generator.ifs {
|
||||
|
@ -721,14 +770,22 @@ impl SymbolTableBuilder {
|
|||
|
||||
// The first iterable is passed as an argument into the created function:
|
||||
assert!(!generators.is_empty());
|
||||
self.scan_expression(&generators[0].iter, &ExpressionContext::Load)?;
|
||||
self.scan_expression(&generators[0].iter, &ExpressionContext::IterDefinitionExp)?;
|
||||
}
|
||||
Call {
|
||||
function,
|
||||
args,
|
||||
keywords,
|
||||
} => {
|
||||
self.scan_expression(function, &ExpressionContext::Load)?;
|
||||
match *context {
|
||||
ExpressionContext::IterDefinitionExp => {
|
||||
self.scan_expression(function, &ExpressionContext::IterDefinitionExp)?;
|
||||
}
|
||||
_ => {
|
||||
self.scan_expression(function, &ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.scan_expressions(args, &ExpressionContext::Load)?;
|
||||
for keyword in keywords {
|
||||
self.scan_expression(&keyword.value, &ExpressionContext::Load)?;
|
||||
|
@ -743,17 +800,27 @@ impl SymbolTableBuilder {
|
|||
ExpressionContext::Delete => {
|
||||
self.register_name(name, SymbolUsage::Used)?;
|
||||
}
|
||||
ExpressionContext::Load => {
|
||||
ExpressionContext::Load | ExpressionContext::IterDefinitionExp => {
|
||||
self.register_name(name, SymbolUsage::Used)?;
|
||||
}
|
||||
ExpressionContext::Store => {
|
||||
self.register_name(name, SymbolUsage::Assigned)?;
|
||||
}
|
||||
ExpressionContext::Iter => {
|
||||
self.register_name(name, SymbolUsage::Iter)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Lambda { args, body } => {
|
||||
self.enter_function("lambda", args, expression.location.row())?;
|
||||
self.scan_expression(body, &ExpressionContext::Load)?;
|
||||
match *context {
|
||||
ExpressionContext::IterDefinitionExp => {
|
||||
self.scan_expression(body, &ExpressionContext::IterDefinitionExp)?;
|
||||
}
|
||||
_ => {
|
||||
self.scan_expression(body, &ExpressionContext::Load)?;
|
||||
}
|
||||
}
|
||||
self.leave_scope();
|
||||
}
|
||||
IfExpression { test, body, orelse } => {
|
||||
|
@ -763,6 +830,15 @@ impl SymbolTableBuilder {
|
|||
}
|
||||
|
||||
NamedExpression { left, right } => {
|
||||
// named expressions are not allowed in the definiton of
|
||||
// comprehension iterator definitions
|
||||
if let ExpressionContext::IterDefinitionExp = *context {
|
||||
return Err(SymbolTableError {
|
||||
error: "assignment expression cannot be used in a comprehension iterable expression".to_string(),
|
||||
location: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
self.scan_expression(right, &ExpressionContext::Load)?;
|
||||
|
||||
// special handling for assigned identifier in named expressions
|
||||
|
@ -933,8 +1009,24 @@ impl SymbolTableBuilder {
|
|||
SymbolUsage::Used => {
|
||||
symbol.is_referenced = true;
|
||||
}
|
||||
SymbolUsage::Iter => {
|
||||
symbol.is_iter = true;
|
||||
symbol.scope = SymbolScope::Local;
|
||||
}
|
||||
}
|
||||
|
||||
// and even more checking
|
||||
// it is not allowed to assign to iterator variables (by named expressions)
|
||||
if symbol.is_iter && symbol.is_assigned
|
||||
/*&& symbol.is_assign_namedexpr_in_comprehension*/
|
||||
{
|
||||
return Err(SymbolTableError {
|
||||
error:
|
||||
"assignment expression cannot be used in a comprehension iterable expression"
|
||||
.to_string(),
|
||||
location,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue