Add box to stmt
This commit is contained in:
@@ -114,13 +114,13 @@ impl<'src> Parser<'src> {
|
||||
let start = self.node_start();
|
||||
|
||||
match self.current_token_kind() {
|
||||
TokenKind::If => Stmt::If(self.parse_if_statement()),
|
||||
TokenKind::For => Stmt::For(self.parse_for_statement(start)),
|
||||
TokenKind::While => Stmt::While(self.parse_while_statement()),
|
||||
TokenKind::Def => Stmt::FunctionDef(self.parse_function_definition(vec![], start)),
|
||||
TokenKind::Class => Stmt::ClassDef(self.parse_class_definition(vec![], start)),
|
||||
TokenKind::Try => Stmt::Try(self.parse_try_statement()),
|
||||
TokenKind::With => Stmt::With(self.parse_with_statement(start)),
|
||||
TokenKind::If => self.parse_if_statement().into(),
|
||||
TokenKind::For => self.parse_for_statement(start).into(),
|
||||
TokenKind::While => self.parse_while_statement().into(),
|
||||
TokenKind::Def => self.parse_function_definition(vec![], start).into(),
|
||||
TokenKind::Class => self.parse_class_definition(vec![], start).into(),
|
||||
TokenKind::Try => self.parse_try_statement().into(),
|
||||
TokenKind::With => self.parse_with_statement(start).into(),
|
||||
TokenKind::At => self.parse_decorators(),
|
||||
TokenKind::Async => self.parse_async_statement(),
|
||||
token => {
|
||||
@@ -130,11 +130,11 @@ impl<'src> Parser<'src> {
|
||||
|
||||
match self.classify_match_token() {
|
||||
MatchTokenKind::Keyword => {
|
||||
return Stmt::Match(self.parse_match_statement());
|
||||
return self.parse_match_statement().into();
|
||||
}
|
||||
MatchTokenKind::KeywordOrIdentifier => {
|
||||
if let Some(match_stmt) = self.try_parse_match_statement() {
|
||||
return Stmt::Match(match_stmt);
|
||||
return match_stmt.into();
|
||||
}
|
||||
}
|
||||
MatchTokenKind::Identifier => {}
|
||||
@@ -262,19 +262,19 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html>
|
||||
fn parse_simple_statement(&mut self) -> Stmt {
|
||||
match self.current_token_kind() {
|
||||
TokenKind::Return => Stmt::Return(self.parse_return_statement()),
|
||||
TokenKind::Import => Stmt::Import(self.parse_import_statement()),
|
||||
TokenKind::From => Stmt::ImportFrom(self.parse_from_import_statement()),
|
||||
TokenKind::Pass => Stmt::Pass(self.parse_pass_statement()),
|
||||
TokenKind::Continue => Stmt::Continue(self.parse_continue_statement()),
|
||||
TokenKind::Break => Stmt::Break(self.parse_break_statement()),
|
||||
TokenKind::Raise => Stmt::Raise(self.parse_raise_statement()),
|
||||
TokenKind::Del => Stmt::Delete(self.parse_delete_statement()),
|
||||
TokenKind::Assert => Stmt::Assert(self.parse_assert_statement()),
|
||||
TokenKind::Global => Stmt::Global(self.parse_global_statement()),
|
||||
TokenKind::Nonlocal => Stmt::Nonlocal(self.parse_nonlocal_statement()),
|
||||
TokenKind::Return => self.parse_return_statement().into(),
|
||||
TokenKind::Import => self.parse_import_statement().into(),
|
||||
TokenKind::From => self.parse_from_import_statement().into(),
|
||||
TokenKind::Pass => self.parse_pass_statement().into(),
|
||||
TokenKind::Continue => self.parse_continue_statement().into(),
|
||||
TokenKind::Break => self.parse_break_statement().into(),
|
||||
TokenKind::Raise => self.parse_raise_statement().into(),
|
||||
TokenKind::Del => self.parse_delete_statement().into(),
|
||||
TokenKind::Assert => self.parse_assert_statement().into(),
|
||||
TokenKind::Global => self.parse_global_statement().into(),
|
||||
TokenKind::Nonlocal => self.parse_nonlocal_statement().into(),
|
||||
TokenKind::IpyEscapeCommand => {
|
||||
Stmt::IpyEscapeCommand(self.parse_ipython_escape_command_statement())
|
||||
self.parse_ipython_escape_command_statement().into()
|
||||
}
|
||||
token => {
|
||||
if token == TokenKind::Type {
|
||||
@@ -285,7 +285,7 @@ impl<'src> Parser<'src> {
|
||||
if (first == TokenKind::Name || first.is_soft_keyword())
|
||||
&& matches!(second, TokenKind::Lsqb | TokenKind::Equal)
|
||||
{
|
||||
return Stmt::TypeAlias(self.parse_type_alias_statement());
|
||||
return self.parse_type_alias_statement().into();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,19 +296,17 @@ impl<'src> Parser<'src> {
|
||||
self.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or());
|
||||
|
||||
if self.at(TokenKind::Equal) {
|
||||
Stmt::Assign(self.parse_assign_statement(parsed_expr, start))
|
||||
self.parse_assign_statement(parsed_expr, start).into()
|
||||
} else if self.at(TokenKind::Colon) {
|
||||
Stmt::AnnAssign(self.parse_annotated_assignment_statement(parsed_expr, start))
|
||||
self.parse_annotated_assignment_statement(parsed_expr, start).into()
|
||||
} else if let Some(op) = self.current_token_kind().as_augmented_assign_operator() {
|
||||
Stmt::AugAssign(self.parse_augmented_assignment_statement(
|
||||
self.parse_augmented_assignment_statement(
|
||||
parsed_expr,
|
||||
op,
|
||||
start,
|
||||
))
|
||||
).into()
|
||||
} else if self.options.mode == Mode::Ipython && self.at(TokenKind::Question) {
|
||||
Stmt::IpyEscapeCommand(
|
||||
self.parse_ipython_help_end_escape_command_statement(&parsed_expr),
|
||||
)
|
||||
self.parse_ipython_help_end_escape_command_statement(&parsed_expr).into()
|
||||
} else {
|
||||
Stmt::Expr(ast::StmtExpr {
|
||||
range: self.node_range(start),
|
||||
@@ -2728,24 +2726,24 @@ impl<'src> Parser<'src> {
|
||||
match self.current_token_kind() {
|
||||
// test_ok async_function_definition
|
||||
// async def foo(): ...
|
||||
TokenKind::Def => Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
TokenKind::Def => ast::StmtFunctionDef {
|
||||
is_async: true,
|
||||
..self.parse_function_definition(vec![], async_start)
|
||||
}),
|
||||
}.into(),
|
||||
|
||||
// test_ok async_with_statement
|
||||
// async with item: ...
|
||||
TokenKind::With => Stmt::With(ast::StmtWith {
|
||||
TokenKind::With => ast::StmtWith {
|
||||
is_async: true,
|
||||
..self.parse_with_statement(async_start)
|
||||
}),
|
||||
}.into(),
|
||||
|
||||
// test_ok async_for_statement
|
||||
// async for target in iter: ...
|
||||
TokenKind::For => Stmt::For(ast::StmtFor {
|
||||
TokenKind::For => ast::StmtFor {
|
||||
is_async: true,
|
||||
..self.parse_for_statement(async_start)
|
||||
}),
|
||||
}.into(),
|
||||
|
||||
kind => {
|
||||
// test_err async_unexpected_token
|
||||
@@ -2888,18 +2886,18 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
match self.current_token_kind() {
|
||||
TokenKind::Def => Stmt::FunctionDef(self.parse_function_definition(decorators, start)),
|
||||
TokenKind::Class => Stmt::ClassDef(self.parse_class_definition(decorators, start)),
|
||||
TokenKind::Def => self.parse_function_definition(decorators, start).into(),
|
||||
TokenKind::Class => self.parse_class_definition(decorators, start).into(),
|
||||
TokenKind::Async if self.peek() == TokenKind::Def => {
|
||||
self.bump(TokenKind::Async);
|
||||
|
||||
// test_ok decorator_async_function
|
||||
// @decorator
|
||||
// async def foo(): ...
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
ast::StmtFunctionDef {
|
||||
is_async: true,
|
||||
..self.parse_function_definition(decorators, start)
|
||||
})
|
||||
}.into()
|
||||
}
|
||||
_ => {
|
||||
// test_err decorator_unexpected_token
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
//! be called in a parent `Visitor`'s `visit_stmt` and `visit_expr` methods, respectively.
|
||||
|
||||
use ruff_python_ast::{
|
||||
self as ast, Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt, StmtExpr,
|
||||
StmtFunctionDef, StmtImportFrom,
|
||||
self as ast, Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt,
|
||||
comparable::ComparableExpr,
|
||||
helpers,
|
||||
visitor::{Visitor, walk_expr, walk_stmt},
|
||||
@@ -59,13 +58,14 @@ impl SemanticSyntaxChecker {
|
||||
|
||||
fn check_stmt<Ctx: SemanticSyntaxContext>(&mut self, stmt: &ast::Stmt, ctx: &Ctx) {
|
||||
match stmt {
|
||||
Stmt::ImportFrom(StmtImportFrom {
|
||||
range,
|
||||
module,
|
||||
level,
|
||||
names,
|
||||
..
|
||||
}) => {
|
||||
Stmt::ImportFrom(node) => {
|
||||
let ast::StmtImportFrom {
|
||||
range,
|
||||
module,
|
||||
level,
|
||||
names,
|
||||
node_index: _,
|
||||
} = &**node;
|
||||
if matches!(module.as_deref(), Some("__future__")) {
|
||||
for name in names {
|
||||
if !is_known_future_feature(&name.name) {
|
||||
@@ -124,17 +124,19 @@ impl SemanticSyntaxChecker {
|
||||
visitor.visit_pattern(&case.pattern);
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
type_params,
|
||||
parameters,
|
||||
..
|
||||
}) => {
|
||||
Stmt::FunctionDef(node) => {
|
||||
let ast::StmtFunctionDef {
|
||||
type_params,
|
||||
parameters,
|
||||
..
|
||||
} = &**node;
|
||||
if let Some(type_params) = type_params {
|
||||
Self::duplicate_type_parameter_name(type_params, ctx);
|
||||
}
|
||||
Self::duplicate_parameter_name(parameters, ctx);
|
||||
}
|
||||
Stmt::Global(ast::StmtGlobal { names, .. }) => {
|
||||
Stmt::Global(node) => {
|
||||
let ast::StmtGlobal { names, .. } = &**node;
|
||||
for name in names {
|
||||
if ctx.is_bound_parameter(name) {
|
||||
Self::add_error(
|
||||
@@ -145,19 +147,18 @@ impl SemanticSyntaxChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::ClassDef(ast::StmtClassDef {
|
||||
type_params: Some(type_params),
|
||||
..
|
||||
})
|
||||
| Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||
type_params: Some(type_params),
|
||||
..
|
||||
}) => {
|
||||
Stmt::ClassDef(node) if node.type_params.is_some() => {
|
||||
let type_params = node.type_params.as_ref().unwrap();
|
||||
Self::duplicate_type_parameter_name(type_params, ctx);
|
||||
Self::type_parameter_default_order(type_params, ctx);
|
||||
}
|
||||
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
|
||||
if let [Expr::Starred(ast::ExprStarred { range, .. })] = targets.as_slice() {
|
||||
Stmt::TypeAlias(node) if node.type_params.is_some() => {
|
||||
let type_params = node.type_params.as_ref().unwrap();
|
||||
Self::duplicate_type_parameter_name(type_params, ctx);
|
||||
Self::type_parameter_default_order(type_params, ctx);
|
||||
}
|
||||
Stmt::Assign(node) => {
|
||||
if let [Expr::Starred(ast::ExprStarred { range, .. })] = node.targets.as_slice() {
|
||||
// test_ok single_starred_assignment_target
|
||||
// (*a,) = (1,)
|
||||
// *a, = (1,)
|
||||
@@ -183,7 +184,7 @@ impl SemanticSyntaxChecker {
|
||||
// _ = *{42}
|
||||
// _ = *list()
|
||||
// _ = *(p + q)
|
||||
Self::invalid_star_expression(value, ctx);
|
||||
Self::invalid_star_expression(&node.value, ctx);
|
||||
}
|
||||
Stmt::Return(ast::StmtReturn {
|
||||
value,
|
||||
@@ -199,12 +200,13 @@ impl SemanticSyntaxChecker {
|
||||
Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range);
|
||||
}
|
||||
}
|
||||
Stmt::For(ast::StmtFor {
|
||||
target,
|
||||
iter,
|
||||
is_async,
|
||||
..
|
||||
}) => {
|
||||
Stmt::For(node) => {
|
||||
let ast::StmtFor {
|
||||
target,
|
||||
iter,
|
||||
is_async,
|
||||
..
|
||||
} = &**node;
|
||||
// test_err single_star_for
|
||||
// for _ in *x: ...
|
||||
// for *x in xs: ...
|
||||
@@ -218,14 +220,15 @@ impl SemanticSyntaxChecker {
|
||||
);
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { is_async: true, .. }) => {
|
||||
Stmt::With(node) if node.is_async => {
|
||||
Self::await_outside_async_function(
|
||||
ctx,
|
||||
stmt,
|
||||
AwaitOutsideAsyncFunctionKind::AsyncWith,
|
||||
);
|
||||
}
|
||||
Stmt::Nonlocal(ast::StmtNonlocal { names, range, .. }) => {
|
||||
Stmt::Nonlocal(node) => {
|
||||
let ast::StmtNonlocal { names, range, .. } = &**node;
|
||||
// test_ok nonlocal_declaration_at_module_level
|
||||
// def _():
|
||||
// nonlocal x
|
||||
@@ -272,7 +275,7 @@ impl SemanticSyntaxChecker {
|
||||
|
||||
fn check_annotation<Ctx: SemanticSyntaxContext>(stmt: &ast::Stmt, ctx: &Ctx) {
|
||||
match stmt {
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => {
|
||||
Stmt::AnnAssign(node) => {
|
||||
if ctx.python_version() > PythonVersion::PY313 {
|
||||
// test_ok valid_annotation_py313
|
||||
// # parse_options: {"target-version": "3.13"}
|
||||
@@ -295,15 +298,10 @@ impl SemanticSyntaxChecker {
|
||||
position: InvalidExpressionPosition::TypeAnnotation,
|
||||
ctx,
|
||||
};
|
||||
visitor.visit_expr(annotation);
|
||||
visitor.visit_expr(&node.annotation);
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
type_params,
|
||||
parameters,
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
Stmt::FunctionDef(node) => {
|
||||
// test_ok valid_annotation_function_py313
|
||||
// # parse_options: {"target-version": "3.13"}
|
||||
// def f() -> (y := 3): ...
|
||||
@@ -355,32 +353,30 @@ impl SemanticSyntaxChecker {
|
||||
position: InvalidExpressionPosition::TypeAnnotation,
|
||||
ctx,
|
||||
};
|
||||
if let Some(type_params) = type_params {
|
||||
if let Some(type_params) = &node.type_params {
|
||||
visitor.visit_type_params(type_params);
|
||||
}
|
||||
// the __future__ annotation error takes precedence over the generic error
|
||||
if ctx.future_annotations_or_stub() || ctx.python_version() > PythonVersion::PY313 {
|
||||
visitor.position = InvalidExpressionPosition::TypeAnnotation;
|
||||
} else if type_params.is_some() {
|
||||
} else if node.type_params.is_some() {
|
||||
visitor.position = InvalidExpressionPosition::GenericDefinition;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for param in parameters
|
||||
for param in node
|
||||
.parameters
|
||||
.iter()
|
||||
.filter_map(ast::AnyParameterRef::annotation)
|
||||
{
|
||||
visitor.visit_expr(param);
|
||||
}
|
||||
if let Some(returns) = returns {
|
||||
if let Some(returns) = &node.returns {
|
||||
visitor.visit_expr(returns);
|
||||
}
|
||||
}
|
||||
Stmt::ClassDef(ast::StmtClassDef {
|
||||
type_params: Some(type_params),
|
||||
arguments,
|
||||
..
|
||||
}) => {
|
||||
Stmt::ClassDef(node) if node.type_params.is_some() => {
|
||||
let type_params = node.type_params.as_ref().unwrap();
|
||||
// test_ok valid_annotation_class
|
||||
// class F(y := list): ...
|
||||
// def f():
|
||||
@@ -402,14 +398,12 @@ impl SemanticSyntaxChecker {
|
||||
ctx,
|
||||
};
|
||||
visitor.visit_type_params(type_params);
|
||||
if let Some(arguments) = arguments {
|
||||
if let Some(arguments) = &node.arguments {
|
||||
visitor.position = InvalidExpressionPosition::GenericDefinition;
|
||||
visitor.visit_arguments(arguments);
|
||||
}
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||
type_params, value, ..
|
||||
}) => {
|
||||
Stmt::TypeAlias(node) => {
|
||||
// test_err invalid_annotation_type_alias
|
||||
// type X[T: (yield 1)] = int # TypeVar bound
|
||||
// type X[T = (yield 1)] = int # TypeVar default
|
||||
@@ -423,8 +417,8 @@ impl SemanticSyntaxChecker {
|
||||
position: InvalidExpressionPosition::TypeAlias,
|
||||
ctx,
|
||||
};
|
||||
visitor.visit_expr(value);
|
||||
if let Some(type_params) = type_params {
|
||||
visitor.visit_expr(&node.value);
|
||||
if let Some(type_params) = &node.type_params {
|
||||
visitor.visit_type_params(type_params);
|
||||
}
|
||||
}
|
||||
@@ -485,43 +479,34 @@ impl SemanticSyntaxChecker {
|
||||
/// Check for [`SemanticSyntaxErrorKind::WriteToDebug`] in `stmt`.
|
||||
fn debug_shadowing<Ctx: SemanticSyntaxContext>(stmt: &ast::Stmt, ctx: &Ctx) {
|
||||
match stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
name,
|
||||
type_params,
|
||||
parameters,
|
||||
..
|
||||
}) => {
|
||||
Stmt::FunctionDef(node) => {
|
||||
// test_err debug_shadow_function
|
||||
// def __debug__(): ... # function name
|
||||
// def f[__debug__](): ... # type parameter name
|
||||
// def f(__debug__): ... # parameter name
|
||||
Self::check_identifier(name, ctx);
|
||||
if let Some(type_params) = type_params {
|
||||
Self::check_identifier(&node.name, ctx);
|
||||
if let Some(type_params) = &node.type_params {
|
||||
for type_param in type_params.iter() {
|
||||
Self::check_identifier(type_param.name(), ctx);
|
||||
}
|
||||
}
|
||||
for parameter in parameters {
|
||||
for parameter in &node.parameters {
|
||||
Self::check_identifier(parameter.name(), ctx);
|
||||
}
|
||||
}
|
||||
Stmt::ClassDef(ast::StmtClassDef {
|
||||
name, type_params, ..
|
||||
}) => {
|
||||
Stmt::ClassDef(node) => {
|
||||
// test_err debug_shadow_class
|
||||
// class __debug__: ... # class name
|
||||
// class C[__debug__]: ... # type parameter name
|
||||
Self::check_identifier(name, ctx);
|
||||
if let Some(type_params) = type_params {
|
||||
Self::check_identifier(&node.name, ctx);
|
||||
if let Some(type_params) = &node.type_params {
|
||||
for type_param in type_params.iter() {
|
||||
Self::check_identifier(type_param.name(), ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||
type_params: Some(type_params),
|
||||
..
|
||||
}) => {
|
||||
Stmt::TypeAlias(node) if node.type_params.is_some() => {
|
||||
let type_params = node.type_params.as_ref().unwrap();
|
||||
// test_err debug_shadow_type_alias
|
||||
// type __debug__ = list[int] # visited as an Expr but still flagged
|
||||
// type Debug[__debug__] = str
|
||||
@@ -529,8 +514,7 @@ impl SemanticSyntaxChecker {
|
||||
Self::check_identifier(type_param.name(), ctx);
|
||||
}
|
||||
}
|
||||
Stmt::Import(ast::StmtImport { names, .. })
|
||||
| Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => {
|
||||
Stmt::Import(node) => {
|
||||
// test_err debug_shadow_import
|
||||
// import __debug__
|
||||
// import debug as __debug__
|
||||
@@ -541,18 +525,27 @@ impl SemanticSyntaxChecker {
|
||||
// import __debug__ as debug
|
||||
// from __debug__ import Some
|
||||
// from x import __debug__ as debug
|
||||
for name in names {
|
||||
for name in &node.names {
|
||||
match &name.asname {
|
||||
Some(asname) => Self::check_identifier(asname, ctx),
|
||||
None => Self::check_identifier(&name.name, ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry { handlers, .. }) => {
|
||||
Stmt::ImportFrom(node) => {
|
||||
for name in &node.names {
|
||||
match &name.asname {
|
||||
Some(asname) => Self::check_identifier(asname, ctx),
|
||||
None => Self::check_identifier(&name.name, ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Try(node) => {
|
||||
// test_err debug_shadow_try
|
||||
// try: ...
|
||||
// except Exception as __debug__: ...
|
||||
for handler in handlers
|
||||
for handler in node
|
||||
.handlers
|
||||
.iter()
|
||||
.filter_map(ast::ExceptHandler::as_except_handler)
|
||||
{
|
||||
@@ -732,18 +725,18 @@ impl SemanticSyntaxChecker {
|
||||
|
||||
// update internal state
|
||||
match stmt {
|
||||
Stmt::Expr(StmtExpr { value, .. })
|
||||
if !self.seen_module_docstring_boundary && value.is_string_literal_expr() => {}
|
||||
Stmt::ImportFrom(StmtImportFrom { module, .. }) => {
|
||||
Stmt::Expr(node)
|
||||
if !self.seen_module_docstring_boundary && node.value.is_string_literal_expr() => {}
|
||||
Stmt::ImportFrom(node) => {
|
||||
// Allow __future__ imports until we see a non-__future__ import.
|
||||
if !matches!(module.as_deref(), Some("__future__")) {
|
||||
if !matches!(node.module.as_deref(), Some("__future__")) {
|
||||
self.seen_futures_boundary = true;
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(StmtFunctionDef { is_async, body, .. }) => {
|
||||
if *is_async {
|
||||
Stmt::FunctionDef(node) => {
|
||||
if node.is_async {
|
||||
let mut visitor = ReturnVisitor::default();
|
||||
visitor.visit_body(body);
|
||||
visitor.visit_body(&node.body);
|
||||
|
||||
if visitor.has_yield {
|
||||
if let Some(return_range) = visitor.return_range {
|
||||
|
||||
Reference in New Issue
Block a user