mirror of
https://github.com/RustPython/Parser.git
synced 2025-07-16 17:45:24 +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
|
// 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.
|
// this is required to correct the scope in the analysis.
|
||||||
pub is_assign_namedexpr_in_comprehension: bool,
|
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 {
|
impl Symbol {
|
||||||
|
@ -118,6 +122,7 @@ impl Symbol {
|
||||||
is_parameter: false,
|
is_parameter: false,
|
||||||
is_free: false,
|
is_free: false,
|
||||||
is_assign_namedexpr_in_comprehension: 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
|
if symbol.is_assign_namedexpr_in_comprehension
|
||||||
&& curr_st_typ == SymbolTableType::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)?
|
self.analyze_symbol_comprehension(symbol, 0)?
|
||||||
} else {
|
} else {
|
||||||
match symbol.scope {
|
match symbol.scope {
|
||||||
|
@ -303,16 +312,33 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
||||||
let symbols = &mut last.0;
|
let symbols = &mut last.0;
|
||||||
let table_type = last.1;
|
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 {
|
match table_type {
|
||||||
SymbolTableType::Module => {
|
SymbolTableType::Module => {
|
||||||
symbol.scope = SymbolScope::Global;
|
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 => {
|
SymbolTableType::Function => {
|
||||||
if let Some(parent_symbol) = symbols.get_mut(&symbol.name) {
|
if let Some(parent_symbol) = symbols.get_mut(&symbol.name) {
|
||||||
if let SymbolScope::Unknown = parent_symbol.scope {
|
if let SymbolScope::Unknown = parent_symbol.scope {
|
||||||
parent_symbol.is_assigned = true; // this information is new, as the asignment is done in inner 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 {
|
match symbol.scope {
|
||||||
|
@ -323,17 +349,34 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
||||||
symbol.scope = SymbolScope::Nonlocal;
|
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 => {
|
SymbolTableType::Comprehension => {
|
||||||
// TODO check for conflicts - requires more context information about variables
|
// TODO check for conflicts - requires more context information about variables
|
||||||
match symbols.get_mut(&symbol.name) {
|
match symbols.get_mut(&symbol.name) {
|
||||||
Some(parent_symbol) => {
|
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
|
parent_symbol.is_assigned = true; // more checks are required
|
||||||
}
|
}
|
||||||
None => {
|
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);
|
last.0.insert(cloned_sym.name.to_owned(), cloned_sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,6 +396,7 @@ enum SymbolUsage {
|
||||||
Assigned,
|
Assigned,
|
||||||
Parameter,
|
Parameter,
|
||||||
AssignedNamedExprInCompr,
|
AssignedNamedExprInCompr,
|
||||||
|
Iter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -369,6 +413,8 @@ enum ExpressionContext {
|
||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
Delete,
|
Delete,
|
||||||
|
Iter,
|
||||||
|
IterDefinitionExp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolTableBuilder {
|
impl SymbolTableBuilder {
|
||||||
|
@ -705,11 +751,14 @@ impl SymbolTableBuilder {
|
||||||
|
|
||||||
let mut is_first_generator = true;
|
let mut is_first_generator = true;
|
||||||
for generator in generators {
|
for generator in generators {
|
||||||
self.scan_expression(&generator.target, &ExpressionContext::Store)?;
|
self.scan_expression(&generator.target, &ExpressionContext::Iter)?;
|
||||||
if is_first_generator {
|
if is_first_generator {
|
||||||
is_first_generator = false;
|
is_first_generator = false;
|
||||||
} else {
|
} else {
|
||||||
self.scan_expression(&generator.iter, &ExpressionContext::Load)?;
|
self.scan_expression(
|
||||||
|
&generator.iter,
|
||||||
|
&ExpressionContext::IterDefinitionExp,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for if_expr in &generator.ifs {
|
for if_expr in &generator.ifs {
|
||||||
|
@ -721,14 +770,22 @@ impl SymbolTableBuilder {
|
||||||
|
|
||||||
// The first iterable is passed as an argument into the created function:
|
// The first iterable is passed as an argument into the created function:
|
||||||
assert!(!generators.is_empty());
|
assert!(!generators.is_empty());
|
||||||
self.scan_expression(&generators[0].iter, &ExpressionContext::Load)?;
|
self.scan_expression(&generators[0].iter, &ExpressionContext::IterDefinitionExp)?;
|
||||||
}
|
}
|
||||||
Call {
|
Call {
|
||||||
function,
|
function,
|
||||||
args,
|
args,
|
||||||
keywords,
|
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)?;
|
self.scan_expressions(args, &ExpressionContext::Load)?;
|
||||||
for keyword in keywords {
|
for keyword in keywords {
|
||||||
self.scan_expression(&keyword.value, &ExpressionContext::Load)?;
|
self.scan_expression(&keyword.value, &ExpressionContext::Load)?;
|
||||||
|
@ -743,17 +800,27 @@ impl SymbolTableBuilder {
|
||||||
ExpressionContext::Delete => {
|
ExpressionContext::Delete => {
|
||||||
self.register_name(name, SymbolUsage::Used)?;
|
self.register_name(name, SymbolUsage::Used)?;
|
||||||
}
|
}
|
||||||
ExpressionContext::Load => {
|
ExpressionContext::Load | ExpressionContext::IterDefinitionExp => {
|
||||||
self.register_name(name, SymbolUsage::Used)?;
|
self.register_name(name, SymbolUsage::Used)?;
|
||||||
}
|
}
|
||||||
ExpressionContext::Store => {
|
ExpressionContext::Store => {
|
||||||
self.register_name(name, SymbolUsage::Assigned)?;
|
self.register_name(name, SymbolUsage::Assigned)?;
|
||||||
}
|
}
|
||||||
|
ExpressionContext::Iter => {
|
||||||
|
self.register_name(name, SymbolUsage::Iter)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Lambda { args, body } => {
|
Lambda { args, body } => {
|
||||||
self.enter_function("lambda", args, expression.location.row())?;
|
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();
|
self.leave_scope();
|
||||||
}
|
}
|
||||||
IfExpression { test, body, orelse } => {
|
IfExpression { test, body, orelse } => {
|
||||||
|
@ -763,6 +830,15 @@ impl SymbolTableBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
NamedExpression { left, right } => {
|
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)?;
|
self.scan_expression(right, &ExpressionContext::Load)?;
|
||||||
|
|
||||||
// special handling for assigned identifier in named expressions
|
// special handling for assigned identifier in named expressions
|
||||||
|
@ -933,8 +1009,24 @@ impl SymbolTableBuilder {
|
||||||
SymbolUsage::Used => {
|
SymbolUsage::Used => {
|
||||||
symbol.is_referenced = true;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue